From bb1d662ee134ca8ff6402eb150c718357ce2acbf Mon Sep 17 00:00:00 2001 From: leetcrypt Date: Sat, 30 May 2026 13:29:14 -0700 Subject: [PATCH] =?UTF-8?q?chore:=20rename=20project=20coven=20=E2=86=92?= =?UTF-8?q?=20hack-house=20=E2=9B=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"), README, CLI strings, and branch (coven → hack-house). Gitea repo renamed cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest + golden-vector test still green, binary is now `hack-house`. Co-Authored-By: Claude Opus 4.8 --- .venv/bin/Activate.ps1 | 247 + .venv/bin/activate | 70 + .venv/bin/activate.csh | 27 + .venv/bin/activate.fish | 69 + .venv/bin/httpx | 6 + .venv/bin/idna | 6 + .venv/bin/markdown-it | 6 + .venv/bin/normalizer | 6 + .venv/bin/pip | 8 + .venv/bin/pip3 | 8 + .venv/bin/pip3.12 | 8 + .venv/bin/py.test | 6 + .venv/bin/pygmentize | 6 + .venv/bin/pytest | 6 + .venv/bin/python | 1 + .venv/bin/python3 | 1 + .venv/bin/python3.12 | 1 + .venv/bin/sanic | 6 + .venv/bin/websockets | 6 + ...821__mypyc.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 433312 bytes ...fi_backend.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 348808 bytes .../site-packages/_distutils_hack/__init__.py | 239 + .../site-packages/_distutils_hack/override.py | 1 + .../site-packages/_pytest/__init__.py | 13 + .../site-packages/_pytest/_argcomplete.py | 117 + .../site-packages/_pytest/_code/__init__.py | 26 + .../site-packages/_pytest/_code/code.py | 1571 ++ .../site-packages/_pytest/_code/source.py | 225 + .../site-packages/_pytest/_io/__init__.py | 10 + .../site-packages/_pytest/_io/pprint.py | 673 + .../site-packages/_pytest/_io/saferepr.py | 130 + .../_pytest/_io/terminalwriter.py | 258 + .../site-packages/_pytest/_io/wcwidth.py | 57 + .../site-packages/_pytest/_py/__init__.py | 0 .../site-packages/_pytest/_py/error.py | 119 + .../site-packages/_pytest/_py/path.py | 1475 ++ .../site-packages/_pytest/_version.py | 24 + .../_pytest/assertion/__init__.py | 208 + .../_pytest/assertion/rewrite.py | 1202 ++ .../_pytest/assertion/truncate.py | 137 + .../site-packages/_pytest/assertion/util.py | 615 + .../site-packages/_pytest/cacheprovider.py | 646 + .../site-packages/_pytest/capture.py | 1144 ++ .../site-packages/_pytest/compat.py | 314 + .../site-packages/_pytest/config/__init__.py | 2203 ++ .../_pytest/config/argparsing.py | 578 + .../site-packages/_pytest/config/compat.py | 85 + .../_pytest/config/exceptions.py | 15 + .../site-packages/_pytest/config/findpaths.py | 350 + .../site-packages/_pytest/debugging.py | 407 + .../site-packages/_pytest/deprecated.py | 99 + .../site-packages/_pytest/doctest.py | 736 + .../site-packages/_pytest/faulthandler.py | 119 + .../site-packages/_pytest/fixtures.py | 2047 ++ .../site-packages/_pytest/freeze_support.py | 45 + .../site-packages/_pytest/helpconfig.py | 293 + .../site-packages/_pytest/hookspec.py | 1342 ++ .../site-packages/_pytest/junitxml.py | 695 + .../site-packages/_pytest/legacypath.py | 468 + .../site-packages/_pytest/logging.py | 960 + .../python3.12/site-packages/_pytest/main.py | 1203 ++ .../site-packages/_pytest/mark/__init__.py | 301 + .../site-packages/_pytest/mark/expression.py | 353 + .../site-packages/_pytest/mark/structures.py | 664 + .../site-packages/_pytest/monkeypatch.py | 435 + .../python3.12/site-packages/_pytest/nodes.py | 772 + .../site-packages/_pytest/outcomes.py | 308 + .../site-packages/_pytest/pastebin.py | 117 + .../site-packages/_pytest/pathlib.py | 1063 + .../python3.12/site-packages/_pytest/py.typed | 0 .../site-packages/_pytest/pytester.py | 1791 ++ .../_pytest/pytester_assertions.py | 74 + .../site-packages/_pytest/python.py | 1772 ++ .../site-packages/_pytest/python_api.py | 819 + .../site-packages/_pytest/raises.py | 1517 ++ .../site-packages/_pytest/recwarn.py | 367 + .../site-packages/_pytest/reports.py | 694 + .../site-packages/_pytest/runner.py | 580 + .../python3.12/site-packages/_pytest/scope.py | 91 + .../site-packages/_pytest/setuponly.py | 98 + .../site-packages/_pytest/setupplan.py | 39 + .../site-packages/_pytest/skipping.py | 321 + .../python3.12/site-packages/_pytest/stash.py | 116 + .../site-packages/_pytest/stepwise.py | 209 + .../site-packages/_pytest/subtests.py | 411 + .../site-packages/_pytest/terminal.py | 1763 ++ .../site-packages/_pytest/terminalprogress.py | 30 + .../site-packages/_pytest/threadexception.py | 152 + .../site-packages/_pytest/timing.py | 95 + .../site-packages/_pytest/tmpdir.py | 337 + .../site-packages/_pytest/tracemalloc.py | 24 + .../site-packages/_pytest/unittest.py | 632 + .../_pytest/unraisableexception.py | 163 + .../site-packages/_pytest/warning_types.py | 172 + .../site-packages/_pytest/warnings.py | 151 + .../site-packages/_yaml/__init__.py | 33 + .../aiofiles-25.1.0.dist-info/INSTALLER | 1 + .../aiofiles-25.1.0.dist-info/METADATA | 209 + .../aiofiles-25.1.0.dist-info/RECORD | 26 + .../aiofiles-25.1.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 202 + .../aiofiles-25.1.0.dist-info/licenses/NOTICE | 2 + .../site-packages/aiofiles/__init__.py | 23 + .../python3.12/site-packages/aiofiles/base.py | 79 + .../python3.12/site-packages/aiofiles/os.py | 61 + .../site-packages/aiofiles/ospath.py | 37 + .../aiofiles/tempfile/__init__.py | 357 + .../aiofiles/tempfile/temptypes.py | 70 + .../aiofiles/threadpool/__init__.py | 141 + .../aiofiles/threadpool/binary.py | 104 + .../site-packages/aiofiles/threadpool/text.py | 64 + .../aiofiles/threadpool/utils.py | 71 + .../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.13.0.dist-info/INSTALLER | 1 + .../anyio-4.13.0.dist-info/METADATA | 105 + .../anyio-4.13.0.dist-info/RECORD | 92 + .../anyio-4.13.0.dist-info/WHEEL | 5 + .../anyio-4.13.0.dist-info/entry_points.txt | 2 + .../anyio-4.13.0.dist-info/licenses/LICENSE | 20 + .../anyio-4.13.0.dist-info/top_level.txt | 1 + .../site-packages/anyio/__init__.py | 111 + .../site-packages/anyio/_backends/__init__.py | 0 .../site-packages/anyio/_backends/_asyncio.py | 2996 +++ .../site-packages/anyio/_backends/_trio.py | 1343 ++ .../site-packages/anyio/_core/__init__.py | 0 .../anyio/_core/_asyncio_selector_thread.py | 167 + .../anyio/_core/_contextmanagers.py | 200 + .../site-packages/anyio/_core/_eventloop.py | 234 + .../site-packages/anyio/_core/_exceptions.py | 156 + .../site-packages/anyio/_core/_fileio.py | 799 + .../site-packages/anyio/_core/_resources.py | 18 + .../site-packages/anyio/_core/_signals.py | 29 + .../site-packages/anyio/_core/_sockets.py | 1003 + .../site-packages/anyio/_core/_streams.py | 52 + .../anyio/_core/_subprocesses.py | 196 + .../anyio/_core/_synchronization.py | 757 + .../site-packages/anyio/_core/_tasks.py | 173 + .../site-packages/anyio/_core/_tempfile.py | 613 + .../site-packages/anyio/_core/_testing.py | 82 + .../site-packages/anyio/_core/_typedattr.py | 81 + .../site-packages/anyio/abc/__init__.py | 58 + .../site-packages/anyio/abc/_eventloop.py | 409 + .../site-packages/anyio/abc/_resources.py | 33 + .../site-packages/anyio/abc/_sockets.py | 399 + .../site-packages/anyio/abc/_streams.py | 233 + .../site-packages/anyio/abc/_subprocesses.py | 79 + .../site-packages/anyio/abc/_tasks.py | 117 + .../site-packages/anyio/abc/_testing.py | 65 + .../site-packages/anyio/from_thread.py | 578 + .../site-packages/anyio/functools.py | 409 + .../site-packages/anyio/lowlevel.py | 196 + .../python3.12/site-packages/anyio/py.typed | 0 .../site-packages/anyio/pytest_plugin.py | 363 + .../site-packages/anyio/streams/__init__.py | 0 .../site-packages/anyio/streams/buffered.py | 188 + .../site-packages/anyio/streams/file.py | 154 + .../site-packages/anyio/streams/memory.py | 325 + .../site-packages/anyio/streams/stapled.py | 147 + .../site-packages/anyio/streams/text.py | 176 + .../site-packages/anyio/streams/tls.py | 421 + .../site-packages/anyio/to_interpreter.py | 246 + .../site-packages/anyio/to_process.py | 266 + .../site-packages/anyio/to_thread.py | 78 + .../certifi-2026.5.20.dist-info/INSTALLER | 1 + .../certifi-2026.5.20.dist-info/METADATA | 78 + .../certifi-2026.5.20.dist-info/RECORD | 14 + .../certifi-2026.5.20.dist-info/WHEEL | 5 + .../licenses/LICENSE | 20 + .../certifi-2026.5.20.dist-info/top_level.txt | 1 + .../site-packages/certifi/__init__.py | 4 + .../site-packages/certifi/__main__.py | 12 + .../python3.12/site-packages/certifi/core.py | 83 + .../python3.12/site-packages/certifi/py.typed | 0 .../cffi-2.0.0.dist-info/INSTALLER | 1 + .../cffi-2.0.0.dist-info/METADATA | 68 + .../site-packages/cffi-2.0.0.dist-info/RECORD | 49 + .../site-packages/cffi-2.0.0.dist-info/WHEEL | 6 + .../cffi-2.0.0.dist-info/entry_points.txt | 2 + .../cffi-2.0.0.dist-info/licenses/AUTHORS | 8 + .../cffi-2.0.0.dist-info/licenses/LICENSE | 23 + .../cffi-2.0.0.dist-info/top_level.txt | 2 + .../python3.12/site-packages/cffi/__init__.py | 14 + .../site-packages/cffi/_cffi_errors.h | 149 + .../site-packages/cffi/_cffi_include.h | 389 + .../site-packages/cffi/_embedding.h | 550 + .../site-packages/cffi/_imp_emulation.py | 83 + .../site-packages/cffi/_shimmed_dist_utils.py | 45 + .../lib/python3.12/site-packages/cffi/api.py | 967 + .../site-packages/cffi/backend_ctypes.py | 1121 + .../site-packages/cffi/cffi_opcode.py | 187 + .../site-packages/cffi/commontypes.py | 82 + .../python3.12/site-packages/cffi/cparser.py | 1015 + .../python3.12/site-packages/cffi/error.py | 31 + .../site-packages/cffi/ffiplatform.py | 113 + .../lib/python3.12/site-packages/cffi/lock.py | 30 + .../python3.12/site-packages/cffi/model.py | 618 + .../site-packages/cffi/parse_c_type.h | 181 + .../site-packages/cffi/pkgconfig.py | 121 + .../site-packages/cffi/recompiler.py | 1598 ++ .../site-packages/cffi/setuptools_ext.py | 229 + .../site-packages/cffi/vengine_cpy.py | 1087 + .../site-packages/cffi/vengine_gen.py | 679 + .../python3.12/site-packages/cffi/verifier.py | 306 + .../INSTALLER | 1 + .../METADATA | 808 + .../charset_normalizer-3.4.7.dist-info/RECORD | 36 + .../charset_normalizer-3.4.7.dist-info/WHEEL | 7 + .../entry_points.txt | 2 + .../licenses/LICENSE | 21 + .../top_level.txt | 2 + .../charset_normalizer/__init__.py | 48 + .../charset_normalizer/__main__.py | 6 + .../site-packages/charset_normalizer/api.py | 988 + .../cd.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 15912 bytes .../site-packages/charset_normalizer/cd.py | 454 + .../charset_normalizer/cli/__init__.py | 8 + .../charset_normalizer/cli/__main__.py | 362 + .../charset_normalizer/constant.py | 2050 ++ .../charset_normalizer/legacy.py | 79 + .../md.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 15912 bytes .../site-packages/charset_normalizer/md.py | 936 + .../charset_normalizer/models.py | 369 + .../site-packages/charset_normalizer/py.typed | 0 .../site-packages/charset_normalizer/utils.py | 422 + .../charset_normalizer/version.py | 8 + .../cryptography-48.0.0.dist-info/INSTALLER | 1 + .../cryptography-48.0.0.dist-info/METADATA | 110 + .../cryptography-48.0.0.dist-info/RECORD | 196 + .../cryptography-48.0.0.dist-info/REQUESTED | 0 .../cryptography-48.0.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 3 + .../licenses/LICENSE.APACHE | 202 + .../licenses/LICENSE.BSD | 27 + .../sboms/cryptography-rust.cyclonedx.json | 1362 ++ .../sboms/sbom.json | 43 + .../site-packages/cryptography/__about__.py | 17 + .../site-packages/cryptography/__init__.py | 13 + .../site-packages/cryptography/exceptions.py | 52 + .../site-packages/cryptography/fernet.py | 224 + .../cryptography/hazmat/__init__.py | 13 + .../site-packages/cryptography/hazmat/_oid.py | 356 + .../cryptography/hazmat/asn1/__init__.py | 43 + .../cryptography/hazmat/asn1/asn1.py | 419 + .../cryptography/hazmat/backends/__init__.py | 13 + .../hazmat/backends/openssl/__init__.py | 9 + .../hazmat/backends/openssl/backend.py | 312 + .../cryptography/hazmat/bindings/__init__.py | 3 + .../hazmat/bindings/_rust.abi3.so | Bin 0 -> 13844864 bytes .../hazmat/bindings/_rust/__init__.pyi | 67 + .../hazmat/bindings/_rust/_openssl.pyi | 8 + .../hazmat/bindings/_rust/asn1.pyi | 7 + .../bindings/_rust/declarative_asn1.pyi | 127 + .../hazmat/bindings/_rust/exceptions.pyi | 17 + .../hazmat/bindings/_rust/ocsp.pyi | 117 + .../bindings/_rust/openssl/__init__.pyi | 80 + .../hazmat/bindings/_rust/openssl/aead.pyi | 189 + .../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 | 13 + .../hazmat/bindings/_rust/openssl/ed448.pyi | 13 + .../hazmat/bindings/_rust/openssl/hashes.pyi | 30 + .../hazmat/bindings/_rust/openssl/hmac.pyi | 22 + .../hazmat/bindings/_rust/openssl/hpke.pyi | 108 + .../hazmat/bindings/_rust/openssl/kdf.pyi | 205 + .../hazmat/bindings/_rust/openssl/keys.pyi | 34 + .../hazmat/bindings/_rust/openssl/mldsa.pyi | 23 + .../hazmat/bindings/_rust/openssl/mlkem.pyi | 18 + .../bindings/_rust/openssl/poly1305.pyi | 15 + .../hazmat/bindings/_rust/openssl/rsa.pyi | 55 + .../hazmat/bindings/_rust/openssl/x25519.pyi | 13 + .../hazmat/bindings/_rust/openssl/x448.pyi | 13 + .../hazmat/bindings/_rust/pkcs12.pyi | 52 + .../hazmat/bindings/_rust/pkcs7.pyi | 50 + .../hazmat/bindings/_rust/test_support.pyi | 23 + .../hazmat/bindings/_rust/x509.pyi | 312 + .../hazmat/bindings/openssl/__init__.py | 3 + .../hazmat/bindings/openssl/_conditional.py | 199 + .../hazmat/bindings/openssl/binding.py | 122 + .../cryptography/hazmat/decrepit/__init__.py | 5 + .../hazmat/decrepit/ciphers/__init__.py | 5 + .../hazmat/decrepit/ciphers/algorithms.py | 142 + .../hazmat/decrepit/ciphers/modes.py | 53 + .../hazmat/primitives/__init__.py | 3 + .../hazmat/primitives/_asymmetric.py | 19 + .../hazmat/primitives/_cipheralgorithm.py | 60 + .../cryptography/hazmat/primitives/_modes.py | 105 + .../hazmat/primitives/_serialization.py | 136 + .../hazmat/primitives/asymmetric/__init__.py | 3 + .../hazmat/primitives/asymmetric/dh.py | 159 + .../hazmat/primitives/asymmetric/dsa.py | 179 + .../hazmat/primitives/asymmetric/ec.py | 369 + .../hazmat/primitives/asymmetric/ed25519.py | 116 + .../hazmat/primitives/asymmetric/ed448.py | 143 + .../hazmat/primitives/asymmetric/mldsa.py | 441 + .../hazmat/primitives/asymmetric/mlkem.py | 278 + .../hazmat/primitives/asymmetric/padding.py | 111 + .../hazmat/primitives/asymmetric/rsa.py | 295 + .../hazmat/primitives/asymmetric/types.py | 123 + .../hazmat/primitives/asymmetric/utils.py | 28 + .../hazmat/primitives/asymmetric/x25519.py | 134 + .../hazmat/primitives/asymmetric/x448.py | 137 + .../hazmat/primitives/ciphers/__init__.py | 27 + .../hazmat/primitives/ciphers/aead.py | 23 + .../hazmat/primitives/ciphers/algorithms.py | 138 + .../hazmat/primitives/ciphers/base.py | 146 + .../hazmat/primitives/ciphers/modes.py | 192 + .../cryptography/hazmat/primitives/cmac.py | 10 + .../hazmat/primitives/constant_time.py | 14 + .../cryptography/hazmat/primitives/hashes.py | 246 + .../cryptography/hazmat/primitives/hmac.py | 13 + .../cryptography/hazmat/primitives/hpke.py | 27 + .../hazmat/primitives/kdf/__init__.py | 32 + .../hazmat/primitives/kdf/argon2.py | 17 + .../hazmat/primitives/kdf/concatkdf.py | 16 + .../hazmat/primitives/kdf/hkdf.py | 16 + .../hazmat/primitives/kdf/kbkdf.py | 26 + .../hazmat/primitives/kdf/pbkdf2.py | 13 + .../hazmat/primitives/kdf/scrypt.py | 19 + .../hazmat/primitives/kdf/x963kdf.py | 13 + .../cryptography/hazmat/primitives/keywrap.py | 180 + .../cryptography/hazmat/primitives/padding.py | 69 + .../hazmat/primitives/poly1305.py | 11 + .../primitives/serialization/__init__.py | 65 + .../hazmat/primitives/serialization/base.py | 14 + .../hazmat/primitives/serialization/pkcs12.py | 176 + .../hazmat/primitives/serialization/pkcs7.py | 412 + .../hazmat/primitives/serialization/ssh.py | 1621 ++ .../hazmat/primitives/twofactor/__init__.py | 9 + .../hazmat/primitives/twofactor/hotp.py | 101 + .../hazmat/primitives/twofactor/totp.py | 56 + .../site-packages/cryptography/py.typed | 0 .../site-packages/cryptography/utils.py | 135 + .../cryptography/x509/__init__.py | 271 + .../site-packages/cryptography/x509/base.py | 773 + .../x509/certificate_transparency.py | 35 + .../cryptography/x509/extensions.py | 2533 +++ .../cryptography/x509/general_name.py | 281 + .../site-packages/cryptography/x509/name.py | 482 + .../site-packages/cryptography/x509/ocsp.py | 379 + .../site-packages/cryptography/x509/oid.py | 37 + .../cryptography/x509/verification.py | 34 + .../site-packages/distutils-precedence.pth | 1 + .../h11-0.16.0.dist-info/INSTALLER | 1 + .../h11-0.16.0.dist-info/METADATA | 202 + .../site-packages/h11-0.16.0.dist-info/RECORD | 29 + .../site-packages/h11-0.16.0.dist-info/WHEEL | 5 + .../h11-0.16.0.dist-info/licenses/LICENSE.txt | 22 + .../h11-0.16.0.dist-info/top_level.txt | 1 + .../python3.12/site-packages/h11/__init__.py | 62 + .../lib/python3.12/site-packages/h11/_abnf.py | 132 + .../site-packages/h11/_connection.py | 659 + .../python3.12/site-packages/h11/_events.py | 369 + .../python3.12/site-packages/h11/_headers.py | 282 + .../python3.12/site-packages/h11/_readers.py | 250 + .../site-packages/h11/_receivebuffer.py | 153 + .../python3.12/site-packages/h11/_state.py | 365 + .../lib/python3.12/site-packages/h11/_util.py | 135 + .../python3.12/site-packages/h11/_version.py | 16 + .../python3.12/site-packages/h11/_writers.py | 145 + .../lib/python3.12/site-packages/h11/py.typed | 1 + .../html5tagger-1.3.0.dist-info/INSTALLER | 1 + .../html5tagger-1.3.0.dist-info/LICENSE | 22 + .../html5tagger-1.3.0.dist-info/METADATA | 193 + .../html5tagger-1.3.0.dist-info/RECORD | 19 + .../html5tagger-1.3.0.dist-info/REQUESTED | 0 .../html5tagger-1.3.0.dist-info/WHEEL | 5 + .../html5tagger-1.3.0.dist-info/top_level.txt | 1 + .../site-packages/html5tagger/__init__.py | 10 + .../site-packages/html5tagger/builder.py | 189 + .../site-packages/html5tagger/document.py | 51 + .../site-packages/html5tagger/html5.py | 11 + .../site-packages/html5tagger/makebuilder.py | 14 + .../site-packages/html5tagger/util.py | 43 + .../httpcore-1.0.9.dist-info/INSTALLER | 1 + .../httpcore-1.0.9.dist-info/METADATA | 625 + .../httpcore-1.0.9.dist-info/RECORD | 68 + .../httpcore-1.0.9.dist-info/WHEEL | 4 + .../licenses/LICENSE.md | 27 + .../site-packages/httpcore/__init__.py | 141 + .../python3.12/site-packages/httpcore/_api.py | 94 + .../site-packages/httpcore/_async/__init__.py | 39 + .../httpcore/_async/connection.py | 222 + .../httpcore/_async/connection_pool.py | 420 + .../site-packages/httpcore/_async/http11.py | 379 + .../site-packages/httpcore/_async/http2.py | 592 + .../httpcore/_async/http_proxy.py | 367 + .../httpcore/_async/interfaces.py | 137 + .../httpcore/_async/socks_proxy.py | 341 + .../httpcore/_backends/__init__.py | 0 .../site-packages/httpcore/_backends/anyio.py | 146 + .../site-packages/httpcore/_backends/auto.py | 52 + .../site-packages/httpcore/_backends/base.py | 101 + .../site-packages/httpcore/_backends/mock.py | 143 + .../site-packages/httpcore/_backends/sync.py | 241 + .../site-packages/httpcore/_backends/trio.py | 159 + .../site-packages/httpcore/_exceptions.py | 81 + .../site-packages/httpcore/_models.py | 516 + .../python3.12/site-packages/httpcore/_ssl.py | 9 + .../site-packages/httpcore/_sync/__init__.py | 39 + .../httpcore/_sync/connection.py | 222 + .../httpcore/_sync/connection_pool.py | 420 + .../site-packages/httpcore/_sync/http11.py | 379 + .../site-packages/httpcore/_sync/http2.py | 592 + .../httpcore/_sync/http_proxy.py | 367 + .../httpcore/_sync/interfaces.py | 137 + .../httpcore/_sync/socks_proxy.py | 341 + .../httpcore/_synchronization.py | 318 + .../site-packages/httpcore/_trace.py | 107 + .../site-packages/httpcore/_utils.py | 37 + .../site-packages/httpcore/py.typed | 0 .../httptools-0.8.0.dist-info/INSTALLER | 1 + .../httptools-0.8.0.dist-info/METADATA | 131 + .../httptools-0.8.0.dist-info/RECORD | 28 + .../httptools-0.8.0.dist-info/WHEEL | 7 + .../licenses/LICENSE | 21 + .../licenses/vendor/http-parser/LICENSE-MIT | 19 + .../licenses/vendor/llhttp/LICENSE | 22 + .../httptools-0.8.0.dist-info/top_level.txt | 1 + .../site-packages/httptools/__init__.py | 35 + .../site-packages/httptools/_version.py | 13 + .../httptools/parser/__init__.py | 28 + .../httptools/parser/cparser.pxd | 167 + .../site-packages/httptools/parser/errors.py | 30 + .../parser.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 1262976 bytes .../site-packages/httptools/parser/parser.pyi | 58 + .../site-packages/httptools/parser/parser.pyx | 436 + .../httptools/parser/protocol.py | 15 + .../site-packages/httptools/parser/python.pxd | 6 + .../httptools/parser/url_cparser.pxd | 31 + ...url_parser.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 496168 bytes .../httptools/parser/url_parser.pyi | 13 + .../httptools/parser/url_parser.pyx | 118 + .../site-packages/httptools/py.typed | 0 .../httpx-0.28.1.dist-info/INSTALLER | 1 + .../httpx-0.28.1.dist-info/METADATA | 203 + .../httpx-0.28.1.dist-info/RECORD | 54 + .../httpx-0.28.1.dist-info/WHEEL | 4 + .../httpx-0.28.1.dist-info/entry_points.txt | 2 + .../licenses/LICENSE.md | 12 + .../site-packages/httpx/__init__.py | 105 + .../site-packages/httpx/__version__.py | 3 + .../python3.12/site-packages/httpx/_api.py | 438 + .../python3.12/site-packages/httpx/_auth.py | 348 + .../python3.12/site-packages/httpx/_client.py | 2019 ++ .../python3.12/site-packages/httpx/_config.py | 248 + .../site-packages/httpx/_content.py | 240 + .../site-packages/httpx/_decoders.py | 393 + .../site-packages/httpx/_exceptions.py | 379 + .../python3.12/site-packages/httpx/_main.py | 506 + .../python3.12/site-packages/httpx/_models.py | 1277 ++ .../site-packages/httpx/_multipart.py | 300 + .../site-packages/httpx/_status_codes.py | 162 + .../httpx/_transports/__init__.py | 15 + .../site-packages/httpx/_transports/asgi.py | 187 + .../site-packages/httpx/_transports/base.py | 86 + .../httpx/_transports/default.py | 406 + .../site-packages/httpx/_transports/mock.py | 43 + .../site-packages/httpx/_transports/wsgi.py | 149 + .../python3.12/site-packages/httpx/_types.py | 114 + .../site-packages/httpx/_urlparse.py | 527 + .../python3.12/site-packages/httpx/_urls.py | 641 + .../python3.12/site-packages/httpx/_utils.py | 242 + .../python3.12/site-packages/httpx/py.typed | 0 .../idna-3.17.dist-info/INSTALLER | 1 + .../idna-3.17.dist-info/METADATA | 164 + .../site-packages/idna-3.17.dist-info/RECORD | 28 + .../site-packages/idna-3.17.dist-info/WHEEL | 4 + .../idna-3.17.dist-info/entry_points.txt | 3 + .../idna-3.17.dist-info/licenses/LICENSE.md | 31 + .../python3.12/site-packages/idna/__init__.py | 45 + .../python3.12/site-packages/idna/__main__.py | 6 + .../lib/python3.12/site-packages/idna/cli.py | 128 + .../python3.12/site-packages/idna/codec.py | 159 + .../python3.12/site-packages/idna/compat.py | 41 + .../lib/python3.12/site-packages/idna/core.py | 634 + .../python3.12/site-packages/idna/idnadata.py | 1897 ++ .../site-packages/idna/intranges.py | 55 + .../site-packages/idna/package_data.py | 1 + .../python3.12/site-packages/idna/py.typed | 0 .../site-packages/idna/uts46data.py | 16896 ++++++++++++++++ .../iniconfig-2.3.0.dist-info/INSTALLER | 1 + .../iniconfig-2.3.0.dist-info/METADATA | 79 + .../iniconfig-2.3.0.dist-info/RECORD | 15 + .../iniconfig-2.3.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 21 + .../iniconfig-2.3.0.dist-info/top_level.txt | 1 + .../site-packages/iniconfig/__init__.py | 249 + .../site-packages/iniconfig/_parse.py | 163 + .../site-packages/iniconfig/_version.py | 34 + .../site-packages/iniconfig/exceptions.py | 16 + .../site-packages/iniconfig/py.typed | 0 .../site-packages/markdown_it/__init__.py | 6 + .../site-packages/markdown_it/_compat.py | 1 + .../site-packages/markdown_it/_punycode.py | 67 + .../site-packages/markdown_it/cli/__init__.py | 0 .../site-packages/markdown_it/cli/parse.py | 127 + .../markdown_it/common/__init__.py | 0 .../markdown_it/common/entities.py | 5 + .../markdown_it/common/html_blocks.py | 69 + .../markdown_it/common/html_re.py | 39 + .../markdown_it/common/normalize_url.py | 81 + .../site-packages/markdown_it/common/utils.py | 313 + .../markdown_it/helpers/__init__.py | 6 + .../helpers/parse_link_destination.py | 83 + .../markdown_it/helpers/parse_link_label.py | 44 + .../markdown_it/helpers/parse_link_title.py | 75 + .../site-packages/markdown_it/main.py | 351 + .../site-packages/markdown_it/parser_block.py | 113 + .../site-packages/markdown_it/parser_core.py | 46 + .../markdown_it/parser_inline.py | 215 + .../site-packages/markdown_it/port.yaml | 48 + .../markdown_it/presets/__init__.py | 48 + .../markdown_it/presets/commonmark.py | 75 + .../markdown_it/presets/default.py | 36 + .../site-packages/markdown_it/presets/zero.py | 44 + .../site-packages/markdown_it/py.typed | 1 + .../site-packages/markdown_it/renderer.py | 356 + .../site-packages/markdown_it/ruler.py | 275 + .../markdown_it/rules_block/__init__.py | 28 + .../markdown_it/rules_block/blockquote.py | 368 + .../markdown_it/rules_block/code.py | 36 + .../markdown_it/rules_block/fence.py | 146 + .../markdown_it/rules_block/heading.py | 69 + .../markdown_it/rules_block/hr.py | 56 + .../markdown_it/rules_block/html_block.py | 90 + .../markdown_it/rules_block/lheading.py | 86 + .../markdown_it/rules_block/list.py | 408 + .../markdown_it/rules_block/paragraph.py | 66 + .../markdown_it/rules_block/reference.py | 235 + .../markdown_it/rules_block/state_block.py | 261 + .../markdown_it/rules_block/table.py | 250 + .../markdown_it/rules_core/__init__.py | 19 + .../markdown_it/rules_core/block.py | 13 + .../markdown_it/rules_core/inline.py | 10 + .../markdown_it/rules_core/linkify.py | 149 + .../markdown_it/rules_core/normalize.py | 19 + .../markdown_it/rules_core/replacements.py | 127 + .../markdown_it/rules_core/smartquotes.py | 202 + .../markdown_it/rules_core/state_core.py | 25 + .../markdown_it/rules_core/text_join.py | 53 + .../markdown_it/rules_inline/__init__.py | 31 + .../markdown_it/rules_inline/autolink.py | 77 + .../markdown_it/rules_inline/backticks.py | 72 + .../markdown_it/rules_inline/balance_pairs.py | 138 + .../markdown_it/rules_inline/emphasis.py | 102 + .../markdown_it/rules_inline/entity.py | 53 + .../markdown_it/rules_inline/escape.py | 93 + .../rules_inline/fragments_join.py | 54 + .../markdown_it/rules_inline/html_inline.py | 43 + .../markdown_it/rules_inline/image.py | 148 + .../markdown_it/rules_inline/link.py | 149 + .../markdown_it/rules_inline/linkify.py | 62 + .../markdown_it/rules_inline/newline.py | 44 + .../markdown_it/rules_inline/state_inline.py | 167 + .../markdown_it/rules_inline/strikethrough.py | 173 + .../markdown_it/rules_inline/text.py | 23 + .../site-packages/markdown_it/token.py | 178 + .../site-packages/markdown_it/tree.py | 333 + .../site-packages/markdown_it/utils.py | 194 + .../markdown_it_py-4.2.0.dist-info/INSTALLER | 1 + .../markdown_it_py-4.2.0.dist-info/METADATA | 221 + .../markdown_it_py-4.2.0.dist-info/RECORD | 142 + .../markdown_it_py-4.2.0.dist-info/WHEEL | 4 + .../entry_points.txt | 3 + .../licenses/LICENSE | 21 + .../licenses/LICENSE.markdown-it | 22 + .../mdurl-0.1.2.dist-info/INSTALLER | 1 + .../mdurl-0.1.2.dist-info/LICENSE | 46 + .../mdurl-0.1.2.dist-info/METADATA | 32 + .../mdurl-0.1.2.dist-info/RECORD | 18 + .../site-packages/mdurl-0.1.2.dist-info/WHEEL | 4 + .../site-packages/mdurl/__init__.py | 18 + .../python3.12/site-packages/mdurl/_decode.py | 104 + .../python3.12/site-packages/mdurl/_encode.py | 85 + .../python3.12/site-packages/mdurl/_format.py | 27 + .../python3.12/site-packages/mdurl/_parse.py | 304 + .../python3.12/site-packages/mdurl/_url.py | 14 + .../python3.12/site-packages/mdurl/py.typed | 1 + .../multidict-6.7.1.dist-info/INSTALLER | 1 + .../multidict-6.7.1.dist-info/METADATA | 149 + .../multidict-6.7.1.dist-info/RECORD | 16 + .../multidict-6.7.1.dist-info/WHEEL | 7 + .../licenses/LICENSE | 13 + .../multidict-6.7.1.dist-info/top_level.txt | 1 + .../site-packages/multidict/__init__.py | 60 + .../site-packages/multidict/_abc.py | 73 + .../site-packages/multidict/_compat.py | 15 + ..._multidict.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 847096 bytes .../site-packages/multidict/_multidict_py.py | 1242 ++ .../site-packages/multidict/py.typed | 1 + .../packaging-26.2.dist-info/INSTALLER | 1 + .../packaging-26.2.dist-info/METADATA | 112 + .../packaging-26.2.dist-info/RECORD | 48 + .../packaging-26.2.dist-info/WHEEL | 4 + .../packaging-26.2.dist-info/licenses/LICENSE | 3 + .../licenses/LICENSE.APACHE | 177 + .../licenses/LICENSE.BSD | 23 + .../site-packages/packaging/__init__.py | 15 + .../site-packages/packaging/_elffile.py | 108 + .../site-packages/packaging/_manylinux.py | 262 + .../site-packages/packaging/_musllinux.py | 85 + .../site-packages/packaging/_parser.py | 393 + .../site-packages/packaging/_structures.py | 33 + .../site-packages/packaging/_tokenizer.py | 193 + .../packaging/dependency_groups.py | 302 + .../site-packages/packaging/direct_url.py | 325 + .../site-packages/packaging/errors.py | 94 + .../packaging/licenses/__init__.py | 186 + .../site-packages/packaging/licenses/_spdx.py | 799 + .../site-packages/packaging/markers.py | 492 + .../site-packages/packaging/metadata.py | 964 + .../site-packages/packaging/py.typed | 0 .../site-packages/packaging/pylock.py | 905 + .../site-packages/packaging/requirements.py | 129 + .../site-packages/packaging/specifiers.py | 1943 ++ .../site-packages/packaging/tags.py | 932 + .../site-packages/packaging/utils.py | 296 + .../site-packages/packaging/version.py | 1231 ++ .../pip-26.1.1.dist-info/INSTALLER | 1 + .../pip-26.1.1.dist-info/METADATA | 109 + .../site-packages/pip-26.1.1.dist-info/RECORD | 865 + .../pip-26.1.1.dist-info/REQUESTED | 0 .../site-packages/pip-26.1.1.dist-info/WHEEL | 4 + .../pip-26.1.1.dist-info/entry_points.txt | 4 + .../pip-26.1.1.dist-info/licenses/AUTHORS.txt | 867 + .../pip-26.1.1.dist-info/licenses/LICENSE.txt | 20 + .../src/pip/_vendor/cachecontrol/LICENSE.txt | 13 + .../licenses/src/pip/_vendor/certifi/LICENSE | 20 + .../src/pip/_vendor/distlib/LICENSE.txt | 284 + .../licenses/src/pip/_vendor/distro/LICENSE | 202 + .../licenses/src/pip/_vendor/idna/LICENSE.md | 31 + .../licenses/src/pip/_vendor/msgpack/COPYING | 14 + .../src/pip/_vendor/packaging/LICENSE | 3 + .../src/pip/_vendor/packaging/LICENSE.APACHE | 177 + .../src/pip/_vendor/packaging/LICENSE.BSD | 23 + .../src/pip/_vendor/pkg_resources/LICENSE | 17 + .../src/pip/_vendor/platformdirs/LICENSE | 21 + .../licenses/src/pip/_vendor/pygments/LICENSE | 25 + .../src/pip/_vendor/pyproject_hooks/LICENSE | 21 + .../licenses/src/pip/_vendor/requests/LICENSE | 175 + .../src/pip/_vendor/resolvelib/LICENSE | 13 + .../licenses/src/pip/_vendor/rich/LICENSE | 19 + .../licenses/src/pip/_vendor/tomli/LICENSE | 21 + .../licenses/src/pip/_vendor/tomli_w/LICENSE | 21 + .../src/pip/_vendor/truststore/LICENSE | 21 + .../src/pip/_vendor/urllib3/LICENSE.txt | 21 + .../python3.12/site-packages/pip/__init__.py | 13 + .../python3.12/site-packages/pip/__main__.py | 24 + .../site-packages/pip/__pip-runner__.py | 50 + .../site-packages/pip/_internal/__init__.py | 18 + .../site-packages/pip/_internal/build_env.py | 606 + .../site-packages/pip/_internal/cache.py | 291 + .../pip/_internal/cli/__init__.py | 3 + .../pip/_internal/cli/autocompletion.py | 184 + .../pip/_internal/cli/base_command.py | 264 + .../pip/_internal/cli/cmdoptions.py | 1298 ++ .../pip/_internal/cli/command_context.py | 28 + .../pip/_internal/cli/index_command.py | 212 + .../site-packages/pip/_internal/cli/main.py | 85 + .../pip/_internal/cli/main_parser.py | 136 + .../site-packages/pip/_internal/cli/parser.py | 358 + .../pip/_internal/cli/progress_bars.py | 153 + .../pip/_internal/cli/req_command.py | 472 + .../pip/_internal/cli/spinners.py | 235 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 139 + .../pip/_internal/commands/cache.py | 255 + .../pip/_internal/commands/check.py | 66 + .../pip/_internal/commands/completion.py | 136 + .../pip/_internal/commands/configuration.py | 288 + .../pip/_internal/commands/debug.py | 196 + .../pip/_internal/commands/download.py | 146 + .../pip/_internal/commands/freeze.py | 107 + .../pip/_internal/commands/hash.py | 58 + .../pip/_internal/commands/help.py | 40 + .../pip/_internal/commands/index.py | 166 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 904 + .../pip/_internal/commands/list.py | 403 + .../pip/_internal/commands/lock.py | 175 + .../pip/_internal/commands/search.py | 178 + .../pip/_internal/commands/show.py | 231 + .../pip/_internal/commands/uninstall.py | 113 + .../pip/_internal/commands/wheel.py | 171 + .../pip/_internal/configuration.py | 396 + .../pip/_internal/distributions/__init__.py | 21 + .../pip/_internal/distributions/base.py | 55 + .../pip/_internal/distributions/installed.py | 33 + .../pip/_internal/distributions/sdist.py | 164 + .../pip/_internal/distributions/wheel.py | 44 + .../site-packages/pip/_internal/exceptions.py | 971 + .../pip/_internal/index/__init__.py | 1 + .../pip/_internal/index/collector.py | 488 + .../pip/_internal/index/package_finder.py | 1113 + .../pip/_internal/index/sources.py | 287 + .../pip/_internal/locations/__init__.py | 438 + .../pip/_internal/locations/_distutils.py | 173 + .../pip/_internal/locations/_sysconfig.py | 218 + .../pip/_internal/locations/base.py | 82 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 169 + .../pip/_internal/metadata/_json.py | 87 + .../pip/_internal/metadata/base.py | 685 + .../_internal/metadata/importlib/__init__.py | 6 + .../_internal/metadata/importlib/_compat.py | 87 + .../_internal/metadata/importlib/_dists.py | 235 + .../pip/_internal/metadata/importlib/_envs.py | 143 + .../pip/_internal/metadata/pkg_resources.py | 298 + .../pip/_internal/models/__init__.py | 1 + .../pip/_internal/models/candidate.py | 23 + .../pip/_internal/models/direct_url.py | 42 + .../pip/_internal/models/format_control.py | 78 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 57 + .../pip/_internal/models/link.py | 617 + .../pip/_internal/models/release_control.py | 91 + .../pip/_internal/models/scheme.py | 23 + .../pip/_internal/models/search_scope.py | 124 + .../pip/_internal/models/selection_prefs.py | 36 + .../pip/_internal/models/target_python.py | 122 + .../pip/_internal/models/wheel.py | 80 + .../pip/_internal/network/__init__.py | 1 + .../pip/_internal/network/auth.py | 570 + .../pip/_internal/network/cache.py | 128 + .../pip/_internal/network/download.py | 340 + .../pip/_internal/network/lazy_wheel.py | 215 + .../pip/_internal/network/session.py | 532 + .../pip/_internal/network/utils.py | 98 + .../pip/_internal/network/xmlrpc.py | 61 + .../pip/_internal/operations/__init__.py | 0 .../pip/_internal/operations/check.py | 175 + .../pip/_internal/operations/freeze.py | 259 + .../_internal/operations/install/__init__.py | 1 + .../pip/_internal/operations/install/wheel.py | 745 + .../pip/_internal/operations/prepare.py | 751 + .../site-packages/pip/_internal/pyproject.py | 123 + .../pip/_internal/req/__init__.py | 103 + .../pip/_internal/req/constructors.py | 677 + .../site-packages/pip/_internal/req/pep723.py | 41 + .../pip/_internal/req/req_dependency_group.py | 86 + .../pip/_internal/req/req_file.py | 622 + .../pip/_internal/req/req_install.py | 838 + .../pip/_internal/req/req_set.py | 81 + .../pip/_internal/req/req_uninstall.py | 639 + .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 598 + .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 164 + .../resolution/resolvelib/candidates.py | 599 + .../resolution/resolvelib/factory.py | 914 + .../resolution/resolvelib/found_candidates.py | 166 + .../resolution/resolvelib/provider.py | 306 + .../resolution/resolvelib/reporter.py | 98 + .../resolution/resolvelib/requirements.py | 251 + .../resolution/resolvelib/resolver.py | 332 + .../pip/_internal/self_outdated_check.py | 246 + .../pip/_internal/utils/__init__.py | 0 .../pip/_internal/utils/_jaraco_text.py | 109 + .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 85 + .../pip/_internal/utils/compatibility_tags.py | 201 + .../pip/_internal/utils/datetime.py | 28 + .../pip/_internal/utils/deprecation.py | 139 + .../pip/_internal/utils/direct_url_helpers.py | 92 + .../pip/_internal/utils/egg_link.py | 81 + .../pip/_internal/utils/entrypoints.py | 88 + .../pip/_internal/utils/filesystem.py | 201 + .../pip/_internal/utils/filetypes.py | 24 + .../pip/_internal/utils/glibc.py | 102 + .../pip/_internal/utils/hashes.py | 150 + .../pip/_internal/utils/logging.py | 396 + .../site-packages/pip/_internal/utils/misc.py | 771 + .../pip/_internal/utils/packaging.py | 44 + .../pip/_internal/utils/pylock.py | 283 + .../pip/_internal/utils/retry.py | 45 + .../pip/_internal/utils/subprocess.py | 248 + .../pip/_internal/utils/temp_dir.py | 294 + .../pip/_internal/utils/unpacking.py | 381 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 105 + .../pip/_internal/utils/wheel.py | 132 + .../pip/_internal/vcs/__init__.py | 15 + .../site-packages/pip/_internal/vcs/bazaar.py | 130 + .../site-packages/pip/_internal/vcs/git.py | 571 + .../pip/_internal/vcs/mercurial.py | 186 + .../pip/_internal/vcs/subversion.py | 335 + .../pip/_internal/vcs/versioncontrol.py | 695 + .../pip/_internal/wheel_builder.py | 261 + .../site-packages/pip/_vendor/README.rst | 178 + .../site-packages/pip/_vendor/__init__.py | 117 + .../pip/_vendor/cachecontrol/LICENSE.txt | 13 + .../pip/_vendor/cachecontrol/__init__.py | 32 + .../pip/_vendor/cachecontrol/_cmd.py | 70 + .../pip/_vendor/cachecontrol/adapter.py | 167 + .../pip/_vendor/cachecontrol/cache.py | 75 + .../_vendor/cachecontrol/caches/__init__.py | 8 + .../_vendor/cachecontrol/caches/file_cache.py | 145 + .../cachecontrol/caches/redis_cache.py | 48 + .../pip/_vendor/cachecontrol/controller.py | 511 + .../pip/_vendor/cachecontrol/filewrapper.py | 121 + .../pip/_vendor/cachecontrol/heuristics.py | 157 + .../pip/_vendor/cachecontrol/py.typed | 0 .../pip/_vendor/cachecontrol/serialize.py | 146 + .../pip/_vendor/cachecontrol/wrapper.py | 43 + .../site-packages/pip/_vendor/certifi/LICENSE | 20 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../site-packages/pip/_vendor/certifi/core.py | 83 + .../pip/_vendor/certifi/py.typed | 0 .../pip/_vendor/distlib/LICENSE.txt | 284 + .../pip/_vendor/distlib/__init__.py | 33 + .../pip/_vendor/distlib/compat.py | 1137 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 447 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 97792 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 0 -> 182784 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 108032 bytes .../site-packages/pip/_vendor/distlib/util.py | 1984 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 91648 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 0 -> 168448 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 101888 bytes .../site-packages/pip/_vendor/distro/LICENSE | 202 + .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../pip/_vendor/distro/distro.py | 1403 ++ .../site-packages/pip/_vendor/distro/py.typed | 0 .../site-packages/pip/_vendor/idna/LICENSE.md | 31 + .../pip/_vendor/idna/__init__.py | 45 + .../site-packages/pip/_vendor/idna/codec.py | 122 + .../site-packages/pip/_vendor/idna/compat.py | 15 + .../site-packages/pip/_vendor/idna/core.py | 437 + .../pip/_vendor/idna/idnadata.py | 4309 ++++ .../pip/_vendor/idna/intranges.py | 57 + .../pip/_vendor/idna/package_data.py | 1 + .../site-packages/pip/_vendor/idna/py.typed | 0 .../pip/_vendor/idna/uts46data.py | 8841 ++++++++ .../site-packages/pip/_vendor/msgpack/COPYING | 14 + .../pip/_vendor/msgpack/__init__.py | 55 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 170 + .../pip/_vendor/msgpack/fallback.py | 929 + .../pip/_vendor/packaging/LICENSE | 3 + .../pip/_vendor/packaging/LICENSE.APACHE | 177 + .../pip/_vendor/packaging/LICENSE.BSD | 23 + .../pip/_vendor/packaging/__init__.py | 15 + .../pip/_vendor/packaging/_elffile.py | 108 + .../pip/_vendor/packaging/_manylinux.py | 262 + .../pip/_vendor/packaging/_musllinux.py | 85 + .../pip/_vendor/packaging/_parser.py | 393 + .../pip/_vendor/packaging/_structures.py | 33 + .../pip/_vendor/packaging/_tokenizer.py | 193 + .../_vendor/packaging/dependency_groups.py | 302 + .../pip/_vendor/packaging/direct_url.py | 325 + .../pip/_vendor/packaging/errors.py | 94 + .../_vendor/packaging/licenses/__init__.py | 186 + .../pip/_vendor/packaging/licenses/_spdx.py | 799 + .../pip/_vendor/packaging/markers.py | 492 + .../pip/_vendor/packaging/metadata.py | 964 + .../pip/_vendor/packaging/py.typed | 0 .../pip/_vendor/packaging/pylock.py | 905 + .../pip/_vendor/packaging/requirements.py | 129 + .../pip/_vendor/packaging/specifiers.py | 1943 ++ .../pip/_vendor/packaging/tags.py | 932 + .../pip/_vendor/packaging/utils.py | 296 + .../pip/_vendor/packaging/version.py | 1231 ++ .../pip/_vendor/pkg_resources/LICENSE | 17 + .../pip/_vendor/pkg_resources/__init__.py | 3676 ++++ .../pip/_vendor/platformdirs/LICENSE | 21 + .../pip/_vendor/platformdirs/__init__.py | 631 + .../pip/_vendor/platformdirs/__main__.py | 55 + .../pip/_vendor/platformdirs/android.py | 249 + .../pip/_vendor/platformdirs/api.py | 299 + .../pip/_vendor/platformdirs/macos.py | 146 + .../pip/_vendor/platformdirs/py.typed | 0 .../pip/_vendor/platformdirs/unix.py | 272 + .../pip/_vendor/platformdirs/version.py | 34 + .../pip/_vendor/platformdirs/windows.py | 278 + .../pip/_vendor/pygments/LICENSE | 25 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 70 + .../pip/_vendor/pygments/filters/__init__.py | 940 + .../pip/_vendor/pygments/formatter.py | 129 + .../_vendor/pygments/formatters/__init__.py | 157 + .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/lexer.py | 963 + .../pip/_vendor/pygments/lexers/__init__.py | 362 + .../pip/_vendor/pygments/lexers/_mapping.py | 602 + .../pip/_vendor/pygments/lexers/python.py | 1201 ++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 72 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 247 + .../pip/_vendor/pygments/style.py | 203 + .../pip/_vendor/pygments/styles/__init__.py | 61 + .../pip/_vendor/pygments/styles/_mapping.py | 54 + .../pip/_vendor/pygments/token.py | 214 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 324 + .../pip/_vendor/pyproject_hooks/LICENSE | 21 + .../pip/_vendor/pyproject_hooks/__init__.py | 31 + .../pip/_vendor/pyproject_hooks/_impl.py | 410 + .../pyproject_hooks/_in_process/__init__.py | 21 + .../_in_process/_in_process.py | 389 + .../pip/_vendor/pyproject_hooks/py.typed | 0 .../pip/_vendor/requests/LICENSE | 175 + .../pip/_vendor/requests/__init__.py | 178 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 51 + .../pip/_vendor/requests/adapters.py | 697 + .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 314 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 90 + .../pip/_vendor/requests/cookies.py | 561 + .../pip/_vendor/requests/exceptions.py | 152 + .../pip/_vendor/requests/help.py | 124 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 1041 + .../pip/_vendor/requests/packages.py | 25 + .../pip/_vendor/requests/sessions.py | 834 + .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1083 + .../pip/_vendor/resolvelib/LICENSE | 13 + .../pip/_vendor/resolvelib/__init__.py | 27 + .../pip/_vendor/resolvelib/providers.py | 196 + .../pip/_vendor/resolvelib/py.typed | 0 .../pip/_vendor/resolvelib/reporters.py | 55 + .../_vendor/resolvelib/resolvers/__init__.py | 27 + .../_vendor/resolvelib/resolvers/abstract.py | 47 + .../_vendor/resolvelib/resolvers/criterion.py | 48 + .../resolvelib/resolvers/exceptions.py | 57 + .../resolvelib/resolvers/resolution.py | 627 + .../pip/_vendor/resolvelib/structs.py | 209 + .../site-packages/pip/_vendor/rich/LICENSE | 19 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 245 + .../pip/_vendor/rich/_cell_widths.py | 454 + .../pip/_vendor/rich/_emoji_codes.py | 3610 ++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 76 + .../pip/_vendor/rich/_extension.py | 10 + .../site-packages/pip/_vendor/rich/_fileno.py | 24 + .../pip/_vendor/rich/_inspect.py | 268 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 69 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 153 + .../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 | 661 + .../pip/_vendor/rich/_windows.py | 71 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 93 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 306 + .../site-packages/pip/_vendor/rich/ansi.py | 241 + .../site-packages/pip/_vendor/rich/bar.py | 93 + .../site-packages/pip/_vendor/rich/box.py | 474 + .../site-packages/pip/_vendor/rich/cells.py | 174 + .../site-packages/pip/_vendor/rich/color.py | 621 + .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2680 +++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 219 + .../pip/_vendor/rich/default_styles.py | 193 + .../pip/_vendor/rich/diagnose.py | 39 + .../site-packages/pip/_vendor/rich/emoji.py | 91 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 57 + .../pip/_vendor/rich/filesize.py | 88 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 139 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 442 + .../site-packages/pip/_vendor/rich/live.py | 400 + .../pip/_vendor/rich/live_render.py | 106 + .../site-packages/pip/_vendor/rich/logging.py | 297 + .../site-packages/pip/_vendor/rich/markup.py | 251 + .../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 | 317 + .../site-packages/pip/_vendor/rich/pretty.py | 1016 + .../pip/_vendor/rich/progress.py | 1715 ++ .../pip/_vendor/rich/progress_bar.py | 223 + .../site-packages/pip/_vendor/rich/prompt.py | 400 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/py.typed | 0 .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 130 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 752 + .../site-packages/pip/_vendor/rich/spinner.py | 132 + .../site-packages/pip/_vendor/rich/status.py | 131 + .../site-packages/pip/_vendor/rich/style.py | 792 + .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 985 + .../site-packages/pip/_vendor/rich/table.py | 1006 + .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1361 ++ .../site-packages/pip/_vendor/rich/theme.py | 115 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 899 + .../site-packages/pip/_vendor/rich/tree.py | 257 + .../site-packages/pip/_vendor/tomli/LICENSE | 21 + .../pip/_vendor/tomli/__init__.py | 8 + .../pip/_vendor/tomli/_parser.py | 788 + .../site-packages/pip/_vendor/tomli/_re.py | 115 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../site-packages/pip/_vendor/tomli/py.typed | 1 + .../site-packages/pip/_vendor/tomli_w/LICENSE | 21 + .../pip/_vendor/tomli_w/__init__.py | 4 + .../pip/_vendor/tomli_w/_writer.py | 229 + .../pip/_vendor/tomli_w/py.typed | 1 + .../pip/_vendor/truststore/LICENSE | 21 + .../pip/_vendor/truststore/__init__.py | 36 + .../pip/_vendor/truststore/_api.py | 341 + .../pip/_vendor/truststore/_macos.py | 571 + .../pip/_vendor/truststore/_openssl.py | 68 + .../pip/_vendor/truststore/_ssl_constants.py | 31 + .../pip/_vendor/truststore/_windows.py | 567 + .../pip/_vendor/truststore/py.typed | 0 .../pip/_vendor/urllib3/LICENSE.txt | 21 + .../pip/_vendor/urllib3/__init__.py | 211 + .../pip/_vendor/urllib3/_base_connection.py | 165 + .../pip/_vendor/urllib3/_collections.py | 487 + .../pip/_vendor/urllib3/_request_methods.py | 278 + .../pip/_vendor/urllib3/_version.py | 34 + .../pip/_vendor/urllib3/connection.py | 1099 + .../pip/_vendor/urllib3/connectionpool.py | 1178 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/emscripten/__init__.py | 17 + .../urllib3/contrib/emscripten/connection.py | 260 + .../emscripten/emscripten_fetch_worker.js | 110 + .../urllib3/contrib/emscripten/fetch.py | 726 + .../urllib3/contrib/emscripten/request.py | 22 + .../urllib3/contrib/emscripten/response.py | 277 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 564 + .../pip/_vendor/urllib3/contrib/socks.py | 228 + .../pip/_vendor/urllib3/exceptions.py | 335 + .../pip/_vendor/urllib3/fields.py | 341 + .../pip/_vendor/urllib3/filepost.py | 89 + .../pip/_vendor/urllib3/http2/__init__.py | 53 + .../pip/_vendor/urllib3/http2/connection.py | 356 + .../pip/_vendor/urllib3/http2/probe.py | 87 + .../pip/_vendor/urllib3/poolmanager.py | 651 + .../pip/_vendor/urllib3/py.typed | 2 + .../pip/_vendor/urllib3/response.py | 1474 ++ .../pip/_vendor/urllib3/util/__init__.py | 42 + .../pip/_vendor/urllib3/util/connection.py | 137 + .../pip/_vendor/urllib3/util/proxy.py | 43 + .../pip/_vendor/urllib3/util/request.py | 254 + .../pip/_vendor/urllib3/util/response.py | 101 + .../pip/_vendor/urllib3/util/retry.py | 549 + .../pip/_vendor/urllib3/util/ssl_.py | 527 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 271 + .../pip/_vendor/urllib3/util/timeout.py | 275 + .../pip/_vendor/urllib3/util/url.py | 469 + .../pip/_vendor/urllib3/util/util.py | 42 + .../pip/_vendor/urllib3/util/wait.py | 124 + .../site-packages/pip/_vendor/vendor.txt | 18 + .../lib/python3.12/site-packages/pip/py.typed | 4 + .../pluggy-1.6.0.dist-info/INSTALLER | 1 + .../pluggy-1.6.0.dist-info/METADATA | 152 + .../pluggy-1.6.0.dist-info/RECORD | 23 + .../pluggy-1.6.0.dist-info/WHEEL | 5 + .../pluggy-1.6.0.dist-info/licenses/LICENSE | 21 + .../pluggy-1.6.0.dist-info/top_level.txt | 1 + .../site-packages/pluggy/__init__.py | 30 + .../site-packages/pluggy/_callers.py | 169 + .../python3.12/site-packages/pluggy/_hooks.py | 714 + .../site-packages/pluggy/_manager.py | 523 + .../site-packages/pluggy/_result.py | 107 + .../site-packages/pluggy/_tracing.py | 72 + .../site-packages/pluggy/_version.py | 21 + .../site-packages/pluggy/_warnings.py | 27 + .../python3.12/site-packages/pluggy/py.typed | 0 .venv/lib/python3.12/site-packages/py.py | 15 + .../pycparser-3.0.dist-info/INSTALLER | 1 + .../pycparser-3.0.dist-info/METADATA | 244 + .../pycparser-3.0.dist-info/RECORD | 21 + .../pycparser-3.0.dist-info/WHEEL | 5 + .../pycparser-3.0.dist-info/licenses/LICENSE | 27 + .../pycparser-3.0.dist-info/top_level.txt | 1 + .../site-packages/pycparser/__init__.py | 99 + .../site-packages/pycparser/_ast_gen.py | 355 + .../site-packages/pycparser/_c_ast.cfg | 195 + .../site-packages/pycparser/ast_transforms.py | 174 + .../site-packages/pycparser/c_ast.py | 1341 ++ .../site-packages/pycparser/c_generator.py | 573 + .../site-packages/pycparser/c_lexer.py | 706 + .../site-packages/pycparser/c_parser.py | 2376 +++ .../pydantic-2.13.4.dist-info/INSTALLER | 1 + .../pydantic-2.13.4.dist-info/METADATA | 1294 ++ .../pydantic-2.13.4.dist-info/RECORD | 218 + .../pydantic-2.13.4.dist-info/REQUESTED | 0 .../pydantic-2.13.4.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../site-packages/pydantic/__init__.py | 456 + .../pydantic/_internal/__init__.py | 0 .../pydantic/_internal/_config.py | 386 + .../pydantic/_internal/_core_metadata.py | 97 + .../pydantic/_internal/_core_utils.py | 174 + .../pydantic/_internal/_dataclasses.py | 315 + .../pydantic/_internal/_decorators.py | 873 + .../pydantic/_internal/_decorators_v1.py | 174 + .../_internal/_discriminated_union.py | 494 + .../pydantic/_internal/_docs_extraction.py | 113 + .../pydantic/_internal/_fields.py | 729 + .../pydantic/_internal/_forward_ref.py | 23 + .../pydantic/_internal/_generate_schema.py | 2934 +++ .../pydantic/_internal/_generics.py | 530 + .../site-packages/pydantic/_internal/_git.py | 27 + .../pydantic/_internal/_import_utils.py | 20 + .../pydantic/_internal/_internal_dataclass.py | 7 + .../_internal/_known_annotated_metadata.py | 403 + .../pydantic/_internal/_mock_val_ser.py | 228 + .../pydantic/_internal/_model_construction.py | 868 + .../pydantic/_internal/_namespace_utils.py | 293 + .../site-packages/pydantic/_internal/_repr.py | 124 + .../pydantic/_internal/_schema_gather.py | 212 + .../_internal/_schema_generation_shared.py | 125 + .../pydantic/_internal/_serializers.py | 53 + .../pydantic/_internal/_signature.py | 189 + .../pydantic/_internal/_typing_extra.py | 785 + .../pydantic/_internal/_utils.py | 446 + .../pydantic/_internal/_validate_call.py | 141 + .../pydantic/_internal/_validators.py | 534 + .../site-packages/pydantic/_migration.py | 316 + .../pydantic/alias_generators.py | 62 + .../site-packages/pydantic/aliases.py | 135 + .../pydantic/annotated_handlers.py | 122 + .../pydantic/class_validators.py | 5 + .../site-packages/pydantic/color.py | 604 + .../site-packages/pydantic/config.py | 1296 ++ .../site-packages/pydantic/dataclasses.py | 413 + .../site-packages/pydantic/datetime_parse.py | 5 + .../site-packages/pydantic/decorator.py | 5 + .../pydantic/deprecated/__init__.py | 0 .../pydantic/deprecated/class_validators.py | 256 + .../pydantic/deprecated/config.py | 72 + .../pydantic/deprecated/copy_internals.py | 224 + .../pydantic/deprecated/decorator.py | 284 + .../site-packages/pydantic/deprecated/json.py | 141 + .../pydantic/deprecated/parse.py | 80 + .../pydantic/deprecated/tools.py | 103 + .../site-packages/pydantic/env_settings.py | 5 + .../site-packages/pydantic/error_wrappers.py | 5 + .../site-packages/pydantic/errors.py | 189 + .../pydantic/experimental/__init__.py | 1 + .../pydantic/experimental/arguments_schema.py | 44 + .../pydantic/experimental/missing_sentinel.py | 5 + .../pydantic/experimental/pipeline.py | 663 + .../site-packages/pydantic/fields.py | 1892 ++ .../pydantic/functional_serializers.py | 470 + .../pydantic/functional_validators.py | 889 + .../site-packages/pydantic/generics.py | 5 + .../python3.12/site-packages/pydantic/json.py | 5 + .../site-packages/pydantic/json_schema.py | 2911 +++ .../python3.12/site-packages/pydantic/main.py | 1838 ++ .../python3.12/site-packages/pydantic/mypy.py | 1412 ++ .../site-packages/pydantic/networks.py | 1332 ++ .../site-packages/pydantic/parse.py | 5 + .../site-packages/pydantic/plugin/__init__.py | 193 + .../site-packages/pydantic/plugin/_loader.py | 58 + .../pydantic/plugin/_schema_validator.py | 143 + .../site-packages/pydantic/py.typed | 0 .../site-packages/pydantic/root_model.py | 157 + .../site-packages/pydantic/schema.py | 5 + .../site-packages/pydantic/tools.py | 5 + .../site-packages/pydantic/type_adapter.py | 801 + .../site-packages/pydantic/types.py | 3310 +++ .../site-packages/pydantic/typing.py | 5 + .../site-packages/pydantic/utils.py | 5 + .../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 + .../site-packages/pydantic/v1/color.py | 494 + .../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 + .../site-packages/pydantic/v1/errors.py | 646 + .../site-packages/pydantic/v1/fields.py | 1253 ++ .../site-packages/pydantic/v1/generics.py | 400 + .../site-packages/pydantic/v1/json.py | 112 + .../site-packages/pydantic/v1/main.py | 1130 ++ .../site-packages/pydantic/v1/mypy.py | 949 + .../site-packages/pydantic/v1/networks.py | 747 + .../site-packages/pydantic/v1/parse.py | 66 + .../site-packages/pydantic/v1/py.typed | 0 .../site-packages/pydantic/v1/schema.py | 1163 ++ .../site-packages/pydantic/v1/tools.py | 92 + .../site-packages/pydantic/v1/types.py | 1205 ++ .../site-packages/pydantic/v1/typing.py | 627 + .../site-packages/pydantic/v1/utils.py | 807 + .../site-packages/pydantic/v1/validators.py | 768 + .../site-packages/pydantic/v1/version.py | 38 + .../pydantic/validate_call_decorator.py | 116 + .../site-packages/pydantic/validators.py | 5 + .../site-packages/pydantic/version.py | 113 + .../site-packages/pydantic/warnings.py | 122 + .../pydantic_core-2.46.4.dist-info/INSTALLER | 1 + .../pydantic_core-2.46.4.dist-info/METADATA | 173 + .../pydantic_core-2.46.4.dist-info/RECORD | 13 + .../pydantic_core-2.46.4.dist-info/WHEEL | 5 + .../licenses/LICENSE | 21 + .../sboms/pydantic-core.cyclonedx.json | 3961 ++++ .../site-packages/pydantic_core/__init__.py | 171 + ...antic_core.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 4752552 bytes .../pydantic_core/_pydantic_core.pyi | 1056 + .../pydantic_core/core_schema.py | 4461 ++++ .../site-packages/pydantic_core/py.typed | 0 .../pygments-2.20.0.dist-info/INSTALLER | 1 + .../pygments-2.20.0.dist-info/METADATA | 57 + .../pygments-2.20.0.dist-info/RECORD | 686 + .../pygments-2.20.0.dist-info/WHEEL | 4 + .../entry_points.txt | 2 + .../licenses/AUTHORS | 292 + .../licenses/LICENSE | 25 + .../site-packages/pygments/__init__.py | 82 + .../site-packages/pygments/__main__.py | 17 + .../site-packages/pygments/cmdline.py | 668 + .../site-packages/pygments/console.py | 70 + .../site-packages/pygments/filter.py | 70 + .../pygments/filters/__init__.py | 942 + .../site-packages/pygments/formatter.py | 129 + .../pygments/formatters/__init__.py | 157 + .../pygments/formatters/_mapping.py | 23 + .../pygments/formatters/bbcode.py | 108 + .../pygments/formatters/groff.py | 170 + .../site-packages/pygments/formatters/html.py | 997 + .../site-packages/pygments/formatters/img.py | 686 + .../site-packages/pygments/formatters/irc.py | 153 + .../pygments/formatters/latex.py | 518 + .../pygments/formatters/other.py | 160 + .../pygments/formatters/pangomarkup.py | 83 + .../site-packages/pygments/formatters/rtf.py | 349 + .../site-packages/pygments/formatters/svg.py | 185 + .../pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../site-packages/pygments/lexer.py | 963 + .../site-packages/pygments/lexers/__init__.py | 362 + .../pygments/lexers/_ada_builtins.py | 103 + .../pygments/lexers/_asy_builtins.py | 1644 ++ .../pygments/lexers/_cl_builtins.py | 231 + .../pygments/lexers/_cocoa_builtins.py | 75 + .../pygments/lexers/_csound_builtins.py | 1780 ++ .../pygments/lexers/_css_builtins.py | 558 + .../pygments/lexers/_googlesql_builtins.py | 918 + .../pygments/lexers/_julia_builtins.py | 411 + .../pygments/lexers/_lasso_builtins.py | 5326 +++++ .../pygments/lexers/_lilypond_builtins.py | 5184 +++++ .../pygments/lexers/_lua_builtins.py | 285 + .../pygments/lexers/_luau_builtins.py | 62 + .../site-packages/pygments/lexers/_mapping.py | 603 + .../pygments/lexers/_mql_builtins.py | 1171 ++ .../pygments/lexers/_mysql_builtins.py | 1384 ++ .../pygments/lexers/_openedge_builtins.py | 2600 +++ .../pygments/lexers/_php_builtins.py | 3328 +++ .../pygments/lexers/_postgres_builtins.py | 739 + .../pygments/lexers/_qlik_builtins.py | 666 + .../pygments/lexers/_scheme_builtins.py | 1609 ++ .../pygments/lexers/_scilab_builtins.py | 3093 +++ .../pygments/lexers/_sourcemod_builtins.py | 1151 ++ .../pygments/lexers/_sql_builtins.py | 106 + .../pygments/lexers/_stan_builtins.py | 761 + .../pygments/lexers/_stata_builtins.py | 457 + .../pygments/lexers/_tsql_builtins.py | 1003 + .../pygments/lexers/_usd_builtins.py | 112 + .../pygments/lexers/_vbscript_builtins.py | 279 + .../pygments/lexers/_vim_builtins.py | 1938 ++ .../pygments/lexers/actionscript.py | 243 + .../site-packages/pygments/lexers/ada.py | 144 + .../site-packages/pygments/lexers/agile.py | 25 + .../site-packages/pygments/lexers/algebra.py | 300 + .../site-packages/pygments/lexers/ambient.py | 75 + .../site-packages/pygments/lexers/amdgpu.py | 54 + .../site-packages/pygments/lexers/ampl.py | 87 + .../site-packages/pygments/lexers/apdlexer.py | 593 + .../site-packages/pygments/lexers/apl.py | 103 + .../pygments/lexers/archetype.py | 315 + .../site-packages/pygments/lexers/arrow.py | 116 + .../site-packages/pygments/lexers/arturo.py | 249 + .../site-packages/pygments/lexers/asc.py | 55 + .../site-packages/pygments/lexers/asm.py | 1058 + .../site-packages/pygments/lexers/asn1.py | 178 + .../pygments/lexers/automation.py | 379 + .../site-packages/pygments/lexers/bare.py | 101 + .../site-packages/pygments/lexers/basic.py | 656 + .../site-packages/pygments/lexers/bdd.py | 57 + .../site-packages/pygments/lexers/berry.py | 99 + .../site-packages/pygments/lexers/bibtex.py | 159 + .../pygments/lexers/blueprint.py | 173 + .../site-packages/pygments/lexers/boa.py | 97 + .../site-packages/pygments/lexers/bqn.py | 112 + .../site-packages/pygments/lexers/business.py | 625 + .../site-packages/pygments/lexers/c_cpp.py | 415 + .../site-packages/pygments/lexers/c_like.py | 738 + .../pygments/lexers/capnproto.py | 74 + .../site-packages/pygments/lexers/carbon.py | 95 + .../site-packages/pygments/lexers/cddl.py | 172 + .../site-packages/pygments/lexers/chapel.py | 139 + .../site-packages/pygments/lexers/clean.py | 180 + .../site-packages/pygments/lexers/codeql.py | 80 + .../site-packages/pygments/lexers/comal.py | 81 + .../site-packages/pygments/lexers/compiled.py | 35 + .../site-packages/pygments/lexers/configs.py | 1433 ++ .../site-packages/pygments/lexers/console.py | 114 + .../site-packages/pygments/lexers/cplint.py | 43 + .../site-packages/pygments/lexers/crystal.py | 364 + .../site-packages/pygments/lexers/csound.py | 466 + .../site-packages/pygments/lexers/css.py | 632 + .../site-packages/pygments/lexers/d.py | 259 + .../site-packages/pygments/lexers/dalvik.py | 126 + .../site-packages/pygments/lexers/data.py | 763 + .../site-packages/pygments/lexers/dax.py | 135 + .../pygments/lexers/devicetree.py | 120 + .../site-packages/pygments/lexers/diff.py | 169 + .../site-packages/pygments/lexers/dns.py | 109 + .../site-packages/pygments/lexers/dotnet.py | 873 + .../site-packages/pygments/lexers/dsls.py | 970 + .../site-packages/pygments/lexers/dylan.py | 279 + .../site-packages/pygments/lexers/ecl.py | 144 + .../site-packages/pygments/lexers/eiffel.py | 68 + .../site-packages/pygments/lexers/elm.py | 123 + .../site-packages/pygments/lexers/elpi.py | 201 + .../site-packages/pygments/lexers/email.py | 132 + .../site-packages/pygments/lexers/erlang.py | 526 + .../site-packages/pygments/lexers/esoteric.py | 300 + .../site-packages/pygments/lexers/ezhil.py | 76 + .../site-packages/pygments/lexers/factor.py | 363 + .../site-packages/pygments/lexers/fantom.py | 251 + .../site-packages/pygments/lexers/felix.py | 275 + .../site-packages/pygments/lexers/fift.py | 68 + .../pygments/lexers/floscript.py | 81 + .../site-packages/pygments/lexers/forth.py | 178 + .../site-packages/pygments/lexers/fortran.py | 212 + .../site-packages/pygments/lexers/foxpro.py | 427 + .../site-packages/pygments/lexers/freefem.py | 893 + .../site-packages/pygments/lexers/func.py | 110 + .../pygments/lexers/functional.py | 21 + .../site-packages/pygments/lexers/futhark.py | 105 + .../pygments/lexers/gcodelexer.py | 35 + .../site-packages/pygments/lexers/gdscript.py | 189 + .../site-packages/pygments/lexers/gleam.py | 74 + .../site-packages/pygments/lexers/go.py | 97 + .../pygments/lexers/grammar_notation.py | 262 + .../site-packages/pygments/lexers/graph.py | 108 + .../site-packages/pygments/lexers/graphics.py | 794 + .../site-packages/pygments/lexers/graphql.py | 176 + .../site-packages/pygments/lexers/graphviz.py | 58 + .../site-packages/pygments/lexers/gsql.py | 103 + .../site-packages/pygments/lexers/hare.py | 73 + .../site-packages/pygments/lexers/haskell.py | 867 + .../site-packages/pygments/lexers/haxe.py | 941 + .../site-packages/pygments/lexers/hdl.py | 466 + .../site-packages/pygments/lexers/hexdump.py | 102 + .../site-packages/pygments/lexers/html.py | 670 + .../site-packages/pygments/lexers/idl.py | 284 + .../site-packages/pygments/lexers/igor.py | 435 + .../site-packages/pygments/lexers/inferno.py | 95 + .../pygments/lexers/installers.py | 352 + .../pygments/lexers/int_fiction.py | 1370 ++ .../site-packages/pygments/lexers/iolang.py | 61 + .../site-packages/pygments/lexers/j.py | 151 + .../pygments/lexers/javascript.py | 1591 ++ .../site-packages/pygments/lexers/jmespath.py | 69 + .../site-packages/pygments/lexers/jslt.py | 94 + .../site-packages/pygments/lexers/json5.py | 83 + .../site-packages/pygments/lexers/jsonnet.py | 169 + .../site-packages/pygments/lexers/jsx.py | 100 + .../site-packages/pygments/lexers/julia.py | 294 + .../site-packages/pygments/lexers/jvm.py | 1807 ++ .../site-packages/pygments/lexers/kuin.py | 332 + .../site-packages/pygments/lexers/kusto.py | 93 + .../site-packages/pygments/lexers/ldap.py | 155 + .../site-packages/pygments/lexers/lean.py | 241 + .../site-packages/pygments/lexers/lilypond.py | 225 + .../site-packages/pygments/lexers/lisp.py | 3152 +++ .../pygments/lexers/macaulay2.py | 1847 ++ .../site-packages/pygments/lexers/make.py | 212 + .../site-packages/pygments/lexers/maple.py | 291 + .../site-packages/pygments/lexers/markup.py | 1659 ++ .../site-packages/pygments/lexers/math.py | 21 + .../site-packages/pygments/lexers/matlab.py | 3307 +++ .../site-packages/pygments/lexers/maxima.py | 84 + .../site-packages/pygments/lexers/meson.py | 139 + .../site-packages/pygments/lexers/mime.py | 210 + .../pygments/lexers/minecraft.py | 392 + .../site-packages/pygments/lexers/mips.py | 130 + .../site-packages/pygments/lexers/ml.py | 958 + .../site-packages/pygments/lexers/modeling.py | 368 + .../site-packages/pygments/lexers/modula2.py | 1579 ++ .../site-packages/pygments/lexers/mojo.py | 707 + .../site-packages/pygments/lexers/monte.py | 203 + .../site-packages/pygments/lexers/mosel.py | 447 + .../site-packages/pygments/lexers/ncl.py | 894 + .../site-packages/pygments/lexers/nimrod.py | 199 + .../site-packages/pygments/lexers/nit.py | 63 + .../site-packages/pygments/lexers/nix.py | 144 + .../site-packages/pygments/lexers/numbair.py | 63 + .../site-packages/pygments/lexers/oberon.py | 120 + .../pygments/lexers/objective.py | 513 + .../site-packages/pygments/lexers/ooc.py | 84 + .../site-packages/pygments/lexers/openscad.py | 96 + .../site-packages/pygments/lexers/other.py | 41 + .../site-packages/pygments/lexers/parasail.py | 78 + .../site-packages/pygments/lexers/parsers.py | 798 + .../site-packages/pygments/lexers/pascal.py | 644 + .../site-packages/pygments/lexers/pawn.py | 202 + .../site-packages/pygments/lexers/pddl.py | 82 + .../site-packages/pygments/lexers/perl.py | 733 + .../site-packages/pygments/lexers/phix.py | 363 + .../site-packages/pygments/lexers/php.py | 335 + .../pygments/lexers/pointless.py | 70 + .../site-packages/pygments/lexers/pony.py | 93 + .../site-packages/pygments/lexers/praat.py | 303 + .../site-packages/pygments/lexers/procfile.py | 41 + .../site-packages/pygments/lexers/prolog.py | 318 + .../site-packages/pygments/lexers/promql.py | 176 + .../site-packages/pygments/lexers/prql.py | 251 + .../site-packages/pygments/lexers/ptx.py | 119 + .../site-packages/pygments/lexers/python.py | 1204 ++ .../site-packages/pygments/lexers/q.py | 187 + .../site-packages/pygments/lexers/qlik.py | 117 + .../site-packages/pygments/lexers/qvt.py | 153 + .../site-packages/pygments/lexers/r.py | 196 + .../site-packages/pygments/lexers/rdf.py | 468 + .../site-packages/pygments/lexers/rebol.py | 419 + .../site-packages/pygments/lexers/rego.py | 57 + .../site-packages/pygments/lexers/rell.py | 68 + .../site-packages/pygments/lexers/resource.py | 83 + .../site-packages/pygments/lexers/ride.py | 138 + .../site-packages/pygments/lexers/rita.py | 42 + .../site-packages/pygments/lexers/rnc.py | 66 + .../site-packages/pygments/lexers/roboconf.py | 81 + .../pygments/lexers/robotframework.py | 551 + .../site-packages/pygments/lexers/ruby.py | 518 + .../site-packages/pygments/lexers/rust.py | 222 + .../site-packages/pygments/lexers/sas.py | 227 + .../site-packages/pygments/lexers/savi.py | 171 + .../site-packages/pygments/lexers/scdoc.py | 85 + .../pygments/lexers/scripting.py | 1638 ++ .../site-packages/pygments/lexers/sgf.py | 59 + .../site-packages/pygments/lexers/shell.py | 902 + .../site-packages/pygments/lexers/sieve.py | 78 + .../site-packages/pygments/lexers/slash.py | 183 + .../pygments/lexers/smalltalk.py | 194 + .../site-packages/pygments/lexers/smithy.py | 77 + .../site-packages/pygments/lexers/smv.py | 78 + .../site-packages/pygments/lexers/snobol.py | 82 + .../site-packages/pygments/lexers/solidity.py | 87 + .../site-packages/pygments/lexers/soong.py | 78 + .../site-packages/pygments/lexers/sophia.py | 102 + .../site-packages/pygments/lexers/special.py | 122 + .../site-packages/pygments/lexers/spice.py | 70 + .../site-packages/pygments/lexers/sql.py | 1111 + .../site-packages/pygments/lexers/srcinfo.py | 62 + .../site-packages/pygments/lexers/stata.py | 170 + .../pygments/lexers/supercollider.py | 94 + .../site-packages/pygments/lexers/tablegen.py | 177 + .../site-packages/pygments/lexers/tact.py | 303 + .../site-packages/pygments/lexers/tal.py | 77 + .../site-packages/pygments/lexers/tcl.py | 148 + .../site-packages/pygments/lexers/teal.py | 88 + .../pygments/lexers/templates.py | 2355 +++ .../site-packages/pygments/lexers/teraterm.py | 335 + .../site-packages/pygments/lexers/testing.py | 209 + .../site-packages/pygments/lexers/text.py | 27 + .../site-packages/pygments/lexers/textedit.py | 205 + .../site-packages/pygments/lexers/textfmts.py | 436 + .../site-packages/pygments/lexers/theorem.py | 410 + .../site-packages/pygments/lexers/thingsdb.py | 143 + .../site-packages/pygments/lexers/tlb.py | 59 + .../site-packages/pygments/lexers/tls.py | 54 + .../site-packages/pygments/lexers/tnt.py | 270 + .../pygments/lexers/trafficscript.py | 51 + .../pygments/lexers/typoscript.py | 216 + .../site-packages/pygments/lexers/typst.py | 160 + .../site-packages/pygments/lexers/ul4.py | 309 + .../site-packages/pygments/lexers/unicon.py | 413 + .../site-packages/pygments/lexers/urbi.py | 145 + .../site-packages/pygments/lexers/usd.py | 85 + .../site-packages/pygments/lexers/varnish.py | 189 + .../pygments/lexers/verification.py | 113 + .../site-packages/pygments/lexers/verifpal.py | 65 + .../site-packages/pygments/lexers/vip.py | 150 + .../site-packages/pygments/lexers/vyper.py | 140 + .../site-packages/pygments/lexers/web.py | 24 + .../pygments/lexers/webassembly.py | 119 + .../site-packages/pygments/lexers/webidl.py | 298 + .../site-packages/pygments/lexers/webmisc.py | 1006 + .../site-packages/pygments/lexers/wgsl.py | 406 + .../site-packages/pygments/lexers/whiley.py | 115 + .../site-packages/pygments/lexers/wowtoc.py | 120 + .../site-packages/pygments/lexers/wren.py | 98 + .../site-packages/pygments/lexers/x10.py | 66 + .../site-packages/pygments/lexers/xorg.py | 38 + .../site-packages/pygments/lexers/yang.py | 103 + .../site-packages/pygments/lexers/yara.py | 69 + .../site-packages/pygments/lexers/zig.py | 125 + .../site-packages/pygments/modeline.py | 43 + .../site-packages/pygments/plugin.py | 74 + .../site-packages/pygments/regexopt.py | 102 + .../site-packages/pygments/scanner.py | 104 + .../site-packages/pygments/sphinxext.py | 247 + .../site-packages/pygments/style.py | 203 + .../site-packages/pygments/styles/__init__.py | 61 + .../site-packages/pygments/styles/_mapping.py | 54 + .../site-packages/pygments/styles/abap.py | 32 + .../site-packages/pygments/styles/algol.py | 65 + .../site-packages/pygments/styles/algol_nu.py | 65 + .../site-packages/pygments/styles/arduino.py | 100 + .../site-packages/pygments/styles/autumn.py | 67 + .../site-packages/pygments/styles/borland.py | 53 + .../site-packages/pygments/styles/bw.py | 52 + .../site-packages/pygments/styles/coffee.py | 80 + .../site-packages/pygments/styles/colorful.py | 83 + .../site-packages/pygments/styles/default.py | 76 + .../site-packages/pygments/styles/dracula.py | 90 + .../site-packages/pygments/styles/emacs.py | 75 + .../site-packages/pygments/styles/friendly.py | 76 + .../pygments/styles/friendly_grayscale.py | 80 + .../site-packages/pygments/styles/fruity.py | 47 + .../site-packages/pygments/styles/gh_dark.py | 113 + .../site-packages/pygments/styles/gruvbox.py | 118 + .../site-packages/pygments/styles/igor.py | 32 + .../site-packages/pygments/styles/inkpot.py | 72 + .../pygments/styles/lightbulb.py | 110 + .../site-packages/pygments/styles/lilypond.py | 62 + .../site-packages/pygments/styles/lovelace.py | 100 + .../site-packages/pygments/styles/manni.py | 79 + .../site-packages/pygments/styles/material.py | 124 + .../site-packages/pygments/styles/monokai.py | 112 + .../site-packages/pygments/styles/murphy.py | 82 + .../site-packages/pygments/styles/native.py | 70 + .../site-packages/pygments/styles/nord.py | 156 + .../site-packages/pygments/styles/onedark.py | 73 + .../pygments/styles/paraiso_dark.py | 124 + .../pygments/styles/paraiso_light.py | 124 + .../site-packages/pygments/styles/pastie.py | 78 + .../site-packages/pygments/styles/perldoc.py | 73 + .../pygments/styles/rainbow_dash.py | 95 + .../site-packages/pygments/styles/rrt.py | 54 + .../site-packages/pygments/styles/sas.py | 46 + .../pygments/styles/solarized.py | 144 + .../pygments/styles/staroffice.py | 31 + .../pygments/styles/stata_dark.py | 42 + .../pygments/styles/stata_light.py | 42 + .../site-packages/pygments/styles/tango.py | 143 + .../site-packages/pygments/styles/trac.py | 66 + .../site-packages/pygments/styles/vim.py | 67 + .../site-packages/pygments/styles/vs.py | 41 + .../site-packages/pygments/styles/xcode.py | 53 + .../site-packages/pygments/styles/zenburn.py | 83 + .../site-packages/pygments/token.py | 214 + .../site-packages/pygments/unistring.py | 153 + .../python3.12/site-packages/pygments/util.py | 324 + .../pytest-9.0.3.dist-info/INSTALLER | 1 + .../pytest-9.0.3.dist-info/METADATA | 212 + .../pytest-9.0.3.dist-info/RECORD | 160 + .../pytest-9.0.3.dist-info/REQUESTED | 0 .../pytest-9.0.3.dist-info/WHEEL | 5 + .../pytest-9.0.3.dist-info/entry_points.txt | 3 + .../pytest-9.0.3.dist-info/licenses/LICENSE | 21 + .../pytest-9.0.3.dist-info/top_level.txt | 3 + .../site-packages/pytest/__init__.py | 186 + .../site-packages/pytest/__main__.py | 9 + .../python3.12/site-packages/pytest/py.typed | 0 .../pytest_asyncio-1.4.0.dist-info/INSTALLER | 1 + .../pytest_asyncio-1.4.0.dist-info/METADATA | 92 + .../pytest_asyncio-1.4.0.dist-info/RECORD | 13 + .../pytest_asyncio-1.4.0.dist-info/REQUESTED | 0 .../pytest_asyncio-1.4.0.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../licenses/LICENSE | 201 + .../top_level.txt | 1 + .../site-packages/pytest_asyncio/__init__.py | 11 + .../site-packages/pytest_asyncio/plugin.py | 1139 ++ .../site-packages/pytest_asyncio/py.typed | 0 .../pyyaml-6.0.3.dist-info/INSTALLER | 1 + .../pyyaml-6.0.3.dist-info/METADATA | 59 + .../pyyaml-6.0.3.dist-info/RECORD | 43 + .../pyyaml-6.0.3.dist-info/WHEEL | 7 + .../pyyaml-6.0.3.dist-info/licenses/LICENSE | 20 + .../pyyaml-6.0.3.dist-info/top_level.txt | 2 + .../requests-2.34.2.dist-info/INSTALLER | 1 + .../requests-2.34.2.dist-info/METADATA | 120 + .../requests-2.34.2.dist-info/RECORD | 47 + .../requests-2.34.2.dist-info/REQUESTED | 0 .../requests-2.34.2.dist-info/WHEEL | 5 + .../licenses/LICENSE | 175 + .../requests-2.34.2.dist-info/licenses/NOTICE | 2 + .../requests-2.34.2.dist-info/top_level.txt | 1 + .../site-packages/requests/__init__.py | 219 + .../site-packages/requests/__version__.py | 14 + .../site-packages/requests/_internal_utils.py | 51 + .../site-packages/requests/_types.py | 183 + .../site-packages/requests/adapters.py | 748 + .../python3.12/site-packages/requests/api.py | 180 + .../python3.12/site-packages/requests/auth.py | 354 + .../site-packages/requests/certs.py | 18 + .../site-packages/requests/compat.py | 113 + .../site-packages/requests/cookies.py | 625 + .../site-packages/requests/exceptions.py | 162 + .../python3.12/site-packages/requests/help.py | 134 + .../site-packages/requests/hooks.py | 48 + .../site-packages/requests/models.py | 1180 ++ .../site-packages/requests/packages.py | 23 + .../site-packages/requests/py.typed | 0 .../site-packages/requests/sessions.py | 920 + .../site-packages/requests/status_codes.py | 128 + .../site-packages/requests/structures.py | 130 + .../site-packages/requests/utils.py | 1155 ++ .../rich-15.0.0.dist-info/INSTALLER | 1 + .../rich-15.0.0.dist-info/METADATA | 479 + .../rich-15.0.0.dist-info/RECORD | 207 + .../rich-15.0.0.dist-info/REQUESTED | 0 .../site-packages/rich-15.0.0.dist-info/WHEEL | 4 + .../rich-15.0.0.dist-info/licenses/LICENSE | 19 + .../python3.12/site-packages/rich/__init__.py | 177 + .../python3.12/site-packages/rich/__main__.py | 245 + .../site-packages/rich/_emoji_codes.py | 3610 ++++ .../site-packages/rich/_emoji_replace.py | 31 + .../site-packages/rich/_export_format.py | 76 + .../site-packages/rich/_extension.py | 10 + .../python3.12/site-packages/rich/_fileno.py | 24 + .../python3.12/site-packages/rich/_inspect.py | 272 + .../site-packages/rich/_log_render.py | 94 + .../python3.12/site-packages/rich/_loop.py | 43 + .../site-packages/rich/_null_file.py | 69 + .../site-packages/rich/_palettes.py | 309 + .../python3.12/site-packages/rich/_pick.py | 17 + .../python3.12/site-packages/rich/_ratio.py | 153 + .../site-packages/rich/_spinners.py | 482 + .../python3.12/site-packages/rich/_stack.py | 16 + .../python3.12/site-packages/rich/_timer.py | 19 + .../rich/_unicode_data/__init__.py | 93 + .../rich/_unicode_data/_versions.py | 23 + .../rich/_unicode_data/unicode10-0-0.py | 611 + .../rich/_unicode_data/unicode11-0-0.py | 625 + .../rich/_unicode_data/unicode12-0-0.py | 637 + .../rich/_unicode_data/unicode12-1-0.py | 636 + .../rich/_unicode_data/unicode13-0-0.py | 648 + .../rich/_unicode_data/unicode14-0-0.py | 661 + .../rich/_unicode_data/unicode15-0-0.py | 671 + .../rich/_unicode_data/unicode15-1-0.py | 670 + .../rich/_unicode_data/unicode16-0-0.py | 683 + .../rich/_unicode_data/unicode17-0-0.py | 691 + .../rich/_unicode_data/unicode4-1-0.py | 425 + .../rich/_unicode_data/unicode5-0-0.py | 430 + .../rich/_unicode_data/unicode5-1-0.py | 433 + .../rich/_unicode_data/unicode5-2-0.py | 461 + .../rich/_unicode_data/unicode6-0-0.py | 469 + .../rich/_unicode_data/unicode6-1-0.py | 480 + .../rich/_unicode_data/unicode6-2-0.py | 480 + .../rich/_unicode_data/unicode6-3-0.py | 481 + .../rich/_unicode_data/unicode7-0-0.py | 507 + .../rich/_unicode_data/unicode8-0-0.py | 515 + .../rich/_unicode_data/unicode9-0-0.py | 598 + .../site-packages/rich/_win32_console.py | 661 + .../python3.12/site-packages/rich/_windows.py | 71 + .../site-packages/rich/_windows_renderer.py | 56 + .../python3.12/site-packages/rich/_wrap.py | 93 + .../lib/python3.12/site-packages/rich/abc.py | 33 + .../python3.12/site-packages/rich/align.py | 320 + .../lib/python3.12/site-packages/rich/ansi.py | 241 + .../lib/python3.12/site-packages/rich/bar.py | 93 + .../lib/python3.12/site-packages/rich/box.py | 474 + .../python3.12/site-packages/rich/cells.py | 352 + .../python3.12/site-packages/rich/color.py | 621 + .../site-packages/rich/color_triplet.py | 38 + .../python3.12/site-packages/rich/columns.py | 187 + .../python3.12/site-packages/rich/console.py | 2698 +++ .../site-packages/rich/constrain.py | 37 + .../site-packages/rich/containers.py | 167 + .../python3.12/site-packages/rich/control.py | 219 + .../site-packages/rich/default_styles.py | 196 + .../python3.12/site-packages/rich/diagnose.py | 39 + .../python3.12/site-packages/rich/emoji.py | 93 + .../python3.12/site-packages/rich/errors.py | 34 + .../site-packages/rich/file_proxy.py | 60 + .../python3.12/site-packages/rich/filesize.py | 88 + .../site-packages/rich/highlighter.py | 232 + .../lib/python3.12/site-packages/rich/json.py | 139 + .../python3.12/site-packages/rich/jupyter.py | 101 + .../python3.12/site-packages/rich/layout.py | 442 + .../lib/python3.12/site-packages/rich/live.py | 404 + .../site-packages/rich/live_render.py | 116 + .../python3.12/site-packages/rich/logging.py | 305 + .../python3.12/site-packages/rich/markdown.py | 802 + .../python3.12/site-packages/rich/markup.py | 251 + .../python3.12/site-packages/rich/measure.py | 151 + .../python3.12/site-packages/rich/padding.py | 141 + .../python3.12/site-packages/rich/pager.py | 34 + .../python3.12/site-packages/rich/palette.py | 100 + .../python3.12/site-packages/rich/panel.py | 317 + .../python3.12/site-packages/rich/pretty.py | 1016 + .../python3.12/site-packages/rich/progress.py | 1716 ++ .../site-packages/rich/progress_bar.py | 223 + .../python3.12/site-packages/rich/prompt.py | 400 + .../python3.12/site-packages/rich/protocol.py | 41 + .../python3.12/site-packages/rich/py.typed | 0 .../python3.12/site-packages/rich/region.py | 10 + .../lib/python3.12/site-packages/rich/repr.py | 150 + .../lib/python3.12/site-packages/rich/rule.py | 130 + .../python3.12/site-packages/rich/scope.py | 92 + .../python3.12/site-packages/rich/screen.py | 54 + .../python3.12/site-packages/rich/segment.py | 780 + .../python3.12/site-packages/rich/spinner.py | 132 + .../python3.12/site-packages/rich/status.py | 131 + .../python3.12/site-packages/rich/style.py | 796 + .../python3.12/site-packages/rich/styled.py | 42 + .../python3.12/site-packages/rich/syntax.py | 988 + .../python3.12/site-packages/rich/table.py | 1015 + .../site-packages/rich/terminal_theme.py | 153 + .../lib/python3.12/site-packages/rich/text.py | 1363 ++ .../python3.12/site-packages/rich/theme.py | 116 + .../python3.12/site-packages/rich/themes.py | 5 + .../site-packages/rich/traceback.py | 924 + .../lib/python3.12/site-packages/rich/tree.py | 257 + .../sanic-25.12.0.dist-info/INSTALLER | 1 + .../sanic-25.12.0.dist-info/METADATA | 278 + .../sanic-25.12.0.dist-info/RECORD | 277 + .../sanic-25.12.0.dist-info/REQUESTED | 0 .../sanic-25.12.0.dist-info/WHEEL | 5 + .../sanic-25.12.0.dist-info/entry_points.txt | 2 + .../sanic-25.12.0.dist-info/licenses/LICENSE | 21 + .../sanic-25.12.0.dist-info/top_level.txt | 1 + .../site-packages/sanic/__init__.py | 86 + .../site-packages/sanic/__main__.py | 29 + .../site-packages/sanic/__version__.py | 1 + .../lib/python3.12/site-packages/sanic/app.py | 2552 +++ .../sanic/application/__init__.py | 0 .../sanic/application/constants.py | 38 + .../site-packages/sanic/application/ext.py | 48 + .../site-packages/sanic/application/logo.py | 72 + .../site-packages/sanic/application/motd.py | 179 + .../sanic/application/spinner.py | 90 + .../site-packages/sanic/application/state.py | 115 + .../python3.12/site-packages/sanic/asgi.py | 263 + .../site-packages/sanic/base/__init__.py | 0 .../site-packages/sanic/base/meta.py | 6 + .../site-packages/sanic/base/root.py | 69 + .../site-packages/sanic/blueprint_group.py | 4 + .../site-packages/sanic/blueprints.py | 983 + .../site-packages/sanic/cli/__init__.py | 0 .../python3.12/site-packages/sanic/cli/app.py | 499 + .../site-packages/sanic/cli/arguments.py | 431 + .../site-packages/sanic/cli/base.py | 35 + .../site-packages/sanic/cli/console.py | 309 + .../site-packages/sanic/cli/daemon.py | 164 + .../site-packages/sanic/cli/executor.py | 100 + .../site-packages/sanic/cli/inspector.py | 105 + .../sanic/cli/inspector_client.py | 119 + .../python3.12/site-packages/sanic/compat.py | 196 + .../python3.12/site-packages/sanic/config.py | 495 + .../site-packages/sanic/constants.py | 38 + .../site-packages/sanic/cookies/__init__.py | 4 + .../site-packages/sanic/cookies/request.py | 159 + .../site-packages/sanic/cookies/response.py | 607 + .../site-packages/sanic/errorpages.py | 405 + .../site-packages/sanic/exceptions.py | 655 + .../site-packages/sanic/handlers/__init__.py | 10 + .../sanic/handlers/content_range.py | 76 + .../site-packages/sanic/handlers/directory.py | 156 + .../site-packages/sanic/handlers/error.py | 203 + .../python3.12/site-packages/sanic/headers.py | 550 + .../python3.12/site-packages/sanic/helpers.py | 173 + .../site-packages/sanic/http/__init__.py | 6 + .../site-packages/sanic/http/constants.py | 30 + .../site-packages/sanic/http/http1.py | 619 + .../site-packages/sanic/http/http3.py | 423 + .../site-packages/sanic/http/stream.py | 27 + .../site-packages/sanic/http/tls/__init__.py | 5 + .../site-packages/sanic/http/tls/context.py | 210 + .../site-packages/sanic/http/tls/creators.py | 287 + .../lib/python3.12/site-packages/sanic/log.py | 24 + .../site-packages/sanic/logging/__init__.py | 0 .../site-packages/sanic/logging/color.py | 68 + .../site-packages/sanic/logging/default.py | 60 + .../sanic/logging/deprecation.py | 33 + .../site-packages/sanic/logging/filter.py | 13 + .../site-packages/sanic/logging/formatter.py | 387 + .../site-packages/sanic/logging/loggers.py | 37 + .../site-packages/sanic/logging/setup.py | 61 + .../site-packages/sanic/middleware.py | 105 + .../site-packages/sanic/mixins/__init__.py | 0 .../site-packages/sanic/mixins/base.py | 45 + .../site-packages/sanic/mixins/commands.py | 31 + .../site-packages/sanic/mixins/exceptions.py | 110 + .../site-packages/sanic/mixins/listeners.py | 433 + .../site-packages/sanic/mixins/middleware.py | 232 + .../site-packages/sanic/mixins/routes.py | 817 + .../site-packages/sanic/mixins/signals.py | 144 + .../site-packages/sanic/mixins/startup.py | 1447 ++ .../site-packages/sanic/mixins/static.py | 425 + .../site-packages/sanic/models/__init__.py | 0 .../site-packages/sanic/models/asgi.py | 97 + .../site-packages/sanic/models/ctx_types.py | 69 + .../site-packages/sanic/models/futures.py | 80 + .../sanic/models/handler_types.py | 30 + .../site-packages/sanic/models/http_types.py | 34 + .../sanic/models/protocol_types.py | 29 + .../sanic/models/server_types.py | 71 + .../site-packages/sanic/pages/__init__.py | 0 .../site-packages/sanic/pages/base.py | 81 + .../site-packages/sanic/pages/css.py | 34 + .../sanic/pages/directory_page.py | 63 + .../site-packages/sanic/pages/error.py | 112 + .../sanic/pages/styles/BasePage.css | 146 + .../sanic/pages/styles/DirectoryPage.css | 63 + .../sanic/pages/styles/ErrorPage.css | 108 + .../python3.12/site-packages/sanic/py.typed | 0 .../site-packages/sanic/request/__init__.py | 11 + .../site-packages/sanic/request/form.py | 114 + .../site-packages/sanic/request/parameters.py | 33 + .../site-packages/sanic/request/types.py | 1145 ++ .../site-packages/sanic/response/__init__.py | 36 + .../sanic/response/convenience.py | 412 + .../site-packages/sanic/response/types.py | 553 + .../python3.12/site-packages/sanic/router.py | 273 + .../site-packages/sanic/server/__init__.py | 15 + .../sanic/server/async_server.py | 112 + .../site-packages/sanic/server/events.py | 36 + .../site-packages/sanic/server/goodbye.py | 31 + .../site-packages/sanic/server/loop.py | 64 + .../sanic/server/protocols/__init__.py | 0 .../sanic/server/protocols/base_protocol.py | 260 + .../sanic/server/protocols/http_protocol.py | 360 + .../server/protocols/websocket_protocol.py | 230 + .../site-packages/sanic/server/runners.py | 404 + .../site-packages/sanic/server/socket.py | 121 + .../sanic/server/websockets/__init__.py | 0 .../sanic/server/websockets/connection.py | 82 + .../sanic/server/websockets/frame.py | 293 + .../sanic/server/websockets/impl.py | 878 + .../python3.12/site-packages/sanic/signals.py | 420 + .../python3.12/site-packages/sanic/simple.py | 18 + .../site-packages/sanic/startup/__init__.py | 0 .../site-packages/sanic/startup/errors.py | 60 + .../site-packages/sanic/touchup/__init__.py | 8 + .../site-packages/sanic/touchup/meta.py | 22 + .../sanic/touchup/schemes/__init__.py | 6 + .../sanic/touchup/schemes/altsvc.py | 56 + .../sanic/touchup/schemes/base.py | 37 + .../sanic/touchup/schemes/ode.py | 90 + .../site-packages/sanic/touchup/service.py | 30 + .../site-packages/sanic/types/__init__.py | 4 + .../sanic/types/hashable_dict.py | 3 + .../site-packages/sanic/types/shared_ctx.py | 58 + .../python3.12/site-packages/sanic/utils.py | 140 + .../python3.12/site-packages/sanic/views.py | 245 + .../site-packages/sanic/worker/__init__.py | 0 .../site-packages/sanic/worker/constants.py | 25 + .../site-packages/sanic/worker/daemon.py | 507 + .../site-packages/sanic/worker/inspector.py | 157 + .../site-packages/sanic/worker/loader.py | 167 + .../site-packages/sanic/worker/manager.py | 550 + .../site-packages/sanic/worker/multiplexer.py | 169 + .../site-packages/sanic/worker/process.py | 292 + .../site-packages/sanic/worker/reloader.py | 122 + .../site-packages/sanic/worker/restarter.py | 90 + .../site-packages/sanic/worker/serve.py | 147 + .../site-packages/sanic/worker/state.py | 83 + .../sanic_ext-24.12.0.dist-info/INSTALLER | 1 + .../sanic_ext-24.12.0.dist-info/LICENSE | 21 + .../sanic_ext-24.12.0.dist-info/METADATA | 136 + .../sanic_ext-24.12.0.dist-info/RECORD | 214 + .../sanic_ext-24.12.0.dist-info/REQUESTED | 0 .../sanic_ext-24.12.0.dist-info/WHEEL | 5 + .../sanic_ext-24.12.0.dist-info/top_level.txt | 2 + .../site-packages/sanic_ext/__init__.py | 26 + .../site-packages/sanic_ext/bootstrap.py | 224 + .../site-packages/sanic_ext/config.py | 181 + .../site-packages/sanic_ext/exceptions.py | 11 + .../sanic_ext/extensions/__init__.py | 0 .../sanic_ext/extensions/base.py | 82 + .../sanic_ext/extensions/health/__init__.py | 0 .../sanic_ext/extensions/health/endpoint.py | 13 + .../sanic_ext/extensions/health/extension.py | 30 + .../sanic_ext/extensions/health/monitor.py | 162 + .../sanic_ext/extensions/http/__init__.py | 0 .../sanic_ext/extensions/http/cors.py | 396 + .../sanic_ext/extensions/http/extension.py | 34 + .../sanic_ext/extensions/http/methods.py | 146 + .../extensions/injection/__init__.py | 0 .../extensions/injection/constructor.py | 180 + .../extensions/injection/extension.py | 22 + .../extensions/injection/injector.py | 123 + .../extensions/injection/registry.py | 115 + .../sanic_ext/extensions/logging/__init__.py | 0 .../sanic_ext/extensions/logging/extension.py | 24 + .../sanic_ext/extensions/logging/extractor.py | 116 + .../sanic_ext/extensions/logging/logger.py | 126 + .../sanic_ext/extensions/openapi/__init__.py | 0 .../sanic_ext/extensions/openapi/autodoc.py | 93 + .../sanic_ext/extensions/openapi/blueprint.py | 261 + .../sanic_ext/extensions/openapi/builders.py | 446 + .../sanic_ext/extensions/openapi/constants.py | 36 + .../extensions/openapi/definitions.py | 455 + .../sanic_ext/extensions/openapi/extension.py | 43 + .../sanic_ext/extensions/openapi/openapi.py | 534 + .../sanic_ext/extensions/openapi/types.py | 452 + .../extensions/openapi/ui/redoc.html | 20 + .../extensions/openapi/ui/swagger.html | 39 + .../extensions/templating/__init__.py | 0 .../sanic_ext/extensions/templating/engine.py | 78 + .../extensions/templating/extension.py | 57 + .../sanic_ext/extensions/templating/render.py | 105 + .../sanic_ext/extras/__init__.py | 0 .../site-packages/sanic_ext/extras/request.py | 46 + .../sanic_ext/extras/serializer/__init__.py | 0 .../sanic_ext/extras/serializer/decorator.py | 42 + .../sanic_ext/extras/validation/__init__.py | 0 .../sanic_ext/extras/validation/check.py | 283 + .../sanic_ext/extras/validation/clean.py | 17 + .../sanic_ext/extras/validation/decorator.py | 80 + .../sanic_ext/extras/validation/schema.py | 133 + .../sanic_ext/extras/validation/setup.py | 69 + .../sanic_ext/extras/validation/validators.py | 52 + .../site-packages/sanic_ext/py.typed | 0 .../site-packages/sanic_ext/utils/__init__.py | 0 .../sanic_ext/utils/extraction.py | 13 + .../site-packages/sanic_ext/utils/route.py | 124 + .../site-packages/sanic_ext/utils/string.py | 13 + .../site-packages/sanic_ext/utils/typing.py | 79 + .../site-packages/sanic_ext/utils/version.py | 44 + .../sanic_routing-23.12.0.dist-info/INSTALLER | 1 + .../sanic_routing-23.12.0.dist-info/LICENSE | 21 + .../sanic_routing-23.12.0.dist-info/METADATA | 207 + .../sanic_routing-23.12.0.dist-info/RECORD | 26 + .../sanic_routing-23.12.0.dist-info/REQUESTED | 0 .../sanic_routing-23.12.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/sanic_routing/__init__.py | 7 + .../site-packages/sanic_routing/exceptions.py | 49 + .../site-packages/sanic_routing/group.py | 206 + .../site-packages/sanic_routing/line.py | 17 + .../site-packages/sanic_routing/patterns.py | 178 + .../site-packages/sanic_routing/py.typed | 0 .../site-packages/sanic_routing/route.py | 380 + .../site-packages/sanic_routing/router.py | 639 + .../site-packages/sanic_routing/tree.py | 484 + .../site-packages/sanic_routing/utils.py | 97 + .../sanic_testing-24.6.0.dist-info/INSTALLER | 1 + .../sanic_testing-24.6.0.dist-info/LICENSE | 21 + .../sanic_testing-24.6.0.dist-info/METADATA | 100 + .../sanic_testing-24.6.0.dist-info/RECORD | 17 + .../sanic_testing-24.6.0.dist-info/REQUESTED | 0 .../sanic_testing-24.6.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/sanic_testing/__init__.py | 4 + .../site-packages/sanic_testing/manager.py | 12 + .../site-packages/sanic_testing/reusable.py | 237 + .../site-packages/sanic_testing/testing.py | 476 + .../site-packages/sanic_testing/websocket.py | 50 + .../setuptools-82.0.1.dist-info/INSTALLER | 1 + .../setuptools-82.0.1.dist-info/METADATA | 140 + .../setuptools-82.0.1.dist-info/RECORD | 759 + .../setuptools-82.0.1.dist-info/WHEEL | 5 + .../entry_points.txt | 51 + .../licenses/LICENSE | 17 + .../setuptools-82.0.1.dist-info/top_level.txt | 2 + .../site-packages/setuptools/__init__.py | 256 + .../setuptools/_core_metadata.py | 337 + .../site-packages/setuptools/_discovery.py | 33 + .../setuptools/_distutils/__init__.py | 14 + .../setuptools/_distutils/_log.py | 3 + .../setuptools/_distutils/_macos_compat.py | 12 + .../setuptools/_distutils/_modified.py | 95 + .../setuptools/_distutils/_msvccompiler.py | 16 + .../setuptools/_distutils/archive_util.py | 284 + .../setuptools/_distutils/ccompiler.py | 26 + .../setuptools/_distutils/cmd.py | 535 + .../setuptools/_distutils/command/__init__.py | 23 + .../_distutils/command/_framework_compat.py | 54 + .../setuptools/_distutils/command/bdist.py | 167 + .../_distutils/command/bdist_dumb.py | 141 + .../_distutils/command/bdist_rpm.py | 597 + .../setuptools/_distutils/command/build.py | 156 + .../_distutils/command/build_clib.py | 199 + .../_distutils/command/build_ext.py | 811 + .../setuptools/_distutils/command/build_py.py | 404 + .../_distutils/command/build_scripts.py | 150 + .../setuptools/_distutils/command/check.py | 152 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 348 + .../setuptools/_distutils/command/install.py | 805 + .../_distutils/command/install_data.py | 94 + .../_distutils/command/install_egg_info.py | 90 + .../_distutils/command/install_headers.py | 46 + .../_distutils/command/install_lib.py | 236 + .../_distutils/command/install_scripts.py | 59 + .../setuptools/_distutils/command/sdist.py | 521 + .../setuptools/_distutils/compat/__init__.py | 18 + .../setuptools/_distutils/compat/numpy.py | 2 + .../setuptools/_distutils/compat/py39.py | 66 + .../setuptools/_distutils/compilers/C/base.py | 1386 ++ .../_distutils/compilers/C/cygwin.py | 340 + .../_distutils/compilers/C/errors.py | 24 + .../setuptools/_distutils/compilers/C/msvc.py | 614 + .../_distutils/compilers/C/tests/test_base.py | 83 + .../compilers/C/tests/test_cygwin.py | 76 + .../compilers/C/tests/test_mingw.py | 48 + .../_distutils/compilers/C/tests/test_msvc.py | 136 + .../_distutils/compilers/C/tests/test_unix.py | 413 + .../setuptools/_distutils/compilers/C/unix.py | 422 + .../setuptools/_distutils/compilers/C/zos.py | 230 + .../setuptools/_distutils/core.py | 289 + .../setuptools/_distutils/cygwinccompiler.py | 31 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 14 + .../setuptools/_distutils/dir_util.py | 232 + .../setuptools/_distutils/dist.py | 1384 ++ .../setuptools/_distutils/errors.py | 108 + .../setuptools/_distutils/extension.py | 258 + .../setuptools/_distutils/fancy_getopt.py | 471 + .../setuptools/_distutils/file_util.py | 228 + .../setuptools/_distutils/filelist.py | 431 + .../setuptools/_distutils/log.py | 56 + .../setuptools/_distutils/spawn.py | 130 + .../setuptools/_distutils/sysconfig.py | 598 + .../setuptools/_distutils/tests/__init__.py | 42 + .../_distutils/tests/compat/__init__.py | 0 .../_distutils/tests/compat/py39.py | 40 + .../setuptools/_distutils/tests/support.py | 134 + .../_distutils/tests/test_archive_util.py | 342 + .../setuptools/_distutils/tests/test_bdist.py | 47 + .../_distutils/tests/test_bdist_dumb.py | 78 + .../_distutils/tests/test_bdist_rpm.py | 127 + .../setuptools/_distutils/tests/test_build.py | 49 + .../_distutils/tests/test_build_clib.py | 134 + .../_distutils/tests/test_build_ext.py | 628 + .../_distutils/tests/test_build_py.py | 196 + .../_distutils/tests/test_build_scripts.py | 96 + .../setuptools/_distutils/tests/test_check.py | 194 + .../setuptools/_distutils/tests/test_clean.py | 45 + .../setuptools/_distutils/tests/test_cmd.py | 107 + .../_distutils/tests/test_config_cmd.py | 87 + .../setuptools/_distutils/tests/test_core.py | 130 + .../_distutils/tests/test_dir_util.py | 139 + .../setuptools/_distutils/tests/test_dist.py | 552 + .../_distutils/tests/test_extension.py | 117 + .../_distutils/tests/test_file_util.py | 95 + .../_distutils/tests/test_filelist.py | 336 + .../_distutils/tests/test_install.py | 245 + .../_distutils/tests/test_install_data.py | 74 + .../_distutils/tests/test_install_headers.py | 33 + .../_distutils/tests/test_install_lib.py | 110 + .../_distutils/tests/test_install_scripts.py | 52 + .../setuptools/_distutils/tests/test_log.py | 12 + .../_distutils/tests/test_modified.py | 126 + .../setuptools/_distutils/tests/test_sdist.py | 470 + .../setuptools/_distutils/tests/test_spawn.py | 141 + .../_distutils/tests/test_sysconfig.py | 319 + .../_distutils/tests/test_text_file.py | 127 + .../setuptools/_distutils/tests/test_util.py | 243 + .../_distutils/tests/test_version.py | 80 + .../_distutils/tests/test_versionpredicate.py | 0 .../_distutils/tests/unix_compat.py | 17 + .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 9 + .../setuptools/_distutils/util.py | 506 + .../setuptools/_distutils/version.py | 348 + .../setuptools/_distutils/versionpredicate.py | 175 + .../setuptools/_distutils/zosccompiler.py | 3 + .../site-packages/setuptools/_entry_points.py | 94 + .../site-packages/setuptools/_imp.py | 87 + .../site-packages/setuptools/_importlib.py | 9 + .../site-packages/setuptools/_itertools.py | 23 + .../setuptools/_normalization.py | 180 + .../site-packages/setuptools/_path.py | 93 + .../site-packages/setuptools/_reqs.py | 42 + .../site-packages/setuptools/_scripts.py | 361 + .../site-packages/setuptools/_shutil.py | 59 + .../site-packages/setuptools/_static.py | 188 + .../site-packages/setuptools/_vendor/.lock | 0 .../autocommand-2.2.2.dist-info/INSTALLER | 1 + .../autocommand-2.2.2.dist-info/LICENSE | 166 + .../autocommand-2.2.2.dist-info/METADATA | 420 + .../autocommand-2.2.2.dist-info/RECORD | 13 + .../autocommand-2.2.2.dist-info/REQUESTED | 0 .../_vendor/autocommand-2.2.2.dist-info/WHEEL | 5 + .../autocommand-2.2.2.dist-info/top_level.txt | 1 + .../_vendor/autocommand/__init__.py | 27 + .../_vendor/autocommand/autoasync.py | 142 + .../_vendor/autocommand/autocommand.py | 70 + .../_vendor/autocommand/automain.py | 59 + .../_vendor/autocommand/autoparse.py | 333 + .../setuptools/_vendor/autocommand/errors.py | 23 + .../INSTALLER | 1 + .../backports.tarfile-1.2.0.dist-info/LICENSE | 17 + .../METADATA | 46 + .../backports.tarfile-1.2.0.dist-info/RECORD | 12 + .../REQUESTED | 0 .../backports.tarfile-1.2.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../setuptools/_vendor/backports/__init__.py | 1 + .../_vendor/backports/tarfile/__init__.py | 2937 +++ .../_vendor/backports/tarfile/__main__.py | 5 + .../backports/tarfile/compat/__init__.py | 0 .../_vendor/backports/tarfile/compat/py38.py | 24 + .../INSTALLER | 1 + .../METADATA | 133 + .../importlib_metadata-8.7.1.dist-info/RECORD | 21 + .../REQUESTED | 0 .../importlib_metadata-8.7.1.dist-info/WHEEL | 5 + .../licenses/LICENSE | 73 + .../top_level.txt | 1 + .../_vendor/importlib_metadata/__init__.py | 1191 ++ .../_vendor/importlib_metadata/_adapters.py | 136 + .../importlib_metadata/_collections.py | 34 + .../_vendor/importlib_metadata/_compat.py | 56 + .../_vendor/importlib_metadata/_functools.py | 135 + .../_vendor/importlib_metadata/_itertools.py | 171 + .../_vendor/importlib_metadata/_meta.py | 71 + .../_vendor/importlib_metadata/_text.py | 99 + .../_vendor/importlib_metadata/_typing.py | 15 + .../importlib_metadata/compat/__init__.py | 0 .../importlib_metadata/compat/py311.py | 22 + .../_vendor/importlib_metadata/compat/py39.py | 42 + .../_vendor/importlib_metadata/diagnose.py | 21 + .../_vendor/importlib_metadata/py.typed | 0 .../jaraco.text-4.0.0.dist-info/INSTALLER | 1 + .../jaraco.text-4.0.0.dist-info/LICENSE | 17 + .../jaraco.text-4.0.0.dist-info/METADATA | 96 + .../jaraco.text-4.0.0.dist-info/RECORD | 14 + .../jaraco.text-4.0.0.dist-info/REQUESTED | 0 .../_vendor/jaraco.text-4.0.0.dist-info/WHEEL | 5 + .../jaraco.text-4.0.0.dist-info/top_level.txt | 1 + .../_vendor/jaraco/context/__init__.py | 367 + .../_vendor/jaraco/context/py.typed | 0 .../_vendor/jaraco/functools/__init__.py | 722 + .../_vendor/jaraco/functools/__init__.pyi | 123 + .../_vendor/jaraco/functools/py.typed | 0 .../_vendor/jaraco/text/Lorem ipsum.txt | 2 + .../_vendor/jaraco/text/__init__.py | 647 + .../setuptools/_vendor/jaraco/text/layouts.py | 25 + .../_vendor/jaraco/text/show-newlines.py | 32 + .../_vendor/jaraco/text/strip-prefix.py | 21 + .../_vendor/jaraco/text/to-dvorak.py | 5 + .../_vendor/jaraco/text/to-qwerty.py | 5 + .../jaraco_context-6.1.0.dist-info/INSTALLER | 1 + .../jaraco_context-6.1.0.dist-info/METADATA | 82 + .../jaraco_context-6.1.0.dist-info/RECORD | 9 + .../jaraco_context-6.1.0.dist-info/REQUESTED | 0 .../jaraco_context-6.1.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 18 + .../top_level.txt | 1 + .../INSTALLER | 1 + .../jaraco_functools-4.4.0.dist-info/METADATA | 69 + .../jaraco_functools-4.4.0.dist-info/RECORD | 10 + .../REQUESTED | 0 .../jaraco_functools-4.4.0.dist-info/WHEEL | 5 + .../licenses/LICENSE | 18 + .../top_level.txt | 1 + .../more_itertools-10.8.0.dist-info/INSTALLER | 1 + .../more_itertools-10.8.0.dist-info/METADATA | 283 + .../more_itertools-10.8.0.dist-info/RECORD | 13 + .../more_itertools-10.8.0.dist-info/REQUESTED | 0 .../more_itertools-10.8.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 19 + .../_vendor/more_itertools/__init__.py | 6 + .../_vendor/more_itertools/__init__.pyi | 2 + .../setuptools/_vendor/more_itertools/more.py | 5303 +++++ .../_vendor/more_itertools/more.pyi | 949 + .../_vendor/more_itertools/py.typed | 0 .../_vendor/more_itertools/recipes.py | 1471 ++ .../_vendor/more_itertools/recipes.pyi | 205 + .../packaging-26.0.dist-info/INSTALLER | 1 + .../_vendor/packaging-26.0.dist-info/METADATA | 107 + .../_vendor/packaging-26.0.dist-info/RECORD | 26 + .../packaging-26.0.dist-info/REQUESTED | 0 .../_vendor/packaging-26.0.dist-info/WHEEL | 4 + .../packaging-26.0.dist-info/licenses/LICENSE | 3 + .../licenses/LICENSE.APACHE | 177 + .../licenses/LICENSE.BSD | 23 + .../setuptools/_vendor/packaging/__init__.py | 15 + .../setuptools/_vendor/packaging/_elffile.py | 108 + .../_vendor/packaging/_manylinux.py | 262 + .../_vendor/packaging/_musllinux.py | 85 + .../setuptools/_vendor/packaging/_parser.py | 365 + .../_vendor/packaging/_structures.py | 69 + .../_vendor/packaging/_tokenizer.py | 193 + .../_vendor/packaging/licenses/__init__.py | 147 + .../_vendor/packaging/licenses/_spdx.py | 799 + .../setuptools/_vendor/packaging/markers.py | 388 + .../setuptools/_vendor/packaging/metadata.py | 978 + .../setuptools/_vendor/packaging/py.typed | 0 .../setuptools/_vendor/packaging/pylock.py | 635 + .../_vendor/packaging/requirements.py | 86 + .../_vendor/packaging/specifiers.py | 1068 + .../setuptools/_vendor/packaging/tags.py | 651 + .../setuptools/_vendor/packaging/utils.py | 158 + .../setuptools/_vendor/packaging/version.py | 792 + .../platformdirs-4.4.0.dist-info/INSTALLER | 1 + .../platformdirs-4.4.0.dist-info/METADATA | 350 + .../platformdirs-4.4.0.dist-info/RECORD | 15 + .../platformdirs-4.4.0.dist-info/REQUESTED | 0 .../platformdirs-4.4.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../_vendor/platformdirs/__init__.py | 631 + .../_vendor/platformdirs/__main__.py | 55 + .../_vendor/platformdirs/android.py | 249 + .../setuptools/_vendor/platformdirs/api.py | 299 + .../setuptools/_vendor/platformdirs/macos.py | 146 + .../setuptools/_vendor/platformdirs/py.typed | 0 .../setuptools/_vendor/platformdirs/unix.py | 272 + .../_vendor/platformdirs/version.py | 34 + .../_vendor/platformdirs/windows.py | 272 + .../_vendor/tomli-2.4.0.dist-info/INSTALLER | 1 + .../_vendor/tomli-2.4.0.dist-info/METADATA | 270 + .../_vendor/tomli-2.4.0.dist-info/RECORD | 11 + .../_vendor/tomli-2.4.0.dist-info/REQUESTED | 0 .../_vendor/tomli-2.4.0.dist-info/WHEEL | 4 + .../tomli-2.4.0.dist-info/licenses/LICENSE | 21 + .../setuptools/_vendor/tomli/__init__.py | 8 + .../setuptools/_vendor/tomli/_parser.py | 782 + .../setuptools/_vendor/tomli/_re.py | 119 + .../setuptools/_vendor/tomli/_types.py | 10 + .../setuptools/_vendor/tomli/py.typed | 1 + .../_vendor/wheel-0.46.3.dist-info/INSTALLER | 1 + .../_vendor/wheel-0.46.3.dist-info/METADATA | 69 + .../_vendor/wheel-0.46.3.dist-info/RECORD | 22 + .../_vendor/wheel-0.46.3.dist-info/REQUESTED | 0 .../_vendor/wheel-0.46.3.dist-info/WHEEL | 4 + .../wheel-0.46.3.dist-info/entry_points.txt | 6 + .../licenses/LICENSE.txt | 21 + .../setuptools/_vendor/wheel/__init__.py | 3 + .../setuptools/_vendor/wheel/__main__.py | 25 + .../setuptools/_vendor/wheel/_bdist_wheel.py | 616 + .../_vendor/wheel/_commands/__init__.py | 153 + .../_vendor/wheel/_commands/convert.py | 337 + .../_vendor/wheel/_commands/pack.py | 84 + .../_vendor/wheel/_commands/tags.py | 140 + .../_vendor/wheel/_commands/unpack.py | 30 + .../setuptools/_vendor/wheel/_metadata.py | 184 + .../_vendor/wheel/_setuptools_logging.py | 26 + .../setuptools/_vendor/wheel/bdist_wheel.py | 26 + .../_vendor/wheel/macosx_libfile.py | 486 + .../setuptools/_vendor/wheel/metadata.py | 17 + .../setuptools/_vendor/wheel/wheelfile.py | 241 + .../_vendor/zipp-3.23.0.dist-info/INSTALLER | 1 + .../_vendor/zipp-3.23.0.dist-info/METADATA | 106 + .../_vendor/zipp-3.23.0.dist-info/RECORD | 14 + .../_vendor/zipp-3.23.0.dist-info/REQUESTED | 0 .../_vendor/zipp-3.23.0.dist-info/WHEEL | 5 + .../zipp-3.23.0.dist-info/licenses/LICENSE | 18 + .../zipp-3.23.0.dist-info/top_level.txt | 1 + .../setuptools/_vendor/zipp/__init__.py | 456 + .../setuptools/_vendor/zipp/_functools.py | 20 + .../_vendor/zipp/compat/__init__.py | 0 .../setuptools/_vendor/zipp/compat/overlay.py | 37 + .../setuptools/_vendor/zipp/compat/py310.py | 13 + .../setuptools/_vendor/zipp/compat/py313.py | 34 + .../setuptools/_vendor/zipp/glob.py | 116 + .../site-packages/setuptools/archive_util.py | 219 + .../site-packages/setuptools/build_meta.py | 556 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 11776 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 14336 bytes .../site-packages/setuptools/cli-arm64.exe | Bin 0 -> 13824 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 11776 bytes .../setuptools/command/__init__.py | 21 + .../setuptools/command/_requirestxt.py | 131 + .../site-packages/setuptools/command/alias.py | 77 + .../setuptools/command/bdist_egg.py | 471 + .../setuptools/command/bdist_rpm.py | 42 + .../setuptools/command/bdist_wheel.py | 603 + .../site-packages/setuptools/command/build.py | 135 + .../setuptools/command/build_clib.py | 103 + .../setuptools/command/build_ext.py | 470 + .../setuptools/command/build_py.py | 403 + .../setuptools/command/develop.py | 58 + .../setuptools/command/dist_info.py | 103 + .../setuptools/command/easy_install.py | 30 + .../setuptools/command/editable_wheel.py | 914 + .../setuptools/command/egg_info.py | 716 + .../setuptools/command/install.py | 131 + .../setuptools/command/install_egg_info.py | 57 + .../setuptools/command/install_lib.py | 137 + .../setuptools/command/install_scripts.py | 66 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 21 + .../site-packages/setuptools/command/sdist.py | 218 + .../setuptools/command/setopt.py | 139 + .../site-packages/setuptools/command/test.py | 47 + .../setuptools/compat/__init__.py | 0 .../site-packages/setuptools/compat/py310.py | 20 + .../site-packages/setuptools/compat/py311.py | 27 + .../site-packages/setuptools/compat/py312.py | 13 + .../site-packages/setuptools/compat/py39.py | 9 + .../site-packages/setuptools/config/NOTICE | 10 + .../setuptools/config/__init__.py | 43 + .../setuptools/config/_apply_pyprojecttoml.py | 534 + .../config/_validate_pyproject/NOTICE | 438 + .../config/_validate_pyproject/__init__.py | 34 + .../_validate_pyproject/error_reporting.py | 338 + .../_validate_pyproject/extra_validations.py | 151 + .../fastjsonschema_exceptions.py | 51 + .../fastjsonschema_validations.py | 1453 ++ .../config/_validate_pyproject/formats.py | 464 + .../setuptools/config/distutils.schema.json | 26 + .../site-packages/setuptools/config/expand.py | 452 + .../setuptools/config/pyprojecttoml.py | 477 + .../setuptools/config/setupcfg.py | 782 + .../setuptools/config/setuptools.schema.json | 433 + .../site-packages/setuptools/depends.py | 185 + .../site-packages/setuptools/discovery.py | 614 + .../site-packages/setuptools/dist.py | 1124 + .../site-packages/setuptools/errors.py | 67 + .../site-packages/setuptools/extension.py | 179 + .../site-packages/setuptools/glob.py | 185 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 11776 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 14336 bytes .../site-packages/setuptools/gui-arm64.exe | Bin 0 -> 13824 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 11776 bytes .../site-packages/setuptools/installer.py | 155 + .../site-packages/setuptools/launch.py | 36 + .../setuptools/launcher manifest.xml | 15 + .../site-packages/setuptools/logging.py | 40 + .../site-packages/setuptools/modified.py | 18 + .../site-packages/setuptools/monkey.py | 126 + .../site-packages/setuptools/msvc.py | 1557 ++ .../site-packages/setuptools/namespaces.py | 101 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../setuptools/tests/__init__.py | 13 + .../setuptools/tests/compat/__init__.py | 0 .../setuptools/tests/compat/py39.py | 3 + .../setuptools/tests/config/__init__.py | 0 .../tests/config/downloads/__init__.py | 59 + .../tests/config/downloads/preload.py | 18 + .../tests/config/setupcfg_examples.txt | 22 + .../tests/config/test_apply_pyprojecttoml.py | 794 + .../setuptools/tests/config/test_expand.py | 247 + .../tests/config/test_pyprojecttoml.py | 421 + .../config/test_pyprojecttoml_dynamic_deps.py | 111 + .../setuptools/tests/config/test_setupcfg.py | 987 + .../setuptools/tests/contexts.py | 131 + .../setuptools/tests/environment.py | 95 + .../setuptools/tests/fixtures.py | 406 + .../indexes/test_links_priority/external.html | 3 + .../simple/foobar/index.html | 4 + .../setuptools/tests/integration/__init__.py | 0 .../setuptools/tests/integration/helpers.py | 80 + .../setuptools/tests/integration/test_pbr.py | 20 + .../integration/test_pip_install_sdist.py | 223 + .../setuptools/tests/mod_with_constant.py | 1 + .../setuptools/tests/namespaces.py | 90 + .../setuptools/tests/script-with-bom.py | 1 + .../setuptools/tests/test_archive_util.py | 36 + .../tests/test_bdist_deprecations.py | 28 + .../setuptools/tests/test_bdist_egg.py | 73 + .../setuptools/tests/test_bdist_wheel.py | 708 + .../setuptools/tests/test_build.py | 33 + .../setuptools/tests/test_build_clib.py | 84 + .../setuptools/tests/test_build_ext.py | 293 + .../setuptools/tests/test_build_meta.py | 959 + .../setuptools/tests/test_build_py.py | 480 + .../setuptools/tests/test_config_discovery.py | 647 + .../setuptools/tests/test_core_metadata.py | 550 + .../setuptools/tests/test_depends.py | 15 + .../setuptools/tests/test_develop.py | 113 + .../setuptools/tests/test_dist.py | 280 + .../setuptools/tests/test_dist_info.py | 147 + .../tests/test_distutils_adoption.py | 198 + .../setuptools/tests/test_editable_install.py | 1261 ++ .../setuptools/tests/test_egg_info.py | 1306 ++ .../setuptools/tests/test_extern.py | 15 + .../setuptools/tests/test_find_packages.py | 218 + .../setuptools/tests/test_find_py_modules.py | 73 + .../setuptools/tests/test_glob.py | 45 + .../setuptools/tests/test_install_scripts.py | 89 + .../setuptools/tests/test_logging.py | 76 + .../setuptools/tests/test_manifest.py | 622 + .../setuptools/tests/test_namespaces.py | 79 + .../setuptools/tests/test_scripts.py | 12 + .../setuptools/tests/test_sdist.py | 980 + .../setuptools/tests/test_setopt.py | 40 + .../setuptools/tests/test_setuptools.py | 294 + .../setuptools/tests/test_shutil_wrapper.py | 23 + .../setuptools/tests/test_unicode_utils.py | 10 + .../setuptools/tests/test_virtualenv.py | 113 + .../setuptools/tests/test_warnings.py | 106 + .../setuptools/tests/test_wheel.py | 690 + .../setuptools/tests/test_windows_wrappers.py | 258 + .../site-packages/setuptools/tests/text.py | 4 + .../setuptools/tests/textwrap.py | 6 + .../site-packages/setuptools/unicode_utils.py | 102 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/warnings.py | 110 + .../site-packages/setuptools/wheel.py | 262 + .../setuptools/windows_support.py | 30 + .../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/python3.12/site-packages/six.py | 1003 + .../site-packages/src/ujson/python/version.h | 39 + .../srp-1.0.22.dist-info/INSTALLER | 1 + .../srp-1.0.22.dist-info/LICENSE | 21 + .../srp-1.0.22.dist-info/METADATA | 50 + .../site-packages/srp-1.0.22.dist-info/RECORD | 19 + .../srp-1.0.22.dist-info/REQUESTED | 0 .../site-packages/srp-1.0.22.dist-info/WHEEL | 5 + .../srp-1.0.22.dist-info/top_level.txt | 1 + .../python3.12/site-packages/srp/__init__.py | 31 + .../python3.12/site-packages/srp/_ctsrp.py | 654 + .../python3.12/site-packages/srp/_pysrp.py | 441 + .../python3.12/site-packages/srp/doc/conf.py | 216 + .../site-packages/srp/doc/index.rst | 22 + .../python3.12/site-packages/srp/doc/srp.rst | 399 + .../python3.12/site-packages/srp/test_srp.py | 316 + .../site-packages/tests/__init__.py | 0 .../site-packages/tests/conftest.py | 59 + .../tests/extensions/__init__.py | 0 .../tests/extensions/http/__init__.py | 0 .../tests/extensions/http/test_methods.py | 129 + .../tests/extensions/injection/__init__.py | 0 .../injection/test_add_dependency.py | 416 + .../extensions/injection/test_constants.py | 69 + .../extensions/injection/test_dependency.py | 45 + .../injection/test_injection_config.py | 42 + .../injection/test_injection_registry.py | 185 + .../tests/extensions/logging/__init__.py | 0 .../logging/test_custom_background_logger.py | 20 + .../tests/extensions/openapi/__init__.py | 0 .../tests/extensions/openapi/test_autodoc.py | 56 + .../tests/extensions/openapi/test_body.py | 83 + .../tests/extensions/openapi/test_config.py | 34 + .../extensions/openapi/test_decorators.py | 890 + .../extensions/openapi/test_definitions.py | 56 + .../extensions/openapi/test_deprecated.py | 39 + .../tests/extensions/openapi/test_exclude.py | 52 + .../extensions/openapi/test_external_docs.py | 56 + .../extensions/openapi/test_func_handler.py | 51 + .../extensions/openapi/test_model_fields.py | 103 + .../extensions/openapi/test_model_spec.py | 108 + .../extensions/openapi/test_parameter.py | 149 + .../tests/extensions/openapi/test_paths.py | 58 + .../tests/extensions/openapi/test_schema.py | 75 + .../tests/extensions/openapi/test_security.py | 98 + .../extensions/openapi/test_specification.py | 37 + .../tests/extensions/openapi/test_summary.py | 38 + .../tests/extensions/openapi/test_tag.py | 35 + .../tests/extensions/openapi/test_typing.py | 49 + .../tests/extensions/openapi/utils.py | 14 + .../tests/extensions/templating/__init__.py | 0 .../templating/templates/__init__.py | 0 .../extensions/templating/test_templating.py | 153 + .../tests/extensions/test_startup.py | 63 + .../site-packages/tests/extra/__init__.py | 0 .../site-packages/tests/extra/__models__.py | 159 + .../tests/extra/test_parse_hint.py | 24 + .../tests/extra/test_request_counted.py | 43 + .../tests/extra/test_serializer.py | 70 + .../tests/extra/test_validation.py | 100 + .../tests/extra/test_validation_attrs.py | 112 + .../tests/extra/test_validation_dataclass.py | 410 + .../tests/extra/test_validation_msgspec.py | 394 + .../tests/extra/test_validation_multiple.py | 42 + .../tests/extra/test_validation_pydantic.py | 148 + .../tracerite-2.3.1.dist-info/INSTALLER | 1 + .../tracerite-2.3.1.dist-info/METADATA | 125 + .../tracerite-2.3.1.dist-info/RECORD | 29 + .../tracerite-2.3.1.dist-info/REQUESTED | 0 .../tracerite-2.3.1.dist-info/WHEEL | 4 + .../site-packages/tracerite/__init__.py | 19 + .../site-packages/tracerite/chain_analysis.py | 703 + .../site-packages/tracerite/fastapi.py | 57 + .../site-packages/tracerite/html.py | 526 + .../site-packages/tracerite/inspector.py | 452 + .../site-packages/tracerite/logging.py | 4 + .../site-packages/tracerite/notebook.py | 115 + .../site-packages/tracerite/script.js | 20 + .../site-packages/tracerite/style.css | 466 + .../site-packages/tracerite/syntaxerror.py | 417 + .../site-packages/tracerite/trace.py | 1916 ++ .../site-packages/tracerite/trace_cpy.py | 408 + .../python3.12/site-packages/tracerite/tty.py | 1380 ++ .../INSTALLER | 1 + .../METADATA | 72 + .../typing_extensions-4.15.0.dist-info/RECORD | 7 + .../typing_extensions-4.15.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .../site-packages/typing_extensions.py | 4317 ++++ .../INSTALLER | 1 + .../METADATA | 49 + .../typing_inspection-0.4.2.dist-info/RECORD | 13 + .../typing_inspection-0.4.2.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../typing_inspection/__init__.py | 0 .../typing_inspection/introspection.py | 587 + .../site-packages/typing_inspection/py.typed | 0 .../typing_inspection/typing_objects.py | 607 + .../typing_inspection/typing_objects.pyi | 417 + .../ujson-5.12.1.dist-info/INSTALLER | 1 + .../ujson-5.12.1.dist-info/METADATA | 205 + .../ujson-5.12.1.dist-info/RECORD | 9 + .../ujson-5.12.1.dist-info/WHEEL | 6 + .../licenses/LICENSE.txt | 114 + .../ujson-5.12.1.dist-info/top_level.txt | 2 + .../site-packages/ujson-stubs/__init__.pyi | 56 + .../ujson.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 106280 bytes .../urllib3-2.7.0.dist-info/INSTALLER | 1 + .../urllib3-2.7.0.dist-info/METADATA | 163 + .../urllib3-2.7.0.dist-info/RECORD | 79 + .../urllib3-2.7.0.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 21 + .../site-packages/urllib3/__init__.py | 211 + .../site-packages/urllib3/_base_connection.py | 167 + .../site-packages/urllib3/_collections.py | 486 + .../site-packages/urllib3/_request_methods.py | 278 + .../site-packages/urllib3/_version.py | 24 + .../site-packages/urllib3/connection.py | 1099 + .../site-packages/urllib3/connectionpool.py | 1191 ++ .../site-packages/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/emscripten/__init__.py | 17 + .../urllib3/contrib/emscripten/connection.py | 260 + .../emscripten/emscripten_fetch_worker.js | 110 + .../urllib3/contrib/emscripten/fetch.py | 726 + .../urllib3/contrib/emscripten/request.py | 22 + .../urllib3/contrib/emscripten/response.py | 281 + .../urllib3/contrib/pyopenssl.py | 563 + .../site-packages/urllib3/contrib/socks.py | 228 + .../site-packages/urllib3/exceptions.py | 335 + .../site-packages/urllib3/fields.py | 341 + .../site-packages/urllib3/filepost.py | 89 + .../site-packages/urllib3/http2/__init__.py | 53 + .../site-packages/urllib3/http2/connection.py | 356 + .../site-packages/urllib3/http2/probe.py | 87 + .../site-packages/urllib3/poolmanager.py | 653 + .../python3.12/site-packages/urllib3/py.typed | 2 + .../site-packages/urllib3/response.py | 1493 ++ .../site-packages/urllib3/util/__init__.py | 42 + .../site-packages/urllib3/util/connection.py | 137 + .../site-packages/urllib3/util/proxy.py | 43 + .../site-packages/urllib3/util/request.py | 263 + .../site-packages/urllib3/util/response.py | 101 + .../site-packages/urllib3/util/retry.py | 557 + .../site-packages/urllib3/util/ssl_.py | 477 + .../urllib3/util/ssl_match_hostname.py | 153 + .../urllib3/util/ssltransport.py | 271 + .../site-packages/urllib3/util/timeout.py | 275 + .../site-packages/urllib3/util/url.py | 469 + .../site-packages/urllib3/util/util.py | 42 + .../site-packages/urllib3/util/wait.py | 124 + .../uvloop-0.22.1.dist-info/INSTALLER | 1 + .../uvloop-0.22.1.dist-info/METADATA | 176 + .../uvloop-0.22.1.dist-info/RECORD | 69 + .../uvloop-0.22.1.dist-info/WHEEL | 7 + .../licenses/LICENSE-APACHE | 203 + .../licenses/LICENSE-MIT | 21 + .../uvloop-0.22.1.dist-info/top_level.txt | 1 + .../site-packages/uvloop/__init__.py | 233 + .../python3.12/site-packages/uvloop/_noop.py | 3 + .../site-packages/uvloop/_testbase.py | 552 + .../site-packages/uvloop/_version.py | 13 + .../site-packages/uvloop/cbhandles.pxd | 39 + .../site-packages/uvloop/cbhandles.pyx | 434 + .../python3.12/site-packages/uvloop/dns.pyx | 479 + .../site-packages/uvloop/errors.pyx | 113 + .../site-packages/uvloop/handles/async_.pxd | 11 + .../site-packages/uvloop/handles/async_.pyx | 56 + .../uvloop/handles/basetransport.pxd | 54 + .../uvloop/handles/basetransport.pyx | 293 + .../site-packages/uvloop/handles/check.pxd | 14 + .../site-packages/uvloop/handles/check.pyx | 72 + .../site-packages/uvloop/handles/fsevent.pxd | 12 + .../site-packages/uvloop/handles/fsevent.pyx | 116 + .../site-packages/uvloop/handles/handle.pxd | 48 + .../site-packages/uvloop/handles/handle.pyx | 395 + .../site-packages/uvloop/handles/idle.pxd | 14 + .../site-packages/uvloop/handles/idle.pyx | 72 + .../site-packages/uvloop/handles/pipe.pxd | 33 + .../site-packages/uvloop/handles/pipe.pyx | 247 + .../site-packages/uvloop/handles/poll.pxd | 25 + .../site-packages/uvloop/handles/poll.pyx | 233 + .../site-packages/uvloop/handles/process.pxd | 80 + .../site-packages/uvloop/handles/process.pyx | 792 + .../site-packages/uvloop/handles/stream.pxd | 50 + .../site-packages/uvloop/handles/stream.pyx | 1016 + .../uvloop/handles/streamserver.pxd | 26 + .../uvloop/handles/streamserver.pyx | 150 + .../site-packages/uvloop/handles/tcp.pxd | 26 + .../site-packages/uvloop/handles/tcp.pyx | 228 + .../site-packages/uvloop/handles/timer.pxd | 18 + .../site-packages/uvloop/handles/timer.pyx | 89 + .../site-packages/uvloop/handles/udp.pxd | 22 + .../site-packages/uvloop/handles/udp.pyx | 408 + .../site-packages/uvloop/includes/__init__.py | 23 + .../site-packages/uvloop/includes/consts.pxi | 33 + .../site-packages/uvloop/includes/debug.pxd | 3 + .../uvloop/includes/flowcontrol.pxd | 23 + .../site-packages/uvloop/includes/python.pxd | 31 + .../site-packages/uvloop/includes/stdlib.pxi | 176 + .../site-packages/uvloop/includes/system.pxd | 96 + .../site-packages/uvloop/includes/uv.pxd | 506 + .../loop.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 15952168 bytes .../python3.12/site-packages/uvloop/loop.pxd | 230 + .../python3.12/site-packages/uvloop/loop.pyi | 297 + .../python3.12/site-packages/uvloop/loop.pyx | 3424 ++++ .../python3.12/site-packages/uvloop/lru.pyx | 79 + .../site-packages/uvloop/pseudosock.pyx | 209 + .../python3.12/site-packages/uvloop/py.typed | 0 .../site-packages/uvloop/request.pxd | 8 + .../site-packages/uvloop/request.pyx | 65 + .../site-packages/uvloop/server.pxd | 19 + .../site-packages/uvloop/server.pyx | 136 + .../site-packages/uvloop/sslproto.pxd | 138 + .../site-packages/uvloop/sslproto.pyx | 950 + .../websockets-16.0.dist-info/INSTALLER | 1 + .../websockets-16.0.dist-info/METADATA | 179 + .../websockets-16.0.dist-info/RECORD | 109 + .../websockets-16.0.dist-info/REQUESTED | 0 .../websockets-16.0.dist-info/WHEEL | 7 + .../entry_points.txt | 2 + .../licenses/LICENSE | 24 + .../websockets-16.0.dist-info/top_level.txt | 1 + .../site-packages/websockets/__init__.py | 236 + .../site-packages/websockets/__main__.py | 5 + .../websockets/asyncio/__init__.py | 0 .../websockets/asyncio/async_timeout.py | 282 + .../websockets/asyncio/client.py | 804 + .../websockets/asyncio/compatibility.py | 30 + .../websockets/asyncio/connection.py | 1247 ++ .../websockets/asyncio/messages.py | 316 + .../websockets/asyncio/router.py | 219 + .../websockets/asyncio/server.py | 997 + .../site-packages/websockets/auth.py | 18 + .../site-packages/websockets/cli.py | 178 + .../site-packages/websockets/client.py | 391 + .../site-packages/websockets/connection.py | 12 + .../websockets/datastructures.py | 183 + .../site-packages/websockets/exceptions.py | 473 + .../websockets/extensions/__init__.py | 4 + .../websockets/extensions/base.py | 123 + .../extensions/permessage_deflate.py | 699 + .../site-packages/websockets/frames.py | 431 + .../site-packages/websockets/headers.py | 586 + .../site-packages/websockets/http.py | 20 + .../site-packages/websockets/http11.py | 438 + .../site-packages/websockets/imports.py | 100 + .../websockets/legacy/__init__.py | 11 + .../site-packages/websockets/legacy/auth.py | 190 + .../site-packages/websockets/legacy/client.py | 703 + .../websockets/legacy/exceptions.py | 71 + .../websockets/legacy/framing.py | 224 + .../websockets/legacy/handshake.py | 158 + .../site-packages/websockets/legacy/http.py | 201 + .../websockets/legacy/protocol.py | 1635 ++ .../site-packages/websockets/legacy/server.py | 1191 ++ .../site-packages/websockets/protocol.py | 768 + .../site-packages/websockets/proxy.py | 150 + .../site-packages/websockets/py.typed | 0 .../site-packages/websockets/server.py | 589 + .../site-packages/websockets/speedups.c | 229 + .../speedups.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 38048 bytes .../site-packages/websockets/speedups.pyi | 3 + .../site-packages/websockets/streams.py | 151 + .../site-packages/websockets/sync/__init__.py | 0 .../site-packages/websockets/sync/client.py | 633 + .../websockets/sync/connection.py | 1078 + .../site-packages/websockets/sync/messages.py | 348 + .../site-packages/websockets/sync/router.py | 213 + .../site-packages/websockets/sync/server.py | 765 + .../site-packages/websockets/sync/utils.py | 45 + .../site-packages/websockets/typing.py | 75 + .../site-packages/websockets/uri.py | 107 + .../site-packages/websockets/utils.py | 53 + .../site-packages/websockets/version.py | 92 + .../python3.12/site-packages/yaml/__init__.py | 390 + .../_yaml.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 2679264 bytes .../python3.12/site-packages/yaml/composer.py | 139 + .../site-packages/yaml/constructor.py | 748 + .../python3.12/site-packages/yaml/cyaml.py | 101 + .../python3.12/site-packages/yaml/dumper.py | 62 + .../python3.12/site-packages/yaml/emitter.py | 1137 ++ .../python3.12/site-packages/yaml/error.py | 75 + .../python3.12/site-packages/yaml/events.py | 86 + .../python3.12/site-packages/yaml/loader.py | 63 + .../python3.12/site-packages/yaml/nodes.py | 49 + .../python3.12/site-packages/yaml/parser.py | 589 + .../python3.12/site-packages/yaml/reader.py | 185 + .../site-packages/yaml/representer.py | 389 + .../python3.12/site-packages/yaml/resolver.py | 227 + .../python3.12/site-packages/yaml/scanner.py | 1435 ++ .../site-packages/yaml/serializer.py | 111 + .../python3.12/site-packages/yaml/tokens.py | 104 + .venv/lib64 | 1 + .venv/pyvenv.cfg | 5 + {coven => hh}/Cargo.lock | 44 +- {coven => hh}/Cargo.toml | 6 +- {coven => hh}/README.md | 28 +- {coven => hh}/src/crypto.rs | 0 {coven => hh}/src/main.rs | 14 +- {coven => hh}/tools/gen_vectors.py | 0 2730 files changed, 712933 insertions(+), 46 deletions(-) create mode 100644 .venv/bin/Activate.ps1 create mode 100644 .venv/bin/activate create mode 100644 .venv/bin/activate.csh create mode 100644 .venv/bin/activate.fish create mode 100755 .venv/bin/httpx create mode 100755 .venv/bin/idna create mode 100755 .venv/bin/markdown-it create mode 100755 .venv/bin/normalizer create mode 100755 .venv/bin/pip create mode 100755 .venv/bin/pip3 create mode 100755 .venv/bin/pip3.12 create mode 100755 .venv/bin/py.test create mode 100755 .venv/bin/pygmentize create mode 100755 .venv/bin/pytest create mode 120000 .venv/bin/python create mode 120000 .venv/bin/python3 create mode 120000 .venv/bin/python3.12 create mode 100755 .venv/bin/sanic create mode 100755 .venv/bin/websockets create mode 100755 .venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/_distutils_hack/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_distutils_hack/override.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_argcomplete.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_code/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_code/code.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_code/source.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_io/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_io/pprint.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_py/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_py/error.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_py/path.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/_version.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/assertion/util.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/cacheprovider.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/capture.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/compat.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/config/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/config/argparsing.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/config/compat.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/config/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/config/findpaths.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/debugging.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/deprecated.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/doctest.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/faulthandler.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/fixtures.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/freeze_support.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/helpconfig.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/hookspec.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/junitxml.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/legacypath.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/logging.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/main.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/mark/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/mark/expression.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/mark/structures.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/monkeypatch.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/nodes.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/outcomes.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/pastebin.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/pathlib.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/py.typed create mode 100644 .venv/lib/python3.12/site-packages/_pytest/pytester.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/python.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/python_api.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/raises.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/recwarn.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/reports.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/runner.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/scope.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/setuponly.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/setupplan.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/skipping.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/stash.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/stepwise.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/subtests.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/terminal.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/terminalprogress.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/threadexception.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/timing.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/tmpdir.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/tracemalloc.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/unittest.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/unraisableexception.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/warning_types.py create mode 100644 .venv/lib/python3.12/site-packages/_pytest/warnings.py create mode 100644 .venv/lib/python3.12/site-packages/_yaml/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/NOTICE create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/base.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/os.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/ospath.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/tempfile/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/tempfile/temptypes.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/threadpool/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/threadpool/binary.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/threadpool/text.py create mode 100644 .venv/lib/python3.12/site-packages/aiofiles/threadpool/utils.py create mode 100644 .venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/annotated_types/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/annotated_types/py.typed create mode 100644 .venv/lib/python3.12/site-packages/annotated_types/test_cases.py create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/anyio/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_backends/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_backends/_trio.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_fileio.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_resources.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_signals.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_sockets.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_streams.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_tasks.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_testing.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_resources.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_sockets.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_streams.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_tasks.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/abc/_testing.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/from_thread.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/functools.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/lowlevel.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/py.typed create mode 100644 .venv/lib/python3.12/site-packages/anyio/pytest_plugin.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/buffered.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/file.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/memory.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/stapled.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/text.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/streams/tls.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/to_interpreter.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/to_process.py create mode 100644 .venv/lib/python3.12/site-packages/anyio/to_thread.py create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/certifi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/certifi/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/certifi/core.py create mode 100644 .venv/lib/python3.12/site-packages/certifi/py.typed create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/cffi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/_cffi_errors.h create mode 100644 .venv/lib/python3.12/site-packages/cffi/_cffi_include.h create mode 100644 .venv/lib/python3.12/site-packages/cffi/_embedding.h create mode 100644 .venv/lib/python3.12/site-packages/cffi/_imp_emulation.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/api.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/backend_ctypes.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/cffi_opcode.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/commontypes.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/cparser.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/error.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/ffiplatform.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/lock.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/model.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/parse_c_type.h create mode 100644 .venv/lib/python3.12/site-packages/cffi/pkgconfig.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/recompiler.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/setuptools_ext.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/vengine_cpy.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/vengine_gen.py create mode 100644 .venv/lib/python3.12/site-packages/cffi/verifier.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/api.py create mode 100755 .venv/lib/python3.12/site-packages/charset_normalizer/cd.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cd.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/constant.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/legacy.py create mode 100755 .venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/md.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/models.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/py.typed create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/utils.py create mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/version.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.APACHE create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.BSD create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json create mode 100644 .venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/sbom.json create mode 100644 .venv/lib/python3.12/site-packages/cryptography/__about__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/fernet.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py create mode 100755 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/py.typed create mode 100644 .venv/lib/python3.12/site-packages/cryptography/utils.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/base.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/extensions.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/general_name.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/name.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/oid.py create mode 100644 .venv/lib/python3.12/site-packages/cryptography/x509/verification.py create mode 100644 .venv/lib/python3.12/site-packages/distutils-precedence.pth create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/h11/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_abnf.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_connection.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_events.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_headers.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_readers.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_receivebuffer.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_state.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_util.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_version.py create mode 100644 .venv/lib/python3.12/site-packages/h11/_writers.py create mode 100644 .venv/lib/python3.12/site-packages/h11/py.typed create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/builder.py create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/document.py create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/html5.py create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/makebuilder.py create mode 100644 .venv/lib/python3.12/site-packages/html5tagger/util.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/httpcore-1.0.9.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.12/site-packages/httpcore/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_api.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/connection.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/connection_pool.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/http11.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/http2.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/http_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/interfaces.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_async/socks_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/anyio.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/auto.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/base.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/mock.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/sync.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_backends/trio.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_models.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_ssl.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/connection.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/http11.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/http2.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/http_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/interfaces.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_sync/socks_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_synchronization.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_trace.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/_utils.py create mode 100644 .venv/lib/python3.12/site-packages/httpcore/py.typed create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/licenses/vendor/http-parser/LICENSE-MIT create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/licenses/vendor/llhttp/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/httptools-0.8.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/httptools/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httptools/_version.py create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/cparser.pxd create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/errors.py create mode 100755 .venv/lib/python3.12/site-packages/httptools/parser/parser.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/parser.pyi create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/parser.pyx create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/python.pxd create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/url_cparser.pxd create mode 100755 .venv/lib/python3.12/site-packages/httptools/parser/url_parser.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/url_parser.pyi create mode 100644 .venv/lib/python3.12/site-packages/httptools/parser/url_parser.pyx create mode 100644 .venv/lib/python3.12/site-packages/httptools/py.typed create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.12/site-packages/httpx/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/__version__.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_api.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_auth.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_client.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_config.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_content.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_decoders.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_main.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_models.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_multipart.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/asgi.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/base.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/default.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/mock.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_transports/wsgi.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_types.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_urlparse.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_urls.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/_utils.py create mode 100644 .venv/lib/python3.12/site-packages/httpx/py.typed create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/idna-3.17.dist-info/licenses/LICENSE.md create mode 100644 .venv/lib/python3.12/site-packages/idna/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/idna/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/idna/cli.py create mode 100644 .venv/lib/python3.12/site-packages/idna/codec.py create mode 100644 .venv/lib/python3.12/site-packages/idna/compat.py create mode 100644 .venv/lib/python3.12/site-packages/idna/core.py create mode 100644 .venv/lib/python3.12/site-packages/idna/idnadata.py create mode 100644 .venv/lib/python3.12/site-packages/idna/intranges.py create mode 100644 .venv/lib/python3.12/site-packages/idna/package_data.py create mode 100644 .venv/lib/python3.12/site-packages/idna/py.typed create mode 100644 .venv/lib/python3.12/site-packages/idna/uts46data.py create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/iniconfig-2.3.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/iniconfig/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/iniconfig/_parse.py create mode 100644 .venv/lib/python3.12/site-packages/iniconfig/_version.py create mode 100644 .venv/lib/python3.12/site-packages/iniconfig/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/iniconfig/py.typed create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/_compat.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/_punycode.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/cli/parse.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/entities.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/html_blocks.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/html_re.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/normalize_url.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/common/utils.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/helpers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_destination.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_label.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_title.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/main.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/parser_block.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/parser_core.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/parser_inline.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/port.yaml create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/presets/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/presets/commonmark.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/presets/default.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/presets/zero.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/py.typed create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/renderer.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/ruler.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/blockquote.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/code.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/fence.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/heading.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/hr.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/html_block.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/lheading.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/list.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/paragraph.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/reference.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/state_block.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_block/table.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/block.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/inline.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/linkify.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/normalize.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/replacements.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/smartquotes.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/state_core.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_core/text_join.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/autolink.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/backticks.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/balance_pairs.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/emphasis.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/entity.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/escape.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/fragments_join.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/html_inline.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/image.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/link.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/linkify.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/newline.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/state_inline.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/strikethrough.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/rules_inline/text.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/token.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/tree.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it/utils.py create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/markdown_it_py-4.2.0.dist-info/licenses/LICENSE.markdown-it create mode 100644 .venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/mdurl-0.1.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/mdurl/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/_decode.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/_encode.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/_format.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/_parse.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/_url.py create mode 100644 .venv/lib/python3.12/site-packages/mdurl/py.typed create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/multidict-6.7.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/multidict/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/multidict/_abc.py create mode 100644 .venv/lib/python3.12/site-packages/multidict/_compat.py create mode 100755 .venv/lib/python3.12/site-packages/multidict/_multidict.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/multidict/_multidict_py.py create mode 100644 .venv/lib/python3.12/site-packages/multidict/py.typed create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/licenses/LICENSE.APACHE create mode 100644 .venv/lib/python3.12/site-packages/packaging-26.2.dist-info/licenses/LICENSE.BSD create mode 100644 .venv/lib/python3.12/site-packages/packaging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_elffile.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_manylinux.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_musllinux.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_structures.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/_tokenizer.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/dependency_groups.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/direct_url.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/errors.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/licenses/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/licenses/_spdx.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/markers.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/py.typed create mode 100644 .venv/lib/python3.12/site-packages/packaging/pylock.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/specifiers.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/tags.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/utils.py create mode 100644 .venv/lib/python3.12/site-packages/packaging/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/AUTHORS.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip-26.1.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/__pip-runner__.py create mode 100755 .venv/lib/python3.12/site-packages/pip/_internal/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/build_env.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/main.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/download.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/help.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/index.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/install.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/list.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/lock.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/search.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/show.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/configuration.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/collector.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/sources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/main.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/index.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/link.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/release_control.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/download.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/session.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/pyproject.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/pep723.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_dependency_group.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/pylock.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/README.rst create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/LICENSE.md create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/COPYING create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.APACHE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/LICENSE.BSD create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/dependency_groups.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/direct_url.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/pylock.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/measure.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/pager.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/palette.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/region.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/rule.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/scope.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/screen.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/styled.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/themes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_types.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/_writer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli_w/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_ssl_constants.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_base_connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_request_methods.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/emscripten_fetch_worker.js create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/fetch.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/request.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/emscripten/response.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/http2/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/http2/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/http2/probe.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/response.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pluggy-1.6.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/pluggy/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_callers.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_hooks.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_manager.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_result.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_tracing.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_version.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/_warnings.py create mode 100644 .venv/lib/python3.12/site-packages/pluggy/py.typed create mode 100644 .venv/lib/python3.12/site-packages/py.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pycparser-3.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/pycparser/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/_ast_gen.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/_c_ast.cfg create mode 100644 .venv/lib/python3.12/site-packages/pycparser/ast_transforms.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/c_ast.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/c_generator.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/c_lexer.py create mode 100644 .venv/lib/python3.12/site-packages/pycparser/c_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pydantic/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_config.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_core_metadata.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_core_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_dataclasses.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_decorators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_decorators_v1.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_discriminated_union.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_docs_extraction.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_fields.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_forward_ref.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_generics.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_git.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_import_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_internal_dataclass.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_known_annotated_metadata.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_mock_val_ser.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_namespace_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_repr.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_schema_gather.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_schema_generation_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_serializers.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_signature.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_typing_extra.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_validate_call.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_internal/_validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/_migration.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/alias_generators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/aliases.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/annotated_handlers.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/class_validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/color.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/config.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/dataclasses.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/datetime_parse.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/decorator.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/class_validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/config.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/copy_internals.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/decorator.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/json.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/parse.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/deprecated/tools.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/env_settings.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/error_wrappers.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/experimental/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/experimental/arguments_schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/experimental/missing_sentinel.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/experimental/pipeline.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/fields.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/functional_serializers.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/functional_validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/generics.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/json.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/json_schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/main.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/mypy.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/networks.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/parse.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/plugin/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/plugin/_loader.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/plugin/_schema_validator.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pydantic/root_model.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/tools.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/type_adapter.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/types.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/typing.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/_hypothesis_plugin.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/annotated_types.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/class_validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/color.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/config.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/dataclasses.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/datetime_parse.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/decorator.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/env_settings.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/error_wrappers.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/fields.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/generics.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/json.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/main.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/mypy.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/networks.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/parse.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/tools.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/types.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/typing.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/v1/version.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/validate_call_decorator.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/validators.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/version.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic/warnings.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core-2.46.4.dist-info/sboms/pydantic-core.cyclonedx.json create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core/__init__.py create mode 100755 .venv/lib/python3.12/site-packages/pydantic_core/_pydantic_core.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core/_pydantic_core.pyi create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core/core_schema.py create mode 100644 .venv/lib/python3.12/site-packages/pydantic_core/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/AUTHORS create mode 100644 .venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pygments/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/cmdline.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/console.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/filter.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/filters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatter.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/bbcode.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/groff.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/html.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/img.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/irc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/latex.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/other.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/pangomarkup.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/rtf.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/svg.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/terminal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/formatters/terminal256.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexer.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_ada_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_asy_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_cl_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_cocoa_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_csound_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_css_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_googlesql_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_julia_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_lasso_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_lilypond_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_lua_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_luau_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_mql_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_mysql_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_openedge_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_php_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_postgres_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_qlik_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_scheme_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_scilab_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_sourcemod_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_sql_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_stan_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_stata_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_tsql_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_usd_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_vbscript_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/_vim_builtins.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/actionscript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ada.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/agile.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/algebra.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ambient.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/amdgpu.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ampl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/apdlexer.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/apl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/archetype.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/arrow.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/arturo.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/asc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/asm.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/asn1.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/automation.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/bare.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/basic.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/bdd.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/berry.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/bibtex.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/blueprint.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/boa.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/bqn.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/business.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/c_cpp.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/c_like.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/capnproto.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/carbon.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/cddl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/chapel.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/clean.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/codeql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/comal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/compiled.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/configs.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/console.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/cplint.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/crystal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/csound.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/css.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/d.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dalvik.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/data.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dax.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/devicetree.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/diff.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dns.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dotnet.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dsls.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/dylan.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ecl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/eiffel.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/elm.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/elpi.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/email.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/erlang.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/esoteric.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ezhil.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/factor.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/fantom.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/felix.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/fift.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/floscript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/forth.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/fortran.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/foxpro.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/freefem.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/func.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/functional.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/futhark.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/gcodelexer.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/gdscript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/gleam.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/go.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/grammar_notation.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/graph.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/graphics.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/graphql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/graphviz.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/gsql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/hare.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/haskell.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/haxe.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/hdl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/hexdump.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/html.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/idl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/igor.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/inferno.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/installers.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/int_fiction.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/iolang.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/j.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/javascript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/jmespath.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/jslt.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/json5.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/jsonnet.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/jsx.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/julia.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/jvm.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/kuin.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/kusto.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ldap.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/lean.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/lilypond.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/lisp.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/macaulay2.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/make.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/maple.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/markup.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/math.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/matlab.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/maxima.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/meson.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/mime.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/minecraft.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/mips.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ml.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/modeling.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/modula2.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/mojo.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/monte.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/mosel.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ncl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/nimrod.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/nit.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/nix.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/numbair.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/oberon.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/objective.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ooc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/openscad.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/other.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/parasail.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/parsers.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/pascal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/pawn.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/pddl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/perl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/phix.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/php.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/pointless.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/pony.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/praat.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/procfile.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/prolog.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/promql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/prql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ptx.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/python.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/q.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/qlik.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/qvt.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/r.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rdf.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rebol.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rego.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rell.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/resource.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ride.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rita.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rnc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/roboconf.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/robotframework.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ruby.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/rust.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/sas.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/savi.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/scdoc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/scripting.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/sgf.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/shell.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/sieve.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/slash.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/smalltalk.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/smithy.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/smv.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/snobol.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/solidity.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/soong.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/sophia.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/special.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/spice.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/sql.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/srcinfo.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/stata.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/supercollider.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tablegen.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tact.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tcl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/teal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/templates.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/teraterm.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/testing.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/text.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/textedit.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/textfmts.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/theorem.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/thingsdb.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tlb.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tls.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/tnt.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/trafficscript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/typoscript.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/typst.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/ul4.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/unicon.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/urbi.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/usd.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/varnish.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/verification.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/verifpal.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/vip.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/vyper.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/web.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/webassembly.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/webidl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/webmisc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/wgsl.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/whiley.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/wowtoc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/wren.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/x10.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/xorg.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/yang.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/yara.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/lexers/zig.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/modeline.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/plugin.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/regexopt.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/scanner.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/sphinxext.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/style.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/abap.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/algol.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/algol_nu.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/arduino.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/autumn.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/borland.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/bw.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/coffee.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/colorful.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/default.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/dracula.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/emacs.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/friendly.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/friendly_grayscale.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/fruity.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/gh_dark.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/gruvbox.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/igor.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/inkpot.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/lightbulb.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/lilypond.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/lovelace.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/manni.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/material.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/monokai.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/murphy.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/native.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/nord.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/onedark.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/paraiso_dark.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/paraiso_light.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/pastie.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/perldoc.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/rainbow_dash.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/rrt.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/sas.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/solarized.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/staroffice.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/stata_dark.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/stata_light.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/tango.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/trac.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/vim.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/vs.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/xcode.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/styles/zenburn.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/token.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/unistring.py create mode 100644 .venv/lib/python3.12/site-packages/pygments/util.py create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pytest-9.0.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/pytest/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pytest/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pytest/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio-1.4.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio/plugin.py create mode 100644 .venv/lib/python3.12/site-packages/pytest_asyncio/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/licenses/NOTICE create mode 100644 .venv/lib/python3.12/site-packages/requests-2.34.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/requests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/requests/__version__.py create mode 100644 .venv/lib/python3.12/site-packages/requests/_internal_utils.py create mode 100644 .venv/lib/python3.12/site-packages/requests/_types.py create mode 100644 .venv/lib/python3.12/site-packages/requests/adapters.py create mode 100644 .venv/lib/python3.12/site-packages/requests/api.py create mode 100644 .venv/lib/python3.12/site-packages/requests/auth.py create mode 100644 .venv/lib/python3.12/site-packages/requests/certs.py create mode 100644 .venv/lib/python3.12/site-packages/requests/compat.py create mode 100644 .venv/lib/python3.12/site-packages/requests/cookies.py create mode 100644 .venv/lib/python3.12/site-packages/requests/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/requests/help.py create mode 100644 .venv/lib/python3.12/site-packages/requests/hooks.py create mode 100644 .venv/lib/python3.12/site-packages/requests/models.py create mode 100644 .venv/lib/python3.12/site-packages/requests/packages.py create mode 100644 .venv/lib/python3.12/site-packages/requests/py.typed create mode 100644 .venv/lib/python3.12/site-packages/requests/sessions.py create mode 100644 .venv/lib/python3.12/site-packages/requests/status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/requests/structures.py create mode 100644 .venv/lib/python3.12/site-packages/requests/utils.py create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/rich-15.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/rich/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/rich/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_emoji_codes.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_emoji_replace.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_export_format.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_extension.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_fileno.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_inspect.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_log_render.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_loop.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_null_file.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_palettes.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_pick.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_ratio.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_spinners.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_stack.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_timer.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/_versions.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode10-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode11-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode12-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode12-1-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode13-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode14-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode15-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode15-1-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode16-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode17-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode4-1-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode5-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode5-1-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode5-2-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode6-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode6-1-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode6-2-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode6-3-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode7-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode8-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_unicode_data/unicode9-0-0.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_win32_console.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_windows.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_windows_renderer.py create mode 100644 .venv/lib/python3.12/site-packages/rich/_wrap.py create mode 100644 .venv/lib/python3.12/site-packages/rich/abc.py create mode 100644 .venv/lib/python3.12/site-packages/rich/align.py create mode 100644 .venv/lib/python3.12/site-packages/rich/ansi.py create mode 100644 .venv/lib/python3.12/site-packages/rich/bar.py create mode 100644 .venv/lib/python3.12/site-packages/rich/box.py create mode 100644 .venv/lib/python3.12/site-packages/rich/cells.py create mode 100644 .venv/lib/python3.12/site-packages/rich/color.py create mode 100644 .venv/lib/python3.12/site-packages/rich/color_triplet.py create mode 100644 .venv/lib/python3.12/site-packages/rich/columns.py create mode 100644 .venv/lib/python3.12/site-packages/rich/console.py create mode 100644 .venv/lib/python3.12/site-packages/rich/constrain.py create mode 100644 .venv/lib/python3.12/site-packages/rich/containers.py create mode 100644 .venv/lib/python3.12/site-packages/rich/control.py create mode 100644 .venv/lib/python3.12/site-packages/rich/default_styles.py create mode 100644 .venv/lib/python3.12/site-packages/rich/diagnose.py create mode 100644 .venv/lib/python3.12/site-packages/rich/emoji.py create mode 100644 .venv/lib/python3.12/site-packages/rich/errors.py create mode 100644 .venv/lib/python3.12/site-packages/rich/file_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/rich/filesize.py create mode 100644 .venv/lib/python3.12/site-packages/rich/highlighter.py create mode 100644 .venv/lib/python3.12/site-packages/rich/json.py create mode 100644 .venv/lib/python3.12/site-packages/rich/jupyter.py create mode 100644 .venv/lib/python3.12/site-packages/rich/layout.py create mode 100644 .venv/lib/python3.12/site-packages/rich/live.py create mode 100644 .venv/lib/python3.12/site-packages/rich/live_render.py create mode 100644 .venv/lib/python3.12/site-packages/rich/logging.py create mode 100644 .venv/lib/python3.12/site-packages/rich/markdown.py create mode 100644 .venv/lib/python3.12/site-packages/rich/markup.py create mode 100644 .venv/lib/python3.12/site-packages/rich/measure.py create mode 100644 .venv/lib/python3.12/site-packages/rich/padding.py create mode 100644 .venv/lib/python3.12/site-packages/rich/pager.py create mode 100644 .venv/lib/python3.12/site-packages/rich/palette.py create mode 100644 .venv/lib/python3.12/site-packages/rich/panel.py create mode 100644 .venv/lib/python3.12/site-packages/rich/pretty.py create mode 100644 .venv/lib/python3.12/site-packages/rich/progress.py create mode 100644 .venv/lib/python3.12/site-packages/rich/progress_bar.py create mode 100644 .venv/lib/python3.12/site-packages/rich/prompt.py create mode 100644 .venv/lib/python3.12/site-packages/rich/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/rich/py.typed create mode 100644 .venv/lib/python3.12/site-packages/rich/region.py create mode 100644 .venv/lib/python3.12/site-packages/rich/repr.py create mode 100644 .venv/lib/python3.12/site-packages/rich/rule.py create mode 100644 .venv/lib/python3.12/site-packages/rich/scope.py create mode 100644 .venv/lib/python3.12/site-packages/rich/screen.py create mode 100644 .venv/lib/python3.12/site-packages/rich/segment.py create mode 100644 .venv/lib/python3.12/site-packages/rich/spinner.py create mode 100644 .venv/lib/python3.12/site-packages/rich/status.py create mode 100644 .venv/lib/python3.12/site-packages/rich/style.py create mode 100644 .venv/lib/python3.12/site-packages/rich/styled.py create mode 100644 .venv/lib/python3.12/site-packages/rich/syntax.py create mode 100644 .venv/lib/python3.12/site-packages/rich/table.py create mode 100644 .venv/lib/python3.12/site-packages/rich/terminal_theme.py create mode 100644 .venv/lib/python3.12/site-packages/rich/text.py create mode 100644 .venv/lib/python3.12/site-packages/rich/theme.py create mode 100644 .venv/lib/python3.12/site-packages/rich/themes.py create mode 100644 .venv/lib/python3.12/site-packages/rich/traceback.py create mode 100644 .venv/lib/python3.12/site-packages/rich/tree.py create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/sanic/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/__version__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/app.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/constants.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/ext.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/logo.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/motd.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/spinner.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/application/state.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/asgi.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/base/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/base/meta.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/base/root.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/blueprint_group.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/blueprints.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/app.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/arguments.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/base.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/console.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/daemon.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/executor.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/inspector.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cli/inspector_client.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/compat.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/config.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/constants.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cookies/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cookies/request.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/cookies/response.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/errorpages.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/handlers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/handlers/content_range.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/handlers/directory.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/handlers/error.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/headers.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/helpers.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/constants.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/http1.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/http3.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/stream.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/tls/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/tls/context.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/http/tls/creators.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/log.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/color.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/default.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/deprecation.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/filter.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/formatter.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/loggers.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/logging/setup.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/middleware.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/base.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/commands.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/listeners.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/middleware.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/routes.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/signals.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/startup.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/mixins/static.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/asgi.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/ctx_types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/futures.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/handler_types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/http_types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/protocol_types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/models/server_types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/base.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/css.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/directory_page.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/error.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/styles/BasePage.css create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/styles/DirectoryPage.css create mode 100644 .venv/lib/python3.12/site-packages/sanic/pages/styles/ErrorPage.css create mode 100644 .venv/lib/python3.12/site-packages/sanic/py.typed create mode 100644 .venv/lib/python3.12/site-packages/sanic/request/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/request/form.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/request/parameters.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/request/types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/response/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/response/convenience.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/response/types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/router.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/async_server.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/events.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/goodbye.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/loop.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/protocols/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/protocols/base_protocol.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/protocols/http_protocol.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/protocols/websocket_protocol.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/runners.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/socket.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/websockets/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/websockets/connection.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/websockets/frame.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/server/websockets/impl.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/signals.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/simple.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/startup/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/startup/errors.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/meta.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/schemes/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/schemes/altsvc.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/schemes/base.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/schemes/ode.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/touchup/service.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/types/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/types/hashable_dict.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/types/shared_ctx.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/utils.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/views.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/constants.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/daemon.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/inspector.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/loader.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/manager.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/multiplexer.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/process.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/reloader.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/restarter.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/serve.py create mode 100644 .venv/lib/python3.12/site-packages/sanic/worker/state.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/bootstrap.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/config.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/base.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/health/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/health/endpoint.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/health/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/health/monitor.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/http/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/http/cors.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/http/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/http/methods.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/constructor.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/injector.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/registry.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extractor.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/logger.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/autodoc.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/blueprint.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/builders.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/constants.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/definitions.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/openapi.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/types.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/redoc.html create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/swagger.html create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/engine.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/extension.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/render.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/request.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/decorator.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/check.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/clean.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/decorator.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/schema.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/setup.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/extras/validation/validators.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/py.typed create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/extraction.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/route.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/string.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/typing.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_ext/utils/version.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/group.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/line.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/patterns.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/py.typed create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/route.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/router.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/tree.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_routing/utils.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing/manager.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing/reusable.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing/testing.py create mode 100644 .venv/lib/python3.12/site-packages/sanic_testing/websocket.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_core_metadata.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_discovery.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/_log.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/_macos_compat.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/_modified.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/archive_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/cmd.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/_framework_compat.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/build.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/check.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/clean.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/config.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compat/numpy.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compat/py39.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/core.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/debug.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/dep_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/dir_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/dist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/errors.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/extension.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/file_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/filelist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/log.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/spawn.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/py39.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/support.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_archive_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_dumb.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_rpm.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_clib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_ext.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_py.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_check.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_clean.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_cmd.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_config_cmd.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_core.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dir_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_extension.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_file_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_filelist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_data.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_headers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_lib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_log.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_modified.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_spawn.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sysconfig.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_text_file.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_version.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_versionpredicate.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/tests/unix_compat.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/text_file.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/version.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_distutils/zosccompiler.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_entry_points.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_imp.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_importlib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_itertools.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_normalization.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_path.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_reqs.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_shutil.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_static.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/.lock create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/py38.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_collections.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_compat.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_functools.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_meta.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_text.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_typing.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py311.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py39.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/diagnose.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/Lorem ipsum.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/layouts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/show-newlines.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/strip-prefix.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-dvorak.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-qwerty.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.pyi create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.pyi create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.pyi create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_elffile.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_tokenizer.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/_spdx.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/pylock.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/android.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/api.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/macos.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/unix.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/version.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/windows.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_types.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/py.typed create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_bdist_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/convert.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/pack.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/tags.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/unpack.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_metadata.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_setuptools_logging.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/bdist_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/macosx_libfile.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/wheelfile.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/_functools.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/overlay.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py310.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py313.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/glob.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/archive_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/build_meta.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/cli-32.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/cli-64.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/cli.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/_requirestxt.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/alias.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/bdist_egg.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/build.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/build_clib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/build_ext.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/build_py.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/develop.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/dist_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/easy_install.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/editable_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/egg_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/install.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/install_egg_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/install_lib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/install_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/rotate.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/saveopts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/sdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/setopt.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/command/test.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/compat/py310.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/compat/py311.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/compat/py312.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/compat/py39.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/NOTICE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_apply_pyprojecttoml.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/NOTICE create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/error_reporting.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/extra_validations.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/formats.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/distutils.schema.json create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/expand.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/setupcfg.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/config/setuptools.schema.json create mode 100644 .venv/lib/python3.12/site-packages/setuptools/depends.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/discovery.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/dist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/errors.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/extension.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/glob.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/gui-32.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/gui-64.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/gui-arm64.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/gui.exe create mode 100644 .venv/lib/python3.12/site-packages/setuptools/installer.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/launch.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/launcher manifest.xml create mode 100644 .venv/lib/python3.12/site-packages/setuptools/logging.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/modified.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/monkey.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/msvc.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/namespaces.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl create mode 100644 .venv/lib/python3.12/site-packages/setuptools/script.tmpl create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/compat/py39.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/contexts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/environment.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/fixtures.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/integration/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/namespaces.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_build.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_build_py.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_depends.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_develop.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_dist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_extern.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_glob.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_logging.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_manifest.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_scripts.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_sdist.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_setopt.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_warnings.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/text.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/tests/textwrap.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/unicode_utils.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/version.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/warnings.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/setuptools/windows_support.py create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/six.py create mode 100644 .venv/lib/python3.12/site-packages/src/ujson/python/version.h create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/srp/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/srp/_ctsrp.py create mode 100644 .venv/lib/python3.12/site-packages/srp/_pysrp.py create mode 100644 .venv/lib/python3.12/site-packages/srp/doc/conf.py create mode 100644 .venv/lib/python3.12/site-packages/srp/doc/index.rst create mode 100644 .venv/lib/python3.12/site-packages/srp/doc/srp.rst create mode 100644 .venv/lib/python3.12/site-packages/srp/test_srp.py create mode 100644 .venv/lib/python3.12/site-packages/tests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/conftest.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/http/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/http/test_methods.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/test_add_dependency.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/test_constants.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/test_dependency.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_config.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_registry.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/logging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/logging/test_custom_background_logger.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_autodoc.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_body.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_config.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_decorators.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_definitions.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_deprecated.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_exclude.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_external_docs.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_func_handler.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_fields.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_spec.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_parameter.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_paths.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_schema.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_security.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_specification.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_summary.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_tag.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/test_typing.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/openapi/utils.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/templating/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/templating/templates/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/templating/test_templating.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extensions/test_startup.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/__models__.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_parse_hint.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_request_counted.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_serializer.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation_attrs.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation_dataclass.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation_msgspec.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation_multiple.py create mode 100644 .venv/lib/python3.12/site-packages/tests/extra/test_validation_pydantic.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/tracerite/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/chain_analysis.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/fastapi.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/html.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/inspector.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/logging.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/notebook.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/script.js create mode 100644 .venv/lib/python3.12/site-packages/tracerite/style.css create mode 100644 .venv/lib/python3.12/site-packages/tracerite/syntaxerror.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/trace.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/trace_cpy.py create mode 100644 .venv/lib/python3.12/site-packages/tracerite/tty.py create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/typing_extensions.py create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection/introspection.py create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection/py.typed create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py create mode 100644 .venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/ujson-stubs/__init__.pyi create mode 100755 .venv/lib/python3.12/site-packages/ujson.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.7.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.7.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.7.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.7.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.7.0.dist-info/licenses/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/urllib3/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/_base_connection.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/_collections.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/_request_methods.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/_version.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/connection.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/fields.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/filepost.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/http2/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/http2/connection.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/http2/probe.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/py.typed create mode 100644 .venv/lib/python3.12/site-packages/urllib3/response.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/connection.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/request.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/response.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/retry.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/url.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/util.py create mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/wait.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/licenses/LICENSE-APACHE create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/licenses/LICENSE-MIT create mode 100644 .venv/lib/python3.12/site-packages/uvloop-0.22.1.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/uvloop/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop/_noop.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop/_testbase.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop/_version.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop/cbhandles.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/cbhandles.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/dns.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/errors.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/async_.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/async_.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/basetransport.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/basetransport.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/check.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/check.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/fsevent.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/fsevent.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/handle.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/handle.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/idle.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/idle.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/pipe.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/pipe.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/poll.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/poll.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/process.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/process.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/stream.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/stream.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/streamserver.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/streamserver.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/tcp.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/tcp.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/timer.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/timer.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/udp.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/handles/udp.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/consts.pxi create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/debug.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/flowcontrol.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/python.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/stdlib.pxi create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/system.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/includes/uv.pxd create mode 100755 .venv/lib/python3.12/site-packages/uvloop/loop.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/uvloop/loop.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/loop.pyi create mode 100644 .venv/lib/python3.12/site-packages/uvloop/loop.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/lru.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/pseudosock.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/py.typed create mode 100644 .venv/lib/python3.12/site-packages/uvloop/request.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/request.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/server.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/server.pyx create mode 100644 .venv/lib/python3.12/site-packages/uvloop/sslproto.pxd create mode 100644 .venv/lib/python3.12/site-packages/uvloop/sslproto.pyx create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/websockets-16.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/websockets/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/async_timeout.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/client.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/compatibility.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/connection.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/messages.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/router.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/asyncio/server.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/auth.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/cli.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/client.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/connection.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/datastructures.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/extensions/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/extensions/base.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/extensions/permessage_deflate.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/frames.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/headers.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/http.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/http11.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/imports.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/auth.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/client.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/framing.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/handshake.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/http.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/legacy/server.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/proxy.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/py.typed create mode 100644 .venv/lib/python3.12/site-packages/websockets/server.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/speedups.c create mode 100755 .venv/lib/python3.12/site-packages/websockets/speedups.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/websockets/speedups.pyi create mode 100644 .venv/lib/python3.12/site-packages/websockets/streams.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/client.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/connection.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/messages.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/router.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/server.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/sync/utils.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/typing.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/uri.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/utils.py create mode 100644 .venv/lib/python3.12/site-packages/websockets/version.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/__init__.py create mode 100755 .venv/lib/python3.12/site-packages/yaml/_yaml.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/yaml/composer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/constructor.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/cyaml.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/dumper.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/emitter.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/error.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/events.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/loader.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/nodes.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/parser.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/reader.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/representer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/scanner.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/serializer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/tokens.py create mode 120000 .venv/lib64 create mode 100644 .venv/pyvenv.cfg rename {coven => hh}/Cargo.lock (99%) rename {coven => hh}/Cargo.toml (81%) rename {coven => hh}/README.md (56%) rename {coven => hh}/src/crypto.rs (100%) rename {coven => hh}/src/main.rs (93%) rename {coven => hh}/tools/gen_vectors.py (100%) diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 0000000..becd02a --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then + # transform D:\path\to\venv to /d/path/to/venv on MSYS + # and to /cygdrive/d/path/to/venv on Cygwin + export VIRTUAL_ENV=$(cygpath /home/dell/coding/learning/cmd-chat/.venv) +else + # use the path as-is + export VIRTUAL_ENV=/home/dell/coding/learning/cmd-chat/.venv +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"bin":$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1='(.venv) '"${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT='(.venv) ' + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 0000000..f67318e --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV /home/dell/coding/learning/cmd-chat/.venv + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/"bin":$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = '(.venv) '"$prompt" + setenv VIRTUAL_ENV_PROMPT '(.venv) ' +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 0000000..b6b56d3 --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV /home/dell/coding/learning/cmd-chat/.venv + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/"bin $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) '(.venv) ' (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT '(.venv) ' +end diff --git a/.venv/bin/httpx b/.venv/bin/httpx new file mode 100755 index 0000000..39ddca7 --- /dev/null +++ b/.venv/bin/httpx @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from httpx import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/bin/idna b/.venv/bin/idna new file mode 100755 index 0000000..4311358 --- /dev/null +++ b/.venv/bin/idna @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from idna.cli import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/bin/markdown-it b/.venv/bin/markdown-it new file mode 100755 index 0000000..823c8d4 --- /dev/null +++ b/.venv/bin/markdown-it @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from markdown_it.cli.parse import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer new file mode 100755 index 0000000..f5bb08a --- /dev/null +++ b/.venv/bin/normalizer @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from charset_normalizer.cli import cli_detect +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(cli_detect()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 0000000..451e201 --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 0000000..451e201 --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.12 b/.venv/bin/pip3.12 new file mode 100755 index 0000000..451e201 --- /dev/null +++ b/.venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/py.test b/.venv/bin/py.test new file mode 100755 index 0000000..2fe52ad --- /dev/null +++ b/.venv/bin/py.test @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(console_main()) diff --git a/.venv/bin/pygmentize b/.venv/bin/pygmentize new file mode 100755 index 0000000..d0d43a7 --- /dev/null +++ b/.venv/bin/pygmentize @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/bin/pytest b/.venv/bin/pytest new file mode 100755 index 0000000..2fe52ad --- /dev/null +++ b/.venv/bin/pytest @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from pytest import console_main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(console_main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/python3.12 b/.venv/bin/python3.12 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python3.12 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/sanic b/.venv/bin/sanic new file mode 100755 index 0000000..383fe05 --- /dev/null +++ b/.venv/bin/sanic @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from sanic.__main__ import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/bin/websockets b/.venv/bin/websockets new file mode 100755 index 0000000..9f9a4e9 --- /dev/null +++ b/.venv/bin/websockets @@ -0,0 +1,6 @@ +#!/home/dell/coding/learning/cmd-chat/.venv/bin/python3 +import sys +from websockets.cli import main +if __name__ == '__main__': + sys.argv[0] = sys.argv[0].removesuffix('.exe') + sys.exit(main()) diff --git a/.venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..83590cfe20797a86c79e0516414f52fc91b1acf1 GIT binary patch literal 433312 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 AZ~y=R literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..156ee43188df643d2639fdd256da4eb46db27dd9 GIT binary patch literal 348808 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%0Aw++4~(v&d?L<&vLhQT?V<%Ca@hS>%= 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�%m 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 VR+@(~_;1b?-v7RkV;Ro%{{WKxw(S4_ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/_distutils_hack/__init__.py b/.venv/lib/python3.12/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..94f71b9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,239 @@ +# don't import any costly modules +import os +import sys + +report_url = ( + "https://github.com/pypa/setuptools/issues/new?template=distutils-deprecation.yml" +) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + 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. Support for replacing " + "an already imported distutils is deprecated. In the future, " + "this condition will fail. " + f"Register concerns at {report_url}" + ) + 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') + if which == 'stdlib': + import warnings + + warnings.warn( + "Reliance on distutils from stdlib is deprecated. Users " + "must rely on setuptools to provide the distutils module. " + "Avoid importing distutils or import setuptools first, " + "and avoid setting SETUPTOOLS_USE_DISTUTILS=stdlib. " + f"Register concerns at {report_url}" + ) + 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) -> None: + 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 None + + 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 None + + 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 None + + 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 sys.version_info >= (3, 12) or 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) -> None: + insert_shim() + + def __exit__(self, exc: object, value: object, tb: object) -> None: + _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 + + +if sys.version_info < (3, 12): + # DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632) + remove_shim = _remove_shim diff --git a/.venv/lib/python3.12/site-packages/_distutils_hack/override.py b/.venv/lib/python3.12/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000..2cc433a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/.venv/lib/python3.12/site-packages/_pytest/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/__init__.py new file mode 100644 index 0000000..8eb8ec9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + + +__all__ = ["__version__", "version_tuple"] + +try: + from ._version import version as __version__ + from ._version import version_tuple +except ImportError: # pragma: no cover + # broken installation, we don't even try + # unknown only works because we do poor mans version compare + __version__ = "unknown" + version_tuple = (0, 0, "unknown") diff --git a/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py b/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py new file mode 100644 index 0000000..59426ef --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py @@ -0,0 +1,117 @@ +"""Allow bash-completion for argparse with argcomplete if installed. + +Needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail +to find the magic string, so _ARGCOMPLETE env. var is never set, and +this does not need special code). + +Function try_argcomplete(parser) should be called directly before +the call to ArgumentParser.parse_args(). + +The filescompleter is what you normally would use on the positional +arguments specification, in order to get "dirname/" after "dirn" +instead of the default "dirname ": + + optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter + +Other, application specific, completers should go in the file +doing the add_argument calls as they need to be specified as .completer +attributes as well. (If argcomplete is not installed, the function the +attribute points to will not be used). + +SPEEDUP +======= + +The generic argcomplete script for bash-completion +(/etc/bash_completion.d/python-argcomplete.sh) +uses a python program to determine startup script generated by pip. +You can speed up completion somewhat by changing this script to include + # PYTHON_ARGCOMPLETE_OK +so the python-argcomplete-check-easy-install-script does not +need to be called to find the entry point of the code and see if that is +marked with PYTHON_ARGCOMPLETE_OK. + +INSTALL/DEBUGGING +================= + +To include this support in another application that has setup.py generated +scripts: + +- Add the line: + # PYTHON_ARGCOMPLETE_OK + near the top of the main python entry point. + +- Include in the file calling parse_args(): + from _argcomplete import try_argcomplete, filescompleter + Call try_argcomplete just before parse_args(), and optionally add + filescompleter to the positional arguments' add_argument(). + +If things do not work right away: + +- Switch on argcomplete debugging with (also helpful when doing custom + completers): + export _ARC_DEBUG=1 + +- Run: + python-argcomplete-check-easy-install-script $(which appname) + echo $? + will echo 0 if the magic line has been found, 1 if not. + +- Sometimes it helps to find early on errors using: + _ARGCOMPLETE=1 _ARC_DEBUG=1 appname + which should throw a KeyError: 'COMPLINE' (which is properly set by the + global argcomplete script). +""" + +from __future__ import annotations + +import argparse +from glob import glob +import os +import sys +from typing import Any + + +class FastFilesCompleter: + """Fast file completer class.""" + + def __init__(self, directories: bool = True) -> None: + self.directories = directories + + def __call__(self, prefix: str, **kwargs: Any) -> list[str]: + # Only called on non option completions. + if os.sep in prefix[1:]: + prefix_dir = len(os.path.dirname(prefix) + os.sep) + else: + prefix_dir = 0 + completion = [] + globbed = [] + if "*" not in prefix and "?" not in prefix: + # We are on unix, otherwise no bash. + if not prefix or prefix[-1] == os.sep: + globbed.extend(glob(prefix + ".*")) + prefix += "*" + globbed.extend(glob(prefix)) + for x in sorted(globbed): + if os.path.isdir(x): + x += "/" + # Append stripping the prefix (like bash, not like compgen). + completion.append(x[prefix_dir:]) + return completion + + +if os.environ.get("_ARGCOMPLETE"): + try: + import argcomplete.completers + except ImportError: + sys.exit(-1) + filescompleter: FastFilesCompleter | None = FastFilesCompleter() + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + argcomplete.autocomplete(parser, always_complete_options=False) + +else: + + def try_argcomplete(parser: argparse.ArgumentParser) -> None: + pass + + filescompleter = None diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py new file mode 100644 index 0000000..7f67a2e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py @@ -0,0 +1,26 @@ +"""Python inspection/code generation API.""" + +from __future__ import annotations + +from .code import Code +from .code import ExceptionInfo +from .code import filter_traceback +from .code import Frame +from .code import getfslineno +from .code import Traceback +from .code import TracebackEntry +from .source import getrawcode +from .source import Source + + +__all__ = [ + "Code", + "ExceptionInfo", + "Frame", + "Source", + "Traceback", + "TracebackEntry", + "filter_traceback", + "getfslineno", + "getrawcode", +] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/code.py b/.venv/lib/python3.12/site-packages/_pytest/_code/code.py new file mode 100644 index 0000000..4cf99a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_code/code.py @@ -0,0 +1,1571 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import ast +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import inspect +from inspect import CO_VARARGS +from inspect import CO_VARKEYWORDS +from io import StringIO +import os +from pathlib import Path +import re +import sys +from traceback import extract_tb +from traceback import format_exception +from traceback import format_exception_only +from traceback import FrameSummary +from types import CodeType +from types import FrameType +from types import TracebackType +from typing import Any +from typing import ClassVar +from typing import Final +from typing import final +from typing import Generic +from typing import Literal +from typing import overload +from typing import SupportsIndex +from typing import TypeAlias +from typing import TypeVar + +import pluggy + +import _pytest +from _pytest._code.source import findsource +from _pytest._code.source import getrawcode +from _pytest._code.source import getstatementrange_ast +from _pytest._code.source import Source +from _pytest._io import TerminalWriter +from _pytest._io.saferepr import safeformat +from _pytest._io.saferepr import saferepr +from _pytest.compat import get_real_func +from _pytest.deprecated import check_ispytest +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + +TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] + +EXCEPTION_OR_MORE = type[BaseException] | tuple[type[BaseException], ...] + + +class Code: + """Wrapper around Python code objects.""" + + __slots__ = ("raw",) + + def __init__(self, obj: CodeType) -> None: + self.raw = obj + + @classmethod + def from_function(cls, obj: object) -> Code: + return cls(getrawcode(obj)) + + def __eq__(self, other): + return self.raw == other.raw + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @property + def firstlineno(self) -> int: + return self.raw.co_firstlineno - 1 + + @property + def name(self) -> str: + return self.raw.co_name + + @property + def path(self) -> Path | str: + """Return a path object pointing to source code, or an ``str`` in + case of ``OSError`` / non-existing file.""" + if not self.raw.co_filename: + return "" + try: + p = absolutepath(self.raw.co_filename) + # maybe don't try this checking + if not p.exists(): + raise OSError("path check failed.") + return p + except OSError: + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? + return self.raw.co_filename + + @property + def fullsource(self) -> Source | None: + """Return a _pytest._code.Source object for the full source file of the code.""" + full, _ = findsource(self.raw) + return full + + def source(self) -> Source: + """Return a _pytest._code.Source object for the code object's source only.""" + # return source only for that part of code + return Source(self.raw) + + def getargs(self, var: bool = False) -> tuple[str, ...]: + """Return a tuple with the argument names for the code object. + + If 'var' is set True also return the names of the variable and + keyword arguments when present. + """ + # Handy shortcut for getting args. + raw = self.raw + argcount = raw.co_argcount + if var: + argcount += raw.co_flags & CO_VARARGS + argcount += raw.co_flags & CO_VARKEYWORDS + return raw.co_varnames[:argcount] + + +class Frame: + """Wrapper around a Python frame holding f_locals and f_globals + in which expressions can be evaluated.""" + + __slots__ = ("raw",) + + def __init__(self, frame: FrameType) -> None: + self.raw = frame + + @property + def lineno(self) -> int: + return self.raw.f_lineno - 1 + + @property + def f_globals(self) -> dict[str, Any]: + return self.raw.f_globals + + @property + def f_locals(self) -> dict[str, Any]: + return self.raw.f_locals + + @property + def code(self) -> Code: + return Code(self.raw.f_code) + + @property + def statement(self) -> Source: + """Statement this frame is at.""" + if self.code.fullsource is None: + return Source("") + return self.code.fullsource.getstatement(self.lineno) + + def eval(self, code, **vars): + """Evaluate 'code' in the frame. + + 'vars' are optional additional local variables. + + Returns the result of the evaluation. + """ + f_locals = self.f_locals.copy() + f_locals.update(vars) + return eval(code, self.f_globals, f_locals) + + def repr(self, object: object) -> str: + """Return a 'safe' (non-recursive, one-line) string repr for 'object'.""" + return saferepr(object) + + def getargs(self, var: bool = False): + """Return a list of tuples (name, value) for all arguments. + + If 'var' is set True, also include the variable and keyword arguments + when present. + """ + retval = [] + for arg in self.code.getargs(var): + try: + retval.append((arg, self.f_locals[arg])) + except KeyError: + pass # this can occur when using Psyco + return retval + + +class TracebackEntry: + """A single entry in a Traceback.""" + + __slots__ = ("_rawentry", "_repr_style") + + def __init__( + self, + rawentry: TracebackType, + repr_style: Literal["short", "long"] | None = None, + ) -> None: + self._rawentry: Final = rawentry + self._repr_style: Final = repr_style + + def with_repr_style( + self, repr_style: Literal["short", "long"] | None + ) -> TracebackEntry: + return TracebackEntry(self._rawentry, repr_style) + + @property + def lineno(self) -> int: + return self._rawentry.tb_lineno - 1 + + def get_python_framesummary(self) -> FrameSummary: + # Python's built-in traceback module implements all the nitty gritty + # details to get column numbers of out frames. + stack_summary = extract_tb(self._rawentry, limit=1) + return stack_summary[0] + + # Column and end line numbers introduced in python 3.11 + if sys.version_info < (3, 11): + + @property + def end_lineno_relative(self) -> int | None: + return None + + @property + def colno(self) -> int | None: + return None + + @property + def end_colno(self) -> int | None: + return None + else: + + @property + def end_lineno_relative(self) -> int | None: + frame_summary = self.get_python_framesummary() + if frame_summary.end_lineno is None: # pragma: no cover + return None + return frame_summary.end_lineno - 1 - self.frame.code.firstlineno + + @property + def colno(self) -> int | None: + """Starting byte offset of the expression in the traceback entry.""" + return self.get_python_framesummary().colno + + @property + def end_colno(self) -> int | None: + """Ending byte offset of the expression in the traceback entry.""" + return self.get_python_framesummary().end_colno + + @property + def frame(self) -> Frame: + return Frame(self._rawentry.tb_frame) + + @property + def relline(self) -> int: + return self.lineno - self.frame.code.firstlineno + + def __repr__(self) -> str: + return f"" + + @property + def statement(self) -> Source: + """_pytest._code.Source object for the current statement.""" + source = self.frame.code.fullsource + assert source is not None + return source.getstatement(self.lineno) + + @property + def path(self) -> Path | str: + """Path to the source code.""" + return self.frame.code.path + + @property + def locals(self) -> dict[str, Any]: + """Locals of underlying frame.""" + return self.frame.f_locals + + def getfirstlinesource(self) -> int: + return self.frame.code.firstlineno + + def getsource( + self, astcache: dict[str | Path, ast.AST] | None = None + ) -> Source | None: + """Return failing source code.""" + # we use the passed in astcache to not reparse asttrees + # within exception info printing + source = self.frame.code.fullsource + if source is None: + return None + key = astnode = None + if astcache is not None: + key = self.frame.code.path + if key is not None: + astnode = astcache.get(key, None) + start = self.getfirstlinesource() + try: + astnode, _, end = getstatementrange_ast( + self.lineno, source, astnode=astnode + ) + except SyntaxError: + end = self.lineno + 1 + else: + if key is not None and astcache is not None: + astcache[key] = astnode + return source[start:end] + + source = property(getsource) + + def ishidden(self, excinfo: ExceptionInfo[BaseException] | None) -> bool: + """Return True if the current frame has a var __tracebackhide__ + resolving to True. + + If __tracebackhide__ is a callable, it gets called with the + ExceptionInfo instance and can decide whether to hide the traceback. + + Mostly for internal use. + """ + tbh: bool | Callable[[ExceptionInfo[BaseException] | None], bool] = False + for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): + # in normal cases, f_locals and f_globals are dictionaries + # however via `exec(...)` / `eval(...)` they can be other types + # (even incorrect types!). + # as such, we suppress all exceptions while accessing __tracebackhide__ + try: + tbh = maybe_ns_dct["__tracebackhide__"] + except Exception: + pass + else: + break + if tbh and callable(tbh): + return tbh(excinfo) + return tbh + + def __str__(self) -> str: + name = self.frame.code.name + try: + line = str(self.statement).lstrip() + except KeyboardInterrupt: + raise + except BaseException: + line = "???" + # This output does not quite match Python's repr for traceback entries, + # but changing it to do so would break certain plugins. See + # https://github.com/pytest-dev/pytest/pull/7535/ for details. + return f" File '{self.path}':{self.lineno + 1} in {name}\n {line}\n" + + @property + def name(self) -> str: + """co_name of underlying code.""" + return self.frame.code.raw.co_name + + +class Traceback(list[TracebackEntry]): + """Traceback objects encapsulate and offer higher level access to Traceback entries.""" + + def __init__( + self, + tb: TracebackType | Iterable[TracebackEntry], + ) -> None: + """Initialize from given python traceback object and ExceptionInfo.""" + if isinstance(tb, TracebackType): + + def f(cur: TracebackType) -> Iterable[TracebackEntry]: + cur_: TracebackType | None = cur + while cur_ is not None: + yield TracebackEntry(cur_) + cur_ = cur_.tb_next + + super().__init__(f(tb)) + else: + super().__init__(tb) + + def cut( + self, + path: os.PathLike[str] | str | None = None, + lineno: int | None = None, + firstlineno: int | None = None, + excludepath: os.PathLike[str] | None = None, + ) -> Traceback: + """Return a Traceback instance wrapping part of this Traceback. + + By providing any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined. + + This allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback). + """ + path_ = None if path is None else os.fspath(path) + excludepath_ = None if excludepath is None else os.fspath(excludepath) + for x in self: + code = x.frame.code + codepath = code.path + if path is not None and str(codepath) != path_: + continue + if ( + excludepath is not None + and isinstance(codepath, Path) + and excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator] + ): + continue + if lineno is not None and x.lineno != lineno: + continue + if firstlineno is not None and x.frame.code.firstlineno != firstlineno: + continue + return Traceback(x._rawentry) + return self + + @overload + def __getitem__(self, key: SupportsIndex) -> TracebackEntry: ... + + @overload + def __getitem__(self, key: slice) -> Traceback: ... + + def __getitem__(self, key: SupportsIndex | slice) -> TracebackEntry | Traceback: + if isinstance(key, slice): + return self.__class__(super().__getitem__(key)) + else: + return super().__getitem__(key) + + def filter( + self, + excinfo_or_fn: ExceptionInfo[BaseException] | Callable[[TracebackEntry], bool], + /, + ) -> Traceback: + """Return a Traceback instance with certain items removed. + + If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s + which are hidden (see ishidden() above). + + Otherwise, the filter is a function that gets a single argument, a + ``TracebackEntry`` instance, and should return True when the item should + be added to the ``Traceback``, False when not. + """ + if isinstance(excinfo_or_fn, ExceptionInfo): + fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731 + else: + fn = excinfo_or_fn + return Traceback(filter(fn, self)) + + def recursionindex(self) -> int | None: + """Return the index of the frame/TracebackEntry where recursion originates if + appropriate, None if no recursion occurred.""" + cache: dict[tuple[Any, int, int], list[dict[str, Any]]] = {} + for i, entry in enumerate(self): + # id for the code.raw is needed to work around + # the strange metaprogramming in the decorator lib from pypi + # which generates code objects that have hash/value equality + # XXX needs a test + key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno + values = cache.setdefault(key, []) + # Since Python 3.13 f_locals is a proxy, freeze it. + loc = dict(entry.frame.f_locals) + if values: + for otherloc in values: + if otherloc == loc: + return i + values.append(loc) + return None + + +def stringify_exception( + exc: BaseException, include_subexception_msg: bool = True +) -> str: + try: + notes = getattr(exc, "__notes__", []) + except KeyError: + # Workaround for https://github.com/python/cpython/issues/98778 on + # some 3.10 and 3.11 patch versions. + HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) + if sys.version_info < (3, 12) and isinstance(exc, HTTPError): + notes = [] + else: # pragma: no cover + # exception not related to above bug, reraise + raise + if not include_subexception_msg and isinstance(exc, BaseExceptionGroup): + message = exc.message + else: + message = str(exc) + + return "\n".join( + [ + message, + *notes, + ] + ) + + +E = TypeVar("E", bound=BaseException, covariant=True) + + +@final +@dataclasses.dataclass +class ExceptionInfo(Generic[E]): + """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" + + _assert_start_repr: ClassVar = "AssertionError('assert " + + _excinfo: tuple[type[E], E, TracebackType] | None + _striptext: str + _traceback: Traceback | None + + def __init__( + self, + excinfo: tuple[type[E], E, TracebackType] | None, + striptext: str = "", + traceback: Traceback | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._excinfo = excinfo + self._striptext = striptext + self._traceback = traceback + + @classmethod + def from_exception( + cls, + # Ignoring error: "Cannot use a covariant type variable as a parameter". + # This is OK to ignore because this class is (conceptually) readonly. + # See https://github.com/python/mypy/issues/7049. + exception: E, # type: ignore[misc] + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: + """Return an ExceptionInfo for an existing exception. + + The exception must have a non-``None`` ``__traceback__`` attribute, + otherwise this function fails with an assertion error. This means that + the exception must have been raised, or added a traceback with the + :py:meth:`~BaseException.with_traceback()` method. + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + + .. versionadded:: 7.4 + """ + assert exception.__traceback__, ( + "Exceptions passed to ExcInfo.from_exception(...)" + " must have a non-None __traceback__." + ) + exc_info = (type(exception), exception, exception.__traceback__) + return cls.from_exc_info(exc_info, exprinfo) + + @classmethod + def from_exc_info( + cls, + exc_info: tuple[type[E], E, TracebackType], + exprinfo: str | None = None, + ) -> ExceptionInfo[E]: + """Like :func:`from_exception`, but using old-style exc_info tuple.""" + _striptext = "" + if exprinfo is None and isinstance(exc_info[1], AssertionError): + exprinfo = getattr(exc_info[1], "msg", None) + if exprinfo is None: + exprinfo = saferepr(exc_info[1]) + if exprinfo and exprinfo.startswith(cls._assert_start_repr): + _striptext = "AssertionError: " + + return cls(exc_info, _striptext, _ispytest=True) + + @classmethod + def from_current(cls, exprinfo: str | None = None) -> ExceptionInfo[BaseException]: + """Return an ExceptionInfo matching the current traceback. + + .. warning:: + + Experimental API + + :param exprinfo: + A text string helping to determine if we should strip + ``AssertionError`` from the output. Defaults to the exception + message/``__str__()``. + """ + tup = sys.exc_info() + assert tup[0] is not None, "no current exception" + assert tup[1] is not None, "no current exception" + assert tup[2] is not None, "no current exception" + exc_info = (tup[0], tup[1], tup[2]) + return ExceptionInfo.from_exc_info(exc_info, exprinfo) + + @classmethod + def for_later(cls) -> ExceptionInfo[E]: + """Return an unfilled ExceptionInfo.""" + return cls(None, _ispytest=True) + + def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None: + """Fill an unfilled ExceptionInfo created with ``for_later()``.""" + assert self._excinfo is None, "ExceptionInfo was already filled" + self._excinfo = exc_info + + @property + def type(self) -> type[E]: + """The exception class.""" + assert self._excinfo is not None, ( + ".type can only be used after the context manager exits" + ) + return self._excinfo[0] + + @property + def value(self) -> E: + """The exception value.""" + assert self._excinfo is not None, ( + ".value can only be used after the context manager exits" + ) + return self._excinfo[1] + + @property + def tb(self) -> TracebackType: + """The exception raw traceback.""" + assert self._excinfo is not None, ( + ".tb can only be used after the context manager exits" + ) + return self._excinfo[2] + + @property + def typename(self) -> str: + """The type name of the exception.""" + assert self._excinfo is not None, ( + ".typename can only be used after the context manager exits" + ) + return self.type.__name__ + + @property + def traceback(self) -> Traceback: + """The traceback.""" + if self._traceback is None: + self._traceback = Traceback(self.tb) + return self._traceback + + @traceback.setter + def traceback(self, value: Traceback) -> None: + self._traceback = value + + def __repr__(self) -> str: + if self._excinfo is None: + return "" + return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" + + def exconly(self, tryshort: bool = False) -> str: + """Return the exception as a string. + + When 'tryshort' resolves to True, and the exception is an + AssertionError, only the actual exception part of the exception + representation is returned (so 'AssertionError: ' is removed from + the beginning). + """ + + def _get_single_subexc( + eg: BaseExceptionGroup[BaseException], + ) -> BaseException | None: + if len(eg.exceptions) != 1: + return None + if isinstance(e := eg.exceptions[0], BaseExceptionGroup): + return _get_single_subexc(e) + return e + + if ( + tryshort + and isinstance(self.value, BaseExceptionGroup) + and (subexc := _get_single_subexc(self.value)) is not None + ): + return f"{subexc!r} [single exception in {type(self.value).__name__}]" + + lines = format_exception_only(self.type, self.value) + text = "".join(lines) + text = text.rstrip() + if tryshort: + if text.startswith(self._striptext): + text = text[len(self._striptext) :] + return text + + def errisinstance(self, exc: EXCEPTION_OR_MORE) -> bool: + """Return True if the exception is an instance of exc. + + Consider using ``isinstance(excinfo.value, exc)`` instead. + """ + return isinstance(self.value, exc) + + def _getreprcrash(self) -> ReprFileLocation | None: + # Find last non-hidden traceback entry that led to the exception of the + # traceback, or None if all hidden. + for i in range(-1, -len(self.traceback) - 1, -1): + entry = self.traceback[i] + if not entry.ishidden(self): + path, lineno = entry.frame.code.raw.co_filename, entry.lineno + exconly = self.exconly(tryshort=True) + return ReprFileLocation(path, lineno + 1, exconly) + return None + + def getrepr( + self, + showlocals: bool = False, + style: TracebackStyle = "long", + abspath: bool = False, + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] = True, + funcargs: bool = False, + truncate_locals: bool = True, + truncate_args: bool = True, + chain: bool = True, + ) -> ReprExceptionInfo | ExceptionChainRepr: + """Return str()able representation of this exception info. + + :param bool showlocals: + Show locals per traceback entry. + Ignored if ``style=="native"``. + + :param str style: + long|short|line|no|native|value traceback style. + + :param bool abspath: + If paths should be changed to absolute or left unchanged. + + :param tbfilter: + A filter for traceback entries. + + * If false, don't hide any entries. + * If true, hide internal entries and entries that contain a local + variable ``__tracebackhide__ = True``. + * If a callable, delegates the filtering to the callable. + + Ignored if ``style`` is ``"native"``. + + :param bool funcargs: + Show fixtures ("funcargs" for legacy purposes) per traceback entry. + + :param bool truncate_locals: + With ``showlocals==True``, make sure locals can be safely represented as strings. + + :param bool truncate_args: + With ``showargs==True``, make sure args can be safely represented as strings. + + :param bool chain: + If chained exceptions in Python 3 should be shown. + + .. versionchanged:: 3.9 + + Added the ``chain`` parameter. + """ + if style == "native": + return ReprExceptionInfo( + reprtraceback=ReprTracebackNative( + format_exception( + self.type, + self.value, + self.traceback[0]._rawentry if self.traceback else None, + ) + ), + reprcrash=self._getreprcrash(), + ) + + fmt = FormattedExcinfo( + showlocals=showlocals, + style=style, + abspath=abspath, + tbfilter=tbfilter, + funcargs=funcargs, + truncate_locals=truncate_locals, + truncate_args=truncate_args, + chain=chain, + ) + return fmt.repr_excinfo(self) + + def match(self, regexp: str | re.Pattern[str]) -> Literal[True]: + """Check whether the regular expression `regexp` matches the string + representation of the exception using :func:`python:re.search`. + + If it matches `True` is returned, otherwise an `AssertionError` is raised. + """ + __tracebackhide__ = True + value = stringify_exception(self.value) + msg = ( + f"Regex pattern did not match.\n" + f" Expected regex: {regexp!r}\n" + f" Actual message: {value!r}" + ) + if regexp == value: + msg += "\n Did you mean to `re.escape()` the regex?" + assert re.search(regexp, value), msg + # Return True to allow for "assert excinfo.match()". + return True + + def _group_contains( + self, + exc_group: BaseExceptionGroup[BaseException], + expected_exception: EXCEPTION_OR_MORE, + match: str | re.Pattern[str] | None, + target_depth: int | None = None, + current_depth: int = 1, + ) -> bool: + """Return `True` if a `BaseExceptionGroup` contains a matching exception.""" + if (target_depth is not None) and (current_depth > target_depth): + # already descended past the target depth + return False + for exc in exc_group.exceptions: + if isinstance(exc, BaseExceptionGroup): + if self._group_contains( + exc, expected_exception, match, target_depth, current_depth + 1 + ): + return True + if (target_depth is not None) and (current_depth != target_depth): + # not at the target depth, no match + continue + if not isinstance(exc, expected_exception): + continue + if match is not None: + value = stringify_exception(exc) + if not re.search(match, value): + continue + return True + return False + + def group_contains( + self, + expected_exception: EXCEPTION_OR_MORE, + *, + match: str | re.Pattern[str] | None = None, + depth: int | None = None, + ) -> bool: + """Check whether a captured exception group contains a matching exception. + + :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception: + The expected exception type, or a tuple if one of multiple possible + exception types are expected. + + :param str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception and its `PEP-678 ` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + :param Optional[int] depth: + If `None`, will search for a matching exception at any nesting depth. + If >= 1, will only match an exception if it's at the specified depth (depth = 1 being + the exceptions contained within the topmost exception group). + + .. versionadded:: 8.0 + + .. warning:: + This helper makes it easy to check for the presence of specific exceptions, + but it is very bad for checking that the group does *not* contain + *any other exceptions*. + You should instead consider using :class:`pytest.RaisesGroup` + + """ + msg = "Captured exception is not an instance of `BaseExceptionGroup`" + assert isinstance(self.value, BaseExceptionGroup), msg + msg = "`depth` must be >= 1 if specified" + assert (depth is None) or (depth >= 1), msg + return self._group_contains(self.value, expected_exception, match, depth) + + +# Type alias for the `tbfilter` setting: +# bool: If True, it should be filtered using Traceback.filter() +# callable: A callable that takes an ExceptionInfo and returns the filtered traceback. +TracebackFilter: TypeAlias = bool | Callable[[ExceptionInfo[BaseException]], Traceback] + + +@dataclasses.dataclass +class FormattedExcinfo: + """Presenting information about failing Functions and Generators.""" + + # for traceback entries + flow_marker: ClassVar = ">" + fail_marker: ClassVar = "E" + + showlocals: bool = False + style: TracebackStyle = "long" + abspath: bool = True + tbfilter: TracebackFilter = True + funcargs: bool = False + truncate_locals: bool = True + truncate_args: bool = True + chain: bool = True + astcache: dict[str | Path, ast.AST] = dataclasses.field( + default_factory=dict, init=False, repr=False + ) + + def _getindent(self, source: Source) -> int: + # Figure out indent for the given source. + try: + s = str(source.getstatement(len(source) - 1)) + except KeyboardInterrupt: + raise + except BaseException: + try: + s = str(source[-1]) + except KeyboardInterrupt: + raise + except BaseException: + return 0 + return 4 + (len(s) - len(s.lstrip())) + + def _getentrysource(self, entry: TracebackEntry) -> Source | None: + source = entry.getsource(self.astcache) + if source is not None: + source = source.deindent() + return source + + def repr_args(self, entry: TracebackEntry) -> ReprFuncArgs | None: + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(var=True): + if self.truncate_args: + str_repr = saferepr(argvalue) + else: + str_repr = saferepr(argvalue, maxsize=None) + args.append((argname, str_repr)) + return ReprFuncArgs(args) + return None + + def get_source( + self, + source: Source | None, + line_index: int = -1, + excinfo: ExceptionInfo[BaseException] | None = None, + short: bool = False, + end_line_index: int | None = None, + colno: int | None = None, + end_colno: int | None = None, + ) -> list[str]: + """Return formatted and marked up source lines.""" + lines = [] + if source is not None and line_index < 0: + line_index += len(source) + if source is None or line_index >= len(source.lines) or line_index < 0: + # `line_index` could still be outside `range(len(source.lines))` if + # we're processing AST with pathological position attributes. + source = Source("???") + line_index = 0 + space_prefix = " " + if short: + lines.append(space_prefix + source.lines[line_index].strip()) + lines.extend( + self.get_highlight_arrows_for_line( + raw_line=source.raw_lines[line_index], + line=source.lines[line_index].strip(), + lineno=line_index, + end_lineno=end_line_index, + colno=colno, + end_colno=end_colno, + ) + ) + else: + for line in source.lines[:line_index]: + lines.append(space_prefix + line) + lines.append(self.flow_marker + " " + source.lines[line_index]) + lines.extend( + self.get_highlight_arrows_for_line( + raw_line=source.raw_lines[line_index], + line=source.lines[line_index], + lineno=line_index, + end_lineno=end_line_index, + colno=colno, + end_colno=end_colno, + ) + ) + for line in source.lines[line_index + 1 :]: + lines.append(space_prefix + line) + if excinfo is not None: + indent = 4 if short else self._getindent(source) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines + + def get_highlight_arrows_for_line( + self, + line: str, + raw_line: str, + lineno: int | None, + end_lineno: int | None, + colno: int | None, + end_colno: int | None, + ) -> list[str]: + """Return characters highlighting a source line. + + Example with colno and end_colno pointing to the bar expression: + "foo() + bar()" + returns " ^^^^^" + """ + if lineno != end_lineno: + # Don't handle expressions that span multiple lines. + return [] + if colno is None or end_colno is None: + # Can't do anything without column information. + return [] + + num_stripped_chars = len(raw_line) - len(line) + + start_char_offset = _byte_offset_to_character_offset(raw_line, colno) + end_char_offset = _byte_offset_to_character_offset(raw_line, end_colno) + num_carets = end_char_offset - start_char_offset + # If the highlight would span the whole line, it is redundant, don't + # show it. + if num_carets >= len(line.strip()): + return [] + + highlights = " " + highlights += " " * (start_char_offset - num_stripped_chars + 1) + highlights += "^" * num_carets + return [highlights] + + def get_exconly( + self, + excinfo: ExceptionInfo[BaseException], + indent: int = 4, + markall: bool = False, + ) -> list[str]: + lines = [] + indentstr = " " * indent + # Get the real exception information out. + exlines = excinfo.exconly(tryshort=True).split("\n") + failindent = self.fail_marker + indentstr[1:] + for line in exlines: + lines.append(failindent + line) + if not markall: + failindent = indentstr + return lines + + def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None: + if self.showlocals: + lines = [] + keys = [loc for loc in locals if loc[0] != "@"] + keys.sort() + for name in keys: + value = locals[name] + if name == "__builtins__": + lines.append("__builtins__ = ") + else: + # This formatting could all be handled by the + # _repr() function, which is only reprlib.Repr in + # disguise, so is very configurable. + if self.truncate_locals: + str_repr = saferepr(value) + else: + str_repr = safeformat(value) + # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): + lines.append(f"{name:<10} = {str_repr}") + # else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) + return None + + def repr_traceback_entry( + self, + entry: TracebackEntry | None, + excinfo: ExceptionInfo[BaseException] | None = None, + ) -> ReprEntry: + lines: list[str] = [] + style = ( + entry._repr_style + if entry is not None and entry._repr_style is not None + else self.style + ) + if style in ("short", "long") and entry is not None: + source = self._getentrysource(entry) + if source is None: + source = Source("???") + line_index = 0 + end_line_index, colno, end_colno = None, None, None + else: + line_index = entry.relline + end_line_index = entry.end_lineno_relative + colno = entry.colno + end_colno = entry.end_colno + short = style == "short" + reprargs = self.repr_args(entry) if not short else None + s = self.get_source( + source=source, + line_index=line_index, + excinfo=excinfo, + short=short, + end_line_index=end_line_index, + colno=colno, + end_colno=end_colno, + ) + lines.extend(s) + if short: + message = f"in {entry.name}" + else: + message = (excinfo and excinfo.typename) or "" + entry_path = entry.path + path = self._makepath(entry_path) + reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) + elif style == "value": + if excinfo: + lines.extend(str(excinfo.value).split("\n")) + return ReprEntry(lines, None, None, None, style) + else: + if excinfo: + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None, None, style) + + def _makepath(self, path: Path | str) -> str: + if not self.abspath and isinstance(path, Path): + try: + np = bestrelpath(Path.cwd(), path) + except OSError: + return str(path) + if len(np) < len(str(path)): + return np + return str(path) + + def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> ReprTraceback: + traceback = filter_excinfo_traceback(self.tbfilter, excinfo) + + if isinstance(excinfo.value, RecursionError): + traceback, extraline = self._truncate_recursive_traceback(traceback) + else: + extraline = None + + if not traceback: + if extraline is None: + extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames." + entries = [self.repr_traceback_entry(None, excinfo)] + return ReprTraceback(entries, extraline, style=self.style) + + last = traceback[-1] + if self.style == "value": + entries = [self.repr_traceback_entry(last, excinfo)] + return ReprTraceback(entries, None, style=self.style) + + entries = [ + self.repr_traceback_entry(entry, excinfo if last == entry else None) + for entry in traceback + ] + return ReprTraceback(entries, extraline, style=self.style) + + def _truncate_recursive_traceback( + self, traceback: Traceback + ) -> tuple[Traceback, str | None]: + """Truncate the given recursive traceback trying to find the starting + point of the recursion. + + The detection is done by going through each traceback entry and + finding the point in which the locals of the frame are equal to the + locals of a previous frame (see ``recursionindex()``). + + Handle the situation where the recursion process might raise an + exception (for example comparing numpy arrays using equality raises a + TypeError), in which case we do our best to warn the user of the + error and show a limited traceback. + """ + try: + recursionindex = traceback.recursionindex() + except Exception as e: + max_frames = 10 + extraline: str | None = ( + "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" + " The following exception happened when comparing locals in the stack frame:\n" + f" {type(e).__name__}: {e!s}\n" + f" Displaying first and last {max_frames} stack frames out of {len(traceback)}." + ) + # Type ignored because adding two instances of a List subtype + # currently incorrectly has type List instead of the subtype. + traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore + else: + if recursionindex is not None: + extraline = "!!! Recursion detected (same locals & position)" + traceback = traceback[: recursionindex + 1] + else: + extraline = None + + return traceback, extraline + + def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr: + repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] + e: BaseException | None = excinfo.value + excinfo_: ExceptionInfo[BaseException] | None = excinfo + descr = None + seen: set[int] = set() + while e is not None and id(e) not in seen: + seen.add(id(e)) + + if excinfo_: + # Fall back to native traceback as a temporary workaround until + # full support for exception groups added to ExceptionInfo. + # See https://github.com/pytest-dev/pytest/issues/9159 + reprtraceback: ReprTraceback | ReprTracebackNative + if isinstance(e, BaseExceptionGroup): + # don't filter any sub-exceptions since they shouldn't have any internal frames + traceback = filter_excinfo_traceback(self.tbfilter, excinfo) + reprtraceback = ReprTracebackNative( + format_exception( + type(excinfo.value), + excinfo.value, + traceback[0]._rawentry if traceback else None, + ) + ) + if not traceback: + reprtraceback.extraline = ( + "All traceback entries are hidden. " + "Pass `--full-trace` to see hidden and internal frames." + ) + + else: + reprtraceback = self.repr_traceback(excinfo_) + reprcrash = excinfo_._getreprcrash() + else: + # Fallback to native repr if the exception doesn't have a traceback: + # ExceptionInfo objects require a full traceback to work. + reprtraceback = ReprTracebackNative(format_exception(type(e), e, None)) + reprcrash = None + repr_chain += [(reprtraceback, reprcrash, descr)] + + if e.__cause__ is not None and self.chain: + e = e.__cause__ + excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None + descr = "The above exception was the direct cause of the following exception:" + elif ( + e.__context__ is not None and not e.__suppress_context__ and self.chain + ): + e = e.__context__ + excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None + descr = "During handling of the above exception, another exception occurred:" + else: + e = None + repr_chain.reverse() + return ExceptionChainRepr(repr_chain) + + +@dataclasses.dataclass(eq=False) +class TerminalRepr: + def __str__(self) -> str: + # FYI this is called from pytest-xdist's serialization of exception + # information. + io = StringIO() + tw = TerminalWriter(file=io) + self.toterminal(tw) + return io.getvalue().strip() + + def __repr__(self) -> str: + return f"<{self.__class__} instance at {id(self):0x}>" + + def toterminal(self, tw: TerminalWriter) -> None: + raise NotImplementedError() + + +# This class is abstract -- only subclasses are instantiated. +@dataclasses.dataclass(eq=False) +class ExceptionRepr(TerminalRepr): + # Provided by subclasses. + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None + sections: list[tuple[str, str, str]] = dataclasses.field( + init=False, default_factory=list + ) + + def addsection(self, name: str, content: str, sep: str = "-") -> None: + self.sections.append((name, content, sep)) + + def toterminal(self, tw: TerminalWriter) -> None: + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) + + +@dataclasses.dataclass(eq=False) +class ExceptionChainRepr(ExceptionRepr): + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]] + + def __init__( + self, + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]], + ) -> None: + # reprcrash and reprtraceback of the outermost (the newest) exception + # in the chain. + super().__init__( + reprtraceback=chain[-1][0], + reprcrash=chain[-1][1], + ) + self.chain = chain + + def toterminal(self, tw: TerminalWriter) -> None: + for element in self.chain: + element[0].toterminal(tw) + if element[2] is not None: + tw.line("") + tw.line(element[2], yellow=True) + super().toterminal(tw) + + +@dataclasses.dataclass(eq=False) +class ReprExceptionInfo(ExceptionRepr): + reprtraceback: ReprTraceback + reprcrash: ReprFileLocation | None + + def toterminal(self, tw: TerminalWriter) -> None: + self.reprtraceback.toterminal(tw) + super().toterminal(tw) + + +@dataclasses.dataclass(eq=False) +class ReprTraceback(TerminalRepr): + reprentries: Sequence[ReprEntry | ReprEntryNative] + extraline: str | None + style: TracebackStyle + + entrysep: ClassVar = "_ " + + def toterminal(self, tw: TerminalWriter) -> None: + # The entries might have different styles. + for i, entry in enumerate(self.reprentries): + if entry.style == "long": + tw.line("") + entry.toterminal(tw) + if i < len(self.reprentries) - 1: + next_entry = self.reprentries[i + 1] + if entry.style == "long" or ( + entry.style == "short" and next_entry.style == "long" + ): + tw.sep(self.entrysep) + + if self.extraline: + tw.line(self.extraline) + + +class ReprTracebackNative(ReprTraceback): + def __init__(self, tblines: Sequence[str]) -> None: + self.reprentries = [ReprEntryNative(tblines)] + self.extraline = None + self.style = "native" + + +@dataclasses.dataclass(eq=False) +class ReprEntryNative(TerminalRepr): + lines: Sequence[str] + + style: ClassVar[TracebackStyle] = "native" + + def toterminal(self, tw: TerminalWriter) -> None: + tw.write("".join(self.lines)) + + +@dataclasses.dataclass(eq=False) +class ReprEntry(TerminalRepr): + lines: Sequence[str] + reprfuncargs: ReprFuncArgs | None + reprlocals: ReprLocals | None + reprfileloc: ReprFileLocation | None + style: TracebackStyle + + def _write_entry_lines(self, tw: TerminalWriter) -> None: + """Write the source code portions of a list of traceback entries with syntax highlighting. + + Usually entries are lines like these: + + " x = 1" + "> assert x == 2" + "E assert 1 == 2" + + This function takes care of rendering the "source" portions of it (the lines without + the "E" prefix) using syntax highlighting, taking care to not highlighting the ">" + character, as doing so might break line continuations. + """ + if not self.lines: + return + + if self.style == "value": + # Using tw.write instead of tw.line for testing purposes due to TWMock implementation; + # lines written with TWMock.line and TWMock._write_source cannot be distinguished + # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE + for line in self.lines: + tw.write(line) + tw.write("\n") + return + + # separate indents and source lines that are not failures: we want to + # highlight the code but not the indentation, which may contain markers + # such as "> assert 0" + fail_marker = f"{FormattedExcinfo.fail_marker} " + indent_size = len(fail_marker) + indents: list[str] = [] + source_lines: list[str] = [] + failure_lines: list[str] = [] + for index, line in enumerate(self.lines): + is_failure_line = line.startswith(fail_marker) + if is_failure_line: + # from this point on all lines are considered part of the failure + failure_lines.extend(self.lines[index:]) + break + else: + indents.append(line[:indent_size]) + source_lines.append(line[indent_size:]) + + tw._write_source(source_lines, indents) + + # failure lines are always completely red and bold + for line in failure_lines: + tw.line(line, bold=True, red=True) + + def toterminal(self, tw: TerminalWriter) -> None: + if self.style == "short": + if self.reprfileloc: + self.reprfileloc.toterminal(tw) + self._write_entry_lines(tw) + if self.reprlocals: + self.reprlocals.toterminal(tw, indent=" " * 8) + return + + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) + + self._write_entry_lines(tw) + + if self.reprlocals: + tw.line("") + self.reprlocals.toterminal(tw) + if self.reprfileloc: + if self.lines: + tw.line("") + self.reprfileloc.toterminal(tw) + + def __str__(self) -> str: + return "{}\n{}\n{}".format( + "\n".join(self.lines), self.reprlocals, self.reprfileloc + ) + + +@dataclasses.dataclass(eq=False) +class ReprFileLocation(TerminalRepr): + path: str + lineno: int + message: str + + def __post_init__(self) -> None: + self.path = str(self.path) + + def toterminal(self, tw: TerminalWriter) -> None: + # Filename and lineno output for each entry, using an output format + # that most editors understand. + msg = self.message + i = msg.find("\n") + if i != -1: + msg = msg[:i] + tw.write(self.path, bold=True, red=True) + tw.line(f":{self.lineno}: {msg}") + + +@dataclasses.dataclass(eq=False) +class ReprLocals(TerminalRepr): + lines: Sequence[str] + + def toterminal(self, tw: TerminalWriter, indent="") -> None: + for line in self.lines: + tw.line(indent + line) + + +@dataclasses.dataclass(eq=False) +class ReprFuncArgs(TerminalRepr): + args: Sequence[tuple[str, object]] + + def toterminal(self, tw: TerminalWriter) -> None: + if self.args: + linesofar = "" + for name, value in self.args: + ns = f"{name} = {value}" + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") + + +def getfslineno(obj: object) -> tuple[str | Path, int]: + """Return source location (path, lineno) for the given object. + + If the source cannot be determined return ("", -1). + + The line number is 0-based. + """ + # xxx let decorators etc specify a sane ordering + # NOTE: this used to be done in _pytest.compat.getfslineno, initially added + # in 6ec13a2b9. It ("place_as") appears to be something very custom. + obj = get_real_func(obj) + if hasattr(obj, "place_as"): + obj = obj.place_as + + try: + code = Code.from_function(obj) + except TypeError: + try: + fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] + except TypeError: + return "", -1 + + fspath = (fn and absolutepath(fn)) or "" + lineno = -1 + if fspath: + try: + _, lineno = findsource(obj) + except OSError: + pass + return fspath, lineno + + return code.path, code.firstlineno + + +def _byte_offset_to_character_offset(str, offset): + """Converts a byte based offset in a string to a code-point.""" + as_utf8 = str.encode("utf-8") + return len(as_utf8[:offset].decode("utf-8", errors="replace")) + + +# Relative paths that we use to filter traceback entries from appearing to the user; +# see filter_traceback. +# note: if we need to add more paths than what we have now we should probably use a list +# for better maintenance. + +_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc")) +# pluggy is either a package or a single module depending on the version +if _PLUGGY_DIR.name == "__init__.py": + _PLUGGY_DIR = _PLUGGY_DIR.parent +_PYTEST_DIR = Path(_pytest.__file__).parent + + +def filter_traceback(entry: TracebackEntry) -> bool: + """Return True if a TracebackEntry instance should be included in tracebacks. + + We hide traceback entries of: + + * dynamically generated code (no code to show up for it); + * internal traceback from pytest or its internal libraries, py and pluggy. + """ + # entry.path might sometimes return a str object when the entry + # points to dynamically generated code. + # See https://bitbucket.org/pytest-dev/py/issues/71. + raw_filename = entry.frame.code.raw.co_filename + is_generated = "<" in raw_filename and ">" in raw_filename + if is_generated: + return False + + # entry.path might point to a non-existing file, in which case it will + # also return a str object. See #1133. + p = Path(entry.path) + + parents = p.parents + if _PLUGGY_DIR in parents: + return False + if _PYTEST_DIR in parents: + return False + + return True + + +def filter_excinfo_traceback( + tbfilter: TracebackFilter, excinfo: ExceptionInfo[BaseException] +) -> Traceback: + """Filter the exception traceback in ``excinfo`` according to ``tbfilter``.""" + if callable(tbfilter): + return tbfilter(excinfo) + elif tbfilter: + return excinfo.traceback.filter(excinfo) + else: + return excinfo.traceback diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/source.py b/.venv/lib/python3.12/site-packages/_pytest/_code/source.py new file mode 100644 index 0000000..99c242d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_code/source.py @@ -0,0 +1,225 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import ast +from bisect import bisect_right +from collections.abc import Iterable +from collections.abc import Iterator +import inspect +import textwrap +import tokenize +import types +from typing import overload +import warnings + + +class Source: + """An immutable object holding a source code fragment. + + When using Source(...), the source lines are deindented. + """ + + def __init__(self, obj: object = None) -> None: + if not obj: + self.lines: list[str] = [] + self.raw_lines: list[str] = [] + elif isinstance(obj, Source): + self.lines = obj.lines + self.raw_lines = obj.raw_lines + elif isinstance(obj, tuple | list): + self.lines = deindent(x.rstrip("\n") for x in obj) + self.raw_lines = list(x.rstrip("\n") for x in obj) + elif isinstance(obj, str): + self.lines = deindent(obj.split("\n")) + self.raw_lines = obj.split("\n") + else: + try: + rawcode = getrawcode(obj) + src = inspect.getsource(rawcode) + except TypeError: + src = inspect.getsource(obj) # type: ignore[arg-type] + self.lines = deindent(src.split("\n")) + self.raw_lines = src.split("\n") + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Source): + return NotImplemented + return self.lines == other.lines + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + @overload + def __getitem__(self, key: int) -> str: ... + + @overload + def __getitem__(self, key: slice) -> Source: ... + + def __getitem__(self, key: int | slice) -> str | Source: + if isinstance(key, int): + return self.lines[key] + else: + if key.step not in (None, 1): + raise IndexError("cannot slice a Source with a step") + newsource = Source() + newsource.lines = self.lines[key.start : key.stop] + newsource.raw_lines = self.raw_lines[key.start : key.stop] + return newsource + + def __iter__(self) -> Iterator[str]: + return iter(self.lines) + + def __len__(self) -> int: + return len(self.lines) + + def strip(self) -> Source: + """Return new Source object with trailing and leading blank lines removed.""" + start, end = 0, len(self) + while start < end and not self.lines[start].strip(): + start += 1 + while end > start and not self.lines[end - 1].strip(): + end -= 1 + source = Source() + source.raw_lines = self.raw_lines + source.lines[:] = self.lines[start:end] + return source + + def indent(self, indent: str = " " * 4) -> Source: + """Return a copy of the source object with all lines indented by the + given indent-string.""" + newsource = Source() + newsource.raw_lines = self.raw_lines + newsource.lines = [(indent + line) for line in self.lines] + return newsource + + def getstatement(self, lineno: int) -> Source: + """Return Source statement which contains the given linenumber + (counted from 0).""" + start, end = self.getstatementrange(lineno) + return self[start:end] + + def getstatementrange(self, lineno: int) -> tuple[int, int]: + """Return (start, end) tuple which spans the minimal statement region + which containing the given lineno.""" + if not (0 <= lineno < len(self)): + raise IndexError("lineno out of range") + _ast, start, end = getstatementrange_ast(lineno, self) + return start, end + + def deindent(self) -> Source: + """Return a new Source object deindented.""" + newsource = Source() + newsource.lines[:] = deindent(self.lines) + newsource.raw_lines = self.raw_lines + return newsource + + def __str__(self) -> str: + return "\n".join(self.lines) + + +# +# helper functions +# + + +def findsource(obj) -> tuple[Source | None, int]: + try: + sourcelines, lineno = inspect.findsource(obj) + except Exception: + return None, -1 + source = Source() + source.lines = [line.rstrip() for line in sourcelines] + source.raw_lines = sourcelines + return source, lineno + + +def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: + """Return code object for given function.""" + try: + return obj.__code__ # type: ignore[attr-defined,no-any-return] + except AttributeError: + pass + if trycall: + call = getattr(obj, "__call__", None) + if call and not isinstance(obj, type): + return getrawcode(call, trycall=False) + raise TypeError(f"could not get code object for {obj!r}") + + +def deindent(lines: Iterable[str]) -> list[str]: + return textwrap.dedent("\n".join(lines)).splitlines() + + +def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]: + # Flatten all statements and except handlers into one lineno-list. + # AST's line numbers start indexing at 1. + values: list[int] = [] + for x in ast.walk(node): + if isinstance(x, ast.stmt | ast.ExceptHandler): + # The lineno points to the class/def, so need to include the decorators. + if isinstance(x, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef): + for d in x.decorator_list: + values.append(d.lineno - 1) + values.append(x.lineno - 1) + for name in ("finalbody", "orelse"): + val: list[ast.stmt] | None = getattr(x, name, None) + if val: + # Treat the finally/orelse part as its own statement. + values.append(val[0].lineno - 1 - 1) + values.sort() + insert_index = bisect_right(values, lineno) + start = values[insert_index - 1] + if insert_index >= len(values): + end = None + else: + end = values[insert_index] + return start, end + + +def getstatementrange_ast( + lineno: int, + source: Source, + assertion: bool = False, + astnode: ast.AST | None = None, +) -> tuple[ast.AST, int, int]: + if astnode is None: + content = str(source) + # See #4260: + # Don't produce duplicate warnings when compiling source to find AST. + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + astnode = ast.parse(content, "source", "exec") + + start, end = get_statement_startend2(lineno, astnode) + # We need to correct the end: + # - ast-parsing strips comments + # - there might be empty lines + # - we might have lesser indented code blocks at the end + if end is None: + end = len(source.lines) + + if end > start + 1: + # Make sure we don't span differently indented code blocks + # by using the BlockFinder helper used which inspect.getsource() uses itself. + block_finder = inspect.BlockFinder() + # If we start with an indented line, put blockfinder to "started" mode. + block_finder.started = ( + bool(source.lines[start]) and source.lines[start][0].isspace() + ) + it = ((x + "\n") for x in source.lines[start:end]) + try: + for tok in tokenize.generate_tokens(lambda: next(it)): + block_finder.tokeneater(*tok) + except (inspect.EndOfBlock, IndentationError): + end = block_finder.last + start + except Exception: + pass + + # The end might still point to a comment or empty line, correct it. + while end: + line = source.lines[end - 1].lstrip() + if line.startswith("#") or not line: + end -= 1 + else: + break + return astnode, start, end diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py new file mode 100644 index 0000000..b0155b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from .terminalwriter import get_terminal_width +from .terminalwriter import TerminalWriter + + +__all__ = [ + "TerminalWriter", + "get_terminal_width", +] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py b/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py new file mode 100644 index 0000000..28f0690 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py @@ -0,0 +1,673 @@ +# mypy: allow-untyped-defs +# This module was imported from the cpython standard library +# (https://github.com/python/cpython/) at commit +# c5140945c723ae6c4b7ee81ff720ac8ea4b52cfd (python3.12). +# +# +# Original Author: Fred L. Drake, Jr. +# fdrake@acm.org +# +# This is a simple little module I wrote to make life easier. I didn't +# see anything quite like it in the library, though I may have overlooked +# something. I wrote this when I was trying to read some heavily nested +# tuples with fairly non-descriptive content. This is modeled very much +# after Lisp/Scheme - style pretty-printing of lists. If you find it +# useful, thank small children who sleep at night. +from __future__ import annotations + +import collections as _collections +from collections.abc import Callable +from collections.abc import Iterator +import dataclasses as _dataclasses +from io import StringIO as _StringIO +import re +import types as _types +from typing import Any +from typing import IO + + +class _safe_key: + """Helper function for key functions when sorting unorderable objects. + + The wrapped-object will fallback to a Py2.x style comparison for + unorderable types (sorting first comparing the type name and then by + the obj ids). Does not work recursively, so dict.items() must have + _safe_key applied to both the key and the value. + + """ + + __slots__ = ["obj"] + + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + try: + return self.obj < other.obj + except TypeError: + return (str(type(self.obj)), id(self.obj)) < ( + str(type(other.obj)), + id(other.obj), + ) + + +def _safe_tuple(t): + """Helper function for comparing 2-tuples""" + return _safe_key(t[0]), _safe_key(t[1]) + + +class PrettyPrinter: + def __init__( + self, + indent: int = 4, + width: int = 80, + depth: int | None = None, + ) -> None: + """Handle pretty printing operations onto a stream using a set of + configured parameters. + + indent + Number of spaces to indent for each level of nesting. + + width + Attempted maximum number of columns in the output. + + depth + The maximum depth to print out nested structures. + + """ + if indent < 0: + raise ValueError("indent must be >= 0") + if depth is not None and depth <= 0: + raise ValueError("depth must be > 0") + if not width: + raise ValueError("width must be != 0") + self._depth = depth + self._indent_per_level = indent + self._width = width + + def pformat(self, object: Any) -> str: + sio = _StringIO() + self._format(object, sio, 0, 0, set(), 0) + return sio.getvalue() + + def _format( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + objid = id(object) + if objid in context: + stream.write(_recursion(object)) + return + + p = self._dispatch.get(type(object).__repr__, None) + if p is not None: + context.add(objid) + p(self, object, stream, indent, allowance, context, level + 1) + context.remove(objid) + elif ( + _dataclasses.is_dataclass(object) + and not isinstance(object, type) + and object.__dataclass_params__.repr # type:ignore[attr-defined] + and + # Check dataclass has generated repr method. + hasattr(object.__repr__, "__wrapped__") + and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ + ): + context.add(objid) + self._pprint_dataclass( + object, stream, indent, allowance, context, level + 1 + ) + context.remove(objid) + else: + stream.write(self._repr(object, context, level)) + + def _pprint_dataclass( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + cls_name = object.__class__.__name__ + items = [ + (f.name, getattr(object, f.name)) + for f in _dataclasses.fields(object) + if f.repr + ] + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch: dict[ + Callable[..., str], + Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None], + ] = {} + + def _pprint_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("{") + items = sorted(object.items(), key=_safe_tuple) + self._format_dict_items(items, stream, indent, allowance, context, level) + write("}") + + _dispatch[dict.__repr__] = _pprint_dict + + def _pprint_ordered_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + cls = object.__class__ + stream.write(cls.__name__ + "(") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict + + def _pprint_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("[") + self._format_items(object, stream, indent, allowance, context, level) + stream.write("]") + + _dispatch[list.__repr__] = _pprint_list + + def _pprint_tuple( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("(") + self._format_items(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[tuple.__repr__] = _pprint_tuple + + def _pprint_set( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object): + stream.write(repr(object)) + return + typ = object.__class__ + if typ is set: + stream.write("{") + endchar = "}" + else: + stream.write(typ.__name__ + "({") + endchar = "})" + object = sorted(object, key=_safe_key) + self._format_items(object, stream, indent, allowance, context, level) + stream.write(endchar) + + _dispatch[set.__repr__] = _pprint_set + _dispatch[frozenset.__repr__] = _pprint_set + + def _pprint_str( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if not len(object): + write(repr(object)) + return + chunks = [] + lines = object.splitlines(True) + if level == 1: + indent += 1 + allowance += 1 + max_width1 = max_width = self._width - indent + for i, line in enumerate(lines): + rep = repr(line) + if i == len(lines) - 1: + max_width1 -= allowance + if len(rep) <= max_width1: + chunks.append(rep) + else: + # A list of alternating (non-space, space) strings + parts = re.findall(r"\S*\s*", line) + assert parts + assert not parts[-1] + parts.pop() # drop empty last part + max_width2 = max_width + current = "" + for j, part in enumerate(parts): + candidate = current + part + if j == len(parts) - 1 and i == len(lines) - 1: + max_width2 -= allowance + if len(repr(candidate)) > max_width2: + if current: + chunks.append(repr(current)) + current = part + else: + current = candidate + if current: + chunks.append(repr(current)) + if len(chunks) == 1: + write(rep) + return + if level == 1: + write("(") + for i, rep in enumerate(chunks): + if i > 0: + write("\n" + " " * indent) + write(rep) + if level == 1: + write(")") + + _dispatch[str.__repr__] = _pprint_str + + def _pprint_bytes( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + if len(object) <= 4: + write(repr(object)) + return + parens = level == 1 + if parens: + indent += 1 + allowance += 1 + write("(") + delim = "" + for rep in _wrap_bytes_repr(object, self._width - indent, allowance): + write(delim) + write(rep) + if not delim: + delim = "\n" + " " * indent + if parens: + write(")") + + _dispatch[bytes.__repr__] = _pprint_bytes + + def _pprint_bytearray( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + write = stream.write + write("bytearray(") + self._pprint_bytes( + bytes(object), stream, indent + 10, allowance + 1, context, level + 1 + ) + write(")") + + _dispatch[bytearray.__repr__] = _pprint_bytearray + + def _pprint_mappingproxy( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write("mappingproxy(") + self._format(object.copy(), stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy + + def _pprint_simplenamespace( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if type(object) is _types.SimpleNamespace: + # The SimpleNamespace repr is "namespace" instead of the class + # name, so we do the same here. For subclasses; use the class name. + cls_name = "namespace" + else: + cls_name = object.__class__.__name__ + items = object.__dict__.items() + stream.write(cls_name + "(") + self._format_namespace_items(items, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace + + def _format_dict_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(self._repr(key, context, level)) + write(": ") + self._format(ent, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _format_namespace_items( + self, + items: list[tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(key) + write("=") + if id(ent) in context: + # Special-case representation of recursion to match standard + # recursive dataclass repr. + write("...") + else: + self._format( + ent, + stream, + item_indent + len(key) + 1, + 1, + context, + level, + ) + + write(",") + + write("\n" + " " * indent) + + def _format_items( + self, + items: list[Any], + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + + for item in items: + write(delimnl) + self._format(item, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _repr(self, object: Any, context: set[int], level: int) -> str: + return self._safe_repr(object, context.copy(), self._depth, level) + + def _pprint_default_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + rdf = self._repr(object.default_factory, context, level) + stream.write(f"{object.__class__.__name__}({rdf}, ") + self._pprint_dict(object, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict + + def _pprint_counter( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + + if object: + stream.write("{") + items = object.most_common() + self._format_dict_items(items, stream, indent, allowance, context, level) + stream.write("}") + + stream.write(")") + + _dispatch[_collections.Counter.__repr__] = _pprint_counter + + def _pprint_chain_map( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): + stream.write(repr(object)) + return + + stream.write(object.__class__.__name__ + "(") + self._format_items(object.maps, stream, indent, allowance, context, level) + stream.write(")") + + _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map + + def _pprint_deque( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + if object.maxlen is not None: + stream.write(f"maxlen={object.maxlen}, ") + stream.write("[") + + self._format_items(object, stream, indent, allowance + 1, context, level) + stream.write("])") + + _dispatch[_collections.deque.__repr__] = _pprint_deque + + def _pprint_user_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict + + def _pprint_user_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserList.__repr__] = _pprint_user_list + + def _pprint_user_string( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: set[int], + level: int, + ) -> None: + self._format(object.data, stream, indent, allowance, context, level - 1) + + _dispatch[_collections.UserString.__repr__] = _pprint_user_string + + def _safe_repr( + self, object: Any, context: set[int], maxlevels: int | None, level: int + ) -> str: + typ = type(object) + if typ in _builtin_scalars: + return repr(object) + + r = getattr(typ, "__repr__", None) + + if issubclass(typ, dict) and r is dict.__repr__: + if not object: + return "{}" + objid = id(object) + if maxlevels and level >= maxlevels: + return "{...}" + if objid in context: + return _recursion(object) + context.add(objid) + components: list[str] = [] + append = components.append + level += 1 + for k, v in sorted(object.items(), key=_safe_tuple): + krepr = self._safe_repr(k, context, maxlevels, level) + vrepr = self._safe_repr(v, context, maxlevels, level) + append(f"{krepr}: {vrepr}") + context.remove(objid) + return "{{{}}}".format(", ".join(components)) + + if (issubclass(typ, list) and r is list.__repr__) or ( + issubclass(typ, tuple) and r is tuple.__repr__ + ): + if issubclass(typ, list): + if not object: + return "[]" + format = "[%s]" + elif len(object) == 1: + format = "(%s,)" + else: + if not object: + return "()" + format = "(%s)" + objid = id(object) + if maxlevels and level >= maxlevels: + return format % "..." + if objid in context: + return _recursion(object) + context.add(objid) + components = [] + append = components.append + level += 1 + for o in object: + orepr = self._safe_repr(o, context, maxlevels, level) + append(orepr) + context.remove(objid) + return format % ", ".join(components) + + return repr(object) + + +_builtin_scalars = frozenset( + {str, bytes, bytearray, float, complex, bool, type(None), int} +) + + +def _recursion(object: Any) -> str: + return f"" + + +def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: + current = b"" + last = len(object) // 4 * 4 + for i in range(0, len(object), 4): + part = object[i : i + 4] + candidate = current + part + if i == last: + width -= allowance + if len(repr(candidate)) > width: + if current: + yield repr(current) + current = part + else: + current = candidate + if current: + yield repr(current) diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py b/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py new file mode 100644 index 0000000..cee70e3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import pprint +import reprlib + + +def _try_repr_or_str(obj: object) -> str: + try: + return repr(obj) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException: + return f'{type(obj).__name__}("{obj}")' + + +def _format_repr_exception(exc: BaseException, obj: object) -> str: + try: + exc_info = _try_repr_or_str(exc) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as inner_exc: + exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})" + return ( + f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" + ) + + +def _ellipsize(s: str, maxsize: int) -> str: + if len(s) > maxsize: + i = max(0, (maxsize - 3) // 2) + j = max(0, maxsize - 3 - i) + return s[:i] + "..." + s[len(s) - j :] + return s + + +class SafeRepr(reprlib.Repr): + """ + repr.Repr that limits the resulting size of repr() and includes + information on exceptions raised during the call. + """ + + def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: + """ + :param maxsize: + If not None, will truncate the resulting repr to that specific size, using ellipsis + somewhere in the middle to hide the extra text. + If None, will not impose any size limits on the returning repr. + """ + super().__init__() + # ``maxstring`` is used by the superclass, and needs to be an int; using a + # very large number in case maxsize is None, meaning we want to disable + # truncation. + self.maxstring = maxsize if maxsize is not None else 1_000_000_000 + self.maxsize = maxsize + self.use_ascii = use_ascii + + def repr(self, x: object) -> str: + try: + if self.use_ascii: + s = ascii(x) + else: + s = super().repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + def repr_instance(self, x: object, level: int) -> str: + try: + s = repr(x) + except (KeyboardInterrupt, SystemExit): + raise + except BaseException as exc: + s = _format_repr_exception(exc, x) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s + + +def safeformat(obj: object) -> str: + """Return a pretty printed string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info. + """ + try: + return pprint.pformat(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) + + +# Maximum size of overall repr of objects to display during assertion errors. +DEFAULT_REPR_MAX_SIZE = 240 + + +def saferepr( + obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False +) -> str: + """Return a size-limited safe repr-string for the given object. + + Failing __repr__ functions of user instances will be represented + with a short exception info and 'saferepr' generally takes + care to never raise exceptions itself. + + This function is a wrapper around the Repr/reprlib functionality of the + stdlib. + """ + return SafeRepr(maxsize, use_ascii).repr(obj) + + +def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: + """Return an unlimited-size safe repr-string for the given object. + + As with saferepr, failing __repr__ functions of user instances + will be represented with a short exception info. + + This function is a wrapper around simple repr. + + Note: a cleaner solution would be to alter ``saferepr``this way + when maxsize=None, but that might affect some other code. + """ + try: + if use_ascii: + return ascii(obj) + return repr(obj) + except Exception as exc: + return _format_repr_exception(exc, obj) diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py b/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py new file mode 100644 index 0000000..9191b4e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py @@ -0,0 +1,258 @@ +"""Helper functions for writing to terminals and files.""" + +from __future__ import annotations + +from collections.abc import Sequence +import os +import shutil +import sys +from typing import final +from typing import Literal +from typing import TextIO + +import pygments +from pygments.formatters.terminal import TerminalFormatter +from pygments.lexer import Lexer +from pygments.lexers.diff import DiffLexer +from pygments.lexers.python import PythonLexer + +from ..compat import assert_never +from .wcwidth import wcswidth + + +# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. + + +def get_terminal_width() -> int: + width, _ = shutil.get_terminal_size(fallback=(80, 24)) + + # The Windows get_terminal_size may be bogus, let's sanify a bit. + if width < 40: + width = 80 + + return width + + +def should_do_markup(file: TextIO) -> bool: + if os.environ.get("PY_COLORS") == "1": + return True + if os.environ.get("PY_COLORS") == "0": + return False + if os.environ.get("NO_COLOR"): + return False + if os.environ.get("FORCE_COLOR"): + return True + return ( + hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" + ) + + +@final +class TerminalWriter: + _esctable = dict( + black=30, + red=31, + green=32, + yellow=33, + blue=34, + purple=35, + cyan=36, + white=37, + Black=40, + Red=41, + Green=42, + Yellow=43, + Blue=44, + Purple=45, + Cyan=46, + White=47, + bold=1, + light=2, + blink=5, + invert=7, + ) + + def __init__(self, file: TextIO | None = None) -> None: + if file is None: + file = sys.stdout + if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32": + try: + import colorama + except ImportError: + pass + else: + file = colorama.AnsiToWin32(file).stream + assert file is not None + self._file = file + self.hasmarkup = should_do_markup(file) + self._current_line = "" + self._terminal_width: int | None = None + self.code_highlight = True + + @property + def fullwidth(self) -> int: + if self._terminal_width is not None: + return self._terminal_width + return get_terminal_width() + + @fullwidth.setter + def fullwidth(self, value: int) -> None: + self._terminal_width = value + + @property + def width_of_current_line(self) -> int: + """Return an estimate of the width so far in the current line.""" + return wcswidth(self._current_line) + + def markup(self, text: str, **markup: bool) -> str: + for name in markup: + if name not in self._esctable: + raise ValueError(f"unknown markup: {name!r}") + if self.hasmarkup: + esc = [self._esctable[name] for name, on in markup.items() if on] + if esc: + text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m" + return text + + def sep( + self, + sepchar: str, + title: str | None = None, + fullwidth: int | None = None, + **markup: bool, + ) -> None: + if fullwidth is None: + fullwidth = self.fullwidth + # The goal is to have the line be as long as possible + # under the condition that len(line) <= fullwidth. + if sys.platform == "win32": + # If we print in the last column on windows we are on a + # new line but there is no way to verify/neutralize this + # (we may not know the exact line width). + # So let's be defensive to avoid empty lines in the output. + fullwidth -= 1 + if title is not None: + # we want 2 + 2*len(fill) + len(title) <= fullwidth + # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth + # 2*len(sepchar)*N <= fullwidth - len(title) - 2 + # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) + N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) + fill = sepchar * N + line = f"{fill} {title} {fill}" + else: + # we want len(sepchar)*N <= fullwidth + # i.e. N <= fullwidth // len(sepchar) + line = sepchar * (fullwidth // len(sepchar)) + # In some situations there is room for an extra sepchar at the right, + # in particular if we consider that with a sepchar like "_ " the + # trailing space is not important at the end of the line. + if len(line) + len(sepchar.rstrip()) <= fullwidth: + line += sepchar.rstrip() + + self.line(line, **markup) + + def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: + if msg: + current_line = msg.rsplit("\n", 1)[-1] + if "\n" in msg: + self._current_line = current_line + else: + self._current_line += current_line + + msg = self.markup(msg, **markup) + + self.write_raw(msg, flush=flush) + + def write_raw(self, msg: str, *, flush: bool = False) -> None: + try: + self._file.write(msg) + except UnicodeEncodeError: + # Some environments don't support printing general Unicode + # strings, due to misconfiguration or otherwise; in that case, + # print the string escaped to ASCII. + # When the Unicode situation improves we should consider + # letting the error propagate instead of masking it (see #7475 + # for one brief attempt). + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) + + if flush: + self.flush() + + def line(self, s: str = "", **markup: bool) -> None: + self.write(s, **markup) + self.write("\n") + + def flush(self) -> None: + self._file.flush() + + def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: + """Write lines of source code possibly highlighted. + + Keeping this private for now because the API is clunky. We should discuss how + to evolve the terminal writer so we can have more precise color support, for example + being able to write part of a line in one color and the rest in another, and so on. + """ + if indents and len(indents) != len(lines): + raise ValueError( + f"indents size ({len(indents)}) should have same size as lines ({len(lines)})" + ) + if not indents: + indents = [""] * len(lines) + source = "\n".join(lines) + new_lines = self._highlight(source).splitlines() + # Would be better to strict=True but that fails some CI jobs. + for indent, new_line in zip(indents, new_lines, strict=False): + self.line(indent + new_line) + + def _get_pygments_lexer(self, lexer: Literal["python", "diff"]) -> Lexer: + if lexer == "python": + return PythonLexer() + elif lexer == "diff": + return DiffLexer() + else: + assert_never(lexer) + + def _get_pygments_formatter(self) -> TerminalFormatter: + from _pytest.config.exceptions import UsageError + + theme = os.getenv("PYTEST_THEME") + theme_mode = os.getenv("PYTEST_THEME_MODE", "dark") + + try: + return TerminalFormatter(bg=theme_mode, style=theme) + except pygments.util.ClassNotFound as e: + raise UsageError( + f"PYTEST_THEME environment variable has an invalid value: '{theme}'. " + "Hint: See available pygments styles with `pygmentize -L styles`." + ) from e + except pygments.util.OptionError as e: + raise UsageError( + f"PYTEST_THEME_MODE environment variable has an invalid value: '{theme_mode}'. " + "The allowed values are 'dark' (default) and 'light'." + ) from e + + def _highlight( + self, source: str, lexer: Literal["diff", "python"] = "python" + ) -> str: + """Highlight the given source if we have markup support.""" + if not source or not self.hasmarkup or not self.code_highlight: + return source + + pygments_lexer = self._get_pygments_lexer(lexer) + pygments_formatter = self._get_pygments_formatter() + + highlighted: str = pygments.highlight( + source, pygments_lexer, pygments_formatter + ) + # pygments terminal formatter may add a newline when there wasn't one. + # We don't want this, remove. + if highlighted[-1] == "\n" and source[-1] != "\n": + highlighted = highlighted[:-1] + + # Some lexers will not set the initial color explicitly + # which may lead to the previous color being propagated to the + # start of the expression, so reset first. + highlighted = "\x1b[0m" + highlighted + + return highlighted diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py b/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py new file mode 100644 index 0000000..23886ff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from functools import lru_cache +import unicodedata + + +@lru_cache(100) +def wcwidth(c: str) -> int: + """Determine how many columns are needed to display a character in a terminal. + + Returns -1 if the character is not printable. + Returns 0, 1 or 2 for other characters. + """ + o = ord(c) + + # ASCII fast path. + if 0x20 <= o < 0x07F: + return 1 + + # Some Cf/Zp/Zl characters which should be zero-width. + if ( + o == 0x0000 + or 0x200B <= o <= 0x200F + or 0x2028 <= o <= 0x202E + or 0x2060 <= o <= 0x2063 + ): + return 0 + + category = unicodedata.category(c) + + # Control characters. + if category == "Cc": + return -1 + + # Combining characters with zero width. + if category in ("Me", "Mn"): + return 0 + + # Full/Wide east asian characters. + if unicodedata.east_asian_width(c) in ("F", "W"): + return 2 + + return 1 + + +def wcswidth(s: str) -> int: + """Determine how many columns are needed to display a string in a terminal. + + Returns -1 if the string contains non-printable characters. + """ + width = 0 + for c in unicodedata.normalize("NFC", s): + wc = wcwidth(c) + if wc < 0: + return -1 + width += wc + return width diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_py/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/error.py b/.venv/lib/python3.12/site-packages/_pytest/_py/error.py new file mode 100644 index 0000000..dace237 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_py/error.py @@ -0,0 +1,119 @@ +"""create errno-specific classes for IO or os calls.""" + +from __future__ import annotations + +from collections.abc import Callable +import errno +import os +import sys +from typing import TYPE_CHECKING +from typing import TypeVar + + +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + P = ParamSpec("P") + +R = TypeVar("R") + + +class Error(EnvironmentError): + def __repr__(self) -> str: + return "{}.{} {!r}: {} ".format( + self.__class__.__module__, + self.__class__.__name__, + self.__class__.__doc__, + " ".join(map(str, self.args)), + # repr(self.args) + ) + + def __str__(self) -> str: + s = "[{}]: {}".format( + self.__class__.__doc__, + " ".join(map(str, self.args)), + ) + return s + + +_winerrnomap = { + 2: errno.ENOENT, + 3: errno.ENOENT, + 17: errno.EEXIST, + 18: errno.EXDEV, + 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailable + 22: errno.ENOTDIR, + 20: errno.ENOTDIR, + 267: errno.ENOTDIR, + 5: errno.EACCES, # anything better? +} + + +class ErrorMaker: + """lazily provides Exception classes for each possible POSIX errno + (as defined per the 'errno' module). All such instances + subclass EnvironmentError. + """ + + _errno2class: dict[int, type[Error]] = {} + + def __getattr__(self, name: str) -> type[Error]: + if name[0] == "_": + raise AttributeError(name) + eno = getattr(errno, name) + cls = self._geterrnoclass(eno) + setattr(self, name, cls) + return cls + + def _geterrnoclass(self, eno: int) -> type[Error]: + try: + return self._errno2class[eno] + except KeyError: + clsname = errno.errorcode.get(eno, f"UnknownErrno{eno}") + errorcls = type( + clsname, + (Error,), + {"__module__": "py.error", "__doc__": os.strerror(eno)}, + ) + self._errno2class[eno] = errorcls + return errorcls + + def checked_call( + self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs + ) -> R: + """Call a function and raise an errno-exception if applicable.""" + __tracebackhide__ = True + try: + return func(*args, **kwargs) + except Error: + raise + except OSError as value: + if not hasattr(value, "errno"): + raise + if sys.platform == "win32": + try: + # error: Invalid index type "Optional[int]" for "dict[int, int]"; expected type "int" [index] + # OK to ignore because we catch the KeyError below. + cls = self._geterrnoclass(_winerrnomap[value.errno]) # type:ignore[index] + except KeyError: + raise value + else: + # we are not on Windows, or we got a proper OSError + if value.errno is None: + cls = type( + "UnknownErrnoNone", + (Error,), + {"__module__": "py.error", "__doc__": None}, + ) + else: + cls = self._geterrnoclass(value.errno) + + raise cls(f"{func.__name__}{args!r}") + + +_error_maker = ErrorMaker() +checked_call = _error_maker.checked_call + + +def __getattr__(attr: str) -> type[Error]: + return getattr(_error_maker, attr) # type: ignore[no-any-return] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/path.py b/.venv/lib/python3.12/site-packages/_pytest/_py/path.py new file mode 100644 index 0000000..998a781 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_py/path.py @@ -0,0 +1,1475 @@ +# mypy: allow-untyped-defs +"""local path implementation.""" + +from __future__ import annotations + +import atexit +from collections.abc import Callable +from contextlib import contextmanager +import fnmatch +import importlib.util +import io +import os +from os.path import abspath +from os.path import dirname +from os.path import exists +from os.path import isabs +from os.path import isdir +from os.path import isfile +from os.path import islink +from os.path import normpath +import posixpath +from stat import S_ISDIR +from stat import S_ISLNK +from stat import S_ISREG +import sys +from typing import Any +from typing import cast +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import uuid +import warnings + +from . import error + + +# Moved from local.py. +iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") + + +class Checkers: + _depend_on_existence = "exists", "link", "dir", "file" + + def __init__(self, path): + self.path = path + + def dotfile(self): + return self.path.basename.startswith(".") + + def ext(self, arg): + if not arg.startswith("."): + arg = "." + arg + return self.path.ext == arg + + def basename(self, arg): + return self.path.basename == arg + + def basestarts(self, arg): + return self.path.basename.startswith(arg) + + def relto(self, arg): + return self.path.relto(arg) + + def fnmatch(self, arg): + return self.path.fnmatch(arg) + + def endswith(self, arg): + return str(self.path).endswith(arg) + + def _evaluate(self, kw): + from .._code.source import getrawcode + + for name, value in kw.items(): + invert = False + meth = None + try: + meth = getattr(self, name) + except AttributeError: + if name[:3] == "not": + invert = True + try: + meth = getattr(self, name[3:]) + except AttributeError: + pass + if meth is None: + raise TypeError(f"no {name!r} checker available for {self.path!r}") + try: + if getrawcode(meth).co_argcount > 1: + if (not meth(value)) ^ invert: + return False + else: + if bool(value) ^ bool(meth()) ^ invert: + return False + except (error.ENOENT, error.ENOTDIR, error.EBUSY): + # EBUSY feels not entirely correct, + # but its kind of necessary since ENOMEDIUM + # is not accessible in python + for name in self._depend_on_existence: + if name in kw: + if kw.get(name): + return False + name = "not" + name + if name in kw: + if not kw.get(name): + return False + return True + + _statcache: Stat + + def _stat(self) -> Stat: + try: + return self._statcache + except AttributeError: + try: + self._statcache = self.path.stat() + except error.ELOOP: + self._statcache = self.path.lstat() + return self._statcache + + def dir(self): + return S_ISDIR(self._stat().mode) + + def file(self): + return S_ISREG(self._stat().mode) + + def exists(self): + return self._stat() + + def link(self): + st = self.path.lstat() + return S_ISLNK(st.mode) + + +class NeverRaised(Exception): + pass + + +class Visitor: + def __init__(self, fil, rec, ignore, bf, sort): + if isinstance(fil, (str, bytes)): + fil = FNMatcher(fil) + if isinstance(rec, str): + self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) + elif not hasattr(rec, "__call__") and rec: + self.rec = lambda path: True + else: + self.rec = rec + self.fil = fil + self.ignore = ignore + self.breadthfirst = bf + self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x) + + def gen(self, path): + try: + entries = path.listdir() + except self.ignore: + return + rec = self.rec + dirs = self.optsort( + [p for p in entries if p.check(dir=1) and (rec is None or rec(p))] + ) + if not self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + for p in self.optsort(entries): + if self.fil is None or self.fil(p): + yield p + if self.breadthfirst: + for subdir in dirs: + yield from self.gen(subdir) + + +class FNMatcher: + def __init__(self, pattern): + self.pattern = pattern + + def __call__(self, path): + pattern = self.pattern + + if ( + pattern.find(path.sep) == -1 + and iswin32 + and pattern.find(posixpath.sep) != -1 + ): + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posixpath.sep, path.sep) + + if pattern.find(path.sep) == -1: + name = path.basename + else: + name = str(path) # path.strpath # XXX svn? + if not os.path.isabs(pattern): + pattern = "*" + path.sep + pattern + return fnmatch.fnmatch(name, pattern) + + +def map_as_list(func, iter): + return list(map(func, iter)) + + +class Stat: + if TYPE_CHECKING: + + @property + def size(self) -> int: ... + + @property + def mtime(self) -> float: ... + + def __getattr__(self, name: str) -> Any: + return getattr(self._osstatresult, "st_" + name) + + def __init__(self, path, osstatresult): + self.path = path + self._osstatresult = osstatresult + + @property + def owner(self): + if iswin32: + raise NotImplementedError("XXX win32") + import pwd + + entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + @property + def group(self): + """Return group name of file.""" + if iswin32: + raise NotImplementedError("XXX win32") + import grp + + entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined,unused-ignore] + return entry[0] + + def isdir(self): + return S_ISDIR(self._osstatresult.st_mode) + + def isfile(self): + return S_ISREG(self._osstatresult.st_mode) + + def islink(self): + self.path.lstat() + return S_ISLNK(self._osstatresult.st_mode) + + +def getuserid(user): + import pwd + + if not isinstance(user, int): + user = pwd.getpwnam(user)[2] # type:ignore[attr-defined,unused-ignore] + return user + + +def getgroupid(group): + import grp + + if not isinstance(group, int): + group = grp.getgrnam(group)[2] # type:ignore[attr-defined,unused-ignore] + return group + + +class LocalPath: + """Object oriented interface to os.path and other local filesystem + related information. + """ + + class ImportMismatchError(ImportError): + """raised on pyimport() if there is a mismatch of __file__'s""" + + sep = os.sep + + def __init__(self, path=None, expanduser=False): + """Initialize and return a local Path instance. + + Path can be relative to the current directory. + If path is None it defaults to the current working directory. + If expanduser is True, tilde-expansion is performed. + Note that Path instances always carry an absolute path. + Note also that passing in a local path object will simply return + the exact same path object. Use new() to get a new copy. + """ + if path is None: + self.strpath = error.checked_call(os.getcwd) + else: + try: + path = os.fspath(path) + except TypeError: + raise ValueError( + "can only pass None, Path instances " + "or non-empty strings to LocalPath" + ) + if expanduser: + path = os.path.expanduser(path) + self.strpath = abspath(path) + + if sys.platform != "win32": + + def chown(self, user, group, rec=0): + """Change ownership to the given user and group. + user and group may be specified by a number or + by a name. if rec is True change ownership + recursively. + """ + uid = getuserid(user) + gid = getgroupid(group) + if rec: + for x in self.visit(rec=lambda x: x.check(link=0)): + if x.check(link=0): + error.checked_call(os.chown, str(x), uid, gid) + error.checked_call(os.chown, str(self), uid, gid) + + def readlink(self) -> str: + """Return value of a symbolic link.""" + # https://github.com/python/mypy/issues/12278 + return error.checked_call(os.readlink, self.strpath) # type: ignore[arg-type,return-value,unused-ignore] + + def mklinkto(self, oldname): + """Posix style hard link to another name.""" + error.checked_call(os.link, str(oldname), str(self)) + + def mksymlinkto(self, value, absolute=1): + """Create a symbolic link with the given value (pointing to another name).""" + if absolute: + error.checked_call(os.symlink, str(value), self.strpath) + else: + base = self.common(value) + # with posix local paths '/' is always a common base + relsource = self.__class__(value).relto(base) + reldest = self.relto(base) + n = reldest.count(self.sep) + target = self.sep.join(("..",) * n + (relsource,)) + error.checked_call(os.symlink, target, self.strpath) + + def __div__(self, other): + return self.join(os.fspath(other)) + + __truediv__ = __div__ # py3k + + @property + def basename(self): + """Basename part of path.""" + return self._getbyspec("basename")[0] + + @property + def dirname(self): + """Dirname part of path.""" + return self._getbyspec("dirname")[0] + + @property + def purebasename(self): + """Pure base name of the path.""" + return self._getbyspec("purebasename")[0] + + @property + def ext(self): + """Extension of the path (including the '.').""" + return self._getbyspec("ext")[0] + + def read_binary(self): + """Read and return a bytestring from reading the path.""" + with self.open("rb") as f: + return f.read() + + def read_text(self, encoding): + """Read and return a Unicode string from reading the path.""" + with self.open("r", encoding=encoding) as f: + return f.read() + + def read(self, mode="r"): + """Read and return a bytestring from reading the path.""" + with self.open(mode) as f: + return f.read() + + def readlines(self, cr=1): + """Read and return a list of lines from the path. if cr is False, the + newline will be removed from the end of each line.""" + mode = "r" + + if not cr: + content = self.read(mode) + return content.split("\n") + else: + f = self.open(mode) + try: + return f.readlines() + finally: + f.close() + + def load(self): + """(deprecated) return object unpickled from self.read()""" + f = self.open("rb") + try: + import pickle + + return error.checked_call(pickle.load, f) + finally: + f.close() + + def move(self, target): + """Move this path to target.""" + if target.relto(self): + raise error.EINVAL(target, "cannot move path into a subdirectory of itself") + try: + self.rename(target) + except error.EXDEV: # invalid cross-device link + self.copy(target) + self.remove() + + def fnmatch(self, pattern): + """Return true if the basename/fullname matches the glob-'pattern'. + + valid pattern characters:: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + If the pattern contains a path-separator then the full path + is used for pattern matching and a '*' is prepended to the + pattern. + + if the pattern doesn't contain a path-separator the pattern + is only matched against the basename. + """ + return FNMatcher(pattern)(self) + + def relto(self, relpath): + """Return a string which is the relative part of the path + to the given 'relpath'. + """ + if not isinstance(relpath, str | LocalPath): + raise TypeError(f"{relpath!r}: not a string or path object") + strrelpath = str(relpath) + if strrelpath and strrelpath[-1] != self.sep: + strrelpath += self.sep + # assert strrelpath[-1] == self.sep + # assert strrelpath[-2] != self.sep + strself = self.strpath + if sys.platform == "win32" or getattr(os, "_name", None) == "nt": + if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)): + return strself[len(strrelpath) :] + elif strself.startswith(strrelpath): + return strself[len(strrelpath) :] + return "" + + def ensure_dir(self, *args): + """Ensure the path joined with args is a directory.""" + return self.ensure(*args, dir=True) + + def bestrelpath(self, dest): + """Return a string which is a relative path from self + (assumed to be a directory) to dest such that + self.join(bestrelpath) == dest and if not such + path can be determined return dest. + """ + try: + if self == dest: + return os.curdir + base = self.common(dest) + if not base: # can be the case on windows + return str(dest) + self2base = self.relto(base) + reldest = dest.relto(base) + if self2base: + n = self2base.count(self.sep) + 1 + else: + n = 0 + lst = [os.pardir] * n + if reldest: + lst.append(reldest) + target = dest.sep.join(lst) + return target + except AttributeError: + return str(dest) + + def exists(self): + return self.check() + + def isdir(self): + return self.check(dir=1) + + def isfile(self): + return self.check(file=1) + + def parts(self, reverse=False): + """Return a root-first list of all ancestor directories + plus the path itself. + """ + current = self + lst = [self] + while 1: + last = current + current = current.dirpath() + if last == current: + break + lst.append(current) + if not reverse: + lst.reverse() + return lst + + def common(self, other): + """Return the common part shared with the other path + or None if there is no common part. + """ + last = None + for x, y in zip(self.parts(), other.parts()): + if x != y: + return last + last = x + return last + + def __add__(self, other): + """Return new path object with 'other' added to the basename""" + return self.new(basename=self.basename + str(other)) + + def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False): + """Yields all paths below the current one + + fil is a filter (glob pattern or callable), if not matching the + path will not be yielded, defaulting to None (everything is + returned) + + rec is a filter (glob pattern or callable) that controls whether + a node is descended, defaulting to None + + ignore is an Exception class that is ignoredwhen calling dirlist() + on any of the paths (by default, all exceptions are reported) + + bf if True will cause a breadthfirst search instead of the + default depthfirst. Default: False + + sort if True will sort entries within each directory level. + """ + yield from Visitor(fil, rec, ignore, bf, sort).gen(self) + + def _sortlist(self, res, sort): + if sort: + if hasattr(sort, "__call__"): + warnings.warn( + DeprecationWarning( + "listdir(sort=callable) is deprecated and breaks on python3" + ), + stacklevel=3, + ) + res.sort(sort) + else: + res.sort() + + def __fspath__(self): + return self.strpath + + def __hash__(self): + s = self.strpath + if iswin32: + s = s.lower() + return hash(s) + + def __eq__(self, other): + s1 = os.fspath(self) + try: + s2 = os.fspath(other) + except TypeError: + return False + if iswin32: + s1 = s1.lower() + try: + s2 = s2.lower() + except AttributeError: + return False + return s1 == s2 + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return os.fspath(self) < os.fspath(other) + + def __gt__(self, other): + return os.fspath(self) > os.fspath(other) + + def samefile(self, other): + """Return True if 'other' references the same file as 'self'.""" + other = os.fspath(other) + if not isabs(other): + other = abspath(other) + if self == other: + return True + if not hasattr(os.path, "samefile"): + return False + return error.checked_call(os.path.samefile, self.strpath, other) + + def remove(self, rec=1, ignore_errors=False): + """Remove a file or directory (or a directory tree if rec=1). + if ignore_errors is True, errors while removing directories will + be ignored. + """ + if self.check(dir=1, link=0): + if rec: + # force remove of readonly files on windows + if iswin32: + self.chmod(0o700, rec=1) + import shutil + + error.checked_call( + shutil.rmtree, self.strpath, ignore_errors=ignore_errors + ) + else: + error.checked_call(os.rmdir, self.strpath) + else: + if iswin32: + self.chmod(0o700) + error.checked_call(os.remove, self.strpath) + + def computehash(self, hashtype="md5", chunksize=524288): + """Return hexdigest of hashvalue for this file.""" + try: + try: + import hashlib as mod + except ImportError: + if hashtype == "sha1": + hashtype = "sha" + mod = __import__(hashtype) + hash = getattr(mod, hashtype)() + except (AttributeError, ImportError): + raise ValueError(f"Don't know how to compute {hashtype!r} hash") + f = self.open("rb") + try: + while 1: + buf = f.read(chunksize) + if not buf: + return hash.hexdigest() + hash.update(buf) + finally: + f.close() + + def new(self, **kw): + """Create a modified version of this path. + the following keyword arguments modify various path parts:: + + a:/some/path/to/a/file.ext + xx drive + xxxxxxxxxxxxxxxxx dirname + xxxxxxxx basename + xxxx purebasename + xxx ext + """ + obj = object.__new__(self.__class__) + if not kw: + obj.strpath = self.strpath + return obj + drive, dirname, _basename, purebasename, ext = self._getbyspec( + "drive,dirname,basename,purebasename,ext" + ) + if "basename" in kw: + if "purebasename" in kw or "ext" in kw: + raise ValueError(f"invalid specification {kw!r}") + else: + pb = kw.setdefault("purebasename", purebasename) + try: + ext = kw["ext"] + except KeyError: + pass + else: + if ext and not ext.startswith("."): + ext = "." + ext + kw["basename"] = pb + ext + + if "dirname" in kw and not kw["dirname"]: + kw["dirname"] = drive + else: + kw.setdefault("dirname", dirname) + kw.setdefault("sep", self.sep) + obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) + return obj + + def _getbyspec(self, spec: str) -> list[str]: + """See new for what 'spec' can be.""" + res = [] + parts = self.strpath.split(self.sep) + + args = filter(None, spec.split(",")) + for name in args: + if name == "drive": + res.append(parts[0]) + elif name == "dirname": + res.append(self.sep.join(parts[:-1])) + else: + basename = parts[-1] + if name == "basename": + res.append(basename) + else: + i = basename.rfind(".") + if i == -1: + purebasename, ext = basename, "" + else: + purebasename, ext = basename[:i], basename[i:] + if name == "purebasename": + res.append(purebasename) + elif name == "ext": + res.append(ext) + else: + raise ValueError(f"invalid part specification {name!r}") + return res + + def dirpath(self, *args, **kwargs): + """Return the directory path joined with any given path arguments.""" + if not kwargs: + path = object.__new__(self.__class__) + path.strpath = dirname(self.strpath) + if args: + path = path.join(*args) + return path + return self.new(basename="").join(*args, **kwargs) + + def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath: + """Return a new path by appending all 'args' as path + components. if abs=1 is used restart from root if any + of the args is an absolute path. + """ + sep = self.sep + strargs = [os.fspath(arg) for arg in args] + strpath = self.strpath + if abs: + newargs: list[str] = [] + for arg in reversed(strargs): + if isabs(arg): + strpath = arg + strargs = newargs + break + newargs.insert(0, arg) + # special case for when we have e.g. strpath == "/" + actual_sep = "" if strpath.endswith(sep) else sep + for arg in strargs: + arg = arg.strip(sep) + if iswin32: + # allow unix style paths even on windows. + arg = arg.strip("/") + arg = arg.replace("/", sep) + strpath = strpath + actual_sep + arg + actual_sep = sep + obj = object.__new__(self.__class__) + obj.strpath = normpath(strpath) + return obj + + def open(self, mode="r", ensure=False, encoding=None): + """Return an opened file with the given mode. + + If ensure is True, create parent directories if needed. + """ + if ensure: + self.dirpath().ensure(dir=1) + if encoding: + return error.checked_call( + io.open, + self.strpath, + mode, + encoding=encoding, + ) + return error.checked_call(open, self.strpath, mode) + + def _fastjoin(self, name): + child = object.__new__(self.__class__) + child.strpath = self.strpath + self.sep + name + return child + + def islink(self): + return islink(self.strpath) + + def check(self, **kw): + """Check a path for existence and properties. + + Without arguments, return True if the path exists, otherwise False. + + valid checkers:: + + file = 1 # is a file + file = 0 # is not a file (may not even exist) + dir = 1 # is a dir + link = 1 # is a link + exists = 1 # exists + + You can specify multiple checker definitions, for example:: + + path.check(file=1, link=1) # a link pointing to a file + """ + if not kw: + return exists(self.strpath) + if len(kw) == 1: + if "dir" in kw: + return not kw["dir"] ^ isdir(self.strpath) + if "file" in kw: + return not kw["file"] ^ isfile(self.strpath) + if not kw: + kw = {"exists": 1} + return Checkers(self)._evaluate(kw) + + _patternchars = set("*?[" + os.sep) + + def listdir(self, fil=None, sort=None): + """List directory contents, possibly filter by the given fil func + and possibly sorted. + """ + if fil is None and sort is None: + names = error.checked_call(os.listdir, self.strpath) + return map_as_list(self._fastjoin, names) + if isinstance(fil, str): + if not self._patternchars.intersection(fil): + child = self._fastjoin(fil) + if exists(child.strpath): + return [child] + return [] + fil = FNMatcher(fil) + names = error.checked_call(os.listdir, self.strpath) + res = [] + for name in names: + child = self._fastjoin(name) + if fil is None or fil(child): + res.append(child) + self._sortlist(res, sort) + return res + + def size(self) -> int: + """Return size of the underlying file object""" + return self.stat().size + + def mtime(self) -> float: + """Return last modification time of the path.""" + return self.stat().mtime + + def copy(self, target, mode=False, stat=False): + """Copy path to target. + + If mode is True, will copy permission from path to target. + If stat is True, copy permission, last modification + time, last access time, and flags from path to target. + """ + if self.check(file=1): + if target.check(dir=1): + target = target.join(self.basename) + assert self != target + copychunked(self, target) + if mode: + copymode(self.strpath, target.strpath) + if stat: + copystat(self, target) + else: + + def rec(p): + return p.check(link=0) + + for x in self.visit(rec=rec): + relpath = x.relto(self) + newx = target.join(relpath) + newx.dirpath().ensure(dir=1) + if x.check(link=1): + newx.mksymlinkto(x.readlink()) + continue + elif x.check(file=1): + copychunked(x, newx) + elif x.check(dir=1): + newx.ensure(dir=1) + if mode: + copymode(x.strpath, newx.strpath) + if stat: + copystat(x, newx) + + def rename(self, target): + """Rename this path to target.""" + target = os.fspath(target) + return error.checked_call(os.rename, self.strpath, target) + + def dump(self, obj, bin=1): + """Pickle object into path location""" + f = self.open("wb") + import pickle + + try: + error.checked_call(pickle.dump, obj, f, bin) + finally: + f.close() + + def mkdir(self, *args): + """Create & return the directory joined with args.""" + p = self.join(*args) + error.checked_call(os.mkdir, os.fspath(p)) + return p + + def write_binary(self, data, ensure=False): + """Write binary data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("wb") as f: + f.write(data) + + def write_text(self, data, encoding, ensure=False): + """Write text data into path using the specified encoding. + If ensure is True create missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + with self.open("w", encoding=encoding) as f: + f.write(data) + + def write(self, data, mode="w", ensure=False): + """Write data into path. If ensure is True create + missing parent directories. + """ + if ensure: + self.dirpath().ensure(dir=1) + if "b" in mode: + if not isinstance(data, bytes): + raise ValueError("can only process bytes") + else: + if not isinstance(data, str): + if not isinstance(data, bytes): + data = str(data) + else: + data = data.decode(sys.getdefaultencoding()) + f = self.open(mode) + try: + f.write(data) + finally: + f.close() + + def _ensuredirs(self): + parent = self.dirpath() + if parent == self: + return self + if parent.check(dir=0): + parent._ensuredirs() + if self.check(dir=0): + try: + self.mkdir() + except error.EEXIST: + # race condition: file/dir created by another thread/process. + # complain if it is not a dir + if self.check(dir=0): + raise + return self + + def ensure(self, *args, **kwargs): + """Ensure that an args-joined path exists (by default as + a file). if you specify a keyword argument 'dir=True' + then the path is forced to be a directory path. + """ + p = self.join(*args) + if kwargs.get("dir", 0): + return p._ensuredirs() + else: + p.dirpath()._ensuredirs() + if not p.check(file=1): + p.open("wb").close() + return p + + @overload + def stat(self, raising: Literal[True] = ...) -> Stat: ... + + @overload + def stat(self, raising: Literal[False]) -> Stat | None: ... + + def stat(self, raising: bool = True) -> Stat | None: + """Return an os.stat() tuple.""" + if raising: + return Stat(self, error.checked_call(os.stat, self.strpath)) + try: + return Stat(self, os.stat(self.strpath)) + except KeyboardInterrupt: + raise + except Exception: + return None + + def lstat(self) -> Stat: + """Return an os.lstat() tuple.""" + return Stat(self, error.checked_call(os.lstat, self.strpath)) + + def setmtime(self, mtime=None): + """Set modification time for the given path. if 'mtime' is None + (the default) then the file's mtime is set to current time. + + Note that the resolution for 'mtime' is platform dependent. + """ + if mtime is None: + return error.checked_call(os.utime, self.strpath, mtime) + try: + return error.checked_call(os.utime, self.strpath, (-1, mtime)) + except error.EINVAL: + return error.checked_call(os.utime, self.strpath, (self.atime(), mtime)) + + def chdir(self): + """Change directory to self and return old current directory""" + try: + old = self.__class__() + except error.ENOENT: + old = None + error.checked_call(os.chdir, self.strpath) + return old + + @contextmanager + def as_cwd(self): + """ + Return a context manager, which changes to the path's dir during the + managed "with" context. + On __enter__ it returns the old dir, which might be ``None``. + """ + old = self.chdir() + try: + yield old + finally: + if old is not None: + old.chdir() + + def realpath(self): + """Return a new path which contains no symbolic links.""" + return self.__class__(os.path.realpath(self.strpath)) + + def atime(self): + """Return last access time of the path.""" + return self.stat().atime + + def __repr__(self): + return f"local({self.strpath!r})" + + def __str__(self): + """Return string representation of the Path.""" + return self.strpath + + def chmod(self, mode, rec=0): + """Change permissions to the given mode. If mode is an + integer it directly encodes the os-specific modes. + if rec is True perform recursively. + """ + if not isinstance(mode, int): + raise TypeError(f"mode {mode!r} must be an integer") + if rec: + for x in self.visit(rec=rec): + error.checked_call(os.chmod, str(x), mode) + error.checked_call(os.chmod, self.strpath, mode) + + def pypkgpath(self): + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + Return None if a pkgpath cannot be determined. + """ + pkgpath = None + for parent in self.parts(reverse=True): + if parent.isdir(): + if not parent.join("__init__.py").exists(): + break + if not isimportable(parent.basename): + break + pkgpath = parent + return pkgpath + + def _ensuresyspath(self, ensuremode, path): + if ensuremode: + s = str(path) + if ensuremode == "append": + if s not in sys.path: + sys.path.append(s) + else: + if s != sys.path[0]: + sys.path.insert(0, s) + + def pyimport(self, modname=None, ensuresyspath=True): + """Return path as an imported python module. + + If modname is None, look for the containing package + and construct an according module name. + The module will be put/looked up in sys.modules. + if ensuresyspath is True then the root dir for importing + the file (taking __init__.py files into account) will + be prepended to sys.path if it isn't there already. + If ensuresyspath=="append" the root dir will be appended + if it isn't already contained in sys.path. + if ensuresyspath is False no modification of syspath happens. + + Special value of ensuresyspath=="importlib" is intended + purely for using in pytest, it is capable only of importing + separate .py files outside packages, e.g. for test suite + without any __init__.py file. It effectively allows having + same-named test modules in different places and offers + mild opt-in via this option. Note that it works only in + recent versions of python. + """ + if not self.check(): + raise error.ENOENT(self) + + if ensuresyspath == "importlib": + if modname is None: + modname = self.purebasename + spec = importlib.util.spec_from_file_location(modname, str(self)) + if spec is None or spec.loader is None: + raise ImportError(f"Can't find module {modname} at location {self!s}") + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + pkgpath = None + if modname is None: + pkgpath = self.pypkgpath() + if pkgpath is not None: + pkgroot = pkgpath.dirpath() + names = self.new(ext="").relto(pkgroot).split(self.sep) + if names[-1] == "__init__": + names.pop() + modname = ".".join(names) + else: + pkgroot = self.dirpath() + modname = self.purebasename + + self._ensuresyspath(ensuresyspath, pkgroot) + __import__(modname) + mod = sys.modules[modname] + if self.basename == "__init__.py": + return mod # we don't check anything as we might + # be in a namespace package ... too icky to check + modfile = mod.__file__ + assert modfile is not None + if modfile[-4:] in (".pyc", ".pyo"): + modfile = modfile[:-1] + elif modfile.endswith("$py.class"): + modfile = modfile[:-9] + ".py" + if modfile.endswith(os.sep + "__init__.py"): + if self.basename != "__init__.py": + modfile = modfile[:-12] + try: + issame = self.samefile(modfile) + except error.ENOENT: + issame = False + if not issame: + ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH") + if ignore != "1": + raise self.ImportMismatchError(modname, modfile, self) + return mod + else: + try: + return sys.modules[modname] + except KeyError: + # we have a custom modname, do a pseudo-import + import types + + mod = types.ModuleType(modname) + mod.__file__ = str(self) + sys.modules[modname] = mod + try: + with open(str(self), "rb") as f: + exec(f.read(), mod.__dict__) + except BaseException: + del sys.modules[modname] + raise + return mod + + def sysexec(self, *argv: os.PathLike[str], **popen_opts: Any) -> str: + """Return stdout text from executing a system child process, + where the 'self' path points to executable. + The process is directly invoked and not through a system shell. + """ + from subprocess import PIPE + from subprocess import Popen + + popen_opts.pop("stdout", None) + popen_opts.pop("stderr", None) + proc = Popen( + [str(self)] + [str(arg) for arg in argv], + **popen_opts, + stdout=PIPE, + stderr=PIPE, + ) + stdout: str | bytes + stdout, stderr = proc.communicate() + ret = proc.wait() + if isinstance(stdout, bytes): + stdout = stdout.decode(sys.getdefaultencoding()) + if ret != 0: + if isinstance(stderr, bytes): + stderr = stderr.decode(sys.getdefaultencoding()) + raise RuntimeError( + ret, + ret, + str(self), + stdout, + stderr, + ) + return stdout + + @classmethod + def sysfind(cls, name, checker=None, paths=None): + """Return a path object found by looking at the systems + underlying PATH specification. If the checker is not None + it will be invoked to filter matching paths. If a binary + cannot be found, None is returned + Note: This is probably not working on plain win32 systems + but may work on cygwin. + """ + if isabs(name): + p = local(name) + if p.check(file=1): + return p + else: + if paths is None: + if iswin32: + paths = os.environ["Path"].split(";") + if "" not in paths and "." not in paths: + paths.append(".") + try: + systemroot = os.environ["SYSTEMROOT"] + except KeyError: + pass + else: + paths = [ + path.replace("%SystemRoot%", systemroot) for path in paths + ] + else: + paths = os.environ["PATH"].split(":") + tryadd = [] + if iswin32: + tryadd += os.environ["PATHEXT"].split(os.pathsep) + tryadd.append("") + + for x in paths: + for addext in tryadd: + p = local(x).join(name, abs=True) + addext + try: + if p.check(file=1): + if checker: + if not checker(p): + continue + return p + except error.EACCES: + pass + return None + + @classmethod + def _gethomedir(cls): + try: + x = os.environ["HOME"] + except KeyError: + try: + x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + except KeyError: + return None + return cls(x) + + # """ + # special class constructors for local filesystem paths + # """ + @classmethod + def get_temproot(cls): + """Return the system's temporary directory + (where tempfiles are usually created in) + """ + import tempfile + + return local(tempfile.gettempdir()) + + @classmethod + def mkdtemp(cls, rootdir=None): + """Return a Path object pointing to a fresh new temporary directory + (which we created ourselves). + """ + import tempfile + + if rootdir is None: + rootdir = cls.get_temproot() + path = error.checked_call(tempfile.mkdtemp, dir=str(rootdir)) + return cls(path) + + @classmethod + def make_numbered_dir( + cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800 + ): # two days + """Return unique directory with a number greater than the current + maximum one. The number is assumed to start directly after prefix. + if keep is true directories with a number less than (maxnum-keep) + will be removed. If .lock files are used (lock_timeout non-zero), + algorithm is multi-process safe. + """ + if rootdir is None: + rootdir = cls.get_temproot() + + nprefix = prefix.lower() + + def parse_num(path): + """Parse the number out of a path (if it matches the prefix)""" + nbasename = path.basename.lower() + if nbasename.startswith(nprefix): + try: + return int(nbasename[len(nprefix) :]) + except ValueError: + pass + + def create_lockfile(path): + """Exclusively create lockfile. Throws when failed""" + mypid = os.getpid() + lockfile = path.join(".lock") + if hasattr(lockfile, "mksymlinkto"): + lockfile.mksymlinkto(str(mypid)) + else: + fd = error.checked_call( + os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 + ) + with os.fdopen(fd, "w") as f: + f.write(str(mypid)) + return lockfile + + def atexit_remove_lockfile(lockfile): + """Ensure lockfile is removed at process exit""" + mypid = os.getpid() + + def try_remove_lockfile(): + # in a fork() situation, only the last process should + # remove the .lock, otherwise the other processes run the + # risk of seeing their temporary dir disappear. For now + # we remove the .lock in the parent only (i.e. we assume + # that the children finish before the parent). + if os.getpid() != mypid: + return + try: + lockfile.remove() + except error.Error: + pass + + atexit.register(try_remove_lockfile) + + # compute the maximum number currently in use with the prefix + lastmax = None + while True: + maxnum = -1 + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None: + maxnum = max(maxnum, num) + + # make the new directory + try: + udir = rootdir.mkdir(prefix + str(maxnum + 1)) + if lock_timeout: + lockfile = create_lockfile(udir) + atexit_remove_lockfile(lockfile) + except (error.EEXIST, error.ENOENT, error.EBUSY): + # race condition (1): another thread/process created the dir + # in the meantime - try again + # race condition (2): another thread/process spuriously acquired + # lock treating empty directory as candidate + # for removal - try again + # race condition (3): another thread/process tried to create the lock at + # the same time (happened in Python 3.3 on Windows) + # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa + if lastmax == maxnum: + raise + lastmax = maxnum + continue + break + + def get_mtime(path): + """Read file modification time""" + try: + return path.lstat().mtime + except error.Error: + pass + + garbage_prefix = prefix + "garbage-" + + def is_garbage(path): + """Check if path denotes directory scheduled for removal""" + bn = path.basename + return bn.startswith(garbage_prefix) + + # prune old directories + udir_time = get_mtime(udir) + if keep and udir_time: + for path in rootdir.listdir(): + num = parse_num(path) + if num is not None and num <= (maxnum - keep): + try: + # try acquiring lock to remove directory as exclusive user + if lock_timeout: + create_lockfile(path) + except (error.EEXIST, error.ENOENT, error.EBUSY): + path_time = get_mtime(path) + if not path_time: + # assume directory doesn't exist now + continue + if abs(udir_time - path_time) < lock_timeout: + # assume directory with lockfile exists + # and lock timeout hasn't expired yet + continue + + # path dir locked for exclusive use + # and scheduled for removal to avoid another thread/process + # treating it as a new directory or removal candidate + garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4())) + try: + path.rename(garbage_path) + garbage_path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + if is_garbage(path): + try: + path.remove(rec=1) + except KeyboardInterrupt: + raise + except Exception: # this might be error.Error, WindowsError ... + pass + + # make link... + try: + username = os.environ["USER"] # linux, et al + except KeyError: + try: + username = os.environ["USERNAME"] # windows + except KeyError: + username = "current" + + src = str(udir) + dest = src[: src.rfind("-")] + "-" + username + try: + os.unlink(dest) + except OSError: + pass + try: + os.symlink(src, dest) + except (OSError, AttributeError, NotImplementedError): + pass + + return udir + + +def copymode(src, dest): + """Copy permission from src to dst.""" + import shutil + + shutil.copymode(src, dest) + + +def copystat(src, dest): + """Copy permission, last modification time, + last access time, and flags from src to dst.""" + import shutil + + shutil.copystat(str(src), str(dest)) + + +def copychunked(src, dest): + chunksize = 524288 # half a meg of bytes + fsrc = src.open("rb") + try: + fdest = dest.open("wb") + try: + while 1: + buf = fsrc.read(chunksize) + if not buf: + break + fdest.write(buf) + finally: + fdest.close() + finally: + fsrc.close() + + +def isimportable(name): + if name and (name[0].isalpha() or name[0] == "_"): + name = name.replace("_", "") + return not name or name.isalnum() + + +local = LocalPath diff --git a/.venv/lib/python3.12/site-packages/_pytest/_version.py b/.venv/lib/python3.12/site-packages/_pytest/_version.py new file mode 100644 index 0000000..98976dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/_version.py @@ -0,0 +1,24 @@ +# file generated by vcs-versioning +# don't change, don't track in version control +from __future__ import annotations + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +version: str +__version__: str +__version_tuple__: tuple[int | str, ...] +version_tuple: tuple[int | str, ...] +commit_id: str | None +__commit_id__: str | None + +__version__ = version = '9.0.3' +__version_tuple__ = version_tuple = (9, 0, 3) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py new file mode 100644 index 0000000..22f3ca8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py @@ -0,0 +1,208 @@ +# mypy: allow-untyped-defs +"""Support for presenting detailed information in failing assertions.""" + +from __future__ import annotations + +from collections.abc import Generator +import sys +from typing import Any +from typing import Protocol +from typing import TYPE_CHECKING + +from _pytest.assertion import rewrite +from _pytest.assertion import truncate +from _pytest.assertion import util +from _pytest.assertion.rewrite import assertstate_key +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item + + +if TYPE_CHECKING: + from _pytest.main import Session + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--assert", + action="store", + dest="assertmode", + choices=("rewrite", "plain"), + default="rewrite", + metavar="MODE", + help=( + "Control assertion debugging tools.\n" + "'plain' performs no assertion debugging.\n" + "'rewrite' (the default) rewrites assert statements in test modules" + " on import to provide assert expression information." + ), + ) + parser.addini( + "enable_assertion_pass_hook", + type="bool", + default=False, + help="Enables the pytest_assertion_pass hook. " + "Make sure to delete any previously generated pyc cache files.", + ) + + parser.addini( + "truncation_limit_lines", + default=None, + help="Set threshold of LINES after which truncation will take effect", + ) + parser.addini( + "truncation_limit_chars", + default=None, + help=("Set threshold of CHARS after which truncation will take effect"), + ) + + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_ASSERTIONS, + help=( + "Specify a verbosity level for assertions, overriding the main level. " + "Higher levels will provide more detailed explanation when an assertion fails." + ), + ) + + +def register_assert_rewrite(*names: str) -> None: + """Register one or more module names to be rewritten on import. + + This function will make sure that this module or all modules inside + the package will get their assert statements rewritten. + Thus you should make sure to call this before the module is + actually imported, usually in your __init__.py if you are a plugin + using a package. + + :param names: The module names to register. + """ + for name in names: + if not isinstance(name, str): + msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] + raise TypeError(msg.format(repr(names))) + rewrite_hook: RewriteHook + for hook in sys.meta_path: + if isinstance(hook, rewrite.AssertionRewritingHook): + rewrite_hook = hook + break + else: + rewrite_hook = DummyRewriteHook() + rewrite_hook.mark_rewrite(*names) + + +class RewriteHook(Protocol): + def mark_rewrite(self, *names: str) -> None: ... + + +class DummyRewriteHook: + """A no-op import hook for when rewriting is disabled.""" + + def mark_rewrite(self, *names: str) -> None: + pass + + +class AssertionState: + """State for the assertion plugin.""" + + def __init__(self, config: Config, mode) -> None: + self.mode = mode + self.trace = config.trace.root.get("assertion") + self.hook: rewrite.AssertionRewritingHook | None = None + + +def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: + """Try to install the rewrite hook, raise SystemError if it fails.""" + config.stash[assertstate_key] = AssertionState(config, "rewrite") + config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config.stash[assertstate_key].trace("installed rewrite import hook") + + def undo() -> None: + hook = config.stash[assertstate_key].hook + if hook is not None and hook in sys.meta_path: + sys.meta_path.remove(hook) + + config.add_cleanup(undo) + return hook + + +def pytest_collection(session: Session) -> None: + # This hook is only called when test modules are collected + # so for example not in the managing process of pytest-xdist + # (which does not collect test modules). + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(session) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks. + + The rewrite module will use util._reprcompare if it exists to use custom + reporting via the pytest_assertrepr_compare hook. This sets up this custom + comparison for the test. + """ + ihook = item.ihook + + def callbinrepr(op, left: object, right: object) -> str | None: + """Call the pytest_assertrepr_compare hook and prepare the result. + + This uses the first result from the hook and then ensures the + following: + * Overly verbose explanations are truncated unless configured otherwise + (eg. if running in verbose mode). + * Embedded newlines are escaped to help util.format_explanation() + later. + * If the rewrite mode is used embedded %-characters are replaced + to protect later % formatting. + + The result can be formatted by util.format_explanation() for + pretty printing. + """ + hook_result = ihook.pytest_assertrepr_compare( + config=item.config, op=op, left=left, right=right + ) + for new_expl in hook_result: + if new_expl: + new_expl = truncate.truncate_if_required(new_expl, item) + new_expl = [line.replace("\n", "\\n") for line in new_expl] + res = "\n~".join(new_expl) + if item.config.getvalue("assertmode") == "rewrite": + res = res.replace("%", "%%") + return res + return None + + saved_assert_hooks = util._reprcompare, util._assertion_pass + util._reprcompare = callbinrepr + util._config = item.config + + if ihook.pytest_assertion_pass.get_hookimpls(): + + def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: + ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl) + + util._assertion_pass = call_assertion_pass_hook + + try: + return (yield) + finally: + util._reprcompare, util._assertion_pass = saved_assert_hooks + util._config = None + + +def pytest_sessionfinish(session: Session) -> None: + assertstate = session.config.stash.get(assertstate_key, None) + if assertstate: + if assertstate.hook is not None: + assertstate.hook.set_session(None) + + +def pytest_assertrepr_compare( + config: Config, op: str, left: Any, right: Any +) -> list[str] | None: + return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py new file mode 100644 index 0000000..566549d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py @@ -0,0 +1,1202 @@ +"""Rewrite assertion AST to produce nice error messages.""" + +from __future__ import annotations + +import ast +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence +import errno +import functools +import importlib.abc +import importlib.machinery +import importlib.util +import io +import itertools +import marshal +import os +from pathlib import Path +from pathlib import PurePath +import struct +import sys +import tokenize +import types +from typing import IO +from typing import TYPE_CHECKING + + +if sys.version_info >= (3, 12): + from importlib.resources.abc import TraversableResources +else: + from importlib.abc import TraversableResources +if sys.version_info < (3, 11): + from importlib.readers import FileReader +else: + from importlib.resources.readers import FileReader + + +from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE +from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr_unlimited +from _pytest._version import version +from _pytest.assertion import util +from _pytest.config import Config +from _pytest.fixtures import FixtureFunctionDefinition +from _pytest.main import Session +from _pytest.pathlib import absolutepath +from _pytest.pathlib import fnmatch_ex +from _pytest.stash import StashKey + + +# fmt: off +from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip +# fmt:on + +if TYPE_CHECKING: + from _pytest.assertion import AssertionState + + +class Sentinel: + pass + + +assertstate_key = StashKey["AssertionState"]() + +# pytest caches rewritten pycs in pycache dirs +PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" +PYC_EXT = ".py" + ((__debug__ and "c") or "o") +PYC_TAIL = "." + PYTEST_TAG + PYC_EXT + +# Special marker that denotes we have just left a scope definition +_SCOPE_END_MARKER = Sentinel() + + +class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): + """PEP302/PEP451 import hook which rewrites asserts.""" + + def __init__(self, config: Config) -> None: + self.config = config + try: + self.fnpats = config.getini("python_files") + except ValueError: + self.fnpats = ["test_*.py", "*_test.py"] + self.session: Session | None = None + self._rewritten_names: dict[str, Path] = {} + self._must_rewrite: set[str] = set() + # flag to guard against trying to rewrite a pyc file while we are already writing another pyc file, + # which might result in infinite recursion (#3506) + self._writing_pyc = False + self._basenames_to_check_rewrite = {"conftest"} + self._marked_for_rewrite_cache: dict[str, bool] = {} + self._session_paths_checked = False + + def set_session(self, session: Session | None) -> None: + self.session = session + self._session_paths_checked = False + + # Indirection so we can mock calls to find_spec originated from the hook during testing + _find_spec = importlib.machinery.PathFinder.find_spec + + def find_spec( + self, + name: str, + path: Sequence[str | bytes] | None = None, + target: types.ModuleType | None = None, + ) -> importlib.machinery.ModuleSpec | None: + if self._writing_pyc: + return None + state = self.config.stash[assertstate_key] + if self._early_rewrite_bailout(name, state): + return None + state.trace(f"find_module called for: {name}") + + # Type ignored because mypy is confused about the `self` binding here. + spec = self._find_spec(name, path) # type: ignore + + if spec is None and path is not None: + # With --import-mode=importlib, PathFinder cannot find spec without modifying `sys.path`, + # causing inability to assert rewriting (#12659). + # At this point, try using the file path to find the module spec. + for _path_str in path: + spec = importlib.util.spec_from_file_location(name, _path_str) + if spec is not None: + break + + if ( + # the import machinery could not find a file to import + spec is None + # this is a namespace package (without `__init__.py`) + # there's nothing to rewrite there + or spec.origin is None + # we can only rewrite source files + or not isinstance(spec.loader, importlib.machinery.SourceFileLoader) + # if the file doesn't exist, we can't rewrite it + or not os.path.exists(spec.origin) + ): + return None + else: + fn = spec.origin + + if not self._should_rewrite(name, fn, state): + return None + + return importlib.util.spec_from_file_location( + name, + fn, + loader=self, + submodule_search_locations=spec.submodule_search_locations, + ) + + def create_module( + self, spec: importlib.machinery.ModuleSpec + ) -> types.ModuleType | None: + return None # default behaviour is fine + + def exec_module(self, module: types.ModuleType) -> None: + assert module.__spec__ is not None + assert module.__spec__.origin is not None + fn = Path(module.__spec__.origin) + state = self.config.stash[assertstate_key] + + self._rewritten_names[module.__name__] = fn + + # The requested module looks like a test file, so rewrite it. This is + # the most magical part of the process: load the source, rewrite the + # asserts, and load the rewritten source. We also cache the rewritten + # module code in a special pyc. We must be aware of the possibility of + # concurrent pytest processes rewriting and loading pycs. To avoid + # tricky race conditions, we maintain the following invariant: The + # cached pyc is always a complete, valid pyc. Operations on it must be + # atomic. POSIX's atomic rename comes in handy. + write = not sys.dont_write_bytecode + cache_dir = get_cache_dir(fn) + if write: + ok = try_makedirs(cache_dir) + if not ok: + write = False + state.trace(f"read only directory: {cache_dir}") + + cache_name = fn.name[:-3] + PYC_TAIL + pyc = cache_dir / cache_name + # Notice that even if we're in a read-only directory, I'm going + # to check for a cached pyc. This may not be optimal... + co = _read_pyc(fn, pyc, state.trace) + if co is None: + state.trace(f"rewriting {fn!r}") + source_stat, co = _rewrite_test(fn, self.config) + if write: + self._writing_pyc = True + try: + _write_pyc(state, co, source_stat, pyc) + finally: + self._writing_pyc = False + else: + state.trace(f"found cached rewritten pyc for {fn}") + exec(co, module.__dict__) + + def _early_rewrite_bailout(self, name: str, state: AssertionState) -> bool: + """A fast way to get out of rewriting modules. + + Profiling has shown that the call to PathFinder.find_spec (inside of + the find_spec from this class) is a major slowdown, so, this method + tries to filter what we're sure won't be rewritten before getting to + it. + """ + if self.session is not None and not self._session_paths_checked: + self._session_paths_checked = True + for initial_path in self.session._initialpaths: + # Make something as c:/projects/my_project/path.py -> + # ['c:', 'projects', 'my_project', 'path.py'] + parts = str(initial_path).split(os.sep) + # add 'path' to basenames to be checked. + self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0]) + + # Note: conftest already by default in _basenames_to_check_rewrite. + parts = name.split(".") + if parts[-1] in self._basenames_to_check_rewrite: + return False + + # For matching the name it must be as if it was a filename. + path = PurePath(*parts).with_suffix(".py") + + for pat in self.fnpats: + # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based + # on the name alone because we need to match against the full path + if os.path.dirname(pat): + return False + if fnmatch_ex(pat, path): + return False + + if self._is_marked_for_rewrite(name, state): + return False + + state.trace(f"early skip of rewriting module: {name}") + return True + + def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool: + # always rewrite conftest files + if os.path.basename(fn) == "conftest.py": + state.trace(f"rewriting conftest file: {fn!r}") + return True + + if self.session is not None: + if self.session.isinitpath(absolutepath(fn)): + state.trace(f"matched test file (was specified on cmdline): {fn!r}") + return True + + # modules not passed explicitly on the command line are only + # rewritten if they match the naming convention for test files + fn_path = PurePath(fn) + for pat in self.fnpats: + if fnmatch_ex(pat, fn_path): + state.trace(f"matched test file {fn!r}") + return True + + return self._is_marked_for_rewrite(name, state) + + def _is_marked_for_rewrite(self, name: str, state: AssertionState) -> bool: + try: + return self._marked_for_rewrite_cache[name] + except KeyError: + for marked in self._must_rewrite: + if name == marked or name.startswith(marked + "."): + state.trace(f"matched marked file {name!r} (from {marked!r})") + self._marked_for_rewrite_cache[name] = True + return True + + self._marked_for_rewrite_cache[name] = False + return False + + def mark_rewrite(self, *names: str) -> None: + """Mark import names as needing to be rewritten. + + The named module or package as well as any nested modules will + be rewritten on import. + """ + already_imported = ( + set(names).intersection(sys.modules).difference(self._rewritten_names) + ) + for name in already_imported: + mod = sys.modules[name] + if not AssertionRewriter.is_rewrite_disabled( + mod.__doc__ or "" + ) and not isinstance(mod.__loader__, type(self)): + self._warn_already_imported(name) + self._must_rewrite.update(names) + self._marked_for_rewrite_cache.clear() + + def _warn_already_imported(self, name: str) -> None: + from _pytest.warning_types import PytestAssertRewriteWarning + + self.config.issue_config_time_warning( + PytestAssertRewriteWarning( + f"Module already imported so cannot be rewritten; {name}" + ), + stacklevel=5, + ) + + def get_data(self, pathname: str | bytes) -> bytes: + """Optional PEP302 get_data API.""" + with open(pathname, "rb") as f: + return f.read() + + def get_resource_reader(self, name: str) -> TraversableResources: + return FileReader(types.SimpleNamespace(path=self._rewritten_names[name])) # type: ignore[arg-type] + + +def _write_pyc_fp( + fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType +) -> None: + # Technically, we don't have to have the same pyc format as + # (C)Python, since these "pycs" should never be seen by builtin + # import. However, there's little reason to deviate. + fp.write(importlib.util.MAGIC_NUMBER) + # https://www.python.org/dev/peps/pep-0552/ + flags = b"\x00\x00\x00\x00" + fp.write(flags) + # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) + mtime = int(source_stat.st_mtime) & 0xFFFFFFFF + size = source_stat.st_size & 0xFFFFFFFF + # " bool: + proc_pyc = f"{pyc}.{os.getpid()}" + try: + with open(proc_pyc, "wb") as fp: + _write_pyc_fp(fp, source_stat, co) + except OSError as e: + state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") + return False + + try: + os.replace(proc_pyc, pyc) + except OSError as e: + state.trace(f"error writing pyc file at {pyc}: {e}") + # we ignore any failure to write the cache file + # there are many reasons, permission-denied, pycache dir being a + # file etc. + return False + return True + + +def _rewrite_test(fn: Path, config: Config) -> tuple[os.stat_result, types.CodeType]: + """Read and rewrite *fn* and return the code object.""" + stat = os.stat(fn) + source = fn.read_bytes() + strfn = str(fn) + tree = ast.parse(source, filename=strfn) + rewrite_asserts(tree, source, strfn, config) + co = compile(tree, strfn, "exec", dont_inherit=True) + return stat, co + + +def _read_pyc( + source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None +) -> types.CodeType | None: + """Possibly read a pytest pyc containing rewritten code. + + Return rewritten code if successful or None if not. + """ + try: + fp = open(pyc, "rb") + except OSError: + return None + with fp: + try: + stat_result = os.stat(source) + mtime = int(stat_result.st_mtime) + size = stat_result.st_size + data = fp.read(16) + except OSError as e: + trace(f"_read_pyc({source}): OSError {e}") + return None + # Check for invalid or out of date pyc file. + if len(data) != (16): + trace(f"_read_pyc({source}): invalid pyc (too short)") + return None + if data[:4] != importlib.util.MAGIC_NUMBER: + trace(f"_read_pyc({source}): invalid pyc (bad magic number)") + return None + if data[4:8] != b"\x00\x00\x00\x00": + trace(f"_read_pyc({source}): invalid pyc (unsupported flags)") + return None + mtime_data = data[8:12] + if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: + trace(f"_read_pyc({source}): out of date") + return None + size_data = data[12:16] + if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: + trace(f"_read_pyc({source}): invalid pyc (incorrect size)") + return None + try: + co = marshal.load(fp) + except Exception as e: + trace(f"_read_pyc({source}): marshal.load error {e}") + return None + if not isinstance(co, types.CodeType): + trace(f"_read_pyc({source}): not a code object") + return None + return co + + +def rewrite_asserts( + mod: ast.Module, + source: bytes, + module_path: str | None = None, + config: Config | None = None, +) -> None: + """Rewrite the assert statements in mod.""" + AssertionRewriter(module_path, config, source).run(mod) + + +def _saferepr(obj: object) -> str: + r"""Get a safe repr of an object for assertion error messages. + + The assertion formatting (util.format_explanation()) requires + newlines to be escaped since they are a special character for it. + Normally assertion.util.format_explanation() does this but for a + custom repr it is possible to contain one of the special escape + sequences, especially '\n{' and '\n}' are likely to be present in + JSON reprs. + """ + if isinstance(obj, types.MethodType): + # for bound methods, skip redundant information + return obj.__name__ + + maxsize = _get_maxsize_for_saferepr(util._config) + if not maxsize: + return saferepr_unlimited(obj).replace("\n", "\\n") + return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") + + +def _get_maxsize_for_saferepr(config: Config | None) -> int | None: + """Get `maxsize` configuration for saferepr based on the given config object.""" + if config is None: + verbosity = 0 + else: + verbosity = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + if verbosity >= 2: + return None + if verbosity >= 1: + return DEFAULT_REPR_MAX_SIZE * 10 + return DEFAULT_REPR_MAX_SIZE + + +def _format_assertmsg(obj: object) -> str: + r"""Format the custom assertion message given. + + For strings this simply replaces newlines with '\n~' so that + util.format_explanation() will preserve them instead of escaping + newlines. For other objects saferepr() is used first. + """ + # reprlib appears to have a bug which means that if a string + # contains a newline it gets escaped, however if an object has a + # .__repr__() which contains newlines it does not get escaped. + # However in either case we want to preserve the newline. + replaces = [("\n", "\n~"), ("%", "%%")] + if not isinstance(obj, str): + obj = saferepr(obj, _get_maxsize_for_saferepr(util._config)) + replaces.append(("\\n", "\n~")) + + for r1, r2 in replaces: + obj = obj.replace(r1, r2) + + return obj + + +def _should_repr_global_name(obj: object) -> bool: + if callable(obj): + # For pytest fixtures the __repr__ method provides more information than the function name. + return isinstance(obj, FixtureFunctionDefinition) + + try: + return not hasattr(obj, "__name__") + except Exception: + return True + + +def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: + explanation = "(" + ((is_or and " or ") or " and ").join(explanations) + ")" + return explanation.replace("%", "%%") + + +def _call_reprcompare( + ops: Sequence[str], + results: Sequence[bool], + expls: Sequence[str], + each_obj: Sequence[object], +) -> str: + for i, res, expl in zip(range(len(ops)), results, expls, strict=True): + try: + done = not res + except Exception: + done = True + if done: + break + if util._reprcompare is not None: + custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1]) + if custom is not None: + return custom + return expl + + +def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None: + if util._assertion_pass is not None: + util._assertion_pass(lineno, orig, expl) + + +def _check_if_assertion_pass_impl() -> bool: + """Check if any plugins implement the pytest_assertion_pass hook + in order not to generate explanation unnecessarily (might be expensive).""" + return True if util._assertion_pass else False + + +UNARY_MAP = {ast.Not: "not %s", ast.Invert: "~%s", ast.USub: "-%s", ast.UAdd: "+%s"} + +BINOP_MAP = { + ast.BitOr: "|", + ast.BitXor: "^", + ast.BitAnd: "&", + ast.LShift: "<<", + ast.RShift: ">>", + ast.Add: "+", + ast.Sub: "-", + ast.Mult: "*", + ast.Div: "/", + ast.FloorDiv: "//", + ast.Mod: "%%", # escaped for string formatting + ast.Eq: "==", + ast.NotEq: "!=", + ast.Lt: "<", + ast.LtE: "<=", + ast.Gt: ">", + ast.GtE: ">=", + ast.Pow: "**", + ast.Is: "is", + ast.IsNot: "is not", + ast.In: "in", + ast.NotIn: "not in", + ast.MatMult: "@", +} + + +def traverse_node(node: ast.AST) -> Iterator[ast.AST]: + """Recursively yield node and all its children in depth-first order.""" + yield node + for child in ast.iter_child_nodes(node): + yield from traverse_node(child) + + +@functools.lru_cache(maxsize=1) +def _get_assertion_exprs(src: bytes) -> dict[int, str]: + """Return a mapping from {lineno: "assertion test expression"}.""" + ret: dict[int, str] = {} + + depth = 0 + lines: list[str] = [] + assert_lineno: int | None = None + seen_lines: set[int] = set() + + def _write_and_reset() -> None: + nonlocal depth, lines, assert_lineno, seen_lines + assert assert_lineno is not None + ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\") + depth = 0 + lines = [] + assert_lineno = None + seen_lines = set() + + tokens = tokenize.tokenize(io.BytesIO(src).readline) + for tp, source, (lineno, offset), _, line in tokens: + if tp == tokenize.NAME and source == "assert": + assert_lineno = lineno + elif assert_lineno is not None: + # keep track of depth for the assert-message `,` lookup + if tp == tokenize.OP and source in "([{": + depth += 1 + elif tp == tokenize.OP and source in ")]}": + depth -= 1 + + if not lines: + lines.append(line[offset:]) + seen_lines.add(lineno) + # a non-nested comma separates the expression from the message + elif depth == 0 and tp == tokenize.OP and source == ",": + # one line assert with message + if lineno in seen_lines and len(lines) == 1: + offset_in_trimmed = offset + len(lines[-1]) - len(line) + lines[-1] = lines[-1][:offset_in_trimmed] + # multi-line assert with message + elif lineno in seen_lines: + lines[-1] = lines[-1][:offset] + # multi line assert with escaped newline before message + else: + lines.append(line[:offset]) + _write_and_reset() + elif tp in {tokenize.NEWLINE, tokenize.ENDMARKER}: + _write_and_reset() + elif lines and lineno not in seen_lines: + lines.append(line) + seen_lines.add(lineno) + + return ret + + +class AssertionRewriter(ast.NodeVisitor): + """Assertion rewriting implementation. + + The main entrypoint is to call .run() with an ast.Module instance, + this will then find all the assert statements and rewrite them to + provide intermediate values and a detailed assertion error. See + http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html + for an overview of how this works. + + The entry point here is .run() which will iterate over all the + statements in an ast.Module and for each ast.Assert statement it + finds call .visit() with it. Then .visit_Assert() takes over and + is responsible for creating new ast statements to replace the + original assert statement: it rewrites the test of an assertion + to provide intermediate values and replace it with an if statement + which raises an assertion error with a detailed explanation in + case the expression is false and calls pytest_assertion_pass hook + if expression is true. + + For this .visit_Assert() uses the visitor pattern to visit all the + AST nodes of the ast.Assert.test field, each visit call returning + an AST node and the corresponding explanation string. During this + state is kept in several instance attributes: + + :statements: All the AST statements which will replace the assert + statement. + + :variables: This is populated by .variable() with each variable + used by the statements so that they can all be set to None at + the end of the statements. + + :variable_counter: Counter to create new unique variables needed + by statements. Variables are created using .variable() and + have the form of "@py_assert0". + + :expl_stmts: The AST statements which will be executed to get + data from the assertion. This is the code which will construct + the detailed assertion message that is used in the AssertionError + or for the pytest_assertion_pass hook. + + :explanation_specifiers: A dict filled by .explanation_param() + with %-formatting placeholders and their corresponding + expressions to use in the building of an assertion message. + This is used by .pop_format_context() to build a message. + + :stack: A stack of the explanation_specifiers dicts maintained by + .push_format_context() and .pop_format_context() which allows + to build another %-formatted string while already building one. + + :scope: A tuple containing the current scope used for variables_overwrite. + + :variables_overwrite: A dict filled with references to variables + that change value within an assert. This happens when a variable is + reassigned with the walrus operator + + This state, except the variables_overwrite, is reset on every new assert + statement visited and used by the other visitors. + """ + + def __init__( + self, module_path: str | None, config: Config | None, source: bytes + ) -> None: + super().__init__() + self.module_path = module_path + self.config = config + if config is not None: + self.enable_assertion_pass_hook = config.getini( + "enable_assertion_pass_hook" + ) + else: + self.enable_assertion_pass_hook = False + self.source = source + self.scope: tuple[ast.AST, ...] = () + self.variables_overwrite: defaultdict[tuple[ast.AST, ...], dict[str, str]] = ( + defaultdict(dict) + ) + + def run(self, mod: ast.Module) -> None: + """Find all assert statements in *mod* and rewrite them.""" + if not mod.body: + # Nothing to do. + return + + # We'll insert some special imports at the top of the module, but after any + # docstrings and __future__ imports, so first figure out where that is. + doc = getattr(mod, "docstring", None) + expect_docstring = doc is None + if doc is not None and self.is_rewrite_disabled(doc): + return + pos = 0 + for item in mod.body: + match item: + case ast.Expr(value=ast.Constant(value=str() as doc)) if ( + expect_docstring + ): + if self.is_rewrite_disabled(doc): + return + expect_docstring = False + case ast.ImportFrom(level=0, module="__future__"): + pass + case _: + break + pos += 1 + # Special case: for a decorated function, set the lineno to that of the + # first decorator, not the `def`. Issue #4984. + if isinstance(item, ast.FunctionDef) and item.decorator_list: + lineno = item.decorator_list[0].lineno + else: + lineno = item.lineno + # Now actually insert the special imports. + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, + ), + ] + imports = [ + ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases + ] + mod.body[pos:pos] = imports + + # Collect asserts. + self.scope = (mod,) + nodes: list[ast.AST | Sentinel] = [mod] + while nodes: + node = nodes.pop() + if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef): + self.scope = tuple((*self.scope, node)) + nodes.append(_SCOPE_END_MARKER) + if node == _SCOPE_END_MARKER: + self.scope = self.scope[:-1] + continue + assert isinstance(node, ast.AST) + for name, field in ast.iter_fields(node): + if isinstance(field, list): + new: list[ast.AST] = [] + for i, child in enumerate(field): + if isinstance(child, ast.Assert): + # Transform assert. + new.extend(self.visit(child)) + else: + new.append(child) + if isinstance(child, ast.AST): + nodes.append(child) + setattr(node, name, new) + elif ( + isinstance(field, ast.AST) + # Don't recurse into expressions as they can't contain + # asserts. + and not isinstance(field, ast.expr) + ): + nodes.append(field) + + @staticmethod + def is_rewrite_disabled(docstring: str) -> bool: + return "PYTEST_DONT_REWRITE" in docstring + + def variable(self) -> str: + """Get a new variable.""" + # Use a character invalid in python identifiers to avoid clashing. + name = "@py_assert" + str(next(self.variable_counter)) + self.variables.append(name) + return name + + def assign(self, expr: ast.expr) -> ast.Name: + """Give *expr* a name.""" + name = self.variable() + self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr)) + return ast.copy_location(ast.Name(name, ast.Load()), expr) + + def display(self, expr: ast.expr) -> ast.expr: + """Call saferepr on the expression.""" + return self.helper("_saferepr", expr) + + def helper(self, name: str, *args: ast.expr) -> ast.expr: + """Call a helper in this module.""" + py_name = ast.Name("@pytest_ar", ast.Load()) + attr = ast.Attribute(py_name, name, ast.Load()) + return ast.Call(attr, list(args), []) + + def builtin(self, name: str) -> ast.Attribute: + """Return the builtin called *name*.""" + builtin_name = ast.Name("@py_builtins", ast.Load()) + return ast.Attribute(builtin_name, name, ast.Load()) + + def explanation_param(self, expr: ast.expr) -> str: + """Return a new named %-formatting placeholder for expr. + + This creates a %-formatting placeholder for expr in the + current formatting context, e.g. ``%(py0)s``. The placeholder + and expr are placed in the current format context so that it + can be used on the next call to .pop_format_context(). + """ + specifier = "py" + str(next(self.variable_counter)) + self.explanation_specifiers[specifier] = expr + return "%(" + specifier + ")s" + + def push_format_context(self) -> None: + """Create a new formatting context. + + The format context is used for when an explanation wants to + have a variable value formatted in the assertion message. In + this case the value required can be added using + .explanation_param(). Finally .pop_format_context() is used + to format a string of %-formatted values as added by + .explanation_param(). + """ + self.explanation_specifiers: dict[str, ast.expr] = {} + self.stack.append(self.explanation_specifiers) + + def pop_format_context(self, expl_expr: ast.expr) -> ast.Name: + """Format the %-formatted string with current format context. + + The expl_expr should be an str ast.expr instance constructed from + the %-placeholders created by .explanation_param(). This will + add the required code to format said string to .expl_stmts and + return the ast.Name instance of the formatted string. + """ + current = self.stack.pop() + if self.stack: + self.explanation_specifiers = self.stack[-1] + keys: list[ast.expr | None] = [ast.Constant(key) for key in current.keys()] + format_dict = ast.Dict(keys, list(current.values())) + form = ast.BinOp(expl_expr, ast.Mod(), format_dict) + name = "@py_format" + str(next(self.variable_counter)) + if self.enable_assertion_pass_hook: + self.format_variables.append(name) + self.expl_stmts.append(ast.Assign([ast.Name(name, ast.Store())], form)) + return ast.Name(name, ast.Load()) + + def generic_visit(self, node: ast.AST) -> tuple[ast.Name, str]: + """Handle expressions we don't have custom code for.""" + assert isinstance(node, ast.expr) + res = self.assign(node) + return res, self.explanation_param(self.display(res)) + + def visit_Assert(self, assert_: ast.Assert) -> list[ast.stmt]: + """Return the AST statements to replace the ast.Assert instance. + + This rewrites the test of an assertion to provide + intermediate values and replace it with an if statement which + raises an assertion error with a detailed explanation in case + the expression is false. + """ + if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: + import warnings + + from _pytest.warning_types import PytestAssertRewriteWarning + + # TODO: This assert should not be needed. + assert self.module_path is not None + warnings.warn_explicit( + PytestAssertRewriteWarning( + "assertion is always true, perhaps remove parentheses?" + ), + category=None, + filename=self.module_path, + lineno=assert_.lineno, + ) + + self.statements: list[ast.stmt] = [] + self.variables: list[str] = [] + self.variable_counter = itertools.count() + + if self.enable_assertion_pass_hook: + self.format_variables: list[str] = [] + + self.stack: list[dict[str, ast.expr]] = [] + self.expl_stmts: list[ast.stmt] = [] + self.push_format_context() + # Rewrite assert into a bunch of statements. + top_condition, explanation = self.visit(assert_.test) + + negation = ast.UnaryOp(ast.Not(), top_condition) + + if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook + msg = self.pop_format_context(ast.Constant(explanation)) + + # Failed + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + gluestr = "\n>assert " + else: + assertmsg = ast.Constant("") + gluestr = "assert " + err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg) + err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) + err_name = ast.Name("AssertionError", ast.Load()) + fmt = self.helper("_format_explanation", err_msg) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + statements_fail = [] + statements_fail.extend(self.expl_stmts) + statements_fail.append(raise_) + + # Passed + fmt_pass = self.helper("_format_explanation", msg) + orig = _get_assertion_exprs(self.source)[assert_.lineno] + hook_call_pass = ast.Expr( + self.helper( + "_call_assertion_pass", + ast.Constant(assert_.lineno), + ast.Constant(orig), + fmt_pass, + ) + ) + # If any hooks implement assert_pass hook + hook_impl_test = ast.If( + self.helper("_check_if_assertion_pass_impl"), + [*self.expl_stmts, hook_call_pass], + [], + ) + statements_pass: list[ast.stmt] = [hook_impl_test] + + # Test for assertion condition + main_test = ast.If(negation, statements_fail, statements_pass) + self.statements.append(main_test) + if self.format_variables: + variables: list[ast.expr] = [ + ast.Name(name, ast.Store()) for name in self.format_variables + ] + clear_format = ast.Assign(variables, ast.Constant(None)) + self.statements.append(clear_format) + + else: # Original assertion rewriting + # Create failure message. + body = self.expl_stmts + self.statements.append(ast.If(negation, body, [])) + if assert_.msg: + assertmsg = self.helper("_format_assertmsg", assert_.msg) + explanation = "\n>assert " + explanation + else: + assertmsg = ast.Constant("") + explanation = "assert " + explanation + template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation)) + msg = self.pop_format_context(template) + fmt = self.helper("_format_explanation", msg) + err_name = ast.Name("AssertionError", ast.Load()) + exc = ast.Call(err_name, [fmt], []) + raise_ = ast.Raise(exc, None) + + body.append(raise_) + + # Clear temporary variables by setting them to None. + if self.variables: + variables = [ast.Name(name, ast.Store()) for name in self.variables] + clear = ast.Assign(variables, ast.Constant(None)) + self.statements.append(clear) + # Fix locations (line numbers/column offsets). + for stmt in self.statements: + for node in traverse_node(stmt): + if getattr(node, "lineno", None) is None: + # apply the assertion location to all generated ast nodes without source location + # and preserve the location of existing nodes or generated nodes with an correct location. + ast.copy_location(node, assert_) + return self.statements + + def visit_NamedExpr(self, name: ast.NamedExpr) -> tuple[ast.NamedExpr, str]: + # This method handles the 'walrus operator' repr of the target + # name if it's a local variable or _should_repr_global_name() + # thinks it's acceptable. + locs = ast.Call(self.builtin("locals"), [], []) + target_id = name.target.id + inlocs = ast.Compare(ast.Constant(target_id), [ast.In()], [locs]) + dorepr = self.helper("_should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Constant(target_id)) + return name, self.explanation_param(expr) + + def visit_Name(self, name: ast.Name) -> tuple[ast.Name, str]: + # Display the repr of the name if it's a local variable or + # _should_repr_global_name() thinks it's acceptable. + locs = ast.Call(self.builtin("locals"), [], []) + inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs]) + dorepr = self.helper("_should_repr_global_name", name) + test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) + expr = ast.IfExp(test, self.display(name), ast.Constant(name.id)) + return name, self.explanation_param(expr) + + def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: + res_var = self.variable() + expl_list = self.assign(ast.List([], ast.Load())) + app = ast.Attribute(expl_list, "append", ast.Load()) + is_or = int(isinstance(boolop.op, ast.Or)) + body = save = self.statements + fail_save = self.expl_stmts + levels = len(boolop.values) - 1 + self.push_format_context() + # Process each operand, short-circuiting if needed. + for i, v in enumerate(boolop.values): + if i: + fail_inner: list[ast.stmt] = [] + # cond is set in a prior loop iteration below + self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa: F821 + self.expl_stmts = fail_inner + match v: + # Check if the left operand is an ast.NamedExpr and the value has already been visited + case ast.Compare( + left=ast.NamedExpr(target=ast.Name(id=target_id)) + ) if target_id in [ + e.id for e in boolop.values[:i] if hasattr(e, "id") + ]: + pytest_temp = self.variable() + self.variables_overwrite[self.scope][target_id] = v.left # type:ignore[assignment] + # mypy's false positive, we're checking that the 'target' attribute exists. + v.left.target.id = pytest_temp # type:ignore[attr-defined] + self.push_format_context() + res, expl = self.visit(v) + body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) + expl_format = self.pop_format_context(ast.Constant(expl)) + call = ast.Call(app, [expl_format], []) + self.expl_stmts.append(ast.Expr(call)) + if i < levels: + cond: ast.expr = res + if is_or: + cond = ast.UnaryOp(ast.Not(), cond) + inner: list[ast.stmt] = [] + self.statements.append(ast.If(cond, inner, [])) + self.statements = body = inner + self.statements = save + self.expl_stmts = fail_save + expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or)) + expl = self.pop_format_context(expl_template) + return ast.Name(res_var, ast.Load()), self.explanation_param(expl) + + def visit_UnaryOp(self, unary: ast.UnaryOp) -> tuple[ast.Name, str]: + pattern = UNARY_MAP[unary.op.__class__] + operand_res, operand_expl = self.visit(unary.operand) + res = self.assign(ast.copy_location(ast.UnaryOp(unary.op, operand_res), unary)) + return res, pattern % (operand_expl,) + + def visit_BinOp(self, binop: ast.BinOp) -> tuple[ast.Name, str]: + symbol = BINOP_MAP[binop.op.__class__] + left_expr, left_expl = self.visit(binop.left) + right_expr, right_expl = self.visit(binop.right) + explanation = f"({left_expl} {symbol} {right_expl})" + res = self.assign( + ast.copy_location(ast.BinOp(left_expr, binop.op, right_expr), binop) + ) + return res, explanation + + def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: + new_func, func_expl = self.visit(call.func) + arg_expls = [] + new_args = [] + new_kwargs = [] + for arg in call.args: + if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( + self.scope, {} + ): + arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] + res, expl = self.visit(arg) + arg_expls.append(expl) + new_args.append(res) + for keyword in call.keywords: + match keyword.value: + case ast.Name(id=id) if id in self.variables_overwrite.get( + self.scope, {} + ): + keyword.value = self.variables_overwrite[self.scope][id] # type:ignore[assignment] + res, expl = self.visit(keyword.value) + new_kwargs.append(ast.keyword(keyword.arg, res)) + if keyword.arg: + arg_expls.append(keyword.arg + "=" + expl) + else: # **args have `arg` keywords with an .arg of None + arg_expls.append("**" + expl) + + expl = "{}({})".format(func_expl, ", ".join(arg_expls)) + new_call = ast.copy_location(ast.Call(new_func, new_args, new_kwargs), call) + res = self.assign(new_call) + res_expl = self.explanation_param(self.display(res)) + outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" + return res, outer_expl + + def visit_Starred(self, starred: ast.Starred) -> tuple[ast.Starred, str]: + # A Starred node can appear in a function call. + res, expl = self.visit(starred.value) + new_starred = ast.Starred(res, starred.ctx) + return new_starred, "*" + expl + + def visit_Attribute(self, attr: ast.Attribute) -> tuple[ast.Name, str]: + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + value, value_expl = self.visit(attr.value) + res = self.assign( + ast.copy_location(ast.Attribute(value, attr.attr, ast.Load()), attr) + ) + res_expl = self.explanation_param(self.display(res)) + pat = "%s\n{%s = %s.%s\n}" + expl = pat % (res_expl, res_expl, value_expl, attr.attr) + return res, expl + + def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: + self.push_format_context() + # We first check if we have overwritten a variable in the previous assert + match comp.left: + case ast.Name(id=name_id) if name_id in self.variables_overwrite.get( + self.scope, {} + ): + comp.left = self.variables_overwrite[self.scope][name_id] # type: ignore[assignment] + case ast.NamedExpr(target=ast.Name(id=target_id)): + self.variables_overwrite[self.scope][target_id] = comp.left # type: ignore[assignment] + left_res, left_expl = self.visit(comp.left) + if isinstance(comp.left, ast.Compare | ast.BoolOp): + left_expl = f"({left_expl})" + res_variables = [self.variable() for i in range(len(comp.ops))] + load_names: list[ast.expr] = [ast.Name(v, ast.Load()) for v in res_variables] + store_names = [ast.Name(v, ast.Store()) for v in res_variables] + it = zip(range(len(comp.ops)), comp.ops, comp.comparators, strict=True) + expls: list[ast.expr] = [] + syms: list[ast.expr] = [] + results = [left_res] + for i, op, next_operand in it: + match (next_operand, left_res): + case ( + ast.NamedExpr(target=ast.Name(id=target_id)), + ast.Name(id=name_id), + ) if target_id == name_id: + next_operand.target.id = self.variable() + self.variables_overwrite[self.scope][name_id] = next_operand # type: ignore[assignment] + + next_res, next_expl = self.visit(next_operand) + if isinstance(next_operand, ast.Compare | ast.BoolOp): + next_expl = f"({next_expl})" + results.append(next_res) + sym = BINOP_MAP[op.__class__] + syms.append(ast.Constant(sym)) + expl = f"{left_expl} {sym} {next_expl}" + expls.append(ast.Constant(expl)) + res_expr = ast.copy_location(ast.Compare(left_res, [op], [next_res]), comp) + self.statements.append(ast.Assign([store_names[i]], res_expr)) + left_res, left_expl = next_res, next_expl + # Use pytest.assertion.util._reprcompare if that's available. + expl_call = self.helper( + "_call_reprcompare", + ast.Tuple(syms, ast.Load()), + ast.Tuple(load_names, ast.Load()), + ast.Tuple(expls, ast.Load()), + ast.Tuple(results, ast.Load()), + ) + if len(comp.ops) > 1: + res: ast.expr = ast.BoolOp(ast.And(), load_names) + else: + res = load_names[0] + + return res, self.explanation_param(self.pop_format_context(expl_call)) + + +def try_makedirs(cache_dir: Path) -> bool: + """Attempt to create the given directory and sub-directories exist. + + Returns True if successful or if it already exists. + """ + try: + os.makedirs(cache_dir, exist_ok=True) + except (FileNotFoundError, NotADirectoryError, FileExistsError): + # One of the path components was not a directory: + # - we're in a zip file + # - it is a file + return False + except PermissionError: + return False + except OSError as e: + # as of now, EROFS doesn't have an equivalent OSError-subclass + # + # squashfuse_ll returns ENOSYS "OSError: [Errno 38] Function not + # implemented" for a read-only error + if e.errno in {errno.EROFS, errno.ENOSYS}: + return False + raise + return True + + +def get_cache_dir(file_path: Path) -> Path: + """Return the cache directory to write .pyc files for the given .py file path.""" + if sys.pycache_prefix: + # given: + # prefix = '/tmp/pycs' + # path = '/home/user/proj/test_app.py' + # we want: + # '/tmp/pycs/home/user/proj' + return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) + else: + # classic pycache directory + return file_path.parent / "__pycache__" diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py new file mode 100644 index 0000000..5820e6e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py @@ -0,0 +1,137 @@ +"""Utilities for truncating assertion output. + +Current default behaviour is to truncate assertion explanations at +terminal lines, unless running with an assertions verbosity level of at least 2 or running on CI. +""" + +from __future__ import annotations + +from _pytest.compat import running_on_ci +from _pytest.config import Config +from _pytest.nodes import Item + + +DEFAULT_MAX_LINES = 8 +DEFAULT_MAX_CHARS = DEFAULT_MAX_LINES * 80 +USAGE_MSG = "use '-vv' to show" + + +def truncate_if_required(explanation: list[str], item: Item) -> list[str]: + """Truncate this assertion explanation if the given test item is eligible.""" + should_truncate, max_lines, max_chars = _get_truncation_parameters(item) + if should_truncate: + return _truncate_explanation( + explanation, + max_lines=max_lines, + max_chars=max_chars, + ) + return explanation + + +def _get_truncation_parameters(item: Item) -> tuple[bool, int, int]: + """Return the truncation parameters related to the given item, as (should truncate, max lines, max chars).""" + # We do not need to truncate if one of conditions is met: + # 1. Verbosity level is 2 or more; + # 2. Test is being run in CI environment; + # 3. Both truncation_limit_lines and truncation_limit_chars + # .ini parameters are set to 0 explicitly. + max_lines = item.config.getini("truncation_limit_lines") + max_lines = int(max_lines if max_lines is not None else DEFAULT_MAX_LINES) + + max_chars = item.config.getini("truncation_limit_chars") + max_chars = int(max_chars if max_chars is not None else DEFAULT_MAX_CHARS) + + verbose = item.config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + + should_truncate = verbose < 2 and not running_on_ci() + should_truncate = should_truncate and (max_lines > 0 or max_chars > 0) + + return should_truncate, max_lines, max_chars + + +def _truncate_explanation( + input_lines: list[str], + max_lines: int, + max_chars: int, +) -> list[str]: + """Truncate given list of strings that makes up the assertion explanation. + + Truncates to either max_lines, or max_chars - whichever the input reaches + first, taking the truncation explanation into account. The remaining lines + will be replaced by a usage message. + """ + # Check if truncation required + input_char_count = len("".join(input_lines)) + # The length of the truncation explanation depends on the number of lines + # removed but is at least 68 characters: + # The real value is + # 64 (for the base message: + # '...\n...Full output truncated (1 line hidden), use '-vv' to show")' + # ) + # + 1 (for plural) + # + int(math.log10(len(input_lines) - max_lines)) (number of hidden line, at least 1) + # + 3 for the '...' added to the truncated line + # But if there's more than 100 lines it's very likely that we're going to + # truncate, so we don't need the exact value using log10. + tolerable_max_chars = ( + max_chars + 70 # 64 + 1 (for plural) + 2 (for '99') + 3 for '...' + ) + # The truncation explanation add two lines to the output + tolerable_max_lines = max_lines + 2 + if ( + len(input_lines) <= tolerable_max_lines + and input_char_count <= tolerable_max_chars + ): + return input_lines + # Truncate first to max_lines, and then truncate to max_chars if necessary + if max_lines > 0: + truncated_explanation = input_lines[:max_lines] + else: + truncated_explanation = input_lines + truncated_char = True + # We reevaluate the need to truncate chars following removal of some lines + if len("".join(truncated_explanation)) > tolerable_max_chars and max_chars > 0: + truncated_explanation = _truncate_by_char_count( + truncated_explanation, max_chars + ) + else: + truncated_char = False + + if truncated_explanation == input_lines: + # No truncation happened, so we do not need to add any explanations + return truncated_explanation + + truncated_line_count = len(input_lines) - len(truncated_explanation) + if truncated_explanation[-1]: + # Add ellipsis and take into account part-truncated final line + truncated_explanation[-1] = truncated_explanation[-1] + "..." + if truncated_char: + # It's possible that we did not remove any char from this line + truncated_line_count += 1 + else: + # Add proper ellipsis when we were able to fit a full line exactly + truncated_explanation[-1] = "..." + return [ + *truncated_explanation, + "", + f"...Full output truncated ({truncated_line_count} line" + f"{'' if truncated_line_count == 1 else 's'} hidden), {USAGE_MSG}", + ] + + +def _truncate_by_char_count(input_lines: list[str], max_chars: int) -> list[str]: + # Find point at which input length exceeds total allowed length + iterated_char_count = 0 + for iterated_index, input_line in enumerate(input_lines): + if iterated_char_count + len(input_line) > max_chars: + break + iterated_char_count += len(input_line) + + # Create truncated explanation with modified final line + truncated_result = input_lines[:iterated_index] + final_line = input_lines[iterated_index] + if final_line: + final_line_truncate_point = max_chars - iterated_char_count + final_line = final_line[:final_line_truncate_point] + truncated_result.append(final_line) + return truncated_result diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py new file mode 100644 index 0000000..f35d83a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py @@ -0,0 +1,615 @@ +# mypy: allow-untyped-defs +"""Utilities for assertion debugging.""" + +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Mapping +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import pprint +from typing import Any +from typing import Literal +from typing import Protocol +from unicodedata import normalize + +from _pytest import outcomes +import _pytest._code +from _pytest._io.pprint import PrettyPrinter +from _pytest._io.saferepr import saferepr +from _pytest._io.saferepr import saferepr_unlimited +from _pytest.compat import running_on_ci +from _pytest.config import Config + + +# The _reprcompare attribute on the util module is used by the new assertion +# interpretation code and assertion rewriter to detect this plugin was +# loaded and in turn call the hooks defined here as part of the +# DebugInterpreter. +_reprcompare: Callable[[str, object, object], str | None] | None = None + +# Works similarly as _reprcompare attribute. Is populated with the hook call +# when pytest_runtest_setup is called. +_assertion_pass: Callable[[int, str, str], None] | None = None + +# Config object which is assigned during pytest_runtest_protocol. +_config: Config | None = None + + +class _HighlightFunc(Protocol): + def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str: + """Apply highlighting to the given source.""" + + +def dummy_highlighter(source: str, lexer: Literal["diff", "python"] = "python") -> str: + """Dummy highlighter that returns the text unprocessed. + + Needed for _notin_text, as the diff gets post-processed to only show the "+" part. + """ + return source + + +def format_explanation(explanation: str) -> str: + r"""Format an explanation. + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + lines = _split_explanation(explanation) + result = _format_lines(lines) + return "\n".join(result) + + +def _split_explanation(explanation: str) -> list[str]: + r"""Return a list of individual lines in the explanation. + + This will return a list of lines split on '\n{', '\n}' and '\n~'. + Any other newlines will be escaped and appear in the line as the + literal '\n' characters. + """ + raw_lines = (explanation or "").split("\n") + lines = [raw_lines[0]] + for values in raw_lines[1:]: + if values and values[0] in ["{", "}", "~", ">"]: + lines.append(values) + else: + lines[-1] += "\\n" + values + return lines + + +def _format_lines(lines: Sequence[str]) -> list[str]: + """Format the individual lines. + + This will replace the '{', '}' and '~' characters of our mini formatting + language with the proper 'where ...', 'and ...' and ' + ...' text, taking + care of indentation along the way. + + Return a list of formatted lines. + """ + result = list(lines[:1]) + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith("{"): + if stackcnt[-1]: + s = "and " + else: + s = "where " + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) + elif line.startswith("}"): + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line[0] in ["~", ">"] + stack[-1] += 1 + indent = len(stack) if line.startswith("~") else len(stack) - 1 + result.append(" " * indent + line[1:]) + assert len(stack) == 1 + return result + + +def issequence(x: Any) -> bool: + return isinstance(x, collections.abc.Sequence) and not isinstance(x, str) + + +def istext(x: Any) -> bool: + return isinstance(x, str) + + +def isdict(x: Any) -> bool: + return isinstance(x, dict) + + +def isset(x: Any) -> bool: + return isinstance(x, set | frozenset) + + +def isnamedtuple(obj: Any) -> bool: + return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None + + +def isdatacls(obj: Any) -> bool: + return getattr(obj, "__dataclass_fields__", None) is not None + + +def isattrs(obj: Any) -> bool: + return getattr(obj, "__attrs_attrs__", None) is not None + + +def isiterable(obj: Any) -> bool: + try: + iter(obj) + return not istext(obj) + except Exception: + return False + + +def has_default_eq( + obj: object, +) -> bool: + """Check if an instance of an object contains the default eq + + First, we check if the object's __eq__ attribute has __code__, + if so, we check the equally of the method code filename (__code__.co_filename) + to the default one generated by the dataclass and attr module + for dataclasses the default co_filename is , for attrs class, the __eq__ should contain "attrs eq generated" + """ + # inspired from https://github.com/willmcgugan/rich/blob/07d51ffc1aee6f16bd2e5a25b4e82850fb9ed778/rich/pretty.py#L68 + if hasattr(obj.__eq__, "__code__") and hasattr(obj.__eq__.__code__, "co_filename"): + code_filename = obj.__eq__.__code__.co_filename + + if isattrs(obj): + return "attrs generated " in code_filename + + return code_filename == "" # data class + return True + + +def assertrepr_compare( + config, op: str, left: Any, right: Any, use_ascii: bool = False +) -> list[str] | None: + """Return specialised explanations for some operators/operands.""" + verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) + + # Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier. + # See issue #3246. + use_ascii = ( + isinstance(left, str) + and isinstance(right, str) + and normalize("NFD", left) == normalize("NFD", right) + ) + + if verbose > 1: + left_repr = saferepr_unlimited(left, use_ascii=use_ascii) + right_repr = saferepr_unlimited(right, use_ascii=use_ascii) + else: + # XXX: "15 chars indentation" is wrong + # ("E AssertionError: assert "); should use term width. + maxsize = ( + 80 - 15 - len(op) - 2 + ) // 2 # 15 chars indentation, 1 space around op + + left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii) + right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii) + + summary = f"{left_repr} {op} {right_repr}" + highlighter = config.get_terminal_writer()._highlight + + explanation = None + try: + if op == "==": + explanation = _compare_eq_any(left, right, highlighter, verbose) + elif op == "not in": + if istext(left) and istext(right): + explanation = _notin_text(left, right, verbose) + elif op == "!=": + if isset(left) and isset(right): + explanation = ["Both sets are equal"] + elif op == ">=": + if isset(left) and isset(right): + explanation = _compare_gte_set(left, right, highlighter, verbose) + elif op == "<=": + if isset(left) and isset(right): + explanation = _compare_lte_set(left, right, highlighter, verbose) + elif op == ">": + if isset(left) and isset(right): + explanation = _compare_gt_set(left, right, highlighter, verbose) + elif op == "<": + if isset(left) and isset(right): + explanation = _compare_lt_set(left, right, highlighter, verbose) + + except outcomes.Exit: + raise + except Exception: + repr_crash = _pytest._code.ExceptionInfo.from_current()._getreprcrash() + explanation = [ + f"(pytest_assertion plugin: representation of details failed: {repr_crash}.", + " Probably an object has a faulty __repr__.)", + ] + + if not explanation: + return None + + if explanation[0] != "": + explanation = ["", *explanation] + return [summary, *explanation] + + +def _compare_eq_any( + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0 +) -> list[str]: + explanation = [] + if istext(left) and istext(right): + explanation = _diff_text(left, right, highlighter, verbose) + else: + from _pytest.python_api import ApproxBase + + if isinstance(left, ApproxBase) or isinstance(right, ApproxBase): + # Although the common order should be obtained == expected, this ensures both ways + approx_side = left if isinstance(left, ApproxBase) else right + other_side = right if isinstance(left, ApproxBase) else left + + explanation = approx_side._repr_compare(other_side) + elif type(left) is type(right) and ( + isdatacls(left) or isattrs(left) or isnamedtuple(left) + ): + # Note: unlike dataclasses/attrs, namedtuples compare only the + # field values, not the type or field names. But this branch + # intentionally only handles the same-type case, which was often + # used in older code bases before dataclasses/attrs were available. + explanation = _compare_eq_cls(left, right, highlighter, verbose) + elif issequence(left) and issequence(right): + explanation = _compare_eq_sequence(left, right, highlighter, verbose) + elif isset(left) and isset(right): + explanation = _compare_eq_set(left, right, highlighter, verbose) + elif isdict(left) and isdict(right): + explanation = _compare_eq_dict(left, right, highlighter, verbose) + + if isiterable(left) and isiterable(right): + expl = _compare_eq_iterable(left, right, highlighter, verbose) + explanation.extend(expl) + + return explanation + + +def _diff_text( + left: str, right: str, highlighter: _HighlightFunc, verbose: int = 0 +) -> list[str]: + """Return the explanation for the diff between text. + + Unless --verbose is used this will skip leading and trailing + characters which are identical to keep the diff minimal. + """ + from difflib import ndiff + + explanation: list[str] = [] + + if verbose < 1: + i = 0 # just in case left or right has zero length + for i in range(min(len(left), len(right))): + if left[i] != right[i]: + break + if i > 42: + i -= 10 # Provide some context + explanation = [ + f"Skipping {i} identical leading characters in diff, use -v to show" + ] + left = left[i:] + right = right[i:] + if len(left) == len(right): + for i in range(len(left)): + if left[-i] != right[-i]: + break + if i > 42: + i -= 10 # Provide some context + explanation += [ + f"Skipping {i} identical trailing " + "characters in diff, use -v to show" + ] + left = left[:-i] + right = right[:-i] + keepends = True + if left.isspace() or right.isspace(): + left = repr(str(left)) + right = repr(str(right)) + explanation += ["Strings contain only whitespace, escaping them using repr()"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + highlighter( + "\n".join( + line.strip("\n") + for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) + ), + lexer="diff", + ).splitlines() + ) + return explanation + + +def _compare_eq_iterable( + left: Iterable[Any], + right: Iterable[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + if verbose <= 0 and not running_on_ci(): + return ["Use -v to get more diff"] + # dynamic import to speedup pytest + import difflib + + left_formatting = PrettyPrinter().pformat(left).splitlines() + right_formatting = PrettyPrinter().pformat(right).splitlines() + + explanation = ["", "Full diff:"] + # "right" is the expected base against which we compare "left", + # see https://github.com/pytest-dev/pytest/issues/3333 + explanation.extend( + highlighter( + "\n".join( + line.rstrip() + for line in difflib.ndiff(right_formatting, left_formatting) + ), + lexer="diff", + ).splitlines() + ) + return explanation + + +def _compare_eq_sequence( + left: Sequence[Any], + right: Sequence[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes) + explanation: list[str] = [] + len_left = len(left) + len_right = len(right) + for i in range(min(len_left, len_right)): + if left[i] != right[i]: + if comparing_bytes: + # when comparing bytes, we want to see their ascii representation + # instead of their numeric values (#5260) + # using a slice gives us the ascii representation: + # >>> s = b'foo' + # >>> s[0] + # 102 + # >>> s[0:1] + # b'f' + left_value = left[i : i + 1] + right_value = right[i : i + 1] + else: + left_value = left[i] + right_value = right[i] + + explanation.append( + f"At index {i} diff:" + f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}" + ) + break + + if comparing_bytes: + # when comparing bytes, it doesn't help to show the "sides contain one or more + # items" longer explanation, so skip it + + return explanation + + len_diff = len_left - len_right + if len_diff: + if len_diff > 0: + dir_with_more = "Left" + extra = saferepr(left[len_right]) + else: + len_diff = 0 - len_diff + dir_with_more = "Right" + extra = saferepr(right[len_left]) + + if len_diff == 1: + explanation += [ + f"{dir_with_more} contains one more item: {highlighter(extra)}" + ] + else: + explanation += [ + f"{dir_with_more} contains {len_diff} more items, first extra item: {highlighter(extra)}" + ] + return explanation + + +def _compare_eq_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = [] + explanation.extend(_set_one_sided_diff("left", left, right, highlighter)) + explanation.extend(_set_one_sided_diff("right", right, left, highlighter)) + return explanation + + +def _compare_gt_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = _compare_gte_set(left, right, highlighter) + if not explanation: + return ["Both sets are equal"] + return explanation + + +def _compare_lt_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation = _compare_lte_set(left, right, highlighter) + if not explanation: + return ["Both sets are equal"] + return explanation + + +def _compare_gte_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + return _set_one_sided_diff("right", right, left, highlighter) + + +def _compare_lte_set( + left: AbstractSet[Any], + right: AbstractSet[Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + return _set_one_sided_diff("left", left, right, highlighter) + + +def _set_one_sided_diff( + posn: str, + set1: AbstractSet[Any], + set2: AbstractSet[Any], + highlighter: _HighlightFunc, +) -> list[str]: + explanation = [] + diff = set1 - set2 + if diff: + explanation.append(f"Extra items in the {posn} set:") + for item in diff: + explanation.append(highlighter(saferepr(item))) + return explanation + + +def _compare_eq_dict( + left: Mapping[Any, Any], + right: Mapping[Any, Any], + highlighter: _HighlightFunc, + verbose: int = 0, +) -> list[str]: + explanation: list[str] = [] + set_left = set(left) + set_right = set(right) + common = set_left.intersection(set_right) + same = {k: left[k] for k in common if left[k] == right[k]} + if same and verbose < 2: + explanation += [f"Omitting {len(same)} identical items, use -vv to show"] + elif same: + explanation += ["Common items:"] + explanation += highlighter(pprint.pformat(same)).splitlines() + diff = {k for k in common if left[k] != right[k]} + if diff: + explanation += ["Differing items:"] + for k in diff: + explanation += [ + highlighter(saferepr({k: left[k]})) + + " != " + + highlighter(saferepr({k: right[k]})) + ] + extra_left = set_left - set_right + len_extra_left = len(extra_left) + if len_extra_left: + explanation.append( + f"Left contains {len_extra_left} more item{'' if len_extra_left == 1 else 's'}:" + ) + explanation.extend( + highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines() + ) + extra_right = set_right - set_left + len_extra_right = len(extra_right) + if len_extra_right: + explanation.append( + f"Right contains {len_extra_right} more item{'' if len_extra_right == 1 else 's'}:" + ) + explanation.extend( + highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines() + ) + return explanation + + +def _compare_eq_cls( + left: Any, right: Any, highlighter: _HighlightFunc, verbose: int +) -> list[str]: + if not has_default_eq(left): + return [] + if isdatacls(left): + import dataclasses + + all_fields = dataclasses.fields(left) + fields_to_check = [info.name for info in all_fields if info.compare] + elif isattrs(left): + all_fields = left.__attrs_attrs__ + fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] + elif isnamedtuple(left): + fields_to_check = left._fields + else: + assert False + + indent = " " + same = [] + diff = [] + for field in fields_to_check: + if getattr(left, field) == getattr(right, field): + same.append(field) + else: + diff.append(field) + + explanation = [] + if same or diff: + explanation += [""] + if same and verbose < 2: + explanation.append(f"Omitting {len(same)} identical items, use -vv to show") + elif same: + explanation += ["Matching attributes:"] + explanation += highlighter(pprint.pformat(same)).splitlines() + if diff: + explanation += ["Differing attributes:"] + explanation += highlighter(pprint.pformat(diff)).splitlines() + for field in diff: + field_left = getattr(left, field) + field_right = getattr(right, field) + explanation += [ + "", + f"Drill down into differing attribute {field}:", + f"{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}", + ] + explanation += [ + indent + line + for line in _compare_eq_any( + field_left, field_right, highlighter, verbose + ) + ] + return explanation + + +def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: + index = text.find(term) + head = text[:index] + tail = text[index + len(term) :] + correct_text = head + tail + diff = _diff_text(text, correct_text, dummy_highlighter, verbose) + newdiff = [f"{saferepr(term, maxsize=42)} is contained here:"] + for line in diff: + if line.startswith("Skipping"): + continue + if line.startswith("- "): + continue + if line.startswith("+ "): + newdiff.append(" " + line[2:]) + else: + newdiff.append(line) + return newdiff diff --git a/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py b/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py new file mode 100644 index 0000000..4383f10 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py @@ -0,0 +1,646 @@ +# mypy: allow-untyped-defs +"""Implementation of the cache provider.""" + +# This plugin was not named "cache" to avoid conflicts with the external +# pytest-cache version. +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Iterable +import dataclasses +import errno +import json +import os +from pathlib import Path +import tempfile +from typing import final + +from .pathlib import resolve_from_str +from .pathlib import rm_rf +from .reports import CollectReport +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.nodes import Directory +from _pytest.nodes import File +from _pytest.reports import TestReport + + +README_CONTENT = """\ +# pytest cache directory # + +This directory contains data from the pytest's cache plugin, +which provides the `--lf` and `--ff` options, as well as the `cache` fixture. + +**Do not** commit this to version control. + +See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. +""" + +CACHEDIR_TAG_CONTENT = b"""\ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by pytest. +# For information about cache directory tags, see: +# https://bford.info/cachedir/spec.html +""" + + +@final +@dataclasses.dataclass +class Cache: + """Instance of the `cache` fixture.""" + + _cachedir: Path = dataclasses.field(repr=False) + _config: Config = dataclasses.field(repr=False) + + # Sub-directory under cache-dir for directories created by `mkdir()`. + _CACHE_PREFIX_DIRS = "d" + + # Sub-directory under cache-dir for values created by `set()`. + _CACHE_PREFIX_VALUES = "v" + + def __init__( + self, cachedir: Path, config: Config, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._cachedir = cachedir + self._config = config + + @classmethod + def for_config(cls, config: Config, *, _ispytest: bool = False) -> Cache: + """Create the Cache instance for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + cachedir = cls.cache_dir_from_config(config, _ispytest=True) + if config.getoption("cacheclear") and cachedir.is_dir(): + cls.clear_cache(cachedir, _ispytest=True) + return cls(cachedir, config, _ispytest=True) + + @classmethod + def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: + """Clear the sub-directories used to hold cached directories and values. + + :meta private: + """ + check_ispytest(_ispytest) + for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): + d = cachedir / prefix + if d.is_dir(): + rm_rf(d) + + @staticmethod + def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: + """Get the path to the cache directory for a Config. + + :meta private: + """ + check_ispytest(_ispytest) + return resolve_from_str(config.getini("cache_dir"), config.rootpath) + + def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: + """Issue a cache warning. + + :meta private: + """ + check_ispytest(_ispytest) + import warnings + + from _pytest.warning_types import PytestCacheWarning + + warnings.warn( + PytestCacheWarning(fmt.format(**args) if args else fmt), + self._config.hook, + stacklevel=3, + ) + + def _mkdir(self, path: Path) -> None: + self._ensure_cache_dir_and_supporting_files() + path.mkdir(exist_ok=True, parents=True) + + def mkdir(self, name: str) -> Path: + """Return a directory path object with the given name. + + If the directory does not yet exist, it will be created. You can use + it to manage files to e.g. store/retrieve database dumps across test + sessions. + + .. versionadded:: 7.0 + + :param name: + Must be a string not containing a ``/`` separator. + Make sure the name contains your plugin or application + identifiers to prevent clashes with other cache users. + """ + path = Path(name) + if len(path.parts) > 1: + raise ValueError("name is not allowed to contain path separators") + res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) + self._mkdir(res) + return res + + def _getvaluepath(self, key: str) -> Path: + return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) + + def get(self, key: str, default): + """Return the cached value for the given key. + + If no value was yet cached or the value cannot be read, the specified + default is returned. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param default: + The value to return in case of a cache-miss or invalid cache value. + """ + path = self._getvaluepath(key) + try: + with path.open("r", encoding="UTF-8") as f: + return json.load(f) + except (ValueError, OSError): + return default + + def set(self, key: str, value: object) -> None: + """Save value for the given key. + + :param key: + Must be a ``/`` separated value. Usually the first + name is the name of your plugin or your application. + :param value: + Must be of any combination of basic python types, + including nested types like lists of dictionaries. + """ + path = self._getvaluepath(key) + try: + self._mkdir(path.parent) + except OSError as exc: + self.warn( + f"could not create cache path {path}: {exc}", + _ispytest=True, + ) + return + data = json.dumps(value, ensure_ascii=False, indent=2) + try: + f = path.open("w", encoding="UTF-8") + except OSError as exc: + self.warn( + f"cache could not write path {path}: {exc}", + _ispytest=True, + ) + else: + with f: + f.write(data) + + def _ensure_cache_dir_and_supporting_files(self) -> None: + """Create the cache dir and its supporting files.""" + if self._cachedir.is_dir(): + return + + self._cachedir.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory( + prefix="pytest-cache-files-", + dir=self._cachedir.parent, + ) as newpath: + path = Path(newpath) + + # Reset permissions to the default, see #12308. + # Note: there's no way to get the current umask atomically, eek. + umask = os.umask(0o022) + os.umask(umask) + path.chmod(0o777 - umask) + + with open(path.joinpath("README.md"), "x", encoding="UTF-8") as f: + f.write(README_CONTENT) + with open(path.joinpath(".gitignore"), "x", encoding="UTF-8") as f: + f.write("# Created by pytest automatically.\n*\n") + with open(path.joinpath("CACHEDIR.TAG"), "xb") as f: + f.write(CACHEDIR_TAG_CONTENT) + + try: + path.rename(self._cachedir) + except OSError as e: + # If 2 concurrent pytests both race to the rename, the loser + # gets "Directory not empty" from the rename. In this case, + # everything is handled so just continue (while letting the + # temporary directory be cleaned up). + # On Windows, the error is a FileExistsError which translates to EEXIST. + if e.errno not in (errno.ENOTEMPTY, errno.EEXIST): + raise + else: + # Create a directory in place of the one we just moved so that + # `TemporaryDirectory`'s cleanup doesn't complain. + # + # TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. + # See https://github.com/python/cpython/issues/74168. Note that passing + # delete=False would do the wrong thing in case of errors and isn't supported + # until python 3.12. + path.mkdir() + + +class LFPluginCollWrapper: + def __init__(self, lfplugin: LFPlugin) -> None: + self.lfplugin = lfplugin + self._collected_at_least_one_failure = False + + @hookimpl(wrapper=True) + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> Generator[None, CollectReport, CollectReport]: + res = yield + if isinstance(collector, Session | Directory): + # Sort any lf-paths to the beginning. + lf_paths = self.lfplugin._last_failed_paths + + # Use stable sort to prioritize last failed. + def sort_key(node: nodes.Item | nodes.Collector) -> bool: + return node.path in lf_paths + + res.result = sorted( + res.result, + key=sort_key, + reverse=True, + ) + + elif isinstance(collector, File): + if collector.path in self.lfplugin._last_failed_paths: + result = res.result + lastfailed = self.lfplugin.lastfailed + + # Only filter with known failures. + if not self._collected_at_least_one_failure: + if not any(x.nodeid in lastfailed for x in result): + return res + self.lfplugin.config.pluginmanager.register( + LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip" + ) + self._collected_at_least_one_failure = True + + session = collector.session + result[:] = [ + x + for x in result + if x.nodeid in lastfailed + # Include any passed arguments (not trivial to filter). + or session.isinitpath(x.path) + # Keep all sub-collectors. + or isinstance(x, nodes.Collector) + ] + + return res + + +class LFPluginCollSkipfiles: + def __init__(self, lfplugin: LFPlugin) -> None: + self.lfplugin = lfplugin + + @hookimpl + def pytest_make_collect_report( + self, collector: nodes.Collector + ) -> CollectReport | None: + if isinstance(collector, File): + if collector.path not in self.lfplugin._last_failed_paths: + self.lfplugin._skipped_files += 1 + + return CollectReport( + collector.nodeid, "passed", longrepr=None, result=[] + ) + return None + + +class LFPlugin: + """Plugin which implements the --lf (run last-failing) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + active_keys = "lf", "failedfirst" + self.active = any(config.getoption(key) for key in active_keys) + assert config.cache + self.lastfailed: dict[str, bool] = config.cache.get("cache/lastfailed", {}) + self._previously_failed_count: int | None = None + self._report_status: str | None = None + self._skipped_files = 0 # count skipped files during collection due to --lf + + if config.getoption("lf"): + self._last_failed_paths = self.get_last_failed_paths() + config.pluginmanager.register( + LFPluginCollWrapper(self), "lfplugin-collwrapper" + ) + + def get_last_failed_paths(self) -> set[Path]: + """Return a set with all Paths of the previously failed nodeids and + their parents.""" + rootpath = self.config.rootpath + result = set() + for nodeid in self.lastfailed: + path = rootpath / nodeid.split("::")[0] + result.add(path) + result.update(path.parents) + return {x for x in result if x.exists()} + + def pytest_report_collectionfinish(self) -> str | None: + if self.active and self.config.get_verbosity() >= 0: + return f"run-last-failure: {self._report_status}" + return None + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if (report.when == "call" and report.passed) or report.skipped: + self.lastfailed.pop(report.nodeid, None) + elif report.failed: + self.lastfailed[report.nodeid] = True + + def pytest_collectreport(self, report: CollectReport) -> None: + passed = report.outcome in ("passed", "skipped") + if passed: + if report.nodeid in self.lastfailed: + self.lastfailed.pop(report.nodeid) + self.lastfailed.update((item.nodeid, True) for item in report.result) + else: + self.lastfailed[report.nodeid] = True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection_modifyitems( + self, config: Config, items: list[nodes.Item] + ) -> Generator[None]: + res = yield + + if not self.active: + return res + + if self.lastfailed: + previously_failed = [] + previously_passed = [] + for item in items: + if item.nodeid in self.lastfailed: + previously_failed.append(item) + else: + previously_passed.append(item) + self._previously_failed_count = len(previously_failed) + + if not previously_failed: + # Running a subset of all tests with recorded failures + # only outside of it. + self._report_status = ( + f"{len(self.lastfailed)} known failures not in selected tests" + ) + else: + if self.config.getoption("lf"): + items[:] = previously_failed + config.hook.pytest_deselected(items=previously_passed) + else: # --failedfirst + items[:] = previously_failed + previously_passed + + noun = "failure" if self._previously_failed_count == 1 else "failures" + suffix = " first" if self.config.getoption("failedfirst") else "" + self._report_status = ( + f"rerun previous {self._previously_failed_count} {noun}{suffix}" + ) + + if self._skipped_files > 0: + files_noun = "file" if self._skipped_files == 1 else "files" + self._report_status += f" (skipped {self._skipped_files} {files_noun})" + else: + self._report_status = "no previously failed tests, " + if self.config.getoption("last_failed_no_failures") == "none": + self._report_status += "deselecting all items." + config.hook.pytest_deselected(items=items[:]) + items[:] = [] + else: + self._report_status += "not deselecting items." + + return res + + def pytest_sessionfinish(self, session: Session) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + assert config.cache is not None + saved_lastfailed = config.cache.get("cache/lastfailed", {}) + if saved_lastfailed != self.lastfailed: + config.cache.set("cache/lastfailed", self.lastfailed) + + +class NFPlugin: + """Plugin which implements the --nf (run new-first) option.""" + + def __init__(self, config: Config) -> None: + self.config = config + self.active = config.option.newfirst + assert config.cache is not None + self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> Generator[None]: + res = yield + + if self.active: + new_items: dict[str, nodes.Item] = {} + other_items: dict[str, nodes.Item] = {} + for item in items: + if item.nodeid not in self.cached_nodeids: + new_items[item.nodeid] = item + else: + other_items[item.nodeid] = item + + items[:] = self._get_increasing_order( + new_items.values() + ) + self._get_increasing_order(other_items.values()) + self.cached_nodeids.update(new_items) + else: + self.cached_nodeids.update(item.nodeid for item in items) + + return res + + def _get_increasing_order(self, items: Iterable[nodes.Item]) -> list[nodes.Item]: + return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) + + def pytest_sessionfinish(self) -> None: + config = self.config + if config.getoption("cacheshow") or hasattr(config, "workerinput"): + return + + if config.getoption("collectonly"): + return + + assert config.cache is not None + config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) + + +def pytest_addoption(parser: Parser) -> None: + """Add command-line options for cache functionality. + + :param parser: Parser object to add command-line options to. + """ + group = parser.getgroup("general") + group.addoption( + "--lf", + "--last-failed", + action="store_true", + dest="lf", + help="Rerun only the tests that failed at the last run (or all if none failed)", + ) + group.addoption( + "--ff", + "--failed-first", + action="store_true", + dest="failedfirst", + help="Run all tests, but run the last failures first. " + "This may re-order tests and thus lead to " + "repeated fixture setup/teardown.", + ) + group.addoption( + "--nf", + "--new-first", + action="store_true", + dest="newfirst", + help="Run tests from new files first, then the rest of the tests " + "sorted by file mtime", + ) + group.addoption( + "--cache-show", + action="append", + nargs="?", + dest="cacheshow", + help=( + "Show cache contents, don't perform collection or tests. " + "Optional argument: glob (default: '*')." + ), + ) + group.addoption( + "--cache-clear", + action="store_true", + dest="cacheclear", + help="Remove all cache contents at start of test run", + ) + cache_dir_default = ".pytest_cache" + if "TOX_ENV_DIR" in os.environ: + cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) + parser.addini("cache_dir", default=cache_dir_default, help="Cache directory path") + group.addoption( + "--lfnf", + "--last-failed-no-failures", + action="store", + dest="last_failed_no_failures", + choices=("all", "none"), + default="all", + help="With ``--lf``, determines whether to execute tests when there " + "are no previously (known) failures or when no " + "cached ``lastfailed`` data was found. " + "``all`` (the default) runs the full test suite again. " + "``none`` just emits a message about no known failures and exits successfully.", + ) + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.cacheshow and not config.option.help: + from _pytest.main import wrap_session + + return wrap_session(config, cacheshow) + return None + + +@hookimpl(tryfirst=True) +def pytest_configure(config: Config) -> None: + """Configure cache system and register related plugins. + + Creates the Cache instance and registers the last-failed (LFPlugin) + and new-first (NFPlugin) plugins with the plugin manager. + + :param config: pytest configuration object. + """ + config.cache = Cache.for_config(config, _ispytest=True) + config.pluginmanager.register(LFPlugin(config), "lfplugin") + config.pluginmanager.register(NFPlugin(config), "nfplugin") + + +@fixture +def cache(request: FixtureRequest) -> Cache: + """Return a cache object that can persist state between testing sessions. + + cache.get(key, default) + cache.set(key, value) + + Keys must be ``/`` separated strings, where the first part is usually the + name of your plugin or application to avoid clashes with other cache users. + + Values can be any object handled by the json stdlib module. + """ + assert request.config.cache is not None + return request.config.cache + + +def pytest_report_header(config: Config) -> str | None: + """Display cachedir with --cache-show and if non-default.""" + if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": + assert config.cache is not None + cachedir = config.cache._cachedir + # TODO: evaluate generating upward relative paths + # starting with .., ../.. if sensible + + try: + displaypath = cachedir.relative_to(config.rootpath) + except ValueError: + displaypath = cachedir + return f"cachedir: {displaypath}" + return None + + +def cacheshow(config: Config, session: Session) -> int: + """Display cache contents when --cache-show is used. + + Shows cached values and directories matching the specified glob pattern + (default: '*'). Displays cache location, cached test results, and + any cached directories created by plugins. + + :param config: pytest configuration object. + :param session: pytest session object. + :returns: Exit code (0 for success). + """ + from pprint import pformat + + assert config.cache is not None + + tw = TerminalWriter() + tw.line("cachedir: " + str(config.cache._cachedir)) + if not config.cache._cachedir.is_dir(): + tw.line("cache is empty") + return 0 + + glob = config.option.cacheshow[0] + if glob is None: + glob = "*" + + dummy = object() + basedir = config.cache._cachedir + vdir = basedir / Cache._CACHE_PREFIX_VALUES + tw.sep("-", f"cache values for {glob!r}") + for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): + key = str(valpath.relative_to(vdir)) + val = config.cache.get(key, dummy) + if val is dummy: + tw.line(f"{key} contains unreadable content, will be ignored") + else: + tw.line(f"{key} contains:") + for line in pformat(val).splitlines(): + tw.line(" " + line) + + ddir = basedir / Cache._CACHE_PREFIX_DIRS + if ddir.is_dir(): + contents = sorted(ddir.rglob(glob)) + tw.sep("-", f"cache directories for {glob!r}") + for p in contents: + # if p.is_dir(): + # print("%s/" % p.relative_to(basedir)) + if p.is_file(): + key = str(p.relative_to(basedir)) + tw.line(f"{key} is a file of length {p.stat().st_size}") + return 0 diff --git a/.venv/lib/python3.12/site-packages/_pytest/capture.py b/.venv/lib/python3.12/site-packages/_pytest/capture.py new file mode 100644 index 0000000..6d98676 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/capture.py @@ -0,0 +1,1144 @@ +# mypy: allow-untyped-defs +"""Per-test stdout/stderr capturing mechanism.""" + +from __future__ import annotations + +import abc +import collections +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +import contextlib +import io +from io import UnsupportedOperation +import os +import sys +from tempfile import TemporaryFile +from types import TracebackType +from typing import Any +from typing import AnyStr +from typing import BinaryIO +from typing import cast +from typing import Final +from typing import final +from typing import Generic +from typing import Literal +from typing import NamedTuple +from typing import TextIO +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from typing_extensions import Self + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import SubRequest +from _pytest.nodes import Collector +from _pytest.nodes import File +from _pytest.nodes import Item +from _pytest.reports import CollectReport + + +_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--capture", + action="store", + default="fd", + metavar="method", + choices=["fd", "sys", "no", "tee-sys"], + help="Per-test capturing method: one of fd|sys|no|tee-sys", + ) + group._addoption( # private to use reserved lower-case short option + "-s", + action="store_const", + const="no", + dest="capture", + help="Shortcut for --capture=no", + ) + + +def _colorama_workaround() -> None: + """Ensure colorama is imported so that it attaches to the correct stdio + handles on Windows. + + colorama uses the terminal on import time. So if something does the + first import of colorama while I/O capture is active, colorama will + fail in various ways. + """ + if sys.platform.startswith("win32"): + try: + import colorama # noqa: F401 + except ImportError: + pass + + +def _readline_workaround() -> None: + """Ensure readline is imported early so it attaches to the correct stdio handles. + + This isn't a problem with the default GNU readline implementation, but in + some configurations, Python uses libedit instead (on macOS, and for prebuilt + binaries such as used by uv). + + In theory this is only needed if readline.backend == "libedit", but the + workaround consists of importing readline here, so we already worked around + the issue by the time we could check if we need to. + """ + try: + import readline # noqa: F401 + except ImportError: + pass + + +def _windowsconsoleio_workaround(stream: TextIO) -> None: + """Workaround for Windows Unicode console handling. + + Python 3.6 implemented Unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + :param stream: + In practice ``sys.stdout`` or ``sys.stderr``, but given + here as parameter for unittesting purposes. + + See https://github.com/pytest-dev/py/issues/103. + """ + if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): + return + + # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). + if not hasattr(stream, "buffer"): # type: ignore[unreachable,unused-ignore] + return + + raw_stdout = stream.buffer.raw if hasattr(stream.buffer, "raw") else stream.buffer + + if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined,unused-ignore] + return + + def _reopen_stdio(f, mode): + if not hasattr(stream.buffer, "raw") and mode[0] == "w": + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), + f.encoding, + f.errors, + f.newlines, + f.line_buffering, + ) + + sys.stdin = _reopen_stdio(sys.stdin, "rb") + sys.stdout = _reopen_stdio(sys.stdout, "wb") + sys.stderr = _reopen_stdio(sys.stderr, "wb") + + +@hookimpl(wrapper=True) +def pytest_load_initial_conftests(early_config: Config) -> Generator[None]: + ns = early_config.known_args_namespace + if ns.capture == "fd": + _windowsconsoleio_workaround(sys.stdout) + _colorama_workaround() + _readline_workaround() + pluginmanager = early_config.pluginmanager + capman = CaptureManager(ns.capture) + pluginmanager.register(capman, "capturemanager") + + # Make sure that capturemanager is properly reset at final shutdown. + early_config.add_cleanup(capman.stop_global_capturing) + + # Finally trigger conftest loading but while capturing (issue #93). + capman.start_global_capturing() + try: + try: + yield + finally: + capman.suspend_global_capture() + except BaseException: + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + raise + + +# IO Helpers. + + +class EncodedFile(io.TextIOWrapper): + __slots__ = () + + @property + def name(self) -> str: + # Ensure that file.name is a string. Workaround for a Python bug + # fixed in >=3.7.4: https://bugs.python.org/issue36015 + return repr(self.buffer) + + @property + def mode(self) -> str: + # TextIOWrapper doesn't expose a mode, but at least some of our + # tests check it. + assert hasattr(self.buffer, "mode") + return cast(str, self.buffer.mode.replace("b", "")) + + +class CaptureIO(io.TextIOWrapper): + def __init__(self) -> None: + super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) + + def getvalue(self) -> str: + assert isinstance(self.buffer, io.BytesIO) + return self.buffer.getvalue().decode("UTF-8") + + +class TeeCaptureIO(CaptureIO): + def __init__(self, other: TextIO) -> None: + self._other = other + super().__init__() + + def write(self, s: str) -> int: + super().write(s) + return self._other.write(s) + + +class DontReadFromInput(TextIO): + @property + def encoding(self) -> str: + assert sys.__stdin__ is not None + return sys.__stdin__.encoding + + def read(self, size: int = -1) -> str: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + readline = read + + def __next__(self) -> str: + return self.readline() + + def readlines(self, hint: int | None = -1) -> list[str]: + raise OSError( + "pytest: reading from stdin while output is captured! Consider using `-s`." + ) + + def __iter__(self) -> Iterator[str]: + return self + + def fileno(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") + + def flush(self) -> None: + raise UnsupportedOperation("redirected stdin is pseudofile, has no flush()") + + def isatty(self) -> bool: + return False + + def close(self) -> None: + pass + + def readable(self) -> bool: + return False + + def seek(self, offset: int, whence: int = 0) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)") + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()") + + def truncate(self, size: int | None = None) -> int: + raise UnsupportedOperation("cannot truncate stdin") + + def write(self, data: str) -> int: + raise UnsupportedOperation("cannot write to stdin") + + def writelines(self, lines: Iterable[str]) -> None: + raise UnsupportedOperation("Cannot write to stdin") + + def writable(self) -> bool: + return False + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + type: type[BaseException] | None, + value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + pass + + @property + def buffer(self) -> BinaryIO: + # The str/bytes doesn't actually matter in this type, so OK to fake. + return self # type: ignore[return-value] + + +# Capture classes. + + +class CaptureBase(abc.ABC, Generic[AnyStr]): + EMPTY_BUFFER: AnyStr + + @abc.abstractmethod + def __init__(self, fd: int) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def start(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def done(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def suspend(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def resume(self) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def writeorg(self, data: AnyStr) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def snap(self) -> AnyStr: + raise NotImplementedError() + + +patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} + + +class NoCapture(CaptureBase[str]): + EMPTY_BUFFER = "" + + def __init__(self, fd: int) -> None: + pass + + def start(self) -> None: + pass + + def done(self) -> None: + pass + + def suspend(self) -> None: + pass + + def resume(self) -> None: + pass + + def snap(self) -> str: + return "" + + def writeorg(self, data: str) -> None: + pass + + +class SysCaptureBase(CaptureBase[AnyStr]): + def __init__( + self, fd: int, tmpfile: TextIO | None = None, *, tee: bool = False + ) -> None: + name = patchsysdict[fd] + self._old: TextIO = getattr(sys, name) + self.name = name + if tmpfile is None: + if name == "stdin": + tmpfile = DontReadFromInput() + else: + tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) + self.tmpfile = tmpfile + self._state = "initialized" + + def repr(self, class_name: str) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + class_name, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def __repr__(self) -> str: + return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( + self.__class__.__name__, + self.name, + (hasattr(self, "_old") and repr(self._old)) or "", + self._state, + self.tmpfile, + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + self._assert_state("start", ("initialized",)) + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + def done(self) -> None: + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + setattr(sys, self.name, self._old) + del self._old + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + setattr(sys, self.name, self._old) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + setattr(sys, self.name, self.tmpfile) + self._state = "started" + + +class SysCaptureBinary(SysCaptureBase[bytes]): + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: bytes) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.flush() + self._old.buffer.write(data) + self._old.buffer.flush() + + +class SysCapture(SysCaptureBase[str]): + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + assert isinstance(self.tmpfile, CaptureIO) + res = self.tmpfile.getvalue() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + self._assert_state("writeorg", ("started", "suspended")) + self._old.write(data) + self._old.flush() + + +class FDCaptureBase(CaptureBase[AnyStr]): + def __init__(self, targetfd: int) -> None: + self.targetfd = targetfd + + try: + os.fstat(targetfd) + except OSError: + # FD capturing is conceptually simple -- create a temporary file, + # redirect the FD to it, redirect back when done. But when the + # target FD is invalid it throws a wrench into this lovely scheme. + # + # Tests themselves shouldn't care if the FD is valid, FD capturing + # should work regardless of external circumstances. So falling back + # to just sys capturing is not a good option. + # + # Further complications are the need to support suspend() and the + # possibility of FD reuse (e.g. the tmpfile getting the very same + # target FD). The following approach is robust, I believe. + self.targetfd_invalid: int | None = os.open(os.devnull, os.O_RDWR) + os.dup2(self.targetfd_invalid, targetfd) + else: + self.targetfd_invalid = None + self.targetfd_save = os.dup(targetfd) + + if targetfd == 0: + self.tmpfile = open(os.devnull, encoding="utf-8") + self.syscapture: CaptureBase[str] = SysCapture(targetfd) + else: + self.tmpfile = EncodedFile( + TemporaryFile(buffering=0), + encoding="utf-8", + errors="replace", + newline="", + write_through=True, + ) + if targetfd in patchsysdict: + self.syscapture = SysCapture(targetfd, self.tmpfile) + else: + self.syscapture = NoCapture(targetfd) + + self._state = "initialized" + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.targetfd} oldfd={self.targetfd_save} " + f"_state={self._state!r} tmpfile={self.tmpfile!r}>" + ) + + def _assert_state(self, op: str, states: tuple[str, ...]) -> None: + assert self._state in states, ( + "cannot {} in state {!r}: expected one of {}".format( + op, self._state, ", ".join(states) + ) + ) + + def start(self) -> None: + """Start capturing on targetfd using memorized tmpfile.""" + self._assert_state("start", ("initialized",)) + os.dup2(self.tmpfile.fileno(), self.targetfd) + self.syscapture.start() + self._state = "started" + + def done(self) -> None: + """Stop capturing, restore streams, return original capture file, + seeked to position zero.""" + self._assert_state("done", ("initialized", "started", "suspended", "done")) + if self._state == "done": + return + os.dup2(self.targetfd_save, self.targetfd) + os.close(self.targetfd_save) + if self.targetfd_invalid is not None: + if self.targetfd_invalid != self.targetfd: + os.close(self.targetfd) + os.close(self.targetfd_invalid) + self.syscapture.done() + self.tmpfile.close() + self._state = "done" + + def suspend(self) -> None: + self._assert_state("suspend", ("started", "suspended")) + if self._state == "suspended": + return + self.syscapture.suspend() + os.dup2(self.targetfd_save, self.targetfd) + self._state = "suspended" + + def resume(self) -> None: + self._assert_state("resume", ("started", "suspended")) + if self._state == "started": + return + self.syscapture.resume() + os.dup2(self.tmpfile.fileno(), self.targetfd) + self._state = "started" + + +class FDCaptureBinary(FDCaptureBase[bytes]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces `bytes`. + """ + + EMPTY_BUFFER = b"" + + def snap(self) -> bytes: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.buffer.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res # type: ignore[return-value] + + def writeorg(self, data: bytes) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + os.write(self.targetfd_save, data) + + +class FDCapture(FDCaptureBase[str]): + """Capture IO to/from a given OS-level file descriptor. + + snap() produces text. + """ + + EMPTY_BUFFER = "" + + def snap(self) -> str: + self._assert_state("snap", ("started", "suspended")) + self.tmpfile.seek(0) + res = self.tmpfile.read() + self.tmpfile.seek(0) + self.tmpfile.truncate() + return res + + def writeorg(self, data: str) -> None: + """Write to original file descriptor.""" + self._assert_state("writeorg", ("started", "suspended")) + # XXX use encoding of original stream + os.write(self.targetfd_save, data.encode("utf-8")) + + +# MultiCapture + + +# Generic NamedTuple only supported since Python 3.11. +if sys.version_info >= (3, 11) or TYPE_CHECKING: + + @final + class CaptureResult(NamedTuple, Generic[AnyStr]): + """The result of :method:`caplog.readouterr() `.""" + + out: AnyStr + err: AnyStr + +else: + + class CaptureResult( + collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024 + Generic[AnyStr], + ): + """The result of :method:`caplog.readouterr() `.""" + + __slots__ = () + + +class MultiCapture(Generic[AnyStr]): + _state = None + _in_suspended = False + + def __init__( + self, + in_: CaptureBase[AnyStr] | None, + out: CaptureBase[AnyStr] | None, + err: CaptureBase[AnyStr] | None, + ) -> None: + self.in_: CaptureBase[AnyStr] | None = in_ + self.out: CaptureBase[AnyStr] | None = out + self.err: CaptureBase[AnyStr] | None = err + + def __repr__(self) -> str: + return ( + f"" + ) + + def start_capturing(self) -> None: + self._state = "started" + if self.in_: + self.in_.start() + if self.out: + self.out.start() + if self.err: + self.err.start() + + def pop_outerr_to_orig(self) -> tuple[AnyStr, AnyStr]: + """Pop current snapshot out/err capture and flush to orig streams.""" + out, err = self.readouterr() + if out: + assert self.out is not None + self.out.writeorg(out) + if err: + assert self.err is not None + self.err.writeorg(err) + return out, err + + def suspend_capturing(self, in_: bool = False) -> None: + self._state = "suspended" + if self.out: + self.out.suspend() + if self.err: + self.err.suspend() + if in_ and self.in_: + self.in_.suspend() + self._in_suspended = True + + def resume_capturing(self) -> None: + self._state = "started" + if self.out: + self.out.resume() + if self.err: + self.err.resume() + if self._in_suspended: + assert self.in_ is not None + self.in_.resume() + self._in_suspended = False + + def stop_capturing(self) -> None: + """Stop capturing and reset capturing streams.""" + if self._state == "stopped": + raise ValueError("was already stopped") + self._state = "stopped" + if self.out: + self.out.done() + if self.err: + self.err.done() + if self.in_: + self.in_.done() + + def is_started(self) -> bool: + """Whether actively capturing -- not suspended or stopped.""" + return self._state == "started" + + def readouterr(self) -> CaptureResult[AnyStr]: + out = self.out.snap() if self.out else "" + err = self.err.snap() if self.err else "" + # TODO: This type error is real, need to fix. + return CaptureResult(out, err) # type: ignore[arg-type] + + +def _get_multicapture(method: _CaptureMethod) -> MultiCapture[str]: + if method == "fd": + return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) + elif method == "sys": + return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) + elif method == "no": + return MultiCapture(in_=None, out=None, err=None) + elif method == "tee-sys": + return MultiCapture( + in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) + ) + raise ValueError(f"unknown capturing method: {method!r}") + + +# CaptureManager and CaptureFixture + + +class CaptureManager: + """The capture plugin. + + Manages that the appropriate capture method is enabled/disabled during + collection and each test phase (setup, call, teardown). After each of + those points, the captured output is obtained and attached to the + collection/runtest report. + + There are two levels of capture: + + * global: enabled by default and can be suppressed by the ``-s`` + option. This is always enabled/disabled during collection and each test + phase. + + * fixture: when a test function or one of its fixture depend on the + ``capsys`` or ``capfd`` fixtures. In this case special handling is + needed to ensure the fixtures take precedence over the global capture. + """ + + def __init__(self, method: _CaptureMethod) -> None: + self._method: Final = method + self._global_capturing: MultiCapture[str] | None = None + self._capture_fixture: CaptureFixture[Any] | None = None + + def __repr__(self) -> str: + return ( + f"" + ) + + def is_capturing(self) -> str | bool: + if self.is_globally_capturing(): + return "global" + if self._capture_fixture: + return f"fixture {self._capture_fixture.request.fixturename}" + return False + + # Global capturing control + + def is_globally_capturing(self) -> bool: + return self._method != "no" + + def start_global_capturing(self) -> None: + assert self._global_capturing is None + self._global_capturing = _get_multicapture(self._method) + self._global_capturing.start_capturing() + + def stop_global_capturing(self) -> None: + if self._global_capturing is not None: + self._global_capturing.pop_outerr_to_orig() + self._global_capturing.stop_capturing() + self._global_capturing = None + + def resume_global_capture(self) -> None: + # During teardown of the python process, and on rare occasions, capture + # attributes can be `None` while trying to resume global capture. + if self._global_capturing is not None: + self._global_capturing.resume_capturing() + + def suspend_global_capture(self, in_: bool = False) -> None: + if self._global_capturing is not None: + self._global_capturing.suspend_capturing(in_=in_) + + def suspend(self, in_: bool = False) -> None: + # Need to undo local capsys-et-al if it exists before disabling global capture. + self.suspend_fixture() + self.suspend_global_capture(in_) + + def resume(self) -> None: + self.resume_global_capture() + self.resume_fixture() + + def read_global_capture(self) -> CaptureResult[str]: + assert self._global_capturing is not None + return self._global_capturing.readouterr() + + # Fixture Control + + def set_fixture(self, capture_fixture: CaptureFixture[Any]) -> None: + if self._capture_fixture: + current_fixture = self._capture_fixture.request.fixturename + requested_fixture = capture_fixture.request.fixturename + capture_fixture.request.raiseerror( + f"cannot use {requested_fixture} and {current_fixture} at the same time" + ) + self._capture_fixture = capture_fixture + + def unset_fixture(self) -> None: + self._capture_fixture = None + + def activate_fixture(self) -> None: + """If the current item is using ``capsys`` or ``capfd``, activate + them so they take precedence over the global capture.""" + if self._capture_fixture: + self._capture_fixture._start() + + def deactivate_fixture(self) -> None: + """Deactivate the ``capsys`` or ``capfd`` fixture of this item, if any.""" + if self._capture_fixture: + self._capture_fixture.close() + + def suspend_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._suspend() + + def resume_fixture(self) -> None: + if self._capture_fixture: + self._capture_fixture._resume() + + # Helper context managers + + @contextlib.contextmanager + def global_and_fixture_disabled(self) -> Generator[None]: + """Context manager to temporarily disable global and current fixture capturing.""" + do_fixture = self._capture_fixture and self._capture_fixture._is_started() + if do_fixture: + self.suspend_fixture() + do_global = self._global_capturing and self._global_capturing.is_started() + if do_global: + self.suspend_global_capture() + try: + yield + finally: + if do_global: + self.resume_global_capture() + if do_fixture: + self.resume_fixture() + + @contextlib.contextmanager + def item_capture(self, when: str, item: Item) -> Generator[None]: + self.resume_global_capture() + self.activate_fixture() + try: + yield + finally: + self.deactivate_fixture() + self.suspend_global_capture(in_=False) + + out, err = self.read_global_capture() + item.add_report_section(when, "stdout", out) + item.add_report_section(when, "stderr", err) + + # Hooks + + @hookimpl(wrapper=True) + def pytest_make_collect_report( + self, collector: Collector + ) -> Generator[None, CollectReport, CollectReport]: + if isinstance(collector, File): + self.resume_global_capture() + try: + rep = yield + finally: + self.suspend_global_capture() + out, err = self.read_global_capture() + if out: + rep.sections.append(("Captured stdout", out)) + if err: + rep.sections.append(("Captured stderr", err)) + else: + rep = yield + return rep + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: Item) -> Generator[None]: + with self.item_capture("setup", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: Item) -> Generator[None]: + with self.item_capture("call", item): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: Item) -> Generator[None]: + with self.item_capture("teardown", item): + return (yield) + + @hookimpl(tryfirst=True) + def pytest_keyboard_interrupt(self) -> None: + self.stop_global_capturing() + + @hookimpl(tryfirst=True) + def pytest_internalerror(self) -> None: + self.stop_global_capturing() + + +class CaptureFixture(Generic[AnyStr]): + """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, + :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" + + def __init__( + self, + captureclass: type[CaptureBase[AnyStr]], + request: SubRequest, + *, + config: dict[str, Any] | None = None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.captureclass: type[CaptureBase[AnyStr]] = captureclass + self.request = request + self._config = config if config else {} + self._capture: MultiCapture[AnyStr] | None = None + self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER + self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER + + def _start(self) -> None: + if self._capture is None: + self._capture = MultiCapture( + in_=None, + out=self.captureclass(1, **self._config), + err=self.captureclass(2, **self._config), + ) + self._capture.start_capturing() + + def close(self) -> None: + if self._capture is not None: + out, err = self._capture.pop_outerr_to_orig() + self._captured_out += out + self._captured_err += err + self._capture.stop_capturing() + self._capture = None + + def readouterr(self) -> CaptureResult[AnyStr]: + """Read and return the captured output so far, resetting the internal + buffer. + + :returns: + The captured content as a namedtuple with ``out`` and ``err`` + string attributes. + """ + captured_out, captured_err = self._captured_out, self._captured_err + if self._capture is not None: + out, err = self._capture.readouterr() + captured_out += out + captured_err += err + self._captured_out = self.captureclass.EMPTY_BUFFER + self._captured_err = self.captureclass.EMPTY_BUFFER + return CaptureResult(captured_out, captured_err) + + def _suspend(self) -> None: + """Suspend this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.suspend_capturing() + + def _resume(self) -> None: + """Resume this fixture's own capturing temporarily.""" + if self._capture is not None: + self._capture.resume_capturing() + + def _is_started(self) -> bool: + """Whether actively capturing -- not disabled or closed.""" + if self._capture is not None: + return self._capture.is_started() + return False + + @contextlib.contextmanager + def disabled(self) -> Generator[None]: + """Temporarily disable capturing while inside the ``with`` block.""" + capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( + "capturemanager" + ) + with capmanager.global_and_fixture_disabled(): + yield + + +# The fixtures. + + +@fixture +def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capsys): + print("hello") + captured = capsys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capteesys(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable simultaneous text capturing and pass-through of writes + to ``sys.stdout`` and ``sys.stderr`` as defined by ``--capture=``. + + + The captured output is made available via ``capteesys.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + The output is also passed-through, allowing it to be "live-printed", + reported, or both as defined by ``--capture=``. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_output(capteesys): + print("hello") + captured = capteesys.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture( + SysCapture, request, config=dict(tee=True), _ispytest=True + ) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. + + The captured output is made available via ``capsysbinary.readouterr()`` + method calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``bytes`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_output(capsysbinary): + print("hello") + captured = capsysbinary.readouterr() + assert captured.out == b"hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]: + r"""Enable text capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``text`` objects. + + Returns an instance of :class:`CaptureFixture[str] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfd): + os.system('echo "hello"') + captured = capfd.readouterr() + assert captured.out == "hello\n" + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() + + +@fixture +def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: + r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``. + + The captured output is made available via ``capfd.readouterr()`` method + calls, which return a ``(out, err)`` namedtuple. + ``out`` and ``err`` will be ``byte`` objects. + + Returns an instance of :class:`CaptureFixture[bytes] `. + + Example: + + .. code-block:: python + + def test_system_echo(capfdbinary): + os.system('echo "hello"') + captured = capfdbinary.readouterr() + assert captured.out == b"hello\n" + + """ + capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") + capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) + capman.set_fixture(capture_fixture) + capture_fixture._start() + yield capture_fixture + capture_fixture.close() + capman.unset_fixture() diff --git a/.venv/lib/python3.12/site-packages/_pytest/compat.py b/.venv/lib/python3.12/site-packages/_pytest/compat.py new file mode 100644 index 0000000..72c3d09 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/compat.py @@ -0,0 +1,314 @@ +# mypy: allow-untyped-defs +"""Python version compatibility code and random general utilities.""" + +from __future__ import annotations + +from collections.abc import Callable +import enum +import functools +import inspect +from inspect import Parameter +from inspect import Signature +import os +from pathlib import Path +import sys +from typing import Any +from typing import Final +from typing import NoReturn + +import py + + +if sys.version_info >= (3, 14): + from annotationlib import Format + + +#: constant to prepare valuing pylib path replacements/lazy proxies later on +# intended for removal in pytest 8.0 or 9.0 + +# fmt: off +# intentional space to create a fake difference for the verification +LEGACY_PATH = py.path. local +# fmt: on + + +def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH: + """Internal wrapper to prepare lazy proxies for legacy_path instances""" + return LEGACY_PATH(path) + + +# fmt: off +# Singleton type for NOTSET, as described in: +# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions +class NotSetType(enum.Enum): + token = 0 +NOTSET: Final = NotSetType.token +# fmt: on + + +def iscoroutinefunction(func: object) -> bool: + """Return True if func is a coroutine function (a function defined with async + def syntax, and doesn't contain yield), or a function decorated with + @asyncio.coroutine. + + Note: copied and modified from Python 3.5's builtin coroutines.py to avoid + importing asyncio directly, which in turns also initializes the "logging" + module as a side-effect (see issue #8). + """ + return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) + + +def is_async_function(func: object) -> bool: + """Return True if the given function seems to be an async function or + an async generator.""" + return iscoroutinefunction(func) or inspect.isasyncgenfunction(func) + + +def signature(obj: Callable[..., Any]) -> Signature: + """Return signature without evaluating annotations.""" + if sys.version_info >= (3, 14): + return inspect.signature(obj, annotation_format=Format.STRING) + return inspect.signature(obj) + + +def getlocation(function, curdir: str | os.PathLike[str] | None = None) -> str: + function = get_real_func(function) + fn = Path(inspect.getfile(function)) + lineno = function.__code__.co_firstlineno + if curdir is not None: + try: + relfn = fn.relative_to(curdir) + except ValueError: + pass + else: + return f"{relfn}:{lineno + 1}" + return f"{fn}:{lineno + 1}" + + +def num_mock_patch_args(function) -> int: + """Return number of arguments used up by mock arguments (if any).""" + patchings = getattr(function, "patchings", None) + if not patchings: + return 0 + + mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) + ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) + + return len( + [ + p + for p in patchings + if not p.attribute_name + and (p.new is mock_sentinel or p.new is ut_mock_sentinel) + ] + ) + + +def getfuncargnames( + function: Callable[..., object], + *, + name: str = "", + cls: type | None = None, +) -> tuple[str, ...]: + """Return the names of a function's mandatory arguments. + + Should return the names of all function arguments that: + * Aren't bound to an instance or type as in instance or class methods. + * Don't have default values. + * Aren't bound with functools.partial. + * Aren't replaced with mocks. + + The cls arguments indicate that the function should be treated as a bound + method even though it's not unless the function is a static method. + + The name parameter should be the original name in which the function was collected. + """ + # TODO(RonnyPfannschmidt): This function should be refactored when we + # revisit fixtures. The fixture mechanism should ask the node for + # the fixture names, and not try to obtain directly from the + # function object well after collection has occurred. + + # The parameters attribute of a Signature object contains an + # ordered mapping of parameter names to Parameter instances. This + # creates a tuple of the names of the parameters that don't have + # defaults. + try: + parameters = signature(function).parameters.values() + except (ValueError, TypeError) as e: + from _pytest.outcomes import fail + + fail( + f"Could not determine arguments of {function!r}: {e}", + pytrace=False, + ) + + arg_names = tuple( + p.name + for p in parameters + if ( + p.kind is Parameter.POSITIONAL_OR_KEYWORD + or p.kind is Parameter.KEYWORD_ONLY + ) + and p.default is Parameter.empty + ) + if not name: + name = function.__name__ + + # If this function should be treated as a bound method even though + # it's passed as an unbound method or function, and its first parameter + # wasn't defined as positional only, remove the first parameter name. + if not any(p.kind is Parameter.POSITIONAL_ONLY for p in parameters) and ( + # Not using `getattr` because we don't want to resolve the staticmethod. + # Not using `cls.__dict__` because we want to check the entire MRO. + cls + and not isinstance( + inspect.getattr_static(cls, name, default=None), staticmethod + ) + ): + arg_names = arg_names[1:] + # Remove any names that will be replaced with mocks. + if hasattr(function, "__wrapped__"): + arg_names = arg_names[num_mock_patch_args(function) :] + return arg_names + + +def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]: + # Note: this code intentionally mirrors the code at the beginning of + # getfuncargnames, to get the arguments which were excluded from its result + # because they had default values. + return tuple( + p.name + for p in signature(function).parameters.values() + if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) + and p.default is not Parameter.empty + ) + + +_non_printable_ascii_translate_table = { + i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) +} +_non_printable_ascii_translate_table.update( + {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} +) + + +def ascii_escaped(val: bytes | str) -> str: + r"""If val is pure ASCII, return it as an str, otherwise, escape + bytes objects into a sequence of escaped bytes: + + b'\xc3\xb4\xc5\xd6' -> r'\xc3\xb4\xc5\xd6' + + and escapes strings into a sequence of escaped unicode ids, e.g.: + + r'4\nV\U00043efa\x0eMXWB\x1e\u3028\u15fd\xcd\U0007d944' + + Note: + The obvious "v.decode('unicode-escape')" will return + valid UTF-8 unicode if it finds them in bytes, but we + want to return escaped bytes for any byte, even if they match + a UTF-8 string. + """ + if isinstance(val, bytes): + ret = val.decode("ascii", "backslashreplace") + else: + ret = val.encode("unicode_escape").decode("ascii") + return ret.translate(_non_printable_ascii_translate_table) + + +def get_real_func(obj): + """Get the real function object of the (possibly) wrapped object by + :func:`functools.wraps`, or :func:`functools.partial`.""" + obj = inspect.unwrap(obj) + + if isinstance(obj, functools.partial): + obj = obj.func + return obj + + +def getimfunc(func): + try: + return func.__func__ + except AttributeError: + return func + + +def safe_getattr(object: Any, name: str, default: Any) -> Any: + """Like getattr but return default upon any Exception or any OutcomeException. + + Attribute access can potentially fail for 'evil' Python objects. + See issue #214. + It catches OutcomeException because of #2490 (issue #580), new outcomes + are derived from BaseException instead of Exception (for more details + check #2707). + """ + from _pytest.outcomes import TEST_OUTCOME + + try: + return getattr(object, name, default) + except TEST_OUTCOME: + return default + + +def safe_isclass(obj: object) -> bool: + """Ignore any exception via isinstance on Python 3.""" + try: + return inspect.isclass(obj) + except Exception: + return False + + +def get_user_id() -> int | None: + """Return the current process's real user id or None if it could not be + determined. + + :return: The user id or None if it could not be determined. + """ + # mypy follows the version and platform checking expectation of PEP 484: + # https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks + # Containment checks are too complex for mypy v1.5.0 and cause failure. + if sys.platform == "win32" or sys.platform == "emscripten": + # win32 does not have a getuid() function. + # Emscripten has a return 0 stub. + return None + else: + # On other platforms, a return value of -1 is assumed to indicate that + # the current process's real user id could not be determined. + ERROR = -1 + uid = os.getuid() + return uid if uid != ERROR else None + + +if sys.version_info >= (3, 11): + from typing import assert_never +else: + + def assert_never(value: NoReturn) -> NoReturn: + assert False, f"Unhandled value: {value} ({type(value).__name__})" + + +class CallableBool: + """ + A bool-like object that can also be called, returning its true/false value. + + Used for backwards compatibility in cases where something was supposed to be a method + but was implemented as a simple attribute by mistake (see `TerminalReporter.isatty`). + + Do not use in new code. + """ + + def __init__(self, value: bool) -> None: + self._value = value + + def __bool__(self) -> bool: + return self._value + + def __call__(self) -> bool: + return self._value + + +def running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + # Only enable CI mode if one of these env variables is defined and non-empty. + # Note: review `regendoc` tox env in case this list is changed. + env_vars = ["CI", "BUILD_NUMBER"] + return any(os.environ.get(var) for var in env_vars) diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py new file mode 100644 index 0000000..a027dbc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py @@ -0,0 +1,2203 @@ +# mypy: allow-untyped-defs +"""Command line options, config-file and conftest.py processing.""" + +from __future__ import annotations + +import argparse +import builtins +import collections.abc +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence +import contextlib +import copy +import dataclasses +import enum +from functools import lru_cache +import glob +import importlib.metadata +import inspect +import os +import pathlib +import re +import shlex +import sys +from textwrap import dedent +import types +from types import FunctionType +from typing import Any +from typing import cast +from typing import Final +from typing import final +from typing import IO +from typing import TextIO +from typing import TYPE_CHECKING +import warnings + +import pluggy +from pluggy import HookimplMarker +from pluggy import HookimplOpts +from pluggy import HookspecMarker +from pluggy import HookspecOpts +from pluggy import PluginManager + +from .compat import PathAwareHookProxy +from .exceptions import PrintHelp as PrintHelp +from .exceptions import UsageError as UsageError +from .findpaths import ConfigValue +from .findpaths import determine_setup +from _pytest import __version__ +import _pytest._code +from _pytest._code import ExceptionInfo +from _pytest._code import filter_traceback +from _pytest._code.code import TracebackStyle +from _pytest._io import TerminalWriter +from _pytest.compat import assert_never +from _pytest.config.argparsing import Argument +from _pytest.config.argparsing import FILE_OR_DIR +from _pytest.config.argparsing import Parser +import _pytest.deprecated +import _pytest.hookspec +from _pytest.outcomes import fail +from _pytest.outcomes import Skipped +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportMode +from _pytest.pathlib import resolve_package_path +from _pytest.pathlib import safe_exists +from _pytest.stash import Stash +from _pytest.warning_types import PytestConfigWarning +from _pytest.warning_types import warn_explicit_for + + +if TYPE_CHECKING: + from _pytest.assertion.rewrite import AssertionRewritingHook + from _pytest.cacheprovider import Cache + from _pytest.terminal import TerminalReporter + +_PluggyPlugin = object +"""A type to represent plugin objects. + +Plugins can be any namespace, so we can't narrow it down much, but we use an +alias to make the intent clear. + +Ideally this type would be provided by pluggy itself. +""" + + +hookimpl = HookimplMarker("pytest") +hookspec = HookspecMarker("pytest") + + +@final +class ExitCode(enum.IntEnum): + """Encodes the valid exit codes by pytest. + + Currently users and plugins may supply other exit codes as well. + + .. versionadded:: 5.0 + """ + + #: Tests passed. + OK = 0 + #: Tests failed. + TESTS_FAILED = 1 + #: pytest was interrupted. + INTERRUPTED = 2 + #: An internal error got in the way. + INTERNAL_ERROR = 3 + #: pytest was misused. + USAGE_ERROR = 4 + #: pytest couldn't find tests. + NO_TESTS_COLLECTED = 5 + + __module__ = "pytest" + + +class ConftestImportFailure(Exception): + def __init__( + self, + path: pathlib.Path, + *, + cause: Exception, + ) -> None: + self.path = path + self.cause = cause + + def __str__(self) -> str: + return f"{type(self.cause).__name__}: {self.cause} (from {self.path})" + + +def filter_traceback_for_conftest_import_failure( + entry: _pytest._code.TracebackEntry, +) -> bool: + """Filter tracebacks entries which point to pytest internals or importlib. + + Make a special case for importlib because we use it to import test modules and conftest files + in _pytest.pathlib.import_path. + """ + return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) + + +def print_conftest_import_error(e: ConftestImportFailure, file: TextIO) -> None: + exc_info = ExceptionInfo.from_exception(e.cause) + tw = TerminalWriter(file) + tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) + exc_info.traceback = exc_info.traceback.filter( + filter_traceback_for_conftest_import_failure + ) + exc_repr = ( + exc_info.getrepr(style="short", chain=False) + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + for line in formatted_tb.splitlines(): + tw.line(line.rstrip(), red=True) + + +def print_usage_error(e: UsageError, file: TextIO) -> None: + tw = TerminalWriter(file) + for msg in e.args: + tw.line(f"ERROR: {msg}\n", red=True) + + +def main( + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> int | ExitCode: + """Perform an in-process test run. + + :param args: + List of command line arguments. If `None` or not given, defaults to reading + arguments directly from the process command line (:data:`sys.argv`). + :param plugins: List of plugin objects to be auto-registered during initialization. + + :returns: An exit code. + """ + # Handle a single `--version` argument early to avoid starting up the entire pytest infrastructure. + new_args = sys.argv[1:] if args is None else args + if isinstance(new_args, Sequence) and new_args.count("--version") == 1: + sys.stdout.write(f"pytest {__version__}\n") + return ExitCode.OK + + old_pytest_version = os.environ.get("PYTEST_VERSION") + try: + os.environ["PYTEST_VERSION"] = __version__ + try: + config = _prepareconfig(new_args, plugins) + except ConftestImportFailure as e: + print_conftest_import_error(e, file=sys.stderr) + return ExitCode.USAGE_ERROR + + try: + ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) + try: + return ExitCode(ret) + except ValueError: + return ret + finally: + config._ensure_unconfigure() + except UsageError as e: + print_usage_error(e, file=sys.stderr) + return ExitCode.USAGE_ERROR + finally: + if old_pytest_version is None: + os.environ.pop("PYTEST_VERSION", None) + else: + os.environ["PYTEST_VERSION"] = old_pytest_version + + +def console_main() -> int: + """The CLI entry point of pytest. + + This function is not meant for programmable use; use `main()` instead. + """ + # https://docs.python.org/3/library/signal.html#note-on-sigpipe + try: + code = main() + sys.stdout.flush() + return code + except BrokenPipeError: + # Python flushes standard streams on exit; redirect remaining output + # to devnull to avoid another BrokenPipeError at shutdown + devnull = os.open(os.devnull, os.O_WRONLY) + os.dup2(devnull, sys.stdout.fileno()) + return 1 # Python exits with error code 1 on EPIPE + + +class cmdline: # compatibility namespace + main = staticmethod(main) + + +def filename_arg(path: str, optname: str) -> str: + """Argparse type validator for filename arguments. + + :path: Path of filename. + :optname: Name of the option. + """ + if os.path.isdir(path): + raise UsageError(f"{optname} must be a filename, given: {path}") + return path + + +def directory_arg(path: str, optname: str) -> str: + """Argparse type validator for directory arguments. + + :path: Path of directory. + :optname: Name of the option. + """ + if not os.path.isdir(path): + raise UsageError(f"{optname} must be a directory, given: {path}") + return path + + +# Plugins that cannot be disabled via "-p no:X" currently. +essential_plugins = ( + "mark", + "main", + "runner", + "fixtures", + "helpconfig", # Provides -p. +) + +default_plugins = ( + *essential_plugins, + "python", + "terminal", + "debugging", + "unittest", + "capture", + "skipping", + "legacypath", + "tmpdir", + "monkeypatch", + "recwarn", + "pastebin", + "assertion", + "junitxml", + "doctest", + "cacheprovider", + "setuponly", + "setupplan", + "stepwise", + "unraisableexception", + "threadexception", + "warnings", + "logging", + "reports", + "faulthandler", + "subtests", +) + +builtin_plugins = { + *default_plugins, + "pytester", + "pytester_assertions", + "terminalprogress", +} + + +def get_config( + args: Iterable[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + # Subsequent calls to main will create a fresh instance. + pluginmanager = PytestPluginManager() + invocation_params = Config.InvocationParams( + args=args or (), + plugins=plugins, + dir=pathlib.Path.cwd(), + ) + config = Config(pluginmanager, invocation_params=invocation_params) + + if invocation_params.args: + # Handle any "-p no:plugin" args. + pluginmanager.consider_preparse(invocation_params.args, exclude_only=True) + + for spec in default_plugins: + pluginmanager.import_plugin(spec) + + return config + + +def get_plugin_manager() -> PytestPluginManager: + """Obtain a new instance of the + :py:class:`pytest.PytestPluginManager`, with default plugins + already loaded. + + This function can be used by integration with other tools, like hooking + into pytest to run tests into an IDE. + """ + return get_config().pluginmanager + + +def _prepareconfig( + args: list[str] | os.PathLike[str], + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> Config: + if isinstance(args, os.PathLike): + args = [os.fspath(args)] + elif not isinstance(args, list): + msg = ( # type:ignore[unreachable] + "`args` parameter expected to be a list of strings, got: {!r} (type: {})" + ) + raise TypeError(msg.format(args, type(args))) + + initial_config = get_config(args, plugins) + pluginmanager = initial_config.pluginmanager + try: + if plugins: + for plugin in plugins: + if isinstance(plugin, str): + pluginmanager.consider_pluginarg(plugin) + else: + pluginmanager.register(plugin) + config: Config = pluginmanager.hook.pytest_cmdline_parse( + pluginmanager=pluginmanager, args=args + ) + return config + except BaseException: + initial_config._ensure_unconfigure() + raise + + +def _get_directory(path: pathlib.Path) -> pathlib.Path: + """Get the directory of a path - itself if already a directory.""" + if path.is_file(): + return path.parent + else: + return path + + +def _get_legacy_hook_marks( + method: Any, + hook_type: str, + opt_names: tuple[str, ...], +) -> dict[str, bool]: + if TYPE_CHECKING: + # abuse typeguard from importlib to avoid massive method type union that's lacking an alias + assert inspect.isroutine(method) + known_marks: set[str] = {m.name for m in getattr(method, "pytestmark", [])} + must_warn: list[str] = [] + opts: dict[str, bool] = {} + for opt_name in opt_names: + opt_attr = getattr(method, opt_name, AttributeError) + if opt_attr is not AttributeError: + must_warn.append(f"{opt_name}={opt_attr}") + opts[opt_name] = True + elif opt_name in known_marks: + must_warn.append(f"{opt_name}=True") + opts[opt_name] = True + else: + opts[opt_name] = False + if must_warn: + hook_opts = ", ".join(must_warn) + message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( + type=hook_type, + fullname=method.__qualname__, + hook_opts=hook_opts, + ) + warn_explicit_for(cast(FunctionType, method), message) + return opts + + +@final +class PytestPluginManager(PluginManager): + """A :py:class:`pluggy.PluginManager ` with + additional pytest-specific functionality: + + * Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and + ``pytest_plugins`` global variables found in plugins being loaded. + * ``conftest.py`` loading during start-up. + """ + + def __init__(self) -> None: + from _pytest.assertion import DummyRewriteHook + from _pytest.assertion import RewriteHook + + super().__init__("pytest") + + # -- State related to local conftest plugins. + # All loaded conftest modules. + self._conftest_plugins: set[types.ModuleType] = set() + # All conftest modules applicable for a directory. + # This includes the directory's own conftest modules as well + # as those of its parent directories. + self._dirpath2confmods: dict[pathlib.Path, list[types.ModuleType]] = {} + # Cutoff directory above which conftests are no longer discovered. + self._confcutdir: pathlib.Path | None = None + # If set, conftest loading is skipped. + self._noconftest = False + + # _getconftestmodules()'s call to _get_directory() causes a stat + # storm when it's called potentially thousands of times in a test + # session (#9478), often with the same path, so cache it. + self._get_directory = lru_cache(256)(_get_directory) + + # plugins that were explicitly skipped with pytest.skip + # list of (module name, skip reason) + # previously we would issue a warning when a plugin was skipped, but + # since we refactored warnings as first citizens of Config, they are + # just stored here to be used later. + self.skipped_plugins: list[tuple[str, str]] = [] + + self.add_hookspecs(_pytest.hookspec) + self.register(self) + if os.environ.get("PYTEST_DEBUG"): + err: IO[str] = sys.stderr + encoding: str = getattr(err, "encoding", "utf8") + try: + err = open( + os.dup(err.fileno()), + mode=err.mode, + buffering=1, + encoding=encoding, + ) + except Exception: + pass + self.trace.root.setwriter(err.write) + self.enable_tracing() + + # Config._consider_importhook will set a real object if required. + self.rewrite_hook: RewriteHook = DummyRewriteHook() + # Used to know when we are importing conftests after the pytest_configure stage. + self._configured = False + + def parse_hookimpl_opts( + self, plugin: _PluggyPlugin, name: str + ) -> HookimplOpts | None: + """:meta private:""" + # pytest hooks are always prefixed with "pytest_", + # so we avoid accessing possibly non-readable attributes + # (see issue #1073). + if not name.startswith("pytest_"): + return None + # Ignore names which cannot be hooks. + if name == "pytest_plugins": + return None + + opts = super().parse_hookimpl_opts(plugin, name) + if opts is not None: + return opts + + method = getattr(plugin, name) + # Consider only actual functions for hooks (#3775). + if not inspect.isroutine(method): + return None + # Collect unmarked hooks as long as they have the `pytest_' prefix. + legacy = _get_legacy_hook_marks( + method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") + ) + return cast(HookimplOpts, legacy) + + def parse_hookspec_opts(self, module_or_class, name: str) -> HookspecOpts | None: + """:meta private:""" + opts = super().parse_hookspec_opts(module_or_class, name) + if opts is None: + method = getattr(module_or_class, name) + if name.startswith("pytest_"): + legacy = _get_legacy_hook_marks( + method, "spec", ("firstresult", "historic") + ) + opts = cast(HookspecOpts, legacy) + return opts + + def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None: + if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: + warnings.warn( + PytestConfigWarning( + "{} plugin has been merged into the core, " + "please remove it from your requirements.".format( + name.replace("_", "-") + ) + ) + ) + return None + plugin_name = super().register(plugin, name) + if plugin_name is not None: + self.hook.pytest_plugin_registered.call_historic( + kwargs=dict( + plugin=plugin, + plugin_name=plugin_name, + manager=self, + ) + ) + + if isinstance(plugin, types.ModuleType): + self.consider_module(plugin) + return plugin_name + + def getplugin(self, name: str): + # Support deprecated naming because plugins (xdist e.g.) use it. + plugin: _PluggyPlugin | None = self.get_plugin(name) + return plugin + + def hasplugin(self, name: str) -> bool: + """Return whether a plugin with the given name is registered.""" + return bool(self.get_plugin(name)) + + def pytest_configure(self, config: Config) -> None: + """:meta private:""" + # XXX now that the pluginmanager exposes hookimpl(tryfirst...) + # we should remove tryfirst/trylast as markers. + config.addinivalue_line( + "markers", + "tryfirst: mark a hook implementation function such that the " + "plugin machinery will try to call it first/as early as possible. " + "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", + ) + config.addinivalue_line( + "markers", + "trylast: mark a hook implementation function such that the " + "plugin machinery will try to call it last/as late as possible. " + "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", + ) + self._configured = True + + # + # Internal API for local conftest plugin handling. + # + def _set_initial_conftests( + self, + args: Sequence[str | pathlib.Path], + pyargs: bool, + noconftest: bool, + rootpath: pathlib.Path, + confcutdir: pathlib.Path | None, + invocation_dir: pathlib.Path, + importmode: ImportMode | str, + *, + consider_namespace_packages: bool, + ) -> None: + """Load initial conftest files given a preparsed "namespace". + + As conftest files may add their own command line options which have + arguments ('--my-opt somepath') we might get some false positives. + All builtin and 3rd party plugins will have been loaded, however, so + common options will not confuse our logic here. + """ + self._confcutdir = ( + absolutepath(invocation_dir / confcutdir) if confcutdir else None + ) + self._noconftest = noconftest + self._using_pyargs = pyargs + foundanchor = False + for initial_path in args: + path = str(initial_path) + # remove node-id syntax + i = path.find("::") + if i != -1: + path = path[:i] + anchor = absolutepath(invocation_dir / path) + + # Ensure we do not break if what appears to be an anchor + # is in fact a very long option (#10169, #11394). + if safe_exists(anchor): + self._try_load_conftest( + anchor, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + foundanchor = True + if not foundanchor: + self._try_load_conftest( + invocation_dir, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + + def _is_in_confcutdir(self, path: pathlib.Path) -> bool: + """Whether to consider the given path to load conftests from.""" + if self._confcutdir is None: + return True + # The semantics here are literally: + # Do not load a conftest if it is found upwards from confcut dir. + # But this is *not* the same as: + # Load only conftests from confcutdir or below. + # At first glance they might seem the same thing, however we do support use cases where + # we want to load conftests that are not found in confcutdir or below, but are found + # in completely different directory hierarchies like packages installed + # in out-of-source trees. + # (see #9767 for a regression where the logic was inverted). + return path not in self._confcutdir.parents + + def _try_load_conftest( + self, + anchor: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> None: + self._loadconftestmodules( + anchor, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + # let's also consider test* subdirs + if anchor.is_dir(): + for x in anchor.glob("test*"): + if x.is_dir(): + self._loadconftestmodules( + x, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + + def _loadconftestmodules( + self, + path: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> None: + if self._noconftest: + return + + directory = self._get_directory(path) + + # Optimization: avoid repeated searches in the same directory. + # Assumes always called with same importmode and rootpath. + if directory in self._dirpath2confmods: + return + + clist = [] + for parent in reversed((directory, *directory.parents)): + if self._is_in_confcutdir(parent): + conftestpath = parent / "conftest.py" + if conftestpath.is_file(): + mod = self._importconftest( + conftestpath, + importmode, + rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + clist.append(mod) + self._dirpath2confmods[directory] = clist + + def _getconftestmodules(self, path: pathlib.Path) -> Sequence[types.ModuleType]: + directory = self._get_directory(path) + return self._dirpath2confmods.get(directory, ()) + + def _rget_with_confmod( + self, + name: str, + path: pathlib.Path, + ) -> tuple[types.ModuleType, Any]: + modules = self._getconftestmodules(path) + for mod in reversed(modules): + try: + return mod, getattr(mod, name) + except AttributeError: + continue + raise KeyError(name) + + def _importconftest( + self, + conftestpath: pathlib.Path, + importmode: str | ImportMode, + rootpath: pathlib.Path, + *, + consider_namespace_packages: bool, + ) -> types.ModuleType: + conftestpath_plugin_name = str(conftestpath) + existing = self.get_plugin(conftestpath_plugin_name) + if existing is not None: + return cast(types.ModuleType, existing) + + # conftest.py files there are not in a Python package all have module + # name "conftest", and thus conflict with each other. Clear the existing + # before loading the new one, otherwise the existing one will be + # returned from the module cache. + pkgpath = resolve_package_path(conftestpath) + if pkgpath is None: + try: + del sys.modules[conftestpath.stem] + except KeyError: + pass + + try: + mod = import_path( + conftestpath, + mode=importmode, + root=rootpath, + consider_namespace_packages=consider_namespace_packages, + ) + except Exception as e: + assert e.__traceback__ is not None + raise ConftestImportFailure(conftestpath, cause=e) from e + + self._check_non_top_pytest_plugins(mod, conftestpath) + + self._conftest_plugins.add(mod) + dirpath = conftestpath.parent + if dirpath in self._dirpath2confmods: + for path, mods in self._dirpath2confmods.items(): + if dirpath in path.parents or path == dirpath: + if mod in mods: + raise AssertionError( + f"While trying to load conftest path {conftestpath!s}, " + f"found that the module {mod} is already loaded with path {mod.__file__}. " + "This is not supposed to happen. Please report this issue to pytest." + ) + mods.append(mod) + self.trace(f"loading conftestmodule {mod!r}") + self.consider_conftest(mod, registration_name=conftestpath_plugin_name) + return mod + + def _check_non_top_pytest_plugins( + self, + mod: types.ModuleType, + conftestpath: pathlib.Path, + ) -> None: + if ( + hasattr(mod, "pytest_plugins") + and self._configured + and not self._using_pyargs + ): + msg = ( + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" + "It affects the entire test suite instead of just below the conftest as expected.\n" + " {}\n" + "Please move it to a top level conftest file at the rootdir:\n" + " {}\n" + "For more information, visit:\n" + " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" + ) + fail(msg.format(conftestpath, self._confcutdir), pytrace=False) + + # + # API for bootstrapping plugin loading + # + # + + def consider_preparse( + self, args: Sequence[str], *, exclude_only: bool = False + ) -> None: + """:meta private:""" + i = 0 + n = len(args) + while i < n: + opt = args[i] + i += 1 + if isinstance(opt, str): + if opt == "-p": + try: + parg = args[i] + except IndexError: + return + i += 1 + elif opt.startswith("-p"): + parg = opt[2:] + else: + continue + parg = parg.strip() + if exclude_only and not parg.startswith("no:"): + continue + self.consider_pluginarg(parg) + + def consider_pluginarg(self, arg: str) -> None: + """:meta private:""" + if arg.startswith("no:"): + name = arg[3:] + if name in essential_plugins: + raise UsageError(f"plugin {name} cannot be disabled") + + if name.endswith("conftest.py"): + raise UsageError( + f"Blocking conftest files using -p is not supported: -p no:{name}\n" + "conftest.py files are not plugins and cannot be disabled via -p.\n" + ) + + # PR #4304: remove stepwise if cacheprovider is blocked. + if name == "cacheprovider": + self.set_blocked("stepwise") + self.set_blocked("pytest_stepwise") + + self.set_blocked(name) + if not name.startswith("pytest_"): + self.set_blocked("pytest_" + name) + else: + name = arg + # Unblock the plugin. + self.unblock(name) + if not name.startswith("pytest_"): + self.unblock("pytest_" + name) + self.import_plugin(arg, consider_entry_points=True) + + def consider_conftest( + self, conftestmodule: types.ModuleType, registration_name: str + ) -> None: + """:meta private:""" + self.register(conftestmodule, name=registration_name) + + def consider_env(self) -> None: + """:meta private:""" + self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) + + def consider_module(self, mod: types.ModuleType) -> None: + """:meta private:""" + self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) + + def _import_plugin_specs( + self, spec: None | types.ModuleType | str | Sequence[str] + ) -> None: + plugins = _get_plugin_specs_as_list(spec) + for import_spec in plugins: + self.import_plugin(import_spec) + + def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: + """Import a plugin with ``modname``. + + If ``consider_entry_points`` is True, entry point names are also + considered to find a plugin. + """ + # Most often modname refers to builtin modules, e.g. "pytester", + # "terminal" or "capture". Those plugins are registered under their + # basename for historic purposes but must be imported with the + # _pytest prefix. + assert isinstance(modname, str), ( + f"module name as text required, got {modname!r}" + ) + if self.is_blocked(modname) or self.get_plugin(modname) is not None: + return + + importspec = "_pytest." + modname if modname in builtin_plugins else modname + self.rewrite_hook.mark_rewrite(importspec) + + if consider_entry_points: + loaded = self.load_setuptools_entrypoints("pytest11", name=modname) + if loaded: + return + + try: + __import__(importspec) + except ImportError as e: + raise ImportError( + f'Error importing plugin "{modname}": {e.args[0]}' + ).with_traceback(e.__traceback__) from e + + except Skipped as e: + self.skipped_plugins.append((modname, e.msg or "")) + else: + mod = sys.modules[importspec] + self.register(mod, modname) + + +def _get_plugin_specs_as_list( + specs: None | types.ModuleType | str | Sequence[str], +) -> list[str]: + """Parse a plugins specification into a list of plugin names.""" + # None means empty. + if specs is None: + return [] + # Workaround for #3899 - a submodule which happens to be called "pytest_plugins". + if isinstance(specs, types.ModuleType): + return [] + # Comma-separated list. + if isinstance(specs, str): + return specs.split(",") if specs else [] + # Direct specification. + if isinstance(specs, collections.abc.Sequence): + return list(specs) + raise UsageError( + f"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: {specs!r}" + ) + + +class Notset: + def __repr__(self): + return "" + + +notset = Notset() + + +def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: + """Given an iterable of file names in a source distribution, return the "names" that should + be marked for assertion rewrite. + + For example the package "pytest_mock/__init__.py" should be added as "pytest_mock" in + the assertion rewrite mechanism. + + This function has to deal with dist-info based distributions and egg based distributions + (which are still very much in use for "editable" installs). + + Here are the file names as seen in a dist-info based distribution: + + pytest_mock/__init__.py + pytest_mock/_version.py + pytest_mock/plugin.py + pytest_mock.egg-info/PKG-INFO + + Here are the file names as seen in an egg based distribution: + + src/pytest_mock/__init__.py + src/pytest_mock/_version.py + src/pytest_mock/plugin.py + src/pytest_mock.egg-info/PKG-INFO + LICENSE + setup.py + + We have to take in account those two distribution flavors in order to determine which + names should be considered for assertion rewriting. + + More information: + https://github.com/pytest-dev/pytest-mock/issues/167 + """ + package_files = list(package_files) + seen_some = False + for fn in package_files: + is_simple_module = "/" not in fn and fn.endswith(".py") + is_package = fn.count("/") == 1 and fn.endswith("__init__.py") + if is_simple_module: + module_name, _ = os.path.splitext(fn) + # we ignore "setup.py" at the root of the distribution + # as well as editable installation finder modules made by setuptools + if module_name != "setup" and not module_name.startswith("__editable__"): + seen_some = True + yield module_name + elif is_package: + package_name = os.path.dirname(fn) + seen_some = True + yield package_name + + if not seen_some: + # At this point we did not find any packages or modules suitable for assertion + # rewriting, so we try again by stripping the first path component (to account for + # "src" based source trees for example). + # This approach lets us have the common case continue to be fast, as egg-distributions + # are rarer. + new_package_files = [] + for fn in package_files: + parts = fn.split("/") + new_fn = "/".join(parts[1:]) + if new_fn: + new_package_files.append(new_fn) + if new_package_files: + yield from _iter_rewritable_modules(new_package_files) + + +class _DeprecatedInicfgProxy(MutableMapping[str, Any]): + """Compatibility proxy for the deprecated Config.inicfg.""" + + __slots__ = ("_config",) + + def __init__(self, config: Config) -> None: + self._config = config + + def __getitem__(self, key: str) -> Any: + return self._config._inicfg[key].value + + def __setitem__(self, key: str, value: Any) -> None: + self._config._inicfg[key] = ConfigValue(value, origin="override", mode="toml") + + def __delitem__(self, key: str) -> None: + del self._config._inicfg[key] + + def __iter__(self) -> Iterator[str]: + return iter(self._config._inicfg) + + def __len__(self) -> int: + return len(self._config._inicfg) + + +@final +class Config: + """Access to configuration values, pluginmanager and plugin hooks. + + :param PytestPluginManager pluginmanager: + A pytest PluginManager. + + :param InvocationParams invocation_params: + Object containing parameters regarding the :func:`pytest.main` + invocation. + """ + + @final + @dataclasses.dataclass(frozen=True) + class InvocationParams: + """Holds parameters passed during :func:`pytest.main`. + + The object attributes are read-only. + + .. versionadded:: 5.1 + + .. note:: + + Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` + configuration option are handled by pytest, not being included in the ``args`` attribute. + + Plugins accessing ``InvocationParams`` must be aware of that. + """ + + args: tuple[str, ...] + """The command-line arguments as passed to :func:`pytest.main`.""" + plugins: Sequence[str | _PluggyPlugin] | None + """Extra plugins, might be `None`.""" + dir: pathlib.Path + """The directory from which :func:`pytest.main` was invoked.""" + + def __init__( + self, + *, + args: Iterable[str], + plugins: Sequence[str | _PluggyPlugin] | None, + dir: pathlib.Path, + ) -> None: + object.__setattr__(self, "args", tuple(args)) + object.__setattr__(self, "plugins", plugins) + object.__setattr__(self, "dir", dir) + + class ArgsSource(enum.Enum): + """Indicates the source of the test arguments. + + .. versionadded:: 7.2 + """ + + #: Command line arguments. + ARGS = enum.auto() + #: Invocation directory. + INVOCATION_DIR = enum.auto() + INCOVATION_DIR = INVOCATION_DIR # backwards compatibility alias + #: 'testpaths' configuration value. + TESTPATHS = enum.auto() + + # Set by cacheprovider plugin. + cache: Cache + + def __init__( + self, + pluginmanager: PytestPluginManager, + *, + invocation_params: InvocationParams | None = None, + ) -> None: + if invocation_params is None: + invocation_params = self.InvocationParams( + args=(), plugins=None, dir=pathlib.Path.cwd() + ) + + self.option = argparse.Namespace() + """Access to command line option as attributes. + + :type: argparse.Namespace + """ + + self.invocation_params = invocation_params + """The parameters with which pytest was invoked. + + :type: InvocationParams + """ + + self._parser = Parser( + usage=f"%(prog)s [options] [{FILE_OR_DIR}] [{FILE_OR_DIR}] [...]", + processopt=self._processopt, + _ispytest=True, + ) + self.pluginmanager = pluginmanager + """The plugin manager handles plugin registration and hook invocation. + + :type: PytestPluginManager + """ + + self.stash = Stash() + """A place where plugins can store information on the config for their + own use. + + :type: Stash + """ + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + + self.trace = self.pluginmanager.trace.root.get("config") + self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] + self._inicache: dict[str, Any] = {} + self._opt2dest: dict[str, str] = {} + self._cleanup_stack = contextlib.ExitStack() + self.pluginmanager.register(self, "pytestconfig") + self._configured = False + self.hook.pytest_addoption.call_historic( + kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) + ) + self.args_source = Config.ArgsSource.ARGS + self.args: list[str] = [] + + @property + def inicfg(self) -> _DeprecatedInicfgProxy: + return _DeprecatedInicfgProxy(self) + + @property + def rootpath(self) -> pathlib.Path: + """The path to the :ref:`rootdir `. + + .. versionadded:: 6.1 + """ + return self._rootpath + + @property + def inipath(self) -> pathlib.Path | None: + """The path to the :ref:`configfile `. + + .. versionadded:: 6.1 + """ + return self._inipath + + def add_cleanup(self, func: Callable[[], None]) -> None: + """Add a function to be called when the config object gets out of + use (usually coinciding with pytest_unconfigure). + """ + self._cleanup_stack.callback(func) + + def _do_configure(self) -> None: + assert not self._configured + self._configured = True + self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) + + def _ensure_unconfigure(self) -> None: + try: + if self._configured: + self._configured = False + try: + self.hook.pytest_unconfigure(config=self) + finally: + self.hook.pytest_configure._call_history = [] + finally: + try: + self._cleanup_stack.close() + finally: + self._cleanup_stack = contextlib.ExitStack() + + def get_terminal_writer(self) -> TerminalWriter: + terminalreporter: TerminalReporter | None = self.pluginmanager.get_plugin( + "terminalreporter" + ) + assert terminalreporter is not None + return terminalreporter._tw + + def pytest_cmdline_parse( + self, pluginmanager: PytestPluginManager, args: list[str] + ) -> Config: + try: + self.parse(args) + except UsageError: + # Handle `--version --version` and `--help` here in a minimal fashion. + # This gets done via helpconfig normally, but its + # pytest_cmdline_main is not called in case of errors. + if getattr(self.option, "version", False) or "--version" in args: + from _pytest.helpconfig import show_version_verbose + + # Note that `--version` (single argument) is handled early by `Config.main()`, so the only + # way we are reaching this point is via `--version --version`. + show_version_verbose(self) + elif ( + getattr(self.option, "help", False) or "--help" in args or "-h" in args + ): + self._parser.optparser.print_help() + sys.stdout.write( + "\nNOTE: displaying only minimal help due to UsageError.\n\n" + ) + + raise + + return self + + def notify_exception( + self, + excinfo: ExceptionInfo[BaseException], + option: argparse.Namespace | None = None, + ) -> None: + if option and getattr(option, "fulltrace", False): + style: TracebackStyle = "long" + else: + style = "native" + excrepr = excinfo.getrepr( + funcargs=True, showlocals=getattr(option, "showlocals", False), style=style + ) + res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) + if not any(res): + for line in str(excrepr).split("\n"): + sys.stderr.write(f"INTERNALERROR> {line}\n") + sys.stderr.flush() + + def cwd_relative_nodeid(self, nodeid: str) -> str: + # nodeid's are relative to the rootpath, compute relative to cwd. + if self.invocation_params.dir != self.rootpath: + base_path_part, *nodeid_part = nodeid.split("::") + # Only process path part + fullpath = self.rootpath / base_path_part + relative_path = bestrelpath(self.invocation_params.dir, fullpath) + + nodeid = "::".join([relative_path, *nodeid_part]) + return nodeid + + @classmethod + def fromdictargs(cls, option_dict: Mapping[str, Any], args: list[str]) -> Config: + """Constructor usable for subprocesses.""" + config = get_config(args) + config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) + for x in config.option.plugins: + config.pluginmanager.consider_pluginarg(x) + return config + + def _processopt(self, opt: Argument) -> None: + for name in opt._short_opts + opt._long_opts: + self._opt2dest[name] = opt.dest + + if hasattr(opt, "default"): + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) + + @hookimpl(trylast=True) + def pytest_load_initial_conftests(self, early_config: Config) -> None: + # We haven't fully parsed the command line arguments yet, so + # early_config.args it not set yet. But we need it for + # discovering the initial conftests. So "pre-run" the logic here. + # It will be done for real in `parse()`. + args, _args_source = early_config._decide_args( + args=early_config.known_args_namespace.file_or_dir, + pyargs=early_config.known_args_namespace.pyargs, + testpaths=early_config.getini("testpaths"), + invocation_dir=early_config.invocation_params.dir, + rootpath=early_config.rootpath, + warn=False, + ) + self.pluginmanager._set_initial_conftests( + args=args, + pyargs=early_config.known_args_namespace.pyargs, + noconftest=early_config.known_args_namespace.noconftest, + rootpath=early_config.rootpath, + confcutdir=early_config.known_args_namespace.confcutdir, + invocation_dir=early_config.invocation_params.dir, + importmode=early_config.known_args_namespace.importmode, + consider_namespace_packages=early_config.getini( + "consider_namespace_packages" + ), + ) + + def _consider_importhook(self) -> None: + """Install the PEP 302 import hook if using assertion rewriting. + + Needs to parse the --assert= option from the commandline + and find all the installed plugins to mark them for rewriting + by the importhook. + """ + mode = getattr(self.known_args_namespace, "assertmode", "plain") + + disable_autoload = getattr( + self.known_args_namespace, "disable_plugin_autoload", False + ) or bool(os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD")) + if mode == "rewrite": + import _pytest.assertion + + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = "plain" + else: + self._mark_plugins_for_rewrite(hook, disable_autoload) + self._warn_about_missing_assertion(mode) + + def _mark_plugins_for_rewrite( + self, hook: AssertionRewritingHook, disable_autoload: bool + ) -> None: + """Given an importhook, mark for rewrite any top-level + modules or packages in the distribution package for + all pytest plugins.""" + self.pluginmanager.rewrite_hook = hook + + if disable_autoload: + # We don't autoload from distribution package entry points, + # no need to continue. + return + + package_files = ( + str(file) + for dist in importlib.metadata.distributions() + if any(ep.group == "pytest11" for ep in dist.entry_points) + for file in dist.files or [] + ) + + for name in _iter_rewritable_modules(package_files): + hook.mark_rewrite(name) + + def _configure_python_path(self) -> None: + # `pythonpath = a b` will set `sys.path` to `[a, b, x, y, z, ...]` + for path in reversed(self.getini("pythonpath")): + sys.path.insert(0, str(path)) + self.add_cleanup(self._unconfigure_python_path) + + def _unconfigure_python_path(self) -> None: + for path in self.getini("pythonpath"): + path_str = str(path) + if path_str in sys.path: + sys.path.remove(path_str) + + def _validate_args(self, args: list[str], via: str) -> list[str]: + """Validate known args.""" + self._parser.extra_info["config source"] = via + try: + self._parser.parse_known_and_unknown_args( + args, namespace=copy.copy(self.option) + ) + finally: + self._parser.extra_info.pop("config source", None) + + return args + + def _decide_args( + self, + *, + args: list[str], + pyargs: bool, + testpaths: list[str], + invocation_dir: pathlib.Path, + rootpath: pathlib.Path, + warn: bool, + ) -> tuple[list[str], ArgsSource]: + """Decide the args (initial paths/nodeids) to use given the relevant inputs. + + :param warn: Whether can issue warnings. + + :returns: The args and the args source. Guaranteed to be non-empty. + """ + if args: + source = Config.ArgsSource.ARGS + result = args + else: + if invocation_dir == rootpath: + source = Config.ArgsSource.TESTPATHS + if pyargs: + result = testpaths + else: + result = [] + for path in testpaths: + result.extend(sorted(glob.iglob(path, recursive=True))) + if testpaths and not result: + if warn: + warning_text = ( + "No files were found in testpaths; " + "consider removing or adjusting your testpaths configuration. " + "Searching recursively from the current directory instead." + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), stacklevel=3 + ) + else: + result = [] + if not result: + source = Config.ArgsSource.INVOCATION_DIR + result = [str(invocation_dir)] + return result, source + + @hookimpl(wrapper=True) + def pytest_collection(self) -> Generator[None, object, object]: + # Validate invalid configuration keys after collection is done so we + # take in account options added by late-loading conftest files. + try: + return (yield) + finally: + self._validate_config_options() + + def _checkversion(self) -> None: + import pytest + + minver_ini_value = self._inicfg.get("minversion", None) + minver = minver_ini_value.value if minver_ini_value is not None else None + if minver: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if not isinstance(minver, str): + raise pytest.UsageError( + f"{self.inipath}: 'minversion' must be a single value" + ) + + if Version(minver) > Version(pytest.__version__): + raise pytest.UsageError( + f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" + ) + + def _validate_config_options(self) -> None: + for key in sorted(self._get_unknown_ini_keys()): + self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") + + def _validate_plugins(self) -> None: + required_plugins = sorted(self.getini("required_plugins")) + if not required_plugins: + return + + # Imported lazily to improve start-up time. + from packaging.requirements import InvalidRequirement + from packaging.requirements import Requirement + from packaging.version import Version + + plugin_info = self.pluginmanager.list_plugin_distinfo() + plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} + + missing_plugins = [] + for required_plugin in required_plugins: + try: + req = Requirement(required_plugin) + except InvalidRequirement: + missing_plugins.append(required_plugin) + continue + + if req.name not in plugin_dist_info: + missing_plugins.append(required_plugin) + elif not req.specifier.contains( + Version(plugin_dist_info[req.name]), prereleases=True + ): + missing_plugins.append(required_plugin) + + if missing_plugins: + raise UsageError( + "Missing required plugins: {}".format(", ".join(missing_plugins)), + ) + + def _warn_or_fail_if_strict(self, message: str) -> None: + strict_config = self.getini("strict_config") + if strict_config is None: + strict_config = self.getini("strict") + if strict_config: + raise UsageError(message) + + self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) + + def _get_unknown_ini_keys(self) -> set[str]: + known_keys = self._parser._inidict.keys() | self._parser._ini_aliases.keys() + return self._inicfg.keys() - known_keys + + def parse(self, args: list[str], addopts: bool = True) -> None: + # Parse given cmdline arguments into this config object. + assert self.args == [], ( + "can only parse cmdline args at most once per Config object" + ) + + self.hook.pytest_addhooks.call_historic( + kwargs=dict(pluginmanager=self.pluginmanager) + ) + + if addopts: + env_addopts = os.environ.get("PYTEST_ADDOPTS", "") + if len(env_addopts): + args[:] = ( + self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + + args + ) + + ns = self._parser.parse_known_args(args, namespace=copy.copy(self.option)) + rootpath, inipath, inicfg, ignored_config_files = determine_setup( + inifile=ns.inifilename, + override_ini=ns.override_ini, + args=ns.file_or_dir, + rootdir_cmd_arg=ns.rootdir or None, + invocation_dir=self.invocation_params.dir, + ) + self._rootpath = rootpath + self._inipath = inipath + self._ignored_config_files = ignored_config_files + self._inicfg = inicfg + self._parser.extra_info["rootdir"] = str(self.rootpath) + self._parser.extra_info["inifile"] = str(self.inipath) + + self._parser.addini("addopts", "Extra command line options", "args") + self._parser.addini("minversion", "Minimally required pytest version") + self._parser.addini( + "pythonpath", type="paths", help="Add paths to sys.path", default=[] + ) + self._parser.addini( + "required_plugins", + "Plugins that must be present for pytest to run", + type="args", + default=[], + ) + + if addopts: + args[:] = ( + self._validate_args(self.getini("addopts"), "via addopts config") + args + ) + + self.known_args_namespace = self._parser.parse_known_args( + args, namespace=copy.copy(self.option) + ) + self._checkversion() + self._consider_importhook() + self._configure_python_path() + self.pluginmanager.consider_preparse(args, exclude_only=False) + if ( + not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD") + and not self.known_args_namespace.disable_plugin_autoload + ): + # Autoloading from distribution package entry point has + # not been disabled. + self.pluginmanager.load_setuptools_entrypoints("pytest11") + # Otherwise only plugins explicitly specified in PYTEST_PLUGINS + # are going to be loaded. + self.pluginmanager.consider_env() + + self._parser.parse_known_args(args, namespace=self.known_args_namespace) + + self._validate_plugins() + self._warn_about_skipped_plugins() + + if self.known_args_namespace.confcutdir is None: + if self.inipath is not None: + confcutdir = str(self.inipath.parent) + else: + confcutdir = str(self.rootpath) + self.known_args_namespace.confcutdir = confcutdir + try: + self.hook.pytest_load_initial_conftests( + early_config=self, args=args, parser=self._parser + ) + except ConftestImportFailure as e: + if self.known_args_namespace.help or self.known_args_namespace.version: + # we don't want to prevent --help/--version to work + # so just let it pass and print a warning at the end + self.issue_config_time_warning( + PytestConfigWarning(f"could not load initial conftests: {e.path}"), + stacklevel=2, + ) + else: + raise + + try: + self._parser.parse(args, namespace=self.option) + except PrintHelp: + return + + self.args, self.args_source = self._decide_args( + args=getattr(self.option, FILE_OR_DIR), + pyargs=self.option.pyargs, + testpaths=self.getini("testpaths"), + invocation_dir=self.invocation_params.dir, + rootpath=self.rootpath, + warn=True, + ) + + def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: + """Issue and handle a warning during the "configure" stage. + + During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` + function because it is not possible to have hook wrappers around ``pytest_configure``. + + This function is mainly intended for plugins that need to issue warnings during + ``pytest_configure`` (or similar stages). + + :param warning: The warning instance. + :param stacklevel: stacklevel forwarded to warnings.warn. + """ + if self.pluginmanager.is_blocked("warnings"): + return + + cmdline_filters = self.known_args_namespace.pythonwarnings or [] + config_filters = self.getini("filterwarnings") + + with warnings.catch_warnings(record=True) as records: + warnings.simplefilter("always", type(warning)) + apply_warning_filters(config_filters, cmdline_filters) + warnings.warn(warning, stacklevel=stacklevel) + + if records: + frame = sys._getframe(stacklevel - 1) + location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + self.hook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=records[0], + when="config", + nodeid="", + location=location, + ) + ) + + def addinivalue_line(self, name: str, line: str) -> None: + """Add a line to a configuration option. The option must have been + declared but might not yet be set in which case the line becomes + the first line in its value.""" + x = self.getini(name) + assert isinstance(x, list) + x.append(line) # modifies the cached list inline + + def getini(self, name: str) -> Any: + """Return configuration value the an :ref:`configuration file `. + + If a configuration value is not defined in a + :ref:`configuration file `, then the ``default`` value + provided while registering the configuration through + :func:`parser.addini ` will be returned. + Please note that you can even provide ``None`` as a valid + default value. + + If ``default`` is not provided while registering using + :func:`parser.addini `, then a default value + based on the ``type`` parameter passed to + :func:`parser.addini ` will be returned. + The default values based on ``type`` are: + ``paths``, ``pathlist``, ``args`` and ``linelist`` : empty list ``[]`` + ``bool`` : ``False`` + ``string`` : empty string ``""`` + ``int`` : ``0`` + ``float`` : ``0.0`` + + If neither the ``default`` nor the ``type`` parameter is passed + while registering the configuration through + :func:`parser.addini `, then the configuration + is treated as a string and a default empty string '' is returned. + + If the specified name hasn't been registered through a prior + :func:`parser.addini ` call (usually from a + plugin), a ValueError is raised. + """ + canonical_name = self._parser._ini_aliases.get(name, name) + try: + return self._inicache[canonical_name] + except KeyError: + pass + self._inicache[canonical_name] = val = self._getini(canonical_name) + return val + + # Meant for easy monkeypatching by legacypath plugin. + # Can be inlined back (with no cover removed) once legacypath is gone. + def _getini_unknown_type(self, name: str, type: str, value: object): + msg = ( + f"Option {name} has unknown configuration type {type} with value {value!r}" + ) + raise ValueError(msg) # pragma: no cover + + def _getini(self, name: str): + # If this is an alias, resolve to canonical name. + canonical_name = self._parser._ini_aliases.get(name, name) + + try: + _description, type, default = self._parser._inidict[canonical_name] + except KeyError as e: + raise ValueError(f"unknown configuration value: {name!r}") from e + + # Collect all possible values (canonical name + aliases) from _inicfg. + # Each candidate is (ConfigValue, is_canonical). + candidates = [] + if canonical_name in self._inicfg: + candidates.append((self._inicfg[canonical_name], True)) + for alias, target in self._parser._ini_aliases.items(): + if target == canonical_name and alias in self._inicfg: + candidates.append((self._inicfg[alias], False)) + + if not candidates: + return default + + # Pick the best candidate based on precedence: + # 1. CLI override takes precedence over file, then + # 2. Canonical name takes precedence over alias. + selected = max(candidates, key=lambda x: (x[0].origin == "override", x[1]))[0] + value = selected.value + mode = selected.mode + + if mode == "ini": + # In ini mode, values are always str | list[str]. + assert isinstance(value, (str, list)) + return self._getini_ini(name, canonical_name, type, value, default) + elif mode == "toml": + return self._getini_toml(name, canonical_name, type, value, default) + else: + assert_never(mode) + + def _getini_ini( + self, + name: str, + canonical_name: str, + type: str, + value: str | list[str], + default: Any, + ): + """Handle config values read in INI mode. + + In INI mode, values are stored as str or list[str] only, and coerced + from string based on the registered type. + """ + # Note: some coercions are only required if we are reading from .ini + # files, because the file format doesn't contain type information, but + # when reading from toml (in ini mode) we will get either str or list of + # str values (see load_config_dict_from_file). For example: + # + # ini: + # a_line_list = "tests acceptance" + # + # in this case, we need to split the string to obtain a list of strings. + # + # toml (ini mode): + # a_line_list = ["tests", "acceptance"] + # + # in this case, we already have a list ready to use. + if type == "paths": + dp = ( + self.inipath.parent + if self.inipath is not None + else self.invocation_params.dir + ) + input_values = shlex.split(value) if isinstance(value, str) else value + return [dp / x for x in input_values] + elif type == "args": + return shlex.split(value) if isinstance(value, str) else value + elif type == "linelist": + if isinstance(value, str): + return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] + else: + return value + elif type == "bool": + return _strtobool(str(value).strip()) + elif type == "string": + return value + elif type == "int": + if not isinstance(value, str): + raise TypeError( + f"Expected an int string for option {name} of type integer, but got: {value!r}" + ) from None + return int(value) + elif type == "float": + if not isinstance(value, str): + raise TypeError( + f"Expected a float string for option {name} of type float, but got: {value!r}" + ) from None + return float(value) + else: + return self._getini_unknown_type(name, type, value) + + def _getini_toml( + self, + name: str, + canonical_name: str, + type: str, + value: object, + default: Any, + ): + """Handle TOML config values with strict type validation and no coercion. + + In TOML mode, values already have native types from TOML parsing. + We validate types match expectations exactly, including list items. + """ + value_type = builtins.type(value).__name__ + if type == "paths": + # Expect a list of strings. + if not isinstance(value, list): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list for type 'paths', " + f"got {value_type}: {value!r}" + ) + for i, item in enumerate(value): + if not isinstance(item, str): + item_type = builtins.type(item).__name__ + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list of strings, " + f"but item at index {i} is {item_type}: {item!r}" + ) + dp = ( + self.inipath.parent + if self.inipath is not None + else self.invocation_params.dir + ) + return [dp / x for x in value] + elif type in {"args", "linelist"}: + # Expect a list of strings. + if not isinstance(value, list): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list for type '{type}', " + f"got {value_type}: {value!r}" + ) + for i, item in enumerate(value): + if not isinstance(item, str): + item_type = builtins.type(item).__name__ + raise TypeError( + f"{self.inipath}: config option '{name}' expects a list of strings, " + f"but item at index {i} is {item_type}: {item!r}" + ) + return list(value) + elif type == "bool": + # Expect a boolean. + if not isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a bool, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "int": + # Expect an integer (but not bool, which is a subclass of int). + if not isinstance(value, int) or isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects an int, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "float": + # Expect a float or integer only. + if not isinstance(value, (float, int)) or isinstance(value, bool): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a float, " + f"got {value_type}: {value!r}" + ) + return value + elif type == "string": + # Expect a string. + if not isinstance(value, str): + raise TypeError( + f"{self.inipath}: config option '{name}' expects a string, " + f"got {value_type}: {value!r}" + ) + return value + else: + return self._getini_unknown_type(name, type, value) + + def _getconftest_pathlist( + self, name: str, path: pathlib.Path + ) -> list[pathlib.Path] | None: + try: + mod, relroots = self.pluginmanager._rget_with_confmod(name, path) + except KeyError: + return None + assert mod.__file__ is not None + modpath = pathlib.Path(mod.__file__).parent + values: list[pathlib.Path] = [] + for relroot in relroots: + if isinstance(relroot, os.PathLike): + relroot = pathlib.Path(relroot) + else: + relroot = relroot.replace("/", os.sep) + relroot = absolutepath(modpath / relroot) + values.append(relroot) + return values + + def getoption(self, name: str, default: Any = notset, skip: bool = False): + """Return command line option value. + + :param name: Name of the option. You may also specify + the literal ``--OPT`` option instead of the "dest" option name. + :param default: Fallback value if no option of that name is **declared** via :hook:`pytest_addoption`. + Note this parameter will be ignored when the option is **declared** even if the option's value is ``None``. + :param skip: If ``True``, raise :func:`pytest.skip` if option is undeclared or has a ``None`` value. + Note that even if ``True``, if a default was specified it will be returned instead of a skip. + """ + name = self._opt2dest.get(name, name) + try: + val = getattr(self.option, name) + if val is None and skip: + raise AttributeError(name) + return val + except AttributeError as e: + if default is not notset: + return default + if skip: + import pytest + + pytest.skip(f"no {name!r} option found") + raise ValueError(f"no option named {name!r}") from e + + def getvalue(self, name: str, path=None): + """Deprecated, use getoption() instead.""" + return self.getoption(name) + + def getvalueorskip(self, name: str, path=None): + """Deprecated, use getoption(skip=True) instead.""" + return self.getoption(name, skip=True) + + #: Verbosity type for failed assertions (see :confval:`verbosity_assertions`). + VERBOSITY_ASSERTIONS: Final = "assertions" + #: Verbosity type for test case execution (see :confval:`verbosity_test_cases`). + VERBOSITY_TEST_CASES: Final = "test_cases" + #: Verbosity type for failed subtests (see :confval:`verbosity_subtests`). + VERBOSITY_SUBTESTS: Final = "subtests" + + _VERBOSITY_INI_DEFAULT: Final = "auto" + + def get_verbosity(self, verbosity_type: str | None = None) -> int: + r"""Retrieve the verbosity level for a fine-grained verbosity type. + + :param verbosity_type: Verbosity type to get level for. If a level is + configured for the given type, that value will be returned. If the + given type is not a known verbosity type, the global verbosity + level will be returned. If the given type is None (default), the + global verbosity level will be returned. + + To configure a level for a fine-grained verbosity type, the + configuration file should have a setting for the configuration name + and a numeric value for the verbosity level. A special value of "auto" + can be used to explicitly use the global verbosity level. + + Example: + + .. tab:: toml + + .. code-block:: toml + + [tool.pytest] + verbosity_assertions = 2 + + .. tab:: ini + + .. code-block:: ini + + [pytest] + verbosity_assertions = 2 + + .. code-block:: console + + pytest -v + + .. code-block:: python + + print(config.get_verbosity()) # 1 + print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 + """ + global_level = self.getoption("verbose", default=0) + assert isinstance(global_level, int) + if verbosity_type is None: + return global_level + + ini_name = Config._verbosity_ini_name(verbosity_type) + if ini_name not in self._parser._inidict: + return global_level + + level = self.getini(ini_name) + if level == Config._VERBOSITY_INI_DEFAULT: + return global_level + + return int(level) + + @staticmethod + def _verbosity_ini_name(verbosity_type: str) -> str: + return f"verbosity_{verbosity_type}" + + @staticmethod + def _add_verbosity_ini(parser: Parser, verbosity_type: str, help: str) -> None: + """Add a output verbosity configuration option for the given output type. + + :param parser: Parser for command line arguments and config-file values. + :param verbosity_type: Fine-grained verbosity category. + :param help: Description of the output this type controls. + + The value should be retrieved via a call to + :py:func:`config.get_verbosity(type) `. + """ + parser.addini( + Config._verbosity_ini_name(verbosity_type), + help=help, + type="string", + default=Config._VERBOSITY_INI_DEFAULT, + ) + + def _warn_about_missing_assertion(self, mode: str) -> None: + if not _assertion_supported(): + if mode == "plain": + warning_text = ( + "ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?" + ) + else: + warning_text = ( + "assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n" + ) + self.issue_config_time_warning( + PytestConfigWarning(warning_text), + stacklevel=3, + ) + + def _warn_about_skipped_plugins(self) -> None: + for module_name, msg in self.pluginmanager.skipped_plugins: + self.issue_config_time_warning( + PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), + stacklevel=2, + ) + + +def _assertion_supported() -> bool: + try: + assert False + except AssertionError: + return True + else: + return False # type: ignore[unreachable] + + +def create_terminal_writer( + config: Config, file: TextIO | None = None +) -> TerminalWriter: + """Create a TerminalWriter instance configured according to the options + in the config object. + + Every code which requires a TerminalWriter object and has access to a + config object should use this function. + """ + tw = TerminalWriter(file=file) + + if config.option.color == "yes": + tw.hasmarkup = True + elif config.option.color == "no": + tw.hasmarkup = False + + if config.option.code_highlight == "yes": + tw.code_highlight = True + elif config.option.code_highlight == "no": + tw.code_highlight = False + + return tw + + +def _strtobool(val: str) -> bool: + """Convert a string representation of truth to True or False. + + 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. + + .. note:: Copied from distutils.util. + """ + val = val.lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return True + elif val in ("n", "no", "f", "false", "off", "0"): + return False + else: + raise ValueError(f"invalid truth value {val!r}") + + +@lru_cache(maxsize=50) +def parse_warning_filter( + arg: str, *, escape: bool +) -> tuple[warnings._ActionKind, str, type[Warning], str, int]: + """Parse a warnings filter string. + + This is copied from warnings._setoption with the following changes: + + * Does not apply the filter. + * Escaping is optional. + * Raises UsageError so we get nice error messages on failure. + """ + __tracebackhide__ = True + error_template = dedent( + f"""\ + while parsing the following warning configuration: + + {arg} + + This error occurred: + + {{error}} + """ + ) + + parts = arg.split(":") + if len(parts) > 5: + doc_url = ( + "https://docs.python.org/3/library/warnings.html#describing-warning-filters" + ) + error = dedent( + f"""\ + Too many fields ({len(parts)}), expected at most 5 separated by colons: + + action:message:category:module:line + + For more information please consult: {doc_url} + """ + ) + raise UsageError(error_template.format(error=error)) + + while len(parts) < 5: + parts.append("") + action_, message, category_, module, lineno_ = (s.strip() for s in parts) + try: + action: warnings._ActionKind = warnings._getaction(action_) # type: ignore[attr-defined] + except warnings._OptionError as e: + raise UsageError(error_template.format(error=str(e))) from None + try: + category: type[Warning] = _resolve_warning_category(category_) + except ImportError: + raise + except Exception: + exc_info = ExceptionInfo.from_current() + exception_text = exc_info.getrepr(style="native") + raise UsageError(error_template.format(error=exception_text)) from None + if message and escape: + message = re.escape(message) + if module and escape: + module = re.escape(module) + r"\Z" + if lineno_: + try: + lineno = int(lineno_) + if lineno < 0: + raise ValueError("number is negative") + except ValueError as e: + raise UsageError( + error_template.format(error=f"invalid lineno {lineno_!r}: {e}") + ) from None + else: + lineno = 0 + try: + re.compile(message) + re.compile(module) + except re.error as e: + raise UsageError( + error_template.format(error=f"Invalid regex {e.pattern!r}: {e}") + ) from None + return action, message, category, module, lineno + + +def _resolve_warning_category(category: str) -> type[Warning]: + """ + Copied from warnings._getcategory, but changed so it lets exceptions (specially ImportErrors) + propagate so we can get access to their tracebacks (#9218). + """ + __tracebackhide__ = True + if not category: + return Warning + + if "." not in category: + import builtins as m + + klass = category + else: + module, _, klass = category.rpartition(".") + m = __import__(module, None, None, [klass]) + cat = getattr(m, klass) + if not issubclass(cat, Warning): + raise UsageError(f"{cat} is not a Warning subclass") + return cast(type[Warning], cat) + + +def apply_warning_filters( + config_filters: Iterable[str], cmdline_filters: Iterable[str] +) -> None: + """Applies pytest-configured filters to the warnings module""" + # Filters should have this precedence: cmdline options, config. + # Filters should be applied in the inverse order of precedence. + for arg in config_filters: + try: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + except ImportError as e: + warnings.warn( + f"Failed to import filter module '{e.name}': {arg}", PytestConfigWarning + ) + continue + + for arg in cmdline_filters: + try: + warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) + except ImportError as e: + warnings.warn( + f"Failed to import filter module '{e.name}': {arg}", PytestConfigWarning + ) + continue diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py b/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py new file mode 100644 index 0000000..8216ad8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py @@ -0,0 +1,578 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Mapping +from collections.abc import Sequence +import os +import sys +from typing import Any +from typing import final +from typing import Literal +from typing import NoReturn + +from .exceptions import UsageError +import _pytest._io +from _pytest.deprecated import check_ispytest + + +FILE_OR_DIR = "file_or_dir" + + +class NotSet: + def __repr__(self) -> str: + return "" + + +NOT_SET = NotSet() + + +@final +class Parser: + """Parser for command line arguments and config-file values. + + :ivar extra_info: Dict of generic param -> value to display in case + there's an error processing the command line arguments. + """ + + def __init__( + self, + usage: str | None = None, + processopt: Callable[[Argument], None] | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + + from _pytest._argcomplete import filescompleter + + self._processopt = processopt + self.extra_info: dict[str, Any] = {} + self.optparser = PytestArgumentParser(self, usage, self.extra_info) + anonymous_arggroup = self.optparser.add_argument_group("Custom options") + self._anonymous = OptionGroup( + anonymous_arggroup, "_anonymous", self, _ispytest=True + ) + self._groups = [self._anonymous] + file_or_dir_arg = self.optparser.add_argument(FILE_OR_DIR, nargs="*") + file_or_dir_arg.completer = filescompleter # type: ignore + + self._inidict: dict[str, tuple[str, str, Any]] = {} + # Maps alias -> canonical name. + self._ini_aliases: dict[str, str] = {} + + @property + def prog(self) -> str: + return self.optparser.prog + + @prog.setter + def prog(self, value: str) -> None: + self.optparser.prog = value + + def processoption(self, option: Argument) -> None: + if self._processopt: + if option.dest: + self._processopt(option) + + def getgroup( + self, name: str, description: str = "", after: str | None = None + ) -> OptionGroup: + """Get (or create) a named option Group. + + :param name: Name of the option group. + :param description: Long description for --help output. + :param after: Name of another group, used for ordering --help output. + :returns: The option group. + + The returned group object has an ``addoption`` method with the same + signature as :func:`parser.addoption ` but + will be shown in the respective group in the output of + ``pytest --help``. + """ + for group in self._groups: + if group.name == name: + return group + + arggroup = self.optparser.add_argument_group(description or name) + group = OptionGroup(arggroup, name, self, _ispytest=True) + i = 0 + for i, grp in enumerate(self._groups): + if grp.name == after: + break + self._groups.insert(i + 1, group) + # argparse doesn't provide a way to control `--help` order, so must + # access its internals ☹. + self.optparser._action_groups.insert(i + 1, self.optparser._action_groups.pop()) + return group + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Register a command line option. + + :param opts: + Option names, can be short or long options. + :param attrs: + Same attributes as the argparse library's :meth:`add_argument() + ` function accepts. + + After command line parsing, options are available on the pytest config + object via ``config.option.NAME`` where ``NAME`` is usually set + by passing a ``dest`` attribute, for example + ``addoption("--long", dest="NAME", ...)``. + """ + self._anonymous.addoption(*opts, **attrs) + + def parse( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> argparse.Namespace: + """Parse the arguments. + + Unlike ``parse_known_args`` and ``parse_known_and_unknown_args``, + raises PrintHelp on `--help` and UsageError on unknown flags + + :meta private: + """ + from _pytest._argcomplete import try_argcomplete + + try_argcomplete(self.optparser) + strargs = [os.fspath(x) for x in args] + if namespace is None: + namespace = argparse.Namespace() + try: + namespace._raise_print_help = True + return self.optparser.parse_intermixed_args(strargs, namespace=namespace) + finally: + del namespace._raise_print_help + + def parse_known_args( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> argparse.Namespace: + """Parse the known arguments at this point. + + :returns: An argparse namespace object. + """ + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] + + def parse_known_and_unknown_args( + self, + args: Sequence[str | os.PathLike[str]], + namespace: argparse.Namespace | None = None, + ) -> tuple[argparse.Namespace, list[str]]: + """Parse the known arguments at this point, and also return the + remaining unknown flag arguments. + + :returns: + A tuple containing an argparse namespace object for the known + arguments, and a list of unknown flag arguments. + """ + strargs = [os.fspath(x) for x in args] + if sys.version_info < (3, 12, 8) or (3, 13) <= sys.version_info < (3, 13, 1): + # Older argparse have a bugged parse_known_intermixed_args. + namespace, unknown = self.optparser.parse_known_args(strargs, namespace) + assert namespace is not None + file_or_dir = getattr(namespace, FILE_OR_DIR) + unknown_flags: list[str] = [] + for arg in unknown: + (unknown_flags if arg.startswith("-") else file_or_dir).append(arg) + return namespace, unknown_flags + else: + return self.optparser.parse_known_intermixed_args(strargs, namespace) + + def addini( + self, + name: str, + help: str, + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ] + | None = None, + default: Any = NOT_SET, + *, + aliases: Sequence[str] = (), + ) -> None: + """Register a configuration file option. + + :param name: + Name of the configuration. + :param type: + Type of the configuration. Can be: + + * ``string``: a string + * ``bool``: a boolean + * ``args``: a list of strings, separated as in a shell + * ``linelist``: a list of strings, separated by line breaks + * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell + * ``pathlist``: a list of ``py.path``, separated as in a shell + * ``int``: an integer + * ``float``: a floating-point number + + .. versionadded:: 8.4 + + The ``float`` and ``int`` types. + + For ``paths`` and ``pathlist`` types, they are considered relative to the config-file. + In case the execution is happening without a config-file defined, + they will be considered relative to the current working directory (for example with ``--override-ini``). + + .. versionadded:: 7.0 + The ``paths`` variable type. + + .. versionadded:: 8.1 + Use the current working directory to resolve ``paths`` and ``pathlist`` in the absence of a config-file. + + Defaults to ``string`` if ``None`` or not passed. + :param default: + Default value if no config-file option exists but is queried. + :param aliases: + Additional names by which this option can be referenced. + Aliases resolve to the canonical name. + + .. versionadded:: 9.0 + The ``aliases`` parameter. + + The value of configuration keys can be retrieved via a call to + :py:func:`config.getini(name) `. + """ + assert type in ( + None, + "string", + "paths", + "pathlist", + "args", + "linelist", + "bool", + "int", + "float", + ) + if type is None: + type = "string" + if default is NOT_SET: + default = get_ini_default_for_type(type) + + self._inidict[name] = (help, type, default) + + for alias in aliases: + if alias in self._inidict: + raise ValueError( + f"alias {alias!r} conflicts with existing configuration option" + ) + if (already := self._ini_aliases.get(alias)) is not None: + raise ValueError(f"{alias!r} is already an alias of {already!r}") + self._ini_aliases[alias] = name + + +def get_ini_default_for_type( + type: Literal[ + "string", "paths", "pathlist", "args", "linelist", "bool", "int", "float" + ], +) -> Any: + """ + Used by addini to get the default value for a given config option type, when + default is not supplied. + """ + if type in ("paths", "pathlist", "args", "linelist"): + return [] + elif type == "bool": + return False + elif type == "int": + return 0 + elif type == "float": + return 0.0 + else: + return "" + + +class ArgumentError(Exception): + """Raised if an Argument instance is created with invalid or + inconsistent arguments.""" + + def __init__(self, msg: str, option: Argument | str) -> None: + self.msg = msg + self.option_id = str(option) + + def __str__(self) -> str: + if self.option_id: + return f"option {self.option_id}: {self.msg}" + else: + return self.msg + + +class Argument: + """Class that mimics the necessary behaviour of optparse.Option. + + It's currently a least effort implementation and ignoring choices + and integer prefixes. + + https://docs.python.org/3/library/optparse.html#optparse-standard-option-types + """ + + def __init__(self, *names: str, **attrs: Any) -> None: + """Store params in private vars for use in add_argument.""" + self._attrs = attrs + self._short_opts: list[str] = [] + self._long_opts: list[str] = [] + try: + self.type = attrs["type"] + except KeyError: + pass + try: + # Attribute existence is tested in Config._processopt. + self.default = attrs["default"] + except KeyError: + pass + self._set_opt_strings(names) + dest: str | None = attrs.get("dest") + if dest: + self.dest = dest + elif self._long_opts: + self.dest = self._long_opts[0][2:].replace("-", "_") + else: + try: + self.dest = self._short_opts[0][1:] + except IndexError as e: + self.dest = "???" # Needed for the error repr. + raise ArgumentError("need a long or short option", self) from e + + def names(self) -> list[str]: + return self._short_opts + self._long_opts + + def attrs(self) -> Mapping[str, Any]: + # Update any attributes set by processopt. + for attr in ("default", "dest", "help", self.dest): + try: + self._attrs[attr] = getattr(self, attr) + except AttributeError: + pass + return self._attrs + + def _set_opt_strings(self, opts: Sequence[str]) -> None: + """Directly from optparse. + + Might not be necessary as this is passed to argparse later on. + """ + for opt in opts: + if len(opt) < 2: + raise ArgumentError( + f"invalid option string {opt!r}: " + "must be at least two characters long", + self, + ) + elif len(opt) == 2: + if not (opt[0] == "-" and opt[1] != "-"): + raise ArgumentError( + f"invalid short option string {opt!r}: " + "must be of the form -x, (x any non-dash char)", + self, + ) + self._short_opts.append(opt) + else: + if not (opt[0:2] == "--" and opt[2] != "-"): + raise ArgumentError( + f"invalid long option string {opt!r}: " + "must start with --, followed by non-dash", + self, + ) + self._long_opts.append(opt) + + def __repr__(self) -> str: + args: list[str] = [] + if self._short_opts: + args += ["_short_opts: " + repr(self._short_opts)] + if self._long_opts: + args += ["_long_opts: " + repr(self._long_opts)] + args += ["dest: " + repr(self.dest)] + if hasattr(self, "type"): + args += ["type: " + repr(self.type)] + if hasattr(self, "default"): + args += ["default: " + repr(self.default)] + return "Argument({})".format(", ".join(args)) + + +class OptionGroup: + """A group of options shown in its own section.""" + + def __init__( + self, + arggroup: argparse._ArgumentGroup, + name: str, + parser: Parser | None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._arggroup = arggroup + self.name = name + self.options: list[Argument] = [] + self.parser = parser + + def addoption(self, *opts: str, **attrs: Any) -> None: + """Add an option to this group. + + If a shortened version of a long option is specified, it will + be suppressed in the help. ``addoption('--twowords', '--two-words')`` + results in help showing ``--two-words`` only, but ``--twowords`` gets + accepted **and** the automatic destination is in ``args.twowords``. + + :param opts: + Option names, can be short or long options. + :param attrs: + Same attributes as the argparse library's :meth:`add_argument() + ` function accepts. + """ + conflict = set(opts).intersection( + name for opt in self.options for name in opt.names() + ) + if conflict: + raise ValueError(f"option names {conflict} already added") + option = Argument(*opts, **attrs) + self._addoption_instance(option, shortupper=False) + + def _addoption(self, *opts: str, **attrs: Any) -> None: + option = Argument(*opts, **attrs) + self._addoption_instance(option, shortupper=True) + + def _addoption_instance(self, option: Argument, shortupper: bool = False) -> None: + if not shortupper: + for opt in option._short_opts: + if opt[0] == "-" and opt[1].islower(): + raise ValueError("lowercase shortoptions reserved") + + if self.parser: + self.parser.processoption(option) + + self._arggroup.add_argument(*option.names(), **option.attrs()) + self.options.append(option) + + +class PytestArgumentParser(argparse.ArgumentParser): + def __init__( + self, + parser: Parser, + usage: str | None, + extra_info: dict[str, str], + ) -> None: + self._parser = parser + super().__init__( + usage=usage, + add_help=False, + formatter_class=DropShorterLongHelpFormatter, + allow_abbrev=False, + fromfile_prefix_chars="@", + ) + # extra_info is a dict of (param -> value) to display if there's + # an usage error to provide more contextual information to the user. + self.extra_info = extra_info + + def error(self, message: str) -> NoReturn: + """Transform argparse error message into UsageError.""" + msg = f"{self.prog}: error: {message}" + if self.extra_info: + msg += "\n" + "\n".join( + f" {k}: {v}" for k, v in sorted(self.extra_info.items()) + ) + raise UsageError(self.format_usage() + msg) + + +class DropShorterLongHelpFormatter(argparse.HelpFormatter): + """Shorten help for long options that differ only in extra hyphens. + + - Collapse **long** options that are the same except for extra hyphens. + - Shortcut if there are only two options and one of them is a short one. + - Cache result on the action object as this is called at least 2 times. + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # Use more accurate terminal width. + if "width" not in kwargs: + kwargs["width"] = _pytest._io.get_terminal_width() + super().__init__(*args, **kwargs) + + def _format_action_invocation(self, action: argparse.Action) -> str: + orgstr = super()._format_action_invocation(action) + if orgstr and orgstr[0] != "-": # only optional arguments + return orgstr + res: str | None = getattr(action, "_formatted_action_invocation", None) + if res: + return res + options = orgstr.split(", ") + if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): + # a shortcut for '-h, --help' or '--abc', '-a' + action._formatted_action_invocation = orgstr # type: ignore + return orgstr + return_list = [] + short_long: dict[str, str] = {} + for option in options: + if len(option) == 2 or option[2] == " ": + continue + if not option.startswith("--"): + raise ArgumentError( + f'long optional argument without "--": [{option}]', option + ) + xxoption = option[2:] + shortened = xxoption.replace("-", "") + if shortened not in short_long or len(short_long[shortened]) < len( + xxoption + ): + short_long[shortened] = xxoption + # now short_long has been filled out to the longest with dashes + # **and** we keep the right option ordering from add_argument + for option in options: + if len(option) == 2 or option[2] == " ": + return_list.append(option) + if option[2:] == short_long.get(option.replace("-", "")): + return_list.append(option.replace(" ", "=", 1)) + formatted_action_invocation = ", ".join(return_list) + action._formatted_action_invocation = formatted_action_invocation # type: ignore + return formatted_action_invocation + + def _split_lines(self, text, width): + """Wrap lines after splitting on original newlines. + + This allows to have explicit line breaks in the help text. + """ + import textwrap + + lines = [] + for line in text.splitlines(): + lines.extend(textwrap.wrap(line.strip(), width)) + return lines + + +class OverrideIniAction(argparse.Action): + """Custom argparse action that makes a CLI flag equivalent to overriding an + option, in addition to behaving like `store_true`. + + This can simplify things since code only needs to inspect the config option + and not consider the CLI flag. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + nargs: int | str | None = None, + *args, + ini_option: str, + ini_value: str, + **kwargs, + ) -> None: + super().__init__(option_strings, dest, 0, *args, **kwargs) + self.ini_option = ini_option + self.ini_value = ini_value + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + *args, + **kwargs, + ) -> None: + setattr(namespace, self.dest, True) + current_overrides = getattr(namespace, "override_ini", None) + if current_overrides is None: + current_overrides = [] + current_overrides.append(f"{self.ini_option}={self.ini_value}") + setattr(namespace, "override_ini", current_overrides) diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/compat.py b/.venv/lib/python3.12/site-packages/_pytest/config/compat.py new file mode 100644 index 0000000..21eab4c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/config/compat.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +from collections.abc import Mapping +import functools +from pathlib import Path +from typing import Any +import warnings + +import pluggy + +from ..compat import LEGACY_PATH +from ..compat import legacy_path +from ..deprecated import HOOK_LEGACY_PATH_ARG + + +# hookname: (Path, LEGACY_PATH) +imply_paths_hooks: Mapping[str, tuple[str, str]] = { + "pytest_ignore_collect": ("collection_path", "path"), + "pytest_collect_file": ("file_path", "path"), + "pytest_pycollect_makemodule": ("module_path", "path"), + "pytest_report_header": ("start_path", "startdir"), + "pytest_report_collectionfinish": ("start_path", "startdir"), +} + + +def _check_path(path: Path, fspath: LEGACY_PATH) -> None: + if Path(fspath) != path: + raise ValueError( + f"Path({fspath!r}) != {path!r}\n" + "if both path and fspath are given they need to be equal" + ) + + +class PathAwareHookProxy: + """ + this helper wraps around hook callers + until pluggy supports fixingcalls, this one will do + + it currently doesn't return full hook caller proxies for fixed hooks, + this may have to be changed later depending on bugs + """ + + def __init__(self, hook_relay: pluggy.HookRelay) -> None: + self._hook_relay = hook_relay + + def __dir__(self) -> list[str]: + return dir(self._hook_relay) + + def __getattr__(self, key: str) -> pluggy.HookCaller: + hook: pluggy.HookCaller = getattr(self._hook_relay, key) + if key not in imply_paths_hooks: + self.__dict__[key] = hook + return hook + else: + path_var, fspath_var = imply_paths_hooks[key] + + @functools.wraps(hook) + def fixed_hook(**kw: Any) -> Any: + path_value: Path | None = kw.pop(path_var, None) + fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None) + if fspath_value is not None: + warnings.warn( + HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg=fspath_var, pathlib_path_arg=path_var + ), + stacklevel=2, + ) + if path_value is not None: + if fspath_value is not None: + _check_path(path_value, fspath_value) + else: + fspath_value = legacy_path(path_value) + else: + assert fspath_value is not None + path_value = Path(fspath_value) + + kw[path_var] = path_value + kw[fspath_var] = fspath_value + return hook(**kw) + + fixed_hook.name = hook.name # type: ignore[attr-defined] + fixed_hook.spec = hook.spec # type: ignore[attr-defined] + fixed_hook.__name__ = key + self.__dict__[key] = fixed_hook + return fixed_hook # type: ignore[return-value] diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py b/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py new file mode 100644 index 0000000..d84a9ea --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from typing import final + + +@final +class UsageError(Exception): + """Error in pytest usage or invocation.""" + + __module__ = "pytest" + + +class PrintHelp(Exception): + """Raised when pytest should print its help to skip the rest of the + argument parsing and validation.""" diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py b/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py new file mode 100644 index 0000000..3c628a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py @@ -0,0 +1,350 @@ +from __future__ import annotations + +from collections.abc import Iterable +from collections.abc import Sequence +from dataclasses import dataclass +from dataclasses import KW_ONLY +import os +from pathlib import Path +import sys +from typing import Literal +from typing import TypeAlias + +import iniconfig + +from .exceptions import UsageError +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.pathlib import commonpath +from _pytest.pathlib import safe_exists + + +@dataclass(frozen=True) +class ConfigValue: + """Represents a configuration value with its origin and parsing mode. + + This allows tracking whether a value came from a configuration file + or from a CLI override (--override-ini), which is important for + determining precedence when dealing with ini option aliases. + + The mode tracks the parsing mode/data model used for the value: + - "ini": from INI files or [tool.pytest.ini_options], where the only + supported value types are `str` or `list[str]`. + - "toml": from TOML files (not in INI mode), where native TOML types + are preserved. + """ + + value: object + _: KW_ONLY + origin: Literal["file", "override"] + mode: Literal["ini", "toml"] + + +ConfigDict: TypeAlias = dict[str, ConfigValue] + + +def _parse_ini_config(path: Path) -> iniconfig.IniConfig: + """Parse the given generic '.ini' file using legacy IniConfig parser, returning + the parsed object. + + Raise UsageError if the file cannot be parsed. + """ + try: + return iniconfig.IniConfig(str(path)) + except iniconfig.ParseError as exc: + raise UsageError(str(exc)) from exc + + +def load_config_dict_from_file( + filepath: Path, +) -> ConfigDict | None: + """Load pytest configuration from the given file path, if supported. + + Return None if the file does not contain valid pytest configuration. + """ + # Configuration from ini files are obtained from the [pytest] section, if present. + if filepath.suffix == ".ini": + iniconfig = _parse_ini_config(filepath) + + if "pytest" in iniconfig: + return { + k: ConfigValue(v, origin="file", mode="ini") + for k, v in iniconfig["pytest"].items() + } + else: + # "pytest.ini" files are always the source of configuration, even if empty. + if filepath.name in {"pytest.ini", ".pytest.ini"}: + return {} + + # '.cfg' files are considered if they contain a "[tool:pytest]" section. + elif filepath.suffix == ".cfg": + iniconfig = _parse_ini_config(filepath) + + if "tool:pytest" in iniconfig.sections: + return { + k: ConfigValue(v, origin="file", mode="ini") + for k, v in iniconfig["tool:pytest"].items() + } + elif "pytest" in iniconfig.sections: + # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that + # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). + fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) + + # '.toml' files are considered if they contain a [tool.pytest] table (toml mode) + # or [tool.pytest.ini_options] table (ini mode) for pyproject.toml, + # or [pytest] table (toml mode) for pytest.toml/.pytest.toml. + elif filepath.suffix == ".toml": + if sys.version_info >= (3, 11): + import tomllib + else: + import tomli as tomllib + + toml_text = filepath.read_text(encoding="utf-8") + try: + config = tomllib.loads(toml_text) + except tomllib.TOMLDecodeError as exc: + raise UsageError(f"{filepath}: {exc}") from exc + + # pytest.toml and .pytest.toml use [pytest] table directly. + if filepath.name in ("pytest.toml", ".pytest.toml"): + pytest_config = config.get("pytest", {}) + if pytest_config: + # TOML mode - preserve native TOML types. + return { + k: ConfigValue(v, origin="file", mode="toml") + for k, v in pytest_config.items() + } + # "pytest.toml" files are always the source of configuration, even if empty. + return {} + + # pyproject.toml uses [tool.pytest] or [tool.pytest.ini_options]. + else: + tool_pytest = config.get("tool", {}).get("pytest", {}) + + # Check for toml mode config: [tool.pytest] with content outside of ini_options. + toml_config = {k: v for k, v in tool_pytest.items() if k != "ini_options"} + # Check for ini mode config: [tool.pytest.ini_options]. + ini_config = tool_pytest.get("ini_options", None) + + if toml_config and ini_config: + raise UsageError( + f"{filepath}: Cannot use both [tool.pytest] (native TOML types) and " + "[tool.pytest.ini_options] (string-based INI format) simultaneously. " + "Please use [tool.pytest] with native TOML types (recommended) " + "or [tool.pytest.ini_options] for backwards compatibility." + ) + + if toml_config: + # TOML mode - preserve native TOML types. + return { + k: ConfigValue(v, origin="file", mode="toml") + for k, v in toml_config.items() + } + + elif ini_config is not None: + # INI mode - TOML supports richer data types than INI files, but we need to + # convert all scalar values to str for compatibility with the INI system. + def make_scalar(v: object) -> str | list[str]: + return v if isinstance(v, list) else str(v) + + return { + k: ConfigValue(make_scalar(v), origin="file", mode="ini") + for k, v in ini_config.items() + } + + return None + + +def locate_config( + invocation_dir: Path, + args: Iterable[Path], +) -> tuple[Path | None, Path | None, ConfigDict, Sequence[str]]: + """Search in the list of arguments for a valid ini-file for pytest, + and return a tuple of (rootdir, inifile, cfg-dict, ignored-config-files), where + ignored-config-files is a list of config basenames found that contain + pytest configuration but were ignored.""" + config_names = [ + "pytest.toml", + ".pytest.toml", + "pytest.ini", + ".pytest.ini", + "pyproject.toml", + "tox.ini", + "setup.cfg", + ] + args = [x for x in args if not str(x).startswith("-")] + if not args: + args = [invocation_dir] + found_pyproject_toml: Path | None = None + ignored_config_files: list[str] = [] + + for arg in args: + argpath = absolutepath(arg) + for base in (argpath, *argpath.parents): + for config_name in config_names: + p = base / config_name + if p.is_file(): + if p.name == "pyproject.toml" and found_pyproject_toml is None: + found_pyproject_toml = p + ini_config = load_config_dict_from_file(p) + if ini_config is not None: + index = config_names.index(config_name) + for remainder in config_names[index + 1 :]: + p2 = base / remainder + if ( + p2.is_file() + and load_config_dict_from_file(p2) is not None + ): + ignored_config_files.append(remainder) + return base, p, ini_config, ignored_config_files + if found_pyproject_toml is not None: + return found_pyproject_toml.parent, found_pyproject_toml, {}, [] + return None, None, {}, [] + + +def get_common_ancestor( + invocation_dir: Path, + paths: Iterable[Path], +) -> Path: + common_ancestor: Path | None = None + for path in paths: + if not path.exists(): + continue + if common_ancestor is None: + common_ancestor = path + else: + if common_ancestor in path.parents or path == common_ancestor: + continue + elif path in common_ancestor.parents: + common_ancestor = path + else: + shared = commonpath(path, common_ancestor) + if shared is not None: + common_ancestor = shared + if common_ancestor is None: + common_ancestor = invocation_dir + elif common_ancestor.is_file(): + common_ancestor = common_ancestor.parent + return common_ancestor + + +def get_dirs_from_args(args: Iterable[str]) -> list[Path]: + def is_option(x: str) -> bool: + return x.startswith("-") + + def get_file_part_from_node_id(x: str) -> str: + return x.split("::")[0] + + def get_dir_from_path(path: Path) -> Path: + if path.is_dir(): + return path + return path.parent + + # These look like paths but may not exist + possible_paths = ( + absolutepath(get_file_part_from_node_id(arg)) + for arg in args + if not is_option(arg) + ) + + return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] + + +def parse_override_ini(override_ini: Sequence[str] | None) -> ConfigDict: + """Parse the -o/--override-ini command line arguments and return the overrides. + + :raises UsageError: + If one of the values is malformed. + """ + overrides = {} + # override_ini is a list of "ini=value" options. + # Always use the last item if multiple values are set for same ini-name, + # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. + for ini_config in override_ini or (): + try: + key, user_ini_value = ini_config.split("=", 1) + except ValueError as e: + raise UsageError( + f"-o/--override-ini expects option=value style (got: {ini_config!r})." + ) from e + else: + overrides[key] = ConfigValue(user_ini_value, origin="override", mode="ini") + return overrides + + +CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." + + +def determine_setup( + *, + inifile: str | None, + override_ini: Sequence[str] | None, + args: Sequence[str], + rootdir_cmd_arg: str | None, + invocation_dir: Path, +) -> tuple[Path, Path | None, ConfigDict, Sequence[str]]: + """Determine the rootdir, inifile and ini configuration values from the + command line arguments. + + :param inifile: + The `--inifile` command line argument, if given. + :param override_ini: + The -o/--override-ini command line arguments, if given. + :param args: + The free command line arguments. + :param rootdir_cmd_arg: + The `--rootdir` command line argument, if given. + :param invocation_dir: + The working directory when pytest was invoked. + + :raises UsageError: + """ + rootdir = None + dirs = get_dirs_from_args(args) + ignored_config_files: Sequence[str] = [] + + if inifile: + inipath_ = absolutepath(inifile) + inipath: Path | None = inipath_ + inicfg = load_config_dict_from_file(inipath_) or {} + if rootdir_cmd_arg is None: + rootdir = inipath_.parent + else: + ancestor = get_common_ancestor(invocation_dir, dirs) + rootdir, inipath, inicfg, ignored_config_files = locate_config( + invocation_dir, [ancestor] + ) + if rootdir is None and rootdir_cmd_arg is None: + for possible_rootdir in (ancestor, *ancestor.parents): + if (possible_rootdir / "setup.py").is_file(): + rootdir = possible_rootdir + break + else: + if dirs != [ancestor]: + rootdir, inipath, inicfg, _ = locate_config(invocation_dir, dirs) + if rootdir is None: + rootdir = get_common_ancestor( + invocation_dir, [invocation_dir, ancestor] + ) + if is_fs_root(rootdir): + rootdir = ancestor + if rootdir_cmd_arg: + rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) + if not rootdir.is_dir(): + raise UsageError( + f"Directory '{rootdir}' not found. Check your '--rootdir' option." + ) + + ini_overrides = parse_override_ini(override_ini) + inicfg.update(ini_overrides) + + assert rootdir is not None + return rootdir, inipath, inicfg, ignored_config_files + + +def is_fs_root(p: Path) -> bool: + r""" + Return True if the given path is pointing to the root of the + file system ("/" on Unix and "C:\\" on Windows for example). + """ + return os.path.splitdrive(str(p))[1] == os.sep diff --git a/.venv/lib/python3.12/site-packages/_pytest/debugging.py b/.venv/lib/python3.12/site-packages/_pytest/debugging.py new file mode 100644 index 0000000..de1b268 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/debugging.py @@ -0,0 +1,407 @@ +# mypy: allow-untyped-defs +# ruff: noqa: T100 +"""Interactive debugging with PDB, the Python Debugger.""" + +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Generator +import functools +import sys +import types +from typing import Any +import unittest + +from _pytest import outcomes +from _pytest._code import ExceptionInfo +from _pytest.capture import CaptureManager +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.config.exceptions import UsageError +from _pytest.nodes import Node +from _pytest.reports import BaseReport +from _pytest.runner import CallInfo + + +def _validate_usepdb_cls(value: str) -> tuple[str, str]: + """Validate syntax of --pdbcls option.""" + try: + modname, classname = value.split(":") + except ValueError as e: + raise argparse.ArgumentTypeError( + f"{value!r} is not in the format 'modname:classname'" + ) from e + return (modname, classname) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--pdb", + dest="usepdb", + action="store_true", + help="Start the interactive Python debugger on errors or KeyboardInterrupt", + ) + group.addoption( + "--pdbcls", + dest="usepdb_cls", + metavar="modulename:classname", + type=_validate_usepdb_cls, + help="Specify a custom interactive Python debugger for use with --pdb." + "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", + ) + group.addoption( + "--trace", + dest="trace", + action="store_true", + help="Immediately break when running each test", + ) + + +def pytest_configure(config: Config) -> None: + import pdb + + if config.getvalue("trace"): + config.pluginmanager.register(PdbTrace(), "pdbtrace") + if config.getvalue("usepdb"): + config.pluginmanager.register(PdbInvoke(), "pdbinvoke") + + pytestPDB._saved.append( + (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) + ) + pdb.set_trace = pytestPDB.set_trace + pytestPDB._pluginmanager = config.pluginmanager + pytestPDB._config = config + + # NOTE: not using pytest_unconfigure, since it might get called although + # pytest_configure was not (if another plugin raises UsageError). + def fin() -> None: + ( + pdb.set_trace, + pytestPDB._pluginmanager, + pytestPDB._config, + ) = pytestPDB._saved.pop() + + config.add_cleanup(fin) + + +class pytestPDB: + """Pseudo PDB that defers to the real pdb.""" + + _pluginmanager: PytestPluginManager | None = None + _config: Config | None = None + _saved: list[ + tuple[Callable[..., None], PytestPluginManager | None, Config | None] + ] = [] + _recursive_debug = 0 + _wrapped_pdb_cls: tuple[type[Any], type[Any]] | None = None + + @classmethod + def _is_capturing(cls, capman: CaptureManager | None) -> str | bool: + if capman: + return capman.is_capturing() + return False + + @classmethod + def _import_pdb_cls(cls, capman: CaptureManager | None): + if not cls._config: + import pdb + + # Happens when using pytest.set_trace outside of a test. + return pdb.Pdb + + usepdb_cls = cls._config.getvalue("usepdb_cls") + + if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: + return cls._wrapped_pdb_cls[1] + + if usepdb_cls: + modname, classname = usepdb_cls + + try: + __import__(modname) + mod = sys.modules[modname] + + # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). + parts = classname.split(".") + pdb_cls = getattr(mod, parts[0]) + for part in parts[1:]: + pdb_cls = getattr(pdb_cls, part) + except Exception as exc: + value = ":".join((modname, classname)) + raise UsageError( + f"--pdbcls: could not import {value!r}: {exc}" + ) from exc + else: + import pdb + + pdb_cls = pdb.Pdb + + wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) + cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) + return wrapped_cls + + @classmethod + def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None): + import _pytest.config + + class PytestPdbWrapper(pdb_cls): + _pytest_capman = capman + _continued = False + + def do_debug(self, arg): + cls._recursive_debug += 1 + ret = super().do_debug(arg) + cls._recursive_debug -= 1 + return ret + + if hasattr(pdb_cls, "do_debug"): + do_debug.__doc__ = pdb_cls.do_debug.__doc__ + + def do_continue(self, arg): + ret = super().do_continue(arg) + if cls._recursive_debug == 0: + assert cls._config is not None + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + capman = self._pytest_capman + capturing = pytestPDB._is_capturing(capman) + if capturing: + if capturing == "global": + tw.sep(">", "PDB continue (IO-capturing resumed)") + else: + tw.sep( + ">", + f"PDB continue (IO-capturing resumed for {capturing})", + ) + assert capman is not None + capman.resume() + else: + tw.sep(">", "PDB continue") + assert cls._pluginmanager is not None + cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) + self._continued = True + return ret + + if hasattr(pdb_cls, "do_continue"): + do_continue.__doc__ = pdb_cls.do_continue.__doc__ + + do_c = do_cont = do_continue + + def do_quit(self, arg): + # Raise Exit outcome when quit command is used in pdb. + # + # This is a bit of a hack - it would be better if BdbQuit + # could be handled, but this would require to wrap the + # whole pytest run, and adjust the report etc. + ret = super().do_quit(arg) + + if cls._recursive_debug == 0: + outcomes.exit("Quitting debugger") + + return ret + + if hasattr(pdb_cls, "do_quit"): + do_quit.__doc__ = pdb_cls.do_quit.__doc__ + + do_q = do_quit + do_exit = do_quit + + def setup(self, f, tb): + """Suspend on setup(). + + Needed after do_continue resumed, and entering another + breakpoint again. + """ + ret = super().setup(f, tb) + if not ret and self._continued: + # pdb.setup() returns True if the command wants to exit + # from the interaction: do not suspend capturing then. + if self._pytest_capman: + self._pytest_capman.suspend_global_capture(in_=True) + return ret + + def get_stack(self, f, t): + stack, i = super().get_stack(f, t) + if f is None: + # Find last non-hidden frame. + i = max(0, len(stack) - 1) + while i and stack[i][0].f_locals.get("__tracebackhide__", False): + i -= 1 + return stack, i + + return PytestPdbWrapper + + @classmethod + def _init_pdb(cls, method, *args, **kwargs): + """Initialize PDB debugging, dropping any IO capturing.""" + import _pytest.config + + if cls._pluginmanager is None: + capman: CaptureManager | None = None + else: + capman = cls._pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend(in_=True) + + if cls._config: + tw = _pytest.config.create_terminal_writer(cls._config) + tw.line() + + if cls._recursive_debug == 0: + # Handle header similar to pdb.set_trace in py37+. + header = kwargs.pop("header", None) + if header is not None: + tw.sep(">", header) + else: + capturing = cls._is_capturing(capman) + if capturing == "global": + tw.sep(">", f"PDB {method} (IO-capturing turned off)") + elif capturing: + tw.sep( + ">", + f"PDB {method} (IO-capturing turned off for {capturing})", + ) + else: + tw.sep(">", f"PDB {method}") + + _pdb = cls._import_pdb_cls(capman)(**kwargs) + + if cls._pluginmanager: + cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) + return _pdb + + @classmethod + def set_trace(cls, *args, **kwargs) -> None: + """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" + frame = sys._getframe().f_back + _pdb = cls._init_pdb("set_trace", *args, **kwargs) + _pdb.set_trace(frame) + + +class PdbInvoke: + def pytest_exception_interact( + self, node: Node, call: CallInfo[Any], report: BaseReport + ) -> None: + capman = node.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stdout.write(err) + assert call.excinfo is not None + + if not isinstance(call.excinfo.value, unittest.SkipTest): + _enter_pdb(node, call.excinfo, report) + + def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: + exc_or_tb = _postmortem_exc_or_tb(excinfo) + post_mortem(exc_or_tb) + + +class PdbTrace: + @hookimpl(wrapper=True) + def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, object, object]: + wrap_pytest_function_for_tracing(pyfuncitem) + return (yield) + + +def wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Change the Python function object of the given Function item by a + wrapper which actually enters pdb before calling the python function + itself, effectively leaving the user in the pdb prompt in the first + statement of the function.""" + _pdb = pytestPDB._init_pdb("runcall") + testfunction = pyfuncitem.obj + + # we can't just return `partial(pdb.runcall, testfunction)` because (on + # python < 3.7.4) runcall's first param is `func`, which means we'd get + # an exception if one of the kwargs to testfunction was called `func`. + @functools.wraps(testfunction) + def wrapper(*args, **kwargs) -> None: + func = functools.partial(testfunction, *args, **kwargs) + _pdb.runcall(func) + + pyfuncitem.obj = wrapper + + +def maybe_wrap_pytest_function_for_tracing(pyfuncitem) -> None: + """Wrap the given pytestfunct item for tracing support if --trace was given in + the command line.""" + if pyfuncitem.config.getvalue("trace"): + wrap_pytest_function_for_tracing(pyfuncitem) + + +def _enter_pdb( + node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport +) -> BaseReport: + # XXX we reuse the TerminalReporter's terminalwriter + # because this seems to avoid some encoding related troubles + # for not completely clear reasons. + tw = node.config.pluginmanager.getplugin("terminalreporter")._tw + tw.line() + + showcapture = node.config.option.showcapture + + for sectionname, content in ( + ("stdout", rep.capstdout), + ("stderr", rep.capstderr), + ("log", rep.caplog), + ): + if showcapture in (sectionname, "all") and content: + tw.sep(">", "captured " + sectionname) + if content[-1:] == "\n": + content = content[:-1] + tw.line(content) + + tw.sep(">", "traceback") + rep.toterminal(tw) + tw.sep(">", "entering PDB") + tb_or_exc = _postmortem_exc_or_tb(excinfo) + rep._pdbshown = True # type: ignore[attr-defined] + post_mortem(tb_or_exc) + return rep + + +def _postmortem_exc_or_tb( + excinfo: ExceptionInfo[BaseException], +) -> types.TracebackType | BaseException: + from doctest import UnexpectedException + + get_exc = sys.version_info >= (3, 13) + if isinstance(excinfo.value, UnexpectedException): + # A doctest.UnexpectedException is not useful for post_mortem. + # Use the underlying exception instead: + underlying_exc = excinfo.value + if get_exc: + return underlying_exc.exc_info[1] + + return underlying_exc.exc_info[2] + elif isinstance(excinfo.value, ConftestImportFailure): + # A config.ConftestImportFailure is not useful for post_mortem. + # Use the underlying exception instead: + cause = excinfo.value.cause + if get_exc: + return cause + + assert cause.__traceback__ is not None + return cause.__traceback__ + else: + assert excinfo._excinfo is not None + if get_exc: + return excinfo._excinfo[1] + + return excinfo._excinfo[2] + + +def post_mortem(tb_or_exc: types.TracebackType | BaseException) -> None: + p = pytestPDB._init_pdb("post_mortem") + p.reset() + p.interaction(None, tb_or_exc) + if p.quitting: + outcomes.exit("Quitting debugger") diff --git a/.venv/lib/python3.12/site-packages/_pytest/deprecated.py b/.venv/lib/python3.12/site-packages/_pytest/deprecated.py new file mode 100644 index 0000000..cb5d2e9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/deprecated.py @@ -0,0 +1,99 @@ +"""Deprecation messages and bits of code used elsewhere in the codebase that +is planned to be removed in the next pytest release. + +Keeping it in a central location makes it easy to track what is deprecated and should +be removed when the time comes. + +All constants defined in this module should be either instances of +:class:`PytestWarning`, or :class:`UnformattedWarning` +in case of warnings which need to format their messages. +""" + +from __future__ import annotations + +from warnings import warn + +from _pytest.warning_types import PytestDeprecationWarning +from _pytest.warning_types import PytestRemovedIn9Warning +from _pytest.warning_types import PytestRemovedIn10Warning +from _pytest.warning_types import UnformattedWarning + + +# set of plugins which have been integrated into the core; we use this list to ignore +# them during registration to avoid conflicts +DEPRECATED_EXTERNAL_PLUGINS = { + "pytest_catchlog", + "pytest_capturelog", + "pytest_faulthandler", + "pytest_subtests", +} + + +# This could have been removed pytest 8, but it's harmless and common, so no rush to remove. +YIELD_FIXTURE = PytestDeprecationWarning( + "@pytest.yield_fixture is deprecated.\n" + "Use @pytest.fixture instead; they are the same." +) + +# This deprecation is never really meant to be removed. +PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") + + +HOOK_LEGACY_PATH_ARG = UnformattedWarning( + PytestRemovedIn9Warning, + "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" + "see https://docs.pytest.org/en/latest/deprecations.html" + "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", +) + +NODE_CTOR_FSPATH_ARG = UnformattedWarning( + PytestRemovedIn9Warning, + "The (fspath: py.path.local) argument to {node_type_name} is deprecated. " + "Please use the (path: pathlib.Path) argument instead.\n" + "See https://docs.pytest.org/en/latest/deprecations.html" + "#fspath-argument-for-node-constructors-replaced-with-pathlib-path", +) + +HOOK_LEGACY_MARKING = UnformattedWarning( + PytestDeprecationWarning, + "The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n" + "Please use the pytest.hook{type}({hook_opts}) decorator instead\n" + " to configure the hooks.\n" + " See https://docs.pytest.org/en/latest/deprecations.html" + "#configuring-hook-specs-impls-using-markers", +) + +MARKED_FIXTURE = PytestRemovedIn9Warning( + "Marks applied to fixtures have no effect\n" + "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" +) + +MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES = PytestRemovedIn10Warning( + "monkeypatch.syspath_prepend() called with pkg_resources legacy namespace packages detected.\n" + "Legacy namespace packages (using pkg_resources.declare_namespace) are deprecated.\n" + "Please use native namespace packages (PEP 420) instead.\n" + "See https://docs.pytest.org/en/stable/deprecations.html#monkeypatch-fixup-namespace-packages" +) + +# You want to make some `__init__` or function "private". +# +# def my_private_function(some, args): +# ... +# +# Do this: +# +# def my_private_function(some, args, *, _ispytest: bool = False): +# check_ispytest(_ispytest) +# ... +# +# Change all internal/allowed calls to +# +# my_private_function(some, args, _ispytest=True) +# +# All other calls will get the default _ispytest=False and trigger +# the warning (possibly error in the future). + + +def check_ispytest(ispytest: bool) -> None: + if not ispytest: + warn(PRIVATE, stacklevel=3) diff --git a/.venv/lib/python3.12/site-packages/_pytest/doctest.py b/.venv/lib/python3.12/site-packages/_pytest/doctest.py new file mode 100644 index 0000000..cd255f5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/doctest.py @@ -0,0 +1,736 @@ +# mypy: allow-untyped-defs +"""Discover and run doctests in modules and test files.""" + +from __future__ import annotations + +import bdb +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence +from contextlib import contextmanager +import functools +import inspect +import os +from pathlib import Path +import platform +import re +import sys +import traceback +import types +from typing import Any +from typing import TYPE_CHECKING +import warnings + +from _pytest import outcomes +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import safe_getattr +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.fixtures import fixture +from _pytest.fixtures import TopRequest +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import OutcomeException +from _pytest.outcomes import skip +from _pytest.pathlib import fnmatch_ex +from _pytest.python import Module +from _pytest.python_api import approx +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + import doctest + + from typing_extensions import Self + +DOCTEST_REPORT_CHOICE_NONE = "none" +DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" +DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" +DOCTEST_REPORT_CHOICE_UDIFF = "udiff" +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure" + +DOCTEST_REPORT_CHOICES = ( + DOCTEST_REPORT_CHOICE_NONE, + DOCTEST_REPORT_CHOICE_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF, + DOCTEST_REPORT_CHOICE_UDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, +) + +# Lazy definition of runner class +RUNNER_CLASS = None +# Lazy definition of output checker class +CHECKER_CLASS: type[doctest.OutputChecker] | None = None + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "doctest_optionflags", + "Option flags for doctests", + type="args", + default=["ELLIPSIS"], + ) + parser.addini( + "doctest_encoding", "Encoding used for doctest files", default="utf-8" + ) + group = parser.getgroup("collect") + group.addoption( + "--doctest-modules", + action="store_true", + default=False, + help="Run doctests in all .py modules", + dest="doctestmodules", + ) + group.addoption( + "--doctest-report", + type=str.lower, + default="udiff", + help="Choose another output format for diffs on doctest failure", + choices=DOCTEST_REPORT_CHOICES, + dest="doctestreport", + ) + group.addoption( + "--doctest-glob", + action="append", + default=[], + metavar="pat", + help="Doctests file matching pattern, default: test*.txt", + dest="doctestglob", + ) + group.addoption( + "--doctest-ignore-import-errors", + action="store_true", + default=False, + help="Ignore doctest collection errors", + dest="doctest_ignore_import_errors", + ) + group.addoption( + "--doctest-continue-on-failure", + action="store_true", + default=False, + help="For a given doctest, continue to run after the first failure", + dest="doctest_continue_on_failure", + ) + + +def pytest_unconfigure() -> None: + global RUNNER_CLASS + + RUNNER_CLASS = None + + +def pytest_collect_file( + file_path: Path, + parent: Collector, +) -> DoctestModule | DoctestTextfile | None: + config = parent.config + if file_path.suffix == ".py": + if config.option.doctestmodules and not any( + (_is_setup_py(file_path), _is_main_py(file_path)) + ): + return DoctestModule.from_parent(parent, path=file_path) + elif _is_doctest(config, file_path, parent): + return DoctestTextfile.from_parent(parent, path=file_path) + return None + + +def _is_setup_py(path: Path) -> bool: + if path.name != "setup.py": + return False + contents = path.read_bytes() + return b"setuptools" in contents or b"distutils" in contents + + +def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: + if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): + return True + globs = config.getoption("doctestglob") or ["test*.txt"] + return any(fnmatch_ex(glob, path) for glob in globs) + + +def _is_main_py(path: Path) -> bool: + return path.name == "__main__.py" + + +class ReprFailDoctest(TerminalRepr): + def __init__( + self, reprlocation_lines: Sequence[tuple[ReprFileLocation, Sequence[str]]] + ) -> None: + self.reprlocation_lines = reprlocation_lines + + def toterminal(self, tw: TerminalWriter) -> None: + for reprlocation, lines in self.reprlocation_lines: + for line in lines: + tw.line(line) + reprlocation.toterminal(tw) + + +class MultipleDoctestFailures(Exception): + def __init__(self, failures: Sequence[doctest.DocTestFailure]) -> None: + super().__init__() + self.failures = failures + + +def _init_runner_class() -> type[doctest.DocTestRunner]: + import doctest + + class PytestDoctestRunner(doctest.DebugRunner): + """Runner to collect failures. + + Note that the out variable in this case is a list instead of a + stdout-like object. + """ + + def __init__( + self, + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, + optionflags: int = 0, + continue_on_failure: bool = True, + ) -> None: + super().__init__(checker=checker, verbose=verbose, optionflags=optionflags) + self.continue_on_failure = continue_on_failure + + def report_failure( + self, + out, + test: doctest.DocTest, + example: doctest.Example, + got: str, + ) -> None: + failure = doctest.DocTestFailure(test, example, got) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + def report_unexpected_exception( + self, + out, + test: doctest.DocTest, + example: doctest.Example, + exc_info: tuple[type[BaseException], BaseException, types.TracebackType], + ) -> None: + if isinstance(exc_info[1], OutcomeException): + raise exc_info[1] + if isinstance(exc_info[1], bdb.BdbQuit): + outcomes.exit("Quitting debugger") + failure = doctest.UnexpectedException(test, example, exc_info) + if self.continue_on_failure: + out.append(failure) + else: + raise failure + + return PytestDoctestRunner + + +def _get_runner( + checker: doctest.OutputChecker | None = None, + verbose: bool | None = None, + optionflags: int = 0, + continue_on_failure: bool = True, +) -> doctest.DocTestRunner: + # We need this in order to do a lazy import on doctest + global RUNNER_CLASS + if RUNNER_CLASS is None: + RUNNER_CLASS = _init_runner_class() + # Type ignored because the continue_on_failure argument is only defined on + # PytestDoctestRunner, which is lazily defined so can't be used as a type. + return RUNNER_CLASS( # type: ignore + checker=checker, + verbose=verbose, + optionflags=optionflags, + continue_on_failure=continue_on_failure, + ) + + +class DoctestItem(Item): + def __init__( + self, + name: str, + parent: DoctestTextfile | DoctestModule, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> None: + super().__init__(name, parent) + self.runner = runner + self.dtest = dtest + + # Stuff needed for fixture support. + self.obj = None + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(node=self, func=None, cls=None) + self._fixtureinfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + @classmethod + def from_parent( # type: ignore[override] + cls, + parent: DoctestTextfile | DoctestModule, + *, + name: str, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> Self: + # incompatible signature due to imposed limits on subclass + """The public named constructor.""" + return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) + + def _initrequest(self) -> None: + self.funcargs: dict[str, object] = {} + self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] + + def setup(self) -> None: + self._request._fillfixtures() + globs = dict(getfixture=self._request.getfixturevalue) + for name, value in self._request.getfixturevalue("doctest_namespace").items(): + globs[name] = value + self.dtest.globs.update(globs) + + def runtest(self) -> None: + _check_all_skipped(self.dtest) + self._disable_output_capturing_for_darwin() + failures: list[doctest.DocTestFailure] = [] + # Type ignored because we change the type of `out` from what + # doctest expects. + self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] + if failures: + raise MultipleDoctestFailures(failures) + + def _disable_output_capturing_for_darwin(self) -> None: + """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" + if platform.system() != "Darwin": + return + capman = self.config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture(in_=True) + out, err = capman.read_global_capture() + sys.stdout.write(out) + sys.stderr.write(err) + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, + excinfo: ExceptionInfo[BaseException], + ) -> str | TerminalRepr: + import doctest + + failures: ( + Sequence[doctest.DocTestFailure | doctest.UnexpectedException] | None + ) = None + if isinstance( + excinfo.value, doctest.DocTestFailure | doctest.UnexpectedException + ): + failures = [excinfo.value] + elif isinstance(excinfo.value, MultipleDoctestFailures): + failures = excinfo.value.failures + + if failures is None: + return super().repr_failure(excinfo) + + reprlocation_lines = [] + for failure in failures: + example = failure.example + test = failure.test + filename = test.filename + if test.lineno is None: + lineno = None + else: + lineno = test.lineno + example.lineno + 1 + message = type(failure).__name__ + # TODO: ReprFileLocation doesn't expect a None lineno. + reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] + checker = _get_checker() + report_choice = _get_report_choice(self.config.getoption("doctestreport")) + if lineno is not None: + assert failure.test.docstring is not None + lines = failure.test.docstring.splitlines(False) + # add line numbers to the left of the error message + assert test.lineno is not None + lines = [ + f"{i + test.lineno + 1:03d} {x}" for (i, x) in enumerate(lines) + ] + # trim docstring error lines to 10 + lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] + else: + lines = [ + "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" + ] + indent = ">>>" + for line in example.source.splitlines(): + lines.append(f"??? {indent} {line}") + indent = "..." + if isinstance(failure, doctest.DocTestFailure): + lines += checker.output_difference( + example, failure.got, report_choice + ).split("\n") + else: + inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) + lines += [f"UNEXPECTED EXCEPTION: {inner_excinfo.value!r}"] + lines += [ + x.strip("\n") for x in traceback.format_exception(*failure.exc_info) + ] + reprlocation_lines.append((reprlocation, lines)) + return ReprFailDoctest(reprlocation_lines) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + return self.path, self.dtest.lineno, f"[doctest] {self.name}" + + +def _get_flag_lookup() -> dict[str, int]: + import doctest + + return dict( + DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1, + DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE, + NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE, + ELLIPSIS=doctest.ELLIPSIS, + IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL, + COMPARISON_FLAGS=doctest.COMPARISON_FLAGS, + ALLOW_UNICODE=_get_allow_unicode_flag(), + ALLOW_BYTES=_get_allow_bytes_flag(), + NUMBER=_get_number_flag(), + ) + + +def get_optionflags(config: Config) -> int: + optionflags_str = config.getini("doctest_optionflags") + flag_lookup_table = _get_flag_lookup() + flag_acc = 0 + for flag in optionflags_str: + flag_acc |= flag_lookup_table[flag] + return flag_acc + + +def _get_continue_on_failure(config: Config) -> bool: + continue_on_failure: bool = config.getvalue("doctest_continue_on_failure") + if continue_on_failure: + # We need to turn off this if we use pdb since we should stop at + # the first failure. + if config.getvalue("usepdb"): + continue_on_failure = False + return continue_on_failure + + +class DoctestTextfile(Module): + obj = None + + def collect(self) -> Iterable[DoctestItem]: + import doctest + + # Inspired by doctest.testfile; ideally we would use it directly, + # but it doesn't support passing a custom checker. + encoding = self.config.getini("doctest_encoding") + text = self.path.read_text(encoding) + filename = str(self.path) + name = self.path.name + globs = {"__name__": "__main__"} + + optionflags = get_optionflags(self.config) + + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + parser = doctest.DocTestParser() + test = parser.get_doctest(text, globs, name, filename, 0) + if test.examples: + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _check_all_skipped(test: doctest.DocTest) -> None: + """Raise pytest.skip() if all examples in the given DocTest have the SKIP + option set.""" + import doctest + + all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) + if all_skipped: + skip("all tests skipped by +SKIP option") + + +def _is_mocked(obj: object) -> bool: + """Return if an object is possibly a mock object by checking the + existence of a highly improbable attribute.""" + return ( + safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) + is not None + ) + + +@contextmanager +def _patch_unwrap_mock_aware() -> Generator[None]: + """Context manager which replaces ``inspect.unwrap`` with a version + that's aware of mock objects and doesn't recurse into them.""" + real_unwrap = inspect.unwrap + + def _mock_aware_unwrap( + func: Callable[..., Any], *, stop: Callable[[Any], Any] | None = None + ) -> Any: + try: + if stop is None or stop is _is_mocked: + return real_unwrap(func, stop=_is_mocked) + _stop = stop + return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) + except Exception as e: + warnings.warn( + f"Got {e!r} when unwrapping {func!r}. This is usually caused " + "by a violation of Python's object protocol; see e.g. " + "https://github.com/pytest-dev/pytest/issues/5080", + PytestWarning, + ) + raise + + inspect.unwrap = _mock_aware_unwrap + try: + yield + finally: + inspect.unwrap = real_unwrap + + +class DoctestModule(Module): + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + py_ver_info_minor = sys.version_info[:2] + is_find_lineno_broken = ( + py_ver_info_minor < (3, 11) + or (py_ver_info_minor == (3, 11) and sys.version_info.micro < 9) + or (py_ver_info_minor == (3, 12) and sys.version_info.micro < 3) + ) + if is_find_lineno_broken: + + def _find_lineno(self, obj, source_lines): + """On older Pythons, doctest code does not take into account + `@property`. https://github.com/python/cpython/issues/61648 + + Moreover, wrapped Doctests need to be unwrapped so the correct + line number is returned. #8796 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + + if hasattr(obj, "__wrapped__"): + # Get the main obj in case of it being wrapped + obj = inspect.unwrap(obj) + + # Type ignored because this is a private function. + return super()._find_lineno( # type:ignore[misc] + obj, + source_lines, + ) + + if sys.version_info < (3, 13): + + def _from_module(self, module, object): + """`cached_property` objects are never considered a part + of the 'current module'. As such they are skipped by doctest. + Here we override `_from_module` to check the underlying + function instead. https://github.com/python/cpython/issues/107995 + """ + if isinstance(object, functools.cached_property): + object = object.func + + # Type ignored because this is a private function. + return super()._from_module(module, object) # type: ignore[misc] + + try: + module = self.obj + except Collector.CollectError: + if self.config.getvalue("doctest_ignore_import_errors"): + skip(f"unable to import module {self.path!r}") + else: + raise + + # While doctests currently don't support fixtures directly, we still + # need to pick up autouse fixtures. + self.session._fixturemanager.parsefactories(self) + + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self.config) + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=_get_checker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + + +def _init_checker_class() -> type[doctest.OutputChecker]: + import doctest + + class LiteralsOutputChecker(doctest.OutputChecker): + # Based on doctest_nose_plugin.py from the nltk project + # (https://github.com/nltk/nltk) and on the "numtest" doctest extension + # by Sebastien Boisgerault (https://github.com/boisgera/numtest). + + _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) + _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE) + _number_re = re.compile( + r""" + (?P + (?P + (?P [+-]?\d*)\.(?P\d+) + | + (?P [+-]?\d+)\. + ) + (?: + [Ee] + (?P [+-]?\d+) + )? + | + (?P [+-]?\d+) + (?: + [Ee] + (?P [+-]?\d+) + ) + ) + """, + re.VERBOSE, + ) + + def check_output(self, want: str, got: str, optionflags: int) -> bool: + if super().check_output(want, got, optionflags): + return True + + allow_unicode = optionflags & _get_allow_unicode_flag() + allow_bytes = optionflags & _get_allow_bytes_flag() + allow_number = optionflags & _get_number_flag() + + if not allow_unicode and not allow_bytes and not allow_number: + return False + + def remove_prefixes(regex: re.Pattern[str], txt: str) -> str: + return re.sub(regex, r"\1\2", txt) + + if allow_unicode: + want = remove_prefixes(self._unicode_literal_re, want) + got = remove_prefixes(self._unicode_literal_re, got) + + if allow_bytes: + want = remove_prefixes(self._bytes_literal_re, want) + got = remove_prefixes(self._bytes_literal_re, got) + + if allow_number: + got = self._remove_unwanted_precision(want, got) + + return super().check_output(want, got, optionflags) + + def _remove_unwanted_precision(self, want: str, got: str) -> str: + wants = list(self._number_re.finditer(want)) + gots = list(self._number_re.finditer(got)) + if len(wants) != len(gots): + return got + offset = 0 + for w, g in zip(wants, gots, strict=True): + fraction: str | None = w.group("fraction") + exponent: str | None = w.group("exponent1") + if exponent is None: + exponent = w.group("exponent2") + precision = 0 if fraction is None else len(fraction) + if exponent is not None: + precision -= int(exponent) + if float(w.group()) == approx(float(g.group()), abs=10**-precision): + # They're close enough. Replace the text we actually + # got with the text we want, so that it will match when we + # check the string literally. + got = ( + got[: g.start() + offset] + w.group() + got[g.end() + offset :] + ) + offset += w.end() - w.start() - (g.end() - g.start()) + return got + + return LiteralsOutputChecker + + +def _get_checker() -> doctest.OutputChecker: + """Return a doctest.OutputChecker subclass that supports some + additional options: + + * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b'' + prefixes (respectively) in string literals. Useful when the same + doctest should run in Python 2 and Python 3. + + * NUMBER to ignore floating-point differences smaller than the + precision of the literal number in the doctest. + + An inner class is used to avoid importing "doctest" at the module + level. + """ + global CHECKER_CLASS + if CHECKER_CLASS is None: + CHECKER_CLASS = _init_checker_class() + return CHECKER_CLASS() + + +def _get_allow_unicode_flag() -> int: + """Register and return the ALLOW_UNICODE flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_UNICODE") + + +def _get_allow_bytes_flag() -> int: + """Register and return the ALLOW_BYTES flag.""" + import doctest + + return doctest.register_optionflag("ALLOW_BYTES") + + +def _get_number_flag() -> int: + """Register and return the NUMBER flag.""" + import doctest + + return doctest.register_optionflag("NUMBER") + + +def _get_report_choice(key: str) -> int: + """Return the actual `doctest` module flag value. + + We want to do it as late as possible to avoid importing `doctest` and all + its dependencies when parsing options, as it adds overhead and breaks tests. + """ + import doctest + + return { + DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, + DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, + DOCTEST_REPORT_CHOICE_NONE: 0, + }[key] + + +@fixture(scope="session") +def doctest_namespace() -> dict[str, Any]: + """Fixture that returns a :py:class:`dict` that will be injected into the + namespace of doctests. + + Usually this fixture is used in conjunction with another ``autouse`` fixture: + + .. code-block:: python + + @pytest.fixture(autouse=True) + def add_np(doctest_namespace): + doctest_namespace["np"] = numpy + + For more details: :ref:`doctest_namespace`. + """ + return dict() diff --git a/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py b/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py new file mode 100644 index 0000000..080cf58 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from collections.abc import Generator +import os +import sys + +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.stash import StashKey +import pytest + + +fault_handler_original_stderr_fd_key = StashKey[int]() +fault_handler_stderr_fd_key = StashKey[int]() + + +def pytest_addoption(parser: Parser) -> None: + help_timeout = ( + "Dump the traceback of all threads if a test takes " + "more than TIMEOUT seconds to finish" + ) + help_exit_on_timeout = ( + "Exit the test process if a test takes more than " + "faulthandler_timeout seconds to finish" + ) + parser.addini("faulthandler_timeout", help_timeout, default=0.0) + parser.addini( + "faulthandler_exit_on_timeout", help_exit_on_timeout, type="bool", default=False + ) + + +def pytest_configure(config: Config) -> None: + import faulthandler + + # at teardown we want to restore the original faulthandler fileno + # but faulthandler has no api to return the original fileno + # so here we stash the stderr fileno to be used at teardown + # sys.stderr and sys.__stderr__ may be closed or patched during the session + # so we can't rely on their values being good at that point (#11572). + stderr_fileno = get_stderr_fileno() + if faulthandler.is_enabled(): + config.stash[fault_handler_original_stderr_fd_key] = stderr_fileno + config.stash[fault_handler_stderr_fd_key] = os.dup(stderr_fileno) + faulthandler.enable(file=config.stash[fault_handler_stderr_fd_key]) + + +def pytest_unconfigure(config: Config) -> None: + import faulthandler + + faulthandler.disable() + # Close the dup file installed during pytest_configure. + if fault_handler_stderr_fd_key in config.stash: + os.close(config.stash[fault_handler_stderr_fd_key]) + del config.stash[fault_handler_stderr_fd_key] + # Re-enable the faulthandler if it was originally enabled. + if fault_handler_original_stderr_fd_key in config.stash: + faulthandler.enable(config.stash[fault_handler_original_stderr_fd_key]) + del config.stash[fault_handler_original_stderr_fd_key] + + +def get_stderr_fileno() -> int: + try: + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno + except (AttributeError, ValueError): + # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. + # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors + # This is potentially dangerous, but the best we can do. + assert sys.__stderr__ is not None + return sys.__stderr__.fileno() + + +def get_timeout_config_value(config: Config) -> float: + return float(config.getini("faulthandler_timeout") or 0.0) + + +def get_exit_on_timeout_config_value(config: Config) -> bool: + exit_on_timeout = config.getini("faulthandler_exit_on_timeout") + assert isinstance(exit_on_timeout, bool) + return exit_on_timeout + + +@pytest.hookimpl(wrapper=True, trylast=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + timeout = get_timeout_config_value(item.config) + exit_on_timeout = get_exit_on_timeout_config_value(item.config) + if timeout > 0: + import faulthandler + + stderr = item.config.stash[fault_handler_stderr_fd_key] + faulthandler.dump_traceback_later(timeout, file=stderr, exit=exit_on_timeout) + try: + return (yield) + finally: + faulthandler.cancel_dump_traceback_later() + else: + return (yield) + + +@pytest.hookimpl(tryfirst=True) +def pytest_enter_pdb() -> None: + """Cancel any traceback dumping due to timeout before entering pdb.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() + + +@pytest.hookimpl(tryfirst=True) +def pytest_exception_interact() -> None: + """Cancel any traceback dumping due to an interactive exception being + raised.""" + import faulthandler + + faulthandler.cancel_dump_traceback_later() diff --git a/.venv/lib/python3.12/site-packages/_pytest/fixtures.py b/.venv/lib/python3.12/site-packages/_pytest/fixtures.py new file mode 100644 index 0000000..27846db --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/fixtures.py @@ -0,0 +1,2047 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import abc +from collections import defaultdict +from collections import deque +from collections import OrderedDict +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import dataclasses +import functools +import inspect +import os +from pathlib import Path +import sys +import types +from typing import Any +from typing import cast +from typing import Final +from typing import final +from typing import Generic +from typing import NoReturn +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +import warnings + +import _pytest +from _pytest import nodes +from _pytest._code import getfslineno +from _pytest._code import Source +from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.compat import assert_never +from _pytest.compat import get_real_func +from _pytest.compat import getfuncargnames +from _pytest.compat import getimfunc +from _pytest.compat import getlocation +from _pytest.compat import NOTSET +from _pytest.compat import NotSetType +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.compat import signature +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import MARKED_FIXTURE +from _pytest.deprecated import YIELD_FIXTURE +from _pytest.main import Session +from _pytest.mark import Mark +from _pytest.mark import ParameterSet +from _pytest.mark.structures import MarkDecorator +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import TEST_OUTCOME +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.scope import _ScopeName +from _pytest.scope import HIGH_SCOPES +from _pytest.scope import Scope +from _pytest.warning_types import PytestRemovedIn9Warning +from _pytest.warning_types import PytestWarning + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +if TYPE_CHECKING: + from _pytest.python import CallSpec2 + from _pytest.python import Function + from _pytest.python import Metafunc + + +# The value of the fixture -- return/yield of the fixture function (type variable). +FixtureValue = TypeVar("FixtureValue", covariant=True) +# The type of the fixture function (type variable). +FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) +# The type of a fixture function (type alias generic in fixture value). +_FixtureFunc = Callable[..., FixtureValue] | Callable[..., Generator[FixtureValue]] +# The type of FixtureDef.cached_result (type alias generic in fixture value). +_FixtureCachedResult = ( + tuple[ + # The result. + FixtureValue, + # Cache key. + object, + None, + ] + | tuple[ + None, + # Cache key. + object, + # The exception and the original traceback. + tuple[BaseException, types.TracebackType | None], + ] +) + + +def pytest_sessionstart(session: Session) -> None: + session._fixturemanager = FixtureManager(session) + + +def get_scope_package( + node: nodes.Item, + fixturedef: FixtureDef[object], +) -> nodes.Node | None: + from _pytest.python import Package + + for parent in node.iter_parents(): + if isinstance(parent, Package) and parent.nodeid == fixturedef.baseid: + return parent + return node.session + + +def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None: + """Get the closest parent node (including self) which matches the given + scope. + + If there is no parent node for the scope (e.g. asking for class scope on a + Module, or on a Function when not defined in a class), returns None. + """ + import _pytest.python + + if scope is Scope.Function: + # Type ignored because this is actually safe, see: + # https://github.com/python/mypy/issues/4717 + return node.getparent(nodes.Item) # type: ignore[type-abstract] + elif scope is Scope.Class: + return node.getparent(_pytest.python.Class) + elif scope is Scope.Module: + return node.getparent(_pytest.python.Module) + elif scope is Scope.Package: + return node.getparent(_pytest.python.Package) + elif scope is Scope.Session: + return node.getparent(_pytest.main.Session) + else: + assert_never(scope) + + +# TODO: Try to use FixtureFunctionDefinition instead of the marker +def getfixturemarker(obj: object) -> FixtureFunctionMarker | None: + """Return fixturemarker or None if it doesn't exist""" + if isinstance(obj, FixtureFunctionDefinition): + return obj._fixture_function_marker + return None + + +# Algorithm for sorting on a per-parametrized resource setup basis. +# It is called for Session scope first and performs sorting +# down to the lower scopes such as to minimize number of "high scope" +# setups and teardowns. + + +@dataclasses.dataclass(frozen=True) +class ParamArgKey: + """A key for a high-scoped parameter used by an item. + + For use as a hashable key in `reorder_items`. The combination of fields + is meant to uniquely identify a particular "instance" of a param, + potentially shared by multiple items in a scope. + """ + + #: The param name. + argname: str + param_index: int + #: For scopes Package, Module, Class, the path to the file (directory in + #: Package's case) of the package/module/class where the item is defined. + scoped_item_path: Path | None + #: For Class scope, the class where the item is defined. + item_cls: type | None + + +_V = TypeVar("_V") +OrderedSet = dict[_V, None] + + +def get_param_argkeys(item: nodes.Item, scope: Scope) -> Iterator[ParamArgKey]: + """Return all ParamArgKeys for item matching the specified high scope.""" + assert scope is not Scope.Function + + try: + callspec: CallSpec2 = item.callspec # type: ignore[attr-defined] + except AttributeError: + return + + item_cls = None + if scope is Scope.Session: + scoped_item_path = None + elif scope is Scope.Package: + # Package key = module's directory. + scoped_item_path = item.path.parent + elif scope is Scope.Module: + scoped_item_path = item.path + elif scope is Scope.Class: + scoped_item_path = item.path + item_cls = item.cls # type: ignore[attr-defined] + else: + assert_never(scope) + + for argname in callspec.indices: + if callspec._arg2scope[argname] != scope: + continue + param_index = callspec.indices[argname] + yield ParamArgKey(argname, param_index, scoped_item_path, item_cls) + + +def reorder_items(items: Sequence[nodes.Item]) -> list[nodes.Item]: + argkeys_by_item: dict[Scope, dict[nodes.Item, OrderedSet[ParamArgKey]]] = {} + items_by_argkey: dict[Scope, dict[ParamArgKey, OrderedDict[nodes.Item, None]]] = {} + for scope in HIGH_SCOPES: + scoped_argkeys_by_item = argkeys_by_item[scope] = {} + scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(OrderedDict) + for item in items: + argkeys = dict.fromkeys(get_param_argkeys(item, scope)) + if argkeys: + scoped_argkeys_by_item[item] = argkeys + for argkey in argkeys: + scoped_items_by_argkey[argkey][item] = None + + items_set = dict.fromkeys(items) + return list( + reorder_items_atscope( + items_set, argkeys_by_item, items_by_argkey, Scope.Session + ) + ) + + +def reorder_items_atscope( + items: OrderedSet[nodes.Item], + argkeys_by_item: Mapping[Scope, Mapping[nodes.Item, OrderedSet[ParamArgKey]]], + items_by_argkey: Mapping[ + Scope, Mapping[ParamArgKey, OrderedDict[nodes.Item, None]] + ], + scope: Scope, +) -> OrderedSet[nodes.Item]: + if scope is Scope.Function or len(items) < 3: + return items + + scoped_items_by_argkey = items_by_argkey[scope] + scoped_argkeys_by_item = argkeys_by_item[scope] + + ignore: set[ParamArgKey] = set() + items_deque = deque(items) + items_done: OrderedSet[nodes.Item] = {} + while items_deque: + no_argkey_items: OrderedSet[nodes.Item] = {} + slicing_argkey = None + while items_deque: + item = items_deque.popleft() + if item in items_done or item in no_argkey_items: + continue + argkeys = dict.fromkeys( + k for k in scoped_argkeys_by_item.get(item, ()) if k not in ignore + ) + if not argkeys: + no_argkey_items[item] = None + else: + slicing_argkey, _ = argkeys.popitem() + # We don't have to remove relevant items from later in the + # deque because they'll just be ignored. + matching_items = [ + i for i in scoped_items_by_argkey[slicing_argkey] if i in items + ] + for i in reversed(matching_items): + items_deque.appendleft(i) + # Fix items_by_argkey order. + for other_scope in HIGH_SCOPES: + other_scoped_items_by_argkey = items_by_argkey[other_scope] + for argkey in argkeys_by_item[other_scope].get(i, ()): + argkey_dict = other_scoped_items_by_argkey[argkey] + if not hasattr(sys, "pypy_version_info"): + argkey_dict[i] = None + argkey_dict.move_to_end(i, last=False) + else: + # Work around a bug in PyPy: + # https://github.com/pypy/pypy/issues/5257 + # https://github.com/pytest-dev/pytest/issues/13312 + bkp = argkey_dict.copy() + argkey_dict.clear() + argkey_dict[i] = None + argkey_dict.update(bkp) + break + if no_argkey_items: + reordered_no_argkey_items = reorder_items_atscope( + no_argkey_items, argkeys_by_item, items_by_argkey, scope.next_lower() + ) + items_done.update(reordered_no_argkey_items) + if slicing_argkey is not None: + ignore.add(slicing_argkey) + return items_done + + +@dataclasses.dataclass(frozen=True) +class FuncFixtureInfo: + """Fixture-related information for a fixture-requesting item (e.g. test + function). + + This is used to examine the fixtures which an item requests statically + (known during collection). This includes autouse fixtures, fixtures + requested by the `usefixtures` marker, fixtures requested in the function + parameters, and the transitive closure of these. + + An item may also request fixtures dynamically (using `request.getfixturevalue`); + these are not reflected here. + """ + + __slots__ = ("argnames", "initialnames", "name2fixturedefs", "names_closure") + + # Fixture names that the item requests directly by function parameters. + argnames: tuple[str, ...] + # Fixture names that the item immediately requires. These include + # argnames + fixture names specified via usefixtures and via autouse=True in + # fixture definitions. + initialnames: tuple[str, ...] + # The transitive closure of the fixture names that the item requires. + # Note: can't include dynamic dependencies (`request.getfixturevalue` calls). + names_closure: list[str] + # A map from a fixture name in the transitive closure to the FixtureDefs + # matching the name which are applicable to this function. + # There may be multiple overriding fixtures with the same name. The + # sequence is ordered from furthest to closes to the function. + name2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] + + def prune_dependency_tree(self) -> None: + """Recompute names_closure from initialnames and name2fixturedefs. + + Can only reduce names_closure, which means that the new closure will + always be a subset of the old one. The order is preserved. + + This method is needed because direct parametrization may shadow some + of the fixtures that were included in the originally built dependency + tree. In this way the dependency tree can get pruned, and the closure + of argnames may get reduced. + """ + closure: set[str] = set() + working_set = set(self.initialnames) + while working_set: + argname = working_set.pop() + # Argname may be something not included in the original names_closure, + # in which case we ignore it. This currently happens with pseudo + # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. + # So they introduce the new dependency 'request' which might have + # been missing in the original tree (closure). + if argname not in closure and argname in self.names_closure: + closure.add(argname) + if argname in self.name2fixturedefs: + working_set.update(self.name2fixturedefs[argname][-1].argnames) + + self.names_closure[:] = sorted(closure, key=self.names_closure.index) + + +class FixtureRequest(abc.ABC): + """The type of the ``request`` fixture. + + A request object gives access to the requesting test context and has a + ``param`` attribute in case the fixture is parametrized. + """ + + def __init__( + self, + pyfuncitem: Function, + fixturename: str | None, + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]], + fixture_defs: dict[str, FixtureDef[Any]], + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + #: Fixture for which this request is being performed. + self.fixturename: Final = fixturename + self._pyfuncitem: Final = pyfuncitem + # The FixtureDefs for each fixture name requested by this item. + # Starts from the statically-known fixturedefs resolved during + # collection. Dynamically requested fixtures (using + # `request.getfixturevalue("foo")`) are added dynamically. + self._arg2fixturedefs: Final = arg2fixturedefs + # The evaluated argnames so far, mapping to the FixtureDef they resolved + # to. + self._fixture_defs: Final = fixture_defs + # Notes on the type of `param`: + # -`request.param` is only defined in parametrized fixtures, and will raise + # AttributeError otherwise. Python typing has no notion of "undefined", so + # this cannot be reflected in the type. + # - Technically `param` is only (possibly) defined on SubRequest, not + # FixtureRequest, but the typing of that is still in flux so this cheats. + # - In the future we might consider using a generic for the param type, but + # for now just using Any. + self.param: Any + + @property + def _fixturemanager(self) -> FixtureManager: + return self._pyfuncitem.session._fixturemanager + + @property + @abc.abstractmethod + def _scope(self) -> Scope: + raise NotImplementedError() + + @property + def scope(self) -> _ScopeName: + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + + @abc.abstractmethod + def _check_scope( + self, + requested_fixturedef: FixtureDef[object], + requested_scope: Scope, + ) -> None: + raise NotImplementedError() + + @property + def fixturenames(self) -> list[str]: + """Names of all active fixtures in this request.""" + result = list(self._pyfuncitem.fixturenames) + result.extend(set(self._fixture_defs).difference(result)) + return result + + @property + @abc.abstractmethod + def node(self): + """Underlying collection node (depends on current request scope).""" + raise NotImplementedError() + + @property + def config(self) -> Config: + """The pytest config object associated with this request.""" + return self._pyfuncitem.config + + @property + def function(self): + """Test function object if the request has a per-function scope.""" + if self.scope != "function": + raise AttributeError( + f"function not available in {self.scope}-scoped context" + ) + return self._pyfuncitem.obj + + @property + def cls(self): + """Class (can be None) where the test function was collected.""" + if self.scope not in ("class", "function"): + raise AttributeError(f"cls not available in {self.scope}-scoped context") + clscol = self._pyfuncitem.getparent(_pytest.python.Class) + if clscol: + return clscol.obj + + @property + def instance(self): + """Instance (can be None) on which test function was collected.""" + if self.scope != "function": + return None + return getattr(self._pyfuncitem, "instance", None) + + @property + def module(self): + """Python module object where the test function was collected.""" + if self.scope not in ("function", "class", "module"): + raise AttributeError(f"module not available in {self.scope}-scoped context") + mod = self._pyfuncitem.getparent(_pytest.python.Module) + assert mod is not None + return mod.obj + + @property + def path(self) -> Path: + """Path where the test function was collected.""" + if self.scope not in ("function", "class", "module", "package"): + raise AttributeError(f"path not available in {self.scope}-scoped context") + return self._pyfuncitem.path + + @property + def keywords(self) -> MutableMapping[str, Any]: + """Keywords/markers dictionary for the underlying node.""" + node: nodes.Node = self.node + return node.keywords + + @property + def session(self) -> Session: + """Pytest session object.""" + return self._pyfuncitem.session + + @abc.abstractmethod + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + """Add finalizer/teardown function to be called without arguments after + the last test within the requesting test context finished execution.""" + raise NotImplementedError() + + def applymarker(self, marker: str | MarkDecorator) -> None: + """Apply a marker to a single test function invocation. + + This method is useful if you don't want to have a keyword/marker + on all function invocations. + + :param marker: + An object created by a call to ``pytest.mark.NAME(...)``. + """ + self.node.add_marker(marker) + + def raiseerror(self, msg: str | None) -> NoReturn: + """Raise a FixtureLookupError exception. + + :param msg: + An optional custom error message. + """ + raise FixtureLookupError(None, self, msg) + + def getfixturevalue(self, argname: str) -> Any: + """Dynamically run a named fixture function. + + Declaring fixtures via function argument is recommended where possible. + But if you can only decide whether to use another fixture at test + setup time, you may use this function to retrieve it inside a fixture + or test function body. + + This method can be used during the test setup phase or the test run + phase, but during the test teardown phase a fixture's value may not + be available. + + :param argname: + The fixture name. + :raises pytest.FixtureLookupError: + If the given fixture could not be found. + """ + # Note that in addition to the use case described in the docstring, + # getfixturevalue() is also called by pytest itself during item and fixture + # setup to evaluate the fixtures that are requested statically + # (using function parameters, autouse, etc). + + fixturedef = self._get_active_fixturedef(argname) + assert fixturedef.cached_result is not None, ( + f'The fixture value for "{argname}" is not available. ' + "This can happen when the fixture has already been torn down." + ) + return fixturedef.cached_result[0] + + def _iter_chain(self) -> Iterator[SubRequest]: + """Yield all SubRequests in the chain, from self up. + + Note: does *not* yield the TopRequest. + """ + current = self + while isinstance(current, SubRequest): + yield current + current = current._parent_request + + def _get_active_fixturedef(self, argname: str) -> FixtureDef[object]: + if argname == "request": + return RequestFixtureDef(self) + + # If we already finished computing a fixture by this name in this item, + # return it. + fixturedef = self._fixture_defs.get(argname) + if fixturedef is not None: + self._check_scope(fixturedef, fixturedef._scope) + return fixturedef + + # Find the appropriate fixturedef. + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # We arrive here because of a dynamic call to + # getfixturevalue(argname) which was naturally + # not known at parsing/collection time. + fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem) + if fixturedefs is not None: + self._arg2fixturedefs[argname] = fixturedefs + # No fixtures defined with this name. + if fixturedefs is None: + raise FixtureLookupError(argname, self) + # The are no fixtures with this name applicable for the function. + if not fixturedefs: + raise FixtureLookupError(argname, self) + + # A fixture may override another fixture with the same name, e.g. a + # fixture in a module can override a fixture in a conftest, a fixture in + # a class can override a fixture in the module, and so on. + # An overriding fixture can request its own name (possibly indirectly); + # in this case it gets the value of the fixture it overrides, one level + # up. + # Check how many `argname`s deep we are, and take the next one. + # `fixturedefs` is sorted from furthest to closest, so use negative + # indexing to go in reverse. + index = -1 + for request in self._iter_chain(): + if request.fixturename == argname: + index -= 1 + # If already consumed all of the available levels, fail. + if -index > len(fixturedefs): + raise FixtureLookupError(argname, self) + fixturedef = fixturedefs[index] + + # Prepare a SubRequest object for calling the fixture. + try: + callspec = self._pyfuncitem.callspec + except AttributeError: + callspec = None + if callspec is not None and argname in callspec.params: + param = callspec.params[argname] + param_index = callspec.indices[argname] + # The parametrize invocation scope overrides the fixture's scope. + scope = callspec._arg2scope[argname] + else: + param = NOTSET + param_index = 0 + scope = fixturedef._scope + self._check_fixturedef_without_param(fixturedef) + # The parametrize invocation scope only controls caching behavior while + # allowing wider-scoped fixtures to keep depending on the parametrized + # fixture. Scope control is enforced for parametrized fixtures + # by recreating the whole fixture tree on parameter change. + # Hence `fixturedef._scope`, not `scope`. + self._check_scope(fixturedef, fixturedef._scope) + subrequest = SubRequest( + self, scope, param, param_index, fixturedef, _ispytest=True + ) + + # Make sure the fixture value is cached, running it if it isn't + fixturedef.execute(request=subrequest) + + self._fixture_defs[argname] = fixturedef + return fixturedef + + def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> None: + """Check that this request is allowed to execute this fixturedef without + a param.""" + funcitem = self._pyfuncitem + has_params = fixturedef.params is not None + fixtures_not_supported = getattr(funcitem, "nofuncargs", False) + if has_params and fixtures_not_supported: + msg = ( + f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" + f"Node id: {funcitem.nodeid}\n" + f"Function type: {type(funcitem).__name__}" + ) + fail(msg, pytrace=False) + if has_params: + frame = inspect.stack()[3] + frameinfo = inspect.getframeinfo(frame[0]) + source_path = absolutepath(frameinfo.filename) + source_lineno = frameinfo.lineno + try: + source_path_str = str(source_path.relative_to(funcitem.config.rootpath)) + except ValueError: + source_path_str = str(source_path) + location = getlocation(fixturedef.func, funcitem.config.rootpath) + msg = ( + "The requested fixture has no parameter defined for test:\n" + f" {funcitem.nodeid}\n\n" + f"Requested fixture '{fixturedef.argname}' defined in:\n" + f"{location}\n\n" + f"Requested here:\n" + f"{source_path_str}:{source_lineno}" + ) + fail(msg, pytrace=False) + + def _get_fixturestack(self) -> list[FixtureDef[Any]]: + values = [request._fixturedef for request in self._iter_chain()] + values.reverse() + return values + + +@final +class TopRequest(FixtureRequest): + """The type of the ``request`` fixture in a test function.""" + + def __init__(self, pyfuncitem: Function, *, _ispytest: bool = False) -> None: + super().__init__( + fixturename=None, + pyfuncitem=pyfuncitem, + arg2fixturedefs=pyfuncitem._fixtureinfo.name2fixturedefs.copy(), + fixture_defs={}, + _ispytest=_ispytest, + ) + + @property + def _scope(self) -> Scope: + return Scope.Function + + def _check_scope( + self, + requested_fixturedef: FixtureDef[object], + requested_scope: Scope, + ) -> None: + # TopRequest always has function scope so always valid. + pass + + @property + def node(self): + return self._pyfuncitem + + def __repr__(self) -> str: + return f"" + + def _fillfixtures(self) -> None: + item = self._pyfuncitem + for argname in item.fixturenames: + if argname not in item.funcargs: + item.funcargs[argname] = self.getfixturevalue(argname) + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self.node.addfinalizer(finalizer) + + +@final +class SubRequest(FixtureRequest): + """The type of the ``request`` fixture in a fixture function requested + (transitively) by a test function.""" + + def __init__( + self, + request: FixtureRequest, + scope: Scope, + param: Any, + param_index: int, + fixturedef: FixtureDef[object], + *, + _ispytest: bool = False, + ) -> None: + super().__init__( + pyfuncitem=request._pyfuncitem, + fixturename=fixturedef.argname, + fixture_defs=request._fixture_defs, + arg2fixturedefs=request._arg2fixturedefs, + _ispytest=_ispytest, + ) + self._parent_request: Final[FixtureRequest] = request + self._scope_field: Final = scope + self._fixturedef: Final[FixtureDef[object]] = fixturedef + if param is not NOTSET: + self.param = param + self.param_index: Final = param_index + + def __repr__(self) -> str: + return f"" + + @property + def _scope(self) -> Scope: + return self._scope_field + + @property + def node(self): + scope = self._scope + if scope is Scope.Function: + # This might also be a non-function Item despite its attribute name. + node: nodes.Node | None = self._pyfuncitem + elif scope is Scope.Package: + node = get_scope_package(self._pyfuncitem, self._fixturedef) + else: + node = get_scope_node(self._pyfuncitem, scope) + if node is None and scope is Scope.Class: + # Fallback to function item itself. + node = self._pyfuncitem + assert node, ( + f'Could not obtain a node for scope "{scope}" for function {self._pyfuncitem!r}' + ) + return node + + def _check_scope( + self, + requested_fixturedef: FixtureDef[object], + requested_scope: Scope, + ) -> None: + if self._scope > requested_scope: + # Try to report something helpful. + argname = requested_fixturedef.argname + fixture_stack = "\n".join( + self._format_fixturedef_line(fixturedef) + for fixturedef in self._get_fixturestack() + ) + requested_fixture = self._format_fixturedef_line(requested_fixturedef) + fail( + f"ScopeMismatch: You tried to access the {requested_scope.value} scoped " + f"fixture {argname} with a {self._scope.value} scoped request object. " + f"Requesting fixture stack:\n{fixture_stack}\n" + f"Requested fixture:\n{requested_fixture}", + pytrace=False, + ) + + def _format_fixturedef_line(self, fixturedef: FixtureDef[object]) -> str: + factory = fixturedef.func + path, lineno = getfslineno(factory) + if isinstance(path, Path): + path = bestrelpath(self._pyfuncitem.session.path, path) + sig = signature(factory) + return f"{path}:{lineno + 1}: def {factory.__name__}{sig}" + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._fixturedef.addfinalizer(finalizer) + + +@final +class FixtureLookupError(LookupError): + """Could not return a requested fixture (missing or invalid).""" + + def __init__( + self, argname: str | None, request: FixtureRequest, msg: str | None = None + ) -> None: + self.argname = argname + self.request = request + self.fixturestack = request._get_fixturestack() + self.msg = msg + + def formatrepr(self) -> FixtureLookupErrorRepr: + tblines: list[str] = [] + addline = tblines.append + stack = [self.request._pyfuncitem.obj] + stack.extend(map(lambda x: x.func, self.fixturestack)) + msg = self.msg + # This function currently makes an assumption that a non-None msg means we + # have a non-empty `self.fixturestack`. This is currently true, but if + # somebody at some point want to extend the use of FixtureLookupError to + # new cases it might break. + # Add the assert to make it clearer to developer that this will fail, otherwise + # it crashes because `fspath` does not get set due to `stack` being empty. + assert self.msg is None or self.fixturestack, ( + "formatrepr assumptions broken, rewrite it to handle it" + ) + if msg is not None: + # The last fixture raise an error, let's present + # it at the requesting side. + stack = stack[:-1] + for function in stack: + fspath, lineno = getfslineno(function) + try: + lines, _ = inspect.getsourcelines(get_real_func(function)) + except (OSError, IndexError, TypeError): + error_msg = "file %s, line %s: source code not available" + addline(error_msg % (fspath, lineno + 1)) + else: + addline(f"file {fspath}, line {lineno + 1}") + for i, line in enumerate(lines): + line = line.rstrip() + addline(" " + line) + if line.lstrip().startswith("def"): + break + + if msg is None: + fm = self.request._fixturemanager + available = set() + parent = self.request._pyfuncitem.parent + assert parent is not None + for name, fixturedefs in fm._arg2fixturedefs.items(): + faclist = list(fm._matchfactories(fixturedefs, parent)) + if faclist: + available.add(name) + if self.argname in available: + msg = ( + f" recursive dependency involving fixture '{self.argname}' detected" + ) + else: + msg = f"fixture '{self.argname}' not found" + msg += "\n available fixtures: {}".format(", ".join(sorted(available))) + msg += "\n use 'pytest --fixtures [testpath]' for help on them." + + return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) + + +class FixtureLookupErrorRepr(TerminalRepr): + def __init__( + self, + filename: str | os.PathLike[str], + firstlineno: int, + tblines: Sequence[str], + errorstring: str, + argname: str | None, + ) -> None: + self.tblines = tblines + self.errorstring = errorstring + self.filename = filename + self.firstlineno = firstlineno + self.argname = argname + + def toterminal(self, tw: TerminalWriter) -> None: + # tw.line("FixtureLookupError: %s" %(self.argname), red=True) + for tbline in self.tblines: + tw.line(tbline.rstrip()) + lines = self.errorstring.split("\n") + if lines: + tw.line( + f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", + red=True, + ) + for line in lines[1:]: + tw.line( + f"{FormattedExcinfo.flow_marker} {line.strip()}", + red=True, + ) + tw.line() + tw.line(f"{os.fspath(self.filename)}:{self.firstlineno + 1}") + + +def call_fixture_func( + fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs +) -> FixtureValue: + if inspect.isgeneratorfunction(fixturefunc): + fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc) + generator = fixturefunc(**kwargs) + try: + fixture_result = next(generator) + except StopIteration: + raise ValueError(f"{request.fixturename} did not yield a value") from None + finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) + request.addfinalizer(finalizer) + else: + fixturefunc = cast(Callable[..., FixtureValue], fixturefunc) + fixture_result = fixturefunc(**kwargs) + return fixture_result + + +def _teardown_yield_fixture(fixturefunc, it) -> None: + """Execute the teardown of a fixture function by advancing the iterator + after the yield and ensure the iteration ends (if not it means there is + more than one yield in the function).""" + try: + next(it) + except StopIteration: + pass + else: + fs, lineno = getfslineno(fixturefunc) + fail( + f"fixture function has more than one 'yield':\n\n" + f"{Source(fixturefunc).indent()}\n" + f"{fs}:{lineno + 1}", + pytrace=False, + ) + + +def _eval_scope_callable( + scope_callable: Callable[[str, Config], _ScopeName], + fixture_name: str, + config: Config, +) -> _ScopeName: + try: + # Type ignored because there is no typing mechanism to specify + # keyword arguments, currently. + result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] + except Exception as e: + raise TypeError( + f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n" + "Expected a function with the signature (*, fixture_name, config)" + ) from e + if not isinstance(result, str): + fail( + f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n" + f"{result!r}", + pytrace=False, + ) + return result + + +class FixtureDef(Generic[FixtureValue]): + """A container for a fixture definition. + + Note: At this time, only explicitly documented fields and methods are + considered public stable API. + """ + + def __init__( + self, + config: Config, + baseid: str | None, + argname: str, + func: _FixtureFunc[FixtureValue], + scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None, + params: Sequence[object] | None, + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, + *, + _ispytest: bool = False, + # only used in a deprecationwarning msg, can be removed in pytest9 + _autouse: bool = False, + ) -> None: + check_ispytest(_ispytest) + # The "base" node ID for the fixture. + # + # This is a node ID prefix. A fixture is only available to a node (e.g. + # a `Function` item) if the fixture's baseid is a nodeid of a parent of + # node. + # + # For a fixture found in a Collector's object (e.g. a `Module`s module, + # a `Class`'s class), the baseid is the Collector's nodeid. + # + # For a fixture found in a conftest plugin, the baseid is the conftest's + # directory path relative to the rootdir. + # + # For other plugins, the baseid is the empty string (always matches). + self.baseid: Final = baseid or "" + # Whether the fixture was found from a node or a conftest in the + # collection tree. Will be false for fixtures defined in non-conftest + # plugins. + self.has_location: Final = baseid is not None + # The fixture factory function. + self.func: Final = func + # The name by which the fixture may be requested. + self.argname: Final = argname + if scope is None: + scope = Scope.Function + elif callable(scope): + scope = _eval_scope_callable(scope, argname, config) + if isinstance(scope, str): + scope = Scope.from_user( + scope, descr=f"Fixture '{func.__name__}'", where=baseid + ) + self._scope: Final = scope + # If the fixture is directly parametrized, the parameter values. + self.params: Final = params + # If the fixture is directly parametrized, a tuple of explicit IDs to + # assign to the parameter values, or a callable to generate an ID given + # a parameter value. + self.ids: Final = ids + # The names requested by the fixtures. + self.argnames: Final = getfuncargnames(func, name=argname) + # If the fixture was executed, the current value of the fixture. + # Can change if the fixture is executed with different parameters. + self.cached_result: _FixtureCachedResult[FixtureValue] | None = None + self._finalizers: Final[list[Callable[[], object]]] = [] + + # only used to emit a deprecationwarning, can be removed in pytest9 + self._autouse = _autouse + + @property + def scope(self) -> _ScopeName: + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + self._finalizers.append(finalizer) + + def finish(self, request: SubRequest) -> None: + exceptions: list[BaseException] = [] + while self._finalizers: + fin = self._finalizers.pop() + try: + fin() + except BaseException as e: + exceptions.append(e) + node = request.node + node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) + # Even if finalization fails, we invalidate the cached fixture + # value and remove all finalizers because they may be bound methods + # which will keep instances alive. + self.cached_result = None + self._finalizers.clear() + if len(exceptions) == 1: + raise exceptions[0] + elif len(exceptions) > 1: + msg = f'errors while tearing down fixture "{self.argname}" of {node}' + raise BaseExceptionGroup(msg, exceptions[::-1]) + + def execute(self, request: SubRequest) -> FixtureValue: + """Return the value of this fixture, executing it if not cached.""" + # Ensure that the dependent fixtures requested by this fixture are loaded. + # This needs to be done before checking if we have a cached value, since + # if a dependent fixture has their cache invalidated, e.g. due to + # parametrization, they finalize themselves and fixtures depending on it + # (which will likely include this fixture) setting `self.cached_result = None`. + # See #4871 + requested_fixtures_that_should_finalize_us = [] + for argname in self.argnames: + fixturedef = request._get_active_fixturedef(argname) + # Saves requested fixtures in a list so we later can add our finalizer + # to them, ensuring that if a requested fixture gets torn down we get torn + # down first. This is generally handled by SetupState, but still currently + # needed when this fixture is not parametrized but depends on a parametrized + # fixture. + requested_fixtures_that_should_finalize_us.append(fixturedef) + + # Check for (and return) cached value/exception. + if self.cached_result is not None: + request_cache_key = self.cache_key(request) + cache_key = self.cached_result[1] + try: + # Attempt to make a normal == check: this might fail for objects + # which do not implement the standard comparison (like numpy arrays -- #6497). + cache_hit = bool(request_cache_key == cache_key) + except (ValueError, RuntimeError): + # If the comparison raises, use 'is' as fallback. + cache_hit = request_cache_key is cache_key + + if cache_hit: + if self.cached_result[2] is not None: + exc, exc_tb = self.cached_result[2] + raise exc.with_traceback(exc_tb) + else: + return self.cached_result[0] + # We have a previous but differently parametrized fixture instance + # so we need to tear it down before creating a new one. + self.finish(request) + assert self.cached_result is None + + # Add finalizer to requested fixtures we saved previously. + # We make sure to do this after checking for cached value to avoid + # adding our finalizer multiple times. (#12135) + finalizer = functools.partial(self.finish, request=request) + for parent_fixture in requested_fixtures_that_should_finalize_us: + parent_fixture.addfinalizer(finalizer) + + ihook = request.node.ihook + try: + # Setup the fixture, run the code in it, and cache the value + # in self.cached_result. + result: FixtureValue = ihook.pytest_fixture_setup( + fixturedef=self, request=request + ) + finally: + # Schedule our finalizer, even if the setup failed. + request.node.addfinalizer(finalizer) + + return result + + def cache_key(self, request: SubRequest) -> object: + return getattr(request, "param", None) + + def __repr__(self) -> str: + return f"" + + +class RequestFixtureDef(FixtureDef[FixtureRequest]): + """A custom FixtureDef for the special "request" fixture. + + A new one is generated on-demand whenever "request" is requested. + """ + + def __init__(self, request: FixtureRequest) -> None: + super().__init__( + config=request.config, + baseid=None, + argname="request", + func=lambda: request, + scope=Scope.Function, + params=None, + _ispytest=True, + ) + self.cached_result = (request, [0], None) + + def addfinalizer(self, finalizer: Callable[[], object]) -> None: + pass + + +def resolve_fixture_function( + fixturedef: FixtureDef[FixtureValue], request: FixtureRequest +) -> _FixtureFunc[FixtureValue]: + """Get the actual callable that can be called to obtain the fixture + value.""" + fixturefunc = fixturedef.func + # The fixture function needs to be bound to the actual + # request.instance so that code working with "fixturedef" behaves + # as expected. + instance = request.instance + if instance is not None: + # Handle the case where fixture is defined not in a test class, but some other class + # (for example a plugin class with a fixture), see #2270. + if hasattr(fixturefunc, "__self__") and not isinstance( + instance, + fixturefunc.__self__.__class__, + ): + return fixturefunc + fixturefunc = getimfunc(fixturedef.func) + if fixturefunc != fixturedef.func: + fixturefunc = fixturefunc.__get__(instance) + return fixturefunc + + +def pytest_fixture_setup( + fixturedef: FixtureDef[FixtureValue], request: SubRequest +) -> FixtureValue: + """Execution of fixture setup.""" + kwargs = {} + for argname in fixturedef.argnames: + kwargs[argname] = request.getfixturevalue(argname) + + fixturefunc = resolve_fixture_function(fixturedef, request) + my_cache_key = fixturedef.cache_key(request) + + if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction( + fixturefunc + ): + auto_str = " with autouse=True" if fixturedef._autouse else "" + + warnings.warn( + PytestRemovedIn9Warning( + f"{request.node.name!r} requested an async fixture " + f"{request.fixturename!r}{auto_str}, with no plugin or hook that " + "handled it. This is usually an error, as pytest does not natively " + "support it. " + "This will turn into an error in pytest 9.\n" + "See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture" + ), + # no stacklevel will point at users code, so we just point here + stacklevel=1, + ) + + try: + result = call_fixture_func(fixturefunc, request, kwargs) + except TEST_OUTCOME as e: + if isinstance(e, skip.Exception): + # The test requested a fixture which caused a skip. + # Don't show the fixture as the skip location, as then the user + # wouldn't know which test skipped. + e._use_item_location = True + fixturedef.cached_result = (None, my_cache_key, (e, e.__traceback__)) + raise + fixturedef.cached_result = (result, my_cache_key, None) + return result + + +@final +@dataclasses.dataclass(frozen=True) +class FixtureFunctionMarker: + scope: _ScopeName | Callable[[str, Config], _ScopeName] + params: tuple[object, ...] | None + autouse: bool = False + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None + name: str | None = None + + _ispytest: dataclasses.InitVar[bool] = False + + def __post_init__(self, _ispytest: bool) -> None: + check_ispytest(_ispytest) + + def __call__(self, function: FixtureFunction) -> FixtureFunctionDefinition: + if inspect.isclass(function): + raise ValueError("class fixtures not supported (maybe in the future)") + + if isinstance(function, FixtureFunctionDefinition): + raise ValueError( + f"@pytest.fixture is being applied more than once to the same function {function.__name__!r}" + ) + + if hasattr(function, "pytestmark"): + warnings.warn(MARKED_FIXTURE, stacklevel=2) + + fixture_definition = FixtureFunctionDefinition( + function=function, fixture_function_marker=self, _ispytest=True + ) + + name = self.name or function.__name__ + if name == "request": + location = getlocation(function) + fail( + f"'request' is a reserved word for fixtures, use another name:\n {location}", + pytrace=False, + ) + + return fixture_definition + + +# TODO: paramspec/return type annotation tracking and storing +class FixtureFunctionDefinition: + def __init__( + self, + *, + function: Callable[..., Any], + fixture_function_marker: FixtureFunctionMarker, + instance: object | None = None, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self.name = fixture_function_marker.name or function.__name__ + # In order to show the function that this fixture contains in messages. + # Set the __name__ to be same as the function __name__ or the given fixture name. + self.__name__ = self.name + self._fixture_function_marker = fixture_function_marker + if instance is not None: + self._fixture_function = cast( + Callable[..., Any], function.__get__(instance) + ) + else: + self._fixture_function = function + functools.update_wrapper(self, function) + + def __repr__(self) -> str: + return f"" + + def __get__(self, instance, owner=None): + """Behave like a method if the function it was applied to was a method.""" + return FixtureFunctionDefinition( + function=self._fixture_function, + fixture_function_marker=self._fixture_function_marker, + instance=instance, + _ispytest=True, + ) + + def __call__(self, *args: Any, **kwds: Any) -> Any: + message = ( + f'Fixture "{self.name}" called directly. Fixtures are not meant to be called directly,\n' + "but are created automatically when test functions request them as parameters.\n" + "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n" + "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly" + ) + fail(message, pytrace=False) + + def _get_wrapped_function(self) -> Callable[..., Any]: + return self._fixture_function + + +@overload +def fixture( + fixture_function: Callable[..., object], + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., + params: Iterable[object] | None = ..., + autouse: bool = ..., + ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., + name: str | None = ..., +) -> FixtureFunctionDefinition: ... + + +@overload +def fixture( + fixture_function: None = ..., + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., + params: Iterable[object] | None = ..., + autouse: bool = ..., + ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., + name: str | None = None, +) -> FixtureFunctionMarker: ... + + +def fixture( + fixture_function: FixtureFunction | None = None, + *, + scope: _ScopeName | Callable[[str, Config], _ScopeName] = "function", + params: Iterable[object] | None = None, + autouse: bool = False, + ids: Sequence[object | None] | Callable[[Any], object | None] | None = None, + name: str | None = None, +) -> FixtureFunctionMarker | FixtureFunctionDefinition: + """Decorator to mark a fixture factory function. + + This decorator can be used, with or without parameters, to define a + fixture function. + + The name of the fixture function can later be referenced to cause its + invocation ahead of running tests: test modules or classes can use the + ``pytest.mark.usefixtures(fixturename)`` marker. + + Test functions can directly use fixture names as input arguments in which + case the fixture instance returned from the fixture function will be + injected. + + Fixtures can provide their values to test functions using ``return`` or + ``yield`` statements. When using ``yield`` the code block after the + ``yield`` statement is executed as teardown code regardless of the test + outcome, and must yield exactly once. + + :param scope: + The scope for which this fixture is shared; one of ``"function"`` + (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. + + This parameter may also be a callable which receives ``(fixture_name, config)`` + as parameters, and must return a ``str`` with one of the values mentioned above. + + See :ref:`dynamic scope` in the docs for more information. + + :param params: + An optional list of parameters which will cause multiple invocations + of the fixture function and all of the tests using it. The current + parameter is available in ``request.param``. + + :param autouse: + If True, the fixture func is activated for all tests that can see it. + If False (the default), an explicit reference is needed to activate + the fixture. + + :param ids: + Sequence of ids each corresponding to the params so that they are + part of the test id. If no ids are provided they will be generated + automatically from the params. + + :param name: + The name of the fixture. This defaults to the name of the decorated + function. If a fixture is used in the same module in which it is + defined, the function name of the fixture will be shadowed by the + function arg that requests the fixture; one way to resolve this is to + name the decorated function ``fixture_`` and then use + ``@pytest.fixture(name='')``. + """ + fixture_marker = FixtureFunctionMarker( + scope=scope, + params=tuple(params) if params is not None else None, + autouse=autouse, + ids=None if ids is None else ids if callable(ids) else tuple(ids), + name=name, + _ispytest=True, + ) + + # Direct decoration. + if fixture_function: + return fixture_marker(fixture_function) + + return fixture_marker + + +def yield_fixture( + fixture_function=None, + *args, + scope="function", + params=None, + autouse=False, + ids=None, + name=None, +): + """(Return a) decorator to mark a yield-fixture factory function. + + .. deprecated:: 3.0 + Use :py:func:`pytest.fixture` directly instead. + """ + warnings.warn(YIELD_FIXTURE, stacklevel=2) + return fixture( + fixture_function, + *args, + scope=scope, + params=params, + autouse=autouse, + ids=ids, + name=name, + ) + + +@fixture(scope="session") +def pytestconfig(request: FixtureRequest) -> Config: + """Session-scoped fixture that returns the session's :class:`pytest.Config` + object. + + Example:: + + def test_foo(pytestconfig): + if pytestconfig.get_verbosity() > 0: + ... + + """ + return request.config + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "usefixtures", + type="args", + default=[], + help="List of default fixtures to be used with this project", + ) + group = parser.getgroup("general") + group.addoption( + "--fixtures", + "--funcargs", + action="store_true", + dest="showfixtures", + default=False, + help="Show available fixtures, sorted by plugin appearance " + "(fixtures with leading '_' are only shown with '-v')", + ) + group.addoption( + "--fixtures-per-test", + action="store_true", + dest="show_fixtures_per_test", + default=False, + help="Show fixtures per test", + ) + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.showfixtures: + showfixtures(config) + return 0 + if config.option.show_fixtures_per_test: + show_fixtures_per_test(config) + return 0 + return None + + +def _get_direct_parametrize_args(node: nodes.Node) -> set[str]: + """Return all direct parametrization arguments of a node, so we don't + mistake them for fixtures. + + Check https://github.com/pytest-dev/pytest/issues/5036. + + These things are done later as well when dealing with parametrization + so this could be improved. + """ + parametrize_argnames: set[str] = set() + for marker in node.iter_markers(name="parametrize"): + if not marker.kwargs.get("indirect", False): + p_argnames, _ = ParameterSet._parse_parametrize_args( + *marker.args, **marker.kwargs + ) + parametrize_argnames.update(p_argnames) + return parametrize_argnames + + +def deduplicate_names(*seqs: Iterable[str]) -> tuple[str, ...]: + """De-duplicate the sequence of names while keeping the original order.""" + # Ideally we would use a set, but it does not preserve insertion order. + return tuple(dict.fromkeys(name for seq in seqs for name in seq)) + + +class FixtureManager: + """pytest fixture definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - config-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i.e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + + def __init__(self, session: Session) -> None: + self.session = session + self.config: Config = session.config + # Maps a fixture name (argname) to all of the FixtureDefs in the test + # suite/plugins defined with this name. Populated by parsefactories(). + # TODO: The order of the FixtureDefs list of each arg is significant, + # explain. + self._arg2fixturedefs: Final[dict[str, list[FixtureDef[Any]]]] = {} + self._holderobjseen: Final[set[object]] = set() + # A mapping from a nodeid to a list of autouse fixtures it defines. + self._nodeid_autousenames: Final[dict[str, list[str]]] = { + "": self.config.getini("usefixtures"), + } + session.config.pluginmanager.register(self, "funcmanage") + + def getfixtureinfo( + self, + node: nodes.Item, + func: Callable[..., object] | None, + cls: type | None, + ) -> FuncFixtureInfo: + """Calculate the :class:`FuncFixtureInfo` for an item. + + If ``func`` is None, or if the item sets an attribute + ``nofuncargs = True``, then ``func`` is not examined at all. + + :param node: + The item requesting the fixtures. + :param func: + The item's function. + :param cls: + If the function is a method, the method's class. + """ + if func is not None and not getattr(node, "nofuncargs", False): + argnames = getfuncargnames(func, name=node.name, cls=cls) + else: + argnames = () + usefixturesnames = self._getusefixturesnames(node) + autousenames = self._getautousenames(node) + initialnames = deduplicate_names(autousenames, usefixturesnames, argnames) + + direct_parametrize_args = _get_direct_parametrize_args(node) + + names_closure, arg2fixturedefs = self.getfixtureclosure( + parentnode=node, + initialnames=initialnames, + ignore_args=direct_parametrize_args, + ) + + return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin, plugin_name: str) -> None: + # Fixtures defined in conftest plugins are only visible to within the + # conftest's directory. This is unlike fixtures in non-conftest plugins + # which have global visibility. So for conftests, construct the base + # nodeid from the plugin name (which is the conftest path). + if plugin_name and plugin_name.endswith("conftest.py"): + # Note: we explicitly do *not* use `plugin.__file__` here -- The + # difference is that plugin_name has the correct capitalization on + # case-insensitive systems (Windows) and other normalization issues + # (issue #11816). + conftestpath = absolutepath(plugin_name) + try: + nodeid = str(conftestpath.parent.relative_to(self.config.rootpath)) + except ValueError: + nodeid = "" + if nodeid == ".": + nodeid = "" + if os.sep != nodes.SEP: + nodeid = nodeid.replace(os.sep, nodes.SEP) + else: + nodeid = None + + self.parsefactories(plugin, nodeid) + + def _getautousenames(self, node: nodes.Node) -> Iterator[str]: + """Return the names of autouse fixtures applicable to node.""" + for parentnode in node.listchain(): + basenames = self._nodeid_autousenames.get(parentnode.nodeid) + if basenames: + yield from basenames + + def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]: + """Return the names of usefixtures fixtures applicable to node.""" + for marker_node, mark in node.iter_markers_with_node(name="usefixtures"): + if not mark.args: + marker_node.warn( + PytestWarning( + f"usefixtures() in {node.nodeid} without arguments has no effect" + ) + ) + yield from mark.args + + def getfixtureclosure( + self, + parentnode: nodes.Node, + initialnames: tuple[str, ...], + ignore_args: AbstractSet[str], + ) -> tuple[list[str], dict[str, Sequence[FixtureDef[Any]]]]: + # Collect the closure of all fixtures, starting with the given + # fixturenames as the initial set. As we have to visit all + # factory definitions anyway, we also return an arg2fixturedefs + # mapping so that the caller can reuse it and does not have + # to re-discover fixturedefs again for each fixturename + # (discovering matching fixtures for a given name/node is expensive). + + fixturenames_closure = list(initialnames) + + arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] = {} + + # Track the index for each fixture name in the simulated stack. + # Needed for handling override chains correctly, similar to _get_active_fixturedef. + # Using negative indices: -1 is the most specific (last), -2 is second to last, etc. + current_indices: dict[str, int] = {} + + def process_argname(argname: str) -> None: + # Optimization: already processed this argname. + if current_indices.get(argname) == -1: + return + + if argname not in fixturenames_closure: + fixturenames_closure.append(argname) + + if argname in ignore_args: + return + + fixturedefs = arg2fixturedefs.get(argname) + if not fixturedefs: + fixturedefs = self.getfixturedefs(argname, parentnode) + if not fixturedefs: + # Fixture not defined or not visible (will error during runtest). + return + arg2fixturedefs[argname] = fixturedefs + + index = current_indices.get(argname, -1) + if -index > len(fixturedefs): + # Exhausted the override chain (will error during runtest). + return + fixturedef = fixturedefs[index] + + current_indices[argname] = index - 1 + for dep in fixturedef.argnames: + process_argname(dep) + current_indices[argname] = index + + for name in initialnames: + process_argname(name) + + def sort_by_scope(arg_name: str) -> Scope: + try: + fixturedefs = arg2fixturedefs[arg_name] + except KeyError: + return Scope.Function + else: + return fixturedefs[-1]._scope + + fixturenames_closure.sort(key=sort_by_scope, reverse=True) + return fixturenames_closure, arg2fixturedefs + + def pytest_generate_tests(self, metafunc: Metafunc) -> None: + """Generate new tests based on parametrized fixtures used by the given metafunc""" + + def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: + args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs) + return args + + for argname in metafunc.fixturenames: + # Get the FixtureDefs for the argname. + fixture_defs = metafunc._arg2fixturedefs.get(argname) + if not fixture_defs: + # Will raise FixtureLookupError at setup time if not parametrized somewhere + # else (e.g @pytest.mark.parametrize) + continue + + # If the test itself parametrizes using this argname, give it + # precedence. + if any( + argname in get_parametrize_mark_argnames(mark) + for mark in metafunc.definition.iter_markers("parametrize") + ): + continue + + # In the common case we only look at the fixture def with the + # closest scope (last in the list). But if the fixture overrides + # another fixture, while requesting the super fixture, keep going + # in case the super fixture is parametrized (#1953). + for fixturedef in reversed(fixture_defs): + # Fixture is parametrized, apply it and stop. + if fixturedef.params is not None: + metafunc.parametrize( + argname, + fixturedef.params, + indirect=True, + scope=fixturedef.scope, + ids=fixturedef.ids, + ) + break + + # Not requesting the overridden super fixture, stop. + if argname not in fixturedef.argnames: + break + + # Try next super fixture, if any. + + def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> None: + # Separate parametrized setups. + items[:] = reorder_items(items) + + def _register_fixture( + self, + *, + name: str, + func: _FixtureFunc[object], + nodeid: str | None, + scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] = "function", + params: Sequence[object] | None = None, + ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, + autouse: bool = False, + ) -> None: + """Register a fixture + + :param name: + The fixture's name. + :param func: + The fixture's implementation function. + :param nodeid: + The visibility of the fixture. The fixture will be available to the + node with this nodeid and its children in the collection tree. + None means that the fixture is visible to the entire collection tree, + e.g. a fixture defined for general use in a plugin. + :param scope: + The fixture's scope. + :param params: + The fixture's parametrization params. + :param ids: + The fixture's IDs. + :param autouse: + Whether this is an autouse fixture. + """ + fixture_def = FixtureDef( + config=self.config, + baseid=nodeid, + argname=name, + func=func, + scope=scope, + params=params, + ids=ids, + _ispytest=True, + _autouse=autouse, + ) + + faclist = self._arg2fixturedefs.setdefault(name, []) + if fixture_def.has_location: + faclist.append(fixture_def) + else: + # fixturedefs with no location are at the front + # so this inserts the current fixturedef after the + # existing fixturedefs from external plugins but + # before the fixturedefs provided in conftests. + i = len([f for f in faclist if not f.has_location]) + faclist.insert(i, fixture_def) + if autouse: + self._nodeid_autousenames.setdefault(nodeid or "", []).append(name) + + @overload + def parsefactories( + self, + node_or_obj: nodes.Node, + ) -> None: + raise NotImplementedError() + + @overload + def parsefactories( + self, + node_or_obj: object, + nodeid: str | None, + ) -> None: + raise NotImplementedError() + + def parsefactories( + self, + node_or_obj: nodes.Node | object, + nodeid: str | NotSetType | None = NOTSET, + ) -> None: + """Collect fixtures from a collection node or object. + + Found fixtures are parsed into `FixtureDef`s and saved. + + If `node_or_object` is a collection node (with an underlying Python + object), the node's object is traversed and the node's nodeid is used to + determine the fixtures' visibility. `nodeid` must not be specified in + this case. + + If `node_or_object` is an object (e.g. a plugin), the object is + traversed and the given `nodeid` is used to determine the fixtures' + visibility. `nodeid` must be specified in this case; None and "" mean + total visibility. + """ + if nodeid is not NOTSET: + holderobj = node_or_obj + else: + assert isinstance(node_or_obj, nodes.Node) + holderobj = cast(object, node_or_obj.obj) # type: ignore[attr-defined] + assert isinstance(node_or_obj.nodeid, str) + nodeid = node_or_obj.nodeid + if holderobj in self._holderobjseen: + return + + # Avoid accessing `@property` (and other descriptors) when iterating fixtures. + if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType): + holderobj_tp: object = type(holderobj) + else: + holderobj_tp = holderobj + + self._holderobjseen.add(holderobj) + for name in dir(holderobj): + # The attribute can be an arbitrary descriptor, so the attribute + # access below can raise. safe_getattr() ignores such exceptions. + obj_ub = safe_getattr(holderobj_tp, name, None) + if type(obj_ub) is FixtureFunctionDefinition: + marker = obj_ub._fixture_function_marker + if marker.name: + fixture_name = marker.name + else: + fixture_name = name + + # OK we know it is a fixture -- now safe to look up on the _instance_. + try: + obj = getattr(holderobj, name) + # if the fixture is named in the decorator we cannot find it in the module + except AttributeError: + obj = obj_ub + + func = obj._get_wrapped_function() + + self._register_fixture( + name=fixture_name, + nodeid=nodeid, + func=func, + scope=marker.scope, + params=marker.params, + ids=marker.ids, + autouse=marker.autouse, + ) + + def getfixturedefs( + self, argname: str, node: nodes.Node + ) -> Sequence[FixtureDef[Any]] | None: + """Get FixtureDefs for a fixture name which are applicable + to a given node. + + Returns None if there are no fixtures at all defined with the given + name. (This is different from the case in which there are fixtures + with the given name, but none applicable to the node. In this case, + an empty result is returned). + + :param argname: Name of the fixture to search for. + :param node: The requesting Node. + """ + try: + fixturedefs = self._arg2fixturedefs[argname] + except KeyError: + return None + return tuple(self._matchfactories(fixturedefs, node)) + + def _matchfactories( + self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node + ) -> Iterator[FixtureDef[Any]]: + parentnodeids = {n.nodeid for n in node.iter_parents()} + for fixturedef in fixturedefs: + if fixturedef.baseid in parentnodeids: + yield fixturedef + + +def show_fixtures_per_test(config: Config) -> int | ExitCode: + from _pytest.main import wrap_session + + return wrap_session(config, _show_fixtures_per_test) + + +_PYTEST_DIR = Path(_pytest.__file__).parent + + +def _pretty_fixture_path(invocation_dir: Path, func) -> str: + loc = Path(getlocation(func, invocation_dir)) + prefix = Path("...", "_pytest") + try: + return str(prefix / loc.relative_to(_PYTEST_DIR)) + except ValueError: + return bestrelpath(invocation_dir, loc) + + +def _show_fixtures_per_test(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + invocation_dir = config.invocation_params.dir + tw = _pytest.config.create_terminal_writer(config) + verbose = config.get_verbosity() + + def get_best_relpath(func) -> str: + loc = getlocation(func, invocation_dir) + return bestrelpath(invocation_dir, Path(loc)) + + def write_fixture(fixture_def: FixtureDef[object]) -> None: + argname = fixture_def.argname + if verbose <= 0 and argname.startswith("_"): + return + prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func) + tw.write(f"{argname}", green=True) + tw.write(f" -- {prettypath}", yellow=True) + tw.write("\n") + fixture_doc = inspect.getdoc(fixture_def.func) + if fixture_doc: + write_docstring( + tw, + fixture_doc.split("\n\n", maxsplit=1)[0] + if verbose <= 0 + else fixture_doc, + ) + else: + tw.line(" no docstring available", red=True) + + def write_item(item: nodes.Item) -> None: + # Not all items have _fixtureinfo attribute. + info: FuncFixtureInfo | None = getattr(item, "_fixtureinfo", None) + if info is None or not info.name2fixturedefs: + # This test item does not use any fixtures. + return + tw.line() + tw.sep("-", f"fixtures used by {item.name}") + # TODO: Fix this type ignore. + tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] + # dict key not used in loop but needed for sorting. + for _, fixturedefs in sorted(info.name2fixturedefs.items()): + assert fixturedefs is not None + if not fixturedefs: + continue + # Last item is expected to be the one used by the test item. + write_fixture(fixturedefs[-1]) + + for session_item in session.items: + write_item(session_item) + + +def showfixtures(config: Config) -> int | ExitCode: + from _pytest.main import wrap_session + + return wrap_session(config, _showfixtures_main) + + +def _showfixtures_main(config: Config, session: Session) -> None: + import _pytest.config + + session.perform_collect() + invocation_dir = config.invocation_params.dir + tw = _pytest.config.create_terminal_writer(config) + verbose = config.get_verbosity() + + fm = session._fixturemanager + + available = [] + seen: set[tuple[str, str]] = set() + + for argname, fixturedefs in fm._arg2fixturedefs.items(): + assert fixturedefs is not None + if not fixturedefs: + continue + for fixturedef in fixturedefs: + loc = getlocation(fixturedef.func, invocation_dir) + if (fixturedef.argname, loc) in seen: + continue + seen.add((fixturedef.argname, loc)) + available.append( + ( + len(fixturedef.baseid), + fixturedef.func.__module__, + _pretty_fixture_path(invocation_dir, fixturedef.func), + fixturedef.argname, + fixturedef, + ) + ) + + available.sort() + currentmodule = None + for baseid, module, prettypath, argname, fixturedef in available: + if currentmodule != module: + if not module.startswith("_pytest."): + tw.line() + tw.sep("-", f"fixtures defined from {module}") + currentmodule = module + if verbose <= 0 and argname.startswith("_"): + continue + tw.write(f"{argname}", green=True) + if fixturedef.scope != "function": + tw.write(f" [{fixturedef.scope} scope]", cyan=True) + tw.write(f" -- {prettypath}", yellow=True) + tw.write("\n") + doc = inspect.getdoc(fixturedef.func) + if doc: + write_docstring( + tw, doc.split("\n\n", maxsplit=1)[0] if verbose <= 0 else doc + ) + else: + tw.line(" no docstring available", red=True) + tw.line() + + +def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: + for line in doc.split("\n"): + tw.line(indent + line) diff --git a/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py b/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py new file mode 100644 index 0000000..959ff07 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py @@ -0,0 +1,45 @@ +"""Provides a function to report all internal modules for using freezing +tools.""" + +from __future__ import annotations + +from collections.abc import Iterator +import types + + +def freeze_includes() -> list[str]: + """Return a list of module names used by pytest that should be + included by cx_freeze.""" + import _pytest + + result = list(_iter_all_modules(_pytest)) + return result + + +def _iter_all_modules( + package: str | types.ModuleType, + prefix: str = "", +) -> Iterator[str]: + """Iterate over the names of all modules that can be found in the given + package, recursively. + + >>> import _pytest + >>> list(_iter_all_modules(_pytest)) + ['_pytest._argcomplete', '_pytest._code.code', ...] + """ + import os + import pkgutil + + if isinstance(package, str): + path = package + else: + # Type ignored because typeshed doesn't define ModuleType.__path__ + # (only defined on packages). + package_path = package.__path__ + path, prefix = package_path[0], package.__name__ + "." + for _, name, is_package in pkgutil.iter_modules([path]): + if is_package: + for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): + yield prefix + m + else: + yield prefix + name diff --git a/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py b/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py new file mode 100644 index 0000000..6a22c9f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py @@ -0,0 +1,293 @@ +# mypy: allow-untyped-defs +"""Version info, help messages, tracing configuration.""" + +from __future__ import annotations + +import argparse +from collections.abc import Generator +from collections.abc import Sequence +import os +import sys +from typing import Any + +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import PrintHelp +from _pytest.config.argparsing import Parser +from _pytest.terminal import TerminalReporter +import pytest + + +class HelpAction(argparse.Action): + """An argparse Action that will raise a PrintHelp exception in order to skip + the rest of the argument parsing when --help is passed. + + This prevents argparse from raising UsageError when `--help` is used along + with missing required arguments when any are defined, for example by + ``pytest_addoption``. This is similar to the way that the builtin argparse + --help option is implemented by raising SystemExit. + + To opt in to this behavior, the parse caller must set + `namespace._raise_print_help = True`. Otherwise it just sets the option. + """ + + def __init__( + self, option_strings: Sequence[str], dest: str, *, help: str | None = None + ) -> None: + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=True, + default=False, + help=help, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: str | Sequence[Any] | None, + option_string: str | None = None, + ) -> None: + setattr(namespace, self.dest, self.const) + + if getattr(namespace, "_raise_print_help", False): + raise PrintHelp + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--version", + "-V", + action="count", + default=0, + dest="version", + help="Display pytest version and information about plugins. " + "When given twice, also display information about plugins.", + ) + group._addoption( # private to use reserved lower-case short option + "-h", + "--help", + action=HelpAction, + dest="help", + help="Show help message and configuration info", + ) + group._addoption( # private to use reserved lower-case short option + "-p", + action="append", + dest="plugins", + default=[], + metavar="name", + help="Early-load given plugin module name or entry point (multi-allowed). " + "To avoid loading of plugins, use the `no:` prefix, e.g. " + "`no:doctest`. See also --disable-plugin-autoload.", + ) + group.addoption( + "--disable-plugin-autoload", + action="store_true", + default=False, + help="Disable plugin auto-loading through entry point packaging metadata. " + "Only plugins explicitly specified in -p or env var PYTEST_PLUGINS will be loaded.", + ) + group.addoption( + "--traceconfig", + "--trace-config", + action="store_true", + default=False, + help="Trace considerations of conftest.py files", + ) + group.addoption( + "--debug", + action="store", + nargs="?", + const="pytestdebug.log", + dest="debug", + metavar="DEBUG_FILE_NAME", + help="Store internal tracing debug information in this log file. " + "This file is opened with 'w' and truncated as a result, care advised. " + "Default: pytestdebug.log.", + ) + group._addoption( # private to use reserved lower-case short option + "-o", + "--override-ini", + dest="override_ini", + action="append", + help='Override configuration option with "option=value" style, ' + "e.g. `-o strict_xfail=True -o cache_dir=cache`.", + ) + + +@pytest.hookimpl(wrapper=True) +def pytest_cmdline_parse() -> Generator[None, Config, Config]: + config = yield + + if config.option.debug: + # --debug | --debug was provided. + path = config.option.debug + debugfile = open(path, "w", encoding="utf-8") + debugfile.write( + "versions pytest-{}, " + "python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format( + pytest.__version__, + ".".join(map(str, sys.version_info)), + config.invocation_params.dir, + os.getcwd(), + config.invocation_params.args, + ) + ) + config.trace.root.setwriter(debugfile.write) + undo_tracing = config.pluginmanager.enable_tracing() + sys.stderr.write(f"writing pytest debug information to {path}\n") + + def unset_tracing() -> None: + debugfile.close() + sys.stderr.write(f"wrote pytest debug information to {debugfile.name}\n") + config.trace.root.setwriter(None) + undo_tracing() + + config.add_cleanup(unset_tracing) + + return config + + +def show_version_verbose(config: Config) -> None: + """Show verbose pytest version installation, including plugins.""" + sys.stdout.write( + f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n" + ) + plugininfo = getpluginversioninfo(config) + if plugininfo: + for line in plugininfo: + sys.stdout.write(line + "\n") + + +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + # Note: a single `--version` argument is handled directly by `Config.main()` to avoid starting up the entire + # pytest infrastructure just to display the version (#13574). + if config.option.version > 1: + show_version_verbose(config) + return ExitCode.OK + elif config.option.help: + config._do_configure() + showhelp(config) + config._ensure_unconfigure() + return ExitCode.OK + return None + + +def showhelp(config: Config) -> None: + import textwrap + + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + "terminalreporter" + ) + assert reporter is not None + tw = reporter._tw + tw.write(config._parser.optparser.format_help()) + tw.line() + tw.line( + "[pytest] configuration options in the first " + "pytest.toml|pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:" + ) + tw.line() + + columns = tw.fullwidth # costly call + indent_len = 24 # based on argparse's max_help_position=24 + indent = " " * indent_len + for name in config._parser._inidict: + help, type, _default = config._parser._inidict[name] + if help is None: + raise TypeError(f"help argument cannot be None for {name}") + spec = f"{name} ({type}):" + tw.write(f" {spec}") + spec_len = len(spec) + if spec_len > (indent_len - 3): + # Display help starting at a new line. + tw.line() + helplines = textwrap.wrap( + help, + columns, + initial_indent=indent, + subsequent_indent=indent, + break_on_hyphens=False, + ) + + for line in helplines: + tw.line(line) + else: + # Display help starting after the spec, following lines indented. + tw.write(" " * (indent_len - spec_len - 2)) + wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) + + if wrapped: + tw.line(wrapped[0]) + for line in wrapped[1:]: + tw.line(indent + line) + + tw.line() + tw.line("Environment variables:") + vars = [ + ( + "CI", + "When set to a non-empty value, pytest knows it is running in a " + "CI process and does not truncate summary info", + ), + ("BUILD_NUMBER", "Equivalent to CI"), + ("PYTEST_ADDOPTS", "Extra command line options"), + ("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"), + ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"), + ("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"), + ("PYTEST_DEBUG_TEMPROOT", "Override the system temporary directory"), + ("PYTEST_THEME", "The Pygments style to use for code output"), + ("PYTEST_THEME_MODE", "Set the PYTEST_THEME to be either 'dark' or 'light'"), + ] + for name, help in vars: + tw.line(f" {name:<24} {help}") + tw.line() + tw.line() + + tw.line("to see available markers type: pytest --markers") + tw.line("to see available fixtures type: pytest --fixtures") + tw.line( + "(shown according to specified file_or_dir or current dir " + "if not specified; fixtures with leading '_' are only shown " + "with the '-v' option" + ) + + for warningreport in reporter.stats.get("warnings", []): + tw.line("warning : " + warningreport.message, red=True) + + +def getpluginversioninfo(config: Config) -> list[str]: + lines = [] + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + lines.append("registered third-party plugins:") + for plugin, dist in plugininfo: + loc = getattr(plugin, "__file__", repr(plugin)) + content = f"{dist.project_name}-{dist.version} at {loc}" + lines.append(" " + content) + return lines + + +def pytest_report_header(config: Config) -> list[str]: + lines = [] + if config.option.debug or config.option.traceconfig: + lines.append(f"using: pytest-{pytest.__version__}") + + verinfo = getpluginversioninfo(config) + if verinfo: + lines.extend(verinfo) + + if config.option.traceconfig: + lines.append("active plugins:") + items = config.pluginmanager.list_name_plugin() + for name, plugin in items: + if hasattr(plugin, "__file__"): + r = plugin.__file__ + else: + r = repr(plugin) + lines.append(f" {name:<20}: {r}") + return lines diff --git a/.venv/lib/python3.12/site-packages/_pytest/hookspec.py b/.venv/lib/python3.12/site-packages/_pytest/hookspec.py new file mode 100644 index 0000000..dab3fb6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/hookspec.py @@ -0,0 +1,1342 @@ +# mypy: allow-untyped-defs +# ruff: noqa: T100 +"""Hook specifications for pytest plugins which are invoked by pytest itself +and by builtin plugins.""" + +from __future__ import annotations + +from collections.abc import Mapping +from collections.abc import Sequence +from pathlib import Path +from typing import Any +from typing import TYPE_CHECKING + +from pluggy import HookspecMarker + +from .deprecated import HOOK_LEGACY_PATH_ARG + + +if TYPE_CHECKING: + import pdb + from typing import Literal + import warnings + + from _pytest._code.code import ExceptionInfo + from _pytest._code.code import ExceptionRepr + from _pytest.compat import LEGACY_PATH + from _pytest.config import _PluggyPlugin + from _pytest.config import Config + from _pytest.config import ExitCode + from _pytest.config import PytestPluginManager + from _pytest.config.argparsing import Parser + from _pytest.fixtures import FixtureDef + from _pytest.fixtures import SubRequest + from _pytest.main import Session + from _pytest.nodes import Collector + from _pytest.nodes import Item + from _pytest.outcomes import Exit + from _pytest.python import Class + from _pytest.python import Function + from _pytest.python import Metafunc + from _pytest.python import Module + from _pytest.reports import CollectReport + from _pytest.reports import TestReport + from _pytest.runner import CallInfo + from _pytest.terminal import TerminalReporter + from _pytest.terminal import TestShortLogReport + + +hookspec = HookspecMarker("pytest") + +# ------------------------------------------------------------------------- +# Initialization hooks called for every plugin +# ------------------------------------------------------------------------- + + +@hookspec(historic=True) +def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: + """Called at plugin registration time to allow adding new hooks via a call to + :func:`pluginmanager.add_hookspecs(module_or_class, prefix) `. + + :param pluginmanager: The pytest plugin manager. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered. + """ + + +@hookspec(historic=True) +def pytest_plugin_registered( + plugin: _PluggyPlugin, + plugin_name: str, + manager: PytestPluginManager, +) -> None: + """A new pytest plugin got registered. + + :param plugin: The plugin module or instance. + :param plugin_name: The name by which the plugin is registered. + :param manager: The pytest plugin manager. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered, once for each plugin registered thus far + (including itself!), and for all plugins thereafter when they are + registered. + """ + + +@hookspec(historic=True) +def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: + """Register argparse-style options and config-style config values, + called once at the beginning of a test run. + + :param parser: + To add command line options, call + :py:func:`parser.addoption(...) `. + To add config-file values call :py:func:`parser.addini(...) + `. + + :param pluginmanager: + The pytest plugin manager, which can be used to install :py:func:`~pytest.hookspec`'s + or :py:func:`~pytest.hookimpl`'s and allow one plugin to call another plugin's hooks + to change how command line options are added. + + Options can later be accessed through the + :py:class:`config ` object, respectively: + + - :py:func:`config.getoption(name) ` to + retrieve the value of a command line option. + + - :py:func:`config.getini(name) ` to retrieve + a value read from a configuration file. + + The config object is passed around on many internal objects via the ``.config`` + attribute or can be retrieved as the ``pytestconfig`` fixture. + + .. note:: + This hook is incompatible with hook wrappers. + + Use in conftest plugins + ======================= + + If a conftest plugin implements this hook, it will be called immediately + when the conftest is registered. + + This hook is only called for :ref:`initial conftests `. + """ + + +@hookspec(historic=True) +def pytest_configure(config: Config) -> None: + """Allow plugins and conftest files to perform initial configuration. + + .. note:: + This hook is incompatible with hook wrappers. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + This hook is called for every :ref:`initial conftest ` file + after command line options have been parsed. After that, the hook is called + for other conftest files as they are registered. + """ + + +# ------------------------------------------------------------------------- +# Bootstrapping hooks called for plugins registered early enough: +# internal and 3rd party plugins. +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_cmdline_parse( + pluginmanager: PytestPluginManager, args: list[str] +) -> Config | None: + """Return an initialized :class:`~pytest.Config`, parsing the specified args. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + This hook is only called for plugin classes passed to the + ``plugins`` arg when using `pytest.main`_ to perform an in-process + test run. + + :param pluginmanager: The pytest plugin manager. + :param args: List of arguments passed on the command line. + :returns: A pytest config object. + + Use in conftest plugins + ======================= + + This hook is not called for conftest files. + """ + + +def pytest_load_initial_conftests( + early_config: Config, parser: Parser, args: list[str] +) -> None: + """Called to implement the loading of :ref:`initial conftest files + ` ahead of command line option parsing. + + :param early_config: The pytest config object. + :param args: Arguments passed on the command line. + :param parser: To add command line options. + + Use in conftest plugins + ======================= + + This hook is not called for conftest files. + """ + + +@hookspec(firstresult=True) +def pytest_cmdline_main(config: Config) -> ExitCode | int | None: + """Called for performing the main command line action. + + The default implementation will invoke the configure hooks and + :hook:`pytest_runtestloop`. + + Stops at first non-None result, see :ref:`firstresult`. + + :param config: The pytest config object. + :returns: The exit code. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +# ------------------------------------------------------------------------- +# collection hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_collection(session: Session) -> object | None: + """Perform the collection phase for the given session. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + The default collection phase is this (see individual hooks for full details): + + 1. Starting from ``session`` as the initial collector: + + 1. ``pytest_collectstart(collector)`` + 2. ``report = pytest_make_collect_report(collector)`` + 3. ``pytest_exception_interact(collector, call, report)`` if an interactive exception occurred + 4. For each collected node: + + 1. If an item, ``pytest_itemcollected(item)`` + 2. If a collector, recurse into it. + + 5. ``pytest_collectreport(report)`` + + 2. ``pytest_collection_modifyitems(session, config, items)`` + + 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) + + 3. Set ``session.items`` to the list of collected items + 4. ``pytest_collection_finish(session)`` + 5. Set ``session.testscollected`` to the number of collected items + + You can implement this hook to only perform some action before collection, + for example the terminal plugin uses it to start displaying the collection + counter (and returns `None`). + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +def pytest_collection_modifyitems( + session: Session, config: Config, items: list[Item] +) -> None: + """Called after collection has been performed. May filter or re-order + the items in-place. + + When items are deselected (filtered out from ``items``), + the hook :hook:`pytest_deselected` must be called explicitly + with the deselected items to properly notify other plugins, + e.g. with ``config.hook.pytest_deselected(items=deselected_items)``. + + :param session: The pytest session object. + :param config: The pytest config object. + :param items: List of item objects. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_collection_finish(session: Session) -> None: + """Called after collection has been performed and modified. + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec( + firstresult=True, + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="collection_path" + ), + }, +) +def pytest_ignore_collect( + collection_path: Path, path: LEGACY_PATH, config: Config +) -> bool | None: + """Return ``True`` to ignore this path for collection. + + Return ``None`` to let other plugins ignore the path for collection. + + Returning ``False`` will forcefully *not* ignore this path for collection, + without giving a chance for other plugins to ignore this path. + + This hook is consulted for all files and directories prior to calling + more specific hooks. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collection_path: The path to analyze. + :type collection_path: pathlib.Path + :param path: The path to analyze (deprecated). + :param config: The pytest config object. + + .. versionchanged:: 7.0.0 + The ``collection_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. The ``path`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collection path, only + conftest files in parent directories of the collection path are consulted + (if the path is a directory, its own conftest file is *not* consulted - a + directory cannot ignore itself!). + """ + + +@hookspec(firstresult=True) +def pytest_collect_directory(path: Path, parent: Collector) -> Collector | None: + """Create a :class:`~pytest.Collector` for the given directory, or None if + not relevant. + + .. versionadded:: 8.0 + + For best results, the returned collector should be a subclass of + :class:`~pytest.Directory`, but this is not required. + + The new node needs to have the specified ``parent`` as a parent. + + Stops at first non-None result, see :ref:`firstresult`. + + :param path: The path to analyze. + :type path: pathlib.Path + + See :ref:`custom directory collectors` for a simple example of use of this + hook. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collection path, only + conftest files in parent directories of the collection path are consulted + (if the path is a directory, its own conftest file is *not* consulted - a + directory cannot collect itself!). + """ + + +@hookspec( + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="file_path" + ), + }, +) +def pytest_collect_file( + file_path: Path, path: LEGACY_PATH, parent: Collector +) -> Collector | None: + """Create a :class:`~pytest.Collector` for the given path, or None if not relevant. + + For best results, the returned collector should be a subclass of + :class:`~pytest.File`, but this is not required. + + The new node needs to have the specified ``parent`` as a parent. + + :param file_path: The path to analyze. + :type file_path: pathlib.Path + :param path: The path to collect (deprecated). + + .. versionchanged:: 7.0.0 + The ``file_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. The ``path`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given file path, only + conftest files in parent directories of the file path are consulted. + """ + + +# logging hooks for collection + + +def pytest_collectstart(collector: Collector) -> None: + """Collector starts collecting. + + :param collector: + The collector. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +def pytest_itemcollected(item: Item) -> None: + """We just collected a test item. + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_collectreport(report: CollectReport) -> None: + """Collector finished collecting. + + :param report: + The collect report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +def pytest_deselected(items: Sequence[Item]) -> None: + """Called for deselected test items, e.g. by keyword. + + Note that this hook has two integration aspects for plugins: + + - it can be *implemented* to be notified of deselected items + - it must be *called* from :hook:`pytest_collection_modifyitems` + implementations when items are deselected (to properly notify other plugins). + + May be called multiple times. + + :param items: + The items. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_make_collect_report(collector: Collector) -> CollectReport | None: + """Perform :func:`collector.collect() ` and return + a :class:`~pytest.CollectReport`. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collector: + The collector. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories are + consulted. + """ + + +# ------------------------------------------------------------------------- +# Python test function related hooks +# ------------------------------------------------------------------------- + + +@hookspec( + firstresult=True, + warn_on_impl_args={ + "path": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="path", pathlib_path_arg="module_path" + ), + }, +) +def pytest_pycollect_makemodule( + module_path: Path, path: LEGACY_PATH, parent +) -> Module | None: + """Return a :class:`pytest.Module` collector or None for the given path. + + This hook will be called for each matching test module path. + The :hook:`pytest_collect_file` hook needs to be used if you want to + create test modules for files that do not match as a test module. + + Stops at first non-None result, see :ref:`firstresult`. + + :param module_path: The path of the module to collect. + :type module_path: pathlib.Path + :param path: The path of the module to collect (deprecated). + + .. versionchanged:: 7.0.0 + The ``module_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. + + The ``path`` parameter has been deprecated in favor of ``fspath``. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given parent collector, + only conftest files in the collector's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> None | Item | Collector | list[Item | Collector]: + """Return a custom item/collector for a Python object in a module, or None. + + Stops at first non-None result, see :ref:`firstresult`. + + :param collector: + The module/class collector. + :param name: + The name of the object in the module/class. + :param obj: + The object. + :returns: + The created items/collectors. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given collector, only + conftest files in the collector's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: + """Call underlying test function. + + Stops at first non-None result, see :ref:`firstresult`. + + :param pyfuncitem: + The function item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only + conftest files in the item's directory and its parent directories + are consulted. + """ + + +def pytest_generate_tests(metafunc: Metafunc) -> None: + """Generate (multiple) parametrized calls to a test function. + + :param metafunc: + The :class:`~pytest.Metafunc` helper for the test function. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given function definition, + only conftest files in the functions's directory and its parent directories + are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_make_parametrize_id(config: Config, val: object, argname: str) -> str | None: + """Return a user-friendly string representation of the given ``val`` + that will be used by @pytest.mark.parametrize calls, or None if the hook + doesn't know about ``val``. + + The parameter name is available as ``argname``, if required. + + Stops at first non-None result, see :ref:`firstresult`. + + :param config: The pytest config object. + :param val: The parametrized value. + :param argname: The automatic parameter name produced by pytest. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +# ------------------------------------------------------------------------- +# runtest related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_runtestloop(session: Session) -> object | None: + """Perform the main runtest loop (after collection finished). + + The default hook implementation performs the runtest protocol for all items + collected in the session (``session.items``), unless the collection failed + or the ``collectonly`` pytest option is set. + + If at any point :py:func:`pytest.exit` is called, the loop is + terminated immediately. + + If at any point ``session.shouldfail`` or ``session.shouldstop`` are set, the + loop is terminated after the runtest protocol for the current item is finished. + + :param session: The pytest session object. + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> object | None: + """Perform the runtest protocol for a single test item. + + The default runtest protocol is this (see individual hooks for full details): + + - ``pytest_runtest_logstart(nodeid, location)`` + + - Setup phase: + - ``call = pytest_runtest_setup(item)`` (wrapped in ``CallInfo(when="setup")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Call phase, if the setup passed and the ``setuponly`` pytest option is not set: + - ``call = pytest_runtest_call(item)`` (wrapped in ``CallInfo(when="call")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - Teardown phase: + - ``call = pytest_runtest_teardown(item, nextitem)`` (wrapped in ``CallInfo(when="teardown")``) + - ``report = pytest_runtest_makereport(item, call)`` + - ``pytest_runtest_logreport(report)`` + - ``pytest_exception_interact(call, report)`` if an interactive exception occurred + + - ``pytest_runtest_logfinish(nodeid, location)`` + + :param item: Test item for which the runtest protocol is performed. + :param nextitem: The scheduled-to-be-next test item (or None if this is the end my friend). + + Stops at first non-None result, see :ref:`firstresult`. + The return value is not used, but only stops further processing. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +def pytest_runtest_logstart(nodeid: str, location: tuple[str, int | None, str]) -> None: + """Called at the start of running the runtest protocol for a single item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)`` + where ``filename`` is a file path relative to ``config.rootpath`` + and ``lineno`` is 0-based. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_logfinish( + nodeid: str, location: tuple[str, int | None, str] +) -> None: + """Called at the end of running the runtest protocol for a single item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param nodeid: Full node ID of the item. + :param location: A tuple of ``(filename, lineno, testname)`` + where ``filename`` is a file path relative to ``config.rootpath`` + and ``lineno`` is 0-based. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_setup(item: Item) -> None: + """Called to perform the setup phase for a test item. + + The default implementation runs ``setup()`` on ``item`` and all of its + parents (which haven't been setup yet). This includes obtaining the + values of fixtures required by the item (which haven't been obtained + yet). + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_call(item: Item) -> None: + """Called to run the test for test item (the call phase). + + The default implementation calls ``item.runtest()``. + + :param item: + The item. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + """Called to perform the teardown phase for a test item. + + The default implementation runs the finalizers and calls ``teardown()`` + on ``item`` and all of its parents (which need to be torn down). This + includes running the teardown phase of fixtures required by the item (if + they go out of scope). + + :param item: + The item. + :param nextitem: + The scheduled-to-be-next test item (None if no further test item is + scheduled). This argument is used to perform exact teardowns, i.e. + calling just enough finalizers so that nextitem only needs to call + setup functions. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport | None: + """Called to create a :class:`~pytest.TestReport` for each of + the setup, call and teardown runtest phases of a test item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + :param item: The item. + :param call: The :class:`~pytest.CallInfo` for the phase. + + Stops at first non-None result, see :ref:`firstresult`. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_runtest_logreport(report: TestReport) -> None: + """Process the :class:`~pytest.TestReport` produced for each + of the setup, call and teardown runtest phases of an item. + + See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +@hookspec(firstresult=True) +def pytest_report_to_serializable( + config: Config, + report: CollectReport | TestReport, +) -> dict[str, Any] | None: + """Serialize the given report object into a data structure suitable for + sending over the wire, e.g. converted to JSON. + + :param config: The pytest config object. + :param report: The report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. The exact details may depend + on the plugin which calls the hook. + """ + + +@hookspec(firstresult=True) +def pytest_report_from_serializable( + config: Config, + data: dict[str, Any], +) -> CollectReport | TestReport | None: + """Restore a report object previously serialized with + :hook:`pytest_report_to_serializable`. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. The exact details may depend + on the plugin which calls the hook. + """ + + +# ------------------------------------------------------------------------- +# Fixture related hooks +# ------------------------------------------------------------------------- + + +@hookspec(firstresult=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[Any], request: SubRequest +) -> object | None: + """Perform fixture setup execution. + + :param fixturedef: + The fixture definition object. + :param request: + The fixture request object. + :returns: + The return value of the call to the fixture function. + + Stops at first non-None result, see :ref:`firstresult`. + + .. note:: + If the fixture function returns None, other implementations of + this hook function will continue to be called, according to the + behavior of the :ref:`firstresult` option. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given fixture, only + conftest files in the fixture scope's directory and its parent directories + are consulted. + """ + + +def pytest_fixture_post_finalizer( + fixturedef: FixtureDef[Any], request: SubRequest +) -> None: + """Called after fixture teardown, but before the cache is cleared, so + the fixture result ``fixturedef.cached_result`` is still available (not + ``None``). + + :param fixturedef: + The fixture definition object. + :param request: + The fixture request object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given fixture, only + conftest files in the fixture scope's directory and its parent directories + are consulted. + """ + + +# ------------------------------------------------------------------------- +# test session related hooks +# ------------------------------------------------------------------------- + + +def pytest_sessionstart(session: Session) -> None: + """Called after the ``Session`` object has been created and before performing collection + and entering the run test loop. + + :param session: The pytest session object. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +def pytest_sessionfinish( + session: Session, + exitstatus: int | ExitCode, +) -> None: + """Called after whole test run finished, right before returning the exit status to the system. + + :param session: The pytest session object. + :param exitstatus: The status which pytest will return to the system. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +def pytest_unconfigure(config: Config) -> None: + """Called before test process is exited. + + :param config: The pytest config object. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. + """ + + +# ------------------------------------------------------------------------- +# hooks for customizing the assert methods +# ------------------------------------------------------------------------- + + +def pytest_assertrepr_compare( + config: Config, op: str, left: object, right: object +) -> list[str] | None: + """Return explanation for comparisons in failing assert expressions. + + Return None for no custom explanation, otherwise return a list + of strings. The strings will be joined by newlines but any newlines + *in* a string will be escaped. Note that all but the first line will + be indented slightly, the intention is for the first line to be a summary. + + :param config: The pytest config object. + :param op: The operator, e.g. `"=="`, `"!="`, `"not in"`. + :param left: The left operand. + :param right: The right operand. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +def pytest_assertion_pass(item: Item, lineno: int, orig: str, expl: str) -> None: + """Called whenever an assertion passes. + + .. versionadded:: 5.0 + + Use this hook to do some processing after a passing assertion. + The original assertion information is available in the `orig` string + and the pytest introspected assertion information is available in the + `expl` string. + + This hook must be explicitly enabled by the :confval:`enable_assertion_pass_hook` + configuration option: + + .. tab:: toml + + .. code-block:: toml + + [pytest] + enable_assertion_pass_hook = true + + .. tab:: ini + + .. code-block:: ini + + [pytest] + enable_assertion_pass_hook = true + + You need to **clean the .pyc** files in your project directory and interpreter libraries + when enabling this option, as assertions will require to be re-written. + + :param item: pytest item object of current test. + :param lineno: Line number of the assert statement. + :param orig: String with the original assertion. + :param expl: String with the assert explanation. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in the item's directory and its parent directories are consulted. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing reporting (invoked from _pytest_terminal). +# ------------------------------------------------------------------------- + + +@hookspec( + warn_on_impl_args={ + "startdir": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="startdir", pathlib_path_arg="start_path" + ), + }, +) +def pytest_report_header( # type:ignore[empty-body] + config: Config, start_path: Path, startdir: LEGACY_PATH +) -> str | list[str]: + """Return a string or list of strings to be displayed as header info for terminal reporting. + + :param config: The pytest config object. + :param start_path: The starting dir. + :type start_path: pathlib.Path + :param startdir: The starting dir (deprecated). + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + + .. versionchanged:: 7.0.0 + The ``start_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. The ``startdir`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + This hook is only called for :ref:`initial conftests `. + """ + + +@hookspec( + warn_on_impl_args={ + "startdir": HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg="startdir", pathlib_path_arg="start_path" + ), + }, +) +def pytest_report_collectionfinish( # type:ignore[empty-body] + config: Config, + start_path: Path, + startdir: LEGACY_PATH, + items: Sequence[Item], +) -> str | list[str]: + """Return a string or list of strings to be displayed after collection + has finished successfully. + + These strings will be displayed after the standard "collected X items" message. + + .. versionadded:: 3.2 + + :param config: The pytest config object. + :param start_path: The starting dir. + :type start_path: pathlib.Path + :param startdir: The starting dir (deprecated). + :param items: List of pytest items that are going to be executed; this list should not be modified. + + .. note:: + + Lines returned by a plugin are displayed before those of plugins which + ran before it. + If you want to have your line(s) displayed first, use + :ref:`trylast=True `. + + .. versionchanged:: 7.0.0 + The ``start_path`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. The ``startdir`` parameter + has been deprecated. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec(firstresult=True) +def pytest_report_teststatus( # type:ignore[empty-body] + report: CollectReport | TestReport, config: Config +) -> TestShortLogReport | tuple[str, str, str | tuple[str, Mapping[str, bool]]]: + """Return result-category, shortletter and verbose word for status + reporting. + + The result-category is a category in which to count the result, for + example "passed", "skipped", "error" or the empty string. + + The shortletter is shown as testing progresses, for example ".", "s", + "E" or the empty string. + + The verbose word is shown as testing progresses in verbose mode, for + example "PASSED", "SKIPPED", "ERROR" or the empty string. + + pytest may style these implicitly according to the report outcome. + To provide explicit styling, return a tuple for the verbose word, + for example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :param report: The report object whose status is to be returned. + :param config: The pytest config object. + :returns: The test status. + + Stops at first non-None result, see :ref:`firstresult`. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_terminal_summary( + terminalreporter: TerminalReporter, + exitstatus: ExitCode, + config: Config, +) -> None: + """Add a section to terminal summary reporting. + + :param terminalreporter: The internal terminal reporter object. + :param exitstatus: The exit status that will be reported back to the OS. + :param config: The pytest config object. + + .. versionadded:: 4.2 + The ``config`` parameter. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +@hookspec(historic=True) +def pytest_warning_recorded( + warning_message: warnings.WarningMessage, + when: Literal["config", "collect", "runtest"], + nodeid: str, + location: tuple[str, int, str] | None, +) -> None: + """Process a warning captured by the internal pytest warnings plugin. + + :param warning_message: + The captured warning. This is the same object produced by :class:`warnings.catch_warnings`, + and contains the same attributes as the parameters of :py:func:`warnings.showwarning`. + + :param when: + Indicates when the warning was captured. Possible values: + + * ``"config"``: during pytest configuration/initialization stage. + * ``"collect"``: during test collection. + * ``"runtest"``: during test execution. + + :param nodeid: + Full id of the item. Empty string for warnings that are not specific to + a particular node. + + :param location: + When available, holds information about the execution context of the captured + warning (filename, linenumber, function). ``function`` evaluates to + when the execution context is at the module level. + + .. versionadded:: 6.0 + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. If the warning is specific to a + particular node, only conftest files in parent directories of the node are + consulted. + """ + + +# ------------------------------------------------------------------------- +# Hooks for influencing skipping +# ------------------------------------------------------------------------- + + +def pytest_markeval_namespace( # type:ignore[empty-body] + config: Config, +) -> dict[str, Any]: + """Called when constructing the globals dictionary used for + evaluating string conditions in xfail/skipif markers. + + This is useful when the condition for a marker requires + objects that are expensive or impossible to obtain during + collection time, which is required by normal boolean + conditions. + + .. versionadded:: 6.2 + + :param config: The pytest config object. + :returns: A dictionary of additional globals to add. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given item, only conftest + files in parent directories of the item are consulted. + """ + + +# ------------------------------------------------------------------------- +# error handling and internal debugging hooks +# ------------------------------------------------------------------------- + + +def pytest_internalerror( + excrepr: ExceptionRepr, + excinfo: ExceptionInfo[BaseException], +) -> bool | None: + """Called for internal errors. + + Return True to suppress the fallback handling of printing an + INTERNALERROR message directly to sys.stderr. + + :param excrepr: The exception repr object. + :param excinfo: The exception info. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_keyboard_interrupt( + excinfo: ExceptionInfo[KeyboardInterrupt | Exit], +) -> None: + """Called for keyboard interrupt. + + :param excinfo: The exception info. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_exception_interact( + node: Item | Collector, + call: CallInfo[Any], + report: CollectReport | TestReport, +) -> None: + """Called when an exception was raised which can potentially be + interactively handled. + + May be called during collection (see :hook:`pytest_make_collect_report`), + in which case ``report`` is a :class:`~pytest.CollectReport`. + + May be called during runtest of an item (see :hook:`pytest_runtest_protocol`), + in which case ``report`` is a :class:`~pytest.TestReport`. + + This hook is not called if the exception that was raised is an internal + exception like ``skip.Exception``. + + :param node: + The item or collector. + :param call: + The call information. Contains the exception. + :param report: + The collection or test report. + + Use in conftest plugins + ======================= + + Any conftest file can implement this hook. For a given node, only conftest + files in parent directories of the node are consulted. + """ + + +def pytest_enter_pdb(config: Config, pdb: pdb.Pdb) -> None: + """Called upon pdb.set_trace(). + + Can be used by plugins to take special action just before the python + debugger enters interactive mode. + + :param config: The pytest config object. + :param pdb: The Pdb instance. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ + + +def pytest_leave_pdb(config: Config, pdb: pdb.Pdb) -> None: + """Called when leaving pdb (e.g. with continue after pdb.set_trace()). + + Can be used by plugins to take special action just after the python + debugger leaves interactive mode. + + :param config: The pytest config object. + :param pdb: The Pdb instance. + + Use in conftest plugins + ======================= + + Any conftest plugin can implement this hook. + """ diff --git a/.venv/lib/python3.12/site-packages/_pytest/junitxml.py b/.venv/lib/python3.12/site-packages/_pytest/junitxml.py new file mode 100644 index 0000000..ae8d2b9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/junitxml.py @@ -0,0 +1,695 @@ +# mypy: allow-untyped-defs +"""Report test results in JUnit-XML format, for use with Jenkins and build +integration servers. + +Based on initial code from Ross Lawley. + +Output conforms to +https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd +""" + +from __future__ import annotations + +from collections.abc import Callable +import functools +import os +import platform +import re +import xml.etree.ElementTree as ET + +from _pytest import nodes +from _pytest import timing +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprFileLocation +from _pytest.config import Config +from _pytest.config import filename_arg +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureRequest +from _pytest.reports import TestReport +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter +import pytest + + +xml_key = StashKey["LogXML"]() + + +def bin_xml_escape(arg: object) -> str: + r"""Visually escape invalid XML characters. + + For example, transforms + 'hello\aworld\b' + into + 'hello#x07world#x08' + Note that the #xABs are *not* XML escapes - missing the ampersand «. + The idea is to escape visually for the user rather than for XML itself. + """ + + def repl(matchobj: re.Match[str]) -> str: + i = ord(matchobj.group()) + if i <= 0xFF: + return f"#x{i:02X}" + else: + return f"#x{i:04X}" + + # The spec range of valid chars is: + # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + # For an unknown(?) reason, we disallow #x7F (DEL) as well. + illegal_xml_re = ( + "[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\u10000-\u10ffff]" + ) + return re.sub(illegal_xml_re, repl, str(arg)) + + +def merge_family(left, right) -> None: + result = {} + for kl, vl in left.items(): + for kr, vr in right.items(): + if not isinstance(vl, list): + raise TypeError(type(vl)) + result[kl] = vl + vr + left.update(result) + + +families = { # pylint: disable=dict-init-mutate + "_base": {"testcase": ["classname", "name"]}, + "_base_legacy": {"testcase": ["file", "line", "url"]}, +} +# xUnit 1.x inherits legacy attributes. +families["xunit1"] = families["_base"].copy() +merge_family(families["xunit1"], families["_base_legacy"]) + +# xUnit 2.x uses strict base attributes. +families["xunit2"] = families["_base"] + + +class _NodeReporter: + def __init__(self, nodeid: str | TestReport, xml: LogXML) -> None: + self.id = nodeid + self.xml = xml + self.add_stats = self.xml.add_stats + self.family = self.xml.family + self.duration = 0.0 + self.properties: list[tuple[str, str]] = [] + self.nodes: list[ET.Element] = [] + self.attrs: dict[str, str] = {} + + def append(self, node: ET.Element) -> None: + self.xml.add_stats(node.tag) + self.nodes.append(node) + + def add_property(self, name: str, value: object) -> None: + self.properties.append((str(name), bin_xml_escape(value))) + + def add_attribute(self, name: str, value: object) -> None: + self.attrs[str(name)] = bin_xml_escape(value) + + def make_properties_node(self) -> ET.Element | None: + """Return a Junit node containing custom properties, if any.""" + if self.properties: + properties = ET.Element("properties") + for name, value in self.properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None + + def record_testreport(self, testreport: TestReport) -> None: + names = mangle_test_address(testreport.nodeid) + existing_attrs = self.attrs + classnames = names[:-1] + if self.xml.prefix: + classnames.insert(0, self.xml.prefix) + attrs: dict[str, str] = { + "classname": ".".join(classnames), + "name": bin_xml_escape(names[-1]), + "file": testreport.location[0], + } + if testreport.location[1] is not None: + attrs["line"] = str(testreport.location[1]) + if hasattr(testreport, "url"): + attrs["url"] = testreport.url + self.attrs = attrs + self.attrs.update(existing_attrs) # Restore any user-defined attributes. + + # Preserve legacy testcase behavior. + if self.family == "xunit1": + return + + # Filter out attributes not permitted by this test family. + # Including custom attributes because they are not valid here. + temp_attrs = {} + for key in self.attrs: + if key in families[self.family]["testcase"]: + temp_attrs[key] = self.attrs[key] + self.attrs = temp_attrs + + def to_xml(self) -> ET.Element: + testcase = ET.Element("testcase", self.attrs, time=f"{self.duration:.3f}") + properties = self.make_properties_node() + if properties is not None: + testcase.append(properties) + testcase.extend(self.nodes) + return testcase + + def _add_simple(self, tag: str, message: str, data: str | None = None) -> None: + node = ET.Element(tag, message=message) + node.text = bin_xml_escape(data) + self.append(node) + + def write_captured_output(self, report: TestReport) -> None: + if not self.xml.log_passing_tests and report.passed: + return + + content_out = report.capstdout + content_log = report.caplog + content_err = report.capstderr + if self.xml.logging == "no": + return + content_all = "" + if self.xml.logging in ["log", "all"]: + content_all = self._prepare_content(content_log, " Captured Log ") + if self.xml.logging in ["system-out", "out-err", "all"]: + content_all += self._prepare_content(content_out, " Captured Out ") + self._write_content(report, content_all, "system-out") + content_all = "" + if self.xml.logging in ["system-err", "out-err", "all"]: + content_all += self._prepare_content(content_err, " Captured Err ") + self._write_content(report, content_all, "system-err") + content_all = "" + if content_all: + self._write_content(report, content_all, "system-out") + + def _prepare_content(self, content: str, header: str) -> str: + return "\n".join([header.center(80, "-"), content, ""]) + + def _write_content(self, report: TestReport, content: str, jheader: str) -> None: + tag = ET.Element(jheader) + tag.text = bin_xml_escape(content) + self.append(tag) + + def append_pass(self, report: TestReport) -> None: + self.add_stats("passed") + + def append_failure(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + if hasattr(report, "wasxfail"): + self._add_simple("skipped", "xfail-marked test passes unexpectedly") + else: + assert report.longrepr is not None + reprcrash: ReprFileLocation | None = getattr( + report.longrepr, "reprcrash", None + ) + if reprcrash is not None: + message = reprcrash.message + else: + message = str(report.longrepr) + message = bin_xml_escape(message) + self._add_simple("failure", message, str(report.longrepr)) + + def append_collect_error(self, report: TestReport) -> None: + # msg = str(report.longrepr.reprtraceback.extraline) + assert report.longrepr is not None + self._add_simple("error", "collection failure", str(report.longrepr)) + + def append_collect_skipped(self, report: TestReport) -> None: + self._add_simple("skipped", "collection skipped", str(report.longrepr)) + + def append_error(self, report: TestReport) -> None: + assert report.longrepr is not None + reprcrash: ReprFileLocation | None = getattr(report.longrepr, "reprcrash", None) + if reprcrash is not None: + reason = reprcrash.message + else: + reason = str(report.longrepr) + + if report.when == "teardown": + msg = f'failed on teardown with "{reason}"' + else: + msg = f'failed on setup with "{reason}"' + self._add_simple("error", bin_xml_escape(msg), str(report.longrepr)) + + def append_skipped(self, report: TestReport) -> None: + if hasattr(report, "wasxfail"): + xfailreason = report.wasxfail + if xfailreason.startswith("reason: "): + xfailreason = xfailreason[8:] + xfailreason = bin_xml_escape(xfailreason) + skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason) + self.append(skipped) + else: + assert isinstance(report.longrepr, tuple) + filename, lineno, skipreason = report.longrepr + if skipreason.startswith("Skipped: "): + skipreason = skipreason[9:] + details = f"{filename}:{lineno}: {skipreason}" + + skipped = ET.Element( + "skipped", type="pytest.skip", message=bin_xml_escape(skipreason) + ) + skipped.text = bin_xml_escape(details) + self.append(skipped) + self.write_captured_output(report) + + def finalize(self) -> None: + data = self.to_xml() + self.__dict__.clear() + # Type ignored because mypy doesn't like overriding a method. + # Also the return value doesn't match... + self.to_xml = lambda: data # type: ignore[method-assign] + + +def _warn_incompatibility_with_xunit2( + request: FixtureRequest, fixture_name: str +) -> None: + """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" + from _pytest.warning_types import PytestWarning + + xml = request.config.stash.get(xml_key, None) + if xml is not None and xml.family not in ("xunit1", "legacy"): + request.node.warn( + PytestWarning( + f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')" + ) + ) + + +@pytest.fixture +def record_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra properties to the calling test. + + User properties become part of the test report and are available to the + configured reporters, like JUnit XML. + + The fixture is callable with ``name, value``. The value is automatically + XML-encoded. + + Example:: + + def test_function(record_property): + record_property("example_key", 1) + """ + _warn_incompatibility_with_xunit2(request, "record_property") + + def append_property(name: str, value: object) -> None: + request.node.user_properties.append((name, value)) + + return append_property + + +@pytest.fixture +def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], None]: + """Add extra xml attributes to the tag for the calling test. + + The fixture is callable with ``name, value``. The value is + automatically XML-encoded. + """ + from _pytest.warning_types import PytestExperimentalApiWarning + + request.node.warn( + PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") + ) + + _warn_incompatibility_with_xunit2(request, "record_xml_attribute") + + # Declare noop + def add_attr_noop(name: str, value: object) -> None: + pass + + attr_func = add_attr_noop + + xml = request.config.stash.get(xml_key, None) + if xml is not None: + node_reporter = xml.node_reporter(request.node.nodeid) + attr_func = node_reporter.add_attribute + + return attr_func + + +def _check_record_param_type(param: str, v: str) -> None: + """Used by record_testsuite_property to check that the given parameter name is of the proper + type.""" + __tracebackhide__ = True + if not isinstance(v, str): + msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable] + raise TypeError(msg.format(param=param, g=type(v).__name__)) + + +@pytest.fixture(scope="session") +def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]: + """Record a new ```` tag as child of the root ````. + + This is suitable to writing global information regarding the entire test + suite, and is compatible with ``xunit2`` JUnit family. + + This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: + + .. code-block:: python + + def test_foo(record_testsuite_property): + record_testsuite_property("ARCH", "PPC") + record_testsuite_property("STORAGE_TYPE", "CEPH") + + :param name: + The property name. + :param value: + The property value. Will be converted to a string. + + .. warning:: + + Currently this fixture **does not work** with the + `pytest-xdist `__ plugin. See + :issue:`7767` for details. + """ + __tracebackhide__ = True + + def record_func(name: str, value: object) -> None: + """No-op function in case --junit-xml was not passed in the command-line.""" + __tracebackhide__ = True + _check_record_param_type("name", name) + + xml = request.config.stash.get(xml_key, None) + if xml is not None: + record_func = xml.add_global_property + return record_func + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--junitxml", + "--junit-xml", + action="store", + dest="xmlpath", + metavar="path", + type=functools.partial(filename_arg, optname="--junitxml"), + default=None, + help="Create junit-xml style report file at given path", + ) + group.addoption( + "--junitprefix", + "--junit-prefix", + action="store", + metavar="str", + default=None, + help="Prepend prefix to classnames in junit-xml output", + ) + parser.addini( + "junit_suite_name", "Test suite name for JUnit report", default="pytest" + ) + parser.addini( + "junit_logging", + "Write captured log messages to JUnit report: " + "one of no|log|system-out|system-err|out-err|all", + default="no", + ) + parser.addini( + "junit_log_passing_tests", + "Capture log information for passing tests to JUnit report: ", + type="bool", + default=True, + ) + parser.addini( + "junit_duration_report", + "Duration time to report: one of total|call", + default="total", + ) # choices=['total', 'call']) + parser.addini( + "junit_family", + "Emit XML for schema: one of legacy|xunit1|xunit2", + default="xunit2", + ) + + +def pytest_configure(config: Config) -> None: + xmlpath = config.option.xmlpath + # Prevent opening xmllog on worker nodes (xdist). + if xmlpath and not hasattr(config, "workerinput"): + junit_family = config.getini("junit_family") + config.stash[xml_key] = LogXML( + xmlpath, + config.option.junitprefix, + config.getini("junit_suite_name"), + config.getini("junit_logging"), + config.getini("junit_duration_report"), + junit_family, + config.getini("junit_log_passing_tests"), + ) + config.pluginmanager.register(config.stash[xml_key]) + + +def pytest_unconfigure(config: Config) -> None: + xml = config.stash.get(xml_key, None) + if xml: + del config.stash[xml_key] + config.pluginmanager.unregister(xml) + + +def mangle_test_address(address: str) -> list[str]: + path, possible_open_bracket, params = address.partition("[") + names = path.split("::") + # Convert file path to dotted path. + names[0] = names[0].replace(nodes.SEP, ".") + names[0] = re.sub(r"\.py$", "", names[0]) + # Put any params back. + names[-1] += possible_open_bracket + params + return names + + +class LogXML: + def __init__( + self, + logfile, + prefix: str | None, + suite_name: str = "pytest", + logging: str = "no", + report_duration: str = "total", + family="xunit1", + log_passing_tests: bool = True, + ) -> None: + logfile = os.path.expanduser(os.path.expandvars(logfile)) + self.logfile = os.path.normpath(os.path.abspath(logfile)) + self.prefix = prefix + self.suite_name = suite_name + self.logging = logging + self.log_passing_tests = log_passing_tests + self.report_duration = report_duration + self.family = family + self.stats: dict[str, int] = dict.fromkeys( + ["error", "passed", "failure", "skipped"], 0 + ) + self.node_reporters: dict[tuple[str | TestReport, object], _NodeReporter] = {} + self.node_reporters_ordered: list[_NodeReporter] = [] + self.global_properties: list[tuple[str, str]] = [] + + # List of reports that failed on call but teardown is pending. + self.open_reports: list[TestReport] = [] + self.cnt_double_fail_tests = 0 + + # Replaces convenience family with real family. + if self.family == "legacy": + self.family = "xunit1" + + def finalize(self, report: TestReport) -> None: + nodeid = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + reporter = self.node_reporters.pop((nodeid, workernode)) + + for propname, propvalue in report.user_properties: + reporter.add_property(propname, str(propvalue)) + + if reporter is not None: + reporter.finalize() + + def node_reporter(self, report: TestReport | str) -> _NodeReporter: + nodeid: str | TestReport = getattr(report, "nodeid", report) + # Local hack to handle xdist report order. + workernode = getattr(report, "node", None) + + key = nodeid, workernode + + if key in self.node_reporters: + # TODO: breaks for --dist=each + return self.node_reporters[key] + + reporter = _NodeReporter(nodeid, self) + + self.node_reporters[key] = reporter + self.node_reporters_ordered.append(reporter) + + return reporter + + def add_stats(self, key: str) -> None: + if key in self.stats: + self.stats[key] += 1 + + def _opentestcase(self, report: TestReport) -> _NodeReporter: + reporter = self.node_reporter(report) + reporter.record_testreport(report) + return reporter + + def pytest_runtest_logreport(self, report: TestReport) -> None: + """Handle a setup/call/teardown report, generating the appropriate + XML tags as necessary. + + Note: due to plugins like xdist, this hook may be called in interlaced + order with reports from other nodes. For example: + + Usual call order: + -> setup node1 + -> call node1 + -> teardown node1 + -> setup node2 + -> call node2 + -> teardown node2 + + Possible call order in xdist: + -> setup node1 + -> call node1 + -> setup node2 + -> call node2 + -> teardown node2 + -> teardown node1 + """ + close_report = None + if report.passed: + if report.when == "call": # ignore setup/teardown + reporter = self._opentestcase(report) + reporter.append_pass(report) + elif report.failed: + if report.when == "teardown": + # The following vars are needed when xdist plugin is used. + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + # We need to open new testcase in case we have failure in + # call and error in teardown in order to follow junit + # schema. + self.finalize(close_report) + self.cnt_double_fail_tests += 1 + reporter = self._opentestcase(report) + if report.when == "call": + reporter.append_failure(report) + self.open_reports.append(report) + if not self.log_passing_tests: + reporter.write_captured_output(report) + else: + reporter.append_error(report) + elif report.skipped: + reporter = self._opentestcase(report) + reporter.append_skipped(report) + self.update_testcase_duration(report) + if report.when == "teardown": + reporter = self._opentestcase(report) + reporter.write_captured_output(report) + + self.finalize(report) + report_wid = getattr(report, "worker_id", None) + report_ii = getattr(report, "item_index", None) + close_report = next( + ( + rep + for rep in self.open_reports + if ( + rep.nodeid == report.nodeid + and getattr(rep, "item_index", None) == report_ii + and getattr(rep, "worker_id", None) == report_wid + ) + ), + None, + ) + if close_report: + self.open_reports.remove(close_report) + + def update_testcase_duration(self, report: TestReport) -> None: + """Accumulate total duration for nodeid from given report and update + the Junit.testcase with the new total if already created.""" + if self.report_duration in {"total", report.when}: + reporter = self.node_reporter(report) + reporter.duration += getattr(report, "duration", 0.0) + + def pytest_collectreport(self, report: TestReport) -> None: + if not report.passed: + reporter = self._opentestcase(report) + if report.failed: + reporter.append_collect_error(report) + else: + reporter.append_collect_skipped(report) + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: + reporter = self.node_reporter("internal") + reporter.attrs.update(classname="pytest", name="internal") + reporter._add_simple("error", "internal error", str(excrepr)) + + def pytest_sessionstart(self) -> None: + self.suite_start = timing.Instant() + + def pytest_sessionfinish(self) -> None: + dirname = os.path.dirname(os.path.abspath(self.logfile)) + # exist_ok avoids filesystem race conditions between checking path existence and requesting creation + os.makedirs(dirname, exist_ok=True) + + with open(self.logfile, "w", encoding="utf-8") as logfile: + duration = self.suite_start.elapsed() + + numtests = ( + self.stats["passed"] + + self.stats["failure"] + + self.stats["skipped"] + + self.stats["error"] + - self.cnt_double_fail_tests + ) + logfile.write('') + + suite_node = ET.Element( + "testsuite", + name=self.suite_name, + errors=str(self.stats["error"]), + failures=str(self.stats["failure"]), + skipped=str(self.stats["skipped"]), + tests=str(numtests), + time=f"{duration.seconds:.3f}", + timestamp=self.suite_start.as_utc().astimezone().isoformat(), + hostname=platform.node(), + ) + global_properties = self._get_global_properties_node() + if global_properties is not None: + suite_node.append(global_properties) + for node_reporter in self.node_reporters_ordered: + suite_node.append(node_reporter.to_xml()) + testsuites = ET.Element("testsuites") + testsuites.set("name", "pytest tests") + testsuites.append(suite_node) + logfile.write(ET.tostring(testsuites, encoding="unicode")) + + def pytest_terminal_summary( + self, terminalreporter: TerminalReporter, config: pytest.Config + ) -> None: + if config.get_verbosity() >= 0: + terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") + + def add_global_property(self, name: str, value: object) -> None: + __tracebackhide__ = True + _check_record_param_type("name", name) + self.global_properties.append((name, bin_xml_escape(value))) + + def _get_global_properties_node(self) -> ET.Element | None: + """Return a Junit node containing custom properties, if any.""" + if self.global_properties: + properties = ET.Element("properties") + for name, value in self.global_properties: + properties.append(ET.Element("property", name=name, value=value)) + return properties + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/legacypath.py b/.venv/lib/python3.12/site-packages/_pytest/legacypath.py new file mode 100644 index 0000000..59e8ef6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/legacypath.py @@ -0,0 +1,468 @@ +# mypy: allow-untyped-defs +"""Add backward compatibility support for the legacy py path type.""" + +from __future__ import annotations + +import dataclasses +from pathlib import Path +import shlex +import subprocess +from typing import Final +from typing import final +from typing import TYPE_CHECKING + +from iniconfig import SectionWrapper + +from _pytest.cacheprovider import Cache +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pytester import HookRecorder +from _pytest.pytester import Pytester +from _pytest.pytester import RunResult +from _pytest.terminal import TerminalReporter +from _pytest.tmpdir import TempPathFactory + + +if TYPE_CHECKING: + import pexpect + + +@final +class Testdir: + """ + Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. + + All methods just forward to an internal :class:`Pytester` instance, converting results + to `legacy_path` objects as necessary. + """ + + __test__ = False + + CLOSE_STDIN: Final = Pytester.CLOSE_STDIN + TimeoutExpired: Final = Pytester.TimeoutExpired + + def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._pytester = pytester + + @property + def tmpdir(self) -> LEGACY_PATH: + """Temporary directory where tests are executed.""" + return legacy_path(self._pytester.path) + + @property + def test_tmproot(self) -> LEGACY_PATH: + return legacy_path(self._pytester._test_tmproot) + + @property + def request(self): + return self._pytester._request + + @property + def plugins(self): + return self._pytester.plugins + + @plugins.setter + def plugins(self, plugins): + self._pytester.plugins = plugins + + @property + def monkeypatch(self) -> MonkeyPatch: + return self._pytester._monkeypatch + + def make_hook_recorder(self, pluginmanager) -> HookRecorder: + """See :meth:`Pytester.make_hook_recorder`.""" + return self._pytester.make_hook_recorder(pluginmanager) + + def chdir(self) -> None: + """See :meth:`Pytester.chdir`.""" + return self._pytester.chdir() + + def finalize(self) -> None: + return self._pytester._finalize() + + def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makefile`.""" + if ext and not ext.startswith("."): + # pytester.makefile is going to throw a ValueError in a way that + # testdir.makefile did not, because + # pathlib.Path is stricter suffixes than py.path + # This ext arguments is likely user error, but since testdir has + # allowed this, we will prepend "." as a workaround to avoid breaking + # testdir usage that worked before + ext = "." + ext + return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) + + def makeconftest(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeconftest`.""" + return legacy_path(self._pytester.makeconftest(source)) + + def makeini(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makeini`.""" + return legacy_path(self._pytester.makeini(source)) + + def getinicfg(self, source: str) -> SectionWrapper: + """See :meth:`Pytester.getinicfg`.""" + return self._pytester.getinicfg(source) + + def makepyprojecttoml(self, source) -> LEGACY_PATH: + """See :meth:`Pytester.makepyprojecttoml`.""" + return legacy_path(self._pytester.makepyprojecttoml(source)) + + def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.makepyfile`.""" + return legacy_path(self._pytester.makepyfile(*args, **kwargs)) + + def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: + """See :meth:`Pytester.maketxtfile`.""" + return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) + + def syspathinsert(self, path=None) -> None: + """See :meth:`Pytester.syspathinsert`.""" + return self._pytester.syspathinsert(path) + + def mkdir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkdir`.""" + return legacy_path(self._pytester.mkdir(name)) + + def mkpydir(self, name) -> LEGACY_PATH: + """See :meth:`Pytester.mkpydir`.""" + return legacy_path(self._pytester.mkpydir(name)) + + def copy_example(self, name=None) -> LEGACY_PATH: + """See :meth:`Pytester.copy_example`.""" + return legacy_path(self._pytester.copy_example(name)) + + def getnode(self, config: Config, arg) -> Item | Collector | None: + """See :meth:`Pytester.getnode`.""" + return self._pytester.getnode(config, arg) + + def getpathnode(self, path): + """See :meth:`Pytester.getpathnode`.""" + return self._pytester.getpathnode(path) + + def genitems(self, colitems: list[Item | Collector]) -> list[Item]: + """See :meth:`Pytester.genitems`.""" + return self._pytester.genitems(colitems) + + def runitem(self, source): + """See :meth:`Pytester.runitem`.""" + return self._pytester.runitem(source) + + def inline_runsource(self, source, *cmdlineargs): + """See :meth:`Pytester.inline_runsource`.""" + return self._pytester.inline_runsource(source, *cmdlineargs) + + def inline_genitems(self, *args): + """See :meth:`Pytester.inline_genitems`.""" + return self._pytester.inline_genitems(*args) + + def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): + """See :meth:`Pytester.inline_run`.""" + return self._pytester.inline_run( + *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc + ) + + def runpytest_inprocess(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest_inprocess`.""" + return self._pytester.runpytest_inprocess(*args, **kwargs) + + def runpytest(self, *args, **kwargs) -> RunResult: + """See :meth:`Pytester.runpytest`.""" + return self._pytester.runpytest(*args, **kwargs) + + def parseconfig(self, *args) -> Config: + """See :meth:`Pytester.parseconfig`.""" + return self._pytester.parseconfig(*args) + + def parseconfigure(self, *args) -> Config: + """See :meth:`Pytester.parseconfigure`.""" + return self._pytester.parseconfigure(*args) + + def getitem(self, source, funcname="test_func"): + """See :meth:`Pytester.getitem`.""" + return self._pytester.getitem(source, funcname) + + def getitems(self, source): + """See :meth:`Pytester.getitems`.""" + return self._pytester.getitems(source) + + def getmodulecol(self, source, configargs=(), withinit=False): + """See :meth:`Pytester.getmodulecol`.""" + return self._pytester.getmodulecol( + source, configargs=configargs, withinit=withinit + ) + + def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: + """See :meth:`Pytester.collect_by_name`.""" + return self._pytester.collect_by_name(modcol, name) + + def popen( + self, + cmdargs, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=CLOSE_STDIN, + **kw, + ): + """See :meth:`Pytester.popen`.""" + return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) + + def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: + """See :meth:`Pytester.run`.""" + return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) + + def runpython(self, script) -> RunResult: + """See :meth:`Pytester.runpython`.""" + return self._pytester.runpython(script) + + def runpython_c(self, command): + """See :meth:`Pytester.runpython_c`.""" + return self._pytester.runpython_c(command) + + def runpytest_subprocess(self, *args, timeout=None) -> RunResult: + """See :meth:`Pytester.runpytest_subprocess`.""" + return self._pytester.runpytest_subprocess(*args, timeout=timeout) + + def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn_pytest`.""" + return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """See :meth:`Pytester.spawn`.""" + return self._pytester.spawn(cmd, expect_timeout=expect_timeout) + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return str(self.tmpdir) + + +class LegacyTestdirPlugin: + @staticmethod + @fixture + def testdir(pytester: Pytester) -> Testdir: + """ + Identical to :fixture:`pytester`, and provides an instance whose methods return + legacy ``LEGACY_PATH`` objects instead when applicable. + + New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. + """ + return Testdir(pytester, _ispytest=True) + + +@final +@dataclasses.dataclass +class TempdirFactory: + """Backward compatibility wrapper that implements ``py.path.local`` + for :class:`TempPathFactory`. + + .. note:: + These days, it is preferred to use ``tmp_path_factory``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + """ + + _tmppath_factory: TempPathFactory + + def __init__( + self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + self._tmppath_factory = tmppath_factory + + def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) + + def getbasetemp(self) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" + return legacy_path(self._tmppath_factory.getbasetemp().resolve()) + + +class LegacyTmpdirPlugin: + @staticmethod + @fixture(scope="session") + def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: + """Return a :class:`pytest.TempdirFactory` instance for the test session.""" + # Set dynamically by pytest_configure(). + return request.config._tmpdirhandler # type: ignore + + @staticmethod + @fixture + def tmpdir(tmp_path: Path) -> LEGACY_PATH: + """Return a temporary directory (as `legacy_path`_ object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + + .. note:: + These days, it is preferred to use ``tmp_path``. + + :ref:`About the tmpdir and tmpdir_factory fixtures`. + + .. _legacy_path: https://py.readthedocs.io/en/latest/path.html + """ + return legacy_path(tmp_path) + + +def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH: + """Return a directory path object with the given name. + + Same as :func:`mkdir`, but returns a legacy py path instance. + """ + return legacy_path(self.mkdir(name)) + + +def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH: + """(deprecated) The file system path of the test module which collected this test.""" + return legacy_path(self.path) + + +def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config_invocation_dir(self: Config) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use :attr:`invocation_params.dir `, + which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.invocation_params.dir)) + + +def Config_rootdir(self: Config) -> LEGACY_PATH: + """The path to the :ref:`rootdir `. + + Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(str(self.rootpath)) + + +def Config_inifile(self: Config) -> LEGACY_PATH | None: + """The path to the :ref:`configfile `. + + Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. + + :type: Optional[LEGACY_PATH] + """ + return legacy_path(str(self.inipath)) if self.inipath else None + + +def Session_startdir(self: Session) -> LEGACY_PATH: + """The path from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + + +def Config__getini_unknown_type(self, name: str, type: str, value: str | list[str]): + if type == "pathlist": + # TODO: This assert is probably not valid in all cases. + assert self.inipath is not None + dp = self.inipath.parent + input_values = shlex.split(value) if isinstance(value, str) else value + return [legacy_path(str(dp / x)) for x in input_values] + else: + raise ValueError(f"unknown configuration type: {type}", value) + + +def Node_fspath(self: Node) -> LEGACY_PATH: + """(deprecated) returns a legacy_path copy of self.path""" + return legacy_path(self.path) + + +def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: + self.path = Path(value) + + +@hookimpl(tryfirst=True) +def pytest_load_initial_conftests(early_config: Config) -> None: + """Monkeypatch legacy path attributes in several classes, as early as possible.""" + mp = MonkeyPatch() + early_config.add_cleanup(mp.undo) + + # Add Cache.makedir(). + mp.setattr(Cache, "makedir", Cache_makedir, raising=False) + + # Add FixtureRequest.fspath property. + mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) + + # Add TerminalReporter.startdir property. + mp.setattr( + TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False + ) + + # Add Config.{invocation_dir,rootdir,inifile} properties. + mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) + mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) + mp.setattr(Config, "inifile", property(Config_inifile), raising=False) + + # Add Session.startdir property. + mp.setattr(Session, "startdir", property(Session_startdir), raising=False) + + # Add pathlist configuration type. + mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) + + # Add Node.fspath property. + mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) + + +@hookimpl +def pytest_configure(config: Config) -> None: + """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" + if config.pluginmanager.has_plugin("tmpdir"): + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + # Create TmpdirFactory and attach it to the config object. + # + # This is to comply with existing plugins which expect the handler to be + # available at pytest_configure time, but ideally should be moved entirely + # to the tmpdir_factory session fixture. + try: + tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] + except AttributeError: + # tmpdir plugin is blocked. + pass + else: + _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) + mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) + + config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") + + +@hookimpl +def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: + # pytester is not loaded by default and is commonly loaded from a conftest, + # so checking for it in `pytest_configure` is not enough. + is_pytester = plugin is manager.get_plugin("pytester") + if is_pytester and not manager.is_registered(LegacyTestdirPlugin): + manager.register(LegacyTestdirPlugin, "legacypath-pytester") diff --git a/.venv/lib/python3.12/site-packages/_pytest/logging.py b/.venv/lib/python3.12/site-packages/_pytest/logging.py new file mode 100644 index 0000000..e4fed57 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/logging.py @@ -0,0 +1,960 @@ +# mypy: allow-untyped-defs +"""Access and control log capturing.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Set as AbstractSet +from contextlib import contextmanager +from contextlib import nullcontext +from datetime import datetime +from datetime import timedelta +from datetime import timezone +import io +from io import StringIO +import logging +from logging import LogRecord +import os +from pathlib import Path +import re +from types import TracebackType +from typing import final +from typing import Generic +from typing import Literal +from typing import TYPE_CHECKING +from typing import TypeVar + +from _pytest import nodes +from _pytest._io import TerminalWriter +from _pytest.capture import CaptureManager +from _pytest.config import _strtobool +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter + + +if TYPE_CHECKING: + logging_StreamHandler = logging.StreamHandler[StringIO] +else: + logging_StreamHandler = logging.StreamHandler + +DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" +DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" +_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") +caplog_handler_key = StashKey["LogCaptureHandler"]() +caplog_records_key = StashKey[dict[str, list[logging.LogRecord]]]() + + +def _remove_ansi_escape_sequences(text: str) -> str: + return _ANSI_ESCAPE_SEQ.sub("", text) + + +class DatetimeFormatter(logging.Formatter): + """A logging formatter which formats record with + :func:`datetime.datetime.strftime` formatter instead of + :func:`time.strftime` in case of microseconds in format string. + """ + + def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: + if datefmt and "%f" in datefmt: + ct = self.converter(record.created) + tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone) + # Construct `datetime.datetime` object from `struct_time` + # and msecs information from `record` + # Using int() instead of round() to avoid it exceeding 1_000_000 and causing a ValueError (#11861). + dt = datetime(*ct[0:6], microsecond=int(record.msecs * 1000), tzinfo=tz) + return dt.strftime(datefmt) + # Use `logging.Formatter` for non-microsecond formats + return super().formatTime(record, datefmt) + + +class ColoredLevelFormatter(DatetimeFormatter): + """A logging formatter which colorizes the %(levelname)..s part of the + log format passed to __init__.""" + + LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { + logging.CRITICAL: {"red"}, + logging.ERROR: {"red", "bold"}, + logging.WARNING: {"yellow"}, + logging.WARN: {"yellow"}, + logging.INFO: {"green"}, + logging.DEBUG: {"purple"}, + logging.NOTSET: set(), + } + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") + + def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._terminalwriter = terminalwriter + self._original_fmt = self._style._fmt + self._level_to_fmt_mapping: dict[int, str] = {} + + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + self.add_color_level(level, *color_opts) + + def add_color_level(self, level: int, *color_opts: str) -> None: + """Add or update color opts for a log level. + + :param level: + Log level to apply a style to, e.g. ``logging.INFO``. + :param color_opts: + ANSI escape sequence color options. Capitalized colors indicates + background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold + green text on yellow background. + + .. warning:: + This is an experimental API. + """ + assert self._fmt is not None + levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) + if not levelname_fmt_match: + return + levelname_fmt = levelname_fmt_match.group() + + formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} + + # add ANSI escape sequences around the formatted levelname + color_kwargs = {name: True for name in color_opts} + colorized_formatted_levelname = self._terminalwriter.markup( + formatted_levelname, **color_kwargs + ) + self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( + colorized_formatted_levelname, self._fmt + ) + + def format(self, record: logging.LogRecord) -> str: + fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) + self._style._fmt = fmt + return super().format(record) + + +class PercentStyleMultiline(logging.PercentStyle): + """A logging style with special support for multiline messages. + + If the message of a record consists of multiple lines, this style + formats the message as if each line were logged separately. + """ + + def __init__(self, fmt: str, auto_indent: int | str | bool | None) -> None: + super().__init__(fmt) + self._auto_indent = self._get_auto_indent(auto_indent) + + @staticmethod + def _get_auto_indent(auto_indent_option: int | str | bool | None) -> int: + """Determine the current auto indentation setting. + + Specify auto indent behavior (on/off/fixed) by passing in + extra={"auto_indent": [value]} to the call to logging.log() or + using a --log-auto-indent [value] command line or the + log_auto_indent [value] config option. + + Default behavior is auto-indent off. + + Using the string "True" or "on" or the boolean True as the value + turns auto indent on, using the string "False" or "off" or the + boolean False or the int 0 turns it off, and specifying a + positive integer fixes the indentation position to the value + specified. + + Any other values for the option are invalid, and will silently be + converted to the default. + + :param None|bool|int|str auto_indent_option: + User specified option for indentation from command line, config + or extra kwarg. Accepts int, bool or str. str option accepts the + same range of values as boolean config options, as well as + positive integers represented in str form. + + :returns: + Indentation value, which can be + -1 (automatically determine indentation) or + 0 (auto-indent turned off) or + >0 (explicitly set indentation position). + """ + if auto_indent_option is None: + return 0 + elif isinstance(auto_indent_option, bool): + if auto_indent_option: + return -1 + else: + return 0 + elif isinstance(auto_indent_option, int): + return int(auto_indent_option) + elif isinstance(auto_indent_option, str): + try: + return int(auto_indent_option) + except ValueError: + pass + try: + if _strtobool(auto_indent_option): + return -1 + except ValueError: + return 0 + + return 0 + + def format(self, record: logging.LogRecord) -> str: + if "\n" in record.message: + if hasattr(record, "auto_indent"): + # Passed in from the "extra={}" kwarg on the call to logging.log(). + auto_indent = self._get_auto_indent(record.auto_indent) + else: + auto_indent = self._auto_indent + + if auto_indent: + lines = record.message.splitlines() + formatted = self._fmt % {**record.__dict__, "message": lines[0]} + + if auto_indent < 0: + indentation = _remove_ansi_escape_sequences(formatted).find( + lines[0] + ) + else: + # Optimizes logging by allowing a fixed indentation. + indentation = auto_indent + lines[0] = formatted + return ("\n" + " " * indentation).join(lines) + return self._fmt % record.__dict__ + + +def get_option_ini(config: Config, *names: str): + for name in names: + ret = config.getoption(name) # 'default' arg won't work as expected + if ret is None: + ret = config.getini(name) + if ret: + return ret + + +def pytest_addoption(parser: Parser) -> None: + """Add options to control log capturing.""" + group = parser.getgroup("logging") + + def add_option_ini(option, dest, default=None, type=None, **kwargs): + parser.addini( + dest, default=default, type=type, help="Default value for " + option + ) + group.addoption(option, dest=dest, **kwargs) + + add_option_ini( + "--log-level", + dest="log_level", + default=None, + metavar="LEVEL", + help=( + "Level of messages to catch/display." + " Not set by default, so it depends on the root/parent log handler's" + ' effective level, where it is "WARNING" by default.' + ), + ) + add_option_ini( + "--log-format", + dest="log_format", + default=DEFAULT_LOG_FORMAT, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-date-format", + dest="log_date_format", + default=DEFAULT_LOG_DATE_FORMAT, + help="Log date format used by the logging module", + ) + parser.addini( + "log_cli", + default=False, + type="bool", + help='Enable log display during test run (also known as "live logging")', + ) + add_option_ini( + "--log-cli-level", dest="log_cli_level", default=None, help="CLI logging level" + ) + add_option_ini( + "--log-cli-format", + dest="log_cli_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-cli-date-format", + dest="log_cli_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-file", + dest="log_file", + default=None, + help="Path to a file when logging will be written to", + ) + add_option_ini( + "--log-file-mode", + dest="log_file_mode", + default="w", + choices=["w", "a"], + help="Log file open mode", + ) + add_option_ini( + "--log-file-level", + dest="log_file_level", + default=None, + help="Log file logging level", + ) + add_option_ini( + "--log-file-format", + dest="log_file_format", + default=None, + help="Log format used by the logging module", + ) + add_option_ini( + "--log-file-date-format", + dest="log_file_date_format", + default=None, + help="Log date format used by the logging module", + ) + add_option_ini( + "--log-auto-indent", + dest="log_auto_indent", + default=None, + help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", + ) + group.addoption( + "--log-disable", + action="append", + default=[], + dest="logger_disable", + help="Disable a logger by name. Can be passed multiple times.", + ) + + +_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) + + +# Not using @contextmanager for performance reasons. +class catching_logs(Generic[_HandlerType]): + """Context manager that prepares the whole logging machinery properly.""" + + __slots__ = ("handler", "level", "orig_level") + + def __init__(self, handler: _HandlerType, level: int | None = None) -> None: + self.handler = handler + self.level = level + + def __enter__(self) -> _HandlerType: + root_logger = logging.getLogger() + if self.level is not None: + self.handler.setLevel(self.level) + root_logger.addHandler(self.handler) + if self.level is not None: + self.orig_level = root_logger.level + root_logger.setLevel(min(self.orig_level, self.level)) + return self.handler + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + root_logger = logging.getLogger() + if self.level is not None: + root_logger.setLevel(self.orig_level) + root_logger.removeHandler(self.handler) + + +class LogCaptureHandler(logging_StreamHandler): + """A logging handler that stores log records and the log text.""" + + def __init__(self) -> None: + """Create a new log handler.""" + super().__init__(StringIO()) + self.records: list[logging.LogRecord] = [] + + def emit(self, record: logging.LogRecord) -> None: + """Keep the log records in a list in addition to the log text.""" + self.records.append(record) + super().emit(record) + + def reset(self) -> None: + self.records = [] + self.stream = StringIO() + + def clear(self) -> None: + self.records.clear() + self.stream = StringIO() + + def handleError(self, record: logging.LogRecord) -> None: + if logging.raiseExceptions: + # Fail the test if the log message is bad (emit failed). + # The default behavior of logging is to print "Logging error" + # to stderr with the call stack and some extra details. + # pytest wants to make such mistakes visible during testing. + raise # noqa: PLE0704 + + +@final +class LogCaptureFixture: + """Provides access and control of log capturing.""" + + def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._item = item + self._initial_handler_level: int | None = None + # Dict of log name -> log level. + self._initial_logger_levels: dict[str | None, int] = {} + self._initial_disabled_logging_level: int | None = None + + def _finalize(self) -> None: + """Finalize the fixture. + + This restores the log levels and the disabled logging levels changed by :meth:`set_level`. + """ + # Restore log levels. + if self._initial_handler_level is not None: + self.handler.setLevel(self._initial_handler_level) + for logger_name, level in self._initial_logger_levels.items(): + logger = logging.getLogger(logger_name) + logger.setLevel(level) + # Disable logging at the original disabled logging level. + if self._initial_disabled_logging_level is not None: + logging.disable(self._initial_disabled_logging_level) + self._initial_disabled_logging_level = None + + @property + def handler(self) -> LogCaptureHandler: + """Get the logging handler used by the fixture.""" + return self._item.stash[caplog_handler_key] + + def get_records( + self, when: Literal["setup", "call", "teardown"] + ) -> list[logging.LogRecord]: + """Get the logging records for one of the possible test phases. + + :param when: + Which test phase to obtain the records from. + Valid values are: "setup", "call" and "teardown". + + :returns: The list of captured records at the given stage. + + .. versionadded:: 3.4 + """ + return self._item.stash[caplog_records_key].get(when, []) + + @property + def text(self) -> str: + """The formatted log text.""" + return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) + + @property + def records(self) -> list[logging.LogRecord]: + """The list of log records.""" + return self.handler.records + + @property + def record_tuples(self) -> list[tuple[str, int, str]]: + """A list of a stripped down version of log records intended + for use in assertion comparison. + + The format of the tuple is: + + (logger_name, log_level, message) + """ + return [(r.name, r.levelno, r.getMessage()) for r in self.records] + + @property + def messages(self) -> list[str]: + """A list of format-interpolated log messages. + + Unlike 'records', which contains the format string and parameters for + interpolation, log messages in this list are all interpolated. + + Unlike 'text', which contains the output from the handler, log + messages in this list are unadorned with levels, timestamps, etc, + making exact comparisons more reliable. + + Note that traceback or stack info (from :func:`logging.exception` or + the `exc_info` or `stack_info` arguments to the logging functions) is + not included, as this is added by the formatter in the handler. + + .. versionadded:: 3.7 + """ + return [r.getMessage() for r in self.records] + + def clear(self) -> None: + """Reset the list of log records and the captured log text.""" + self.handler.clear() + + def _force_enable_logging( + self, level: int | str, logger_obj: logging.Logger + ) -> int: + """Enable the desired logging level if the global level was disabled via ``logging.disabled``. + + Only enables logging levels greater than or equal to the requested ``level``. + + Does nothing if the desired ``level`` wasn't disabled. + + :param level: + The logger level caplog should capture. + All logging is enabled if a non-standard logging level string is supplied. + Valid level strings are in :data:`logging._nameToLevel`. + :param logger_obj: The logger object to check. + + :return: The original disabled logging level. + """ + original_disable_level: int = logger_obj.manager.disable + + if isinstance(level, str): + # Try to translate the level string to an int for `logging.disable()` + level = logging.getLevelName(level) + + if not isinstance(level, int): + # The level provided was not valid, so just un-disable all logging. + logging.disable(logging.NOTSET) + elif not logger_obj.isEnabledFor(level): + # Each level is `10` away from other levels. + # https://docs.python.org/3/library/logging.html#logging-levels + disable_level = max(level - 10, logging.NOTSET) + logging.disable(disable_level) + + return original_disable_level + + def set_level(self, level: int | str, logger: str | None = None) -> None: + """Set the threshold level of a logger for the duration of a test. + + Logging messages which are less severe than this level will not be captured. + + .. versionchanged:: 3.4 + The levels of the loggers changed by this function will be + restored to their initial values at the end of the test. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + # Save the original log-level to restore it during teardown. + self._initial_logger_levels.setdefault(logger, logger_obj.level) + logger_obj.setLevel(level) + if self._initial_handler_level is None: + self._initial_handler_level = self.handler.level + self.handler.setLevel(level) + initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) + if self._initial_disabled_logging_level is None: + self._initial_disabled_logging_level = initial_disabled_logging_level + + @contextmanager + def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]: + """Context manager that sets the level for capturing of logs. After + the end of the 'with' statement the level is restored to its original + value. + + Will enable the requested logging level if it was disabled via :func:`logging.disable`. + + :param level: The level. + :param logger: The logger to update. If not given, the root logger. + """ + logger_obj = logging.getLogger(logger) + orig_level = logger_obj.level + logger_obj.setLevel(level) + handler_orig_level = self.handler.level + self.handler.setLevel(level) + original_disable_level = self._force_enable_logging(level, logger_obj) + try: + yield + finally: + logger_obj.setLevel(orig_level) + self.handler.setLevel(handler_orig_level) + logging.disable(original_disable_level) + + @contextmanager + def filtering(self, filter_: logging.Filter) -> Generator[None]: + """Context manager that temporarily adds the given filter to the caplog's + :meth:`handler` for the 'with' statement block, and removes that filter at the + end of the block. + + :param filter_: A custom :class:`logging.Filter` object. + + .. versionadded:: 7.5 + """ + self.handler.addFilter(filter_) + try: + yield + finally: + self.handler.removeFilter(filter_) + + +@fixture +def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]: + """Access and control log capturing. + + Captured logs are available through the following properties/methods:: + + * caplog.messages -> list of format-interpolated log messages + * caplog.text -> string containing formatted log output + * caplog.records -> list of logging.LogRecord instances + * caplog.record_tuples -> list of (logger_name, level, message) tuples + * caplog.clear() -> clear captured records and formatted log output string + """ + result = LogCaptureFixture(request.node, _ispytest=True) + yield result + result._finalize() + + +def get_log_level_for_setting(config: Config, *setting_names: str) -> int | None: + for setting_name in setting_names: + log_level = config.getoption(setting_name) + if log_level is None: + log_level = config.getini(setting_name) + if log_level: + break + else: + return None + + if isinstance(log_level, str): + log_level = log_level.upper() + try: + return int(getattr(logging, log_level, log_level)) + except ValueError as e: + # Python logging does not recognise this as a logging level + raise UsageError( + f"'{log_level}' is not recognized as a logging level name for " + f"'{setting_name}'. Please consider passing the " + "logging level num instead." + ) from e + + +# run after terminalreporter/capturemanager are configured +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") + + +class LoggingPlugin: + """Attaches to the logging module and captures log messages for each test.""" + + def __init__(self, config: Config) -> None: + """Create a new plugin to capture log messages. + + The formatter can be safely shared across all handlers so + create a single one for the entire test session here. + """ + self._config = config + + # Report logging. + self.formatter = self._create_formatter( + get_option_ini(config, "log_format"), + get_option_ini(config, "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_level = get_log_level_for_setting(config, "log_level") + self.caplog_handler = LogCaptureHandler() + self.caplog_handler.setFormatter(self.formatter) + self.report_handler = LogCaptureHandler() + self.report_handler.setFormatter(self.formatter) + + # File logging. + self.log_file_level = get_log_level_for_setting( + config, "log_file_level", "log_level" + ) + log_file = get_option_ini(config, "log_file") or os.devnull + if log_file != os.devnull: + directory = os.path.dirname(os.path.abspath(log_file)) + if not os.path.isdir(directory): + os.makedirs(directory) + + self.log_file_mode = get_option_ini(config, "log_file_mode") or "w" + self.log_file_handler = _FileHandler( + log_file, mode=self.log_file_mode, encoding="UTF-8" + ) + log_file_format = get_option_ini(config, "log_file_format", "log_format") + log_file_date_format = get_option_ini( + config, "log_file_date_format", "log_date_format" + ) + + log_file_formatter = DatetimeFormatter( + log_file_format, datefmt=log_file_date_format + ) + self.log_file_handler.setFormatter(log_file_formatter) + + # CLI/live logging. + self.log_cli_level = get_log_level_for_setting( + config, "log_cli_level", "log_level" + ) + if self._log_cli_enabled(): + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") + # Guaranteed by `_log_cli_enabled()`. + assert terminal_reporter is not None + capture_manager = config.pluginmanager.get_plugin("capturemanager") + # if capturemanager plugin is disabled, live logging still works. + self.log_cli_handler: ( + _LiveLoggingStreamHandler | _LiveLoggingNullHandler + ) = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) + else: + self.log_cli_handler = _LiveLoggingNullHandler() + log_cli_formatter = self._create_formatter( + get_option_ini(config, "log_cli_format", "log_format"), + get_option_ini(config, "log_cli_date_format", "log_date_format"), + get_option_ini(config, "log_auto_indent"), + ) + self.log_cli_handler.setFormatter(log_cli_formatter) + self._disable_loggers(loggers_to_disable=config.option.logger_disable) + + def _disable_loggers(self, loggers_to_disable: list[str]) -> None: + if not loggers_to_disable: + return + + for name in loggers_to_disable: + logger = logging.getLogger(name) + logger.disabled = True + + def _create_formatter(self, log_format, log_date_format, auto_indent): + # Color option doesn't exist if terminal plugin is disabled. + color = getattr(self._config.option, "color", "no") + if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( + log_format + ): + formatter: logging.Formatter = ColoredLevelFormatter( + create_terminal_writer(self._config), log_format, log_date_format + ) + else: + formatter = DatetimeFormatter(log_format, log_date_format) + + formatter._style = PercentStyleMultiline( + formatter._style._fmt, auto_indent=auto_indent + ) + + return formatter + + def set_log_path(self, fname: str) -> None: + """Set the filename parameter for Logging.FileHandler(). + + Creates parent directory if it does not exist. + + .. warning:: + This is an experimental API. + """ + fpath = Path(fname) + + if not fpath.is_absolute(): + fpath = self._config.rootpath / fpath + + if not fpath.parent.exists(): + fpath.parent.mkdir(exist_ok=True, parents=True) + + # https://github.com/python/mypy/issues/11193 + stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding="UTF-8") # type: ignore[assignment] + old_stream = self.log_file_handler.setStream(stream) + if old_stream: + old_stream.close() + + def _log_cli_enabled(self) -> bool: + """Return whether live logging is enabled.""" + enabled = self._config.getoption( + "--log-cli-level" + ) is not None or self._config.getini("log_cli") + if not enabled: + return False + + terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") + if terminal_reporter is None: + # terminal reporter is disabled e.g. by pytest-xdist. + return False + + return True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionstart(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionstart") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_collection(self) -> Generator[None]: + self.log_cli_handler.set_when("collection") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl(wrapper=True) + def pytest_runtestloop(self, session: Session) -> Generator[None, object, object]: + if session.config.option.collectonly: + return (yield) + + if self._log_cli_enabled() and self._config.get_verbosity() < 1: + # The verbose flag is needed to avoid messy test progress output. + self._config.option.verbose = 1 + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) # Run all the tests. + + @hookimpl + def pytest_runtest_logstart(self) -> None: + self.log_cli_handler.reset() + self.log_cli_handler.set_when("start") + + @hookimpl + def pytest_runtest_logreport(self) -> None: + self.log_cli_handler.set_when("logreport") + + @contextmanager + def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]: + """Implement the internals of the pytest_runtest_xxx() hooks.""" + with ( + catching_logs( + self.caplog_handler, + level=self.log_level, + ) as caplog_handler, + catching_logs( + self.report_handler, + level=self.log_level, + ) as report_handler, + ): + caplog_handler.reset() + report_handler.reset() + item.stash[caplog_records_key][when] = caplog_handler.records + item.stash[caplog_handler_key] = caplog_handler + + try: + yield + finally: + log = report_handler.stream.getvalue().strip() + item.add_report_section(when, "log", log) + + @hookimpl(wrapper=True) + def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("setup") + + empty: dict[str, list[logging.LogRecord]] = {} + item.stash[caplog_records_key] = empty + with self._runtest_for(item, "setup"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("call") + + with self._runtest_for(item, "call"): + yield + + @hookimpl(wrapper=True) + def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]: + self.log_cli_handler.set_when("teardown") + + try: + with self._runtest_for(item, "teardown"): + yield + finally: + del item.stash[caplog_records_key] + del item.stash[caplog_handler_key] + + @hookimpl + def pytest_runtest_logfinish(self) -> None: + self.log_cli_handler.set_when("finish") + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_sessionfinish(self) -> Generator[None]: + self.log_cli_handler.set_when("sessionfinish") + + with catching_logs(self.log_cli_handler, level=self.log_cli_level): + with catching_logs(self.log_file_handler, level=self.log_file_level): + return (yield) + + @hookimpl + def pytest_unconfigure(self) -> None: + # Close the FileHandler explicitly. + # (logging.shutdown might have lost the weakref?!) + self.log_file_handler.close() + + +class _FileHandler(logging.FileHandler): + """A logging FileHandler with pytest tweaks.""" + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingStreamHandler(logging_StreamHandler): + """A logging StreamHandler used by the live logging feature: it will + write a newline before the first log message in each test. + + During live logging we must also explicitly disable stdout/stderr + capturing otherwise it will get captured and won't appear in the + terminal. + """ + + # Officially stream needs to be a IO[str], but TerminalReporter + # isn't. So force it. + stream: TerminalReporter = None # type: ignore + + def __init__( + self, + terminal_reporter: TerminalReporter, + capture_manager: CaptureManager | None, + ) -> None: + super().__init__(stream=terminal_reporter) # type: ignore[arg-type] + self.capture_manager = capture_manager + self.reset() + self.set_when(None) + self._test_outcome_written = False + + def reset(self) -> None: + """Reset the handler; should be called before the start of each test.""" + self._first_record_emitted = False + + def set_when(self, when: str | None) -> None: + """Prepare for the given test phase (setup/call/teardown).""" + self._when = when + self._section_name_shown = False + if when == "start": + self._test_outcome_written = False + + def emit(self, record: logging.LogRecord) -> None: + ctx_manager = ( + self.capture_manager.global_and_fixture_disabled() + if self.capture_manager + else nullcontext() + ) + with ctx_manager: + if not self._first_record_emitted: + self.stream.write("\n") + self._first_record_emitted = True + elif self._when in ("teardown", "finish"): + if not self._test_outcome_written: + self._test_outcome_written = True + self.stream.write("\n") + if not self._section_name_shown and self._when: + self.stream.section("live log " + self._when, sep="-", bold=True) + self._section_name_shown = True + super().emit(record) + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass + + +class _LiveLoggingNullHandler(logging.NullHandler): + """A logging handler used when live logging is disabled.""" + + def reset(self) -> None: + pass + + def set_when(self, when: str) -> None: + pass + + def handleError(self, record: logging.LogRecord) -> None: + # Handled by LogCaptureHandler. + pass diff --git a/.venv/lib/python3.12/site-packages/_pytest/main.py b/.venv/lib/python3.12/site-packages/_pytest/main.py new file mode 100644 index 0000000..9bc930d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/main.py @@ -0,0 +1,1203 @@ +"""Core implementation of the testing process: init, session, runtest loop.""" + +from __future__ import annotations + +import argparse +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Sequence +from collections.abc import Set as AbstractSet +import dataclasses +import fnmatch +import functools +import importlib +import importlib.util +import os +from pathlib import Path +import sys +from typing import final +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import warnings + +import pluggy + +from _pytest import nodes +import _pytest._code +from _pytest.config import Config +from _pytest.config import directory_arg +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import PytestPluginManager +from _pytest.config import UsageError +from _pytest.config.argparsing import OverrideIniAction +from _pytest.config.argparsing import Parser +from _pytest.config.compat import PathAwareHookProxy +from _pytest.outcomes import exit +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import fnmatch_ex +from _pytest.pathlib import safe_exists +from _pytest.pathlib import samefile_nofollow +from _pytest.pathlib import scandir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.runner import collect_one_node +from _pytest.runner import SetupState +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + from _pytest.fixtures import FixtureManager + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( # private to use reserved lower-case short option + "-x", + "--exitfirst", + action="store_const", + dest="maxfail", + const=1, + help="Exit instantly on first error or failed test", + ) + group.addoption( + "--maxfail", + metavar="num", + action="store", + type=int, + dest="maxfail", + default=0, + help="Exit after first num failures or errors", + ) + group.addoption( + "--strict-config", + action=OverrideIniAction, + ini_option="strict_config", + ini_value="true", + help="Enables the strict_config option", + ) + group.addoption( + "--strict-markers", + action=OverrideIniAction, + ini_option="strict_markers", + ini_value="true", + help="Enables the strict_markers option", + ) + group.addoption( + "--strict", + action=OverrideIniAction, + ini_option="strict", + ini_value="true", + help="Enables the strict option", + ) + parser.addini( + "strict_config", + "Any warnings encountered while parsing the `pytest` section of the " + "configuration file raise errors", + type="bool", + # None => fallback to `strict`. + default=None, + ) + parser.addini( + "strict_markers", + "Markers not registered in the `markers` section of the configuration " + "file raise errors", + type="bool", + # None => fallback to `strict`. + default=None, + ) + parser.addini( + "strict", + "Enables all strictness options, currently: " + "strict_config, strict_markers, strict_xfail, strict_parametrization_ids", + type="bool", + default=False, + ) + + group = parser.getgroup("pytest-warnings") + group.addoption( + "-W", + "--pythonwarnings", + action="append", + help="Set which warnings to report, see -W option of Python itself", + ) + parser.addini( + "filterwarnings", + type="linelist", + help="Each line specifies a pattern for " + "warnings.filterwarnings. " + "Processed after -W/--pythonwarnings.", + ) + + group = parser.getgroup("collect", "collection") + group.addoption( + "--collectonly", + "--collect-only", + "--co", + action="store_true", + help="Only collect tests, don't execute them", + ) + group.addoption( + "--pyargs", + action="store_true", + help="Try to interpret all arguments as Python packages", + ) + group.addoption( + "--ignore", + action="append", + metavar="path", + help="Ignore path during collection (multi-allowed)", + ) + group.addoption( + "--ignore-glob", + action="append", + metavar="path", + help="Ignore path pattern during collection (multi-allowed)", + ) + group.addoption( + "--deselect", + action="append", + metavar="nodeid_prefix", + help="Deselect item (via node id prefix) during collection (multi-allowed)", + ) + group.addoption( + "--confcutdir", + dest="confcutdir", + default=None, + metavar="dir", + type=functools.partial(directory_arg, optname="--confcutdir"), + help="Only load conftest.py's relative to specified dir", + ) + group.addoption( + "--noconftest", + action="store_true", + dest="noconftest", + default=False, + help="Don't load any conftest.py files", + ) + group.addoption( + "--keepduplicates", + "--keep-duplicates", + action="store_true", + dest="keepduplicates", + default=False, + help="Keep duplicate tests", + ) + group.addoption( + "--collect-in-virtualenv", + action="store_true", + dest="collect_in_virtualenv", + default=False, + help="Don't ignore tests in a local virtualenv directory", + ) + group.addoption( + "--continue-on-collection-errors", + action="store_true", + default=False, + dest="continue_on_collection_errors", + help="Force test execution even if collection errors occur", + ) + group.addoption( + "--import-mode", + default="prepend", + choices=["prepend", "append", "importlib"], + dest="importmode", + help="Prepend/append to sys.path when importing test modules and conftest " + "files. Default: prepend.", + ) + parser.addini( + "norecursedirs", + "Directory patterns to avoid for recursion", + type="args", + default=[ + "*.egg", + ".*", + "_darcs", + "build", + "CVS", + "dist", + "node_modules", + "venv", + "{arch}", + ], + ) + parser.addini( + "testpaths", + "Directories to search for tests when no files or directories are given on the " + "command line", + type="args", + default=[], + ) + parser.addini( + "collect_imported_tests", + "Whether to collect tests in imported modules outside `testpaths`", + type="bool", + default=True, + ) + parser.addini( + "consider_namespace_packages", + type="bool", + default=False, + help="Consider namespace packages when resolving module names during import", + ) + + group = parser.getgroup("debugconfig", "test session debugging and configuration") + group._addoption( # private to use reserved lower-case short option + "-c", + "--config-file", + metavar="FILE", + type=str, + dest="inifilename", + help="Load configuration from `FILE` instead of trying to locate one of the " + "implicit configuration files.", + ) + group.addoption( + "--rootdir", + action="store", + dest="rootdir", + help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " + "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " + "'$HOME/root_dir'.", + ) + group.addoption( + "--basetemp", + dest="basetemp", + default=None, + type=validate_basetemp, + metavar="dir", + help=( + "Base temporary directory for this test run. " + "(Warning: this directory is removed if it exists.)" + ), + ) + + +def validate_basetemp(path: str) -> str: + # GH 7119 + msg = "basetemp must not be empty, the current working directory or any parent directory of it" + + # empty path + if not path: + raise argparse.ArgumentTypeError(msg) + + def is_ancestor(base: Path, query: Path) -> bool: + """Return whether query is an ancestor of base.""" + if base == query: + return True + return query in base.parents + + # check if path is an ancestor of cwd + if is_ancestor(Path.cwd(), Path(path).absolute()): + raise argparse.ArgumentTypeError(msg) + + # check symlinks for ancestors + if is_ancestor(Path.cwd().resolve(), Path(path).resolve()): + raise argparse.ArgumentTypeError(msg) + + return path + + +def wrap_session( + config: Config, doit: Callable[[Config, Session], int | ExitCode | None] +) -> int | ExitCode: + """Skeleton command line program.""" + session = Session.from_config(config) + session.exitstatus = ExitCode.OK + initstate = 0 + try: + try: + config._do_configure() + initstate = 1 + config.hook.pytest_sessionstart(session=session) + initstate = 2 + session.exitstatus = doit(config, session) or 0 + except UsageError: + session.exitstatus = ExitCode.USAGE_ERROR + raise + except Failed: + session.exitstatus = ExitCode.TESTS_FAILED + except (KeyboardInterrupt, exit.Exception): + excinfo = _pytest._code.ExceptionInfo.from_current() + exitstatus: int | ExitCode = ExitCode.INTERRUPTED + if isinstance(excinfo.value, exit.Exception): + if excinfo.value.returncode is not None: + exitstatus = excinfo.value.returncode + if initstate < 2: + sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") + config.hook.pytest_keyboard_interrupt(excinfo=excinfo) + session.exitstatus = exitstatus + except BaseException: + session.exitstatus = ExitCode.INTERNAL_ERROR + excinfo = _pytest._code.ExceptionInfo.from_current() + try: + config.notify_exception(excinfo, config.option) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + else: + if isinstance(excinfo.value, SystemExit): + sys.stderr.write("mainloop: caught unexpected SystemExit!\n") + + finally: + # Explicitly break reference cycle. + excinfo = None # type: ignore + os.chdir(session.startpath) + if initstate >= 2: + try: + config.hook.pytest_sessionfinish( + session=session, exitstatus=session.exitstatus + ) + except exit.Exception as exc: + if exc.returncode is not None: + session.exitstatus = exc.returncode + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") + config._ensure_unconfigure() + return session.exitstatus + + +def pytest_cmdline_main(config: Config) -> int | ExitCode: + return wrap_session(config, _main) + + +def _main(config: Config, session: Session) -> int | ExitCode | None: + """Default command line protocol for initialization, session, + running tests and reporting.""" + config.hook.pytest_collection(session=session) + config.hook.pytest_runtestloop(session=session) + + if session.testsfailed: + return ExitCode.TESTS_FAILED + elif session.testscollected == 0: + return ExitCode.NO_TESTS_COLLECTED + return None + + +def pytest_collection(session: Session) -> None: + session.perform_collect() + + +def pytest_runtestloop(session: Session) -> bool: + if session.testsfailed and not session.config.option.continue_on_collection_errors: + raise session.Interrupted( + f"{session.testsfailed} error{'s' if session.testsfailed != 1 else ''} during collection" + ) + + if session.config.option.collectonly: + return True + + for i, item in enumerate(session.items): + nextitem = session.items[i + 1] if i + 1 < len(session.items) else None + item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) + if session.shouldfail: + raise session.Failed(session.shouldfail) + if session.shouldstop: + raise session.Interrupted(session.shouldstop) + return True + + +def _in_venv(path: Path) -> bool: + """Attempt to detect if ``path`` is the root of a Virtual Environment by + checking for the existence of the pyvenv.cfg file. + + [https://peps.python.org/pep-0405/] + + For regression protection we also check for conda environments that do not include pyenv.cfg yet -- + https://github.com/conda/conda/issues/13337 is the conda issue tracking adding pyenv.cfg. + + Checking for the `conda-meta/history` file per https://github.com/pytest-dev/pytest/issues/12652#issuecomment-2246336902. + + """ + try: + return ( + path.joinpath("pyvenv.cfg").is_file() + or path.joinpath("conda-meta", "history").is_file() + ) + except OSError: + return False + + +def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None: + if collection_path.name == "__pycache__": + return True + + ignore_paths = config._getconftest_pathlist( + "collect_ignore", path=collection_path.parent + ) + ignore_paths = ignore_paths or [] + excludeopt = config.getoption("ignore") + if excludeopt: + ignore_paths.extend(absolutepath(x) for x in excludeopt) + + if collection_path in ignore_paths: + return True + + ignore_globs = config._getconftest_pathlist( + "collect_ignore_glob", path=collection_path.parent + ) + ignore_globs = ignore_globs or [] + excludeglobopt = config.getoption("ignore_glob") + if excludeglobopt: + ignore_globs.extend(absolutepath(x) for x in excludeglobopt) + + if any(fnmatch.fnmatch(str(collection_path), str(glob)) for glob in ignore_globs): + return True + + allow_in_venv = config.getoption("collect_in_virtualenv") + if not allow_in_venv and _in_venv(collection_path): + return True + + if collection_path.is_dir(): + norecursepatterns = config.getini("norecursedirs") + if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns): + return True + + return None + + +def pytest_collect_directory( + path: Path, parent: nodes.Collector +) -> nodes.Collector | None: + return Dir.from_parent(parent, path=path) + + +def pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> None: + deselect_prefixes = tuple(config.getoption("deselect") or []) + if not deselect_prefixes: + return + + remaining = [] + deselected = [] + for colitem in items: + if colitem.nodeid.startswith(deselect_prefixes): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +class FSHookProxy: + def __init__( + self, + pm: PytestPluginManager, + remove_mods: AbstractSet[object], + ) -> None: + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name: str) -> pluggy.HookCaller: + x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) + self.__dict__[name] = x + return x + + +class Interrupted(KeyboardInterrupt): + """Signals that the test run was interrupted.""" + + __module__ = "builtins" # For py3. + + +class Failed(Exception): + """Signals a stop as failed test run.""" + + +@dataclasses.dataclass +class _bestrelpath_cache(dict[Path, str]): + __slots__ = ("path",) + + path: Path + + def __missing__(self, path: Path) -> str: + r = bestrelpath(self.path, path) + self[path] = r + return r + + +@final +class Dir(nodes.Directory): + """Collector of files in a file system directory. + + .. versionadded:: 8.0 + + .. note:: + + Python directories with an `__init__.py` file are instead collected by + :class:`~pytest.Package` by default. Both are :class:`~pytest.Directory` + collectors. + """ + + @classmethod + def from_parent( # type: ignore[override] + cls, + parent: nodes.Collector, + *, + path: Path, + ) -> Self: + """The public constructor. + + :param parent: The parent collector of this Dir. + :param path: The directory's path. + :type path: pathlib.Path + """ + return super().from_parent(parent=parent, path=path) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + config = self.config + col: nodes.Collector | None + cols: Sequence[nodes.Collector] + ihook = self.ihook + for direntry in scandir(self.path): + if direntry.is_dir(): + path = Path(direntry.path) + if not self.session.isinitpath(path, with_parents=True): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + col = ihook.pytest_collect_directory(path=path, parent=self) + if col is not None: + yield col + + elif direntry.is_file(): + path = Path(direntry.path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + cols = ihook.pytest_collect_file(file_path=path, parent=self) + yield from cols + + +@final +class Session(nodes.Collector): + """The root of the collection tree. + + ``Session`` collects the initial paths given as arguments to pytest. + """ + + Interrupted = Interrupted + Failed = Failed + # Set on the session by runner.pytest_sessionstart. + _setupstate: SetupState + # Set on the session by fixtures.pytest_sessionstart. + _fixturemanager: FixtureManager + exitstatus: int | ExitCode + + def __init__(self, config: Config) -> None: + super().__init__( + name="", + path=config.rootpath, + fspath=None, + parent=None, + config=config, + session=self, + nodeid="", + ) + self.testsfailed = 0 + self.testscollected = 0 + self._shouldstop: bool | str = False + self._shouldfail: bool | str = False + self.trace = config.trace.root.get("collection") + self._initialpaths: frozenset[Path] = frozenset() + self._initialpaths_with_parents: frozenset[Path] = frozenset() + self._notfound: list[tuple[str, Sequence[nodes.Collector]]] = [] + self._initial_parts: list[CollectionArgument] = [] + self._collection_cache: dict[nodes.Collector, CollectReport] = {} + self.items: list[nodes.Item] = [] + + self._bestrelpathcache: dict[Path, str] = _bestrelpath_cache(config.rootpath) + + self.config.pluginmanager.register(self, name="session") + + @classmethod + def from_config(cls, config: Config) -> Session: + session: Session = cls._create(config=config) + return session + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} {self.name} " + f"exitstatus=%r " + f"testsfailed={self.testsfailed} " + f"testscollected={self.testscollected}>" + ) % getattr(self, "exitstatus", "") + + @property + def shouldstop(self) -> bool | str: + return self._shouldstop + + @shouldstop.setter + def shouldstop(self, value: bool | str) -> None: + # The runner checks shouldfail and assumes that if it is set we are + # definitely stopping, so prevent unsetting it. + if value is False and self._shouldstop: + warnings.warn( + PytestWarning( + "session.shouldstop cannot be unset after it has been set; ignoring." + ), + stacklevel=2, + ) + return + self._shouldstop = value + + @property + def shouldfail(self) -> bool | str: + return self._shouldfail + + @shouldfail.setter + def shouldfail(self, value: bool | str) -> None: + # The runner checks shouldfail and assumes that if it is set we are + # definitely stopping, so prevent unsetting it. + if value is False and self._shouldfail: + warnings.warn( + PytestWarning( + "session.shouldfail cannot be unset after it has been set; ignoring." + ), + stacklevel=2, + ) + return + self._shouldfail = value + + @property + def startpath(self) -> Path: + """The path from which pytest was invoked. + + .. versionadded:: 7.0.0 + """ + return self.config.invocation_params.dir + + def _node_location_to_relpath(self, node_path: Path) -> str: + # bestrelpath is a quite slow function. + return self._bestrelpathcache[node_path] + + @hookimpl(tryfirst=True) + def pytest_collectstart(self) -> None: + if self.shouldfail: + raise self.Failed(self.shouldfail) + if self.shouldstop: + raise self.Interrupted(self.shouldstop) + + @hookimpl(tryfirst=True) + def pytest_runtest_logreport(self, report: TestReport | CollectReport) -> None: + if report.failed and not hasattr(report, "wasxfail"): + self.testsfailed += 1 + maxfail = self.config.getvalue("maxfail") + if maxfail and self.testsfailed >= maxfail: + self.shouldfail = f"stopping after {self.testsfailed} failures" + + pytest_collectreport = pytest_runtest_logreport + + def isinitpath( + self, + path: str | os.PathLike[str], + *, + with_parents: bool = False, + ) -> bool: + """Is path an initial path? + + An initial path is a path explicitly given to pytest on the command + line. + + :param with_parents: + If set, also return True if the path is a parent of an initial path. + + .. versionchanged:: 8.0 + Added the ``with_parents`` parameter. + """ + # Optimization: Path(Path(...)) is much slower than isinstance. + path_ = path if isinstance(path, Path) else Path(path) + if with_parents: + return path_ in self._initialpaths_with_parents + else: + return path_ in self._initialpaths + + def gethookproxy(self, fspath: os.PathLike[str]) -> pluggy.HookRelay: + # Optimization: Path(Path(...)) is much slower than isinstance. + path = fspath if isinstance(fspath, Path) else Path(fspath) + pm = self.config.pluginmanager + # Check if we have the common case of running + # hooks with all conftest.py files. + my_conftestmodules = pm._getconftestmodules(path) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + proxy: pluggy.HookRelay + if remove_mods: + # One or more conftests are not in use at this path. + proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment] + else: + # All plugins are active for this fspath. + proxy = self.config.hook + return proxy + + def _collect_path( + self, + path: Path, + path_cache: dict[Path, Sequence[nodes.Collector]], + ) -> Sequence[nodes.Collector]: + """Create a Collector for the given path. + + `path_cache` makes it so the same Collectors are returned for the same + path. + """ + if path in path_cache: + return path_cache[path] + + if path.is_dir(): + ihook = self.gethookproxy(path.parent) + col: nodes.Collector | None = ihook.pytest_collect_directory( + path=path, parent=self + ) + cols: Sequence[nodes.Collector] = (col,) if col is not None else () + + elif path.is_file(): + ihook = self.gethookproxy(path) + cols = ihook.pytest_collect_file(file_path=path, parent=self) + + else: + # Broken symlink or invalid/missing file. + cols = () + + path_cache[path] = cols + return cols + + @overload + def perform_collect( + self, args: Sequence[str] | None = ..., genitems: Literal[True] = ... + ) -> Sequence[nodes.Item]: ... + + @overload + def perform_collect( + self, args: Sequence[str] | None = ..., genitems: bool = ... + ) -> Sequence[nodes.Item | nodes.Collector]: ... + + def perform_collect( + self, args: Sequence[str] | None = None, genitems: bool = True + ) -> Sequence[nodes.Item | nodes.Collector]: + """Perform the collection phase for this session. + + This is called by the default :hook:`pytest_collection` hook + implementation; see the documentation of this hook for more details. + For testing purposes, it may also be called directly on a fresh + ``Session``. + + This function normally recursively expands any collectors collected + from the session to their items, and only items are returned. For + testing purposes, this may be suppressed by passing ``genitems=False``, + in which case the return value contains these collectors unexpanded, + and ``session.items`` is empty. + """ + if args is None: + args = self.config.args + + self.trace("perform_collect", self, args) + self.trace.root.indent += 1 + + hook = self.config.hook + + self._notfound = [] + self._initial_parts = [] + self._collection_cache = {} + self.items = [] + items: Sequence[nodes.Item | nodes.Collector] = self.items + consider_namespace_packages: bool = self.config.getini( + "consider_namespace_packages" + ) + try: + initialpaths: list[Path] = [] + initialpaths_with_parents: list[Path] = [] + + collection_args = [ + resolve_collection_argument( + self.config.invocation_params.dir, + arg, + i, + as_pypath=self.config.option.pyargs, + consider_namespace_packages=consider_namespace_packages, + ) + for i, arg in enumerate(args) + ] + + if not self.config.getoption("keepduplicates"): + # Normalize the collection arguments -- remove duplicates and overlaps. + self._initial_parts = normalize_collection_arguments(collection_args) + else: + self._initial_parts = collection_args + + for collection_argument in self._initial_parts: + initialpaths.append(collection_argument.path) + initialpaths_with_parents.append(collection_argument.path) + initialpaths_with_parents.extend(collection_argument.path.parents) + self._initialpaths = frozenset(initialpaths) + self._initialpaths_with_parents = frozenset(initialpaths_with_parents) + + rep = collect_one_node(self) + self.ihook.pytest_collectreport(report=rep) + self.trace.root.indent -= 1 + if self._notfound: + errors = [] + for arg, collectors in self._notfound: + if collectors: + errors.append( + f"not found: {arg}\n(no match in any of {collectors!r})" + ) + else: + errors.append(f"found no collectors for {arg}") + + raise UsageError(*errors) + + if not genitems: + items = rep.result + else: + if rep.passed: + for node in rep.result: + self.items.extend(self.genitems(node)) + + self.config.pluginmanager.check_pending() + hook.pytest_collection_modifyitems( + session=self, config=self.config, items=items + ) + finally: + self._notfound = [] + self._initial_parts = [] + self._collection_cache = {} + hook.pytest_collection_finish(session=self) + + if genitems: + self.testscollected = len(items) + + return items + + def _collect_one_node( + self, + node: nodes.Collector, + handle_dupes: bool = True, + ) -> tuple[CollectReport, bool]: + if node in self._collection_cache and handle_dupes: + rep = self._collection_cache[node] + return rep, True + else: + rep = collect_one_node(node) + self._collection_cache[node] = rep + return rep, False + + def collect(self) -> Iterator[nodes.Item | nodes.Collector]: + # This is a cache for the root directories of the initial paths. + # We can't use collection_cache for Session because of its special + # role as the bootstrapping collector. + path_cache: dict[Path, Sequence[nodes.Collector]] = {} + + pm = self.config.pluginmanager + + for collection_argument in self._initial_parts: + self.trace("processing argument", collection_argument) + self.trace.root.indent += 1 + + argpath = collection_argument.path + names = collection_argument.parts + parametrization = collection_argument.parametrization + module_name = collection_argument.module_name + + # resolve_collection_argument() ensures this. + if argpath.is_dir(): + assert not names, f"invalid arg {(argpath, names)!r}" + + paths = [argpath] + # Add relevant parents of the path, from the root, e.g. + # /a/b/c.py -> [/, /a, /a/b, /a/b/c.py] + if module_name is None: + # Paths outside of the confcutdir should not be considered. + for path in argpath.parents: + if not pm._is_in_confcutdir(path): + break + paths.insert(0, path) + else: + # For --pyargs arguments, only consider paths matching the module + # name. Paths beyond the package hierarchy are not included. + module_name_parts = module_name.split(".") + for i, path in enumerate(argpath.parents, 2): + if i > len(module_name_parts) or path.stem != module_name_parts[-i]: + break + paths.insert(0, path) + + # Start going over the parts from the root, collecting each level + # and discarding all nodes which don't match the level's part. + any_matched_in_initial_part = False + notfound_collectors = [] + work: list[tuple[nodes.Collector | nodes.Item, list[Path | str]]] = [ + (self, [*paths, *names]) + ] + while work: + matchnode, matchparts = work.pop() + + # Pop'd all of the parts, this is a match. + if not matchparts: + yield matchnode + any_matched_in_initial_part = True + continue + + # Should have been matched by now, discard. + if not isinstance(matchnode, nodes.Collector): + continue + + # Collect this level of matching. + # Collecting Session (self) is done directly to avoid endless + # recursion to this function. + subnodes: Sequence[nodes.Collector | nodes.Item] + if isinstance(matchnode, Session): + assert isinstance(matchparts[0], Path) + subnodes = matchnode._collect_path(matchparts[0], path_cache) + else: + # For backward compat, files given directly multiple + # times on the command line should not be deduplicated. + handle_dupes = not ( + len(matchparts) == 1 + and isinstance(matchparts[0], Path) + and matchparts[0].is_file() + ) + rep, duplicate = self._collect_one_node(matchnode, handle_dupes) + if not duplicate and not rep.passed: + # Report collection failures here to avoid failing to + # run some test specified in the command line because + # the module could not be imported (#134). + matchnode.ihook.pytest_collectreport(report=rep) + if not rep.passed: + continue + subnodes = rep.result + + # Prune this level. + any_matched_in_collector = False + for node in reversed(subnodes): + # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. + if isinstance(matchparts[0], Path): + is_match = node.path == matchparts[0] + if sys.platform == "win32" and not is_match: + # In case the file paths do not match, fallback to samefile() to + # account for short-paths on Windows (#11895). But use a version + # which doesn't resolve symlinks, otherwise we might match the + # same file more than once (#12039). + is_match = samefile_nofollow(node.path, matchparts[0]) + + # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. + else: + if len(matchparts) == 1: + # This the last part, one parametrization goes. + if parametrization is not None: + # A parametrized arg must match exactly. + is_match = node.name == matchparts[0] + parametrization + else: + # A non-parameterized arg matches all parametrizations (if any). + # TODO: Remove the hacky split once the collection structure + # contains parametrization. + is_match = node.name.split("[")[0] == matchparts[0] + else: + is_match = node.name == matchparts[0] + if is_match: + work.append((node, matchparts[1:])) + any_matched_in_collector = True + + if not any_matched_in_collector: + notfound_collectors.append(matchnode) + + if not any_matched_in_initial_part: + report_arg = "::".join((str(argpath), *names)) + self._notfound.append((report_arg, notfound_collectors)) + + self.trace.root.indent -= 1 + + def genitems(self, node: nodes.Item | nodes.Collector) -> Iterator[nodes.Item]: + self.trace("genitems", node) + if isinstance(node, nodes.Item): + node.ihook.pytest_itemcollected(item=node) + yield node + else: + assert isinstance(node, nodes.Collector) + # For backward compat, dedup only applies to files. + handle_dupes = not isinstance(node, nodes.File) + rep, duplicate = self._collect_one_node(node, handle_dupes) + if rep.passed: + for subnode in rep.result: + yield from self.genitems(subnode) + if not duplicate: + node.ihook.pytest_collectreport(report=rep) + + +def search_pypath( + module_name: str, *, consider_namespace_packages: bool = False +) -> str | None: + """Search sys.path for the given a dotted module name, and return its file + system path if found.""" + try: + spec = importlib.util.find_spec(module_name) + # AttributeError: looks like package module, but actually filename + # ImportError: module does not exist + # ValueError: not a module name + except (AttributeError, ImportError, ValueError): + return None + + if spec is None: + return None + + if ( + spec.submodule_search_locations is None + or len(spec.submodule_search_locations) == 0 + ): + # Must be a simple module. + return spec.origin + + if consider_namespace_packages: + # If submodule_search_locations is set, it's a package (regular or namespace). + # Typically there is a single entry, but documentation claims it can be empty too + # (e.g. if the package has no physical location). + return spec.submodule_search_locations[0] + + if spec.origin is None: + # This is only the case for namespace packages + return None + + return os.path.dirname(spec.origin) + + +@dataclasses.dataclass(frozen=True) +class CollectionArgument: + """A resolved collection argument.""" + + path: Path + parts: Sequence[str] + parametrization: str | None + module_name: str | None + original_index: int + + +def resolve_collection_argument( + invocation_path: Path, + arg: str, + arg_index: int, + *, + as_pypath: bool = False, + consider_namespace_packages: bool = False, +) -> CollectionArgument: + """Parse path arguments optionally containing selection parts and return (fspath, names). + + Command-line arguments can point to files and/or directories, and optionally contain + parts for specific tests selection, for example: + + "pkg/tests/test_foo.py::TestClass::test_foo" + + This function ensures the path exists, and returns a resolved `CollectionArgument`: + + CollectionArgument( + path=Path("/full/path/to/pkg/tests/test_foo.py"), + parts=["TestClass", "test_foo"], + module_name=None, + ) + + When as_pypath is True, expects that the command-line argument actually contains + module paths instead of file-system paths: + + "pkg.tests.test_foo::TestClass::test_foo[a,b]" + + In which case we search sys.path for a matching module, and then return the *path* to the + found module, which may look like this: + + CollectionArgument( + path=Path("/home/u/myvenv/lib/site-packages/pkg/tests/test_foo.py"), + parts=["TestClass", "test_foo"], + parametrization="[a,b]", + module_name="pkg.tests.test_foo", + ) + + If the path doesn't exist, raise UsageError. + If the path is a directory and selection parts are present, raise UsageError. + """ + base, squacket, rest = arg.partition("[") + strpath, *parts = base.split("::") + if squacket and not parts: + raise UsageError(f"path cannot contain [] parametrization: {arg}") + parametrization = f"{squacket}{rest}" if squacket else None + module_name = None + if as_pypath: + pyarg_strpath = search_pypath( + strpath, consider_namespace_packages=consider_namespace_packages + ) + if pyarg_strpath is not None: + module_name = strpath + strpath = pyarg_strpath + fspath = invocation_path / strpath + fspath = absolutepath(fspath) + if not safe_exists(fspath): + msg = ( + "module or package not found: {arg} (missing __init__.py?)" + if as_pypath + else "file or directory not found: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + if parts and fspath.is_dir(): + msg = ( + "package argument cannot contain :: selection parts: {arg}" + if as_pypath + else "directory argument cannot contain :: selection parts: {arg}" + ) + raise UsageError(msg.format(arg=arg)) + return CollectionArgument( + path=fspath, + parts=parts, + parametrization=parametrization, + module_name=module_name, + original_index=arg_index, + ) + + +def is_collection_argument_subsumed_by( + arg: CollectionArgument, by: CollectionArgument +) -> bool: + """Check if `arg` is subsumed (contained) by `by`.""" + # First check path subsumption. + if by.path != arg.path: + # `by` subsumes `arg` if `by` is a parent directory of `arg` and has no + # parts (collects everything in that directory). + if not by.parts: + return arg.path.is_relative_to(by.path) + return False + # Paths are equal, check parts. + # For example: ("TestClass",) is a prefix of ("TestClass", "test_method"). + if len(by.parts) > len(arg.parts) or arg.parts[: len(by.parts)] != by.parts: + return False + # Paths and parts are equal, check parametrization. + # A `by` without parametrization (None) matches everything, e.g. + # `pytest x.py::test_it` matches `x.py::test_it[0]`. Otherwise must be + # exactly equal. + if by.parametrization is not None and by.parametrization != arg.parametrization: + return False + return True + + +def normalize_collection_arguments( + collection_args: Sequence[CollectionArgument], +) -> list[CollectionArgument]: + """Normalize collection arguments to eliminate overlapping paths and parts. + + Detects when collection arguments overlap in either paths or parts and only + keeps the shorter prefix, or the earliest argument if duplicate, preserving + order. The result is prefix-free. + """ + # A quadratic algorithm is not acceptable since large inputs are possible. + # So this uses an O(n*log(n)) algorithm which takes advantage of the + # property that after sorting, a collection argument will immediately + # precede collection arguments it subsumes. An O(n) algorithm is not worth + # it. + collection_args_sorted = sorted( + collection_args, + key=lambda arg: (arg.path, arg.parts, arg.parametrization or ""), + ) + normalized: list[CollectionArgument] = [] + last_kept = None + for arg in collection_args_sorted: + if last_kept is None or not is_collection_argument_subsumed_by(arg, last_kept): + normalized.append(arg) + last_kept = arg + normalized.sort(key=lambda arg: arg.original_index) + return normalized diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py new file mode 100644 index 0000000..841d781 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py @@ -0,0 +1,301 @@ +"""Generic mechanism for marking and selecting python functions.""" + +from __future__ import annotations + +import collections +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Set as AbstractSet +import dataclasses +from typing import TYPE_CHECKING + +from .expression import Expression +from .structures import _HiddenParam +from .structures import EMPTY_PARAMETERSET_OPTION +from .structures import get_empty_parameterset_mark +from .structures import HIDDEN_PARAM +from .structures import Mark +from .structures import MARK_GEN +from .structures import MarkDecorator +from .structures import MarkGenerator +from .structures import ParameterSet +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import UsageError +from _pytest.config.argparsing import NOT_SET +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey + + +if TYPE_CHECKING: + from _pytest.nodes import Item + + +__all__ = [ + "HIDDEN_PARAM", + "MARK_GEN", + "Mark", + "MarkDecorator", + "MarkGenerator", + "ParameterSet", + "get_empty_parameterset_mark", +] + + +old_mark_config_key = StashKey[Config | None]() + + +def param( + *values: object, + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | _HiddenParam | None = None, +) -> ParameterSet: + """Specify a parameter in `pytest.mark.parametrize`_ calls or + :ref:`parametrized fixtures `. + + .. code-block:: python + + @pytest.mark.parametrize( + "test_input,expected", + [ + ("3+5", 8), + pytest.param("6*9", 42, marks=pytest.mark.xfail), + ], + ) + def test_eval(test_input, expected): + assert eval(test_input) == expected + + :param values: Variable args of the values of the parameter set, in order. + + :param marks: + A single mark or a list of marks to be applied to this parameter set. + + :ref:`pytest.mark.usefixtures ` cannot be added via this parameter. + + :type id: str | Literal[pytest.HIDDEN_PARAM] | None + :param id: + The id to attribute to this parameter set. + + .. versionadded:: 8.4 + :ref:`hidden-param` means to hide the parameter set + from the test name. Can only be used at most 1 time, as + test names need to be unique. + """ + return ParameterSet.param(*values, marks=marks, id=id) + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group._addoption( # private to use reserved lower-case short option + "-k", + action="store", + dest="keyword", + default="", + metavar="EXPRESSION", + help="Only run tests which match the given substring expression. " + "An expression is a Python evaluable expression " + "where all names are substring-matched against test names " + "and their parent classes. Example: -k 'test_method or test_" + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other', while -k 'not test_method' " + "matches those that don't contain 'test_method' in their names. " + "-k 'not test_method and not test_other' will eliminate the matches. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' set, " + "as well as functions which have names assigned directly to them. " + "The matching is case-insensitive.", + ) + + group._addoption( # private to use reserved lower-case short option + "-m", + action="store", + dest="markexpr", + default="", + metavar="MARKEXPR", + help="Only run tests matching given mark expression. " + "For example: -m 'mark1 and not mark2'.", + ) + + group.addoption( + "--markers", + action="store_true", + help="show markers (builtin, plugin and per-project ones).", + ) + + parser.addini("markers", "Register new markers for test functions", "linelist") + parser.addini(EMPTY_PARAMETERSET_OPTION, "Default marker for empty parametersets") + + +@hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + import _pytest.config + + if config.option.markers: + config._do_configure() + tw = _pytest.config.create_terminal_writer(config) + for line in config.getini("markers"): + parts = line.split(":", 1) + name = parts[0] + rest = parts[1] if len(parts) == 2 else "" + tw.write(f"@pytest.mark.{name}:", bold=True) + tw.line(rest) + tw.line() + config._ensure_unconfigure() + return 0 + + return None + + +@dataclasses.dataclass +class KeywordMatcher: + """A matcher for keywords. + + Given a list of names, matches any substring of one of these names. The + string inclusion check is case-insensitive. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + + Additionally, matches on names in the 'extra_keyword_matches' set of + any item, as well as names directly assigned to test functions. + """ + + __slots__ = ("_names",) + + _names: AbstractSet[str] + + @classmethod + def from_item(cls, item: Item) -> KeywordMatcher: + mapped_names = set() + + # Add the names of the current item and any parent items, + # except the Session and root Directory's which are not + # interesting for matching. + import pytest + + for node in item.listchain(): + if isinstance(node, pytest.Session): + continue + if isinstance(node, pytest.Directory) and isinstance( + node.parent, pytest.Session + ): + continue + mapped_names.add(node.name) + + # Add the names added as extra keywords to current or parent items. + mapped_names.update(item.listextrakeywords()) + + # Add the names attached to the current function through direct assignment. + function_obj = getattr(item, "function", None) + if function_obj: + mapped_names.update(function_obj.__dict__) + + # Add the markers to the keywords as we no longer handle them correctly. + mapped_names.update(mark.name for mark in item.iter_markers()) + + return cls(mapped_names) + + def __call__(self, subname: str, /, **kwargs: str | int | bool | None) -> bool: + if kwargs: + raise UsageError("Keyword expressions do not support call parameters.") + subname = subname.lower() + return any(subname in name.lower() for name in self._names) + + +def deselect_by_keyword(items: list[Item], config: Config) -> None: + keywordexpr = config.option.keyword.lstrip() + if not keywordexpr: + return + + expr = _parse_expression(keywordexpr, "Wrong expression passed to '-k'") + + remaining = [] + deselected = [] + for colitem in items: + if not expr.evaluate(KeywordMatcher.from_item(colitem)): + deselected.append(colitem) + else: + remaining.append(colitem) + + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +@dataclasses.dataclass +class MarkMatcher: + """A matcher for markers which are present. + + Tries to match on any marker names, attached to the given colitem. + """ + + __slots__ = ("own_mark_name_mapping",) + + own_mark_name_mapping: dict[str, list[Mark]] + + @classmethod + def from_markers(cls, markers: Iterable[Mark]) -> MarkMatcher: + mark_name_mapping = collections.defaultdict(list) + for mark in markers: + mark_name_mapping[mark.name].append(mark) + return cls(mark_name_mapping) + + def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: + if not (matches := self.own_mark_name_mapping.get(name, [])): + return False + + for mark in matches: # pylint: disable=consider-using-any-or-all + if all(mark.kwargs.get(k, NOT_SET) == v for k, v in kwargs.items()): + return True + return False + + +def deselect_by_mark(items: list[Item], config: Config) -> None: + matchexpr = config.option.markexpr + if not matchexpr: + return + + expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'") + remaining: list[Item] = [] + deselected: list[Item] = [] + for item in items: + if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())): + remaining.append(item) + else: + deselected.append(item) + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = remaining + + +def _parse_expression(expr: str, exc_message: str) -> Expression: + try: + return Expression.compile(expr) + except SyntaxError as e: + raise UsageError( + f"{exc_message}: {e.text}: at column {e.offset}: {e.msg}" + ) from None + + +def pytest_collection_modifyitems(items: list[Item], config: Config) -> None: + deselect_by_keyword(items, config) + deselect_by_mark(items, config) + + +def pytest_configure(config: Config) -> None: + config.stash[old_mark_config_key] = MARK_GEN._config + MARK_GEN._config = config + + empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) + + if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): + raise UsageError( + f"{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect" + f" but it is {empty_parameterset!r}" + ) + + +def pytest_unconfigure(config: Config) -> None: + MARK_GEN._config = config.stash.get(old_mark_config_key, None) diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py b/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py new file mode 100644 index 0000000..3bdbd03 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py @@ -0,0 +1,353 @@ +r"""Evaluate match expressions, as used by `-k` and `-m`. + +The grammar is: + +expression: expr? EOF +expr: and_expr ('or' and_expr)* +and_expr: not_expr ('and' not_expr)* +not_expr: 'not' not_expr | '(' expr ')' | ident kwargs? + +ident: (\w|:|\+|-|\.|\[|\]|\\|/)+ +kwargs: ('(' name '=' value ( ', ' name '=' value )* ')') +name: a valid ident, but not a reserved keyword +value: (unescaped) string literal | (-)?[0-9]+ | 'False' | 'True' | 'None' + +The semantics are: + +- Empty expression evaluates to False. +- ident evaluates to True or False according to a provided matcher function. +- ident with parentheses and keyword arguments evaluates to True or False according to a provided matcher function. +- or/and/not evaluate according to the usual boolean semantics. +""" + +from __future__ import annotations + +import ast +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import enum +import keyword +import re +import types +from typing import Final +from typing import final +from typing import Literal +from typing import NoReturn +from typing import overload +from typing import Protocol + + +__all__ = [ + "Expression", + "ExpressionMatcher", +] + + +FILE_NAME: Final = "" + + +class TokenType(enum.Enum): + LPAREN = "left parenthesis" + RPAREN = "right parenthesis" + OR = "or" + AND = "and" + NOT = "not" + IDENT = "identifier" + EOF = "end of input" + EQUAL = "=" + STRING = "string literal" + COMMA = "," + + +@dataclasses.dataclass(frozen=True) +class Token: + __slots__ = ("pos", "type", "value") + type: TokenType + value: str + pos: int + + +class Scanner: + __slots__ = ("current", "input", "tokens") + + def __init__(self, input: str) -> None: + self.input = input + self.tokens = self.lex(input) + self.current = next(self.tokens) + + def lex(self, input: str) -> Iterator[Token]: + pos = 0 + while pos < len(input): + if input[pos] in (" ", "\t"): + pos += 1 + elif input[pos] == "(": + yield Token(TokenType.LPAREN, "(", pos) + pos += 1 + elif input[pos] == ")": + yield Token(TokenType.RPAREN, ")", pos) + pos += 1 + elif input[pos] == "=": + yield Token(TokenType.EQUAL, "=", pos) + pos += 1 + elif input[pos] == ",": + yield Token(TokenType.COMMA, ",", pos) + pos += 1 + elif (quote_char := input[pos]) in ("'", '"'): + end_quote_pos = input.find(quote_char, pos + 1) + if end_quote_pos == -1: + raise SyntaxError( + f'closing quote "{quote_char}" is missing', + (FILE_NAME, 1, pos + 1, input), + ) + value = input[pos : end_quote_pos + 1] + if (backslash_pos := input.find("\\")) != -1: + raise SyntaxError( + r'escaping with "\" not supported in marker expression', + (FILE_NAME, 1, backslash_pos + 1, input), + ) + yield Token(TokenType.STRING, value, pos) + pos += len(value) + else: + match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\|/)+", input[pos:]) + if match: + value = match.group(0) + if value == "or": + yield Token(TokenType.OR, value, pos) + elif value == "and": + yield Token(TokenType.AND, value, pos) + elif value == "not": + yield Token(TokenType.NOT, value, pos) + else: + yield Token(TokenType.IDENT, value, pos) + pos += len(value) + else: + raise SyntaxError( + f'unexpected character "{input[pos]}"', + (FILE_NAME, 1, pos + 1, input), + ) + yield Token(TokenType.EOF, "", pos) + + @overload + def accept(self, type: TokenType, *, reject: Literal[True]) -> Token: ... + + @overload + def accept( + self, type: TokenType, *, reject: Literal[False] = False + ) -> Token | None: ... + + def accept(self, type: TokenType, *, reject: bool = False) -> Token | None: + if self.current.type is type: + token = self.current + if token.type is not TokenType.EOF: + self.current = next(self.tokens) + return token + if reject: + self.reject((type,)) + return None + + def reject(self, expected: Sequence[TokenType]) -> NoReturn: + raise SyntaxError( + "expected {}; got {}".format( + " OR ".join(type.value for type in expected), + self.current.type.value, + ), + (FILE_NAME, 1, self.current.pos + 1, self.input), + ) + + +# True, False and None are legal match expression identifiers, +# but illegal as Python identifiers. To fix this, this prefix +# is added to identifiers in the conversion to Python AST. +IDENT_PREFIX = "$" + + +def expression(s: Scanner) -> ast.Expression: + if s.accept(TokenType.EOF): + ret: ast.expr = ast.Constant(False) + else: + ret = expr(s) + s.accept(TokenType.EOF, reject=True) + return ast.fix_missing_locations(ast.Expression(ret)) + + +def expr(s: Scanner) -> ast.expr: + ret = and_expr(s) + while s.accept(TokenType.OR): + rhs = and_expr(s) + ret = ast.BoolOp(ast.Or(), [ret, rhs]) + return ret + + +def and_expr(s: Scanner) -> ast.expr: + ret = not_expr(s) + while s.accept(TokenType.AND): + rhs = not_expr(s) + ret = ast.BoolOp(ast.And(), [ret, rhs]) + return ret + + +def not_expr(s: Scanner) -> ast.expr: + if s.accept(TokenType.NOT): + return ast.UnaryOp(ast.Not(), not_expr(s)) + if s.accept(TokenType.LPAREN): + ret = expr(s) + s.accept(TokenType.RPAREN, reject=True) + return ret + ident = s.accept(TokenType.IDENT) + if ident: + name = ast.Name(IDENT_PREFIX + ident.value, ast.Load()) + if s.accept(TokenType.LPAREN): + ret = ast.Call(func=name, args=[], keywords=all_kwargs(s)) + s.accept(TokenType.RPAREN, reject=True) + else: + ret = name + return ret + + s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT)) + + +BUILTIN_MATCHERS = {"True": True, "False": False, "None": None} + + +def single_kwarg(s: Scanner) -> ast.keyword: + keyword_name = s.accept(TokenType.IDENT, reject=True) + if not keyword_name.value.isidentifier(): + raise SyntaxError( + f"not a valid python identifier {keyword_name.value}", + (FILE_NAME, 1, keyword_name.pos + 1, s.input), + ) + if keyword.iskeyword(keyword_name.value): + raise SyntaxError( + f"unexpected reserved python keyword `{keyword_name.value}`", + (FILE_NAME, 1, keyword_name.pos + 1, s.input), + ) + s.accept(TokenType.EQUAL, reject=True) + + if value_token := s.accept(TokenType.STRING): + value: str | int | bool | None = value_token.value[1:-1] # strip quotes + else: + value_token = s.accept(TokenType.IDENT, reject=True) + if (number := value_token.value).isdigit() or ( + number.startswith("-") and number[1:].isdigit() + ): + value = int(number) + elif value_token.value in BUILTIN_MATCHERS: + value = BUILTIN_MATCHERS[value_token.value] + else: + raise SyntaxError( + f'unexpected character/s "{value_token.value}"', + (FILE_NAME, 1, value_token.pos + 1, s.input), + ) + + ret = ast.keyword(keyword_name.value, ast.Constant(value)) + return ret + + +def all_kwargs(s: Scanner) -> list[ast.keyword]: + ret = [single_kwarg(s)] + while s.accept(TokenType.COMMA): + ret.append(single_kwarg(s)) + return ret + + +class ExpressionMatcher(Protocol): + """A callable which, given an identifier and optional kwargs, should return + whether it matches in an :class:`Expression` evaluation. + + Should be prepared to handle arbitrary strings as input. + + If no kwargs are provided, the expression of the form `foo`. + If kwargs are provided, the expression is of the form `foo(1, b=True, "s")`. + + If the expression is not supported (e.g. don't want to accept the kwargs + syntax variant), should raise :class:`~pytest.UsageError`. + + Example:: + + def matcher(name: str, /, **kwargs: str | int | bool | None) -> bool: + # Match `cat`. + if name == "cat" and not kwargs: + return True + # Match `dog(barks=True)`. + if name == "dog" and kwargs == {"barks": False}: + return True + return False + """ + + def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: ... + + +@dataclasses.dataclass +class MatcherNameAdapter: + matcher: ExpressionMatcher + name: str + + def __bool__(self) -> bool: + return self.matcher(self.name) + + def __call__(self, **kwargs: str | int | bool | None) -> bool: + return self.matcher(self.name, **kwargs) + + +class MatcherAdapter(Mapping[str, MatcherNameAdapter]): + """Adapts a matcher function to a locals mapping as required by eval().""" + + def __init__(self, matcher: ExpressionMatcher) -> None: + self.matcher = matcher + + def __getitem__(self, key: str) -> MatcherNameAdapter: + return MatcherNameAdapter(matcher=self.matcher, name=key[len(IDENT_PREFIX) :]) + + def __iter__(self) -> Iterator[str]: + raise NotImplementedError() + + def __len__(self) -> int: + raise NotImplementedError() + + +@final +class Expression: + """A compiled match expression as used by -k and -m. + + The expression can be evaluated against different matchers. + """ + + __slots__ = ("_code", "input") + + def __init__(self, input: str, code: types.CodeType) -> None: + #: The original input line, as a string. + self.input: Final = input + self._code: Final = code + + @classmethod + def compile(cls, input: str) -> Expression: + """Compile a match expression. + + :param input: The input expression - one line. + + :raises SyntaxError: If the expression is malformed. + """ + astexpr = expression(Scanner(input)) + code = compile( + astexpr, + filename="", + mode="eval", + ) + return Expression(input, code) + + def evaluate(self, matcher: ExpressionMatcher) -> bool: + """Evaluate the match expression. + + :param matcher: + A callback which determines whether an identifier matches or not. + See the :class:`ExpressionMatcher` protocol for details and example. + + :returns: Whether the expression matches or not. + + :raises UsageError: + If the matcher doesn't support the expression. Cannot happen if the + matcher supports all expressions. + """ + return bool(eval(self._code, {"__builtins__": {}}, MatcherAdapter(matcher))) diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py b/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py new file mode 100644 index 0000000..97842fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py @@ -0,0 +1,664 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Collection +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import MutableMapping +from collections.abc import Sequence +import dataclasses +import enum +import inspect +from typing import Any +from typing import final +from typing import NamedTuple +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +import warnings + +from .._code import getfslineno +from ..compat import NOTSET +from ..compat import NotSetType +from _pytest.config import Config +from _pytest.deprecated import check_ispytest +from _pytest.deprecated import MARKED_FIXTURE +from _pytest.outcomes import fail +from _pytest.raises import AbstractRaises +from _pytest.scope import _ScopeName +from _pytest.warning_types import PytestUnknownMarkWarning + + +if TYPE_CHECKING: + from ..nodes import Node + + +EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" + + +# Singleton type for HIDDEN_PARAM, as described in: +# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions +class _HiddenParam(enum.Enum): + token = 0 + + +#: Can be used as a parameter set id to hide it from the test name. +HIDDEN_PARAM = _HiddenParam.token + + +def istestfunc(func) -> bool: + return callable(func) and getattr(func, "__name__", "") != "" + + +def get_empty_parameterset_mark( + config: Config, argnames: Sequence[str], func +) -> MarkDecorator: + from ..nodes import Collector + + argslisting = ", ".join(argnames) + + _fs, lineno = getfslineno(func) + reason = f"got empty parameter set for ({argslisting})" + requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) + if requested_mark in ("", None, "skip"): + mark = MARK_GEN.skip(reason=reason) + elif requested_mark == "xfail": + mark = MARK_GEN.xfail(reason=reason, run=False) + elif requested_mark == "fail_at_collect": + raise Collector.CollectError( + f"Empty parameter set in '{func.__name__}' at line {lineno + 1}" + ) + else: + raise LookupError(requested_mark) + return mark + + +class ParameterSet(NamedTuple): + """A set of values for a set of parameters along with associated marks and + an optional ID for the set. + + Examples:: + + pytest.param(1, 2, 3) + # ParameterSet(values=(1, 2, 3), marks=(), id=None) + + pytest.param("hello", id="greeting") + # ParameterSet(values=("hello",), marks=(), id="greeting") + + # Parameter set with marks + pytest.param(42, marks=pytest.mark.xfail) + # ParameterSet(values=(42,), marks=(MarkDecorator(...),), id=None) + + # From parametrize mark (parameter names + list of parameter sets) + pytest.mark.parametrize( + ("a", "b", "expected"), + [ + (1, 2, 3), + pytest.param(40, 2, 42, id="everything"), + ], + ) + # ParameterSet(values=(1, 2, 3), marks=(), id=None) + # ParameterSet(values=(40, 2, 42), marks=(), id="everything") + """ + + values: Sequence[object | NotSetType] + marks: Collection[MarkDecorator | Mark] + id: str | _HiddenParam | None + + @classmethod + def param( + cls, + *values: object, + marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), + id: str | _HiddenParam | None = None, + ) -> ParameterSet: + if isinstance(marks, MarkDecorator): + marks = (marks,) + else: + assert isinstance(marks, collections.abc.Collection) + if any(i.name == "usefixtures" for i in marks): + raise ValueError( + "pytest.param cannot add pytest.mark.usefixtures; see " + "https://docs.pytest.org/en/stable/reference/reference.html#pytest-param" + ) + + if id is not None: + if not isinstance(id, str) and id is not HIDDEN_PARAM: + raise TypeError( + "Expected id to be a string or a `pytest.HIDDEN_PARAM` sentinel, " + f"got {type(id)}: {id!r}", + ) + return cls(values, marks, id) + + @classmethod + def extract_from( + cls, + parameterset: ParameterSet | Sequence[object] | object, + force_tuple: bool = False, + ) -> ParameterSet: + """Extract from an object or objects. + + :param parameterset: + A legacy style parameterset that may or may not be a tuple, + and may or may not be wrapped into a mess of mark objects. + + :param force_tuple: + Enforce tuple wrapping so single argument tuple values + don't get decomposed and break tests. + """ + if isinstance(parameterset, cls): + return parameterset + if force_tuple: + return cls.param(parameterset) + else: + # TODO: Refactor to fix this type-ignore. Currently the following + # passes type-checking but crashes: + # + # @pytest.mark.parametrize(('x', 'y'), [1, 2]) + # def test_foo(x, y): pass + return cls(parameterset, marks=[], id=None) # type: ignore[arg-type] + + @staticmethod + def _parse_parametrize_args( + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + *args, + **kwargs, + ) -> tuple[Sequence[str], bool]: + if isinstance(argnames, str): + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + force_tuple = len(argnames) == 1 + else: + force_tuple = False + return argnames, force_tuple + + @staticmethod + def _parse_parametrize_parameters( + argvalues: Iterable[ParameterSet | Sequence[object] | object], + force_tuple: bool, + ) -> list[ParameterSet]: + return [ + ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues + ] + + @classmethod + def _for_parametrize( + cls, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + func, + config: Config, + nodeid: str, + ) -> tuple[Sequence[str], list[ParameterSet]]: + argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) + parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) + del argvalues + + if parameters: + # Check all parameter sets have the correct number of values. + for param in parameters: + if len(param.values) != len(argnames): + msg = ( + '{nodeid}: in "parametrize" the number of names ({names_len}):\n' + " {names}\n" + "must be equal to the number of values ({values_len}):\n" + " {values}" + ) + fail( + msg.format( + nodeid=nodeid, + values=param.values, + names=argnames, + names_len=len(argnames), + values_len=len(param.values), + ), + pytrace=False, + ) + else: + # Empty parameter set (likely computed at runtime): create a single + # parameter set with NOTSET values, with the "empty parameter set" mark applied to it. + mark = get_empty_parameterset_mark(config, argnames, func) + parameters.append( + ParameterSet( + values=(NOTSET,) * len(argnames), marks=[mark], id="NOTSET" + ) + ) + return argnames, parameters + + +@final +@dataclasses.dataclass(frozen=True) +class Mark: + """A pytest mark.""" + + #: Name of the mark. + name: str + #: Positional arguments of the mark decorator. + args: tuple[Any, ...] + #: Keyword arguments of the mark decorator. + kwargs: Mapping[str, Any] + + #: Source Mark for ids with parametrize Marks. + _param_ids_from: Mark | None = dataclasses.field(default=None, repr=False) + #: Resolved/generated ids with parametrize Marks. + _param_ids_generated: Sequence[str] | None = dataclasses.field( + default=None, repr=False + ) + + def __init__( + self, + name: str, + args: tuple[Any, ...], + kwargs: Mapping[str, Any], + param_ids_from: Mark | None = None, + param_ids_generated: Sequence[str] | None = None, + *, + _ispytest: bool = False, + ) -> None: + """:meta private:""" + check_ispytest(_ispytest) + # Weirdness to bypass frozen=True. + object.__setattr__(self, "name", name) + object.__setattr__(self, "args", args) + object.__setattr__(self, "kwargs", kwargs) + object.__setattr__(self, "_param_ids_from", param_ids_from) + object.__setattr__(self, "_param_ids_generated", param_ids_generated) + + def _has_param_ids(self) -> bool: + return "ids" in self.kwargs or len(self.args) >= 4 + + def combined_with(self, other: Mark) -> Mark: + """Return a new Mark which is a combination of this + Mark and another Mark. + + Combines by appending args and merging kwargs. + + :param Mark other: The mark to combine with. + :rtype: Mark + """ + assert self.name == other.name + + # Remember source of ids with parametrize Marks. + param_ids_from: Mark | None = None + if self.name == "parametrize": + if other._has_param_ids(): + param_ids_from = other + elif self._has_param_ids(): + param_ids_from = self + + return Mark( + self.name, + self.args + other.args, + dict(self.kwargs, **other.kwargs), + param_ids_from=param_ids_from, + _ispytest=True, + ) + + +# A generic parameter designating an object to which a Mark may +# be applied -- a test function (callable) or class. +# Note: a lambda is not allowed, but this can't be represented. +Markable = TypeVar("Markable", bound=Callable[..., object] | type) + + +@dataclasses.dataclass +class MarkDecorator: + """A decorator for applying a mark on test functions and classes. + + ``MarkDecorators`` are created with ``pytest.mark``:: + + mark1 = pytest.mark.NAME # Simple MarkDecorator + mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator + + and can then be applied as decorators to test functions:: + + @mark2 + def test_function(): + pass + + When a ``MarkDecorator`` is called, it does the following: + + 1. If called with a single class as its only positional argument and no + additional keyword arguments, it attaches the mark to the class so it + gets applied automatically to all test cases found in that class. + + 2. If called with a single function as its only positional argument and + no additional keyword arguments, it attaches the mark to the function, + containing all the arguments already stored internally in the + ``MarkDecorator``. + + 3. When called in any other case, it returns a new ``MarkDecorator`` + instance with the original ``MarkDecorator``'s content updated with + the arguments passed to this call. + + Note: The rules above prevent a ``MarkDecorator`` from storing only a + single function or class reference as its positional argument with no + additional keyword or positional arguments. You can work around this by + using `with_args()`. + """ + + mark: Mark + + def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: + """:meta private:""" + check_ispytest(_ispytest) + self.mark = mark + + @property + def name(self) -> str: + """Alias for mark.name.""" + return self.mark.name + + @property + def args(self) -> tuple[Any, ...]: + """Alias for mark.args.""" + return self.mark.args + + @property + def kwargs(self) -> Mapping[str, Any]: + """Alias for mark.kwargs.""" + return self.mark.kwargs + + @property + def markname(self) -> str: + """:meta private:""" + return self.name # for backward-compat (2.4.1 had this attr) + + def with_args(self, *args: object, **kwargs: object) -> MarkDecorator: + """Return a MarkDecorator with extra arguments added. + + Unlike calling the MarkDecorator, with_args() can be used even + if the sole argument is a callable/class. + """ + mark = Mark(self.name, args, kwargs, _ispytest=True) + return MarkDecorator(self.mark.combined_with(mark), _ispytest=True) + + # Type ignored because the overloads overlap with an incompatible + # return type. Not much we can do about that. Thankfully mypy picks + # the first match so it works out even if we break the rules. + @overload + def __call__(self, arg: Markable) -> Markable: # type: ignore[overload-overlap] + pass + + @overload + def __call__(self, *args: object, **kwargs: object) -> MarkDecorator: + pass + + def __call__(self, *args: object, **kwargs: object): + """Call the MarkDecorator.""" + if args and not kwargs: + func = args[0] + is_class = inspect.isclass(func) + # For staticmethods/classmethods, the marks are eventually fetched from the + # function object, not the descriptor, so unwrap. + unwrapped_func = func + if isinstance(func, staticmethod | classmethod): + unwrapped_func = func.__func__ + if len(args) == 1 and (istestfunc(unwrapped_func) or is_class): + store_mark(unwrapped_func, self.mark, stacklevel=3) + return func + return self.with_args(*args, **kwargs) + + +def get_unpacked_marks( + obj: object | type, + *, + consider_mro: bool = True, +) -> list[Mark]: + """Obtain the unpacked marks that are stored on an object. + + If obj is a class and consider_mro is true, return marks applied to + this class and all of its super-classes in MRO order. If consider_mro + is false, only return marks applied directly to this class. + """ + if isinstance(obj, type): + if not consider_mro: + mark_lists = [obj.__dict__.get("pytestmark", [])] + else: + mark_lists = [ + x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__) + ] + mark_list = [] + for item in mark_lists: + if isinstance(item, list): + mark_list.extend(item) + else: + mark_list.append(item) + else: + mark_attribute = getattr(obj, "pytestmark", []) + if isinstance(mark_attribute, list): + mark_list = mark_attribute + else: + mark_list = [mark_attribute] + return list(normalize_mark_list(mark_list)) + + +def normalize_mark_list( + mark_list: Iterable[Mark | MarkDecorator], +) -> Iterable[Mark]: + """ + Normalize an iterable of Mark or MarkDecorator objects into a list of marks + by retrieving the `mark` attribute on MarkDecorator instances. + + :param mark_list: marks to normalize + :returns: A new list of the extracted Mark objects + """ + for mark in mark_list: + mark_obj = getattr(mark, "mark", mark) + if not isinstance(mark_obj, Mark): + raise TypeError(f"got {mark_obj!r} instead of Mark") + yield mark_obj + + +def store_mark(obj, mark: Mark, *, stacklevel: int = 2) -> None: + """Store a Mark on an object. + + This is used to implement the Mark declarations/decorators correctly. + """ + assert isinstance(mark, Mark), mark + + from ..fixtures import getfixturemarker + + if getfixturemarker(obj) is not None: + warnings.warn(MARKED_FIXTURE, stacklevel=stacklevel) + + # Always reassign name to avoid updating pytestmark in a reference that + # was only borrowed. + obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark] + + +# Typing for builtin pytest marks. This is cheating; it gives builtin marks +# special privilege, and breaks modularity. But practicality beats purity... +if TYPE_CHECKING: + + class _SkipMarkDecorator(MarkDecorator): + @overload # type: ignore[override,no-overload-impl] + def __call__(self, arg: Markable) -> Markable: ... + + @overload + def __call__(self, reason: str = ...) -> MarkDecorator: ... + + class _SkipifMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + condition: str | bool = ..., + *conditions: str | bool, + reason: str = ..., + ) -> MarkDecorator: ... + + class _XfailMarkDecorator(MarkDecorator): + @overload # type: ignore[override,no-overload-impl] + def __call__(self, arg: Markable) -> Markable: ... + + @overload + def __call__( + self, + condition: str | bool = True, + *conditions: str | bool, + reason: str = ..., + run: bool = ..., + raises: None + | type[BaseException] + | tuple[type[BaseException], ...] + | AbstractRaises[BaseException] = ..., + strict: bool = ..., + ) -> MarkDecorator: ... + + class _ParametrizeMarkDecorator(MarkDecorator): + def __call__( # type: ignore[override] + self, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + *, + indirect: bool | Sequence[str] = ..., + ids: Iterable[None | str | float | int | bool] + | Callable[[Any], object | None] + | None = ..., + scope: _ScopeName | None = ..., + ) -> MarkDecorator: ... + + class _UsefixturesMarkDecorator(MarkDecorator): + def __call__(self, *fixtures: str) -> MarkDecorator: # type: ignore[override] + ... + + class _FilterwarningsMarkDecorator(MarkDecorator): + def __call__(self, *filters: str) -> MarkDecorator: # type: ignore[override] + ... + + +@final +class MarkGenerator: + """Factory for :class:`MarkDecorator` objects - exposed as + a ``pytest.mark`` singleton instance. + + Example:: + + import pytest + + + @pytest.mark.slowtest + def test_function(): + pass + + applies a 'slowtest' :class:`Mark` on ``test_function``. + """ + + # See TYPE_CHECKING above. + if TYPE_CHECKING: + skip: _SkipMarkDecorator + skipif: _SkipifMarkDecorator + xfail: _XfailMarkDecorator + parametrize: _ParametrizeMarkDecorator + usefixtures: _UsefixturesMarkDecorator + filterwarnings: _FilterwarningsMarkDecorator + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._config: Config | None = None + self._markers: set[str] = set() + + def __getattr__(self, name: str) -> MarkDecorator: + """Generate a new :class:`MarkDecorator` with the given name.""" + if name[0] == "_": + raise AttributeError("Marker name must NOT start with underscore") + + if self._config is not None: + # We store a set of markers as a performance optimisation - if a mark + # name is in the set we definitely know it, but a mark may be known and + # not in the set. We therefore start by updating the set! + if name not in self._markers: + for line in self._config.getini("markers"): + # example lines: "skipif(condition): skip the given test if..." + # or "hypothesis: tests which use Hypothesis", so to get the + # marker name we split on both `:` and `(`. + marker = line.split(":")[0].split("(")[0].strip() + self._markers.add(marker) + + # If the name is not in the set of known marks after updating, + # then it really is time to issue a warning or an error. + if name not in self._markers: + # Raise a specific error for common misspellings of "parametrize". + if name in ["parameterize", "parametrise", "parameterise"]: + __tracebackhide__ = True + fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") + + strict_markers = self._config.getini("strict_markers") + if strict_markers is None: + strict_markers = self._config.getini("strict") + if strict_markers: + fail( + f"{name!r} not found in `markers` configuration option", + pytrace=False, + ) + + warnings.warn( + f"Unknown pytest.mark.{name} - is this a typo? You can register " + "custom marks to avoid this warning - for details, see " + "https://docs.pytest.org/en/stable/how-to/mark.html", + PytestUnknownMarkWarning, + 2, + ) + + return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True) + + +MARK_GEN = MarkGenerator(_ispytest=True) + + +@final +class NodeKeywords(MutableMapping[str, Any]): + __slots__ = ("_markers", "node", "parent") + + def __init__(self, node: Node) -> None: + self.node = node + self.parent = node.parent + self._markers = {node.name: True} + + def __getitem__(self, key: str) -> Any: + try: + return self._markers[key] + except KeyError: + if self.parent is None: + raise + return self.parent.keywords[key] + + def __setitem__(self, key: str, value: Any) -> None: + self._markers[key] = value + + # Note: we could've avoided explicitly implementing some of the methods + # below and use the collections.abc fallback, but that would be slow. + + def __contains__(self, key: object) -> bool: + return key in self._markers or ( + self.parent is not None and key in self.parent.keywords + ) + + def update( # type: ignore[override] + self, + other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), + **kwds: Any, + ) -> None: + self._markers.update(other) + self._markers.update(kwds) + + def __delitem__(self, key: str) -> None: + raise ValueError("cannot delete key in keywords dict") + + def __iter__(self) -> Iterator[str]: + # Doesn't need to be fast. + yield from self._markers + if self.parent is not None: + for keyword in self.parent.keywords: + # self._marks and self.parent.keywords can have duplicates. + if keyword not in self._markers: + yield keyword + + def __len__(self) -> int: + # Doesn't need to be fast. + return sum(1 for keyword in self) + + def __repr__(self) -> str: + return f"" diff --git a/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py b/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py new file mode 100644 index 0000000..07cc3fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py @@ -0,0 +1,435 @@ +# mypy: allow-untyped-defs +"""Monkeypatching and mocking functionality.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import MutableMapping +from contextlib import contextmanager +import os +from pathlib import Path +import re +import sys +from typing import Any +from typing import final +from typing import overload +from typing import TypeVar +import warnings + +from _pytest.deprecated import MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES +from _pytest.fixtures import fixture +from _pytest.warning_types import PytestWarning + + +RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") + + +K = TypeVar("K") +V = TypeVar("V") + + +@fixture +def monkeypatch() -> Generator[MonkeyPatch]: + """A convenient fixture for monkey-patching. + + The fixture provides these methods to modify objects, dictionaries, or + :data:`os.environ`: + + * :meth:`monkeypatch.setattr(obj, name, value, raising=True) ` + * :meth:`monkeypatch.delattr(obj, name, raising=True) ` + * :meth:`monkeypatch.setitem(mapping, name, value) ` + * :meth:`monkeypatch.delitem(obj, name, raising=True) ` + * :meth:`monkeypatch.setenv(name, value, prepend=None) ` + * :meth:`monkeypatch.delenv(name, raising=True) ` + * :meth:`monkeypatch.syspath_prepend(path) ` + * :meth:`monkeypatch.chdir(path) ` + * :meth:`monkeypatch.context() ` + + All modifications will be undone after the requesting test function or + fixture has finished. The ``raising`` parameter determines if a :class:`KeyError` + or :class:`AttributeError` will be raised if the set/deletion operation does not have the + specified target. + + To undo modifications done by the fixture in a contained scope, + use :meth:`context() `. + """ + mpatch = MonkeyPatch() + yield mpatch + mpatch.undo() + + +def resolve(name: str) -> object: + # Simplified from zope.dottedname. + parts = name.split(".") + + used = parts.pop(0) + found: object = __import__(used) + for part in parts: + used += "." + part + try: + found = getattr(found, part) + except AttributeError: + pass + else: + continue + # We use explicit un-nesting of the handling block in order + # to avoid nested exceptions. + try: + __import__(used) + except ImportError as ex: + expected = str(ex).split()[-1] + if expected == used: + raise + else: + raise ImportError(f"import error in {used}: {ex}") from ex + found = annotated_getattr(found, part, used) + return found + + +def annotated_getattr(obj: object, name: str, ann: str) -> object: + try: + obj = getattr(obj, name) + except AttributeError as e: + raise AttributeError( + f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}" + ) from e + return obj + + +def derive_importpath(import_path: str, raising: bool) -> tuple[str, object]: + if not isinstance(import_path, str) or "." not in import_path: + raise TypeError(f"must be absolute import path string, not {import_path!r}") + module, attr = import_path.rsplit(".", 1) + target = resolve(module) + if raising: + annotated_getattr(target, attr, ann=module) + return attr, target + + +class Notset: + def __repr__(self) -> str: + return "" + + +notset = Notset() + + +@final +class MonkeyPatch: + """Helper to conveniently monkeypatch attributes/items/environment + variables/syspath. + + Returned by the :fixture:`monkeypatch` fixture. + + .. versionchanged:: 6.2 + Can now also be used directly as `pytest.MonkeyPatch()`, for when + the fixture is not available. In this case, use + :meth:`with MonkeyPatch.context() as mp: ` or remember to call + :meth:`undo` explicitly. + """ + + def __init__(self) -> None: + self._setattr: list[tuple[object, str, object]] = [] + self._setitem: list[tuple[Mapping[Any, Any], object, object]] = [] + self._cwd: str | None = None + self._savesyspath: list[str] | None = None + + @classmethod + @contextmanager + def context(cls) -> Generator[MonkeyPatch]: + """Context manager that returns a new :class:`MonkeyPatch` object + which undoes any patching done inside the ``with`` block upon exit. + + Example: + + .. code-block:: python + + import functools + + + def test_partial(monkeypatch): + with monkeypatch.context() as m: + m.setattr(functools, "partial", 3) + + Useful in situations where it is desired to undo some patches before the test ends, + such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples + of this see :issue:`3290`). + """ + m = cls() + try: + yield m + finally: + m.undo() + + @overload + def setattr( + self, + target: str, + name: object, + value: Notset = ..., + raising: bool = ..., + ) -> None: ... + + @overload + def setattr( + self, + target: object, + name: str, + value: object, + raising: bool = ..., + ) -> None: ... + + def setattr( + self, + target: str | object, + name: object | str, + value: object = notset, + raising: bool = True, + ) -> None: + """ + Set attribute value on target, memorizing the old value. + + For example: + + .. code-block:: python + + import os + + monkeypatch.setattr(os, "getcwd", lambda: "/") + + The code above replaces the :func:`os.getcwd` function by a ``lambda`` which + always returns ``"/"``. + + For convenience, you can specify a string as ``target`` which + will be interpreted as a dotted import path, with the last part + being the attribute name: + + .. code-block:: python + + monkeypatch.setattr("os.getcwd", lambda: "/") + + Raises :class:`AttributeError` if the attribute does not exist, unless + ``raising`` is set to False. + + **Where to patch** + + ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. + There can be many names pointing to any individual object, so for patching to work you must ensure + that you patch the name used by the system under test. + + See the section :ref:`Where to patch ` in the :mod:`unittest.mock` + docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but + applies to ``monkeypatch.setattr`` as well. + """ + __tracebackhide__ = True + import inspect + + if isinstance(value, Notset): + if not isinstance(target, str): + raise TypeError( + "use setattr(target, name, value) or " + "setattr(target, value) with target being a dotted " + "import string" + ) + value = name + name, target = derive_importpath(target, raising) + else: + if not isinstance(name, str): + raise TypeError( + "use setattr(target, name, value) with name being a string or " + "setattr(target, value) with target being a dotted " + "import string" + ) + + oldval = getattr(target, name, notset) + if raising and oldval is notset: + raise AttributeError(f"{target!r} has no attribute {name!r}") + + # avoid class descriptors like staticmethod/classmethod + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + setattr(target, name, value) + + def delattr( + self, + target: object | str, + name: str | Notset = notset, + raising: bool = True, + ) -> None: + """Delete attribute ``name`` from ``target``. + + If no ``name`` is specified and ``target`` is a string + it will be interpreted as a dotted import path with the + last part being the attribute name. + + Raises AttributeError it the attribute does not exist, unless + ``raising`` is set to False. + """ + __tracebackhide__ = True + import inspect + + if isinstance(name, Notset): + if not isinstance(target, str): + raise TypeError( + "use delattr(target, name) or " + "delattr(target) with target being a dotted " + "import string" + ) + name, target = derive_importpath(target, raising) + + if not hasattr(target, name): + if raising: + raise AttributeError(name) + else: + oldval = getattr(target, name, notset) + # Avoid class descriptors like staticmethod/classmethod. + if inspect.isclass(target): + oldval = target.__dict__.get(name, notset) + self._setattr.append((target, name, oldval)) + delattr(target, name) + + def setitem(self, dic: Mapping[K, V], name: K, value: V) -> None: + """Set dictionary entry ``name`` to value.""" + self._setitem.append((dic, name, dic.get(name, notset))) + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + dic[name] = value # type: ignore[index] + + def delitem(self, dic: Mapping[K, V], name: K, raising: bool = True) -> None: + """Delete ``name`` from dict. + + Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to + False. + """ + if name not in dic: + if raising: + raise KeyError(name) + else: + self._setitem.append((dic, name, dic.get(name, notset))) + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + del dic[name] # type: ignore[attr-defined] + + def setenv(self, name: str, value: str, prepend: str | None = None) -> None: + """Set environment variable ``name`` to ``value``. + + If ``prepend`` is a character, read the current environment variable + value and prepend the ``value`` adjoined with the ``prepend`` + character. + """ + if not isinstance(value, str): + warnings.warn( # type: ignore[unreachable] + PytestWarning( + f"Value of environment variable {name} type should be str, but got " + f"{value!r} (type: {type(value).__name__}); converted to str implicitly" + ), + stacklevel=2, + ) + value = str(value) + if prepend and name in os.environ: + value = value + prepend + os.environ[name] + self.setitem(os.environ, name, value) + + def delenv(self, name: str, raising: bool = True) -> None: + """Delete ``name`` from the environment. + + Raises ``KeyError`` if it does not exist, unless ``raising`` is set to + False. + """ + environ: MutableMapping[str, str] = os.environ + self.delitem(environ, name, raising=raising) + + def syspath_prepend(self, path) -> None: + """Prepend ``path`` to ``sys.path`` list of import locations.""" + if self._savesyspath is None: + self._savesyspath = sys.path[:] + sys.path.insert(0, str(path)) + + # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 + # this is only needed when pkg_resources was already loaded by the namespace package + if "pkg_resources" in sys.modules: + import pkg_resources + from pkg_resources import fixup_namespace_packages + + # Only issue deprecation warning if this call would actually have an + # effect for this specific path. + if ( + hasattr(pkg_resources, "_namespace_packages") + and pkg_resources._namespace_packages + ): + path_obj = Path(str(path)) + for ns_pkg in pkg_resources._namespace_packages: + if ns_pkg is None: + continue + ns_pkg_path = path_obj / ns_pkg.replace(".", os.sep) + if ns_pkg_path.is_dir(): + warnings.warn( + MONKEYPATCH_LEGACY_NAMESPACE_PACKAGES, stacklevel=2 + ) + break + + fixup_namespace_packages(str(path)) + + # A call to syspathinsert() usually means that the caller wants to + # import some dynamically created files, thus with python3 we + # invalidate its import caches. + # This is especially important when any namespace package is in use, + # since then the mtime based FileFinder cache (that gets created in + # this case already) gets not invalidated when writing the new files + # quickly afterwards. + from importlib import invalidate_caches + + invalidate_caches() + + def chdir(self, path: str | os.PathLike[str]) -> None: + """Change the current working directory to the specified path. + + :param path: + The path to change into. + """ + if self._cwd is None: + self._cwd = os.getcwd() + os.chdir(path) + + def undo(self) -> None: + """Undo previous changes. + + This call consumes the undo stack. Calling it a second time has no + effect unless you do more monkeypatching after the undo call. + + There is generally no need to call `undo()`, since it is + called automatically during tear-down. + + .. note:: + The same `monkeypatch` fixture is used across a + single test function invocation. If `monkeypatch` is used both by + the test function itself and one of the test fixtures, + calling `undo()` will undo all of the changes made in + both functions. + + Prefer to use :meth:`context() ` instead. + """ + for obj, name, value in reversed(self._setattr): + if value is not notset: + setattr(obj, name, value) + else: + delattr(obj, name) + self._setattr[:] = [] + for dictionary, key, value in reversed(self._setitem): + if value is notset: + try: + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + del dictionary[key] # type: ignore[attr-defined] + except KeyError: + pass # Was already deleted, so we have the desired state. + else: + # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict + dictionary[key] = value # type: ignore[index] + self._setitem[:] = [] + if self._savesyspath is not None: + sys.path[:] = self._savesyspath + self._savesyspath = None + + if self._cwd is not None: + os.chdir(self._cwd) + self._cwd = None diff --git a/.venv/lib/python3.12/site-packages/_pytest/nodes.py b/.venv/lib/python3.12/site-packages/_pytest/nodes.py new file mode 100644 index 0000000..6690f6a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/nodes.py @@ -0,0 +1,772 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +import abc +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import MutableMapping +from functools import cached_property +from functools import lru_cache +import os +import pathlib +from pathlib import Path +from typing import Any +from typing import cast +from typing import NoReturn +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar +import warnings + +import pluggy + +import _pytest._code +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback +from _pytest._code.code import TracebackStyle +from _pytest.compat import LEGACY_PATH +from _pytest.compat import signature +from _pytest.config import Config +from _pytest.config import ConftestImportFailure +from _pytest.config.compat import _check_path +from _pytest.deprecated import NODE_CTOR_FSPATH_ARG +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import NodeKeywords +from _pytest.outcomes import fail +from _pytest.pathlib import absolutepath +from _pytest.stash import Stash +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + # Imported here due to circular import. + from _pytest.main import Session + + +SEP = "/" + +tracebackcutdir = Path(_pytest.__file__).parent + + +_T = TypeVar("_T") + + +def _imply_path( + node_type: type[Node], + path: Path | None, + fspath: LEGACY_PATH | None, +) -> Path: + if fspath is not None: + warnings.warn( + NODE_CTOR_FSPATH_ARG.format( + node_type_name=node_type.__name__, + ), + stacklevel=6, + ) + if path is not None: + if fspath is not None: + _check_path(path, fspath) + return path + else: + assert fspath is not None + return Path(fspath) + + +_NodeType = TypeVar("_NodeType", bound="Node") + + +class NodeMeta(abc.ABCMeta): + """Metaclass used by :class:`Node` to enforce that direct construction raises + :class:`Failed`. + + This behaviour supports the indirection introduced with :meth:`Node.from_parent`, + the named constructor to be used instead of direct construction. The design + decision to enforce indirection with :class:`NodeMeta` was made as a + temporary aid for refactoring the collection tree, which was diagnosed to + have :class:`Node` objects whose creational patterns were overly entangled. + Once the refactoring is complete, this metaclass can be removed. + + See https://github.com/pytest-dev/pytest/projects/3 for an overview of the + progress on detangling the :class:`Node` classes. + """ + + def __call__(cls, *k, **kw) -> NoReturn: + msg = ( + "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" + "See " + "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" + " for more details." + ).format(name=f"{cls.__module__}.{cls.__name__}") + fail(msg, pytrace=False) + + def _create(cls: type[_T], *k, **kw) -> _T: + try: + return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] + except TypeError: + sig = signature(getattr(cls, "__init__")) + known_kw = {k: v for k, v in kw.items() if k in sig.parameters} + from .warning_types import PytestDeprecationWarning + + warnings.warn( + PytestDeprecationWarning( + f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" + "See https://docs.pytest.org/en/stable/deprecations.html" + "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " + "for more details." + ) + ) + + return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc] + + +class Node(abc.ABC, metaclass=NodeMeta): + r"""Base class of :class:`Collector` and :class:`Item`, the components of + the test collection tree. + + ``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the + leaf nodes. + """ + + # Implemented in the legacypath plugin. + #: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage + #: for methods not migrated to ``pathlib.Path`` yet, such as + #: :meth:`Item.reportinfo `. Will be deprecated in + #: a future release, prefer using :attr:`path` instead. + fspath: LEGACY_PATH + + # Use __slots__ to make attribute access faster. + # Note that __dict__ is still available. + __slots__ = ( + "__dict__", + "_nodeid", + "_store", + "config", + "name", + "parent", + "path", + "session", + ) + + def __init__( + self, + name: str, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + nodeid: str | None = None, + ) -> None: + #: A unique name within the scope of the parent node. + self.name: str = name + + #: The parent collector node. + self.parent = parent + + if config: + #: The pytest config object. + self.config: Config = config + else: + if not parent: + raise TypeError("config or parent must be provided") + self.config = parent.config + + if session: + #: The pytest session this node is part of. + self.session: Session = session + else: + if not parent: + raise TypeError("session or parent must be provided") + self.session = parent.session + + if path is None and fspath is None: + path = getattr(parent, "path", None) + #: Filesystem path where this node was collected from (can be None). + self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) + + # The explicit annotation is to avoid publicly exposing NodeKeywords. + #: Keywords/markers collected from all scopes. + self.keywords: MutableMapping[str, Any] = NodeKeywords(self) + + #: The marker objects belonging to this node. + self.own_markers: list[Mark] = [] + + #: Allow adding of extra keywords to use for matching. + self.extra_keyword_matches: set[str] = set() + + if nodeid is not None: + assert "::()" not in nodeid + self._nodeid = nodeid + else: + if not self.parent: + raise TypeError("nodeid or parent must be provided") + self._nodeid = self.parent.nodeid + "::" + self.name + + #: A place where plugins can store information on the node for their + #: own use. + self.stash: Stash = Stash() + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + + @classmethod + def from_parent(cls, parent: Node, **kw) -> Self: + """Public constructor for Nodes. + + This indirection got introduced in order to enable removing + the fragile logic from the node constructors. + + Subclasses can use ``super().from_parent(...)`` when overriding the + construction. + + :param parent: The parent node of this Node. + """ + if "config" in kw: + raise TypeError("config is not a valid argument for from_parent") + if "session" in kw: + raise TypeError("session is not a valid argument for from_parent") + return cls._create(parent=parent, **kw) + + @property + def ihook(self) -> pluggy.HookRelay: + """fspath-sensitive hook proxy used to call pytest hooks.""" + return self.session.gethookproxy(self.path) + + def __repr__(self) -> str: + return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) + + def warn(self, warning: Warning) -> None: + """Issue a warning for this Node. + + Warnings will be displayed after the test session, unless explicitly suppressed. + + :param Warning warning: + The warning instance to issue. + + :raises ValueError: If ``warning`` instance is not a subclass of Warning. + + Example usage: + + .. code-block:: python + + node.warn(PytestWarning("some message")) + node.warn(UserWarning("some message")) + + .. versionchanged:: 6.2 + Any subclass of :class:`Warning` is now accepted, rather than only + :class:`PytestWarning ` subclasses. + """ + # enforce type checks here to avoid getting a generic type error later otherwise. + if not isinstance(warning, Warning): + raise ValueError( + f"warning must be an instance of Warning or subclass, got {warning!r}" + ) + path, lineno = get_fslocation_from_item(self) + assert lineno is not None + warnings.warn_explicit( + warning, + category=None, + filename=str(path), + lineno=lineno + 1, + ) + + # Methods for ordering nodes. + + @property + def nodeid(self) -> str: + """A ::-separated string denoting its collection tree address.""" + return self._nodeid + + def __hash__(self) -> int: + return hash(self._nodeid) + + def setup(self) -> None: + pass + + def teardown(self) -> None: + pass + + def iter_parents(self) -> Iterator[Node]: + """Iterate over all parent collectors starting from and including self + up to the root of the collection tree. + + .. versionadded:: 8.1 + """ + parent: Node | None = self + while parent is not None: + yield parent + parent = parent.parent + + def listchain(self) -> list[Node]: + """Return a list of all parent collectors starting from the root of the + collection tree down to and including self.""" + chain = [] + item: Node | None = self + while item is not None: + chain.append(item) + item = item.parent + chain.reverse() + return chain + + def add_marker(self, marker: str | MarkDecorator, append: bool = True) -> None: + """Dynamically add a marker object to the node. + + :param marker: + The marker. + :param append: + Whether to append the marker, or prepend it. + """ + from _pytest.mark import MARK_GEN + + if isinstance(marker, MarkDecorator): + marker_ = marker + elif isinstance(marker, str): + marker_ = getattr(MARK_GEN, marker) + else: + raise ValueError("is not a string or pytest.mark.* Marker") + self.keywords[marker_.name] = marker_ + if append: + self.own_markers.append(marker_.mark) + else: + self.own_markers.insert(0, marker_.mark) + + def iter_markers(self, name: str | None = None) -> Iterator[Mark]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of the markers of the node. + """ + return (x[1] for x in self.iter_markers_with_node(name=name)) + + def iter_markers_with_node( + self, name: str | None = None + ) -> Iterator[tuple[Node, Mark]]: + """Iterate over all markers of the node. + + :param name: If given, filter the results by the name attribute. + :returns: An iterator of (node, mark) tuples. + """ + for node in self.iter_parents(): + for mark in node.own_markers: + if name is None or getattr(mark, "name", None) == name: + yield node, mark + + @overload + def get_closest_marker(self, name: str) -> Mark | None: ... + + @overload + def get_closest_marker(self, name: str, default: Mark) -> Mark: ... + + def get_closest_marker(self, name: str, default: Mark | None = None) -> Mark | None: + """Return the first marker matching the name, from closest (for + example function) to farther level (for example module level). + + :param default: Fallback return value if no marker was found. + :param name: Name to filter by. + """ + return next(self.iter_markers(name=name), default) + + def listextrakeywords(self) -> set[str]: + """Return a set of all extra keywords in self and any parents.""" + extra_keywords: set[str] = set() + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + + def listnames(self) -> list[str]: + return [x.name for x in self.listchain()] + + def addfinalizer(self, fin: Callable[[], object]) -> None: + """Register a function to be called without arguments when this node is + finalized. + + This method can only be called when this node is active + in a setup chain, for example during self.setup(). + """ + self.session._setupstate.addfinalizer(fin, self) + + def getparent(self, cls: type[_NodeType]) -> _NodeType | None: + """Get the closest parent node (including self) which is an instance of + the given class. + + :param cls: The node class to search for. + :returns: The node, if found. + """ + for node in self.iter_parents(): + if isinstance(node, cls): + return node + return None + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + return excinfo.traceback + + def _repr_failure_py( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> TerminalRepr: + from _pytest.fixtures import FixtureLookupError + + if isinstance(excinfo.value, ConftestImportFailure): + excinfo = ExceptionInfo.from_exception(excinfo.value.cause) + if isinstance(excinfo.value, fail.Exception): + if not excinfo.value.pytrace: + style = "value" + if isinstance(excinfo.value, FixtureLookupError): + return excinfo.value.formatrepr() + + tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] + if self.config.getoption("fulltrace", False): + style = "long" + tbfilter = False + else: + tbfilter = self._traceback_filter + if style == "auto": + style = "long" + # XXX should excinfo.getrepr record all data and toterminal() process it? + if style is None: + if self.config.getoption("tbstyle", "auto") == "short": + style = "short" + else: + style = "long" + + if self.config.get_verbosity() > 1: + truncate_locals = False + else: + truncate_locals = True + + truncate_args = False if self.config.get_verbosity() > 2 else True + + # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. + # It is possible for a fixture/test to change the CWD while this code runs, which + # would then result in the user seeing confusing paths in the failure message. + # To fix this, if the CWD changed, always display the full absolute path. + # It will be better to just always display paths relative to invocation_dir, but + # this requires a lot of plumbing (#6428). + try: + abspath = Path(os.getcwd()) != self.config.invocation_params.dir + except OSError: + abspath = True + + return excinfo.getrepr( + funcargs=True, + abspath=abspath, + showlocals=self.config.getoption("showlocals", False), + style=style, + tbfilter=tbfilter, + truncate_locals=truncate_locals, + truncate_args=truncate_args, + ) + + def repr_failure( + self, + excinfo: ExceptionInfo[BaseException], + style: TracebackStyle | None = None, + ) -> str | TerminalRepr: + """Return a representation of a collection or test failure. + + .. seealso:: :ref:`non-python tests` + + :param excinfo: Exception information for the failure. + """ + return self._repr_failure_py(excinfo, style) + + +def get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]: + """Try to extract the actual location from a node, depending on available attributes: + + * "location": a pair (path, lineno) + * "obj": a Python object that the node wraps. + * "path": just a path + + :rtype: A tuple of (str|Path, int) with filename and 0-based line number. + """ + # See Item.location. + location: tuple[str, int | None, str] | None = getattr(node, "location", None) + if location is not None: + return location[:2] + obj = getattr(node, "obj", None) + if obj is not None: + return getfslineno(obj) + return getattr(node, "path", "unknown location"), -1 + + +class Collector(Node, abc.ABC): + """Base class of all collectors. + + Collector create children through `collect()` and thus iteratively build + the collection tree. + """ + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """Collect children (items and collectors) for this collector.""" + raise NotImplementedError("abstract") + + # TODO: This omits the style= parameter which breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, excinfo: ExceptionInfo[BaseException] + ) -> str | TerminalRepr: + """Return a representation of a collection failure. + + :param excinfo: Exception information for the failure. + """ + if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( + "fulltrace", False + ): + exc = excinfo.value + return str(exc.args[0]) + + # Respect explicit tbstyle option, but default to "short" + # (_repr_failure_py uses "long" with "fulltrace" option always). + tbstyle = self.config.getoption("tbstyle", "auto") + if tbstyle == "auto": + tbstyle = "short" + + return self._repr_failure_py(excinfo, style=tbstyle) + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + if hasattr(self, "path"): + traceback = excinfo.traceback + ntraceback = traceback.cut(path=self.path) + if ntraceback == traceback: + ntraceback = ntraceback.cut(excludepath=tracebackcutdir) + return ntraceback.filter(excinfo) + return excinfo.traceback + + +@lru_cache(maxsize=1000) +def _check_initialpaths_for_relpath( + initial_paths: frozenset[Path], path: Path +) -> str | None: + if path in initial_paths: + return "" + + for parent in path.parents: + if parent in initial_paths: + return str(path.relative_to(parent)) + + return None + + +class FSCollector(Collector, abc.ABC): + """Base class for filesystem collectors.""" + + def __init__( + self, + fspath: LEGACY_PATH | None = None, + path_or_parent: Path | Node | None = None, + path: Path | None = None, + name: str | None = None, + parent: Node | None = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + ) -> None: + if path_or_parent: + if isinstance(path_or_parent, Node): + assert parent is None + parent = cast(FSCollector, path_or_parent) + elif isinstance(path_or_parent, Path): + assert path is None + path = path_or_parent + + path = _imply_path(type(self), path, fspath=fspath) + if name is None: + name = path.name + if parent is not None and parent.path != path: + try: + rel = path.relative_to(parent.path) + except ValueError: + pass + else: + name = str(rel) + name = name.replace(os.sep, SEP) + self.path = path + + if session is None: + assert parent is not None + session = parent.session + + if nodeid is None: + try: + nodeid = str(self.path.relative_to(session.config.rootpath)) + except ValueError: + nodeid = _check_initialpaths_for_relpath(session._initialpaths, path) + + if nodeid and os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super().__init__( + name=name, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + path=path, + ) + + @classmethod + def from_parent( + cls, + parent, + *, + fspath: LEGACY_PATH | None = None, + path: Path | None = None, + **kw, + ) -> Self: + """The public constructor.""" + return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) + + +class File(FSCollector, abc.ABC): + """Base class for collecting tests from a file. + + :ref:`non-python tests`. + """ + + +class Directory(FSCollector, abc.ABC): + """Base class for collecting files from a directory. + + A basic directory collector does the following: goes over the files and + sub-directories in the directory and creates collectors for them by calling + the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, + after checking that they are not ignored using + :hook:`pytest_ignore_collect`. + + The default directory collectors are :class:`~pytest.Dir` and + :class:`~pytest.Package`. + + .. versionadded:: 8.0 + + :ref:`custom directory collectors`. + """ + + +class Item(Node, abc.ABC): + """Base class of all test invocation items. + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None + + def __init__( + self, + name, + parent=None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + **kw, + ) -> None: + # The first two arguments are intentionally passed positionally, + # to keep plugins who define a node type which inherits from + # (pytest.Item, pytest.File) working (see issue #8435). + # They can be made kwargs when the deprecation above is done. + super().__init__( + name, + parent, + config=config, + session=session, + nodeid=nodeid, + **kw, + ) + self._report_sections: list[tuple[str, str, str]] = [] + + #: A list of tuples (name, value) that holds user defined properties + #: for this test. + self.user_properties: list[tuple[str, object]] = [] + + self._check_item_and_collector_diamond_inheritance() + + def _check_item_and_collector_diamond_inheritance(self) -> None: + """ + Check if the current type inherits from both File and Collector + at the same time, emitting a warning accordingly (#8447). + """ + cls = type(self) + + # We inject an attribute in the type to avoid issuing this warning + # for the same class more than once, which is not helpful. + # It is a hack, but was deemed acceptable in order to avoid + # flooding the user in the common case. + attr_name = "_pytest_diamond_inheritance_warning_shown" + if getattr(cls, attr_name, False): + return + setattr(cls, attr_name, True) + + problems = ", ".join( + base.__name__ for base in cls.__bases__ if issubclass(base, Collector) + ) + if problems: + warnings.warn( + f"{cls.__name__} is an Item subclass and should not be a collector, " + f"however its bases {problems} are collectors.\n" + "Please split the Collectors and the Item into separate node types.\n" + "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" + "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", + PytestWarning, + ) + + @abc.abstractmethod + def runtest(self) -> None: + """Run the test case for this item. + + Must be implemented by subclasses. + + .. seealso:: :ref:`non-python tests` + """ + raise NotImplementedError("runtest must be implemented by Item subclass") + + def add_report_section(self, when: str, key: str, content: str) -> None: + """Add a new report section, similar to what's done internally to add + stdout and stderr captured output:: + + item.add_report_section("call", "stdout", "report section contents") + + :param str when: + One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. + :param str key: + Name of the section, can be customized at will. Pytest uses ``"stdout"`` and + ``"stderr"`` internally. + :param str content: + The full contents as a string. + """ + if content: + self._report_sections.append((when, key, content)) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + """Get location information for this item for test reports. + + Returns a tuple with three elements: + + - The path of the test (default ``self.path``) + - The 0-based line number of the test (default ``None``) + - A name of the test to be shown (default ``""``) + + .. seealso:: :ref:`non-python tests` + """ + return self.path, None, "" + + @cached_property + def location(self) -> tuple[str, int | None, str]: + """ + Returns a tuple of ``(relfspath, lineno, testname)`` for this item + where ``relfspath`` is file path relative to ``config.rootpath`` + and lineno is a 0-based line number. + """ + location = self.reportinfo() + path = absolutepath(location[0]) + relfspath = self.session._node_location_to_relpath(path) + assert type(location[2]) is str + return (relfspath, location[1], location[2]) diff --git a/.venv/lib/python3.12/site-packages/_pytest/outcomes.py b/.venv/lib/python3.12/site-packages/_pytest/outcomes.py new file mode 100644 index 0000000..766be95 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/outcomes.py @@ -0,0 +1,308 @@ +"""Exception classes and constants handling test outcomes as well as +functions creating them.""" + +from __future__ import annotations + +import sys +from typing import Any +from typing import ClassVar +from typing import NoReturn + +from .warning_types import PytestDeprecationWarning + + +class OutcomeException(BaseException): + """OutcomeException and its subclass instances indicate and contain info + about test and collection outcomes.""" + + def __init__(self, msg: str | None = None, pytrace: bool = True) -> None: + if msg is not None and not isinstance(msg, str): + error_msg = ( # type: ignore[unreachable] + "{} expected string as 'msg' parameter, got '{}' instead.\n" + "Perhaps you meant to use a mark?" + ) + raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__)) + super().__init__(msg) + self.msg = msg + self.pytrace = pytrace + + def __repr__(self) -> str: + if self.msg is not None: + return self.msg + return f"<{self.__class__.__name__} instance>" + + __str__ = __repr__ + + +TEST_OUTCOME = (OutcomeException, Exception) + + +class Skipped(OutcomeException): + # XXX hackish: on 3k we fake to live in the builtins + # in order to have Skipped exception printing shorter/nicer + __module__ = "builtins" + + def __init__( + self, + msg: str | None = None, + pytrace: bool = True, + allow_module_level: bool = False, + *, + _use_item_location: bool = False, + ) -> None: + super().__init__(msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + # If true, the skip location is reported as the item's location, + # instead of the place that raises the exception/calls skip(). + self._use_item_location = _use_item_location + + +class Failed(OutcomeException): + """Raised from an explicit call to pytest.fail().""" + + __module__ = "builtins" + + +class Exit(Exception): + """Raised for immediate program exits (no tracebacks/summaries).""" + + def __init__( + self, msg: str = "unknown reason", returncode: int | None = None + ) -> None: + self.msg = msg + self.returncode = returncode + super().__init__(msg) + + +class XFailed(Failed): + """Raised from an explicit call to pytest.xfail().""" + + +class _Exit: + """Exit testing process. + + :param reason: + The message to show as the reason for exiting pytest. reason has a default value + only because `msg` is deprecated. + + :param returncode: + Return code to be used when exiting pytest. None means the same as ``0`` (no error), + same as :func:`sys.exit`. + + :raises pytest.exit.Exception: + The exception that is raised. + """ + + Exception: ClassVar[type[Exit]] = Exit + + def __call__(self, reason: str = "", returncode: int | None = None) -> NoReturn: + __tracebackhide__ = True + raise Exit(msg=reason, returncode=returncode) + + +exit: _Exit = _Exit() + + +class _Skip: + """Skip an executing test with the given message. + + This function should be called only during testing (setup, call or teardown) or + during collection by using the ``allow_module_level`` flag. This function can + be called in doctests as well. + + :param reason: + The message to show the user as reason for the skip. + + :param allow_module_level: + Allows this function to be called at module level. + Raising the skip exception at module level will stop + the execution of the module and prevent the collection of all tests in the module, + even those defined before the `skip` call. + + Defaults to False. + + :raises pytest.skip.Exception: + The exception that is raised. + + .. note:: + It is better to use the :ref:`pytest.mark.skipif ref` marker when + possible to declare a test to be skipped under certain conditions + like mismatching platforms or dependencies. + Similarly, use the ``# doctest: +SKIP`` directive (see :py:data:`doctest.SKIP`) + to skip a doctest statically. + """ + + Exception: ClassVar[type[Skipped]] = Skipped + + def __call__(self, reason: str = "", allow_module_level: bool = False) -> NoReturn: + __tracebackhide__ = True + raise Skipped(msg=reason, allow_module_level=allow_module_level) + + +skip: _Skip = _Skip() + + +class _Fail: + """Explicitly fail an executing test with the given message. + + :param reason: + The message to show the user as reason for the failure. + + :param pytrace: + If False, msg represents the full failure information and no + python traceback will be reported. + + :raises pytest.fail.Exception: + The exception that is raised. + """ + + Exception: ClassVar[type[Failed]] = Failed + + def __call__(self, reason: str = "", pytrace: bool = True) -> NoReturn: + __tracebackhide__ = True + raise Failed(msg=reason, pytrace=pytrace) + + +fail: _Fail = _Fail() + + +class _XFail: + """Imperatively xfail an executing test or setup function with the given reason. + + This function should be called only during testing (setup, call or teardown). + + No other code is executed after using ``xfail()`` (it is implemented + internally by raising an exception). + + :param reason: + The message to show the user as reason for the xfail. + + .. note:: + It is better to use the :ref:`pytest.mark.xfail ref` marker when + possible to declare a test to be xfailed under certain conditions + like known bugs or missing features. + + :raises pytest.xfail.Exception: + The exception that is raised. + """ + + Exception: ClassVar[type[XFailed]] = XFailed + + def __call__(self, reason: str = "") -> NoReturn: + __tracebackhide__ = True + raise XFailed(msg=reason) + + +xfail: _XFail = _XFail() + + +def importorskip( + modname: str, + minversion: str | None = None, + reason: str | None = None, + *, + exc_type: type[ImportError] | None = None, +) -> Any: + """Import and return the requested module ``modname``, or skip the + current test if the module cannot be imported. + + :param modname: + The name of the module to import. + :param minversion: + If given, the imported module's ``__version__`` attribute must be at + least this minimal version, otherwise the test is still skipped. + :param reason: + If given, this reason is shown as the message when the module cannot + be imported. + :param exc_type: + The exception that should be captured in order to skip modules. + Must be :py:class:`ImportError` or a subclass. + + If the module can be imported but raises :class:`ImportError`, pytest will + issue a warning to the user, as often users expect the module not to be + found (which would raise :class:`ModuleNotFoundError` instead). + + This warning can be suppressed by passing ``exc_type=ImportError`` explicitly. + + See :ref:`import-or-skip-import-error` for details. + + + :returns: + The imported module. This should be assigned to its canonical name. + + :raises pytest.skip.Exception: + If the module cannot be imported. + + Example:: + + docutils = pytest.importorskip("docutils") + + .. versionadded:: 8.2 + + The ``exc_type`` parameter. + """ + import warnings + + __tracebackhide__ = True + compile(modname, "", "eval") # to catch syntaxerrors + + # Until pytest 9.1, we will warn the user if we catch ImportError (instead of ModuleNotFoundError), + # as this might be hiding an installation/environment problem, which is not usually what is intended + # when using importorskip() (#11523). + # In 9.1, to keep the function signature compatible, we just change the code below to: + # 1. Use `exc_type = ModuleNotFoundError` if `exc_type` is not given. + # 2. Remove `warn_on_import` and the warning handling. + if exc_type is None: + exc_type = ImportError + warn_on_import_error = True + else: + warn_on_import_error = False + + skipped: Skipped | None = None + warning: Warning | None = None + + with warnings.catch_warnings(): + # Make sure to ignore ImportWarnings that might happen because + # of existing directories with the same name we're trying to + # import but without a __init__.py file. + warnings.simplefilter("ignore") + + try: + __import__(modname) + except exc_type as exc: + # Do not raise or issue warnings inside the catch_warnings() block. + if reason is None: + reason = f"could not import {modname!r}: {exc}" + skipped = Skipped(reason, allow_module_level=True) + + if warn_on_import_error and not isinstance(exc, ModuleNotFoundError): + lines = [ + "", + f"Module '{modname}' was found, but when imported by pytest it raised:", + f" {exc!r}", + "In pytest 9.1 this warning will become an error by default.", + "You can fix the underlying problem, or alternatively overwrite this behavior and silence this " + "warning by passing exc_type=ImportError explicitly.", + "See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror", + ] + warning = PytestDeprecationWarning("\n".join(lines)) + + if warning: + warnings.warn(warning, stacklevel=2) + if skipped: + raise skipped + + mod = sys.modules[modname] + if minversion is None: + return mod + verattr = getattr(mod, "__version__", None) + if minversion is not None: + # Imported lazily to improve start-up time. + from packaging.version import Version + + if verattr is None or Version(verattr) < Version(minversion): + raise Skipped( + f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", + allow_module_level=True, + ) + return mod diff --git a/.venv/lib/python3.12/site-packages/_pytest/pastebin.py b/.venv/lib/python3.12/site-packages/_pytest/pastebin.py new file mode 100644 index 0000000..c7b39d9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/pastebin.py @@ -0,0 +1,117 @@ +# mypy: allow-untyped-defs +"""Submit failure or test session information to a pastebin service.""" + +from __future__ import annotations + +from io import StringIO +import tempfile +from typing import IO + +from _pytest.config import Config +from _pytest.config import create_terminal_writer +from _pytest.config.argparsing import Parser +from _pytest.stash import StashKey +from _pytest.terminal import TerminalReporter +import pytest + + +pastebinfile_key = StashKey[IO[bytes]]() + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting") + group.addoption( + "--pastebin", + metavar="mode", + action="store", + dest="pastebin", + default=None, + choices=["failed", "all"], + help="Send failed|all info to bpaste.net pastebin service", + ) + + +@pytest.hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + if config.option.pastebin == "all": + tr = config.pluginmanager.getplugin("terminalreporter") + # If no terminal reporter plugin is present, nothing we can do here; + # this can happen when this function executes in a worker node + # when using pytest-xdist, for example. + if tr is not None: + # pastebin file will be UTF-8 encoded binary file. + config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") + oldwrite = tr._tw.write + + def tee_write(s, **kwargs): + oldwrite(s, **kwargs) + if isinstance(s, str): + s = s.encode("utf-8") + config.stash[pastebinfile_key].write(s) + + tr._tw.write = tee_write + + +def pytest_unconfigure(config: Config) -> None: + if pastebinfile_key in config.stash: + pastebinfile = config.stash[pastebinfile_key] + # Get terminal contents and delete file. + pastebinfile.seek(0) + sessionlog = pastebinfile.read() + pastebinfile.close() + del config.stash[pastebinfile_key] + # Undo our patching in the terminal reporter. + tr = config.pluginmanager.getplugin("terminalreporter") + del tr._tw.__dict__["write"] + # Write summary. + tr.write_sep("=", "Sending information to Paste Service") + pastebinurl = create_new_paste(sessionlog) + tr.write_line(f"pastebin session-log: {pastebinurl}\n") + + +def create_new_paste(contents: str | bytes) -> str: + """Create a new paste using the bpaste.net service. + + :contents: Paste contents string. + :returns: URL to the pasted contents, or an error message. + """ + import re + from urllib.error import HTTPError + from urllib.parse import urlencode + from urllib.request import urlopen + + params = {"code": contents, "lexer": "text", "expiry": "1week"} + url = "https://bpa.st" + try: + response: str = ( + urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") + ) + except HTTPError as e: + with e: # HTTPErrors are also http responses that must be closed! + return f"bad response: {e}" + except OSError as e: # eg urllib.error.URLError + return f"bad response: {e}" + m = re.search(r'href="/raw/(\w+)"', response) + if m: + return f"{url}/show/{m.group(1)}" + else: + return "bad response: invalid format ('" + response + "')" + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + if terminalreporter.config.option.pastebin != "failed": + return + if "failed" in terminalreporter.stats: + terminalreporter.write_sep("=", "Sending information to Paste Service") + for rep in terminalreporter.stats["failed"]: + try: + msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc + except AttributeError: + msg = terminalreporter._getfailureheadline(rep) + file = StringIO() + tw = create_terminal_writer(terminalreporter.config, file) + rep.toterminal(tw) + s = file.getvalue() + assert len(s) + pastebinurl = create_new_paste(s) + terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/.venv/lib/python3.12/site-packages/_pytest/pathlib.py b/.venv/lib/python3.12/site-packages/_pytest/pathlib.py new file mode 100644 index 0000000..cd15434 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/pathlib.py @@ -0,0 +1,1063 @@ +from __future__ import annotations + +import atexit +from collections.abc import Callable +from collections.abc import Iterable +from collections.abc import Iterator +import contextlib +from enum import Enum +from errno import EBADF +from errno import ELOOP +from errno import ENOENT +from errno import ENOTDIR +import fnmatch +from functools import partial +from importlib.machinery import ModuleSpec +from importlib.machinery import PathFinder +import importlib.util +import itertools +import os +from os.path import expanduser +from os.path import expandvars +from os.path import isabs +from os.path import sep +from pathlib import Path +from pathlib import PurePath +from posixpath import sep as posix_sep +import shutil +import sys +import types +from types import ModuleType +from typing import Any +from typing import TypeVar +import uuid +import warnings + +from _pytest.compat import assert_never +from _pytest.outcomes import skip +from _pytest.warning_types import PytestWarning + + +if sys.version_info < (3, 11): + from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader +else: + from importlib.machinery import NamespaceLoader + +LOCK_TIMEOUT = 60 * 60 * 24 * 3 + +_AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) + +# The following function, variables and comments were +# copied from cpython 3.9 Lib/pathlib.py file. + +# EBADF - guard against macOS `stat` throwing EBADF +_IGNORED_ERRORS = (ENOENT, ENOTDIR, EBADF, ELOOP) + +_IGNORED_WINERRORS = ( + 21, # ERROR_NOT_READY - drive exists but is not accessible + 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself +) + + +def _ignore_error(exception: Exception) -> bool: + return ( + getattr(exception, "errno", None) in _IGNORED_ERRORS + or getattr(exception, "winerror", None) in _IGNORED_WINERRORS + ) + + +def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: + return path.joinpath(".lock") + + +def on_rm_rf_error( + func: Callable[..., Any] | None, + path: str, + excinfo: BaseException + | tuple[type[BaseException], BaseException, types.TracebackType | None], + *, + start_path: Path, +) -> bool: + """Handle known read-only errors during rmtree. + + The returned value is used only by our own tests. + """ + if isinstance(excinfo, BaseException): + exc = excinfo + else: + exc = excinfo[1] + + # Another process removed the file in the middle of the "rm_rf" (xdist for example). + # More context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 + if isinstance(exc, FileNotFoundError): + return False + + if not isinstance(exc, PermissionError): + warnings.warn( + PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}") + ) + return False + + if func not in (os.rmdir, os.remove, os.unlink): + if func not in (os.open,): + warnings.warn( + PytestWarning( + f"(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}" + ) + ) + return False + + # Chmod + retry. + import stat + + def chmod_rw(p: str) -> None: + mode = os.stat(p).st_mode + os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) + + # For files, we need to recursively go upwards in the directories to + # ensure they all are also writable. + p = Path(path) + if p.is_file(): + for parent in p.parents: + chmod_rw(str(parent)) + # Stop when we reach the original path passed to rm_rf. + if parent == start_path: + break + chmod_rw(str(path)) + + func(path) + return True + + +def ensure_extended_length_path(path: Path) -> Path: + """Get the extended-length version of a path (Windows). + + On Windows, by default, the maximum length of a path (MAX_PATH) is 260 + characters, and operations on paths longer than that fail. But it is possible + to overcome this by converting the path to "extended-length" form before + performing the operation: + https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation + + On Windows, this function returns the extended-length absolute version of path. + On other platforms it returns path unchanged. + """ + if sys.platform.startswith("win32"): + path = path.resolve() + path = Path(get_extended_length_path_str(str(path))) + return path + + +def get_extended_length_path_str(path: str) -> str: + """Convert a path to a Windows extended length path.""" + long_path_prefix = "\\\\?\\" + unc_long_path_prefix = "\\\\?\\UNC\\" + if path.startswith((long_path_prefix, unc_long_path_prefix)): + return path + # UNC + if path.startswith("\\\\"): + return unc_long_path_prefix + path[2:] + return long_path_prefix + path + + +def rm_rf(path: Path) -> None: + """Remove the path contents recursively, even if some elements + are read-only.""" + path = ensure_extended_length_path(path) + onerror = partial(on_rm_rf_error, start_path=path) + if sys.version_info >= (3, 12): + shutil.rmtree(str(path), onexc=onerror) + else: + shutil.rmtree(str(path), onerror=onerror) + + +def find_prefixed(root: Path, prefix: str) -> Iterator[os.DirEntry[str]]: + """Find all elements in root that begin with the prefix, case-insensitive.""" + l_prefix = prefix.lower() + for x in os.scandir(root): + if x.name.lower().startswith(l_prefix): + yield x + + +def extract_suffixes(iter: Iterable[os.DirEntry[str]], prefix: str) -> Iterator[str]: + """Return the parts of the paths following the prefix. + + :param iter: Iterator over path names. + :param prefix: Expected prefix of the path names. + """ + p_len = len(prefix) + for entry in iter: + yield entry.name[p_len:] + + +def find_suffixes(root: Path, prefix: str) -> Iterator[str]: + """Combine find_prefixes and extract_suffixes.""" + return extract_suffixes(find_prefixed(root, prefix), prefix) + + +def parse_num(maybe_num: str) -> int: + """Parse number path suffixes, returns -1 on error.""" + try: + return int(maybe_num) + except ValueError: + return -1 + + +def _force_symlink(root: Path, target: str | PurePath, link_to: str | Path) -> None: + """Helper to create the current symlink. + + It's full of race conditions that are reasonably OK to ignore + for the context of best effort linking to the latest test run. + + The presumption being that in case of much parallelism + the inaccuracy is going to be acceptable. + """ + current_symlink = root.joinpath(target) + try: + current_symlink.unlink() + except OSError: + pass + try: + current_symlink.symlink_to(link_to) + except Exception: + pass + + +def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: + """Create a directory with an increased number as suffix for the given prefix.""" + for i in range(10): + # try up to 10 times to create the folder + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + new_number = max_existing + 1 + new_path = root.joinpath(f"{prefix}{new_number}") + try: + new_path.mkdir(mode=mode) + except Exception: + pass + else: + _force_symlink(root, prefix + "current", new_path) + return new_path + else: + raise OSError( + "could not create numbered dir with prefix " + f"{prefix} in {root} after 10 tries" + ) + + +def create_cleanup_lock(p: Path) -> Path: + """Create a lock to prevent premature folder cleanup.""" + lock_path = get_lock_path(p) + try: + fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) + except FileExistsError as e: + raise OSError(f"cannot create lockfile in {p}") from e + else: + pid = os.getpid() + spid = str(pid).encode() + os.write(fd, spid) + os.close(fd) + if not lock_path.is_file(): + raise OSError("lock path got renamed after successful creation") + return lock_path + + +def register_cleanup_lock_removal( + lock_path: Path, register: Any = atexit.register +) -> Any: + """Register a cleanup function for removing a lock, by default on atexit.""" + pid = os.getpid() + + def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None: + current_pid = os.getpid() + if current_pid != original_pid: + # fork + return + try: + lock_path.unlink() + except OSError: + pass + + return register(cleanup_on_exit) + + +def maybe_delete_a_numbered_dir(path: Path) -> None: + """Remove a numbered directory if its lock can be obtained and it does + not seem to be in use.""" + path = ensure_extended_length_path(path) + lock_path = None + try: + lock_path = create_cleanup_lock(path) + parent = path.parent + + garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") + path.rename(garbage) + rm_rf(garbage) + except OSError: + # known races: + # * other process did a cleanup at the same time + # * deletable folder was found + # * process cwd (Windows) + return + finally: + # If we created the lock, ensure we remove it even if we failed + # to properly remove the numbered dir. + if lock_path is not None: + try: + lock_path.unlink() + except OSError: + pass + + +def ensure_deletable(path: Path, consider_lock_dead_if_created_before: float) -> bool: + """Check if `path` is deletable based on whether the lock file is expired.""" + if path.is_symlink(): + return False + lock = get_lock_path(path) + try: + if not lock.is_file(): + return True + except OSError: + # we might not have access to the lock file at all, in this case assume + # we don't have access to the entire directory (#7491). + return False + try: + lock_time = lock.stat().st_mtime + except Exception: + return False + else: + if lock_time < consider_lock_dead_if_created_before: + # We want to ignore any errors while trying to remove the lock such as: + # - PermissionDenied, like the file permissions have changed since the lock creation; + # - FileNotFoundError, in case another pytest process got here first; + # and any other cause of failure. + with contextlib.suppress(OSError): + lock.unlink() + return True + return False + + +def try_cleanup(path: Path, consider_lock_dead_if_created_before: float) -> None: + """Try to cleanup a folder if we can ensure it's deletable.""" + if ensure_deletable(path, consider_lock_dead_if_created_before): + maybe_delete_a_numbered_dir(path) + + +def cleanup_candidates(root: Path, prefix: str, keep: int) -> Iterator[Path]: + """List candidates for numbered directories to be removed - follows py.path.""" + max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) + max_delete = max_existing - keep + entries = find_prefixed(root, prefix) + entries, entries2 = itertools.tee(entries) + numbers = map(parse_num, extract_suffixes(entries2, prefix)) + for entry, number in zip(entries, numbers, strict=True): + if number <= max_delete: + yield Path(entry) + + +def cleanup_dead_symlinks(root: Path) -> None: + for left_dir in root.iterdir(): + if left_dir.is_symlink(): + if not left_dir.resolve().exists(): + left_dir.unlink() + + +def cleanup_numbered_dir( + root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float +) -> None: + """Cleanup for lock driven numbered directories.""" + if not root.exists(): + return + for path in cleanup_candidates(root, prefix, keep): + try_cleanup(path, consider_lock_dead_if_created_before) + for path in root.glob("garbage-*"): + try_cleanup(path, consider_lock_dead_if_created_before) + + cleanup_dead_symlinks(root) + + +def make_numbered_dir_with_cleanup( + root: Path, + prefix: str, + keep: int, + lock_timeout: float, + mode: int, +) -> Path: + """Create a numbered dir with a cleanup lock and remove old ones.""" + e = None + for i in range(10): + try: + p = make_numbered_dir(root, prefix, mode) + # Only lock the current dir when keep is not 0 + if keep != 0: + lock_path = create_cleanup_lock(p) + register_cleanup_lock_removal(lock_path) + except Exception as exc: + e = exc + else: + consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout + # Register a cleanup for program exit + atexit.register( + cleanup_numbered_dir, + root, + prefix, + keep, + consider_lock_dead_if_created_before, + ) + return p + assert e is not None + raise e + + +def resolve_from_str(input: str, rootpath: Path) -> Path: + input = expanduser(input) + input = expandvars(input) + if isabs(input): + return Path(input) + else: + return rootpath.joinpath(input) + + +def fnmatch_ex(pattern: str, path: str | os.PathLike[str]) -> bool: + """A port of FNMatcher from py.path.common which works with PurePath() instances. + + The difference between this algorithm and PurePath.match() is that the + latter matches "**" glob expressions for each part of the path, while + this algorithm uses the whole path instead. + + For example: + "tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" + with this algorithm, but not with PurePath.match(). + + This algorithm was ported to keep backward-compatibility with existing + settings which assume paths match according this logic. + + References: + * https://bugs.python.org/issue29249 + * https://bugs.python.org/issue34731 + """ + path = PurePath(path) + iswin32 = sys.platform.startswith("win") + + if iswin32 and sep not in pattern and posix_sep in pattern: + # Running on Windows, the pattern has no Windows path separators, + # and the pattern has one or more Posix path separators. Replace + # the Posix path separators with the Windows path separator. + pattern = pattern.replace(posix_sep, sep) + + if sep not in pattern: + name = path.name + else: + name = str(path) + if path.is_absolute() and not os.path.isabs(pattern): + pattern = f"*{os.sep}{pattern}" + return fnmatch.fnmatch(name, pattern) + + +def parts(s: str) -> set[str]: + parts = s.split(sep) + return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))} + + +def symlink_or_skip( + src: os.PathLike[str] | str, + dst: os.PathLike[str] | str, + **kwargs: Any, +) -> None: + """Make a symlink, or skip the test in case symlinks are not supported.""" + try: + os.symlink(src, dst, **kwargs) + except OSError as e: + skip(f"symlinks not supported: {e}") + + +class ImportMode(Enum): + """Possible values for `mode` parameter of `import_path`.""" + + prepend = "prepend" + append = "append" + importlib = "importlib" + + +class ImportPathMismatchError(ImportError): + """Raised on import_path() if there is a mismatch of __file__'s. + + This can happen when `import_path` is called multiple times with different filenames that has + the same basename but reside in packages + (for example "/tests1/test_foo.py" and "/tests2/test_foo.py"). + """ + + +def import_path( + path: str | os.PathLike[str], + *, + mode: str | ImportMode = ImportMode.prepend, + root: Path, + consider_namespace_packages: bool, +) -> ModuleType: + """ + Import and return a module from the given path, which can be a file (a module) or + a directory (a package). + + :param path: + Path to the file to import. + + :param mode: + Controls the underlying import mechanism that will be used: + + * ImportMode.prepend: the directory containing the module (or package, taking + `__init__.py` files into account) will be put at the *start* of `sys.path` before + being imported with `importlib.import_module`. + + * ImportMode.append: same as `prepend`, but the directory will be appended + to the end of `sys.path`, if not already in `sys.path`. + + * ImportMode.importlib: uses more fine control mechanisms provided by `importlib` + to import the module, which avoids having to muck with `sys.path` at all. It effectively + allows having same-named test modules in different places. + + :param root: + Used as an anchor when mode == ImportMode.importlib to obtain + a unique name for the module being imported so it can safely be stored + into ``sys.modules``. + + :param consider_namespace_packages: + If True, consider namespace packages when resolving module names. + + :raises ImportPathMismatchError: + If after importing the given `path` and the module `__file__` + are different. Only raised in `prepend` and `append` modes. + """ + path = Path(path) + mode = ImportMode(mode) + + if not path.exists(): + raise ImportError(path) + + if mode is ImportMode.importlib: + # Try to import this module using the standard import mechanisms, but + # without touching sys.path. + try: + pkg_root, module_name = resolve_pkg_root_and_module_name( + path, consider_namespace_packages=consider_namespace_packages + ) + except CouldNotResolvePathError: + pass + else: + # If the given module name is already in sys.modules, do not import it again. + with contextlib.suppress(KeyError): + return sys.modules[module_name] + + mod = _import_module_using_spec( + module_name, path, pkg_root, insert_modules=False + ) + if mod is not None: + return mod + + # Could not import the module with the current sys.path, so we fall back + # to importing the file as a single module, not being a part of a package. + module_name = module_name_from_path(path, root) + with contextlib.suppress(KeyError): + return sys.modules[module_name] + + mod = _import_module_using_spec( + module_name, path, path.parent, insert_modules=True + ) + if mod is None: + raise ImportError(f"Can't find module {module_name} at location {path}") + return mod + + try: + pkg_root, module_name = resolve_pkg_root_and_module_name( + path, consider_namespace_packages=consider_namespace_packages + ) + except CouldNotResolvePathError: + pkg_root, module_name = path.parent, path.stem + + # Change sys.path permanently: restoring it at the end of this function would cause surprising + # problems because of delayed imports: for example, a conftest.py file imported by this function + # might have local imports, which would fail at runtime if we restored sys.path. + if mode is ImportMode.append: + if str(pkg_root) not in sys.path: + sys.path.append(str(pkg_root)) + elif mode is ImportMode.prepend: + if str(pkg_root) != sys.path[0]: + sys.path.insert(0, str(pkg_root)) + else: + assert_never(mode) + + importlib.import_module(module_name) + + mod = sys.modules[module_name] + if path.name == "__init__.py": + return mod + + ignore = os.environ.get("PY_IGNORE_IMPORTMISMATCH", "") + if ignore != "1": + module_file = mod.__file__ + if module_file is None: + raise ImportPathMismatchError(module_name, module_file, path) + + if module_file.endswith((".pyc", ".pyo")): + module_file = module_file[:-1] + if module_file.endswith(os.sep + "__init__.py"): + module_file = module_file[: -(len(os.sep + "__init__.py"))] + + try: + is_same = _is_same(str(path), module_file) + except FileNotFoundError: + is_same = False + + if not is_same: + raise ImportPathMismatchError(module_name, module_file, path) + + return mod + + +def _import_module_using_spec( + module_name: str, module_path: Path, module_location: Path, *, insert_modules: bool +) -> ModuleType | None: + """ + Tries to import a module by its canonical name, path, and its parent location. + + :param module_name: + The expected module name, will become the key of `sys.modules`. + + :param module_path: + The file path of the module, for example `/foo/bar/test_demo.py`. + If module is a package, pass the path to the `__init__.py` of the package. + If module is a namespace package, pass directory path. + + :param module_location: + The parent location of the module. + If module is a package, pass the directory containing the `__init__.py` file. + + :param insert_modules: + If True, will call `insert_missing_modules` to create empty intermediate modules + with made-up module names (when importing test files not reachable from `sys.path`). + + Example 1 of parent_module_*: + + module_name: "a.b.c.demo" + module_path: Path("a/b/c/demo.py") + module_location: Path("a/b/c/") + if "a.b.c" is package ("a/b/c/__init__.py" exists), then + parent_module_name: "a.b.c" + parent_module_path: Path("a/b/c/__init__.py") + parent_module_location: Path("a/b/c/") + else: + parent_module_name: "a.b.c" + parent_module_path: Path("a/b/c") + parent_module_location: Path("a/b/") + + Example 2 of parent_module_*: + + module_name: "a.b.c" + module_path: Path("a/b/c/__init__.py") + module_location: Path("a/b/c/") + if "a.b" is package ("a/b/__init__.py" exists), then + parent_module_name: "a.b" + parent_module_path: Path("a/b/__init__.py") + parent_module_location: Path("a/b/") + else: + parent_module_name: "a.b" + parent_module_path: Path("a/b/") + parent_module_location: Path("a/") + """ + # Attempt to import the parent module, seems is our responsibility: + # https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1308-L1311 + parent_module_name, _, name = module_name.rpartition(".") + parent_module: ModuleType | None = None + if parent_module_name: + parent_module = sys.modules.get(parent_module_name) + # If the parent_module lacks the `__path__` attribute, AttributeError when finding a submodule's spec, + # requiring re-import according to the path. + need_reimport = not hasattr(parent_module, "__path__") + if parent_module is None or need_reimport: + # Get parent_location based on location, get parent_path based on path. + if module_path.name == "__init__.py": + # If the current module is in a package, + # need to leave the package first and then enter the parent module. + parent_module_path = module_path.parent.parent + else: + parent_module_path = module_path.parent + + if (parent_module_path / "__init__.py").is_file(): + # If the parent module is a package, loading by __init__.py file. + parent_module_path = parent_module_path / "__init__.py" + + parent_module = _import_module_using_spec( + parent_module_name, + parent_module_path, + parent_module_path.parent, + insert_modules=insert_modules, + ) + + # Checking with sys.meta_path first in case one of its hooks can import this module, + # such as our own assertion-rewrite hook. + for meta_importer in sys.meta_path: + module_name_of_meta = getattr(meta_importer.__class__, "__module__", "") + if module_name_of_meta == "_pytest.assertion.rewrite" and module_path.is_file(): + # Import modules in subdirectories by module_path + # to ensure assertion rewrites are not missed (#12659). + find_spec_path = [str(module_location), str(module_path)] + else: + find_spec_path = [str(module_location)] + + spec = meta_importer.find_spec(module_name, find_spec_path) + + if spec_matches_module_path(spec, module_path): + break + else: + loader = None + if module_path.is_dir(): + # The `spec_from_file_location` matches a loader based on the file extension by default. + # For a namespace package, need to manually specify a loader. + loader = NamespaceLoader(name, module_path, PathFinder()) # type: ignore[arg-type] + + spec = importlib.util.spec_from_file_location( + module_name, str(module_path), loader=loader + ) + + if spec_matches_module_path(spec, module_path): + assert spec is not None + # Find spec and import this module. + mod = importlib.util.module_from_spec(spec) + sys.modules[module_name] = mod + spec.loader.exec_module(mod) # type: ignore[union-attr] + + # Set this module as an attribute of the parent module (#12194). + if parent_module is not None: + setattr(parent_module, name, mod) + + if insert_modules: + insert_missing_modules(sys.modules, module_name) + return mod + + return None + + +def spec_matches_module_path(module_spec: ModuleSpec | None, module_path: Path) -> bool: + """Return true if the given ModuleSpec can be used to import the given module path.""" + if module_spec is None: + return False + + if module_spec.origin: + return Path(module_spec.origin) == module_path + + # Compare the path with the `module_spec.submodule_Search_Locations` in case + # the module is part of a namespace package. + # https://docs.python.org/3/library/importlib.html#importlib.machinery.ModuleSpec.submodule_search_locations + if module_spec.submodule_search_locations: # can be None. + for path in module_spec.submodule_search_locations: + if Path(path) == module_path: + return True + + return False + + +# Implement a special _is_same function on Windows which returns True if the two filenames +# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). +if sys.platform.startswith("win"): + + def _is_same(f1: str, f2: str) -> bool: + return Path(f1) == Path(f2) or os.path.samefile(f1, f2) + +else: + + def _is_same(f1: str, f2: str) -> bool: + return os.path.samefile(f1, f2) + + +def module_name_from_path(path: Path, root: Path) -> str: + """ + Return a dotted module name based on the given path, anchored on root. + + For example: path="projects/src/tests/test_foo.py" and root="/projects", the + resulting module name will be "src.tests.test_foo". + """ + path = path.with_suffix("") + try: + relative_path = path.relative_to(root) + except ValueError: + # If we can't get a relative path to root, use the full path, except + # for the first part ("d:\\" or "/" depending on the platform, for example). + path_parts = path.parts[1:] + else: + # Use the parts for the relative path to the root path. + path_parts = relative_path.parts + + # Module name for packages do not contain the __init__ file, unless + # the `__init__.py` file is at the root. + if len(path_parts) >= 2 and path_parts[-1] == "__init__": + path_parts = path_parts[:-1] + + # Module names cannot contain ".", normalize them to "_". This prevents + # a directory having a "." in the name (".env.310" for example) causing extra intermediate modules. + # Also, important to replace "." at the start of paths, as those are considered relative imports. + path_parts = tuple(x.replace(".", "_") for x in path_parts) + + return ".".join(path_parts) + + +def insert_missing_modules(modules: dict[str, ModuleType], module_name: str) -> None: + """ + Used by ``import_path`` to create intermediate modules when using mode=importlib. + + When we want to import a module as "src.tests.test_foo" for example, we need + to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo", + otherwise "src.tests.test_foo" is not importable by ``__import__``. + """ + module_parts = module_name.split(".") + while module_name: + parent_module_name, _, child_name = module_name.rpartition(".") + if parent_module_name: + parent_module = modules.get(parent_module_name) + if parent_module is None: + try: + # If sys.meta_path is empty, calling import_module will issue + # a warning and raise ModuleNotFoundError. To avoid the + # warning, we check sys.meta_path explicitly and raise the error + # ourselves to fall back to creating a dummy module. + if not sys.meta_path: + raise ModuleNotFoundError + parent_module = importlib.import_module(parent_module_name) + except ModuleNotFoundError: + parent_module = ModuleType( + module_name, + doc="Empty module created by pytest's importmode=importlib.", + ) + modules[parent_module_name] = parent_module + + # Add child attribute to the parent that can reference the child + # modules. + if not hasattr(parent_module, child_name): + setattr(parent_module, child_name, modules[module_name]) + + module_parts.pop(-1) + module_name = ".".join(module_parts) + + +def resolve_package_path(path: Path) -> Path | None: + """Return the Python package path by looking for the last + directory upwards which still contains an __init__.py. + + Returns None if it cannot be determined. + """ + result = None + for parent in itertools.chain((path,), path.parents): + if parent.is_dir(): + if not (parent / "__init__.py").is_file(): + break + if not parent.name.isidentifier(): + break + result = parent + return result + + +def resolve_pkg_root_and_module_name( + path: Path, *, consider_namespace_packages: bool = False +) -> tuple[Path, str]: + """ + Return the path to the directory of the root package that contains the + given Python file, and its module name: + + src/ + app/ + __init__.py + core/ + __init__.py + models.py + + Passing the full path to `models.py` will yield Path("src") and "app.core.models". + + If consider_namespace_packages is True, then we additionally check upwards in the hierarchy + for namespace packages: + + https://packaging.python.org/en/latest/guides/packaging-namespace-packages + + Raises CouldNotResolvePathError if the given path does not belong to a package (missing any __init__.py files). + """ + pkg_root: Path | None = None + pkg_path = resolve_package_path(path) + if pkg_path is not None: + pkg_root = pkg_path.parent + if consider_namespace_packages: + start = pkg_root if pkg_root is not None else path.parent + for candidate in (start, *start.parents): + module_name = compute_module_name(candidate, path) + if module_name and is_importable(module_name, path): + # Point the pkg_root to the root of the namespace package. + pkg_root = candidate + break + + if pkg_root is not None: + module_name = compute_module_name(pkg_root, path) + if module_name: + return pkg_root, module_name + + raise CouldNotResolvePathError(f"Could not resolve for {path}") + + +def is_importable(module_name: str, module_path: Path) -> bool: + """ + Return if the given module path could be imported normally by Python, akin to the user + entering the REPL and importing the corresponding module name directly, and corresponds + to the module_path specified. + + :param module_name: + Full module name that we want to check if is importable. + For example, "app.models". + + :param module_path: + Full path to the python module/package we want to check if is importable. + For example, "/projects/src/app/models.py". + """ + try: + # Note this is different from what we do in ``_import_module_using_spec``, where we explicitly search through + # sys.meta_path to be able to pass the path of the module that we want to import (``meta_importer.find_spec``). + # Using importlib.util.find_spec() is different, it gives the same results as trying to import + # the module normally in the REPL. + spec = importlib.util.find_spec(module_name) + except (ImportError, ValueError, ImportWarning): + return False + else: + return spec_matches_module_path(spec, module_path) + + +def compute_module_name(root: Path, module_path: Path) -> str | None: + """Compute a module name based on a path and a root anchor.""" + try: + path_without_suffix = module_path.with_suffix("") + except ValueError: + # Empty paths (such as Path.cwd()) might break meta_path hooks (like our own assertion rewriter). + return None + + try: + relative = path_without_suffix.relative_to(root) + except ValueError: # pragma: no cover + return None + names = list(relative.parts) + if not names: + return None + if names[-1] == "__init__": + names.pop() + return ".".join(names) + + +class CouldNotResolvePathError(Exception): + """Custom exception raised by resolve_pkg_root_and_module_name.""" + + +def scandir( + path: str | os.PathLike[str], + sort_key: Callable[[os.DirEntry[str]], object] = lambda entry: entry.name, +) -> list[os.DirEntry[str]]: + """Scan a directory recursively, in breadth-first order. + + The returned entries are sorted according to the given key. + The default is to sort by name. + If the directory does not exist, return an empty list. + """ + entries = [] + # Attempt to create a scandir iterator for the given path. + try: + scandir_iter = os.scandir(path) + except FileNotFoundError: + # If the directory does not exist, return an empty list. + return [] + # Use the scandir iterator in a context manager to ensure it is properly closed. + with scandir_iter as s: + for entry in s: + try: + entry.is_file() + except OSError as err: + if _ignore_error(err): + continue + # Reraise non-ignorable errors to avoid hiding issues. + raise + entries.append(entry) + entries.sort(key=sort_key) # type: ignore[arg-type] + return entries + + +def visit( + path: str | os.PathLike[str], recurse: Callable[[os.DirEntry[str]], bool] +) -> Iterator[os.DirEntry[str]]: + """Walk a directory recursively, in breadth-first order. + + The `recurse` predicate determines whether a directory is recursed. + + Entries at each directory level are sorted. + """ + entries = scandir(path) + yield from entries + for entry in entries: + if entry.is_dir() and recurse(entry): + yield from visit(entry.path, recurse) + + +def absolutepath(path: str | os.PathLike[str]) -> Path: + """Convert a path to an absolute path using os.path.abspath. + + Prefer this over Path.resolve() (see #6523). + Prefer this over Path.absolute() (not public, doesn't normalize). + """ + return Path(os.path.abspath(path)) + + +def commonpath(path1: Path, path2: Path) -> Path | None: + """Return the common part shared with the other path, or None if there is + no common part. + + If one path is relative and one is absolute, returns None. + """ + try: + return Path(os.path.commonpath((str(path1), str(path2)))) + except ValueError: + return None + + +def bestrelpath(directory: Path, dest: Path) -> str: + """Return a string which is a relative path from directory to dest such + that directory/bestrelpath == dest. + + The paths must be either both absolute or both relative. + + If no such path can be determined, returns dest. + """ + assert isinstance(directory, Path) + assert isinstance(dest, Path) + if dest == directory: + return os.curdir + # Find the longest common directory. + base = commonpath(directory, dest) + # Can be the case on Windows for two absolute paths on different drives. + # Can be the case for two relative paths without common prefix. + # Can be the case for a relative path and an absolute path. + if not base: + return str(dest) + reldirectory = directory.relative_to(base) + reldest = dest.relative_to(base) + return os.path.join( + # Back from directory to base. + *([os.pardir] * len(reldirectory.parts)), + # Forward from base to dest. + *reldest.parts, + ) + + +def safe_exists(p: Path) -> bool: + """Like Path.exists(), but account for input arguments that might be too long (#11394).""" + try: + return p.exists() + except (ValueError, OSError): + # ValueError: stat: path too long for Windows + # OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect + return False + + +def samefile_nofollow(p1: Path, p2: Path) -> bool: + """Test whether two paths reference the same actual file or directory. + + Unlike Path.samefile(), does not resolve symlinks. + """ + return os.path.samestat(p1.lstat(), p2.lstat()) diff --git a/.venv/lib/python3.12/site-packages/_pytest/py.typed b/.venv/lib/python3.12/site-packages/_pytest/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/_pytest/pytester.py b/.venv/lib/python3.12/site-packages/_pytest/pytester.py new file mode 100644 index 0000000..1cd5f05 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/pytester.py @@ -0,0 +1,1791 @@ +# mypy: allow-untyped-defs +"""(Disabled by default) support for testing pytest and pytest plugins. + +PYTEST_DONT_REWRITE +""" + +from __future__ import annotations + +import collections.abc +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Sequence +import contextlib +from fnmatch import fnmatch +import gc +import importlib +from io import StringIO +import locale +import os +from pathlib import Path +import platform +import re +import shutil +import subprocess +import sys +import traceback +from typing import Any +from typing import Final +from typing import final +from typing import IO +from typing import Literal +from typing import overload +from typing import TextIO +from typing import TYPE_CHECKING +from weakref import WeakKeyDictionary + +from iniconfig import IniConfig +from iniconfig import SectionWrapper + +from _pytest import timing +from _pytest._code import Source +from _pytest.capture import _get_multicapture +from _pytest.compat import NOTSET +from _pytest.compat import NotSetType +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config import main +from _pytest.config import PytestPluginManager +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import importorskip +from _pytest.outcomes import skip +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import make_numbered_dir +from _pytest.reports import CollectReport +from _pytest.reports import TestReport +from _pytest.tmpdir import TempPathFactory +from _pytest.warning_types import PytestFDWarning + + +if TYPE_CHECKING: + import pexpect + + +pytest_plugins = ["pytester_assertions"] + + +IGNORE_PAM = [ # filenames added when obtaining details about the current user + "/var/lib/sss/mc/passwd" +] + + +def pytest_addoption(parser: Parser) -> None: + parser.addoption( + "--lsof", + action="store_true", + dest="lsof", + default=False, + help="Run FD checks if lsof is available", + ) + + parser.addoption( + "--runpytest", + default="inprocess", + dest="runpytest", + choices=("inprocess", "subprocess"), + help=( + "Run pytest sub runs in tests using an 'inprocess' " + "or 'subprocess' (python -m main) method" + ), + ) + + parser.addini( + "pytester_example_dir", help="Directory to take the pytester example files from" + ) + + +def pytest_configure(config: Config) -> None: + if config.getvalue("lsof"): + checker = LsofFdLeakChecker() + if checker.matching_platform(): + config.pluginmanager.register(checker) + + config.addinivalue_line( + "markers", + "pytester_example_path(*path_segments): join the given path " + "segments to `pytester_example_dir` for this test.", + ) + + +class LsofFdLeakChecker: + def get_open_files(self) -> list[tuple[str, str]]: + if sys.version_info >= (3, 11): + # New in Python 3.11, ignores utf-8 mode + encoding = locale.getencoding() + else: + encoding = locale.getpreferredencoding(False) + out = subprocess.run( + ("lsof", "-Ffn0", "-p", str(os.getpid())), + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + check=True, + text=True, + encoding=encoding, + ).stdout + + def isopen(line: str) -> bool: + return line.startswith("f") and ( + "deleted" not in line + and "mem" not in line + and "txt" not in line + and "cwd" not in line + ) + + open_files = [] + + for line in out.split("\n"): + if isopen(line): + fields = line.split("\0") + fd = fields[0][1:] + filename = fields[1][1:] + if filename in IGNORE_PAM: + continue + if filename.startswith("/"): + open_files.append((fd, filename)) + + return open_files + + def matching_platform(self) -> bool: + try: + subprocess.run(("lsof", "-v"), check=True) + except (OSError, subprocess.CalledProcessError): + return False + else: + return True + + @hookimpl(wrapper=True, tryfirst=True) + def pytest_runtest_protocol(self, item: Item) -> Generator[None, object, object]: + lines1 = self.get_open_files() + try: + return (yield) + finally: + if hasattr(sys, "pypy_version_info"): + gc.collect() + lines2 = self.get_open_files() + + new_fds = {t[0] for t in lines2} - {t[0] for t in lines1} + leaked_files = [t for t in lines2 if t[0] in new_fds] + if leaked_files: + error = [ + f"***** {len(leaked_files)} FD leakage detected", + *(str(f) for f in leaked_files), + "*** Before:", + *(str(f) for f in lines1), + "*** After:", + *(str(f) for f in lines2), + f"***** {len(leaked_files)} FD leakage detected", + "*** function {}:{}: {} ".format(*item.location), + "See issue #2366", + ] + item.warn(PytestFDWarning("\n".join(error))) + + +# used at least by pytest-xdist plugin + + +@fixture +def _pytest(request: FixtureRequest) -> PytestArg: + """Return a helper which offers a gethookrecorder(hook) method which + returns a HookRecorder instance which helps to make assertions about called + hooks.""" + return PytestArg(request) + + +class PytestArg: + def __init__(self, request: FixtureRequest) -> None: + self._request = request + + def gethookrecorder(self, hook) -> HookRecorder: + hookrecorder = HookRecorder(hook._pm) + self._request.addfinalizer(hookrecorder.finish_recording) + return hookrecorder + + +def get_public_names(values: Iterable[str]) -> list[str]: + """Only return names from iterator values without a leading underscore.""" + return [x for x in values if x[0] != "_"] + + +@final +class RecordedHookCall: + """A recorded call to a hook. + + The arguments to the hook call are set as attributes. + For example: + + .. code-block:: python + + calls = hook_recorder.getcalls("pytest_runtest_setup") + # Suppose pytest_runtest_setup was called once with `item=an_item`. + assert calls[0].item is an_item + """ + + def __init__(self, name: str, kwargs) -> None: + self.__dict__.update(kwargs) + self._name = name + + def __repr__(self) -> str: + d = self.__dict__.copy() + del d["_name"] + return f"" + + if TYPE_CHECKING: + # The class has undetermined attributes, this tells mypy about it. + def __getattr__(self, key: str): ... + + +@final +class HookRecorder: + """Record all hooks called in a plugin manager. + + Hook recorders are created by :class:`Pytester`. + + This wraps all the hook calls in the plugin manager, recording each call + before propagating the normal calls. + """ + + def __init__( + self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False + ) -> None: + check_ispytest(_ispytest) + + self._pluginmanager = pluginmanager + self.calls: list[RecordedHookCall] = [] + self.ret: int | ExitCode | None = None + + def before(hook_name: str, hook_impls, kwargs) -> None: + self.calls.append(RecordedHookCall(hook_name, kwargs)) + + def after(outcome, hook_name: str, hook_impls, kwargs) -> None: + pass + + self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) + + def finish_recording(self) -> None: + self._undo_wrapping() + + def getcalls(self, names: str | Iterable[str]) -> list[RecordedHookCall]: + """Get all recorded calls to hooks with the given names (or name).""" + if isinstance(names, str): + names = names.split() + return [call for call in self.calls if call._name in names] + + def assert_contains(self, entries: Sequence[tuple[str, str]]) -> None: + __tracebackhide__ = True + i = 0 + entries = list(entries) + # Since Python 3.13, f_locals is not a dict, but eval requires a dict. + backlocals = dict(sys._getframe(1).f_locals) + while entries: + name, check = entries.pop(0) + for ind, call in enumerate(self.calls[i:]): + if call._name == name: + print("NAMEMATCH", name, call) + if eval(check, backlocals, call.__dict__): + print("CHECKERMATCH", repr(check), "->", call) + else: + print("NOCHECKERMATCH", repr(check), "-", call) + continue + i += ind + 1 + break + print("NONAMEMATCH", name, "with", call) + else: + fail(f"could not find {name!r} check {check!r}") + + def popcall(self, name: str) -> RecordedHookCall: + __tracebackhide__ = True + for i, call in enumerate(self.calls): + if call._name == name: + del self.calls[i] + return call + lines = [f"could not find call {name!r}, in:"] + lines.extend([f" {x}" for x in self.calls]) + fail("\n".join(lines)) + + def getcall(self, name: str) -> RecordedHookCall: + values = self.getcalls(name) + assert len(values) == 1, (name, values) + return values[0] + + # functionality for test reports + + @overload + def getreports( + self, + names: Literal["pytest_collectreport"], + ) -> Sequence[CollectReport]: ... + + @overload + def getreports( + self, + names: Literal["pytest_runtest_logreport"], + ) -> Sequence[TestReport]: ... + + @overload + def getreports( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: ... + + def getreports( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: + return [x.report for x in self.getcalls(names)] + + def matchreport( + self, + inamepart: str = "", + names: str | Iterable[str] = ( + "pytest_runtest_logreport", + "pytest_collectreport", + ), + when: str | None = None, + ) -> CollectReport | TestReport: + """Return a testreport whose dotted import path matches.""" + values = [] + for rep in self.getreports(names=names): + if not when and rep.when != "call" and rep.passed: + # setup/teardown passing reports - let's ignore those + continue + if when and rep.when != when: + continue + if not inamepart or inamepart in rep.nodeid.split("::"): + values.append(rep) + if not values: + raise ValueError( + f"could not find test report matching {inamepart!r}: " + "no test reports at all!" + ) + if len(values) > 1: + raise ValueError( + f"found 2 or more testreports matching {inamepart!r}: {values}" + ) + return values[0] + + @overload + def getfailures( + self, + names: Literal["pytest_collectreport"], + ) -> Sequence[CollectReport]: ... + + @overload + def getfailures( + self, + names: Literal["pytest_runtest_logreport"], + ) -> Sequence[TestReport]: ... + + @overload + def getfailures( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: ... + + def getfailures( + self, + names: str | Iterable[str] = ( + "pytest_collectreport", + "pytest_runtest_logreport", + ), + ) -> Sequence[CollectReport | TestReport]: + return [rep for rep in self.getreports(names) if rep.failed] + + def getfailedcollections(self) -> Sequence[CollectReport]: + return self.getfailures("pytest_collectreport") + + def listoutcomes( + self, + ) -> tuple[ + Sequence[TestReport], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], + ]: + passed = [] + skipped = [] + failed = [] + for rep in self.getreports( + ("pytest_collectreport", "pytest_runtest_logreport") + ): + if rep.passed: + if rep.when == "call": + assert isinstance(rep, TestReport) + passed.append(rep) + elif rep.skipped: + skipped.append(rep) + else: + assert rep.failed, f"Unexpected outcome: {rep!r}" + failed.append(rep) + return passed, skipped, failed + + def countoutcomes(self) -> list[int]: + return [len(x) for x in self.listoutcomes()] + + def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: + __tracebackhide__ = True + from _pytest.pytester_assertions import assertoutcome + + outcomes = self.listoutcomes() + assertoutcome( + outcomes, + passed=passed, + skipped=skipped, + failed=failed, + ) + + def clear(self) -> None: + self.calls[:] = [] + + +@fixture +def linecomp() -> LineComp: + """A :class: `LineComp` instance for checking that an input linearly + contains a sequence of strings.""" + return LineComp() + + +@fixture(name="LineMatcher") +def LineMatcher_fixture(request: FixtureRequest) -> type[LineMatcher]: + """A reference to the :class: `LineMatcher`. + + This is instantiable with a list of lines (without their trailing newlines). + This is useful for testing large texts, such as the output of commands. + """ + return LineMatcher + + +@fixture +def pytester( + request: FixtureRequest, tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch +) -> Pytester: + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to ``path`` and environment variables during initialization. + + It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path` + fixture but provides methods which aid in testing pytest itself. + """ + return Pytester(request, tmp_path_factory, monkeypatch, _ispytest=True) + + +@fixture +def _sys_snapshot() -> Generator[None]: + snappaths = SysPathsSnapshot() + snapmods = SysModulesSnapshot() + yield + snapmods.restore() + snappaths.restore() + + +@fixture +def _config_for_test() -> Generator[Config]: + from _pytest.config import get_config + + config = get_config() + yield config + config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. + + +# Regex to match the session duration string in the summary: "74.34s". +rex_session_duration = re.compile(r"\d+\.\d\ds") +# Regex to match all the counts and phrases in the summary line: "34 passed, 111 skipped". +rex_outcome = re.compile(r"(\d+) (\w+)") + + +@final +class RunResult: + """The result of running a command from :class:`~pytest.Pytester`.""" + + def __init__( + self, + ret: int | ExitCode, + outlines: list[str], + errlines: list[str], + duration: float, + ) -> None: + try: + self.ret: int | ExitCode = ExitCode(ret) + """The return value.""" + except ValueError: + self.ret = ret + self.outlines = outlines + """List of lines captured from stdout.""" + self.errlines = errlines + """List of lines captured from stderr.""" + self.stdout = LineMatcher(outlines) + """:class:`~pytest.LineMatcher` of stdout. + + Use e.g. :func:`str(stdout) ` to reconstruct stdout, or the commonly used + :func:`stdout.fnmatch_lines() ` method. + """ + self.stderr = LineMatcher(errlines) + """:class:`~pytest.LineMatcher` of stderr.""" + self.duration = duration + """Duration in seconds.""" + + def __repr__(self) -> str: + return ( + f"" + ) + + def parseoutcomes(self) -> dict[str, int]: + """Return a dictionary of outcome noun -> count from parsing the terminal + output that the test process produced. + + The returned nouns will always be in plural form:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + return self.parse_summary_nouns(self.outlines) + + @classmethod + def parse_summary_nouns(cls, lines) -> dict[str, int]: + """Extract the nouns from a pytest terminal summary line. + + It always returns the plural noun for consistency:: + + ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== + + Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. + """ + for line in reversed(lines): + if rex_session_duration.search(line): + outcomes = rex_outcome.findall(line) + ret = {noun: int(count) for (count, noun) in outcomes} + break + else: + raise ValueError("Pytest terminal summary report not found") + + to_plural = { + "warning": "warnings", + "error": "errors", + } + return {to_plural.get(k, k): v for k, v in ret.items()} + + def assert_outcomes( + self, + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + warnings: int | None = None, + deselected: int | None = None, + ) -> None: + """ + Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run. + + ``warnings`` and ``deselected`` are only checked if not None. + """ + __tracebackhide__ = True + from _pytest.pytester_assertions import assert_outcomes + + outcomes = self.parseoutcomes() + assert_outcomes( + outcomes, + passed=passed, + skipped=skipped, + failed=failed, + errors=errors, + xpassed=xpassed, + xfailed=xfailed, + warnings=warnings, + deselected=deselected, + ) + + +class SysModulesSnapshot: + def __init__(self, preserve: Callable[[str], bool] | None = None) -> None: + self.__preserve = preserve + self.__saved = dict(sys.modules) + + def restore(self) -> None: + if self.__preserve: + self.__saved.update( + (k, m) for k, m in sys.modules.items() if self.__preserve(k) + ) + sys.modules.clear() + sys.modules.update(self.__saved) + + +class SysPathsSnapshot: + def __init__(self) -> None: + self.__saved = list(sys.path), list(sys.meta_path) + + def restore(self) -> None: + sys.path[:], sys.meta_path[:] = self.__saved + + +@final +class Pytester: + """ + Facilities to write tests/configuration files, execute pytest in isolation, and match + against expected output, perfect for black-box testing of pytest plugins. + + It attempts to isolate the test run from external factors as much as possible, modifying + the current working directory to :attr:`path` and environment variables during initialization. + """ + + __test__ = False + + CLOSE_STDIN: Final = NOTSET + + class TimeoutExpired(Exception): + pass + + def __init__( + self, + request: FixtureRequest, + tmp_path_factory: TempPathFactory, + monkeypatch: MonkeyPatch, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._request = request + self._mod_collections: WeakKeyDictionary[Collector, list[Item | Collector]] = ( + WeakKeyDictionary() + ) + if request.function: + name: str = request.function.__name__ + else: + name = request.node.name + self._name = name + self._path: Path = tmp_path_factory.mktemp(name, numbered=True) + #: A list of plugins to use with :py:meth:`parseconfig` and + #: :py:meth:`runpytest`. Initially this is an empty list but plugins can + #: be added to the list. + #: + #: When running in subprocess mode, specify plugins by name (str) - adding + #: plugin objects directly is not supported. + self.plugins: list[str | _PluggyPlugin] = [] + self._sys_path_snapshot = SysPathsSnapshot() + self._sys_modules_snapshot = self.__take_sys_modules_snapshot() + self._request.addfinalizer(self._finalize) + self._method = self._request.config.getoption("--runpytest") + self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) + + self._monkeypatch = mp = monkeypatch + self.chdir() + mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) + # Ensure no unexpected caching via tox. + mp.delenv("TOX_ENV_DIR", raising=False) + # Discard outer pytest options. + mp.delenv("PYTEST_ADDOPTS", raising=False) + # Ensure no user config is used. + tmphome = str(self.path) + mp.setenv("HOME", tmphome) + mp.setenv("USERPROFILE", tmphome) + # Do not use colors for inner runs by default. + mp.setenv("PY_COLORS", "0") + + @property + def path(self) -> Path: + """Temporary directory path used to create files/run tests from, etc.""" + return self._path + + def __repr__(self) -> str: + return f"" + + def _finalize(self) -> None: + """ + Clean up global state artifacts. + + Some methods modify the global interpreter state and this tries to + clean this up. It does not remove the temporary directory however so + it can be looked at after the test run has finished. + """ + self._sys_modules_snapshot.restore() + self._sys_path_snapshot.restore() + + def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: + # Some zope modules used by twisted-related tests keep internal state + # and can't be deleted; we had some trouble in the past with + # `zope.interface` for example. + # + # Preserve readline due to https://bugs.python.org/issue41033. + # pexpect issues a SIGWINCH. + def preserve_module(name): + return name.startswith(("zope", "readline")) + + return SysModulesSnapshot(preserve=preserve_module) + + def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: + """Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`.""" + pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined] + self._request.addfinalizer(reprec.finish_recording) + return reprec + + def chdir(self) -> None: + """Cd into the temporary directory. + + This is done automatically upon instantiation. + """ + self._monkeypatch.chdir(self.path) + + def _makefile( + self, + ext: str, + lines: Sequence[Any | bytes], + files: dict[str, str], + encoding: str = "utf-8", + ) -> Path: + items = list(files.items()) + + if ext is None: + raise TypeError("ext must not be None") + + if ext and not ext.startswith("."): + raise ValueError( + f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" + ) + + def to_text(s: Any | bytes) -> str: + return s.decode(encoding) if isinstance(s, bytes) else str(s) + + if lines: + source = "\n".join(to_text(x) for x in lines) + basename = self._name + items.insert(0, (basename, source)) + + ret = None + for basename, value in items: + p = self.path.joinpath(basename).with_suffix(ext) + p.parent.mkdir(parents=True, exist_ok=True) + source_ = Source(value) + source = "\n".join(to_text(line) for line in source_.lines) + p.write_text(source.strip(), encoding=encoding) + if ret is None: + ret = p + assert ret is not None + return ret + + def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: + r"""Create new text file(s) in the test directory. + + :param ext: + The extension the file(s) should use, including the dot, e.g. `.py`. + :param args: + All args are treated as strings and joined using newlines. + The result is written as contents to the file. The name of the + file is based on the test function requesting this fixture. + :param kwargs: + Each keyword is the name of a file, while the value of it will + be written as contents of the file. + :returns: + The first created file. + + Examples: + + .. code-block:: python + + pytester.makefile(".txt", "line1", "line2") + + pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") + + To create binary files, use :meth:`pathlib.Path.write_bytes` directly: + + .. code-block:: python + + filename = pytester.path.joinpath("foo.bin") + filename.write_bytes(b"...") + """ + return self._makefile(ext, args, kwargs) + + def makeconftest(self, source: str) -> Path: + """Write a conftest.py file. + + :param source: The contents. + :returns: The conftest.py file. + """ + return self.makepyfile(conftest=source) + + def makeini(self, source: str) -> Path: + """Write a tox.ini file. + + :param source: The contents. + :returns: The tox.ini file. + """ + return self.makefile(".ini", tox=source) + + def maketoml(self, source: str) -> Path: + """Write a pytest.toml file. + + :param source: The contents. + :returns: The pytest.toml file. + + .. versionadded:: 9.0 + """ + return self.makefile(".toml", pytest=source) + + def getinicfg(self, source: str) -> SectionWrapper: + """Return the pytest section from the tox.ini config file.""" + p = self.makeini(source) + return IniConfig(str(p))["pytest"] + + def makepyprojecttoml(self, source: str) -> Path: + """Write a pyproject.toml file. + + :param source: The contents. + :returns: The pyproject.ini file. + + .. versionadded:: 6.0 + """ + return self.makefile(".toml", pyproject=source) + + def makepyfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .py extension. + + Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.py. + pytester.makepyfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.makepyfile(custom="foobar") + # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. + + """ + return self._makefile(".py", args, kwargs) + + def maketxtfile(self, *args, **kwargs) -> Path: + r"""Shortcut for .makefile() with a .txt extension. + + Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting + existing files. + + Examples: + + .. code-block:: python + + def test_something(pytester): + # Initial file is created test_something.txt. + pytester.maketxtfile("foobar") + # To create multiple files, pass kwargs accordingly. + pytester.maketxtfile(custom="foobar") + # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. + + """ + return self._makefile(".txt", args, kwargs) + + def syspathinsert(self, path: str | os.PathLike[str] | None = None) -> None: + """Prepend a directory to sys.path, defaults to :attr:`path`. + + This is undone automatically when this object dies at the end of each + test. + + :param path: + The path. + """ + if path is None: + path = self.path + + self._monkeypatch.syspath_prepend(str(path)) + + def mkdir(self, name: str | os.PathLike[str]) -> Path: + """Create a new (sub)directory. + + :param name: + The name of the directory, relative to the pytester path. + :returns: + The created directory. + :rtype: pathlib.Path + """ + p = self.path / name + p.mkdir() + return p + + def mkpydir(self, name: str | os.PathLike[str]) -> Path: + """Create a new python package. + + This creates a (sub)directory with an empty ``__init__.py`` file so it + gets recognised as a Python package. + """ + p = self.path / name + p.mkdir() + p.joinpath("__init__.py").touch() + return p + + def copy_example(self, name: str | None = None) -> Path: + """Copy file from project's directory into the testdir. + + :param name: + The name of the file to copy. + :return: + Path to the copied directory (inside ``self.path``). + :rtype: pathlib.Path + """ + example_dir_ = self._request.config.getini("pytester_example_dir") + if example_dir_ is None: + raise ValueError("pytester_example_dir is unset, can't copy examples") + example_dir: Path = self._request.config.rootpath / example_dir_ + + for extra_element in self._request.node.iter_markers("pytester_example_path"): + assert extra_element.args + example_dir = example_dir.joinpath(*extra_element.args) + + if name is None: + func_name = self._name + maybe_dir = example_dir / func_name + maybe_file = example_dir / (func_name + ".py") + + if maybe_dir.is_dir(): + example_path = maybe_dir + elif maybe_file.is_file(): + example_path = maybe_file + else: + raise LookupError( + f"{func_name} can't be found as module or package in {example_dir}" + ) + else: + example_path = example_dir.joinpath(name) + + if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): + shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) + return self.path + elif example_path.is_file(): + result = self.path.joinpath(example_path.name) + shutil.copy(example_path, result) + return result + else: + raise LookupError( + f'example "{example_path}" is not found as a file or directory' + ) + + def getnode(self, config: Config, arg: str | os.PathLike[str]) -> Collector | Item: + """Get the collection node of a file. + + :param config: + A pytest config. + See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. + :param arg: + Path to the file. + :returns: + The node. + """ + session = Session.from_config(config) + assert "::" not in str(arg) + p = Path(os.path.abspath(arg)) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([str(p)], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def getpathnode(self, path: str | os.PathLike[str]) -> Collector | Item: + """Return the collection node of a file. + + This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to + create the (configured) pytest Config instance. + + :param path: + Path to the file. + :returns: + The node. + """ + path = Path(path) + config = self.parseconfigure(path) + session = Session.from_config(config) + x = bestrelpath(session.path, path) + config.hook.pytest_sessionstart(session=session) + res = session.perform_collect([x], genitems=False)[0] + config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) + return res + + def genitems(self, colitems: Sequence[Item | Collector]) -> list[Item]: + """Generate all test items from a collection node. + + This recurses into the collection node and returns a list of all the + test items contained within. + + :param colitems: + The collection nodes. + :returns: + The collected items. + """ + session = colitems[0].session + result: list[Item] = [] + for colitem in colitems: + result.extend(session.genitems(colitem)) + return result + + def runitem(self, source: str) -> Any: + """Run the "test_func" Item. + + The calling test instance (class containing the test method) must + provide a ``.getrunner()`` method which should return a runner which + can run the test protocol for a single item, e.g. + ``_pytest.runner.runtestprotocol``. + """ + # used from runner functional tests + item = self.getitem(source) + # the test class where we are called from wants to provide the runner + testclassinstance = self._request.instance + runner = testclassinstance.getrunner() + return runner(item) + + def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: + """Run a test module in process using ``pytest.main()``. + + This run writes "source" into a temporary file and runs + ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance + for the result. + + :param source: The source code of the test module. + :param cmdlineargs: Any extra command line arguments to use. + """ + p = self.makepyfile(source) + values = [*list(cmdlineargs), p] + return self.inline_run(*values) + + def inline_genitems(self, *args) -> tuple[list[Item], HookRecorder]: + """Run ``pytest.main(['--collect-only'])`` in-process. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself like :py:meth:`inline_run`, but returns a + tuple of the collected items and a :py:class:`HookRecorder` instance. + """ + rec = self.inline_run("--collect-only", *args) + items = [x.item for x in rec.getcalls("pytest_itemcollected")] + return items, rec + + def inline_run( + self, + *args: str | os.PathLike[str], + plugins=(), + no_reraise_ctrlc: bool = False, + ) -> HookRecorder: + """Run ``pytest.main()`` in-process, returning a HookRecorder. + + Runs the :py:func:`pytest.main` function to run all of pytest inside + the test process itself. This means it can return a + :py:class:`HookRecorder` instance which gives more detailed results + from that run than can be done by matching stdout/stderr from + :py:meth:`runpytest`. + + :param args: + Command line arguments to pass to :py:func:`pytest.main`. + :param plugins: + Extra plugin instances the ``pytest.main()`` instance should use. + :param no_reraise_ctrlc: + Typically we reraise keyboard interrupts from the child run. If + True, the KeyboardInterrupt exception is captured. + """ + from _pytest.unraisableexception import gc_collect_iterations_key + + # (maybe a cpython bug?) the importlib cache sometimes isn't updated + # properly between file creation and inline_run (especially if imports + # are interspersed with file creation) + importlib.invalidate_caches() + + plugins = list(plugins) + finalizers = [] + try: + # Any sys.module or sys.path changes done while running pytest + # inline should be reverted after the test run completes to avoid + # clashing with later inline tests run within the same pytest test, + # e.g. just because they use matching test module names. + finalizers.append(self.__take_sys_modules_snapshot().restore) + finalizers.append(SysPathsSnapshot().restore) + + # Important note: + # - our tests should not leave any other references/registrations + # laying around other than possibly loaded test modules + # referenced from sys.modules, as nothing will clean those up + # automatically + + rec = [] + + class PytesterHelperPlugin: + @staticmethod + def pytest_configure(config: Config) -> None: + rec.append(self.make_hook_recorder(config.pluginmanager)) + + # The unraisable plugin GC collect slows down inline + # pytester runs too much. + config.stash[gc_collect_iterations_key] = 0 + + plugins.append(PytesterHelperPlugin()) + ret = main([str(x) for x in args], plugins=plugins) + if len(rec) == 1: + reprec = rec.pop() + else: + + class reprec: # type: ignore + pass + + reprec.ret = ret + + # Typically we reraise keyboard interrupts from the child run + # because it's our user requesting interruption of the testing. + if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: + calls = reprec.getcalls("pytest_keyboard_interrupt") + if calls and calls[-1].excinfo.type == KeyboardInterrupt: + raise KeyboardInterrupt() + return reprec + finally: + for finalizer in finalizers: + finalizer() + + def runpytest_inprocess( + self, *args: str | os.PathLike[str], **kwargs: Any + ) -> RunResult: + """Return result of running pytest in-process, providing a similar + interface to what self.runpytest() provides.""" + syspathinsert = kwargs.pop("syspathinsert", False) + + if syspathinsert: + self.syspathinsert() + instant = timing.Instant() + capture = _get_multicapture("sys") + capture.start_capturing() + try: + try: + reprec = self.inline_run(*args, **kwargs) + except SystemExit as e: + ret = e.args[0] + try: + ret = ExitCode(e.args[0]) + except ValueError: + pass + + class reprec: # type: ignore + ret = ret + + except Exception: + traceback.print_exc() + + class reprec: # type: ignore + ret = ExitCode(3) + + finally: + out, err = capture.readouterr() + capture.stop_capturing() + sys.stdout.write(out) + sys.stderr.write(err) + + assert reprec.ret is not None + res = RunResult( + reprec.ret, out.splitlines(), err.splitlines(), instant.elapsed().seconds + ) + res.reprec = reprec # type: ignore + return res + + def runpytest(self, *args: str | os.PathLike[str], **kwargs: Any) -> RunResult: + """Run pytest inline or in a subprocess, depending on the command line + option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" + new_args = self._ensure_basetemp(args) + if self._method == "inprocess": + return self.runpytest_inprocess(*new_args, **kwargs) + elif self._method == "subprocess": + return self.runpytest_subprocess(*new_args, **kwargs) + raise RuntimeError(f"Unrecognized runpytest option: {self._method}") + + def _ensure_basetemp( + self, args: Sequence[str | os.PathLike[str]] + ) -> list[str | os.PathLike[str]]: + new_args = list(args) + for x in new_args: + if str(x).startswith("--basetemp"): + break + else: + new_args.append( + "--basetemp={}".format(self.path.parent.joinpath("basetemp")) + ) + return new_args + + def parseconfig(self, *args: str | os.PathLike[str]) -> Config: + """Return a new pytest :class:`pytest.Config` instance from given + commandline args. + + This invokes the pytest bootstrapping code in _pytest.config to create a + new :py:class:`pytest.PytestPluginManager` and call the + :hook:`pytest_cmdline_parse` hook to create a new :class:`pytest.Config` + instance. + + If :attr:`plugins` has been populated they should be plugin modules + to be registered with the plugin manager. + """ + import _pytest.config + + new_args = [str(x) for x in self._ensure_basetemp(args)] + + config = _pytest.config._prepareconfig(new_args, self.plugins) + # we don't know what the test will do with this half-setup config + # object and thus we make sure it gets unconfigured properly in any + # case (otherwise capturing could still be active, for example) + self._request.addfinalizer(config._ensure_unconfigure) + return config + + def parseconfigure(self, *args: str | os.PathLike[str]) -> Config: + """Return a new pytest configured Config instance. + + Returns a new :py:class:`pytest.Config` instance like + :py:meth:`parseconfig`, but also calls the :hook:`pytest_configure` + hook. + """ + config = self.parseconfig(*args) + config._do_configure() + return config + + def getitem( + self, source: str | os.PathLike[str], funcname: str = "test_func" + ) -> Item: + """Return the test item for a test function. + + Writes the source to a python file and runs pytest's collection on + the resulting module, returning the test item for the requested + function name. + + :param source: + The module source. + :param funcname: + The name of the test function for which to return a test item. + :returns: + The test item. + """ + items = self.getitems(source) + for item in items: + if item.name == funcname: + return item + assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" + + def getitems(self, source: str | os.PathLike[str]) -> list[Item]: + """Return all test items collected from the module. + + Writes the source to a Python file and runs pytest's collection on + the resulting module, returning all test items contained within. + """ + modcol = self.getmodulecol(source) + return self.genitems([modcol]) + + def getmodulecol( + self, + source: str | os.PathLike[str], + configargs=(), + *, + withinit: bool = False, + ): + """Return the module collection node for ``source``. + + Writes ``source`` to a file using :py:meth:`makepyfile` and then + runs the pytest collection on it, returning the collection node for the + test module. + + :param source: + The source code of the module to collect. + + :param configargs: + Any extra arguments to pass to :py:meth:`parseconfigure`. + + :param withinit: + Whether to also write an ``__init__.py`` file to the same + directory to ensure it is a package. + """ + if isinstance(source, os.PathLike): + path = self.path.joinpath(source) + assert not withinit, "not supported for paths" + else: + kw = {self._name: str(source)} + path = self.makepyfile(**kw) + if withinit: + self.makepyfile(__init__="#") + self.config = config = self.parseconfigure(path, *configargs) + return self.getnode(config, path) + + def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: + """Return the collection node for name from the module collection. + + Searches a module collection node for a collection node matching the + given name. + + :param modcol: A module collection node; see :py:meth:`getmodulecol`. + :param name: The name of the node to return. + """ + if modcol not in self._mod_collections: + self._mod_collections[modcol] = list(modcol.collect()) + for colitem in self._mod_collections[modcol]: + if colitem.name == name: + return colitem + return None + + def popen( + self, + cmdargs: Sequence[str | os.PathLike[str]], + stdout: int | TextIO = subprocess.PIPE, + stderr: int | TextIO = subprocess.PIPE, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, + **kw, + ): + """Invoke :py:class:`subprocess.Popen`. + + Calls :py:class:`subprocess.Popen` making sure the current working + directory is in ``PYTHONPATH``. + + You probably want to use :py:meth:`run` instead. + """ + env = os.environ.copy() + env["PYTHONPATH"] = os.pathsep.join( + filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) + ) + kw["env"] = env + + if stdin is self.CLOSE_STDIN: + kw["stdin"] = subprocess.PIPE + elif isinstance(stdin, bytes): + kw["stdin"] = subprocess.PIPE + else: + kw["stdin"] = stdin + + popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) + if stdin is self.CLOSE_STDIN: + assert popen.stdin is not None + popen.stdin.close() + elif isinstance(stdin, bytes): + assert popen.stdin is not None + popen.stdin.write(stdin) + + return popen + + def run( + self, + *cmdargs: str | os.PathLike[str], + timeout: float | None = None, + stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, + ) -> RunResult: + """Run a command with arguments. + + Run a process using :py:class:`subprocess.Popen` saving the stdout and + stderr. + + :param cmdargs: + The sequence of arguments to pass to :py:class:`subprocess.Popen`, + with path-like objects being converted to :py:class:`str` + automatically. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :param stdin: + Optional standard input. + + - If it is ``CLOSE_STDIN`` (Default), then this method calls + :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and + the standard input is closed immediately after the new command is + started. + + - If it is of type :py:class:`bytes`, these bytes are sent to the + standard input of the command. + + - Otherwise, it is passed through to :py:class:`subprocess.Popen`. + For further information in this case, consult the document of the + ``stdin`` parameter in :py:class:`subprocess.Popen`. + :type stdin: _pytest.compat.NotSetType | bytes | IO[Any] | int + :returns: + The result. + + """ + __tracebackhide__ = True + + cmdargs = tuple(os.fspath(arg) for arg in cmdargs) + p1 = self.path.joinpath("stdout") + p2 = self.path.joinpath("stderr") + print("running:", *cmdargs) + print(" in:", Path.cwd()) + + with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: + instant = timing.Instant() + popen = self.popen( + cmdargs, + stdin=stdin, + stdout=f1, + stderr=f2, + ) + if popen.stdin is not None: + popen.stdin.close() + + def handle_timeout() -> None: + __tracebackhide__ = True + + timeout_message = f"{timeout} second timeout expired running: {cmdargs}" + + popen.kill() + popen.wait() + raise self.TimeoutExpired(timeout_message) + + if timeout is None: + ret = popen.wait() + else: + try: + ret = popen.wait(timeout) + except subprocess.TimeoutExpired: + handle_timeout() + f1.flush() + f2.flush() + + with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: + out = f1.read().splitlines() + err = f2.read().splitlines() + + self._dump_lines(out, sys.stdout) + self._dump_lines(err, sys.stderr) + + with contextlib.suppress(ValueError): + ret = ExitCode(ret) + return RunResult(ret, out, err, instant.elapsed().seconds) + + def _dump_lines(self, lines, fp): + try: + for line in lines: + print(line, file=fp) + except UnicodeEncodeError: + print(f"couldn't print to {fp} because of encoding") + + def _getpytestargs(self) -> tuple[str, ...]: + return sys.executable, "-mpytest" + + def runpython(self, script: os.PathLike[str]) -> RunResult: + """Run a python script using sys.executable as interpreter.""" + return self.run(sys.executable, script) + + def runpython_c(self, command: str) -> RunResult: + """Run ``python -c "command"``.""" + return self.run(sys.executable, "-c", command) + + def runpytest_subprocess( + self, *args: str | os.PathLike[str], timeout: float | None = None + ) -> RunResult: + """Run pytest as a subprocess with given arguments. + + Any plugins added to the :py:attr:`plugins` list will be added using the + ``-p`` command line option. Additionally ``--basetemp`` is used to put + any temporary files and directories in a numbered directory prefixed + with "runpytest-" to not conflict with the normal numbered pytest + location for temporary files and directories. + + :param args: + The sequence of arguments to pass to the pytest subprocess. + :param timeout: + The period in seconds after which to timeout and raise + :py:class:`Pytester.TimeoutExpired`. + :returns: + The result. + """ + __tracebackhide__ = True + p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) + args = (f"--basetemp={p}", *args) + for plugin in self.plugins: + if not isinstance(plugin, str): + raise ValueError( + f"Specifying plugins as objects is not supported in pytester subprocess mode; " + f"specify by name instead: {plugin}" + ) + args = ("-p", plugin, *args) + args = self._getpytestargs() + args + return self.run(*args, timeout=timeout) + + def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """Run pytest using pexpect. + + This makes sure to use the right pytest and sets up the temporary + directory locations. + + The pexpect child is returned. + """ + basetemp = self.path / "temp-pexpect" + basetemp.mkdir(mode=0o700) + invoke = " ".join(map(str, self._getpytestargs())) + cmd = f"{invoke} --basetemp={basetemp} {string}" + return self.spawn(cmd, expect_timeout=expect_timeout) + + def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: + """Run a command using pexpect. + + The pexpect child is returned. + """ + pexpect = importorskip("pexpect", "3.0") + if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): + skip("pypy-64 bit not supported") + if not hasattr(pexpect, "spawn"): + skip("pexpect.spawn not available") + logfile = self.path.joinpath("spawn.out").open("wb") + + child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) + self._request.addfinalizer(logfile.close) + return child + + +class LineComp: + def __init__(self) -> None: + self.stringio = StringIO() + """:class:`python:io.StringIO()` instance used for input.""" + + def assert_contains_lines(self, lines2: Sequence[str]) -> None: + """Assert that ``lines2`` are contained (linearly) in :attr:`stringio`'s value. + + Lines are matched using :func:`LineMatcher.fnmatch_lines `. + """ + __tracebackhide__ = True + val = self.stringio.getvalue() + self.stringio.truncate(0) + self.stringio.seek(0) + lines1 = val.split("\n") + LineMatcher(lines1).fnmatch_lines(lines2) + + +class LineMatcher: + """Flexible matching of text. + + This is a convenience class to test large texts like the output of + commands. + + The constructor takes a list of lines without their trailing newlines, i.e. + ``text.splitlines()``. + """ + + def __init__(self, lines: list[str]) -> None: + self.lines = lines + self._log_output: list[str] = [] + + def __str__(self) -> str: + """Return the entire original text. + + .. versionadded:: 6.2 + You can use :meth:`str` in older versions. + """ + return "\n".join(self.lines) + + def _getlines(self, lines2: str | Sequence[str] | Source) -> Sequence[str]: + if isinstance(lines2, str): + lines2 = Source(lines2) + if isinstance(lines2, Source): + lines2 = lines2.strip().lines + return lines2 + + def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, fnmatch) + + def re_match_lines_random(self, lines2: Sequence[str]) -> None: + """Check lines exist in the output in any order (using :func:`python:re.match`).""" + __tracebackhide__ = True + self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) + + def _match_lines_random( + self, lines2: Sequence[str], match_func: Callable[[str, str], bool] + ) -> None: + __tracebackhide__ = True + lines2 = self._getlines(lines2) + for line in lines2: + for x in self.lines: + if line == x or match_func(x, line): + self._log("matched: ", repr(line)) + break + else: + msg = f"line {line!r} not found in output" + self._log(msg) + self._fail(msg) + + def get_lines_after(self, fnline: str) -> Sequence[str]: + """Return all lines following the given line in the text. + + The given line can contain glob wildcards. + """ + for i, line in enumerate(self.lines): + if fnline == line or fnmatch(line, fnline): + return self.lines[i + 1 :] + raise ValueError(f"line {fnline!r} not found in output") + + def _log(self, *args) -> None: + self._log_output.append(" ".join(str(x) for x in args)) + + @property + def _log_text(self) -> str: + return "\n".join(self._log_output) + + def fnmatch_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). + + The argument is a list of lines which have to match and can use glob + wildcards. If they do not match a pytest.fail() is called. The + matches and non-matches are also shown as part of the error message. + + :param lines2: String patterns to match. + :param consecutive: Match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) + + def re_match_lines( + self, lines2: Sequence[str], *, consecutive: bool = False + ) -> None: + """Check lines exist in the output (using :func:`python:re.match`). + + The argument is a list of lines which have to match using ``re.match``. + If they do not match a pytest.fail() is called. + + The matches and non-matches are also shown as part of the error message. + + :param lines2: string patterns to match. + :param consecutive: match lines consecutively? + """ + __tracebackhide__ = True + self._match_lines( + lines2, + lambda name, pat: bool(re.match(pat, name)), + "re.match", + consecutive=consecutive, + ) + + def _match_lines( + self, + lines2: Sequence[str], + match_func: Callable[[str, str], bool], + match_nickname: str, + *, + consecutive: bool = False, + ) -> None: + """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``. + + :param Sequence[str] lines2: + List of string patterns to match. The actual format depends on + ``match_func``. + :param match_func: + A callable ``match_func(line, pattern)`` where line is the + captured line from stdout/stderr and pattern is the matching + pattern. + :param str match_nickname: + The nickname for the match function that will be logged to stdout + when a match occurs. + :param consecutive: + Match lines consecutively? + """ + if not isinstance(lines2, collections.abc.Sequence): + raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") + lines2 = self._getlines(lines2) + lines1 = self.lines[:] + extralines = [] + __tracebackhide__ = True + wnick = len(match_nickname) + 1 + started = False + for line in lines2: + nomatchprinted = False + while lines1: + nextline = lines1.pop(0) + if line == nextline: + self._log("exact match:", repr(line)) + started = True + break + elif match_func(nextline, line): + self._log(f"{match_nickname}:", repr(line)) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + started = True + break + else: + if consecutive and started: + msg = f"no consecutive match: {line!r}" + self._log(msg) + self._log( + "{:>{width}}".format("with:", width=wnick), repr(nextline) + ) + self._fail(msg) + if not nomatchprinted: + self._log( + "{:>{width}}".format("nomatch:", width=wnick), repr(line) + ) + nomatchprinted = True + self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) + extralines.append(nextline) + else: + msg = f"remains unmatched: {line!r}" + self._log(msg) + self._fail(msg) + self._log_output = [] + + def no_fnmatch_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + self._no_match_line(pat, fnmatch, "fnmatch") + + def no_re_match_line(self, pat: str) -> None: + """Ensure captured lines do not match the given pattern, using ``re.match``. + + :param str pat: The regular expression to match lines. + """ + __tracebackhide__ = True + self._no_match_line( + pat, lambda name, pat: bool(re.match(pat, name)), "re.match" + ) + + def _no_match_line( + self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str + ) -> None: + """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. + + :param str pat: The pattern to match lines. + """ + __tracebackhide__ = True + nomatch_printed = False + wnick = len(match_nickname) + 1 + for line in self.lines: + if match_func(line, pat): + msg = f"{match_nickname}: {pat!r}" + self._log(msg) + self._log("{:>{width}}".format("with:", width=wnick), repr(line)) + self._fail(msg) + else: + if not nomatch_printed: + self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) + nomatch_printed = True + self._log("{:>{width}}".format("and:", width=wnick), repr(line)) + self._log_output = [] + + def _fail(self, msg: str) -> None: + __tracebackhide__ = True + log_text = self._log_text + self._log_output = [] + fail(log_text) + + def str(self) -> str: + """Return the entire original text.""" + return str(self) diff --git a/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py b/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py new file mode 100644 index 0000000..915cc8a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py @@ -0,0 +1,74 @@ +"""Helper plugin for pytester; should not be loaded on its own.""" + +# This plugin contains assertions used by pytester. pytester cannot +# contain them itself, since it is imported by the `pytest` module, +# hence cannot be subject to assertion rewriting, which requires a +# module to not be already imported. +from __future__ import annotations + +from collections.abc import Sequence + +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +def assertoutcome( + outcomes: tuple[ + Sequence[TestReport], + Sequence[CollectReport | TestReport], + Sequence[CollectReport | TestReport], + ], + passed: int = 0, + skipped: int = 0, + failed: int = 0, +) -> None: + __tracebackhide__ = True + + realpassed, realskipped, realfailed = outcomes + obtained = { + "passed": len(realpassed), + "skipped": len(realskipped), + "failed": len(realfailed), + } + expected = {"passed": passed, "skipped": skipped, "failed": failed} + assert obtained == expected, outcomes + + +def assert_outcomes( + outcomes: dict[str, int], + passed: int = 0, + skipped: int = 0, + failed: int = 0, + errors: int = 0, + xpassed: int = 0, + xfailed: int = 0, + warnings: int | None = None, + deselected: int | None = None, +) -> None: + """Assert that the specified outcomes appear with the respective + numbers (0 means it didn't occur) in the text output from a test run.""" + __tracebackhide__ = True + + obtained = { + "passed": outcomes.get("passed", 0), + "skipped": outcomes.get("skipped", 0), + "failed": outcomes.get("failed", 0), + "errors": outcomes.get("errors", 0), + "xpassed": outcomes.get("xpassed", 0), + "xfailed": outcomes.get("xfailed", 0), + } + expected = { + "passed": passed, + "skipped": skipped, + "failed": failed, + "errors": errors, + "xpassed": xpassed, + "xfailed": xfailed, + } + if warnings is not None: + obtained["warnings"] = outcomes.get("warnings", 0) + expected["warnings"] = warnings + if deselected is not None: + obtained["deselected"] = outcomes.get("deselected", 0) + expected["deselected"] = deselected + assert obtained == expected diff --git a/.venv/lib/python3.12/site-packages/_pytest/python.py b/.venv/lib/python3.12/site-packages/_pytest/python.py new file mode 100644 index 0000000..e637518 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/python.py @@ -0,0 +1,1772 @@ +# mypy: allow-untyped-defs +"""Python test discovery, setup and run of test functions.""" + +from __future__ import annotations + +import abc +from collections import Counter +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import enum +import fnmatch +from functools import partial +import inspect +import itertools +import os +from pathlib import Path +import re +import textwrap +import types +from typing import Any +from typing import cast +from typing import final +from typing import Literal +from typing import NoReturn +from typing import TYPE_CHECKING +import warnings + +import _pytest +from _pytest import fixtures +from _pytest import nodes +from _pytest._code import filter_traceback +from _pytest._code import getfslineno +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback +from _pytest._io.saferepr import saferepr +from _pytest.compat import ascii_escaped +from _pytest.compat import get_default_arg_names +from _pytest.compat import get_real_func +from _pytest.compat import getimfunc +from _pytest.compat import is_async_function +from _pytest.compat import LEGACY_PATH +from _pytest.compat import NOTSET +from _pytest.compat import safe_getattr +from _pytest.compat import safe_isclass +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import FixtureRequest +from _pytest.fixtures import FuncFixtureInfo +from _pytest.fixtures import get_scope_node +from _pytest.main import Session +from _pytest.mark import ParameterSet +from _pytest.mark.structures import _HiddenParam +from _pytest.mark.structures import get_unpacked_marks +from _pytest.mark.structures import HIDDEN_PARAM +from _pytest.mark.structures import Mark +from _pytest.mark.structures import MarkDecorator +from _pytest.mark.structures import normalize_mark_list +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.pathlib import fnmatch_ex +from _pytest.pathlib import import_path +from _pytest.pathlib import ImportPathMismatchError +from _pytest.pathlib import scandir +from _pytest.scope import _ScopeName +from _pytest.scope import Scope +from _pytest.stash import StashKey +from _pytest.warning_types import PytestCollectionWarning +from _pytest.warning_types import PytestReturnNotNoneWarning + + +if TYPE_CHECKING: + from typing_extensions import Self + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "python_files", + type="args", + # NOTE: default is also used in AssertionRewritingHook. + default=["test_*.py", "*_test.py"], + help="Glob-style file patterns for Python test module discovery", + ) + parser.addini( + "python_classes", + type="args", + default=["Test"], + help="Prefixes or glob names for Python test class discovery", + ) + parser.addini( + "python_functions", + type="args", + default=["test"], + help="Prefixes or glob names for Python test function and method discovery", + ) + parser.addini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", + type="bool", + default=False, + help="Disable string escape non-ASCII characters, might cause unwanted " + "side effects(use at your own risk)", + ) + parser.addini( + "strict_parametrization_ids", + type="bool", + # None => fallback to `strict`. + default=None, + help="Emit an error if non-unique parameter set IDs are detected", + ) + + +def pytest_generate_tests(metafunc: Metafunc) -> None: + for marker in metafunc.definition.iter_markers(name="parametrize"): + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) + + +def pytest_configure(config: Config) -> None: + config.addinivalue_line( + "markers", + "parametrize(argnames, argvalues): call a test function multiple " + "times passing in different arguments in turn. argvalues generally " + "needs to be a list of values if argnames specifies only one name " + "or a list of tuples of values if argnames specifies multiple names. " + "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " + "decorated test function, one with arg1=1 and another with arg1=2." + "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info " + "and examples.", + ) + config.addinivalue_line( + "markers", + "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " + "all of the specified fixtures. see " + "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ", + ) + + +def async_fail(nodeid: str) -> None: + msg = ( + "async def functions are not natively supported.\n" + "You need to install a suitable plugin for your async framework, for example:\n" + " - anyio\n" + " - pytest-asyncio\n" + " - pytest-tornasync\n" + " - pytest-trio\n" + " - pytest-twisted" + ) + fail(msg, pytrace=False) + + +@hookimpl(trylast=True) +def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: + testfunction = pyfuncitem.obj + if is_async_function(testfunction): + async_fail(pyfuncitem.nodeid) + funcargs = pyfuncitem.funcargs + testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} + result = testfunction(**testargs) + if hasattr(result, "__await__") or hasattr(result, "__aiter__"): + async_fail(pyfuncitem.nodeid) + elif result is not None: + warnings.warn( + PytestReturnNotNoneWarning( + f"Test functions should return None, but {pyfuncitem.nodeid} returned {type(result)!r}.\n" + "Did you mean to use `assert` instead of `return`?\n" + "See https://docs.pytest.org/en/stable/how-to/assert.html#return-not-none for more information." + ) + ) + return True + + +def pytest_collect_directory( + path: Path, parent: nodes.Collector +) -> nodes.Collector | None: + pkginit = path / "__init__.py" + try: + has_pkginit = pkginit.is_file() + except PermissionError: + # See https://github.com/pytest-dev/pytest/issues/12120#issuecomment-2106349096. + return None + if has_pkginit: + return Package.from_parent(parent, path=path) + return None + + +def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Module | None: + if file_path.suffix == ".py": + if not parent.session.isinitpath(file_path): + if not path_matches_patterns( + file_path, parent.config.getini("python_files") + ): + return None + ihook = parent.session.gethookproxy(file_path) + module: Module = ihook.pytest_pycollect_makemodule( + module_path=file_path, parent=parent + ) + return module + return None + + +def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: + """Return whether path matches any of the patterns in the list of globs given.""" + return any(fnmatch_ex(pattern, path) for pattern in patterns) + + +def pytest_pycollect_makemodule(module_path: Path, parent) -> Module: + return Module.from_parent(parent, path=module_path) + + +@hookimpl(trylast=True) +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> None | nodes.Item | nodes.Collector | list[nodes.Item | nodes.Collector]: + assert isinstance(collector, Class | Module), type(collector) + # Nothing was collected elsewhere, let's do it here. + if safe_isclass(obj): + if collector.istestclass(obj, name): + return Class.from_parent(collector, name=name, obj=obj) + elif collector.istestfunction(obj, name): + # mock seems to store unbound methods (issue473), normalize it. + obj = getattr(obj, "__func__", obj) + # We need to try and unwrap the function if it's a functools.partial + # or a functools.wrapped. + # We mustn't if it's been wrapped with mock.patch (python 2 only). + if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))): + filename, lineno = getfslineno(obj) + warnings.warn_explicit( + message=PytestCollectionWarning( + f"cannot collect {name!r} because it is not a function." + ), + category=None, + filename=str(filename), + lineno=lineno + 1, + ) + elif getattr(obj, "__test__", True): + if inspect.isgeneratorfunction(obj): + fail( + f"'yield' keyword is allowed in fixtures, but not in tests ({name})", + pytrace=False, + ) + return list(collector._genfunctions(name, obj)) + return None + return None + + +class PyobjMixin(nodes.Node): + """this mix-in inherits from Node to carry over the typing information + + as its intended to always mix in before a node + its position in the mro is unaffected""" + + _ALLOW_MARKERS = True + + @property + def module(self): + """Python module object this node was collected from (can be None).""" + node = self.getparent(Module) + return node.obj if node is not None else None + + @property + def cls(self): + """Python class object this node was collected from (can be None).""" + node = self.getparent(Class) + return node.obj if node is not None else None + + @property + def instance(self): + """Python instance object the function is bound to. + + Returns None if not a test method, e.g. for a standalone test function, + a class or a module. + """ + # Overridden by Function. + return None + + @property + def obj(self): + """Underlying Python object.""" + obj = getattr(self, "_obj", None) + if obj is None: + self._obj = obj = self._getobj() + # XXX evil hack + # used to avoid Function marker duplication + if self._ALLOW_MARKERS: + self.own_markers.extend(get_unpacked_marks(self.obj)) + # This assumes that `obj` is called before there is a chance + # to add custom keys to `self.keywords`, so no fear of overriding. + self.keywords.update((mark.name, mark) for mark in self.own_markers) + return obj + + @obj.setter + def obj(self, value): + self._obj = value + + def _getobj(self): + """Get the underlying Python object. May be overwritten by subclasses.""" + # TODO: Improve the type of `parent` such that assert/ignore aren't needed. + assert self.parent is not None + obj = self.parent.obj # type: ignore[attr-defined] + return getattr(obj, self.name) + + def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str: + """Return Python path relative to the containing module.""" + parts = [] + for node in self.iter_parents(): + name = node.name + if isinstance(node, Module): + name = os.path.splitext(name)[0] + if stopatmodule: + if includemodule: + parts.append(name) + break + parts.append(name) + parts.reverse() + return ".".join(parts) + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + # XXX caching? + path, lineno = getfslineno(self.obj) + modpath = self.getmodpath() + return path, lineno, modpath + + +# As an optimization, these builtin attribute names are pre-ignored when +# iterating over an object during collection -- the pytest_pycollect_makeitem +# hook is not called for them. +# fmt: off +class _EmptyClass: pass # noqa: E701 +IGNORED_ATTRIBUTES = frozenset.union( + frozenset(), + # Module. + dir(types.ModuleType("empty_module")), + # Some extra module attributes the above doesn't catch. + {"__builtins__", "__file__", "__cached__"}, + # Class. + dir(_EmptyClass), + # Instance. + dir(_EmptyClass()), +) +del _EmptyClass +# fmt: on + + +class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): + def funcnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_functions", name) + + def isnosetest(self, obj: object) -> bool: + """Look for the __test__ attribute, which is applied by the + @nose.tools.istest decorator. + """ + # We explicitly check for "is True" here to not mistakenly treat + # classes with a custom __getattr__ returning something truthy (like a + # function) as test classes. + return safe_getattr(obj, "__test__", False) is True + + def classnamefilter(self, name: str) -> bool: + return self._matches_prefix_or_glob_option("python_classes", name) + + def istestfunction(self, obj: object, name: str) -> bool: + if self.funcnamefilter(name) or self.isnosetest(obj): + if isinstance(obj, staticmethod | classmethod): + # staticmethods and classmethods need to be unwrapped. + obj = safe_getattr(obj, "__func__", False) + return callable(obj) and fixtures.getfixturemarker(obj) is None + else: + return False + + def istestclass(self, obj: object, name: str) -> bool: + if not (self.classnamefilter(name) or self.isnosetest(obj)): + return False + if inspect.isabstract(obj): + return False + return True + + def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool: + """Check if the given name matches the prefix or glob-pattern defined + in configuration.""" + for option in self.config.getini(option_name): + if name.startswith(option): + return True + # Check that name looks like a glob-string before calling fnmatch + # because this is called for every name in each collected module, + # and fnmatch is somewhat expensive to call. + elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( + name, option + ): + return True + return False + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not getattr(self.obj, "__test__", True): + return [] + + # Avoid random getattrs and peek in the __dict__ instead. + dicts = [getattr(self.obj, "__dict__", {})] + if isinstance(self.obj, type): + for basecls in self.obj.__mro__: + dicts.append(basecls.__dict__) + + # In each class, nodes should be definition ordered. + # __dict__ is definition ordered. + seen: set[str] = set() + dict_values: list[list[nodes.Item | nodes.Collector]] = [] + collect_imported_tests = self.session.config.getini("collect_imported_tests") + ihook = self.ihook + for dic in dicts: + values: list[nodes.Item | nodes.Collector] = [] + # Note: seems like the dict can change during iteration - + # be careful not to remove the list() without consideration. + for name, obj in list(dic.items()): + if name in IGNORED_ATTRIBUTES: + continue + if name in seen: + continue + seen.add(name) + + if not collect_imported_tests and isinstance(self, Module): + # Do not collect functions and classes from other modules. + if inspect.isfunction(obj) or inspect.isclass(obj): + if obj.__module__ != self._getobj().__name__: + continue + + res = ihook.pytest_pycollect_makeitem( + collector=self, name=name, obj=obj + ) + if res is None: + continue + elif isinstance(res, list): + values.extend(res) + else: + values.append(res) + dict_values.append(values) + + # Between classes in the class hierarchy, reverse-MRO order -- nodes + # inherited from base classes should come before subclasses. + result = [] + for values in reversed(dict_values): + result.extend(values) + return result + + def _genfunctions(self, name: str, funcobj) -> Iterator[Function]: + modulecol = self.getparent(Module) + assert modulecol is not None + module = modulecol.obj + clscol = self.getparent(Class) + cls = (clscol and clscol.obj) or None + + definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) + fixtureinfo = definition._fixtureinfo + + # pytest_generate_tests impls call metafunc.parametrize() which fills + # metafunc._calls, the outcome of the hook. + metafunc = Metafunc( + definition=definition, + fixtureinfo=fixtureinfo, + config=self.config, + cls=cls, + module=module, + _ispytest=True, + ) + methods = [] + if hasattr(module, "pytest_generate_tests"): + methods.append(module.pytest_generate_tests) + if cls is not None and hasattr(cls, "pytest_generate_tests"): + methods.append(cls().pytest_generate_tests) + self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) + + if not metafunc._calls: + yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) + else: + metafunc._recompute_direct_params_indices() + # Direct parametrizations taking place in module/class-specific + # `metafunc.parametrize` calls may have shadowed some fixtures, so make sure + # we update what the function really needs a.k.a its fixture closure. Note that + # direct parametrizations using `@pytest.mark.parametrize` have already been considered + # into making the closure using `ignore_args` arg to `getfixtureclosure`. + fixtureinfo.prune_dependency_tree() + + for callspec in metafunc._calls: + subname = f"{name}[{callspec.id}]" if callspec._idlist else name + yield Function.from_parent( + self, + name=subname, + callspec=callspec, + fixtureinfo=fixtureinfo, + keywords={callspec.id: True}, + originalname=name, + ) + + +def importtestmodule( + path: Path, + config: Config, +): + # We assume we are only called once per module. + importmode = config.getoption("--import-mode") + try: + mod = import_path( + path, + mode=importmode, + root=config.rootpath, + consider_namespace_packages=config.getini("consider_namespace_packages"), + ) + except SyntaxError as e: + raise nodes.Collector.CollectError( + ExceptionInfo.from_current().getrepr(style="short") + ) from e + except ImportPathMismatchError as e: + raise nodes.Collector.CollectError( + "import file mismatch:\n" + "imported module {!r} has this __file__ attribute:\n" + " {}\n" + "which is not the same as the test file we want to collect:\n" + " {}\n" + "HINT: remove __pycache__ / .pyc files and/or use a " + "unique basename for your test file modules".format(*e.args) + ) from e + except ImportError as e: + exc_info = ExceptionInfo.from_current() + if config.get_verbosity() < 2: + exc_info.traceback = exc_info.traceback.filter(filter_traceback) + exc_repr = ( + exc_info.getrepr(style="short") + if exc_info.traceback + else exc_info.exconly() + ) + formatted_tb = str(exc_repr) + raise nodes.Collector.CollectError( + f"ImportError while importing test module '{path}'.\n" + "Hint: make sure your test modules/packages have valid Python names.\n" + "Traceback:\n" + f"{formatted_tb}" + ) from e + except skip.Exception as e: + if e.allow_module_level: + raise + raise nodes.Collector.CollectError( + "Using pytest.skip outside of a test will skip the entire module. " + "If that's your intention, pass `allow_module_level=True`. " + "If you want to skip a specific test or an entire class, " + "use the @pytest.mark.skip or @pytest.mark.skipif decorators." + ) from e + config.pluginmanager.consider_module(mod) + return mod + + +class Module(nodes.File, PyCollector): + """Collector for test classes and functions in a Python module.""" + + def _getobj(self): + return importtestmodule(self.path, self.config) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + self._register_setup_module_fixture() + self._register_setup_function_fixture() + self.session._fixturemanager.parsefactories(self) + return super().collect() + + def _register_setup_module_fixture(self) -> None: + """Register an autouse, module-scoped fixture for the collected module object + that invokes setUpModule/tearDownModule if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_module = _get_first_non_fixture_func( + self.obj, ("setUpModule", "setup_module") + ) + teardown_module = _get_first_non_fixture_func( + self.obj, ("tearDownModule", "teardown_module") + ) + + if setup_module is None and teardown_module is None: + return + + def xunit_setup_module_fixture(request) -> Generator[None]: + module = request.module + if setup_module is not None: + _call_with_optional_argument(setup_module, module) + yield + if teardown_module is not None: + _call_with_optional_argument(teardown_module, module) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_module_fixture_{self.obj.__name__}", + func=xunit_setup_module_fixture, + nodeid=self.nodeid, + scope="module", + autouse=True, + ) + + def _register_setup_function_fixture(self) -> None: + """Register an autouse, function-scoped fixture for the collected module object + that invokes setup_function/teardown_function if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) + teardown_function = _get_first_non_fixture_func( + self.obj, ("teardown_function",) + ) + if setup_function is None and teardown_function is None: + return + + def xunit_setup_function_fixture(request) -> Generator[None]: + if request.instance is not None: + # in this case we are bound to an instance, so we need to let + # setup_method handle this + yield + return + function = request.function + if setup_function is not None: + _call_with_optional_argument(setup_function, function) + yield + if teardown_function is not None: + _call_with_optional_argument(teardown_function, function) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_function_fixture_{self.obj.__name__}", + func=xunit_setup_function_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +class Package(nodes.Directory): + """Collector for files and directories in a Python packages -- directories + with an `__init__.py` file. + + .. note:: + + Directories without an `__init__.py` file are instead collected by + :class:`~pytest.Dir` by default. Both are :class:`~pytest.Directory` + collectors. + + .. versionchanged:: 8.0 + + Now inherits from :class:`~pytest.Directory`. + """ + + def __init__( + self, + fspath: LEGACY_PATH | None, + parent: nodes.Collector, + # NOTE: following args are unused: + config=None, + session=None, + nodeid=None, + path: Path | None = None, + ) -> None: + # NOTE: Could be just the following, but kept as-is for compat. + # super().__init__(self, fspath, parent=parent) + session = parent.session + super().__init__( + fspath=fspath, + path=path, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + ) + + def setup(self) -> None: + init_mod = importtestmodule(self.path / "__init__.py", self.config) + + # Not using fixtures to call setup_module here because autouse fixtures + # from packages are not called automatically (#4085). + setup_module = _get_first_non_fixture_func( + init_mod, ("setUpModule", "setup_module") + ) + if setup_module is not None: + _call_with_optional_argument(setup_module, init_mod) + + teardown_module = _get_first_non_fixture_func( + init_mod, ("tearDownModule", "teardown_module") + ) + if teardown_module is not None: + func = partial(_call_with_optional_argument, teardown_module, init_mod) + self.addfinalizer(func) + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + # Always collect __init__.py first. + def sort_key(entry: os.DirEntry[str]) -> object: + return (entry.name != "__init__.py", entry.name) + + config = self.config + col: nodes.Collector | None + cols: Sequence[nodes.Collector] + ihook = self.ihook + for direntry in scandir(self.path, sort_key): + if direntry.is_dir(): + path = Path(direntry.path) + if not self.session.isinitpath(path, with_parents=True): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + col = ihook.pytest_collect_directory(path=path, parent=self) + if col is not None: + yield col + + elif direntry.is_file(): + path = Path(direntry.path) + if not self.session.isinitpath(path): + if ihook.pytest_ignore_collect(collection_path=path, config=config): + continue + cols = ihook.pytest_collect_file(file_path=path, parent=self) + yield from cols + + +def _call_with_optional_argument(func, arg) -> None: + """Call the given function with the given argument if func accepts one argument, otherwise + calls func without arguments.""" + arg_count = func.__code__.co_argcount + if inspect.ismethod(func): + arg_count -= 1 + if arg_count: + func(arg) + else: + func() + + +def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> object | None: + """Return the attribute from the given object to be used as a setup/teardown + xunit-style function, but only if not marked as a fixture to avoid calling it twice. + """ + for name in names: + meth: object | None = getattr(obj, name, None) + if meth is not None and fixtures.getfixturemarker(meth) is None: + return meth + return None + + +class Class(PyCollector): + """Collector for test methods (and nested classes) in a Python class.""" + + @classmethod + def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: # type: ignore[override] + """The public constructor.""" + return super().from_parent(name=name, parent=parent, **kw) + + def newinstance(self): + return self.obj() + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + if not safe_getattr(self.obj, "__test__", True): + return [] + if hasinit(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__init__ constructor (from: {self.parent.nodeid})" + ) + ) + return [] + elif hasnew(self.obj): + assert self.parent is not None + self.warn( + PytestCollectionWarning( + f"cannot collect test class {self.obj.__name__!r} because it has a " + f"__new__ constructor (from: {self.parent.nodeid})" + ) + ) + return [] + + self._register_setup_class_fixture() + self._register_setup_method_fixture() + + self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) + + return super().collect() + + def _register_setup_class_fixture(self) -> None: + """Register an autouse, class scoped fixture into the collected class object + that invokes setup_class/teardown_class if either or both are available. + + Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) + teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",)) + if setup_class is None and teardown_class is None: + return + + def xunit_setup_class_fixture(request) -> Generator[None]: + cls = request.cls + if setup_class is not None: + func = getimfunc(setup_class) + _call_with_optional_argument(func, cls) + yield + if teardown_class is not None: + func = getimfunc(teardown_class) + _call_with_optional_argument(func, cls) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", + func=xunit_setup_class_fixture, + nodeid=self.nodeid, + scope="class", + autouse=True, + ) + + def _register_setup_method_fixture(self) -> None: + """Register an autouse, function scoped fixture into the collected class object + that invokes setup_method/teardown_method if either or both are available. + + Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with + other fixtures (#517). + """ + setup_name = "setup_method" + setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) + teardown_name = "teardown_method" + teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) + if setup_method is None and teardown_method is None: + return + + def xunit_setup_method_fixture(request) -> Generator[None]: + instance = request.instance + method = request.function + if setup_method is not None: + func = getattr(instance, setup_name) + _call_with_optional_argument(func, method) + yield + if teardown_method is not None: + func = getattr(instance, teardown_name) + _call_with_optional_argument(func, method) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", + func=xunit_setup_method_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +def hasinit(obj: object) -> bool: + init: object = getattr(obj, "__init__", None) + if init: + return init != object.__init__ + return False + + +def hasnew(obj: object) -> bool: + new: object = getattr(obj, "__new__", None) + if new: + return new != object.__new__ + return False + + +@final +@dataclasses.dataclass(frozen=True) +class IdMaker: + """Make IDs for a parametrization.""" + + __slots__ = ( + "argnames", + "config", + "func_name", + "idfn", + "ids", + "nodeid", + "parametersets", + ) + + # The argnames of the parametrization. + argnames: Sequence[str] + # The ParameterSets of the parametrization. + parametersets: Sequence[ParameterSet] + # Optionally, a user-provided callable to make IDs for parameters in a + # ParameterSet. + idfn: Callable[[Any], object | None] | None + # Optionally, explicit IDs for ParameterSets by index. + ids: Sequence[object | None] | None + # Optionally, the pytest config. + # Used for controlling ASCII escaping, determining parametrization ID + # strictness, and for calling the :hook:`pytest_make_parametrize_id` hook. + config: Config | None + # Optionally, the ID of the node being parametrized. + # Used only for clearer error messages. + nodeid: str | None + # Optionally, the ID of the function being parametrized. + # Used only for clearer error messages. + func_name: str | None + + def make_unique_parameterset_ids(self) -> list[str | _HiddenParam]: + """Make a unique identifier for each ParameterSet, that may be used to + identify the parametrization in a node ID. + + If strict_parametrization_ids is enabled, and duplicates are detected, + raises CollectError. Otherwise makes the IDs unique as follows: + + Format is -...-[counter], where prm_x_token is + - user-provided id, if given + - else an id derived from the value, applicable for certain types + - else + The counter suffix is appended only in case a string wouldn't be unique + otherwise. + """ + resolved_ids = list(self._resolve_ids()) + # All IDs must be unique! + if len(resolved_ids) != len(set(resolved_ids)): + # Record the number of occurrences of each ID. + id_counts = Counter(resolved_ids) + + if self._strict_parametrization_ids_enabled(): + parameters = ", ".join(self.argnames) + parametersets = ", ".join( + [saferepr(list(param.values)) for param in self.parametersets] + ) + ids = ", ".join( + id if id is not HIDDEN_PARAM else "" for id in resolved_ids + ) + duplicates = ", ".join( + id if id is not HIDDEN_PARAM else "" + for id, count in id_counts.items() + if count > 1 + ) + msg = textwrap.dedent(f""" + Duplicate parametrization IDs detected, but strict_parametrization_ids is set. + + Test name: {self.nodeid} + Parameters: {parameters} + Parameter sets: {parametersets} + IDs: {ids} + Duplicates: {duplicates} + + You can fix this problem using `@pytest.mark.parametrize(..., ids=...)` or `pytest.param(..., id=...)`. + """).strip() # noqa: E501 + raise nodes.Collector.CollectError(msg) + + # Map the ID to its next suffix. + id_suffixes: dict[str, int] = defaultdict(int) + # Suffix non-unique IDs to make them unique. + for index, id in enumerate(resolved_ids): + if id_counts[id] > 1: + if id is HIDDEN_PARAM: + self._complain_multiple_hidden_parameter_sets() + suffix = "" + if id and id[-1].isdigit(): + suffix = "_" + new_id = f"{id}{suffix}{id_suffixes[id]}" + while new_id in set(resolved_ids): + id_suffixes[id] += 1 + new_id = f"{id}{suffix}{id_suffixes[id]}" + resolved_ids[index] = new_id + id_suffixes[id] += 1 + assert len(resolved_ids) == len(set(resolved_ids)), ( + f"Internal error: {resolved_ids=}" + ) + return resolved_ids + + def _strict_parametrization_ids_enabled(self) -> bool: + if self.config is None: + return False + strict_parametrization_ids = self.config.getini("strict_parametrization_ids") + if strict_parametrization_ids is None: + strict_parametrization_ids = self.config.getini("strict") + return cast(bool, strict_parametrization_ids) + + def _resolve_ids(self) -> Iterable[str | _HiddenParam]: + """Resolve IDs for all ParameterSets (may contain duplicates).""" + for idx, parameterset in enumerate(self.parametersets): + if parameterset.id is not None: + # ID provided directly - pytest.param(..., id="...") + if parameterset.id is HIDDEN_PARAM: + yield HIDDEN_PARAM + else: + yield _ascii_escaped_by_config(parameterset.id, self.config) + elif self.ids and idx < len(self.ids) and self.ids[idx] is not None: + # ID provided in the IDs list - parametrize(..., ids=[...]). + if self.ids[idx] is HIDDEN_PARAM: + yield HIDDEN_PARAM + else: + yield self._idval_from_value_required(self.ids[idx], idx) + else: + # ID not provided - generate it. + yield "-".join( + self._idval(val, argname, idx) + for val, argname in zip( + parameterset.values, self.argnames, strict=True + ) + ) + + def _idval(self, val: object, argname: str, idx: int) -> str: + """Make an ID for a parameter in a ParameterSet.""" + idval = self._idval_from_function(val, argname, idx) + if idval is not None: + return idval + idval = self._idval_from_hook(val, argname) + if idval is not None: + return idval + idval = self._idval_from_value(val) + if idval is not None: + return idval + return self._idval_from_argname(argname, idx) + + def _idval_from_function(self, val: object, argname: str, idx: int) -> str | None: + """Try to make an ID for a parameter in a ParameterSet using the + user-provided id callable, if given.""" + if self.idfn is None: + return None + try: + id = self.idfn(val) + except Exception as e: + prefix = f"{self.nodeid}: " if self.nodeid is not None else "" + msg = "error raised while trying to determine id of parameter '{}' at position {}" + msg = prefix + msg.format(argname, idx) + raise ValueError(msg) from e + if id is None: + return None + return self._idval_from_value(id) + + def _idval_from_hook(self, val: object, argname: str) -> str | None: + """Try to make an ID for a parameter in a ParameterSet by calling the + :hook:`pytest_make_parametrize_id` hook.""" + if self.config: + id: str | None = self.config.hook.pytest_make_parametrize_id( + config=self.config, val=val, argname=argname + ) + return id + return None + + def _idval_from_value(self, val: object) -> str | None: + """Try to make an ID for a parameter in a ParameterSet from its value, + if the value type is supported.""" + if isinstance(val, str | bytes): + return _ascii_escaped_by_config(val, self.config) + elif val is None or isinstance(val, float | int | bool | complex): + return str(val) + elif isinstance(val, re.Pattern): + return ascii_escaped(val.pattern) + elif val is NOTSET: + # Fallback to default. Note that NOTSET is an enum.Enum. + pass + elif isinstance(val, enum.Enum): + return str(val) + elif isinstance(getattr(val, "__name__", None), str): + # Name of a class, function, module, etc. + name: str = getattr(val, "__name__") + return name + return None + + def _idval_from_value_required(self, val: object, idx: int) -> str: + """Like _idval_from_value(), but fails if the type is not supported.""" + id = self._idval_from_value(val) + if id is not None: + return id + + # Fail. + prefix = self._make_error_prefix() + msg = ( + f"{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. " + "Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__." + ) + fail(msg, pytrace=False) + + @staticmethod + def _idval_from_argname(argname: str, idx: int) -> str: + """Make an ID for a parameter in a ParameterSet from the argument name + and the index of the ParameterSet.""" + return str(argname) + str(idx) + + def _complain_multiple_hidden_parameter_sets(self) -> NoReturn: + fail( + f"{self._make_error_prefix()}multiple instances of HIDDEN_PARAM " + "cannot be used in the same parametrize call, " + "because the tests names need to be unique." + ) + + def _make_error_prefix(self) -> str: + if self.func_name is not None: + return f"In {self.func_name}: " + elif self.nodeid is not None: + return f"In {self.nodeid}: " + else: + return "" + + +@final +@dataclasses.dataclass(frozen=True) +class CallSpec2: + """A planned parameterized invocation of a test function. + + Calculated during collection for a given test function's Metafunc. + Once collection is over, each callspec is turned into a single Item + and stored in item.callspec. + """ + + # arg name -> arg value which will be passed to a fixture or pseudo-fixture + # of the same name. (indirect or direct parametrization respectively) + params: dict[str, object] = dataclasses.field(default_factory=dict) + # arg name -> arg index. + indices: dict[str, int] = dataclasses.field(default_factory=dict) + # arg name -> parameter scope. + # Used for sorting parametrized resources. + _arg2scope: Mapping[str, Scope] = dataclasses.field(default_factory=dict) + # Parts which will be added to the item's name in `[..]` separated by "-". + _idlist: Sequence[str] = dataclasses.field(default_factory=tuple) + # Marks which will be applied to the item. + marks: list[Mark] = dataclasses.field(default_factory=list) + + def setmulti( + self, + *, + argnames: Iterable[str], + valset: Iterable[object], + id: str | _HiddenParam, + marks: Iterable[Mark | MarkDecorator], + scope: Scope, + param_index: int, + nodeid: str, + ) -> CallSpec2: + params = self.params.copy() + indices = self.indices.copy() + arg2scope = dict(self._arg2scope) + for arg, val in zip(argnames, valset, strict=True): + if arg in params: + raise nodes.Collector.CollectError( + f"{nodeid}: duplicate parametrization of {arg!r}" + ) + params[arg] = val + indices[arg] = param_index + arg2scope[arg] = scope + return CallSpec2( + params=params, + indices=indices, + _arg2scope=arg2scope, + _idlist=self._idlist if id is HIDDEN_PARAM else [*self._idlist, id], + marks=[*self.marks, *normalize_mark_list(marks)], + ) + + def getparam(self, name: str) -> object: + try: + return self.params[name] + except KeyError as e: + raise ValueError(name) from e + + @property + def id(self) -> str: + return "-".join(self._idlist) + + +def get_direct_param_fixture_func(request: FixtureRequest) -> Any: + return request.param + + +# Used for storing pseudo fixturedefs for direct parametrization. +name2pseudofixturedef_key = StashKey[dict[str, FixtureDef[Any]]]() + + +@final +class Metafunc: + """Objects passed to the :hook:`pytest_generate_tests` hook. + + They help to inspect a test function and to generate tests according to + test configuration or values specified in the class or module where a + test function is defined. + """ + + def __init__( + self, + definition: FunctionDefinition, + fixtureinfo: fixtures.FuncFixtureInfo, + config: Config, + cls=None, + module=None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + + #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. + self.definition = definition + + #: Access to the :class:`pytest.Config` object for the test session. + self.config = config + + #: The module object where the test function is defined in. + self.module = module + + #: Underlying Python test function. + self.function = definition.obj + + #: Set of fixture names required by the test function. + self.fixturenames = fixtureinfo.names_closure + + #: Class object where the test function is defined in or ``None``. + self.cls = cls + + self._arg2fixturedefs = fixtureinfo.name2fixturedefs + + # Result of parametrize(). + self._calls: list[CallSpec2] = [] + + self._params_directness: dict[str, Literal["indirect", "direct"]] = {} + + def parametrize( + self, + argnames: str | Sequence[str], + argvalues: Iterable[ParameterSet | Sequence[object] | object], + indirect: bool | Sequence[str] = False, + ids: Iterable[object | None] | Callable[[Any], object | None] | None = None, + scope: _ScopeName | None = None, + *, + _param_mark: Mark | None = None, + ) -> None: + """Add new invocations to the underlying test function using the list + of argvalues for the given argnames. Parametrization is performed + during the collection phase. If you need to setup expensive resources + see about setting ``indirect`` to do it at test setup time instead. + + Can be called multiple times per test function (but only on different + argument names), in which case each call parametrizes all previous + parametrizations, e.g. + + :: + + unparametrized: t + parametrize ["x", "y"]: t[x], t[y] + parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2] + + :param argnames: + A comma-separated string denoting one or more argument names, or + a list/tuple of argument strings. + + :param argvalues: + The list of argvalues determines how often a test is invoked with + different argument values. + + If only one argname was specified argvalues is a list of values. + If N argnames were specified, argvalues must be a list of + N-tuples, where each tuple-element specifies a value for its + respective argname. + + :param indirect: + A list of arguments' names (subset of argnames) or a boolean. + If True the list contains all names from the argnames. Each + argvalue corresponding to an argname in this list will + be passed as request.param to its respective argname fixture + function so that it can perform more expensive setups during the + setup phase of a test rather than at collection time. + + :param ids: + Sequence of (or generator for) ids for ``argvalues``, + or a callable to return part of the id for each argvalue. + + With sequences (and generators like ``itertools.count()``) the + returned ids should be of type ``string``, ``int``, ``float``, + ``bool``, or ``None``. + They are mapped to the corresponding index in ``argvalues``. + ``None`` means to use the auto-generated id. + + .. versionadded:: 8.4 + :ref:`hidden-param` means to hide the parameter set + from the test name. Can only be used at most 1 time, as + test names need to be unique. + + If it is a callable it will be called for each entry in + ``argvalues``, and the return value is used as part of the + auto-generated id for the whole set (where parts are joined with + dashes ("-")). + This is useful to provide more specific ids for certain items, e.g. + dates. Returning ``None`` will use an auto-generated id. + + If no ids are provided they will be generated automatically from + the argvalues. + + :param scope: + If specified it denotes the scope of the parameters. + The scope is used for grouping tests by parameter instances. + It will also override any fixture-function defined scope, allowing + to set a dynamic scope using test context or configuration. + """ + nodeid = self.definition.nodeid + + argnames, parametersets = ParameterSet._for_parametrize( + argnames, + argvalues, + self.function, + self.config, + nodeid=self.definition.nodeid, + ) + del argvalues + + if "request" in argnames: + fail( + f"{nodeid}: 'request' is a reserved name and cannot be used in @pytest.mark.parametrize", + pytrace=False, + ) + + if scope is not None: + scope_ = Scope.from_user( + scope, descr=f"parametrize() call in {self.function.__name__}" + ) + else: + scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + + self._validate_if_using_arg_names(argnames, indirect) + + # Use any already (possibly) generated ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from: + generated_ids = _param_mark._param_ids_from._param_ids_generated + if generated_ids is not None: + ids = generated_ids + + ids = self._resolve_parameter_set_ids( + argnames, ids, parametersets, nodeid=self.definition.nodeid + ) + + # Store used (possibly generated) ids with parametrize Marks. + if _param_mark and _param_mark._param_ids_from and generated_ids is None: + object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) + + # Calculate directness. + arg_directness = self._resolve_args_directness(argnames, indirect) + self._params_directness.update(arg_directness) + + # Add direct parametrizations as fixturedefs to arg2fixturedefs by + # registering artificial "pseudo" FixtureDef's such that later at test + # setup time we can rely on FixtureDefs to exist for all argnames. + node = None + # For scopes higher than function, a "pseudo" FixtureDef might have + # already been created for the scope. We thus store and cache the + # FixtureDef on the node related to the scope. + if scope_ is Scope.Function: + name2pseudofixturedef = None + else: + collector = self.definition.parent + assert collector is not None + node = get_scope_node(collector, scope_) + if node is None: + # If used class scope and there is no class, use module-level + # collector (for now). + if scope_ is Scope.Class: + assert isinstance(collector, Module) + node = collector + # If used package scope and there is no package, use session + # (for now). + elif scope_ is Scope.Package: + node = collector.session + else: + assert False, f"Unhandled missing scope: {scope}" + default: dict[str, FixtureDef[Any]] = {} + name2pseudofixturedef = node.stash.setdefault( + name2pseudofixturedef_key, default + ) + for argname in argnames: + if arg_directness[argname] == "indirect": + continue + if name2pseudofixturedef is not None and argname in name2pseudofixturedef: + fixturedef = name2pseudofixturedef[argname] + else: + fixturedef = FixtureDef( + config=self.config, + baseid="", + argname=argname, + func=get_direct_param_fixture_func, + scope=scope_, + params=None, + ids=None, + _ispytest=True, + ) + if name2pseudofixturedef is not None: + name2pseudofixturedef[argname] = fixturedef + self._arg2fixturedefs[argname] = [fixturedef] + + # Create the new calls: if we are parametrize() multiple times (by applying the decorator + # more than once) then we accumulate those calls generating the cartesian product + # of all calls. + newcalls = [] + for callspec in self._calls or [CallSpec2()]: + for param_index, (param_id, param_set) in enumerate( + zip(ids, parametersets, strict=True) + ): + newcallspec = callspec.setmulti( + argnames=argnames, + valset=param_set.values, + id=param_id, + marks=param_set.marks, + scope=scope_, + param_index=param_index, + nodeid=nodeid, + ) + newcalls.append(newcallspec) + self._calls = newcalls + + def _resolve_parameter_set_ids( + self, + argnames: Sequence[str], + ids: Iterable[object | None] | Callable[[Any], object | None] | None, + parametersets: Sequence[ParameterSet], + nodeid: str, + ) -> list[str | _HiddenParam]: + """Resolve the actual ids for the given parameter sets. + + :param argnames: + Argument names passed to ``parametrize()``. + :param ids: + The `ids` parameter of the ``parametrize()`` call (see docs). + :param parametersets: + The parameter sets, each containing a set of values corresponding + to ``argnames``. + :param nodeid str: + The nodeid of the definition item that generated this + parametrization. + :returns: + List with ids for each parameter set given. + """ + if ids is None: + idfn = None + ids_ = None + elif callable(ids): + idfn = ids + ids_ = None + else: + idfn = None + ids_ = self._validate_ids(ids, parametersets, self.function.__name__) + id_maker = IdMaker( + argnames, + parametersets, + idfn, + ids_, + self.config, + nodeid=nodeid, + func_name=self.function.__name__, + ) + return id_maker.make_unique_parameterset_ids() + + def _validate_ids( + self, + ids: Iterable[object | None], + parametersets: Sequence[ParameterSet], + func_name: str, + ) -> list[object | None]: + try: + num_ids = len(ids) # type: ignore[arg-type] + except TypeError: + try: + iter(ids) + except TypeError as e: + raise TypeError("ids must be a callable or an iterable") from e + num_ids = len(parametersets) + + # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 + if num_ids != len(parametersets) and num_ids != 0: + msg = "In {}: {} parameter sets specified, with different number of ids: {}" + fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False) + + return list(itertools.islice(ids, num_ids)) + + def _resolve_args_directness( + self, + argnames: Sequence[str], + indirect: bool | Sequence[str], + ) -> dict[str, Literal["indirect", "direct"]]: + """Resolve if each parametrized argument must be considered an indirect + parameter to a fixture of the same name, or a direct parameter to the + parametrized function, based on the ``indirect`` parameter of the + parametrized() call. + + :param argnames: + List of argument names passed to ``parametrize()``. + :param indirect: + Same as the ``indirect`` parameter of ``parametrize()``. + :returns + A dict mapping each arg name to either "indirect" or "direct". + """ + arg_directness: dict[str, Literal["indirect", "direct"]] + if isinstance(indirect, bool): + arg_directness = dict.fromkeys( + argnames, "indirect" if indirect else "direct" + ) + elif isinstance(indirect, Sequence): + arg_directness = dict.fromkeys(argnames, "direct") + for arg in indirect: + if arg not in argnames: + fail( + f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", + pytrace=False, + ) + arg_directness[arg] = "indirect" + else: + fail( + f"In {self.function.__name__}: expected Sequence or boolean" + f" for indirect, got {type(indirect).__name__}", + pytrace=False, + ) + return arg_directness + + def _validate_if_using_arg_names( + self, + argnames: Sequence[str], + indirect: bool | Sequence[str], + ) -> None: + """Check if all argnames are being used, by default values, or directly/indirectly. + + :param List[str] argnames: List of argument names passed to ``parametrize()``. + :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. + :raises ValueError: If validation fails. + """ + default_arg_names = set(get_default_arg_names(self.function)) + func_name = self.function.__name__ + for arg in argnames: + if arg not in self.fixturenames: + if arg in default_arg_names: + fail( + f"In {func_name}: function already takes an argument '{arg}' with a default value", + pytrace=False, + ) + else: + if isinstance(indirect, Sequence): + name = "fixture" if arg in indirect else "argument" + else: + name = "fixture" if indirect else "argument" + fail( + f"In {func_name}: function uses no {name} '{arg}'", + pytrace=False, + ) + + def _recompute_direct_params_indices(self) -> None: + for argname, param_type in self._params_directness.items(): + if param_type == "direct": + for i, callspec in enumerate(self._calls): + callspec.indices[argname] = i + + +def _find_parametrized_scope( + argnames: Sequence[str], + arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], + indirect: bool | Sequence[str], +) -> Scope: + """Find the most appropriate scope for a parametrized call based on its arguments. + + When there's at least one direct argument, always use "function" scope. + + When a test function is parametrized and all its arguments are indirect + (e.g. fixtures), return the most narrow scope based on the fixtures used. + + Related to issue #1832, based on code posted by @Kingdread. + """ + if isinstance(indirect, Sequence): + all_arguments_are_fixtures = len(indirect) == len(argnames) + else: + all_arguments_are_fixtures = bool(indirect) + + if all_arguments_are_fixtures: + fixturedefs = arg2fixturedefs or {} + used_scopes = [ + fixturedef[-1]._scope + for name, fixturedef in fixturedefs.items() + if name in argnames + ] + # Takes the most narrow scope from used fixtures. + return min(used_scopes, default=Scope.Function) + + return Scope.Function + + +def _ascii_escaped_by_config(val: str | bytes, config: Config | None) -> str: + if config is None: + escape_option = False + else: + escape_option = config.getini( + "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" + ) + # TODO: If escaping is turned off and the user passes bytes, + # will return a bytes. For now we ignore this but the + # code *probably* doesn't handle this case. + return val if escape_option else ascii_escaped(val) # type: ignore + + +class Function(PyobjMixin, nodes.Item): + """Item responsible for setting up and executing a Python test function. + + :param name: + The full function name, including any decorations like those + added by parametrization (``my_func[my_param]``). + :param parent: + The parent Node. + :param config: + The pytest Config object. + :param callspec: + If given, this function has been parametrized and the callspec contains + meta information about the parametrization. + :param callobj: + If given, the object which will be called when the Function is invoked, + otherwise the callobj will be obtained from ``parent`` using ``originalname``. + :param keywords: + Keywords bound to the function object for "-k" matching. + :param session: + The pytest Session object. + :param fixtureinfo: + Fixture information already resolved at this fixture node.. + :param originalname: + The attribute name to use for accessing the underlying function object. + Defaults to ``name``. Set this if name is different from the original name, + for example when it contains decorations like those added by parametrization + (``my_func[my_param]``). + """ + + # Disable since functions handle it themselves. + _ALLOW_MARKERS = False + + def __init__( + self, + name: str, + parent, + config: Config | None = None, + callspec: CallSpec2 | None = None, + callobj=NOTSET, + keywords: Mapping[str, Any] | None = None, + session: Session | None = None, + fixtureinfo: FuncFixtureInfo | None = None, + originalname: str | None = None, + ) -> None: + super().__init__(name, parent, config=config, session=session) + + if callobj is not NOTSET: + self._obj = callobj + self._instance = getattr(callobj, "__self__", None) + + #: Original function name, without any decorations (for example + #: parametrization adds a ``"[...]"`` suffix to function names), used to access + #: the underlying function object from ``parent`` (in case ``callobj`` is not given + #: explicitly). + #: + #: .. versionadded:: 3.0 + self.originalname = originalname or name + + # Note: when FunctionDefinition is introduced, we should change ``originalname`` + # to a readonly property that returns FunctionDefinition.name. + + self.own_markers.extend(get_unpacked_marks(self.obj)) + if callspec: + self.callspec = callspec + self.own_markers.extend(callspec.marks) + + # todo: this is a hell of a hack + # https://github.com/pytest-dev/pytest/issues/4569 + # Note: the order of the updates is important here; indicates what + # takes priority (ctor argument over function attributes over markers). + # Take own_markers only; NodeKeywords handles parent traversal on its own. + self.keywords.update((mark.name, mark) for mark in self.own_markers) + self.keywords.update(self.obj.__dict__) + if keywords: + self.keywords.update(keywords) + + if fixtureinfo is None: + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) + self._fixtureinfo: FuncFixtureInfo = fixtureinfo + self.fixturenames = fixtureinfo.names_closure + self._initrequest() + + # todo: determine sound type limitations + @classmethod + def from_parent(cls, parent, **kw) -> Self: + """The public constructor.""" + return super().from_parent(parent=parent, **kw) + + def _initrequest(self) -> None: + self.funcargs: dict[str, object] = {} + self._request = fixtures.TopRequest(self, _ispytest=True) + + @property + def function(self): + """Underlying python 'function' object.""" + return getimfunc(self.obj) + + @property + def instance(self): + try: + return self._instance + except AttributeError: + if isinstance(self.parent, Class): + # Each Function gets a fresh class instance. + self._instance = self._getinstance() + else: + self._instance = None + return self._instance + + def _getinstance(self): + if isinstance(self.parent, Class): + # Each Function gets a fresh class instance. + return self.parent.newinstance() + else: + return None + + def _getobj(self): + instance = self.instance + if instance is not None: + parent_obj = instance + else: + assert self.parent is not None + parent_obj = self.parent.obj # type: ignore[attr-defined] + return getattr(parent_obj, self.originalname) + + @property + def _pyfuncitem(self): + """(compatonly) for code expecting pytest-2.2 style request objects.""" + return self + + def runtest(self) -> None: + """Execute the underlying test function.""" + self.ihook.pytest_pyfunc_call(pyfuncitem=self) + + def setup(self) -> None: + self._request._fillfixtures() + + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): + code = _pytest._code.Code.from_function(get_real_func(self.obj)) + path, firstlineno = code.path, code.firstlineno + traceback = excinfo.traceback + ntraceback = traceback.cut(path=path, firstlineno=firstlineno) + if ntraceback == traceback: + ntraceback = ntraceback.cut(path=path) + if ntraceback == traceback: + ntraceback = ntraceback.filter(filter_traceback) + if not ntraceback: + ntraceback = traceback + ntraceback = ntraceback.filter(excinfo) + + # issue364: mark all but first and last frames to + # only show a single-line message for each frame. + if self.config.getoption("tbstyle", "auto") == "auto": + if len(ntraceback) > 2: + ntraceback = Traceback( + ( + ntraceback[0], + *(t.with_repr_style("short") for t in ntraceback[1:-1]), + ntraceback[-1], + ) + ) + + return ntraceback + return excinfo.traceback + + # TODO: Type ignored -- breaks Liskov Substitution. + def repr_failure( # type: ignore[override] + self, + excinfo: ExceptionInfo[BaseException], + ) -> str | TerminalRepr: + style = self.config.getoption("tbstyle", "auto") + if style == "auto": + style = "long" + return self._repr_failure_py(excinfo, style=style) + + +class FunctionDefinition(Function): + """This class is a stop gap solution until we evolve to have actual function + definition nodes and manage to get rid of ``metafunc``.""" + + def runtest(self) -> None: + raise RuntimeError("function definitions are not supposed to be run as tests") + + setup = runtest diff --git a/.venv/lib/python3.12/site-packages/_pytest/python_api.py b/.venv/lib/python3.12/site-packages/_pytest/python_api.py new file mode 100644 index 0000000..bab70aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/python_api.py @@ -0,0 +1,819 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Collection +from collections.abc import Mapping +from collections.abc import Sequence +from collections.abc import Sized +from decimal import Decimal +import math +from numbers import Complex +import pprint +import sys +from typing import Any +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from numpy import ndarray + + +def _compare_approx( + full_object: object, + message_data: Sequence[tuple[str, str, str]], + number_of_elements: int, + different_ids: Sequence[object], + max_abs_diff: float, + max_rel_diff: float, +) -> list[str]: + message_list = list(message_data) + message_list.insert(0, ("Index", "Obtained", "Expected")) + max_sizes = [0, 0, 0] + for index, obtained, expected in message_list: + max_sizes[0] = max(max_sizes[0], len(index)) + max_sizes[1] = max(max_sizes[1], len(obtained)) + max_sizes[2] = max(max_sizes[2], len(expected)) + explanation = [ + f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", + f"Max absolute difference: {max_abs_diff}", + f"Max relative difference: {max_rel_diff}", + ] + [ + f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" + for indexes, obtained, expected in message_list + ] + return explanation + + +# builtin pytest.approx helper + + +class ApproxBase: + """Provide shared utilities for making approximate comparisons between + numbers or sequences of numbers.""" + + # Tell numpy to use our `__eq__` operator instead of its. + __array_ufunc__ = None + __array_priority__ = 100 + + def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: + __tracebackhide__ = True + self.expected = expected + self.abs = abs + self.rel = rel + self.nan_ok = nan_ok + self._check_type() + + def __repr__(self) -> str: + raise NotImplementedError + + def _repr_compare(self, other_side: Any) -> list[str]: + return [ + "comparison failed", + f"Obtained: {other_side}", + f"Expected: {self}", + ] + + def __eq__(self, actual) -> bool: + return all( + a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) + ) + + def __bool__(self): + __tracebackhide__ = True + raise AssertionError( + "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" + ) + + # Ignore type because of https://github.com/python/mypy/issues/4266. + __hash__ = None # type: ignore + + def __ne__(self, actual) -> bool: + return not (actual == self) + + def _approx_scalar(self, x) -> ApproxScalar: + if isinstance(x, Decimal): + return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + + def _yield_comparisons(self, actual): + """Yield all the pairs of numbers to be compared. + + This is used to implement the `__eq__` method. + """ + raise NotImplementedError + + def _check_type(self) -> None: + """Raise a TypeError if the expected value is not a valid type.""" + # This is only a concern if the expected value is a sequence. In every + # other case, the approx() function ensures that the expected value has + # a numeric type. For this reason, the default is to do nothing. The + # classes that deal with sequences should reimplement this method to + # raise if there are any non-numeric elements in the sequence. + + +def _recursive_sequence_map(f, x): + """Recursively map a function over a sequence of arbitrary depth""" + if isinstance(x, list | tuple): + seq_type = type(x) + return seq_type(_recursive_sequence_map(f, xi) for xi in x) + elif _is_sequence_like(x): + return [_recursive_sequence_map(f, xi) for xi in x] + else: + return f(x) + + +class ApproxNumpy(ApproxBase): + """Perform approximate comparisons where the expected value is numpy array.""" + + def __repr__(self) -> str: + list_scalars = _recursive_sequence_map( + self._approx_scalar, self.expected.tolist() + ) + return f"approx({list_scalars!r})" + + def _repr_compare(self, other_side: ndarray | list[Any]) -> list[str]: + import itertools + import math + + def get_value_from_nested_list( + nested_list: list[Any], nd_index: tuple[Any, ...] + ) -> Any: + """ + Helper function to get the value out of a nested list, given an n-dimensional index. + This mimics numpy's indexing, but for raw nested python lists. + """ + value: Any = nested_list + for i in nd_index: + value = value[i] + return value + + np_array_shape = self.expected.shape + approx_side_as_seq = _recursive_sequence_map( + self._approx_scalar, self.expected.tolist() + ) + + # convert other_side to numpy array to ensure shape attribute is available + other_side_as_array = _as_numpy_array(other_side) + assert other_side_as_array is not None + + if np_array_shape != other_side_as_array.shape: + return [ + "Impossible to compare arrays with different shapes.", + f"Shapes: {np_array_shape} and {other_side_as_array.shape}", + ] + + number_of_elements = self.expected.size + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for index in itertools.product(*(range(i) for i in np_array_shape)): + approx_value = get_value_from_nested_list(approx_side_as_seq, index) + other_value = get_value_from_nested_list(other_side_as_array, index) + if approx_value != other_value: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + if other_value == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(index) + + message_data = [ + ( + str(index), + str(get_value_from_nested_list(other_side_as_array, index)), + str(get_value_from_nested_list(approx_side_as_seq, index)), + ) + for index in different_ids + ] + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + import numpy as np + + # self.expected is supposed to always be an array here. + + if not np.isscalar(actual): + try: + actual = np.asarray(actual) + except Exception as e: + raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e + + if not np.isscalar(actual) and actual.shape != self.expected.shape: + return False + + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + import numpy as np + + # `actual` can either be a numpy array or a scalar, it is treated in + # `__eq__` before being passed to `ApproxBase.__eq__`, which is the + # only method that calls this one. + + if np.isscalar(actual): + for i in np.ndindex(self.expected.shape): + yield actual, self.expected[i].item() + else: + for i in np.ndindex(self.expected.shape): + yield actual[i].item(), self.expected[i].item() + + +class ApproxMapping(ApproxBase): + """Perform approximate comparisons where the expected value is a mapping + with numeric values (the keys can be anything).""" + + def __repr__(self) -> str: + return f"approx({ ({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})" + + def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: + import math + + if len(self.expected) != len(other_side): + return [ + "Impossible to compare mappings with different sizes.", + f"Lengths: {len(self.expected)} and {len(other_side)}", + ] + + if self.expected.keys() != other_side.keys(): + return [ + "comparison failed.", + f"Mappings has different keys: expected {self.expected.keys()} but got {other_side.keys()}", + ] + + approx_side_as_map = { + k: self._approx_scalar(v) for k, v in self.expected.items() + } + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for approx_key, approx_value in approx_side_as_map.items(): + other_value = other_side[approx_key] + if approx_value != other_value: + if approx_value.expected is not None and other_value is not None: + try: + max_abs_diff = max( + max_abs_diff, abs(approx_value.expected - other_value) + ) + if approx_value.expected == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max( + max_rel_diff, + abs( + (approx_value.expected - other_value) + / approx_value.expected + ), + ) + except ZeroDivisionError: + pass + different_ids.append(approx_key) + + message_data = [ + (str(key), str(other_side[key]), str(approx_side_as_map[key])) + for key in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + try: + if set(actual.keys()) != set(self.expected.keys()): + return False + except AttributeError: + return False + + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + for k in self.expected.keys(): + yield actual[k], self.expected[k] + + def _check_type(self) -> None: + __tracebackhide__ = True + for key, value in self.expected.items(): + if isinstance(value, type(self.expected)): + msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" + raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) + + +class ApproxSequenceLike(ApproxBase): + """Perform approximate comparisons where the expected value is a sequence of numbers.""" + + def __repr__(self) -> str: + seq_type = type(self.expected) + if seq_type not in (tuple, list): + seq_type = list + return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})" + + def _repr_compare(self, other_side: Sequence[float]) -> list[str]: + import math + + if len(self.expected) != len(other_side): + return [ + "Impossible to compare lists with different sizes.", + f"Lengths: {len(self.expected)} and {len(other_side)}", + ] + + approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected) + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for i, (approx_value, other_value) in enumerate( + zip(approx_side_as_map, other_side, strict=True) + ): + if approx_value != other_value: + try: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + # Ignore non-numbers for the diff calculations (#13012). + except TypeError: + pass + else: + if other_value == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(i) + message_data = [ + (str(i), str(other_side[i]), str(approx_side_as_map[i])) + for i in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + + def __eq__(self, actual) -> bool: + try: + if len(actual) != len(self.expected): + return False + except TypeError: + return False + return super().__eq__(actual) + + def _yield_comparisons(self, actual): + return zip(actual, self.expected, strict=True) + + def _check_type(self) -> None: + __tracebackhide__ = True + for index, x in enumerate(self.expected): + if isinstance(x, type(self.expected)): + msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" + raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) + + +class ApproxScalar(ApproxBase): + """Perform approximate comparisons where the expected value is a single number.""" + + # Using Real should be better than this Union, but not possible yet: + # https://github.com/python/typeshed/pull/3108 + DEFAULT_ABSOLUTE_TOLERANCE: float | Decimal = 1e-12 + DEFAULT_RELATIVE_TOLERANCE: float | Decimal = 1e-6 + + def __repr__(self) -> str: + """Return a string communicating both the expected value and the + tolerance for the comparison being made. + + For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. + """ + # Don't show a tolerance for values that aren't compared using + # tolerances, i.e. non-numerics and infinities. Need to call abs to + # handle complex numbers, e.g. (inf + 1j). + if ( + isinstance(self.expected, bool) + or (not isinstance(self.expected, Complex | Decimal)) + or math.isinf(abs(self.expected) or isinstance(self.expected, bool)) + ): + return str(self.expected) + + # If a sensible tolerance can't be calculated, self.tolerance will + # raise a ValueError. In this case, display '???'. + try: + if 1e-3 <= self.tolerance < 1e3: + vetted_tolerance = f"{self.tolerance:n}" + else: + vetted_tolerance = f"{self.tolerance:.1e}" + + if ( + isinstance(self.expected, Complex) + and self.expected.imag + and not math.isinf(self.tolerance) + ): + vetted_tolerance += " ∠ ±180°" + except ValueError: + vetted_tolerance = "???" + + return f"{self.expected} ± {vetted_tolerance}" + + def __eq__(self, actual) -> bool: + """Return whether the given value is equal to the expected value + within the pre-specified tolerance.""" + + def is_bool(val: Any) -> bool: + # Check if `val` is a native bool or numpy bool. + if isinstance(val, bool): + return True + if np := sys.modules.get("numpy"): + return isinstance(val, np.bool_) + return False + + asarray = _as_numpy_array(actual) + if asarray is not None: + # Call ``__eq__()`` manually to prevent infinite-recursion with + # numpy<1.13. See #3748. + return all(self.__eq__(a) for a in asarray.flat) + + # Short-circuit exact equality, except for bool and np.bool_ + if is_bool(self.expected) and not is_bool(actual): + return False + elif actual == self.expected: + return True + + # If either type is non-numeric, fall back to strict equality. + # NB: we need Complex, rather than just Number, to ensure that __abs__, + # __sub__, and __float__ are defined. Also, consider bool to be + # non-numeric, even though it has the required arithmetic. + if is_bool(self.expected) or not ( + isinstance(self.expected, Complex | Decimal) + and isinstance(actual, Complex | Decimal) + ): + return False + + # Allow the user to control whether NaNs are considered equal to each + # other or not. The abs() calls are for compatibility with complex + # numbers. + if math.isnan(abs(self.expected)): + return self.nan_ok and math.isnan(abs(actual)) + + # Infinity shouldn't be approximately equal to anything but itself, but + # if there's a relative tolerance, it will be infinite and infinity + # will seem approximately equal to everything. The equal-to-itself + # case would have been short circuited above, so here we can just + # return false if the expected value is infinite. The abs() call is + # for compatibility with complex numbers. + if math.isinf(abs(self.expected)): + return False + + # Return true if the two numbers are within the tolerance. + result: bool = abs(self.expected - actual) <= self.tolerance + return result + + __hash__ = None + + @property + def tolerance(self): + """Return the tolerance for the comparison. + + This could be either an absolute tolerance or a relative tolerance, + depending on what the user specified or which would be larger. + """ + + def set_default(x, default): + return x if x is not None else default + + # Figure out what the absolute tolerance should be. ``self.abs`` is + # either None or a value specified by the user. + absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) + + if absolute_tolerance < 0: + raise ValueError( + f"absolute tolerance can't be negative: {absolute_tolerance}" + ) + if math.isnan(absolute_tolerance): + raise ValueError("absolute tolerance can't be NaN.") + + # If the user specified an absolute tolerance but not a relative one, + # just return the absolute tolerance. + if self.rel is None: + if self.abs is not None: + return absolute_tolerance + + # Figure out what the relative tolerance should be. ``self.rel`` is + # either None or a value specified by the user. This is done after + # we've made sure the user didn't ask for an absolute tolerance only, + # because we don't want to raise errors about the relative tolerance if + # we aren't even going to use it. + relative_tolerance = set_default( + self.rel, self.DEFAULT_RELATIVE_TOLERANCE + ) * abs(self.expected) + + if relative_tolerance < 0: + raise ValueError( + f"relative tolerance can't be negative: {relative_tolerance}" + ) + if math.isnan(relative_tolerance): + raise ValueError("relative tolerance can't be NaN.") + + # Return the larger of the relative and absolute tolerances. + return max(relative_tolerance, absolute_tolerance) + + +class ApproxDecimal(ApproxScalar): + """Perform approximate comparisons where the expected value is a Decimal.""" + + DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") + DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") + + def __repr__(self) -> str: + if isinstance(self.rel, float): + rel = Decimal.from_float(self.rel) + else: + rel = self.rel + + if isinstance(self.abs, float): + abs_ = Decimal.from_float(self.abs) + else: + abs_ = self.abs + + tol_str = "???" + if rel is not None and Decimal("1e-3") <= rel <= Decimal("1e3"): + tol_str = f"{rel:.1e}" + elif abs_ is not None: + tol_str = f"{abs_:.1e}" + + return f"{self.expected} ± {tol_str}" + + +def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: + """Assert that two numbers (or two ordered sequences of numbers) are equal to each other + within some tolerance. + + Due to the :doc:`python:tutorial/floatingpoint`, numbers that we + would intuitively expect to be equal are not always so:: + + >>> 0.1 + 0.2 == 0.3 + False + + This problem is commonly encountered when writing tests, e.g. when making + sure that floating-point values are what you expect them to be. One way to + deal with this problem is to assert that two floating-point numbers are + equal to within some appropriate tolerance:: + + >>> abs((0.1 + 0.2) - 0.3) < 1e-6 + True + + However, comparisons like this are tedious to write and difficult to + understand. Furthermore, absolute comparisons like the one above are + usually discouraged because there's no tolerance that works well for all + situations. ``1e-6`` is good for numbers around ``1``, but too small for + very big numbers and too big for very small ones. It's better to express + the tolerance as a fraction of the expected value, but relative comparisons + like that are even more difficult to write correctly and concisely. + + The ``approx`` class performs floating-point comparisons using a syntax + that's as intuitive as possible:: + + >>> from pytest import approx + >>> 0.1 + 0.2 == approx(0.3) + True + + The same syntax also works for ordered sequences of numbers:: + + >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) + True + + ``numpy`` arrays:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP + True + + And for a ``numpy`` array against a scalar:: + + >>> import numpy as np # doctest: +SKIP + >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP + True + + Only ordered sequences are supported, because ``approx`` needs + to infer the relative position of the sequences without ambiguity. This means + ``sets`` and other unordered sequences are not supported. + + Finally, dictionary *values* can also be compared:: + + >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) + True + + The comparison will be true if both mappings have the same keys and their + respective values match the expected tolerances. + + **Tolerances** + + By default, ``approx`` considers numbers within a relative tolerance of + ``1e-6`` (i.e. one part in a million) of its expected value to be equal. + This treatment would lead to surprising results if the expected value was + ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. + To handle this case less surprisingly, ``approx`` also considers numbers + within an absolute tolerance of ``1e-12`` of its expected value to be + equal. Infinity and NaN are special cases. Infinity is only considered + equal to itself, regardless of the relative tolerance. NaN is not + considered equal to anything by default, but you can make it be equal to + itself by setting the ``nan_ok`` argument to True. (This is meant to + facilitate comparing arrays that use NaN to mean "no data".) + + Both the relative and absolute tolerances can be changed by passing + arguments to the ``approx`` constructor:: + + >>> 1.0001 == approx(1) + False + >>> 1.0001 == approx(1, rel=1e-3) + True + >>> 1.0001 == approx(1, abs=1e-3) + True + + If you specify ``abs`` but not ``rel``, the comparison will not consider + the relative tolerance at all. In other words, two numbers that are within + the default relative tolerance of ``1e-6`` will still be considered unequal + if they exceed the specified absolute tolerance. If you specify both + ``abs`` and ``rel``, the numbers will be considered equal if either + tolerance is met:: + + >>> 1 + 1e-8 == approx(1) + True + >>> 1 + 1e-8 == approx(1, abs=1e-12) + False + >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) + True + + **Non-numeric types** + + You can also use ``approx`` to compare non-numeric types, or dicts and + sequences containing non-numeric types, in which case it falls back to + strict equality. This can be useful for comparing dicts and sequences that + can contain optional values:: + + >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) + True + >>> [None, 1.0000005] == approx([None,1]) + True + >>> ["foo", 1.0000005] == approx([None,1]) + False + + If you're thinking about using ``approx``, then you might want to know how + it compares to other good ways of comparing floating-point numbers. All of + these algorithms are based on relative and absolute tolerances and should + agree for the most part, but they do have meaningful differences: + + - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative + tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute + tolerance is met. Because the relative tolerance is calculated w.r.t. + both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor + ``b`` is a "reference value"). You have to specify an absolute tolerance + if you want to compare to ``0.0`` because there is no tolerance by + default. More information: :py:func:`math.isclose`. + + - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference + between ``a`` and ``b`` is less that the sum of the relative tolerance + w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance + is only calculated w.r.t. ``b``, this test is asymmetric and you can + think of ``b`` as the reference value. Support for comparing sequences + is provided by :py:func:`numpy.allclose`. More information: + :std:doc:`numpy:reference/generated/numpy.isclose`. + + - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` + are within an absolute tolerance of ``1e-7``. No relative tolerance is + considered , so this function is not appropriate for very large or very + small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` + and it's ugly because it doesn't follow PEP8. More information: + :py:meth:`unittest.TestCase.assertAlmostEqual`. + + - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative + tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. + Because the relative tolerance is only calculated w.r.t. ``b``, this test + is asymmetric and you can think of ``b`` as the reference value. In the + special case that you explicitly specify an absolute tolerance but not a + relative tolerance, only the absolute tolerance is considered. + + .. note:: + + ``approx`` can handle numpy arrays, but we recommend the + specialised test helpers in :std:doc:`numpy:reference/routines.testing` + if you need support for comparisons, NaNs, or ULP-based tolerances. + + To match strings using regex, you can use + `Matches `_ + from the + `re_assert package `_. + + + .. note:: + + Unlike built-in equality, this function considers + booleans unequal to numeric zero or one. For example:: + + >>> 1 == approx(True) + False + + .. warning:: + + .. versionchanged:: 3.2 + + In order to avoid inconsistent behavior, :py:exc:`TypeError` is + raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. + The example below illustrates the problem:: + + assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) + assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) + + In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` + to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to + comparison. This is because the call hierarchy of rich comparisons + follows a fixed behavior. More information: :py:meth:`object.__ge__` + + .. versionchanged:: 3.7.1 + ``approx`` raises ``TypeError`` when it encounters a dict value or + sequence element of non-numeric type. + + .. versionchanged:: 6.1.0 + ``approx`` falls back to strict equality for non-numeric types instead + of raising ``TypeError``. + """ + # Delegate the comparison to a class that knows how to deal with the type + # of the expected value (e.g. int, float, list, dict, numpy.array, etc). + # + # The primary responsibility of these classes is to implement ``__eq__()`` + # and ``__repr__()``. The former is used to actually check if some + # "actual" value is equivalent to the given expected value within the + # allowed tolerance. The latter is used to show the user the expected + # value and tolerance, in the case that a test failed. + # + # The actual logic for making approximate comparisons can be found in + # ApproxScalar, which is used to compare individual numbers. All of the + # other Approx classes eventually delegate to this class. The ApproxBase + # class provides some convenient methods and overloads, but isn't really + # essential. + + __tracebackhide__ = True + + if isinstance(expected, Decimal): + cls: type[ApproxBase] = ApproxDecimal + elif isinstance(expected, Mapping): + cls = ApproxMapping + elif _is_numpy_array(expected): + expected = _as_numpy_array(expected) + cls = ApproxNumpy + elif _is_sequence_like(expected): + cls = ApproxSequenceLike + elif isinstance(expected, Collection) and not isinstance(expected, str | bytes): + msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}" + raise TypeError(msg) + else: + cls = ApproxScalar + + return cls(expected, rel, abs, nan_ok) + + +def _is_sequence_like(expected: object) -> bool: + return ( + hasattr(expected, "__getitem__") + and isinstance(expected, Sized) + and not isinstance(expected, str | bytes) + ) + + +def _is_numpy_array(obj: object) -> bool: + """ + Return true if the given object is implicitly convertible to ndarray, + and numpy is already imported. + """ + return _as_numpy_array(obj) is not None + + +def _as_numpy_array(obj: object) -> ndarray | None: + """ + Return an ndarray if the given object is implicitly convertible to ndarray, + and numpy is already imported, otherwise None. + """ + np: Any = sys.modules.get("numpy") + if np is not None: + # avoid infinite recursion on numpy scalars, which have __array__ + if np.isscalar(obj): + return None + elif isinstance(obj, np.ndarray): + return obj + elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + return np.asarray(obj) + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/raises.py b/.venv/lib/python3.12/site-packages/_pytest/raises.py new file mode 100644 index 0000000..7c246fd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/raises.py @@ -0,0 +1,1517 @@ +from __future__ import annotations + +from abc import ABC +from abc import abstractmethod +import re +from re import Pattern +import sys +from textwrap import indent +from typing import Any +from typing import cast +from typing import final +from typing import Generic +from typing import get_args +from typing import get_origin +from typing import Literal +from typing import overload +from typing import TYPE_CHECKING +import warnings + +from _pytest._code import ExceptionInfo +from _pytest._code.code import stringify_exception +from _pytest.outcomes import fail +from _pytest.warning_types import PytestWarning + + +if TYPE_CHECKING: + from collections.abc import Callable + from collections.abc import Sequence + + # for some reason Sphinx does not play well with 'from types import TracebackType' + import types + from typing import TypeGuard + + from typing_extensions import ParamSpec + from typing_extensions import TypeVar + + P = ParamSpec("P") + + # this conditional definition is because we want to allow a TypeVar default + BaseExcT_co_default = TypeVar( + "BaseExcT_co_default", + bound=BaseException, + default=BaseException, + covariant=True, + ) + + # Use short name because it shows up in docs. + E = TypeVar("E", bound=BaseException, default=BaseException) +else: + from typing import TypeVar + + BaseExcT_co_default = TypeVar( + "BaseExcT_co_default", bound=BaseException, covariant=True + ) + +# RaisesGroup doesn't work with a default. +BaseExcT_co = TypeVar("BaseExcT_co", bound=BaseException, covariant=True) +BaseExcT_1 = TypeVar("BaseExcT_1", bound=BaseException) +BaseExcT_2 = TypeVar("BaseExcT_2", bound=BaseException) +ExcT_1 = TypeVar("ExcT_1", bound=Exception) +ExcT_2 = TypeVar("ExcT_2", bound=Exception) + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + from exceptiongroup import ExceptionGroup + + +# String patterns default to including the unicode flag. +_REGEX_NO_FLAGS = re.compile(r"").flags + + +# pytest.raises helper +@overload +def raises( + expected_exception: type[E] | tuple[type[E], ...], + *, + match: str | re.Pattern[str] | None = ..., + check: Callable[[E], bool] = ..., +) -> RaisesExc[E]: ... + + +@overload +def raises( + *, + match: str | re.Pattern[str], + # If exception_type is not provided, check() must do any typechecks itself. + check: Callable[[BaseException], bool] = ..., +) -> RaisesExc[BaseException]: ... + + +@overload +def raises(*, check: Callable[[BaseException], bool]) -> RaisesExc[BaseException]: ... + + +@overload +def raises( + expected_exception: type[E] | tuple[type[E], ...], + func: Callable[..., Any], + *args: Any, + **kwargs: Any, +) -> ExceptionInfo[E]: ... + + +def raises( + expected_exception: type[E] | tuple[type[E], ...] | None = None, + *args: Any, + **kwargs: Any, +) -> RaisesExc[BaseException] | ExceptionInfo[E]: + r"""Assert that a code block/function call raises an exception type, or one of its subclasses. + + :param expected_exception: + The expected exception type, or a tuple if one of multiple possible + exception types are expected. Note that subclasses of the passed exceptions + will also match. + + This is not a required parameter, you may opt to only use ``match`` and/or + ``check`` for verifying the raised exception. + + :kwparam str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception and its :pep:`678` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + (This is only used when ``pytest.raises`` is used as a context manager, + and passed through to the function otherwise. + When using ``pytest.raises`` as a function, you can use: + ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) + + :kwparam Callable[[BaseException], bool] check: + + .. versionadded:: 8.4 + + If specified, a callable that will be called with the exception as a parameter + after checking the type and the match regex if specified. + If it returns ``True`` it will be considered a match, if not it will + be considered a failed match. + + + Use ``pytest.raises`` as a context manager, which will capture the exception of the given + type, or any of its subclasses:: + + >>> import pytest + >>> with pytest.raises(ZeroDivisionError): + ... 1/0 + + If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example + above), or no exception at all, the check will fail instead. + + You can also use the keyword argument ``match`` to assert that the + exception matches a text or regex:: + + >>> with pytest.raises(ValueError, match='must be 0 or None'): + ... raise ValueError("value must be 0 or None") + + >>> with pytest.raises(ValueError, match=r'must be \d+$'): + ... raise ValueError("value must be 42") + + The ``match`` argument searches the formatted exception string, which includes any + `PEP-678 `__ ``__notes__``: + + >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP + ... e = ValueError("value must be 42") + ... e.add_note("had a note added") + ... raise e + + The ``check`` argument, if provided, must return True when passed the raised exception + for the match to be successful, otherwise an :exc:`AssertionError` is raised. + + >>> import errno + >>> with pytest.raises(OSError, check=lambda e: e.errno == errno.EACCES): + ... raise OSError(errno.EACCES, "no permission to view") + + The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the + details of the captured exception:: + + >>> with pytest.raises(ValueError) as exc_info: + ... raise ValueError("value must be 42") + >>> assert exc_info.type is ValueError + >>> assert exc_info.value.args[0] == "value must be 42" + + .. warning:: + + Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: + + # Careful, this will catch ANY exception raised. + with pytest.raises(Exception): + some_function() + + Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide + real bugs, where the user wrote this expecting a specific exception, but some other exception is being + raised due to a bug introduced during a refactoring. + + Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch + **any** exception raised. + + .. note:: + + When using ``pytest.raises`` as a context manager, it's worthwhile to + note that normal context manager rules apply and that the exception + raised *must* be the final line in the scope of the context manager. + Lines of code after that, within the scope of the context manager will + not be executed. For example:: + + >>> value = 15 + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... assert exc_info.type is ValueError # This will not execute. + + Instead, the following approach must be taken (note the difference in + scope):: + + >>> with pytest.raises(ValueError) as exc_info: + ... if value > 10: + ... raise ValueError("value must be <= 10") + ... + >>> assert exc_info.type is ValueError + + **Expecting exception groups** + + When expecting exceptions wrapped in :exc:`BaseExceptionGroup` or + :exc:`ExceptionGroup`, you should instead use :class:`pytest.RaisesGroup`. + + **Using with** ``pytest.mark.parametrize`` + + When using :ref:`pytest.mark.parametrize ref` + it is possible to parametrize tests such that + some runs raise an exception and others do not. + + See :ref:`parametrizing_conditional_raising` for an example. + + .. seealso:: + + :ref:`assertraises` for more examples and detailed discussion. + + **Legacy form** + + It is possible to specify a callable by passing a to-be-called lambda:: + + >>> raises(ZeroDivisionError, lambda: 1/0) + + + or you can specify an arbitrary callable with arguments:: + + >>> def f(x): return 1/x + ... + >>> raises(ZeroDivisionError, f, 0) + + >>> raises(ZeroDivisionError, f, x=0) + + + The form above is fully supported but discouraged for new code because the + context manager form is regarded as more readable and less error-prone. + + .. note:: + Similar to caught exception objects in Python, explicitly clearing + local references to returned ``ExceptionInfo`` objects can + help the Python interpreter speed up its garbage collection. + + Clearing those references breaks a reference cycle + (``ExceptionInfo`` --> caught exception --> frame stack raising + the exception --> current frame stack --> local variables --> + ``ExceptionInfo``) which makes Python keep all objects referenced + from that cycle (including all local variables in the current + frame) alive until the next cyclic garbage collection run. + More detailed information can be found in the official Python + documentation for :ref:`the try statement `. + """ + __tracebackhide__ = True + + if not args: + if set(kwargs) - {"match", "check", "expected_exception"}: + msg = "Unexpected keyword arguments passed to pytest.raises: " + msg += ", ".join(sorted(kwargs)) + msg += "\nUse context-manager form instead?" + raise TypeError(msg) + + if expected_exception is None: + return RaisesExc(**kwargs) + return RaisesExc(expected_exception, **kwargs) + + if not expected_exception: + raise ValueError( + f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " + f"Raising exceptions is already understood as failing the test, so you don't need " + f"any special code to say 'this should never raise an exception'." + ) + func = args[0] + if not callable(func): + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + with RaisesExc(expected_exception) as excinfo: + func(*args[1:], **kwargs) + try: + return excinfo + finally: + del excinfo + + +# note: RaisesExc/RaisesGroup uses fail() internally, so this alias +# indicates (to [internal] plugins?) that `pytest.raises` will +# raise `_pytest.outcomes.Failed`, where +# `outcomes.Failed is outcomes.fail.Exception is raises.Exception` +# note: this is *not* the same as `_pytest.main.Failed` +# note: mypy does not recognize this attribute, and it's not possible +# to use a protocol/decorator like the others in outcomes due to +# https://github.com/python/mypy/issues/18715 +raises.Exception = fail.Exception # type: ignore[attr-defined] + + +def _match_pattern(match: Pattern[str]) -> str | Pattern[str]: + """Helper function to remove redundant `re.compile` calls when printing regex""" + return match.pattern if match.flags == _REGEX_NO_FLAGS else match + + +def repr_callable(fun: Callable[[BaseExcT_1], bool]) -> str: + """Get the repr of a ``check`` parameter. + + Split out so it can be monkeypatched (e.g. by hypothesis) + """ + return repr(fun) + + +def backquote(s: str) -> str: + return "`" + s + "`" + + +def _exception_type_name( + e: type[BaseException] | tuple[type[BaseException], ...], +) -> str: + if isinstance(e, type): + return e.__name__ + if len(e) == 1: + return e[0].__name__ + return "(" + ", ".join(ee.__name__ for ee in e) + ")" + + +def _check_raw_type( + expected_type: type[BaseException] | tuple[type[BaseException], ...] | None, + exception: BaseException, +) -> str | None: + if expected_type is None or expected_type == (): + return None + + if not isinstance( + exception, + expected_type, + ): + actual_type_str = backquote(_exception_type_name(type(exception)) + "()") + expected_type_str = backquote(_exception_type_name(expected_type)) + if ( + isinstance(exception, BaseExceptionGroup) + and isinstance(expected_type, type) + and not issubclass(expected_type, BaseExceptionGroup) + ): + return f"Unexpected nested {actual_type_str}, expected {expected_type_str}" + return f"{actual_type_str} is not an instance of {expected_type_str}" + return None + + +def is_fully_escaped(s: str) -> bool: + # we know we won't compile with re.VERBOSE, so whitespace doesn't need to be escaped + metacharacters = "{}()+.*?^$[]" + return not any( + c in metacharacters and (i == 0 or s[i - 1] != "\\") for (i, c) in enumerate(s) + ) + + +def unescape(s: str) -> str: + return re.sub(r"\\([{}()+-.*?^$\[\]\s\\])", r"\1", s) + + +# These classes conceptually differ from ExceptionInfo in that ExceptionInfo is tied, and +# constructed from, a particular exception - whereas these are constructed with expected +# exceptions, and later allow matching towards particular exceptions. +# But there's overlap in `ExceptionInfo.match` and `AbstractRaises._check_match`, as with +# `AbstractRaises.matches` and `ExceptionInfo.errisinstance`+`ExceptionInfo.group_contains`. +# The interaction between these classes should perhaps be improved. +class AbstractRaises(ABC, Generic[BaseExcT_co]): + """ABC with common functionality shared between RaisesExc and RaisesGroup""" + + def __init__( + self, + *, + match: str | Pattern[str] | None, + check: Callable[[BaseExcT_co], bool] | None, + ) -> None: + if isinstance(match, str): + # juggle error in order to avoid context to fail (necessary?) + re_error = None + try: + self.match: Pattern[str] | None = re.compile(match) + except re.error as e: + re_error = e + if re_error is not None: + fail(f"Invalid regex pattern provided to 'match': {re_error}") + if match == "": + warnings.warn( + PytestWarning( + "matching against an empty string will *always* pass. If you want " + "to check for an empty message you need to pass '^$'. If you don't " + "want to match you should pass `None` or leave out the parameter." + ), + stacklevel=2, + ) + else: + self.match = match + + # check if this is a fully escaped regex and has ^$ to match fully + # in which case we can do a proper diff on error + self.rawmatch: str | None = None + if isinstance(match, str) or ( + isinstance(match, Pattern) and match.flags == _REGEX_NO_FLAGS + ): + if isinstance(match, Pattern): + match = match.pattern + if ( + match + and match[0] == "^" + and match[-1] == "$" + and is_fully_escaped(match[1:-1]) + ): + self.rawmatch = unescape(match[1:-1]) + + self.check = check + self._fail_reason: str | None = None + + # used to suppress repeated printing of `repr(self.check)` + self._nested: bool = False + + # set in self._parse_exc + self.is_baseexception = False + + def _parse_exc( + self, exc: type[BaseExcT_1] | types.GenericAlias, expected: str + ) -> type[BaseExcT_1]: + if isinstance(exc, type) and issubclass(exc, BaseException): + if not issubclass(exc, Exception): + self.is_baseexception = True + return exc + # because RaisesGroup does not support variable number of exceptions there's + # still a use for RaisesExc(ExceptionGroup[Exception]). + origin_exc: type[BaseException] | None = get_origin(exc) + if origin_exc and issubclass(origin_exc, BaseExceptionGroup): + exc_type = get_args(exc)[0] + if ( + issubclass(origin_exc, ExceptionGroup) and exc_type in (Exception, Any) + ) or ( + issubclass(origin_exc, BaseExceptionGroup) + and exc_type in (BaseException, Any) + ): + if not issubclass(origin_exc, ExceptionGroup): + self.is_baseexception = True + return cast(type[BaseExcT_1], origin_exc) + else: + raise ValueError( + f"Only `ExceptionGroup[Exception]` or `BaseExceptionGroup[BaseException]` " + f"are accepted as generic types but got `{exc}`. " + f"As `raises` will catch all instances of the specified group regardless of the " + f"generic argument specific nested exceptions has to be checked " + f"with `RaisesGroup`." + ) + # unclear if the Type/ValueError distinction is even helpful here + msg = f"Expected {expected}, but got " + if isinstance(exc, type): # type: ignore[unreachable] + raise ValueError(msg + f"{exc.__name__!r}") + if isinstance(exc, BaseException): # type: ignore[unreachable] + raise TypeError(msg + f"an exception instance: {type(exc).__name__}") + raise TypeError(msg + repr(type(exc).__name__)) + + @property + def fail_reason(self) -> str | None: + """Set after a call to :meth:`matches` to give a human-readable reason for why the match failed. + When used as a context manager the string will be printed as the reason for the + test failing.""" + return self._fail_reason + + def _check_check( + self: AbstractRaises[BaseExcT_1], + exception: BaseExcT_1, + ) -> bool: + if self.check is None: + return True + + if self.check(exception): + return True + + check_repr = "" if self._nested else " " + repr_callable(self.check) + self._fail_reason = f"check{check_repr} did not return True" + return False + + # TODO: harmonize with ExceptionInfo.match + def _check_match(self, e: BaseException) -> bool: + if self.match is None or re.search( + self.match, + stringified_exception := stringify_exception( + e, include_subexception_msg=False + ), + ): + return True + + # if we're matching a group, make sure we're explicit to reduce confusion + # if they're trying to match an exception contained within the group + maybe_specify_type = ( + f" the `{_exception_type_name(type(e))}()`" + if isinstance(e, BaseExceptionGroup) + else "" + ) + if isinstance(self.rawmatch, str): + # TODO: it instructs to use `-v` to print leading text, but that doesn't work + # I also don't know if this is the proper entry point, or tool to use at all + from _pytest.assertion.util import _diff_text + from _pytest.assertion.util import dummy_highlighter + + diff = _diff_text(self.rawmatch, stringified_exception, dummy_highlighter) + self._fail_reason = ("\n" if diff[0][0] == "-" else "") + "\n".join(diff) + return False + + self._fail_reason = ( + f"Regex pattern did not match{maybe_specify_type}.\n" + f" Expected regex: {_match_pattern(self.match)!r}\n" + f" Actual message: {stringified_exception!r}" + ) + if _match_pattern(self.match) == stringified_exception: + self._fail_reason += "\n Did you mean to `re.escape()` the regex?" + return False + + @abstractmethod + def matches( + self: AbstractRaises[BaseExcT_1], exception: BaseException + ) -> TypeGuard[BaseExcT_1]: + """Check if an exception matches the requirements of this AbstractRaises. + If it fails, :meth:`AbstractRaises.fail_reason` should be set. + """ + + +@final +class RaisesExc(AbstractRaises[BaseExcT_co_default]): + """ + .. versionadded:: 8.4 + + + This is the class constructed when calling :func:`pytest.raises`, but may be used + directly as a helper class with :class:`RaisesGroup` when you want to specify + requirements on sub-exceptions. + + You don't need this if you only want to specify the type, since :class:`RaisesGroup` + accepts ``type[BaseException]``. + + :param type[BaseException] | tuple[type[BaseException]] | None expected_exception: + The expected type, or one of several possible types. + May be ``None`` in order to only make use of ``match`` and/or ``check`` + + The type is checked with :func:`isinstance`, and does not need to be an exact match. + If that is wanted you can use the ``check`` parameter. + + :kwparam str | Pattern[str] match: + A regex to match. + + :kwparam Callable[[BaseException], bool] check: + If specified, a callable that will be called with the exception as a parameter + after checking the type and the match regex if specified. + If it returns ``True`` it will be considered a match, if not it will + be considered a failed match. + + :meth:`RaisesExc.matches` can also be used standalone to check individual exceptions. + + Examples:: + + with RaisesGroup(RaisesExc(ValueError, match="string")) + ... + with RaisesGroup(RaisesExc(check=lambda x: x.args == (3, "hello"))): + ... + with RaisesGroup(RaisesExc(check=lambda x: type(x) is ValueError)): + ... + """ + + # Trio bundled hypothesis monkeypatching, we will probably instead assume that + # hypothesis will handle that in their pytest plugin by the time this is released. + # Alternatively we could add a version of get_pretty_function_description ourselves + # https://github.com/HypothesisWorks/hypothesis/blob/8ced2f59f5c7bea3344e35d2d53e1f8f8eb9fcd8/hypothesis-python/src/hypothesis/internal/reflection.py#L439 + + # At least one of the three parameters must be passed. + @overload + def __init__( + self, + expected_exception: ( + type[BaseExcT_co_default] | tuple[type[BaseExcT_co_default], ...] + ), + /, + *, + match: str | Pattern[str] | None = ..., + check: Callable[[BaseExcT_co_default], bool] | None = ..., + ) -> None: ... + + @overload + def __init__( + self: RaisesExc[BaseException], # Give E a value. + /, + *, + match: str | Pattern[str] | None, + # If exception_type is not provided, check() must do any typechecks itself. + check: Callable[[BaseException], bool] | None = ..., + ) -> None: ... + + @overload + def __init__(self, /, *, check: Callable[[BaseException], bool]) -> None: ... + + def __init__( + self, + expected_exception: ( + type[BaseExcT_co_default] | tuple[type[BaseExcT_co_default], ...] | None + ) = None, + /, + *, + match: str | Pattern[str] | None = None, + check: Callable[[BaseExcT_co_default], bool] | None = None, + ): + super().__init__(match=match, check=check) + if isinstance(expected_exception, tuple): + expected_exceptions = expected_exception + elif expected_exception is None: + expected_exceptions = () + else: + expected_exceptions = (expected_exception,) + + if (expected_exceptions == ()) and match is None and check is None: + raise ValueError("You must specify at least one parameter to match on.") + + self.expected_exceptions = tuple( + self._parse_exc(e, expected="a BaseException type") + for e in expected_exceptions + ) + + self._just_propagate = False + + def matches( + self, + exception: BaseException | None, + ) -> TypeGuard[BaseExcT_co_default]: + """Check if an exception matches the requirements of this :class:`RaisesExc`. + If it fails, :attr:`RaisesExc.fail_reason` will be set. + + Examples:: + + assert RaisesExc(ValueError).matches(my_exception): + # is equivalent to + assert isinstance(my_exception, ValueError) + + # this can be useful when checking e.g. the ``__cause__`` of an exception. + with pytest.raises(ValueError) as excinfo: + ... + assert RaisesExc(SyntaxError, match="foo").matches(excinfo.value.__cause__) + # above line is equivalent to + assert isinstance(excinfo.value.__cause__, SyntaxError) + assert re.search("foo", str(excinfo.value.__cause__) + + """ + self._just_propagate = False + if exception is None: + self._fail_reason = "exception is None" + return False + if not self._check_type(exception): + self._just_propagate = True + return False + + if not self._check_match(exception): + return False + + return self._check_check(exception) + + def __repr__(self) -> str: + parameters = [] + if self.expected_exceptions: + parameters.append(_exception_type_name(self.expected_exceptions)) + if self.match is not None: + # If no flags were specified, discard the redundant re.compile() here. + parameters.append( + f"match={_match_pattern(self.match)!r}", + ) + if self.check is not None: + parameters.append(f"check={repr_callable(self.check)}") + return f"RaisesExc({', '.join(parameters)})" + + def _check_type(self, exception: BaseException) -> TypeGuard[BaseExcT_co_default]: + self._fail_reason = _check_raw_type(self.expected_exceptions, exception) + return self._fail_reason is None + + def __enter__(self) -> ExceptionInfo[BaseExcT_co_default]: + self.excinfo: ExceptionInfo[BaseExcT_co_default] = ExceptionInfo.for_later() + return self.excinfo + + # TODO: move common code into superclass + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + if not self.expected_exceptions: + fail("DID NOT RAISE any exception") + if len(self.expected_exceptions) > 1: + fail(f"DID NOT RAISE any of {self.expected_exceptions!r}") + + fail(f"DID NOT RAISE {self.expected_exceptions[0]!r}") + + assert self.excinfo is not None, ( + "Internal error - should have been constructed in __enter__" + ) + + if not self.matches(exc_val): + if self._just_propagate: + return False + raise AssertionError(self._fail_reason) + + # Cast to narrow the exception type now that it's verified.... + # even though the TypeGuard in self.matches should be narrowing + exc_info = cast( + "tuple[type[BaseExcT_co_default], BaseExcT_co_default, types.TracebackType]", + (exc_type, exc_val, exc_tb), + ) + self.excinfo.fill_unfilled(exc_info) + return True + + +@final +class RaisesGroup(AbstractRaises[BaseExceptionGroup[BaseExcT_co]]): + """ + .. versionadded:: 8.4 + + Contextmanager for checking for an expected :exc:`ExceptionGroup`. + This works similar to :func:`pytest.raises`, but allows for specifying the structure of an :exc:`ExceptionGroup`. + :meth:`ExceptionInfo.group_contains` also tries to handle exception groups, + but it is very bad at checking that you *didn't* get unexpected exceptions. + + The catching behaviour differs from :ref:`except* `, being much + stricter about the structure by default. + By using ``allow_unwrapped=True`` and ``flatten_subgroups=True`` you can match + :ref:`except* ` fully when expecting a single exception. + + :param args: + Any number of exception types, :class:`RaisesGroup` or :class:`RaisesExc` + to specify the exceptions contained in this exception. + All specified exceptions must be present in the raised group, *and no others*. + + If you expect a variable number of exceptions you need to use + :func:`pytest.raises(ExceptionGroup) ` and manually check + the contained exceptions. Consider making use of :meth:`RaisesExc.matches`. + + It does not care about the order of the exceptions, so + ``RaisesGroup(ValueError, TypeError)`` + is equivalent to + ``RaisesGroup(TypeError, ValueError)``. + :kwparam str | re.Pattern[str] | None match: + If specified, a string containing a regular expression, + or a regular expression object, that is tested against the string + representation of the exception group and its :pep:`678` `__notes__` + using :func:`re.search`. + + To match a literal string that may contain :ref:`special characters + `, the pattern can first be escaped with :func:`re.escape`. + + Note that " (5 subgroups)" will be stripped from the ``repr`` before matching. + :kwparam Callable[[E], bool] check: + If specified, a callable that will be called with the group as a parameter + after successfully matching the expected exceptions. If it returns ``True`` + it will be considered a match, if not it will be considered a failed match. + :kwparam bool allow_unwrapped: + If expecting a single exception or :class:`RaisesExc` it will match even + if the exception is not inside an exceptiongroup. + + Using this together with ``match``, ``check`` or expecting multiple exceptions + will raise an error. + :kwparam bool flatten_subgroups: + "flatten" any groups inside the raised exception group, extracting all exceptions + inside any nested groups, before matching. Without this it expects you to + fully specify the nesting structure by passing :class:`RaisesGroup` as expected + parameter. + + Examples:: + + with RaisesGroup(ValueError): + raise ExceptionGroup("", (ValueError(),)) + # match + with RaisesGroup( + ValueError, + ValueError, + RaisesExc(TypeError, match="^expected int$"), + match="^my group$", + ): + raise ExceptionGroup( + "my group", + [ + ValueError(), + TypeError("expected int"), + ValueError(), + ], + ) + # check + with RaisesGroup( + KeyboardInterrupt, + match="^hello$", + check=lambda x: isinstance(x.__cause__, ValueError), + ): + raise BaseExceptionGroup("hello", [KeyboardInterrupt()]) from ValueError + # nested groups + with RaisesGroup(RaisesGroup(ValueError)): + raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),)) + + # flatten_subgroups + with RaisesGroup(ValueError, flatten_subgroups=True): + raise ExceptionGroup("", (ExceptionGroup("", (ValueError(),)),)) + + # allow_unwrapped + with RaisesGroup(ValueError, allow_unwrapped=True): + raise ValueError + + + :meth:`RaisesGroup.matches` can also be used directly to check a standalone exception group. + + + The matching algorithm is greedy, which means cases such as this may fail:: + + with RaisesGroup(ValueError, RaisesExc(ValueError, match="hello")): + raise ExceptionGroup("", (ValueError("hello"), ValueError("goodbye"))) + + even though it generally does not care about the order of the exceptions in the group. + To avoid the above you should specify the first :exc:`ValueError` with a :class:`RaisesExc` as well. + + .. note:: + When raised exceptions don't match the expected ones, you'll get a detailed error + message explaining why. This includes ``repr(check)`` if set, which in Python can be + overly verbose, showing memory locations etc etc. + + If installed and imported (in e.g. ``conftest.py``), the ``hypothesis`` library will + monkeypatch this output to provide shorter & more readable repr's. + """ + + # allow_unwrapped=True requires: singular exception, exception not being + # RaisesGroup instance, match is None, check is None + @overload + def __init__( + self, + expected_exception: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + /, + *, + allow_unwrapped: Literal[True], + flatten_subgroups: bool = False, + ) -> None: ... + + # flatten_subgroups = True also requires no nested RaisesGroup + @overload + def __init__( + self, + expected_exception: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + /, + *other_exceptions: type[BaseExcT_co] | RaisesExc[BaseExcT_co], + flatten_subgroups: Literal[True], + match: str | Pattern[str] | None = None, + check: Callable[[BaseExceptionGroup[BaseExcT_co]], bool] | None = None, + ) -> None: ... + + # simplify the typevars if possible (the following 3 are equivalent but go simpler->complicated) + # ... the first handles RaisesGroup[ValueError], the second RaisesGroup[ExceptionGroup[ValueError]], + # the third RaisesGroup[ValueError | ExceptionGroup[ValueError]]. + # ... otherwise, we will get results like RaisesGroup[ValueError | ExceptionGroup[Never]] (I think) + # (technically correct but misleading) + @overload + def __init__( + self: RaisesGroup[ExcT_1], + expected_exception: type[ExcT_1] | RaisesExc[ExcT_1], + /, + *other_exceptions: type[ExcT_1] | RaisesExc[ExcT_1], + match: str | Pattern[str] | None = None, + check: Callable[[ExceptionGroup[ExcT_1]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[ExceptionGroup[ExcT_2]], + expected_exception: RaisesGroup[ExcT_2], + /, + *other_exceptions: RaisesGroup[ExcT_2], + match: str | Pattern[str] | None = None, + check: Callable[[ExceptionGroup[ExceptionGroup[ExcT_2]]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[ExcT_1 | ExceptionGroup[ExcT_2]], + expected_exception: type[ExcT_1] | RaisesExc[ExcT_1] | RaisesGroup[ExcT_2], + /, + *other_exceptions: type[ExcT_1] | RaisesExc[ExcT_1] | RaisesGroup[ExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[[ExceptionGroup[ExcT_1 | ExceptionGroup[ExcT_2]]], bool] | None + ) = None, + ) -> None: ... + + # same as the above 3 but handling BaseException + @overload + def __init__( + self: RaisesGroup[BaseExcT_1], + expected_exception: type[BaseExcT_1] | RaisesExc[BaseExcT_1], + /, + *other_exceptions: type[BaseExcT_1] | RaisesExc[BaseExcT_1], + match: str | Pattern[str] | None = None, + check: Callable[[BaseExceptionGroup[BaseExcT_1]], bool] | None = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[BaseExceptionGroup[BaseExcT_2]], + expected_exception: RaisesGroup[BaseExcT_2], + /, + *other_exceptions: RaisesGroup[BaseExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[[BaseExceptionGroup[BaseExceptionGroup[BaseExcT_2]]], bool] | None + ) = None, + ) -> None: ... + + @overload + def __init__( + self: RaisesGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]], + expected_exception: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + /, + *other_exceptions: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + match: str | Pattern[str] | None = None, + check: ( + Callable[ + [BaseExceptionGroup[BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]]], + bool, + ] + | None + ) = None, + ) -> None: ... + + def __init__( + self: RaisesGroup[ExcT_1 | BaseExcT_1 | BaseExceptionGroup[BaseExcT_2]], + expected_exception: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + /, + *other_exceptions: type[BaseExcT_1] + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2], + allow_unwrapped: bool = False, + flatten_subgroups: bool = False, + match: str | Pattern[str] | None = None, + check: ( + Callable[[BaseExceptionGroup[BaseExcT_1]], bool] + | Callable[[ExceptionGroup[ExcT_1]], bool] + | None + ) = None, + ): + # The type hint on the `self` and `check` parameters uses different formats + # that are *very* hard to reconcile while adhering to the overloads, so we cast + # it to avoid an error when passing it to super().__init__ + check = cast( + "Callable[[BaseExceptionGroup[ExcT_1|BaseExcT_1|BaseExceptionGroup[BaseExcT_2]]], bool]", + check, + ) + super().__init__(match=match, check=check) + self.allow_unwrapped = allow_unwrapped + self.flatten_subgroups: bool = flatten_subgroups + self.is_baseexception = False + + if allow_unwrapped and other_exceptions: + raise ValueError( + "You cannot specify multiple exceptions with `allow_unwrapped=True.`" + " If you want to match one of multiple possible exceptions you should" + " use a `RaisesExc`." + " E.g. `RaisesExc(check=lambda e: isinstance(e, (...)))`", + ) + if allow_unwrapped and isinstance(expected_exception, RaisesGroup): + raise ValueError( + "`allow_unwrapped=True` has no effect when expecting a `RaisesGroup`." + " You might want it in the expected `RaisesGroup`, or" + " `flatten_subgroups=True` if you don't care about the structure.", + ) + if allow_unwrapped and (match is not None or check is not None): + raise ValueError( + "`allow_unwrapped=True` bypasses the `match` and `check` parameters" + " if the exception is unwrapped. If you intended to match/check the" + " exception you should use a `RaisesExc` object. If you want to match/check" + " the exceptiongroup when the exception *is* wrapped you need to" + " do e.g. `if isinstance(exc.value, ExceptionGroup):" + " assert RaisesGroup(...).matches(exc.value)` afterwards.", + ) + + self.expected_exceptions: tuple[ + type[BaseExcT_co] | RaisesExc[BaseExcT_co] | RaisesGroup[BaseException], ... + ] = tuple( + self._parse_excgroup(e, "a BaseException type, RaisesExc, or RaisesGroup") + for e in ( + expected_exception, + *other_exceptions, + ) + ) + + def _parse_excgroup( + self, + exc: ( + type[BaseExcT_co] + | types.GenericAlias + | RaisesExc[BaseExcT_1] + | RaisesGroup[BaseExcT_2] + ), + expected: str, + ) -> type[BaseExcT_co] | RaisesExc[BaseExcT_1] | RaisesGroup[BaseExcT_2]: + # verify exception type and set `self.is_baseexception` + if isinstance(exc, RaisesGroup): + if self.flatten_subgroups: + raise ValueError( + "You cannot specify a nested structure inside a RaisesGroup with" + " `flatten_subgroups=True`. The parameter will flatten subgroups" + " in the raised exceptiongroup before matching, which would never" + " match a nested structure.", + ) + self.is_baseexception |= exc.is_baseexception + exc._nested = True + return exc + elif isinstance(exc, RaisesExc): + self.is_baseexception |= exc.is_baseexception + exc._nested = True + return exc + elif isinstance(exc, tuple): + raise TypeError( + f"Expected {expected}, but got {type(exc).__name__!r}.\n" + "RaisesGroup does not support tuples of exception types when expecting one of " + "several possible exception types like RaisesExc.\n" + "If you meant to expect a group with multiple exceptions, list them as separate arguments." + ) + else: + return super()._parse_exc(exc, expected) + + @overload + def __enter__( + self: RaisesGroup[ExcT_1], + ) -> ExceptionInfo[ExceptionGroup[ExcT_1]]: ... + @overload + def __enter__( + self: RaisesGroup[BaseExcT_1], + ) -> ExceptionInfo[BaseExceptionGroup[BaseExcT_1]]: ... + + def __enter__(self) -> ExceptionInfo[BaseExceptionGroup[BaseException]]: + self.excinfo: ExceptionInfo[BaseExceptionGroup[BaseExcT_co]] = ( + ExceptionInfo.for_later() + ) + return self.excinfo + + def __repr__(self) -> str: + reqs = [ + e.__name__ if isinstance(e, type) else repr(e) + for e in self.expected_exceptions + ] + if self.allow_unwrapped: + reqs.append(f"allow_unwrapped={self.allow_unwrapped}") + if self.flatten_subgroups: + reqs.append(f"flatten_subgroups={self.flatten_subgroups}") + if self.match is not None: + # If no flags were specified, discard the redundant re.compile() here. + reqs.append(f"match={_match_pattern(self.match)!r}") + if self.check is not None: + reqs.append(f"check={repr_callable(self.check)}") + return f"RaisesGroup({', '.join(reqs)})" + + def _unroll_exceptions( + self, + exceptions: Sequence[BaseException], + ) -> Sequence[BaseException]: + """Used if `flatten_subgroups=True`.""" + res: list[BaseException] = [] + for exc in exceptions: + if isinstance(exc, BaseExceptionGroup): + res.extend(self._unroll_exceptions(exc.exceptions)) + + else: + res.append(exc) + return res + + @overload + def matches( + self: RaisesGroup[ExcT_1], + exception: BaseException | None, + ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ... + @overload + def matches( + self: RaisesGroup[BaseExcT_1], + exception: BaseException | None, + ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ... + + def matches( + self, + exception: BaseException | None, + ) -> bool: + """Check if an exception matches the requirements of this RaisesGroup. + If it fails, `RaisesGroup.fail_reason` will be set. + + Example:: + + with pytest.raises(TypeError) as excinfo: + ... + assert RaisesGroup(ValueError).matches(excinfo.value.__cause__) + # the above line is equivalent to + myexc = excinfo.value.__cause + assert isinstance(myexc, BaseExceptionGroup) + assert len(myexc.exceptions) == 1 + assert isinstance(myexc.exceptions[0], ValueError) + """ + self._fail_reason = None + if exception is None: + self._fail_reason = "exception is None" + return False + if not isinstance(exception, BaseExceptionGroup): + # we opt to only print type of the exception here, as the repr would + # likely be quite long + not_group_msg = f"`{type(exception).__name__}()` is not an exception group" + if len(self.expected_exceptions) > 1: + self._fail_reason = not_group_msg + return False + # if we have 1 expected exception, check if it would work even if + # allow_unwrapped is not set + res = self._check_expected(self.expected_exceptions[0], exception) + if res is None and self.allow_unwrapped: + return True + + if res is None: + self._fail_reason = ( + f"{not_group_msg}, but would match with `allow_unwrapped=True`" + ) + elif self.allow_unwrapped: + self._fail_reason = res + else: + self._fail_reason = not_group_msg + return False + + actual_exceptions: Sequence[BaseException] = exception.exceptions + if self.flatten_subgroups: + actual_exceptions = self._unroll_exceptions(actual_exceptions) + + if not self._check_match(exception): + self._fail_reason = cast(str, self._fail_reason) + old_reason = self._fail_reason + if ( + len(actual_exceptions) == len(self.expected_exceptions) == 1 + and isinstance(expected := self.expected_exceptions[0], type) + and isinstance(actual := actual_exceptions[0], expected) + and self._check_match(actual) + ): + assert self.match is not None, "can't be None if _check_match failed" + assert self._fail_reason is old_reason is not None + self._fail_reason += ( + f"\n" + f" but matched the expected `{self._repr_expected(expected)}`.\n" + f" You might want " + f"`RaisesGroup(RaisesExc({expected.__name__}, match={_match_pattern(self.match)!r}))`" + ) + else: + self._fail_reason = old_reason + return False + + # do the full check on expected exceptions + if not self._check_exceptions( + exception, + actual_exceptions, + ): + self._fail_reason = cast(str, self._fail_reason) + assert self._fail_reason is not None + old_reason = self._fail_reason + # if we're not expecting a nested structure, and there is one, do a second + # pass where we try flattening it + if ( + not self.flatten_subgroups + and not any( + isinstance(e, RaisesGroup) for e in self.expected_exceptions + ) + and any(isinstance(e, BaseExceptionGroup) for e in actual_exceptions) + and self._check_exceptions( + exception, + self._unroll_exceptions(exception.exceptions), + ) + ): + # only indent if it's a single-line reason. In a multi-line there's already + # indented lines that this does not belong to. + indent = " " if "\n" not in self._fail_reason else "" + self._fail_reason = ( + old_reason + + f"\n{indent}Did you mean to use `flatten_subgroups=True`?" + ) + else: + self._fail_reason = old_reason + return False + + # Only run `self.check` once we know `exception` is of the correct type. + if not self._check_check(exception): + reason = ( + cast(str, self._fail_reason) + f" on the {type(exception).__name__}" + ) + if ( + len(actual_exceptions) == len(self.expected_exceptions) == 1 + and isinstance(expected := self.expected_exceptions[0], type) + # we explicitly break typing here :) + and self._check_check(actual_exceptions[0]) # type: ignore[arg-type] + ): + self._fail_reason = reason + ( + f", but did return True for the expected {self._repr_expected(expected)}." + f" You might want RaisesGroup(RaisesExc({expected.__name__}, check=<...>))" + ) + else: + self._fail_reason = reason + return False + + return True + + @staticmethod + def _check_expected( + expected_type: ( + type[BaseException] | RaisesExc[BaseException] | RaisesGroup[BaseException] + ), + exception: BaseException, + ) -> str | None: + """Helper method for `RaisesGroup.matches` and `RaisesGroup._check_exceptions` + to check one of potentially several expected exceptions.""" + if isinstance(expected_type, type): + return _check_raw_type(expected_type, exception) + res = expected_type.matches(exception) + if res: + return None + assert expected_type.fail_reason is not None + if expected_type.fail_reason.startswith("\n"): + return f"\n{expected_type!r}: {indent(expected_type.fail_reason, ' ')}" + return f"{expected_type!r}: {expected_type.fail_reason}" + + @staticmethod + def _repr_expected(e: type[BaseException] | AbstractRaises[BaseException]) -> str: + """Get the repr of an expected type/RaisesExc/RaisesGroup, but we only want + the name if it's a type""" + if isinstance(e, type): + return _exception_type_name(e) + return repr(e) + + @overload + def _check_exceptions( + self: RaisesGroup[ExcT_1], + _exception: Exception, + actual_exceptions: Sequence[Exception], + ) -> TypeGuard[ExceptionGroup[ExcT_1]]: ... + @overload + def _check_exceptions( + self: RaisesGroup[BaseExcT_1], + _exception: BaseException, + actual_exceptions: Sequence[BaseException], + ) -> TypeGuard[BaseExceptionGroup[BaseExcT_1]]: ... + + def _check_exceptions( + self, + _exception: BaseException, + actual_exceptions: Sequence[BaseException], + ) -> bool: + """Helper method for RaisesGroup.matches that attempts to pair up expected and actual exceptions""" + # The _exception parameter is not used, but necessary for the TypeGuard + + # full table with all results + results = ResultHolder(self.expected_exceptions, actual_exceptions) + + # (indexes of) raised exceptions that haven't (yet) found an expected + remaining_actual = list(range(len(actual_exceptions))) + # (indexes of) expected exceptions that haven't found a matching raised + failed_expected: list[int] = [] + # successful greedy matches + matches: dict[int, int] = {} + + # loop over expected exceptions first to get a more predictable result + for i_exp, expected in enumerate(self.expected_exceptions): + for i_rem in remaining_actual: + res = self._check_expected(expected, actual_exceptions[i_rem]) + results.set_result(i_exp, i_rem, res) + if res is None: + remaining_actual.remove(i_rem) + matches[i_exp] = i_rem + break + else: + failed_expected.append(i_exp) + + # All exceptions matched up successfully + if not remaining_actual and not failed_expected: + return True + + # in case of a single expected and single raised we simplify the output + if 1 == len(actual_exceptions) == len(self.expected_exceptions): + assert not matches + self._fail_reason = res + return False + + # The test case is failing, so we can do a slow and exhaustive check to find + # duplicate matches etc that will be helpful in debugging + for i_exp, expected in enumerate(self.expected_exceptions): + for i_actual, actual in enumerate(actual_exceptions): + if results.has_result(i_exp, i_actual): + continue + results.set_result( + i_exp, i_actual, self._check_expected(expected, actual) + ) + + successful_str = ( + f"{len(matches)} matched exception{'s' if len(matches) > 1 else ''}. " + if matches + else "" + ) + + # all expected were found + if not failed_expected and results.no_match_for_actual(remaining_actual): + self._fail_reason = ( + f"{successful_str}Unexpected exception(s):" + f" {[actual_exceptions[i] for i in remaining_actual]!r}" + ) + return False + # all raised exceptions were expected + if not remaining_actual and results.no_match_for_expected(failed_expected): + no_match_for_str = ", ".join( + self._repr_expected(self.expected_exceptions[i]) + for i in failed_expected + ) + self._fail_reason = f"{successful_str}Too few exceptions raised, found no match for: [{no_match_for_str}]" + return False + + # if there's only one remaining and one failed, and the unmatched didn't match anything else, + # we elect to only print why the remaining and the failed didn't match. + if ( + 1 == len(remaining_actual) == len(failed_expected) + and results.no_match_for_actual(remaining_actual) + and results.no_match_for_expected(failed_expected) + ): + self._fail_reason = f"{successful_str}{results.get_result(failed_expected[0], remaining_actual[0])}" + return False + + # there's both expected and raised exceptions without matches + s = "" + if matches: + s += f"\n{successful_str}" + indent_1 = " " * 2 + indent_2 = " " * 4 + + if not remaining_actual: + s += "\nToo few exceptions raised!" + elif not failed_expected: + s += "\nUnexpected exception(s)!" + + if failed_expected: + s += "\nThe following expected exceptions did not find a match:" + rev_matches = {v: k for k, v in matches.items()} + for i_failed in failed_expected: + s += ( + f"\n{indent_1}{self._repr_expected(self.expected_exceptions[i_failed])}" + ) + for i_actual, actual in enumerate(actual_exceptions): + if results.get_result(i_exp, i_actual) is None: + # we print full repr of match target + s += ( + f"\n{indent_2}It matches {backquote(repr(actual))} which was paired with " + + backquote( + self._repr_expected( + self.expected_exceptions[rev_matches[i_actual]] + ) + ) + ) + + if remaining_actual: + s += "\nThe following raised exceptions did not find a match" + for i_actual in remaining_actual: + s += f"\n{indent_1}{actual_exceptions[i_actual]!r}:" + for i_exp, expected in enumerate(self.expected_exceptions): + res = results.get_result(i_exp, i_actual) + if i_exp in failed_expected: + assert res is not None + if res[0] != "\n": + s += "\n" + s += indent(res, indent_2) + if res is None: + # we print full repr of match target + s += ( + f"\n{indent_2}It matches {backquote(self._repr_expected(expected))} " + f"which was paired with {backquote(repr(actual_exceptions[matches[i_exp]]))}" + ) + + if len(self.expected_exceptions) == len(actual_exceptions) and possible_match( + results + ): + s += ( + "\nThere exist a possible match when attempting an exhaustive check," + " but RaisesGroup uses a greedy algorithm. " + "Please make your expected exceptions more stringent with `RaisesExc` etc" + " so the greedy algorithm can function." + ) + self._fail_reason = s + return False + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_type is None: + fail(f"DID NOT RAISE any exception, expected `{self.expected_type()}`") + + assert self.excinfo is not None, ( + "Internal error - should have been constructed in __enter__" + ) + + # group_str is the only thing that differs between RaisesExc and RaisesGroup... + # I might just scrap it? Or make it part of fail_reason + group_str = ( + "(group)" + if self.allow_unwrapped and not issubclass(exc_type, BaseExceptionGroup) + else "group" + ) + + if not self.matches(exc_val): + fail(f"Raised exception {group_str} did not match: {self._fail_reason}") + + # Cast to narrow the exception type now that it's verified.... + # even though the TypeGuard in self.matches should be narrowing + exc_info = cast( + "tuple[type[BaseExceptionGroup[BaseExcT_co]], BaseExceptionGroup[BaseExcT_co], types.TracebackType]", + (exc_type, exc_val, exc_tb), + ) + self.excinfo.fill_unfilled(exc_info) + return True + + def expected_type(self) -> str: + subexcs = [] + for e in self.expected_exceptions: + if isinstance(e, RaisesExc): + subexcs.append(repr(e)) + elif isinstance(e, RaisesGroup): + subexcs.append(e.expected_type()) + elif isinstance(e, type): + subexcs.append(e.__name__) + else: # pragma: no cover + raise AssertionError("unknown type") + group_type = "Base" if self.is_baseexception else "" + return f"{group_type}ExceptionGroup({', '.join(subexcs)})" + + +@final +class NotChecked: + """Singleton for unchecked values in ResultHolder""" + + +class ResultHolder: + """Container for results of checking exceptions. + Used in RaisesGroup._check_exceptions and possible_match. + """ + + def __init__( + self, + expected_exceptions: tuple[ + type[BaseException] | AbstractRaises[BaseException], ... + ], + actual_exceptions: Sequence[BaseException], + ) -> None: + self.results: list[list[str | type[NotChecked] | None]] = [ + [NotChecked for _ in expected_exceptions] for _ in actual_exceptions + ] + + def set_result(self, expected: int, actual: int, result: str | None) -> None: + self.results[actual][expected] = result + + def get_result(self, expected: int, actual: int) -> str | None: + res = self.results[actual][expected] + assert res is not NotChecked + # mypy doesn't support identity checking against anything but None + return res # type: ignore[return-value] + + def has_result(self, expected: int, actual: int) -> bool: + return self.results[actual][expected] is not NotChecked + + def no_match_for_expected(self, expected: list[int]) -> bool: + for i in expected: + for actual_results in self.results: + assert actual_results[i] is not NotChecked + if actual_results[i] is None: + return False + return True + + def no_match_for_actual(self, actual: list[int]) -> bool: + for i in actual: + for res in self.results[i]: + assert res is not NotChecked + if res is None: + return False + return True + + +def possible_match(results: ResultHolder, used: set[int] | None = None) -> bool: + if used is None: + used = set() + curr_row = len(used) + if curr_row == len(results.results): + return True + return any( + val is None and i not in used and possible_match(results, used | {i}) + for (i, val) in enumerate(results.results[curr_row]) + ) diff --git a/.venv/lib/python3.12/site-packages/_pytest/recwarn.py b/.venv/lib/python3.12/site-packages/_pytest/recwarn.py new file mode 100644 index 0000000..e3db717 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/recwarn.py @@ -0,0 +1,367 @@ +# mypy: allow-untyped-defs +"""Record warnings during test function execution.""" + +from __future__ import annotations + +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterator +from pprint import pformat +import re +from types import TracebackType +from typing import Any +from typing import final +from typing import overload +from typing import TYPE_CHECKING +from typing import TypeVar + + +if TYPE_CHECKING: + from typing_extensions import Self + +import warnings + +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.outcomes import Exit +from _pytest.outcomes import fail + + +T = TypeVar("T") + + +@fixture +def recwarn() -> Generator[WarningsRecorder]: + """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. + + See :ref:`warnings` for information on warning categories. + """ + wrec = WarningsRecorder(_ispytest=True) + with wrec: + warnings.simplefilter("default") + yield wrec + + +@overload +def deprecated_call( + *, match: str | re.Pattern[str] | None = ... +) -> WarningsRecorder: ... + + +@overload +def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: ... + + +def deprecated_call( + func: Callable[..., Any] | None = None, *args: Any, **kwargs: Any +) -> WarningsRecorder | Any: + """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``. + + This function can be used as a context manager:: + + >>> import warnings + >>> def api_call_v2(): + ... warnings.warn('use v3 of this api', DeprecationWarning) + ... return 200 + + >>> import pytest + >>> with pytest.deprecated_call(): + ... assert api_call_v2() == 200 + + It can also be used by passing a function and ``*args`` and ``**kwargs``, + in which case it will ensure calling ``func(*args, **kwargs)`` produces one of + the warnings types above. The return value is the return value of the function. + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex. + + The context manager produces a list of :class:`warnings.WarningMessage` objects, + one for each warning raised. + """ + __tracebackhide__ = True + if func is not None: + args = (func, *args) + return warns( + (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs + ) + + +@overload +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...] = ..., + *, + match: str | re.Pattern[str] | None = ..., +) -> WarningsChecker: ... + + +@overload +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...], + func: Callable[..., T], + *args: Any, + **kwargs: Any, +) -> T: ... + + +def warns( + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, + *args: Any, + match: str | re.Pattern[str] | None = None, + **kwargs: Any, +) -> WarningsChecker | Any: + r"""Assert that code raises a particular class of warning. + + Specifically, the parameter ``expected_warning`` can be a warning class or tuple + of warning classes, and the code inside the ``with`` block must issue at least one + warning of that class or classes. + + This helper produces a list of :class:`warnings.WarningMessage` objects, one for + each warning emitted (regardless of whether it is an ``expected_warning`` or not). + Since pytest 8.0, unmatched warnings are also re-emitted when the context closes. + + This function can be used as a context manager:: + + >>> import pytest + >>> with pytest.warns(RuntimeWarning): + ... warnings.warn("my warning", RuntimeWarning) + + In the context manager form you may use the keyword argument ``match`` to assert + that the warning matches a text or regex:: + + >>> with pytest.warns(UserWarning, match='must be 0 or None'): + ... warnings.warn("value must be 0 or None", UserWarning) + + >>> with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("value must be 42", UserWarning) + + >>> with pytest.warns(UserWarning): # catch re-emitted warning + ... with pytest.warns(UserWarning, match=r'must be \d+$'): + ... warnings.warn("this is not here", UserWarning) + Traceback (most recent call last): + ... + Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted... + + **Using with** ``pytest.mark.parametrize`` + + When using :ref:`pytest.mark.parametrize ref` it is possible to parametrize tests + such that some runs raise a warning and others do not. + + This could be achieved in the same way as with exceptions, see + :ref:`parametrizing_conditional_raising` for an example. + + """ + __tracebackhide__ = True + if not args: + if kwargs: + argnames = ", ".join(sorted(kwargs)) + raise TypeError( + f"Unexpected keyword arguments passed to pytest.warns: {argnames}" + "\nUse context-manager form instead?" + ) + return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) + else: + func = args[0] + if not callable(func): + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") + with WarningsChecker(expected_warning, _ispytest=True): + return func(*args[1:], **kwargs) + + +class WarningsRecorder(warnings.catch_warnings): + """A context manager to record raised warnings. + + Each recorded warning is an instance of :class:`warnings.WarningMessage`. + + Adapted from `warnings.catch_warnings`. + + .. note:: + ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated + differently; see :ref:`ensuring_function_triggers`. + + """ + + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + super().__init__(record=True) + self._entered = False + self._list: list[warnings.WarningMessage] = [] + + @property + def list(self) -> list[warnings.WarningMessage]: + """The list of recorded warnings.""" + return self._list + + def __getitem__(self, i: int) -> warnings.WarningMessage: + """Get a recorded warning by index.""" + return self._list[i] + + def __iter__(self) -> Iterator[warnings.WarningMessage]: + """Iterate through the recorded warnings.""" + return iter(self._list) + + def __len__(self) -> int: + """The number of recorded warnings.""" + return len(self._list) + + def pop(self, cls: type[Warning] = Warning) -> warnings.WarningMessage: + """Pop the first recorded warning which is an instance of ``cls``, + but not an instance of a child class of any other match. + Raises ``AssertionError`` if there is no match. + """ + best_idx: int | None = None + for i, w in enumerate(self._list): + if w.category == cls: + return self._list.pop(i) # exact match, stop looking + if issubclass(w.category, cls) and ( + best_idx is None + or not issubclass(w.category, self._list[best_idx].category) + ): + best_idx = i + if best_idx is not None: + return self._list.pop(best_idx) + __tracebackhide__ = True + raise AssertionError(f"{cls!r} not found in warning list") + + def clear(self) -> None: + """Clear the list of recorded warnings.""" + self._list[:] = [] + + # Type ignored because we basically want the `catch_warnings` generic type + # parameter to be ourselves but that is not possible(?). + def __enter__(self) -> Self: # type: ignore[override] + if self._entered: + __tracebackhide__ = True + raise RuntimeError(f"Cannot enter {self!r} twice") + _list = super().__enter__() + # record=True means it's None. + assert _list is not None + self._list = _list + warnings.simplefilter("always") + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if not self._entered: + __tracebackhide__ = True + raise RuntimeError(f"Cannot exit {self!r} without entering first") + + super().__exit__(exc_type, exc_val, exc_tb) + + # Built-in catch_warnings does not reset entered state so we do it + # manually here for this context manager to become reusable. + self._entered = False + + +@final +class WarningsChecker(WarningsRecorder): + def __init__( + self, + expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, + match_expr: str | re.Pattern[str] | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + super().__init__(_ispytest=True) + + msg = "exceptions must be derived from Warning, not %s" + if isinstance(expected_warning, tuple): + for exc in expected_warning: + if not issubclass(exc, Warning): + raise TypeError(msg % type(exc)) + expected_warning_tup = expected_warning + elif isinstance(expected_warning, type) and issubclass( + expected_warning, Warning + ): + expected_warning_tup = (expected_warning,) + else: + raise TypeError(msg % type(expected_warning)) + + self.expected_warning = expected_warning_tup + self.match_expr = match_expr + + def matches(self, warning: warnings.WarningMessage) -> bool: + assert self.expected_warning is not None + return issubclass(warning.category, self.expected_warning) and bool( + self.match_expr is None or re.search(self.match_expr, str(warning.message)) + ) + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + super().__exit__(exc_type, exc_val, exc_tb) + + __tracebackhide__ = True + + # BaseExceptions like pytest.{skip,fail,xfail,exit} or Ctrl-C within + # pytest.warns should *not* trigger "DID NOT WARN" and get suppressed + # when the warning doesn't happen. Control-flow exceptions should always + # propagate. + if exc_val is not None and ( + not isinstance(exc_val, Exception) + # Exit is an Exception, not a BaseException, for some reason. + or isinstance(exc_val, Exit) + ): + return + + def found_str() -> str: + return pformat([record.message for record in self], indent=2) + + try: + if not any(issubclass(w.category, self.expected_warning) for w in self): + fail( + f"DID NOT WARN. No warnings of type {self.expected_warning} were emitted.\n" + f" Emitted warnings: {found_str()}." + ) + elif not any(self.matches(w) for w in self): + fail( + f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n" + f" Regex: {self.match_expr}\n" + f" Emitted warnings: {found_str()}." + ) + finally: + # Whether or not any warnings matched, we want to re-emit all unmatched warnings. + for w in self: + if not self.matches(w): + warnings.warn_explicit( + message=w.message, + category=w.category, + filename=w.filename, + lineno=w.lineno, + module=w.__module__, + source=w.source, + ) + + # Currently in Python it is possible to pass other types than an + # `str` message when creating `Warning` instances, however this + # causes an exception when :func:`warnings.filterwarnings` is used + # to filter those warnings. See + # https://github.com/python/cpython/issues/103577 for a discussion. + # While this can be considered a bug in CPython, we put guards in + # pytest as the error message produced without this check in place + # is confusing (#10865). + for w in self: + if type(w.message) is not UserWarning: + # If the warning was of an incorrect type then `warnings.warn()` + # creates a UserWarning. Any other warning must have been specified + # explicitly. + continue + if not w.message.args: + # UserWarning() without arguments must have been specified explicitly. + continue + msg = w.message.args[0] + if isinstance(msg, str): + continue + # It's possible that UserWarning was explicitly specified, and + # its first argument was not a string. But that case can't be + # distinguished from an invalid type. + raise TypeError( + f"Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})" + ) diff --git a/.venv/lib/python3.12/site-packages/_pytest/reports.py b/.venv/lib/python3.12/site-packages/_pytest/reports.py new file mode 100644 index 0000000..011a69d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/reports.py @@ -0,0 +1,694 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Iterable +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +from io import StringIO +import os +from pprint import pprint +import sys +from typing import Any +from typing import cast +from typing import final +from typing import Literal +from typing import NoReturn +from typing import TYPE_CHECKING + +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._code.code import ReprEntry +from _pytest._code.code import ReprEntryNative +from _pytest._code.code import ReprExceptionInfo +from _pytest._code.code import ReprFileLocation +from _pytest._code.code import ReprFuncArgs +from _pytest._code.code import ReprLocals +from _pytest._code.code import ReprTraceback +from _pytest._code.code import TerminalRepr +from _pytest._io import TerminalWriter +from _pytest.config import Config +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import skip + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + + +if TYPE_CHECKING: + from typing_extensions import Self + + from _pytest.runner import CallInfo + + +def getworkerinfoline(node): + try: + return node._workerinfocache + except AttributeError: + d = node.workerinfo + ver = "{}.{}.{}".format(*d["version_info"][:3]) + node._workerinfocache = s = "[{}] {} -- Python {} {}".format( + d["id"], d["sysplatform"], ver, d["executable"] + ) + return s + + +class BaseReport: + when: str | None + location: tuple[str, int | None, str] | None + longrepr: ( + None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr + ) + sections: list[tuple[str, str]] + nodeid: str + outcome: Literal["passed", "failed", "skipped"] + + def __init__(self, **kw: Any) -> None: + self.__dict__.update(kw) + + if TYPE_CHECKING: + # Can have arbitrary fields given to __init__(). + def __getattr__(self, key: str) -> Any: ... + + def toterminal(self, out: TerminalWriter) -> None: + if hasattr(self, "node"): + worker_info = getworkerinfoline(self.node) + if worker_info: + out.line(worker_info) + + longrepr = self.longrepr + if longrepr is None: + return + + if hasattr(longrepr, "toterminal"): + longrepr_terminal = cast(TerminalRepr, longrepr) + longrepr_terminal.toterminal(out) + else: + try: + s = str(longrepr) + except UnicodeEncodeError: + s = "" + out.line(s) + + def get_sections(self, prefix: str) -> Iterator[tuple[str, str]]: + for name, content in self.sections: + if name.startswith(prefix): + yield prefix, content + + @property + def longreprtext(self) -> str: + """Read-only property that returns the full string representation of + ``longrepr``. + + .. versionadded:: 3.0 + """ + file = StringIO() + tw = TerminalWriter(file) + tw.hasmarkup = False + self.toterminal(tw) + exc = file.getvalue() + return exc.strip() + + @property + def caplog(self) -> str: + """Return captured log lines, if log capturing is enabled. + + .. versionadded:: 3.5 + """ + return "\n".join( + content for (prefix, content) in self.get_sections("Captured log") + ) + + @property + def capstdout(self) -> str: + """Return captured text from stdout, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stdout") + ) + + @property + def capstderr(self) -> str: + """Return captured text from stderr, if capturing is enabled. + + .. versionadded:: 3.0 + """ + return "".join( + content for (prefix, content) in self.get_sections("Captured stderr") + ) + + @property + def passed(self) -> bool: + """Whether the outcome is passed.""" + return self.outcome == "passed" + + @property + def failed(self) -> bool: + """Whether the outcome is failed.""" + return self.outcome == "failed" + + @property + def skipped(self) -> bool: + """Whether the outcome is skipped.""" + return self.outcome == "skipped" + + @property + def fspath(self) -> str: + """The path portion of the reported node, as a string.""" + return self.nodeid.split("::")[0] + + @property + def count_towards_summary(self) -> bool: + """**Experimental** Whether this report should be counted towards the + totals shown at the end of the test session: "1 passed, 1 failure, etc". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + return True + + @property + def head_line(self) -> str | None: + """**Experimental** The head line shown with longrepr output for this + report, more commonly during traceback representation during + failures:: + + ________ Test.foo ________ + + + In the example above, the head_line is "Test.foo". + + .. note:: + + This function is considered **experimental**, so beware that it is subject to changes + even in patch releases. + """ + if self.location is not None: + _fspath, _lineno, domain = self.location + return domain + return None + + def _get_verbose_word_with_markup( + self, config: Config, default_markup: Mapping[str, bool] + ) -> tuple[str, Mapping[str, bool]]: + _category, _short, verbose = config.hook.pytest_report_teststatus( + report=self, config=config + ) + + if isinstance(verbose, str): + return verbose, default_markup + + if isinstance(verbose, Sequence) and len(verbose) == 2: + word, markup = verbose + if isinstance(word, str) and isinstance(markup, Mapping): + return word, markup + + fail( # pragma: no cover + "pytest_report_teststatus() hook (from a plugin) returned " + f"an invalid verbose value: {verbose!r}.\nExpected either a string " + "or a tuple of (word, markup)." + ) + + def _to_json(self) -> dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + return _report_to_json(self) + + @classmethod + def _from_json(cls, reportdict: dict[str, object]) -> Self: + """Create either a TestReport or CollectReport, depending on the calling class. + + It is the callers responsibility to know which class to pass here. + + This was originally the serialize_report() function from xdist (ca03269). + + Experimental method. + """ + kwargs = _report_kwargs_from_json(reportdict) + return cls(**kwargs) + + +def _report_unserialization_failure( + type_name: str, report_class: type[BaseReport], reportdict +) -> NoReturn: + url = "https://github.com/pytest-dev/pytest/issues" + stream = StringIO() + pprint("-" * 100, stream=stream) + pprint(f"INTERNALERROR: Unknown entry type returned: {type_name}", stream=stream) + pprint(f"report_name: {report_class}", stream=stream) + pprint(reportdict, stream=stream) + pprint(f"Please report this bug at {url}", stream=stream) + pprint("-" * 100, stream=stream) + raise RuntimeError(stream.getvalue()) + + +def _format_failed_longrepr( + item: Item, call: CallInfo[None], excinfo: ExceptionInfo[BaseException] +): + if call.when == "call": + longrepr = item.repr_failure(excinfo) + else: + # Exception in setup or teardown. + longrepr = item._repr_failure_py( + excinfo, style=item.config.getoption("tbstyle", "auto") + ) + return longrepr + + +def _format_exception_group_all_skipped_longrepr( + item: Item, + excinfo: ExceptionInfo[BaseExceptionGroup[BaseException | BaseExceptionGroup]], +) -> tuple[str, int, str]: + r = excinfo._getreprcrash() + assert r is not None, ( + "There should always be a traceback entry for skipping a test." + ) + if all( + getattr(skip, "_use_item_location", False) for skip in excinfo.value.exceptions + ): + path, line = item.reportinfo()[:2] + assert line is not None + loc = (os.fspath(path), line + 1) + default_msg = "skipped" + else: + loc = (str(r.path), r.lineno) + default_msg = r.message + + # Get all unique skip messages. + msgs: list[str] = [] + for exception in excinfo.value.exceptions: + m = getattr(exception, "msg", None) or ( + exception.args[0] if exception.args else None + ) + if m and m not in msgs: + msgs.append(m) + + reason = "; ".join(msgs) if msgs else default_msg + longrepr = (*loc, reason) + return longrepr + + +class TestReport(BaseReport): + """Basic test report object (also used for setup and teardown calls if + they fail). + + Reports can contain arbitrary extra attributes. + """ + + __test__ = False + + # Defined by skipping plugin. + # xfail reason if xfailed, otherwise not defined. Use hasattr to distinguish. + wasxfail: str + + def __init__( + self, + nodeid: str, + location: tuple[str, int | None, str], + keywords: Mapping[str, Any], + outcome: Literal["passed", "failed", "skipped"], + longrepr: None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr, + when: Literal["setup", "call", "teardown"], + sections: Iterable[tuple[str, str]] = (), + duration: float = 0, + start: float = 0, + stop: float = 0, + user_properties: Iterable[tuple[str, object]] | None = None, + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: A (filesystempath, lineno, domaininfo) tuple indicating the + #: actual location of a test item - it might be different from the + #: collected one e.g. if a method is inherited from a different module. + #: The filesystempath may be relative to ``config.rootdir``. + #: The line number is 0-based. + self.location: tuple[str, int | None, str] = location + + #: A name -> value dictionary containing all keywords and + #: markers associated with a test invocation. + self.keywords: Mapping[str, Any] = keywords + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: One of 'setup', 'call', 'teardown' to indicate runtest phase. + self.when: Literal["setup", "call", "teardown"] = when + + #: User properties is a list of tuples (name, value) that holds user + #: defined properties of the test. + self.user_properties = list(user_properties or []) + + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. + self.sections = list(sections) + + #: Time it took to run just the test. + self.duration: float = duration + + #: The system time when the call started, in seconds since the epoch. + self.start: float = start + #: The system time when the call ended, in seconds since the epoch. + self.stop: float = stop + + self.__dict__.update(extra) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" + + @classmethod + def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: + """Create and fill a TestReport with standard item and call info. + + :param item: The item. + :param call: The call info. + """ + when = call.when + # Remove "collect" from the Literal type -- only for collection calls. + assert when != "collect" + duration = call.duration + start = call.start + stop = call.stop + keywords = {x: 1 for x in item.keywords} + excinfo = call.excinfo + sections = [] + if not call.excinfo: + outcome: Literal["passed", "failed", "skipped"] = "passed" + longrepr: ( + None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr + ) = None + else: + if not isinstance(excinfo, ExceptionInfo): + outcome = "failed" + longrepr = excinfo + elif isinstance(excinfo.value, skip.Exception): + outcome = "skipped" + r = excinfo._getreprcrash() + assert r is not None, ( + "There should always be a traceback entry for skipping a test." + ) + if excinfo.value._use_item_location: + path, line = item.reportinfo()[:2] + assert line is not None + longrepr = (os.fspath(path), line + 1, r.message) + else: + longrepr = (str(r.path), r.lineno, r.message) + elif isinstance(excinfo.value, BaseExceptionGroup) and ( + excinfo.value.split(skip.Exception)[1] is None + ): + # All exceptions in the group are skip exceptions. + outcome = "skipped" + excinfo = cast( + ExceptionInfo[ + BaseExceptionGroup[BaseException | BaseExceptionGroup] + ], + excinfo, + ) + longrepr = _format_exception_group_all_skipped_longrepr(item, excinfo) + else: + outcome = "failed" + longrepr = _format_failed_longrepr(item, call, excinfo) + for rwhen, key, content in item._report_sections: + sections.append((f"Captured {key} {rwhen}", content)) + return cls( + item.nodeid, + item.location, + keywords, + outcome, + longrepr, + when, + sections, + duration, + start, + stop, + user_properties=item.user_properties, + ) + + +@final +class CollectReport(BaseReport): + """Collection report object. + + Reports can contain arbitrary extra attributes. + """ + + when = "collect" + + def __init__( + self, + nodeid: str, + outcome: Literal["passed", "failed", "skipped"], + longrepr: None + | ExceptionInfo[BaseException] + | tuple[str, int, str] + | str + | TerminalRepr, + result: list[Item | Collector] | None, + sections: Iterable[tuple[str, str]] = (), + **extra, + ) -> None: + #: Normalized collection nodeid. + self.nodeid = nodeid + + #: Test outcome, always one of "passed", "failed", "skipped". + self.outcome = outcome + + #: None or a failure representation. + self.longrepr = longrepr + + #: The collected items and collection nodes. + self.result = result or [] + + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. + self.sections = list(sections) + + self.__dict__.update(extra) + + @property + def location( # type:ignore[override] + self, + ) -> tuple[str, int | None, str] | None: + return (self.fspath, None, self.fspath) + + def __repr__(self) -> str: + return f"" + + +class CollectErrorRepr(TerminalRepr): + def __init__(self, msg: str) -> None: + self.longrepr = msg + + def toterminal(self, out: TerminalWriter) -> None: + out.line(self.longrepr, red=True) + + +def pytest_report_to_serializable( + report: CollectReport | TestReport, +) -> dict[str, Any] | None: + if isinstance(report, TestReport | CollectReport): + data = report._to_json() + data["$report_type"] = report.__class__.__name__ + return data + # TODO: Check if this is actually reachable. + return None # type: ignore[unreachable] + + +def pytest_report_from_serializable( + data: dict[str, Any], +) -> CollectReport | TestReport | None: + if "$report_type" in data: + if data["$report_type"] == "TestReport": + return TestReport._from_json(data) + elif data["$report_type"] == "CollectReport": + return CollectReport._from_json(data) + assert False, "Unknown report_type unserialize data: {}".format( + data["$report_type"] + ) + return None + + +def _report_to_json(report: BaseReport) -> dict[str, Any]: + """Return the contents of this report as a dict of builtin entries, + suitable for serialization. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def serialize_repr_entry( + entry: ReprEntry | ReprEntryNative, + ) -> dict[str, Any]: + data = dataclasses.asdict(entry) + for key, value in data.items(): + if hasattr(value, "__dict__"): + data[key] = dataclasses.asdict(value) + entry_data = {"type": type(entry).__name__, "data": data} + return entry_data + + def serialize_repr_traceback(reprtraceback: ReprTraceback) -> dict[str, Any]: + result = dataclasses.asdict(reprtraceback) + result["reprentries"] = [ + serialize_repr_entry(x) for x in reprtraceback.reprentries + ] + return result + + def serialize_repr_crash( + reprcrash: ReprFileLocation | None, + ) -> dict[str, Any] | None: + if reprcrash is not None: + return dataclasses.asdict(reprcrash) + else: + return None + + def serialize_exception_longrepr(rep: BaseReport) -> dict[str, Any]: + assert rep.longrepr is not None + # TODO: Investigate whether the duck typing is really necessary here. + longrepr = cast(ExceptionRepr, rep.longrepr) + result: dict[str, Any] = { + "reprcrash": serialize_repr_crash(longrepr.reprcrash), + "reprtraceback": serialize_repr_traceback(longrepr.reprtraceback), + "sections": longrepr.sections, + } + if isinstance(longrepr, ExceptionChainRepr): + result["chain"] = [] + for repr_traceback, repr_crash, description in longrepr.chain: + result["chain"].append( + ( + serialize_repr_traceback(repr_traceback), + serialize_repr_crash(repr_crash), + description, + ) + ) + else: + result["chain"] = None + return result + + d = report.__dict__.copy() + if hasattr(report.longrepr, "toterminal"): + if hasattr(report.longrepr, "reprtraceback") and hasattr( + report.longrepr, "reprcrash" + ): + d["longrepr"] = serialize_exception_longrepr(report) + else: + d["longrepr"] = str(report.longrepr) + else: + d["longrepr"] = report.longrepr + for name in d: + if isinstance(d[name], os.PathLike): + d[name] = os.fspath(d[name]) + elif name == "result": + d[name] = None # for now + return d + + +def _report_kwargs_from_json(reportdict: dict[str, Any]) -> dict[str, Any]: + """Return **kwargs that can be used to construct a TestReport or + CollectReport instance. + + This was originally the serialize_report() function from xdist (ca03269). + """ + + def deserialize_repr_entry(entry_data): + data = entry_data["data"] + entry_type = entry_data["type"] + if entry_type == "ReprEntry": + reprfuncargs = None + reprfileloc = None + reprlocals = None + if data["reprfuncargs"]: + reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) + if data["reprfileloc"]: + reprfileloc = ReprFileLocation(**data["reprfileloc"]) + if data["reprlocals"]: + reprlocals = ReprLocals(data["reprlocals"]["lines"]) + + reprentry: ReprEntry | ReprEntryNative = ReprEntry( + lines=data["lines"], + reprfuncargs=reprfuncargs, + reprlocals=reprlocals, + reprfileloc=reprfileloc, + style=data["style"], + ) + elif entry_type == "ReprEntryNative": + reprentry = ReprEntryNative(data["lines"]) + else: + _report_unserialization_failure(entry_type, TestReport, reportdict) + return reprentry + + def deserialize_repr_traceback(repr_traceback_dict): + repr_traceback_dict["reprentries"] = [ + deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"] + ] + return ReprTraceback(**repr_traceback_dict) + + def deserialize_repr_crash(repr_crash_dict: dict[str, Any] | None): + if repr_crash_dict is not None: + return ReprFileLocation(**repr_crash_dict) + else: + return None + + if ( + reportdict["longrepr"] + and "reprcrash" in reportdict["longrepr"] + and "reprtraceback" in reportdict["longrepr"] + ): + reprtraceback = deserialize_repr_traceback( + reportdict["longrepr"]["reprtraceback"] + ) + reprcrash = deserialize_repr_crash(reportdict["longrepr"]["reprcrash"]) + if reportdict["longrepr"]["chain"]: + chain = [] + for repr_traceback_data, repr_crash_data, description in reportdict[ + "longrepr" + ]["chain"]: + chain.append( + ( + deserialize_repr_traceback(repr_traceback_data), + deserialize_repr_crash(repr_crash_data), + description, + ) + ) + exception_info: ExceptionChainRepr | ReprExceptionInfo = ExceptionChainRepr( + chain + ) + else: + exception_info = ReprExceptionInfo( + reprtraceback=reprtraceback, + reprcrash=reprcrash, + ) + + for section in reportdict["longrepr"]["sections"]: + exception_info.addsection(*section) + reportdict["longrepr"] = exception_info + + return reportdict diff --git a/.venv/lib/python3.12/site-packages/_pytest/runner.py b/.venv/lib/python3.12/site-packages/_pytest/runner.py new file mode 100644 index 0000000..9c20ff9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/runner.py @@ -0,0 +1,580 @@ +# mypy: allow-untyped-defs +"""Basic collect and runtest protocol implementations.""" + +from __future__ import annotations + +import bdb +from collections.abc import Callable +import dataclasses +import os +import sys +import types +from typing import cast +from typing import final +from typing import Generic +from typing import Literal +from typing import TYPE_CHECKING +from typing import TypeVar + +from .config import Config +from .reports import BaseReport +from .reports import CollectErrorRepr +from .reports import CollectReport +from .reports import TestReport +from _pytest import timing +from _pytest._code.code import ExceptionChainRepr +from _pytest._code.code import ExceptionInfo +from _pytest._code.code import TerminalRepr +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.nodes import Collector +from _pytest.nodes import Directory +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.outcomes import Exit +from _pytest.outcomes import OutcomeException +from _pytest.outcomes import Skipped +from _pytest.outcomes import TEST_OUTCOME + + +if sys.version_info < (3, 11): + from exceptiongroup import BaseExceptionGroup + +if TYPE_CHECKING: + from _pytest.main import Session + from _pytest.terminal import TerminalReporter + +# +# pytest plugin hooks. + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "Reporting", after="general") + group.addoption( + "--durations", + action="store", + type=int, + default=None, + metavar="N", + help="Show N slowest setup/test durations (N=0 for all)", + ) + group.addoption( + "--durations-min", + action="store", + type=float, + default=None, + metavar="N", + help="Minimal duration in seconds for inclusion in slowest list. " + "Default: 0.005 (or 0.0 if -vv is given).", + ) + + +def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: + durations = terminalreporter.config.option.durations + durations_min = terminalreporter.config.option.durations_min + verbose = terminalreporter.config.get_verbosity() + if durations is None: + return + if durations_min is None: + durations_min = 0.005 if verbose < 2 else 0.0 + tr = terminalreporter + dlist = [] + for replist in tr.stats.values(): + for rep in replist: + if hasattr(rep, "duration"): + dlist.append(rep) + if not dlist: + return + dlist.sort(key=lambda x: x.duration, reverse=True) + if not durations: + tr.write_sep("=", "slowest durations") + else: + tr.write_sep("=", f"slowest {durations} durations") + dlist = dlist[:durations] + + for i, rep in enumerate(dlist): + if rep.duration < durations_min: + tr.write_line("") + message = f"({len(dlist) - i} durations < {durations_min:g}s hidden." + if terminalreporter.config.option.durations_min is None: + message += " Use -vv to show these durations." + message += ")" + tr.write_line(message) + break + tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") + + +def pytest_sessionstart(session: Session) -> None: + session._setupstate = SetupState() + + +def pytest_sessionfinish(session: Session) -> None: + session._setupstate.teardown_exact(None) + + +def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> bool: + ihook = item.ihook + ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) + runtestprotocol(item, nextitem=nextitem) + ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location) + return True + + +def runtestprotocol( + item: Item, log: bool = True, nextitem: Item | None = None +) -> list[TestReport]: + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: # type: ignore[attr-defined] + # This only happens if the item is re-run, as is done by + # pytest-rerunfailures. + item._initrequest() # type: ignore[attr-defined] + rep = call_and_report(item, "setup", log) + reports = [rep] + if rep.passed: + if item.config.getoption("setupshow", False): + show_test_item(item) + if not item.config.getoption("setuponly", False): + reports.append(call_and_report(item, "call", log)) + # If the session is about to fail or stop, teardown everything - this is + # necessary to correctly report fixture teardown errors (see #11706) + if item.session.shouldfail or item.session.shouldstop: + nextitem = None + reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) + # After all teardown hooks have been called + # want funcargs and request info to go away. + if hasrequest: + item._request = False # type: ignore[attr-defined] + item.funcargs = None # type: ignore[attr-defined] + return reports + + +def show_test_item(item: Item) -> None: + """Show test function, parameters and the fixtures of the test item.""" + tw = item.config.get_terminal_writer() + tw.line() + tw.write(" " * 8) + tw.write(item.nodeid) + used_fixtures = sorted(getattr(item, "fixturenames", [])) + if used_fixtures: + tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) + tw.flush() + + +def pytest_runtest_setup(item: Item) -> None: + _update_current_test_var(item, "setup") + item.session._setupstate.setup(item) + + +def pytest_runtest_call(item: Item) -> None: + _update_current_test_var(item, "call") + try: + del sys.last_type + del sys.last_value + del sys.last_traceback + if sys.version_info >= (3, 12, 0): + del sys.last_exc # type:ignore[attr-defined] + except AttributeError: + pass + try: + item.runtest() + except Exception as e: + # Store trace info to allow postmortem debugging + sys.last_type = type(e) + sys.last_value = e + if sys.version_info >= (3, 12, 0): + sys.last_exc = e # type:ignore[attr-defined] + assert e.__traceback__ is not None + # Skip *this* frame + sys.last_traceback = e.__traceback__.tb_next + raise + + +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + _update_current_test_var(item, "teardown") + item.session._setupstate.teardown_exact(nextitem) + _update_current_test_var(item, None) + + +def _update_current_test_var( + item: Item, when: Literal["setup", "call", "teardown"] | None +) -> None: + """Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage. + + If ``when`` is None, delete ``PYTEST_CURRENT_TEST`` from the environment. + """ + var_name = "PYTEST_CURRENT_TEST" + if when: + value = f"{item.nodeid} ({when})" + # don't allow null bytes on environment variables (see #2644, #2957) + value = value.replace("\x00", "(null)") + os.environ[var_name] = value + else: + os.environ.pop(var_name) + + +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if report.when in ("setup", "teardown"): + if report.failed: + # category, shortletter, verbose-word + return "error", "E", "ERROR" + elif report.skipped: + return "skipped", "s", "SKIPPED" + else: + return "", "", "" + return None + + +# +# Implementation + + +def call_and_report( + item: Item, when: Literal["setup", "call", "teardown"], log: bool = True, **kwds +) -> TestReport: + ihook = item.ihook + if when == "setup": + runtest_hook: Callable[..., None] = ihook.pytest_runtest_setup + elif when == "call": + runtest_hook = ihook.pytest_runtest_call + elif when == "teardown": + runtest_hook = ihook.pytest_runtest_teardown + else: + assert False, f"Unhandled runtest hook case: {when}" + + call = CallInfo.from_call( + lambda: runtest_hook(item=item, **kwds), + when=when, + reraise=get_reraise_exceptions(item.config), + ) + report: TestReport = ihook.pytest_runtest_makereport(item=item, call=call) + if log: + ihook.pytest_runtest_logreport(report=report) + if check_interactive_exception(call, report): + ihook.pytest_exception_interact(node=item, call=call, report=report) + return report + + +def get_reraise_exceptions(config: Config) -> tuple[type[BaseException], ...]: + """Return exception types that should not be suppressed in general.""" + reraise: tuple[type[BaseException], ...] = (Exit,) + if not config.getoption("usepdb", False): + reraise += (KeyboardInterrupt,) + return reraise + + +def check_interactive_exception(call: CallInfo[object], report: BaseReport) -> bool: + """Check whether the call raised an exception that should be reported as + interactive.""" + if call.excinfo is None: + # Didn't raise. + return False + if hasattr(report, "wasxfail"): + # Exception was expected. + return False + if isinstance(call.excinfo.value, Skipped | bdb.BdbQuit): + # Special control flow exception. + return False + return True + + +TResult = TypeVar("TResult", covariant=True) + + +@final +@dataclasses.dataclass +class CallInfo(Generic[TResult]): + """Result/Exception info of a function invocation.""" + + _result: TResult | None + #: The captured exception of the call, if it raised. + excinfo: ExceptionInfo[BaseException] | None + #: The system time when the call started, in seconds since the epoch. + start: float + #: The system time when the call ended, in seconds since the epoch. + stop: float + #: The call duration, in seconds. + duration: float + #: The context of invocation: "collect", "setup", "call" or "teardown". + when: Literal["collect", "setup", "call", "teardown"] + + def __init__( + self, + result: TResult | None, + excinfo: ExceptionInfo[BaseException] | None, + start: float, + stop: float, + duration: float, + when: Literal["collect", "setup", "call", "teardown"], + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._result = result + self.excinfo = excinfo + self.start = start + self.stop = stop + self.duration = duration + self.when = when + + @property + def result(self) -> TResult: + """The return value of the call, if it didn't raise. + + Can only be accessed if excinfo is None. + """ + if self.excinfo is not None: + raise AttributeError(f"{self!r} has no valid result") + # The cast is safe because an exception wasn't raised, hence + # _result has the expected function return type (which may be + # None, that's why a cast and not an assert). + return cast(TResult, self._result) + + @classmethod + def from_call( + cls, + func: Callable[[], TResult], + when: Literal["collect", "setup", "call", "teardown"], + reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None, + ) -> CallInfo[TResult]: + """Call func, wrapping the result in a CallInfo. + + :param func: + The function to call. Called without arguments. + :type func: Callable[[], _pytest.runner.TResult] + :param when: + The phase in which the function is called. + :param reraise: + Exception or exceptions that shall propagate if raised by the + function, instead of being wrapped in the CallInfo. + """ + excinfo = None + instant = timing.Instant() + try: + result: TResult | None = func() + except BaseException: + excinfo = ExceptionInfo.from_current() + if reraise is not None and isinstance(excinfo.value, reraise): + raise + result = None + duration = instant.elapsed() + return cls( + start=duration.start.time, + stop=duration.stop.time, + duration=duration.seconds, + when=when, + result=result, + excinfo=excinfo, + _ispytest=True, + ) + + def __repr__(self) -> str: + if self.excinfo is None: + return f"" + return f"" + + +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: + return TestReport.from_item_and_call(item, call) + + +def pytest_make_collect_report(collector: Collector) -> CollectReport: + def collect() -> list[Item | Collector]: + # Before collecting, if this is a Directory, load the conftests. + # If a conftest import fails to load, it is considered a collection + # error of the Directory collector. This is why it's done inside of the + # CallInfo wrapper. + # + # Note: initial conftests are loaded early, not here. + if isinstance(collector, Directory): + collector.config.pluginmanager._loadconftestmodules( + collector.path, + collector.config.getoption("importmode"), + rootpath=collector.config.rootpath, + consider_namespace_packages=collector.config.getini( + "consider_namespace_packages" + ), + ) + + return list(collector.collect()) + + call = CallInfo.from_call( + collect, "collect", reraise=(KeyboardInterrupt, SystemExit) + ) + longrepr: None | tuple[str, int, str] | str | TerminalRepr = None + if not call.excinfo: + outcome: Literal["passed", "skipped", "failed"] = "passed" + else: + skip_exceptions = [Skipped] + unittest = sys.modules.get("unittest") + if unittest is not None: + skip_exceptions.append(unittest.SkipTest) + if isinstance(call.excinfo.value, tuple(skip_exceptions)): + outcome = "skipped" + r_ = collector._repr_failure_py(call.excinfo, "line") + assert isinstance(r_, ExceptionChainRepr), repr(r_) + r = r_.reprcrash + assert r + longrepr = (str(r.path), r.lineno, r.message) + else: + outcome = "failed" + errorinfo = collector.repr_failure(call.excinfo) + if not hasattr(errorinfo, "toterminal"): + assert isinstance(errorinfo, str) + errorinfo = CollectErrorRepr(errorinfo) + longrepr = errorinfo + result = call.result if not call.excinfo else None + rep = CollectReport(collector.nodeid, outcome, longrepr, result) + rep.call = call # type: ignore # see collect_one_node + return rep + + +class SetupState: + """Shared state for setting up/tearing down test items or collectors + in a session. + + Suppose we have a collection tree as follows: + + + + + + + + The SetupState maintains a stack. The stack starts out empty: + + [] + + During the setup phase of item1, setup(item1) is called. What it does + is: + + push session to stack, run session.setup() + push mod1 to stack, run mod1.setup() + push item1 to stack, run item1.setup() + + The stack is: + + [session, mod1, item1] + + While the stack is in this shape, it is allowed to add finalizers to + each of session, mod1, item1 using addfinalizer(). + + During the teardown phase of item1, teardown_exact(item2) is called, + where item2 is the next item to item1. What it does is: + + pop item1 from stack, run its teardowns + pop mod1 from stack, run its teardowns + + mod1 was popped because it ended its purpose with item1. The stack is: + + [session] + + During the setup phase of item2, setup(item2) is called. What it does + is: + + push mod2 to stack, run mod2.setup() + push item2 to stack, run item2.setup() + + Stack: + + [session, mod2, item2] + + During the teardown phase of item2, teardown_exact(None) is called, + because item2 is the last item. What it does is: + + pop item2 from stack, run its teardowns + pop mod2 from stack, run its teardowns + pop session from stack, run its teardowns + + Stack: + + [] + + The end! + """ + + def __init__(self) -> None: + # The stack is in the dict insertion order. + self.stack: dict[ + Node, + tuple[ + # Node's finalizers. + list[Callable[[], object]], + # Node's exception and original traceback, if its setup raised. + tuple[OutcomeException | Exception, types.TracebackType | None] | None, + ], + ] = {} + + def setup(self, item: Item) -> None: + """Setup objects along the collector chain to the item.""" + needed_collectors = item.listchain() + + # If a collector fails its setup, fail its entire subtree of items. + # The setup is not retried for each item - the same exception is used. + for col, (finalizers, exc) in self.stack.items(): + assert col in needed_collectors, "previous item was not torn down properly" + if exc: + raise exc[0].with_traceback(exc[1]) + + for col in needed_collectors[len(self.stack) :]: + assert col not in self.stack + # Push onto the stack. + self.stack[col] = ([col.teardown], None) + try: + col.setup() + except TEST_OUTCOME as exc: + self.stack[col] = (self.stack[col][0], (exc, exc.__traceback__)) + raise + + def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: + """Attach a finalizer to the given node. + + The node must be currently active in the stack. + """ + assert node and not isinstance(node, tuple) + assert callable(finalizer) + assert node in self.stack, (node, self.stack) + self.stack[node][0].append(finalizer) + + def teardown_exact(self, nextitem: Item | None) -> None: + """Teardown the current stack up until reaching nodes that nextitem + also descends from. + + When nextitem is None (meaning we're at the last item), the entire + stack is torn down. + """ + needed_collectors = (nextitem and nextitem.listchain()) or [] + exceptions: list[BaseException] = [] + while self.stack: + if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: + break + node, (finalizers, _) = self.stack.popitem() + these_exceptions = [] + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + these_exceptions.append(e) + + if len(these_exceptions) == 1: + exceptions.extend(these_exceptions) + elif these_exceptions: + msg = f"errors while tearing down {node!r}" + exceptions.append(BaseExceptionGroup(msg, these_exceptions[::-1])) + + if len(exceptions) == 1: + raise exceptions[0] + elif exceptions: + raise BaseExceptionGroup("errors during test teardown", exceptions[::-1]) + if nextitem is None: + assert not self.stack + + +def collect_one_node(collector: Collector) -> CollectReport: + ihook = collector.ihook + ihook.pytest_collectstart(collector=collector) + rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) + call = rep.__dict__.pop("call", None) + if call and check_interactive_exception(call, rep): + ihook.pytest_exception_interact(node=collector, call=call, report=rep) + return rep diff --git a/.venv/lib/python3.12/site-packages/_pytest/scope.py b/.venv/lib/python3.12/site-packages/_pytest/scope.py new file mode 100644 index 0000000..2b007e8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/scope.py @@ -0,0 +1,91 @@ +""" +Scope definition and related utilities. + +Those are defined here, instead of in the 'fixtures' module because +their use is spread across many other pytest modules, and centralizing it in 'fixtures' +would cause circular references. + +Also this makes the module light to import, as it should. +""" + +from __future__ import annotations + +from enum import Enum +from functools import total_ordering +from typing import Literal + + +_ScopeName = Literal["session", "package", "module", "class", "function"] + + +@total_ordering +class Scope(Enum): + """ + Represents one of the possible fixture scopes in pytest. + + Scopes are ordered from lower to higher, that is: + + ->>> higher ->>> + + Function < Class < Module < Package < Session + + <<<- lower <<<- + """ + + # Scopes need to be listed from lower to higher. + Function = "function" + Class = "class" + Module = "module" + Package = "package" + Session = "session" + + def next_lower(self) -> Scope: + """Return the next lower scope.""" + index = _SCOPE_INDICES[self] + if index == 0: + raise ValueError(f"{self} is the lower-most scope") + return _ALL_SCOPES[index - 1] + + def next_higher(self) -> Scope: + """Return the next higher scope.""" + index = _SCOPE_INDICES[self] + if index == len(_SCOPE_INDICES) - 1: + raise ValueError(f"{self} is the upper-most scope") + return _ALL_SCOPES[index + 1] + + def __lt__(self, other: Scope) -> bool: + self_index = _SCOPE_INDICES[self] + other_index = _SCOPE_INDICES[other] + return self_index < other_index + + @classmethod + def from_user( + cls, scope_name: _ScopeName, descr: str, where: str | None = None + ) -> Scope: + """ + Given a scope name from the user, return the equivalent Scope enum. Should be used + whenever we want to convert a user provided scope name to its enum object. + + If the scope name is invalid, construct a user friendly message and call pytest.fail. + """ + from _pytest.outcomes import fail + + try: + # Holding this reference is necessary for mypy at the moment. + scope = Scope(scope_name) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope_name + ), + pytrace=False, + ) + return scope + + +_ALL_SCOPES = list(Scope) +_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)} + + +# Ordered list of scopes which can contain many tests (in practice all except Function). +HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] diff --git a/.venv/lib/python3.12/site-packages/_pytest/setuponly.py b/.venv/lib/python3.12/site-packages/_pytest/setuponly.py new file mode 100644 index 0000000..7e6b46b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/setuponly.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Generator + +from _pytest._io.saferepr import saferepr +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +from _pytest.scope import Scope +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setuponly", + "--setup-only", + action="store_true", + help="Only setup fixtures, do not execute tests", + ) + group.addoption( + "--setupshow", + "--setup-show", + action="store_true", + help="Show setup of fixtures while executing tests", + ) + + +@pytest.hookimpl(wrapper=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> Generator[None, object, object]: + try: + return (yield) + finally: + if request.config.option.setupshow: + if hasattr(request, "param"): + # Save the fixture parameter so ._show_fixture_action() can + # display it now and during the teardown (in .finish()). + if fixturedef.ids: + if callable(fixturedef.ids): + param = fixturedef.ids(request.param) + else: + param = fixturedef.ids[request.param_index] + else: + param = request.param + fixturedef.cached_param = param # type: ignore[attr-defined] + _show_fixture_action(fixturedef, request.config, "SETUP") + + +def pytest_fixture_post_finalizer( + fixturedef: FixtureDef[object], request: SubRequest +) -> None: + if fixturedef.cached_result is not None: + config = request.config + if config.option.setupshow: + _show_fixture_action(fixturedef, request.config, "TEARDOWN") + if hasattr(fixturedef, "cached_param"): + del fixturedef.cached_param + + +def _show_fixture_action( + fixturedef: FixtureDef[object], config: Config, msg: str +) -> None: + capman = config.pluginmanager.getplugin("capturemanager") + if capman: + capman.suspend_global_capture() + + tw = config.get_terminal_writer() + tw.line() + # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. + scope_indent = list(reversed(Scope)).index(fixturedef._scope) + tw.write(" " * 2 * scope_indent) + + scopename = fixturedef.scope[0].upper() + tw.write(f"{msg:<8} {scopename} {fixturedef.argname}") + + if msg == "SETUP": + deps = sorted(arg for arg in fixturedef.argnames if arg != "request") + if deps: + tw.write(" (fixtures used: {})".format(", ".join(deps))) + + if hasattr(fixturedef, "cached_param"): + tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") + + tw.flush() + + if capman: + capman.resume_global_capture() + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setuponly: + config.option.setupshow = True + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/setupplan.py b/.venv/lib/python3.12/site-packages/_pytest/setupplan.py new file mode 100644 index 0000000..4e124cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/setupplan.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config.argparsing import Parser +from _pytest.fixtures import FixtureDef +from _pytest.fixtures import SubRequest +import pytest + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("debugconfig") + group.addoption( + "--setupplan", + "--setup-plan", + action="store_true", + help="Show what fixtures and tests would be executed but " + "don't execute anything", + ) + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup( + fixturedef: FixtureDef[object], request: SubRequest +) -> object | None: + # Will return a dummy fixture if the setuponly option is provided. + if request.config.option.setupplan: + my_cache_key = fixturedef.cache_key(request) + fixturedef.cached_result = (None, my_cache_key, None) + return fixturedef.cached_result + return None + + +@pytest.hookimpl(tryfirst=True) +def pytest_cmdline_main(config: Config) -> int | ExitCode | None: + if config.option.setupplan: + config.option.setuponly = True + config.option.setupshow = True + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/skipping.py b/.venv/lib/python3.12/site-packages/_pytest/skipping.py new file mode 100644 index 0000000..3b06762 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/skipping.py @@ -0,0 +1,321 @@ +# mypy: allow-untyped-defs +"""Support for skip/xfail functions and markers.""" + +from __future__ import annotations + +from collections.abc import Generator +from collections.abc import Mapping +import dataclasses +import os +import platform +import sys +import traceback + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.mark.structures import Mark +from _pytest.nodes import Item +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.raises import AbstractRaises +from _pytest.reports import BaseReport +from _pytest.reports import TestReport +from _pytest.runner import CallInfo +from _pytest.stash import StashKey + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--runxfail", + action="store_true", + dest="runxfail", + default=False, + help="Report the results of xfail tests as if they were not marked", + ) + + parser.addini( + "strict_xfail", + "Default for the strict parameter of xfail " + "markers when not given explicitly (default: False) (alias: xfail_strict)", + type="bool", + # None => fallback to `strict`. + default=None, + aliases=["xfail_strict"], + ) + + +def pytest_configure(config: Config) -> None: + if config.option.runxfail: + # yay a hack + import pytest + + old = pytest.xfail + config.add_cleanup(lambda: setattr(pytest, "xfail", old)) + + def nop(*args, **kwargs): + pass + + nop.Exception = xfail.Exception # type: ignore[attr-defined] + setattr(pytest, "xfail", nop) + + config.addinivalue_line( + "markers", + "skip(reason=None): skip the given test function with an optional reason. " + 'Example: skip(reason="no way of currently testing this") skips the ' + "test.", + ) + config.addinivalue_line( + "markers", + "skipif(condition, ..., *, reason=...): " + "skip the given test function if any of the conditions evaluate to True. " + "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " + "See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif", + ) + config.addinivalue_line( + "markers", + "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=strict_xfail): " + "mark the test function as an expected failure if any of the conditions " + "evaluate to True. Optionally specify a reason for better reporting " + "and run=False if you don't even want to execute the test function. " + "If only specific exception(s) are expected, you can list them in " + "raises, and if the test fails in other ways, it will be reported as " + "a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail", + ) + + +def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, str]: + """Evaluate a single skipif/xfail condition. + + If an old-style string condition is given, it is eval()'d, otherwise the + condition is bool()'d. If this fails, an appropriately formatted pytest.fail + is raised. + + Returns (result, reason). The reason is only relevant if the result is True. + """ + # String condition. + if isinstance(condition, str): + globals_ = { + "os": os, + "sys": sys, + "platform": platform, + "config": item.config, + } + for dictionary in reversed( + item.ihook.pytest_markeval_namespace(config=item.config) + ): + if not isinstance(dictionary, Mapping): + raise ValueError( + f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}" + ) + globals_.update(dictionary) + if hasattr(item, "obj"): + globals_.update(item.obj.__globals__) + try: + filename = f"<{mark.name} condition>" + condition_code = compile(condition, filename, "eval") + result = eval(condition_code, globals_) + except SyntaxError as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition", + " " + condition, + " " + " " * (exc.offset or 0) + "^", + "SyntaxError: invalid syntax", + ] + fail("\n".join(msglines), pytrace=False) + except Exception as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition", + " " + condition, + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + # Boolean condition. + else: + try: + result = bool(condition) + except Exception as exc: + msglines = [ + f"Error evaluating {mark.name!r} condition as a boolean", + *traceback.format_exception_only(type(exc), exc), + ] + fail("\n".join(msglines), pytrace=False) + + reason = mark.kwargs.get("reason", None) + if reason is None: + if isinstance(condition, str): + reason = "condition: " + condition + else: + # XXX better be checked at collection time + msg = ( + f"Error evaluating {mark.name!r}: " + + "you need to specify reason=STRING when using booleans as conditions." + ) + fail(msg, pytrace=False) + + return result, reason + + +@dataclasses.dataclass(frozen=True) +class Skip: + """The result of evaluate_skip_marks().""" + + reason: str = "unconditional skip" + + +def evaluate_skip_marks(item: Item) -> Skip | None: + """Evaluate skip and skipif marks on item, returning Skip if triggered.""" + for mark in item.iter_markers(name="skipif"): + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Skip(reason) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Skip(reason) + + for mark in item.iter_markers(name="skip"): + try: + return Skip(*mark.args, **mark.kwargs) + except TypeError as e: + raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None + + return None + + +@dataclasses.dataclass(frozen=True) +class Xfail: + """The result of evaluate_xfail_marks().""" + + __slots__ = ("raises", "reason", "run", "strict") + + reason: str + run: bool + strict: bool + raises: ( + type[BaseException] + | tuple[type[BaseException], ...] + | AbstractRaises[BaseException] + | None + ) + + +def evaluate_xfail_marks(item: Item) -> Xfail | None: + """Evaluate xfail marks on item, returning Xfail if triggered.""" + for mark in item.iter_markers(name="xfail"): + run = mark.kwargs.get("run", True) + strict = mark.kwargs.get("strict") + if strict is None: + strict = item.config.getini("strict_xfail") + if strict is None: + strict = item.config.getini("strict") + raises = mark.kwargs.get("raises", None) + if "condition" not in mark.kwargs: + conditions = mark.args + else: + conditions = (mark.kwargs["condition"],) + + # Unconditional. + if not conditions: + reason = mark.kwargs.get("reason", "") + return Xfail(reason, run, strict, raises) + + # If any of the conditions are true. + for condition in conditions: + result, reason = evaluate_condition(item, mark, condition) + if result: + return Xfail(reason, run, strict, raises) + + return None + + +# Saves the xfail mark evaluation. Can be refreshed during call if None. +xfailed_key = StashKey[Xfail | None]() + + +@hookimpl(tryfirst=True) +def pytest_runtest_setup(item: Item) -> None: + skipped = evaluate_skip_marks(item) + if skipped: + raise skip.Exception(skipped.reason, _use_item_location=True) + + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + +@hookimpl(wrapper=True) +def pytest_runtest_call(item: Item) -> Generator[None]: + xfailed = item.stash.get(xfailed_key, None) + if xfailed is None: + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + if xfailed and not item.config.option.runxfail and not xfailed.run: + xfail("[NOTRUN] " + xfailed.reason) + + try: + return (yield) + finally: + # The test run may have added an xfail mark dynamically. + xfailed = item.stash.get(xfailed_key, None) + if xfailed is None: + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) + + +@hookimpl(wrapper=True) +def pytest_runtest_makereport( + item: Item, call: CallInfo[None] +) -> Generator[None, TestReport, TestReport]: + rep = yield + xfailed = item.stash.get(xfailed_key, None) + if item.config.option.runxfail: + pass # don't interfere + elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): + assert call.excinfo.value.msg is not None + rep.wasxfail = call.excinfo.value.msg + rep.outcome = "skipped" + elif not rep.skipped and xfailed: + if call.excinfo: + raises = xfailed.raises + if raises is None or ( + ( + isinstance(raises, type | tuple) + and isinstance(call.excinfo.value, raises) + ) + or ( + isinstance(raises, AbstractRaises) + and raises.matches(call.excinfo.value) + ) + ): + rep.outcome = "skipped" + rep.wasxfail = xfailed.reason + else: + rep.outcome = "failed" + elif call.when == "call": + if xfailed.strict: + rep.outcome = "failed" + rep.longrepr = "[XPASS(strict)] " + xfailed.reason + else: + rep.outcome = "passed" + rep.wasxfail = xfailed.reason + return rep + + +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: + if hasattr(report, "wasxfail"): + if report.skipped: + return "xfailed", "x", "XFAIL" + elif report.passed: + return "xpassed", "X", "XPASS" + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/stash.py b/.venv/lib/python3.12/site-packages/_pytest/stash.py new file mode 100644 index 0000000..6a9ff88 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/stash.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from typing import Any +from typing import cast +from typing import Generic +from typing import TypeVar + + +__all__ = ["Stash", "StashKey"] + + +T = TypeVar("T") +D = TypeVar("D") + + +class StashKey(Generic[T]): + """``StashKey`` is an object used as a key to a :class:`Stash`. + + A ``StashKey`` is associated with the type ``T`` of the value of the key. + + A ``StashKey`` is unique and cannot conflict with another key. + + .. versionadded:: 7.0 + """ + + __slots__ = () + + +class Stash: + r"""``Stash`` is a type-safe heterogeneous mutable mapping that + allows keys and value types to be defined separately from + where it (the ``Stash``) is created. + + Usually you will be given an object which has a ``Stash``, for example + :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: + + .. code-block:: python + + stash: Stash = some_object.stash + + If a module or plugin wants to store data in this ``Stash``, it creates + :class:`StashKey`\s for its keys (at the module level): + + .. code-block:: python + + # At the top-level of the module + some_str_key = StashKey[str]() + some_bool_key = StashKey[bool]() + + To store information: + + .. code-block:: python + + # Value type must match the key. + stash[some_str_key] = "value" + stash[some_bool_key] = True + + To retrieve the information: + + .. code-block:: python + + # The static type of some_str is str. + some_str = stash[some_str_key] + # The static type of some_bool is bool. + some_bool = stash[some_bool_key] + + .. versionadded:: 7.0 + """ + + __slots__ = ("_storage",) + + def __init__(self) -> None: + self._storage: dict[StashKey[Any], object] = {} + + def __setitem__(self, key: StashKey[T], value: T) -> None: + """Set a value for key.""" + self._storage[key] = value + + def __getitem__(self, key: StashKey[T]) -> T: + """Get the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + return cast(T, self._storage[key]) + + def get(self, key: StashKey[T], default: D) -> T | D: + """Get the value for key, or return default if the key wasn't set + before.""" + try: + return self[key] + except KeyError: + return default + + def setdefault(self, key: StashKey[T], default: T) -> T: + """Return the value of key if already set, otherwise set the value + of key to default and return default.""" + try: + return self[key] + except KeyError: + self[key] = default + return default + + def __delitem__(self, key: StashKey[T]) -> None: + """Delete the value for key. + + Raises ``KeyError`` if the key wasn't set before. + """ + del self._storage[key] + + def __contains__(self, key: StashKey[T]) -> bool: + """Return whether key was set.""" + return key in self._storage + + def __len__(self) -> int: + """Return how many items exist in the stash.""" + return len(self._storage) diff --git a/.venv/lib/python3.12/site-packages/_pytest/stepwise.py b/.venv/lib/python3.12/site-packages/_pytest/stepwise.py new file mode 100644 index 0000000..8901540 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/stepwise.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import dataclasses +from datetime import datetime +from datetime import timedelta +from typing import Any +from typing import TYPE_CHECKING + +from _pytest import nodes +from _pytest.cacheprovider import Cache +from _pytest.config import Config +from _pytest.config.argparsing import Parser +from _pytest.main import Session +from _pytest.reports import TestReport + + +if TYPE_CHECKING: + from typing_extensions import Self + +STEPWISE_CACHE_DIR = "cache/stepwise" + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("general") + group.addoption( + "--sw", + "--stepwise", + action="store_true", + default=False, + dest="stepwise", + help="Exit on test failure and continue from last failing test next time", + ) + group.addoption( + "--sw-skip", + "--stepwise-skip", + action="store_true", + default=False, + dest="stepwise_skip", + help="Ignore the first failing test but stop on the next failing test. " + "Implicitly enables --stepwise.", + ) + group.addoption( + "--sw-reset", + "--stepwise-reset", + action="store_true", + default=False, + dest="stepwise_reset", + help="Resets stepwise state, restarting the stepwise workflow. " + "Implicitly enables --stepwise.", + ) + + +def pytest_configure(config: Config) -> None: + # --stepwise-skip/--stepwise-reset implies stepwise. + if config.option.stepwise_skip or config.option.stepwise_reset: + config.option.stepwise = True + if config.getoption("stepwise"): + config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") + + +def pytest_sessionfinish(session: Session) -> None: + if not session.config.getoption("stepwise"): + assert session.config.cache is not None + if hasattr(session.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + + +@dataclasses.dataclass +class StepwiseCacheInfo: + # The nodeid of the last failed test. + last_failed: str | None + + # The number of tests in the last time --stepwise was run. + # We use this information as a simple way to invalidate the cache information, avoiding + # confusing behavior in case the cache is stale. + last_test_count: int | None + + # The date when the cache was last updated, for information purposes only. + last_cache_date_str: str + + @property + def last_cache_date(self) -> datetime: + return datetime.fromisoformat(self.last_cache_date_str) + + @classmethod + def empty(cls) -> Self: + return cls( + last_failed=None, + last_test_count=None, + last_cache_date_str=datetime.now().isoformat(), + ) + + def update_date_to_now(self) -> None: + self.last_cache_date_str = datetime.now().isoformat() + + +class StepwisePlugin: + def __init__(self, config: Config) -> None: + self.config = config + self.session: Session | None = None + self.report_status: list[str] = [] + assert config.cache is not None + self.cache: Cache = config.cache + self.skip: bool = config.getoption("stepwise_skip") + self.reset: bool = config.getoption("stepwise_reset") + self.cached_info = self._load_cached_info() + + def _load_cached_info(self) -> StepwiseCacheInfo: + cached_dict: dict[str, Any] | None = self.cache.get(STEPWISE_CACHE_DIR, None) + if cached_dict: + try: + return StepwiseCacheInfo( + cached_dict["last_failed"], + cached_dict["last_test_count"], + cached_dict["last_cache_date_str"], + ) + except (KeyError, TypeError) as e: + error = f"{type(e).__name__}: {e}" + self.report_status.append(f"error reading cache, discarding ({error})") + + # Cache not found or error during load, return a new cache. + return StepwiseCacheInfo.empty() + + def pytest_sessionstart(self, session: Session) -> None: + self.session = session + + def pytest_collection_modifyitems( + self, config: Config, items: list[nodes.Item] + ) -> None: + last_test_count = self.cached_info.last_test_count + self.cached_info.last_test_count = len(items) + + if self.reset: + self.report_status.append("resetting state, not skipping.") + self.cached_info.last_failed = None + return + + if not self.cached_info.last_failed: + self.report_status.append("no previously failed tests, not skipping.") + return + + if last_test_count is not None and last_test_count != len(items): + self.report_status.append( + f"test count changed, not skipping (now {len(items)} tests, previously {last_test_count})." + ) + self.cached_info.last_failed = None + return + + # Check all item nodes until we find a match on last failed. + failed_index = None + for index, item in enumerate(items): + if item.nodeid == self.cached_info.last_failed: + failed_index = index + break + + # If the previously failed test was not found among the test items, + # do not skip any tests. + if failed_index is None: + self.report_status.append("previously failed test not found, not skipping.") + else: + cache_age = datetime.now() - self.cached_info.last_cache_date + # Round up to avoid showing microseconds. + cache_age = timedelta(seconds=int(cache_age.total_seconds())) + self.report_status.append( + f"skipping {failed_index} already passed items (cache from {cache_age} ago," + f" use --sw-reset to discard)." + ) + deselected = items[:failed_index] + del items[:failed_index] + config.hook.pytest_deselected(items=deselected) + + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + if self.skip: + # Remove test from the failed ones (if it exists) and unset the skip option + # to make sure the following tests will not be skipped. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + self.skip = False + else: + # Mark test as the last failing and interrupt the test session. + self.cached_info.last_failed = report.nodeid + assert self.session is not None + self.session.shouldstop = ( + "Test failed, continuing from this test next run." + ) + + else: + # If the test was actually run and did pass. + if report.when == "call": + # Remove test from the failed ones, if exists. + if report.nodeid == self.cached_info.last_failed: + self.cached_info.last_failed = None + + def pytest_report_collectionfinish(self) -> list[str] | None: + if self.config.get_verbosity() >= 0 and self.report_status: + return [f"stepwise: {x}" for x in self.report_status] + return None + + def pytest_sessionfinish(self) -> None: + if hasattr(self.config, "workerinput"): + # Do not update cache if this process is a xdist worker to prevent + # race conditions (#10641). + return + self.cached_info.update_date_to_now() + self.cache.set(STEPWISE_CACHE_DIR, dataclasses.asdict(self.cached_info)) diff --git a/.venv/lib/python3.12/site-packages/_pytest/subtests.py b/.venv/lib/python3.12/site-packages/_pytest/subtests.py new file mode 100644 index 0000000..e0ceb27 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/subtests.py @@ -0,0 +1,411 @@ +"""Builtin plugin that adds subtests support.""" + +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Callable +from collections.abc import Iterator +from collections.abc import Mapping +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from contextlib import nullcontext +import dataclasses +import time +from types import TracebackType +from typing import Any +from typing import TYPE_CHECKING + +import pluggy + +from _pytest._code import ExceptionInfo +from _pytest._io.saferepr import saferepr +from _pytest.capture import CaptureFixture +from _pytest.capture import FDCapture +from _pytest.capture import SysCapture +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import SubRequest +from _pytest.logging import catching_logs +from _pytest.logging import LogCaptureHandler +from _pytest.logging import LoggingPlugin +from _pytest.reports import TestReport +from _pytest.runner import CallInfo +from _pytest.runner import check_interactive_exception +from _pytest.runner import get_reraise_exceptions +from _pytest.stash import StashKey + + +if TYPE_CHECKING: + from typing_extensions import Self + + +def pytest_addoption(parser: Parser) -> None: + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_SUBTESTS, + help=( + "Specify verbosity level for subtests. " + "Higher levels will generate output for passed subtests. Failed subtests are always reported." + ), + ) + + +@dataclasses.dataclass(frozen=True, slots=True, kw_only=True) +class SubtestContext: + """The values passed to Subtests.test() that are included in the test report.""" + + msg: str | None + kwargs: Mapping[str, Any] + + def _to_json(self) -> dict[str, Any]: + return dataclasses.asdict(self) + + @classmethod + def _from_json(cls, d: dict[str, Any]) -> Self: + return cls(msg=d["msg"], kwargs=d["kwargs"]) + + +@dataclasses.dataclass(init=False) +class SubtestReport(TestReport): + context: SubtestContext + + @property + def head_line(self) -> str: + _, _, domain = self.location + return f"{domain} {self._sub_test_description()}" + + def _sub_test_description(self) -> str: + parts = [] + if self.context.msg is not None: + parts.append(f"[{self.context.msg}]") + if self.context.kwargs: + params_desc = ", ".join( + f"{k}={saferepr(v)}" for (k, v) in self.context.kwargs.items() + ) + parts.append(f"({params_desc})") + return " ".join(parts) or "()" + + def _to_json(self) -> dict[str, Any]: + data = super()._to_json() + del data["context"] + data["_report_type"] = "SubTestReport" + data["_subtest.context"] = self.context._to_json() + return data + + @classmethod + def _from_json(cls, reportdict: dict[str, Any]) -> SubtestReport: + report = super()._from_json(reportdict) + report.context = SubtestContext._from_json(reportdict["_subtest.context"]) + return report + + @classmethod + def _new( + cls, + test_report: TestReport, + context: SubtestContext, + captured_output: Captured | None, + captured_logs: CapturedLogs | None, + ) -> Self: + result = super()._from_json(test_report._to_json()) + result.context = context + + if captured_output: + if captured_output.out: + result.sections.append(("Captured stdout call", captured_output.out)) + if captured_output.err: + result.sections.append(("Captured stderr call", captured_output.err)) + + if captured_logs and (log := captured_logs.handler.stream.getvalue()): + result.sections.append(("Captured log call", log)) + + return result + + +@fixture +def subtests(request: SubRequest) -> Subtests: + """Provides subtests functionality.""" + capmam = request.node.config.pluginmanager.get_plugin("capturemanager") + suspend_capture_ctx = ( + capmam.global_and_fixture_disabled if capmam is not None else nullcontext + ) + return Subtests(request.node.ihook, suspend_capture_ctx, request, _ispytest=True) + + +class Subtests: + """Subtests fixture, enables declaring subtests inside test functions via the :meth:`test` method.""" + + def __init__( + self, + ihook: pluggy.HookRelay, + suspend_capture_ctx: Callable[[], AbstractContextManager[None]], + request: SubRequest, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._ihook = ihook + self._suspend_capture_ctx = suspend_capture_ctx + self._request = request + + def test( + self, + msg: str | None = None, + **kwargs: Any, + ) -> _SubTestContextManager: + """ + Context manager for subtests, capturing exceptions raised inside the subtest scope and + reporting assertion failures and errors individually. + + Usage + ----- + + .. code-block:: python + + def test(subtests): + for i in range(5): + with subtests.test("custom message", i=i): + assert i % 2 == 0 + + :param msg: + If given, the message will be shown in the test report in case of subtest failure. + + :param kwargs: + Arbitrary values that are also added to the subtest report. + """ + return _SubTestContextManager( + self._ihook, + msg, + kwargs, + request=self._request, + suspend_capture_ctx=self._suspend_capture_ctx, + config=self._request.config, + ) + + +@dataclasses.dataclass +class _SubTestContextManager: + """ + Context manager for subtests, capturing exceptions raised inside the subtest scope and handling + them through the pytest machinery. + """ + + # Note: initially the logic for this context manager was implemented directly + # in Subtests.test() as a @contextmanager, however, it is not possible to control the output fully when + # exiting from it due to an exception when in `--exitfirst` mode, so this was refactored into an + # explicit context manager class (pytest-dev/pytest-subtests#134). + + ihook: pluggy.HookRelay + msg: str | None + kwargs: dict[str, Any] + suspend_capture_ctx: Callable[[], AbstractContextManager[None]] + request: SubRequest + config: Config + + def __enter__(self) -> None: + __tracebackhide__ = True + + self._start = time.time() + self._precise_start = time.perf_counter() + self._exc_info = None + + self._exit_stack = ExitStack() + self._captured_output = self._exit_stack.enter_context( + capturing_output(self.request) + ) + self._captured_logs = self._exit_stack.enter_context( + capturing_logs(self.request) + ) + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> bool: + __tracebackhide__ = True + if exc_val is not None: + exc_info = ExceptionInfo.from_exception(exc_val) + else: + exc_info = None + + self._exit_stack.close() + + precise_stop = time.perf_counter() + duration = precise_stop - self._precise_start + stop = time.time() + + call_info = CallInfo[None]( + None, + exc_info, + start=self._start, + stop=stop, + duration=duration, + when="call", + _ispytest=True, + ) + report = self.ihook.pytest_runtest_makereport( + item=self.request.node, call=call_info + ) + sub_report = SubtestReport._new( + report, + SubtestContext(msg=self.msg, kwargs=self.kwargs), + captured_output=self._captured_output, + captured_logs=self._captured_logs, + ) + + if sub_report.failed: + failed_subtests = self.config.stash[failed_subtests_key] + failed_subtests[self.request.node.nodeid] += 1 + + with self.suspend_capture_ctx(): + self.ihook.pytest_runtest_logreport(report=sub_report) + + if check_interactive_exception(call_info, sub_report): + self.ihook.pytest_exception_interact( + node=self.request.node, call=call_info, report=sub_report + ) + + if exc_val is not None: + if isinstance(exc_val, get_reraise_exceptions(self.config)): + return False + if self.request.session.shouldfail: + return False + return True + + +@contextmanager +def capturing_output(request: SubRequest) -> Iterator[Captured]: + option = request.config.getoption("capture", None) + + capman = request.config.pluginmanager.getplugin("capturemanager") + if getattr(capman, "_capture_fixture", None): + # capsys or capfd are active, subtest should not capture. + fixture = None + elif option == "sys": + fixture = CaptureFixture(SysCapture, request, _ispytest=True) + elif option == "fd": + fixture = CaptureFixture(FDCapture, request, _ispytest=True) + else: + fixture = None + + if fixture is not None: + fixture._start() + + captured = Captured() + try: + yield captured + finally: + if fixture is not None: + out, err = fixture.readouterr() + fixture.close() + captured.out = out + captured.err = err + + +@contextmanager +def capturing_logs( + request: SubRequest, +) -> Iterator[CapturedLogs | None]: + logging_plugin: LoggingPlugin | None = request.config.pluginmanager.getplugin( + "logging-plugin" + ) + if logging_plugin is None: + yield None + else: + handler = LogCaptureHandler() + handler.setFormatter(logging_plugin.formatter) + + captured_logs = CapturedLogs(handler) + with catching_logs(handler, level=logging_plugin.log_level): + yield captured_logs + + +@dataclasses.dataclass +class Captured: + out: str = "" + err: str = "" + + +@dataclasses.dataclass +class CapturedLogs: + handler: LogCaptureHandler + + +def pytest_report_to_serializable(report: TestReport) -> dict[str, Any] | None: + if isinstance(report, SubtestReport): + return report._to_json() + return None + + +def pytest_report_from_serializable(data: dict[str, Any]) -> SubtestReport | None: + if data.get("_report_type") == "SubTestReport": + return SubtestReport._from_json(data) + return None + + +# Dict of nodeid -> number of failed subtests. +# Used to fail top-level tests that passed but contain failed subtests. +failed_subtests_key = StashKey[defaultdict[str, int]]() + + +def pytest_configure(config: Config) -> None: + config.stash[failed_subtests_key] = defaultdict(lambda: 0) + + +@hookimpl(tryfirst=True) +def pytest_report_teststatus( + report: TestReport, + config: Config, +) -> tuple[str, str, str | Mapping[str, bool]] | None: + if report.when != "call": + return None + + quiet = config.get_verbosity(Config.VERBOSITY_SUBTESTS) == 0 + if isinstance(report, SubtestReport): + outcome = report.outcome + description = report._sub_test_description() + + if hasattr(report, "wasxfail"): + if quiet: + return "", "", "" + elif outcome == "skipped": + category = "xfailed" + short = "y" # x letter is used for regular xfail, y for subtest xfail + status = "SUBXFAIL" + # outcome == "passed" in an xfail is only possible via a @pytest.mark.xfail mark, which + # is not applicable to a subtest, which only handles pytest.xfail(). + else: # pragma: no cover + # This should not normally happen, unless some plugin is setting wasxfail without + # the correct outcome. Pytest expects the call outcome to be either skipped or + # passed in case of xfail. + # Let's pass this report to the next hook. + return None + return category, short, f"{status}{description}" + + if report.failed: + return outcome, "u", f"SUBFAILED{description}" + else: + if report.passed: + if quiet: + return "", "", "" + else: + return f"subtests {outcome}", "u", f"SUBPASSED{description}" + elif report.skipped: + if quiet: + return "", "", "" + else: + return outcome, "-", f"SUBSKIPPED{description}" + + else: + failed_subtests_count = config.stash[failed_subtests_key][report.nodeid] + # Top-level test, fail if it contains failed subtests and it has passed. + if report.passed and failed_subtests_count > 0: + report.outcome = "failed" + suffix = "s" if failed_subtests_count > 1 else "" + report.longrepr = f"contains {failed_subtests_count} failed subtest{suffix}" + + return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/terminal.py b/.venv/lib/python3.12/site-packages/_pytest/terminal.py new file mode 100644 index 0000000..e66e4f4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/terminal.py @@ -0,0 +1,1763 @@ +# mypy: allow-untyped-defs +"""Terminal reporting of the full testing process. + +This is a good source for looking at the various reporting hooks. +""" + +from __future__ import annotations + +import argparse +from collections import Counter +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Mapping +from collections.abc import Sequence +import dataclasses +import datetime +from functools import partial +import inspect +from pathlib import Path +import platform +import sys +import textwrap +from typing import Any +from typing import ClassVar +from typing import final +from typing import Literal +from typing import NamedTuple +from typing import TextIO +from typing import TYPE_CHECKING +import warnings + +import pluggy + +from _pytest import compat +from _pytest import nodes +from _pytest import timing +from _pytest._code import ExceptionInfo +from _pytest._code.code import ExceptionRepr +from _pytest._io import TerminalWriter +from _pytest._io.wcwidth import wcswidth +import _pytest._version +from _pytest.compat import running_on_ci +from _pytest.config import _PluggyPlugin +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.nodes import Item +from _pytest.nodes import Node +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath +from _pytest.reports import BaseReport +from _pytest.reports import CollectReport +from _pytest.reports import TestReport + + +if TYPE_CHECKING: + from _pytest.main import Session + + +REPORT_COLLECTING_RESOLUTION = 0.5 + +KNOWN_TYPES = ( + "failed", + "passed", + "skipped", + "deselected", + "xfailed", + "xpassed", + "warnings", + "error", + "subtests passed", + "subtests failed", + "subtests skipped", +) + +_REPORTCHARS_DEFAULT = "fE" + + +class MoreQuietAction(argparse.Action): + """A modified copy of the argparse count action which counts down and updates + the legacy quiet attribute at the same time. + + Used to unify verbosity handling. + """ + + def __init__( + self, + option_strings: Sequence[str], + dest: str, + default: object = None, + required: bool = False, + help: str | None = None, + ) -> None: + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + default=default, + required=required, + help=help, + ) + + def __call__( + self, + parser: argparse.ArgumentParser, + namespace: argparse.Namespace, + values: str | Sequence[object] | None, + option_string: str | None = None, + ) -> None: + new_count = getattr(namespace, self.dest, 0) - 1 + setattr(namespace, self.dest, new_count) + # todo Deprecate config.quiet + namespace.quiet = getattr(namespace, "quiet", 0) + 1 + + +class TestShortLogReport(NamedTuple): + """Used to store the test status result category, shortletter and verbose word. + For example ``"rerun", "R", ("RERUN", {"yellow": True})``. + + :ivar category: + The class of result, for example ``“passed”``, ``“skipped”``, ``“error”``, or the empty string. + + :ivar letter: + The short letter shown as testing progresses, for example ``"."``, ``"s"``, ``"E"``, or the empty string. + + :ivar word: + Verbose word is shown as testing progresses in verbose mode, for example ``"PASSED"``, ``"SKIPPED"``, + ``"ERROR"``, or the empty string. + """ + + category: str + letter: str + word: str | tuple[str, Mapping[str, bool]] + + +def pytest_addoption(parser: Parser) -> None: + group = parser.getgroup("terminal reporting", "Reporting", after="general") + group._addoption( # private to use reserved lower-case short option + "-v", + "--verbose", + action="count", + default=0, + dest="verbose", + help="Increase verbosity", + ) + group.addoption( + "--no-header", + action="store_true", + default=False, + dest="no_header", + help="Disable header", + ) + group.addoption( + "--no-summary", + action="store_true", + default=False, + dest="no_summary", + help="Disable summary", + ) + group.addoption( + "--no-fold-skipped", + action="store_false", + dest="fold_skipped", + default=True, + help="Do not fold skipped tests in short summary.", + ) + group.addoption( + "--force-short-summary", + action="store_true", + dest="force_short_summary", + default=False, + help="Force condensed summary output regardless of verbosity level.", + ) + group._addoption( # private to use reserved lower-case short option + "-q", + "--quiet", + action=MoreQuietAction, + default=0, + dest="verbose", + help="Decrease verbosity", + ) + group.addoption( + "--verbosity", + dest="verbose", + type=int, + default=0, + help="Set verbosity. Default: 0.", + ) + group._addoption( # private to use reserved lower-case short option + "-r", + action="store", + dest="reportchars", + default=_REPORTCHARS_DEFAULT, + metavar="chars", + help="Show extra test summary info as specified by chars: (f)ailed, " + "(E)rror, (s)kipped, (x)failed, (X)passed, " + "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " + "(w)arnings are enabled by default (see --disable-warnings), " + "'N' can be used to reset the list. (default: 'fE').", + ) + group.addoption( + "--disable-warnings", + "--disable-pytest-warnings", + default=False, + dest="disable_warnings", + action="store_true", + help="Disable warnings summary", + ) + group._addoption( # private to use reserved lower-case short option + "-l", + "--showlocals", + action="store_true", + dest="showlocals", + default=False, + help="Show locals in tracebacks (disabled by default)", + ) + group.addoption( + "--no-showlocals", + action="store_false", + dest="showlocals", + help="Hide locals in tracebacks (negate --showlocals passed through addopts)", + ) + group.addoption( + "--tb", + metavar="style", + action="store", + dest="tbstyle", + default="auto", + choices=["auto", "long", "short", "no", "line", "native"], + help="Traceback print mode (auto/long/short/line/native/no)", + ) + group.addoption( + "--xfail-tb", + action="store_true", + dest="xfail_tb", + default=False, + help="Show tracebacks for xfail (as long as --tb != no)", + ) + group.addoption( + "--show-capture", + action="store", + dest="showcapture", + choices=["no", "stdout", "stderr", "log", "all"], + default="all", + help="Controls how captured stdout/stderr/log is shown on failed tests. " + "Default: all.", + ) + group.addoption( + "--fulltrace", + "--full-trace", + action="store_true", + default=False, + help="Don't cut any tracebacks (default is to cut)", + ) + group.addoption( + "--color", + metavar="color", + action="store", + dest="color", + default="auto", + choices=["yes", "no", "auto"], + help="Color terminal output (yes/no/auto)", + ) + group.addoption( + "--code-highlight", + default="yes", + choices=["yes", "no"], + help="Whether code should be highlighted (only if --color is also enabled). " + "Default: yes.", + ) + + parser.addini( + "console_output_style", + help='Console output: "classic", or with additional progress information ' + '("progress" (percentage) | "count" | "progress-even-when-capture-no" (forces ' + "progress even when capture=no)", + default="progress", + ) + Config._add_verbosity_ini( + parser, + Config.VERBOSITY_TEST_CASES, + help=( + "Specify a verbosity level for test case execution, overriding the main level. " + "Higher levels will provide more detailed information about each test case executed." + ), + ) + + +def pytest_configure(config: Config) -> None: + reporter = TerminalReporter(config, sys.stdout) + config.pluginmanager.register(reporter, "terminalreporter") + if config.option.debug or config.option.traceconfig: + + def mywriter(tags, args): + msg = " ".join(map(str, args)) + reporter.write_line("[traceconfig] " + msg) + + config.trace.root.setprocessor("pytest:config", mywriter) + + # See terminalprogress.py. + # On Windows it's safe to load by default. + if sys.platform == "win32": + config.pluginmanager.import_plugin("terminalprogress") + + +def getreportopt(config: Config) -> str: + reportchars: str = config.option.reportchars + + old_aliases = {"F", "S"} + reportopts = "" + for char in reportchars: + if char in old_aliases: + char = char.lower() + if char == "a": + reportopts = "sxXEf" + elif char == "A": + reportopts = "PpsxXEf" + elif char == "N": + reportopts = "" + elif char not in reportopts: + reportopts += char + + if not config.option.disable_warnings and "w" not in reportopts: + reportopts = "w" + reportopts + elif config.option.disable_warnings and "w" in reportopts: + reportopts = reportopts.replace("w", "") + + return reportopts + + +@hookimpl(trylast=True) # after _pytest.runner +def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str]: + letter = "F" + if report.passed: + letter = "." + elif report.skipped: + letter = "s" + + outcome: str = report.outcome + if report.when in ("collect", "setup", "teardown") and outcome == "failed": + outcome = "error" + letter = "E" + + return outcome, letter, outcome.upper() + + +@dataclasses.dataclass +class WarningReport: + """Simple structure to hold warnings information captured by ``pytest_warning_recorded``. + + :ivar str message: + User friendly message about the warning. + :ivar str|None nodeid: + nodeid that generated the warning (see ``get_location``). + :ivar tuple fslocation: + File system location of the source of the warning (see ``get_location``). + """ + + message: str + nodeid: str | None = None + fslocation: tuple[str, int] | None = None + + count_towards_summary: ClassVar = True + + def get_location(self, config: Config) -> str | None: + """Return the more user-friendly information about the location of a warning, or None.""" + if self.nodeid: + return self.nodeid + if self.fslocation: + filename, linenum = self.fslocation + relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) + return f"{relpath}:{linenum}" + return None + + +@final +class TerminalReporter: + def __init__(self, config: Config, file: TextIO | None = None) -> None: + import _pytest.config + + self.config = config + self._numcollected = 0 + self._session: Session | None = None + self._showfspath: bool | None = None + + self.stats: dict[str, list[Any]] = {} + self._main_color: str | None = None + self._known_types: list[str] | None = None + self.startpath = config.invocation_params.dir + if file is None: + file = sys.stdout + self._tw = _pytest.config.create_terminal_writer(config, file) + self._screen_width = self._tw.fullwidth + self.currentfspath: None | Path | str | int = None + self.reportchars = getreportopt(config) + self.foldskipped = config.option.fold_skipped + self.hasmarkup = self._tw.hasmarkup + # isatty should be a method but was wrongly implemented as a boolean. + # We use CallableBool here to support both. + self.isatty = compat.CallableBool(file.isatty()) + self._progress_nodeids_reported: set[str] = set() + self._timing_nodeids_reported: set[str] = set() + self._show_progress_info = self._determine_show_progress_info() + self._collect_report_last_write = timing.Instant() + self._already_displayed_warnings: int | None = None + self._keyboardinterrupt_memo: ExceptionRepr | None = None + + def _determine_show_progress_info( + self, + ) -> Literal["progress", "count", "times", False]: + """Return whether we should display progress information based on the current config.""" + # do not show progress if we are not capturing output (#3038) unless explicitly + # overridden by progress-even-when-capture-no + if ( + self.config.getoption("capture", "no") == "no" + and self.config.getini("console_output_style") + != "progress-even-when-capture-no" + ): + return False + # do not show progress if we are showing fixture setup/teardown + if self.config.getoption("setupshow", False): + return False + cfg: str = self.config.getini("console_output_style") + if cfg in {"progress", "progress-even-when-capture-no"}: + return "progress" + elif cfg == "count": + return "count" + elif cfg == "times": + return "times" + else: + return False + + @property + def verbosity(self) -> int: + verbosity: int = self.config.option.verbose + return verbosity + + @property + def showheader(self) -> bool: + return self.verbosity >= 0 + + @property + def no_header(self) -> bool: + return bool(self.config.option.no_header) + + @property + def no_summary(self) -> bool: + return bool(self.config.option.no_summary) + + @property + def showfspath(self) -> bool: + if self._showfspath is None: + return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) >= 0 + return self._showfspath + + @showfspath.setter + def showfspath(self, value: bool | None) -> None: + self._showfspath = value + + @property + def showlongtestinfo(self) -> bool: + return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) > 0 + + @property + def reported_progress(self) -> int: + """The amount of items reported in the progress so far. + + :meta private: + """ + return len(self._progress_nodeids_reported) + + def hasopt(self, char: str) -> bool: + char = {"xfailed": "x", "skipped": "s"}.get(char, char) + return char in self.reportchars + + def write_fspath_result(self, nodeid: str, res: str, **markup: bool) -> None: + fspath = self.config.rootpath / nodeid.split("::")[0] + if self.currentfspath is None or fspath != self.currentfspath: + if self.currentfspath is not None and self._show_progress_info: + self._write_progress_information_filling_space() + self.currentfspath = fspath + relfspath = bestrelpath(self.startpath, fspath) + self._tw.line() + self._tw.write(relfspath + " ") + self._tw.write(res, flush=True, **markup) + + def write_ensure_prefix(self, prefix: str, extra: str = "", **kwargs) -> None: + if self.currentfspath != prefix: + self._tw.line() + self.currentfspath = prefix + self._tw.write(prefix) + if extra: + self._tw.write(extra, **kwargs) + self.currentfspath = -2 + + def ensure_newline(self) -> None: + if self.currentfspath: + self._tw.line() + self.currentfspath = None + + def wrap_write( + self, + content: str, + *, + flush: bool = False, + margin: int = 8, + line_sep: str = "\n", + **markup: bool, + ) -> None: + """Wrap message with margin for progress info.""" + width_of_current_line = self._tw.width_of_current_line + wrapped = line_sep.join( + textwrap.wrap( + " " * width_of_current_line + content, + width=self._screen_width - margin, + drop_whitespace=True, + replace_whitespace=False, + ), + ) + wrapped = wrapped[width_of_current_line:] + self._tw.write(wrapped, flush=flush, **markup) + + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + self._tw.write(content, flush=flush, **markup) + + def write_raw(self, content: str, *, flush: bool = False) -> None: + self._tw.write_raw(content, flush=flush) + + def flush(self) -> None: + self._tw.flush() + + def write_line(self, line: str | bytes, **markup: bool) -> None: + if not isinstance(line, str): + line = str(line, errors="replace") + self.ensure_newline() + self._tw.line(line, **markup) + + def rewrite(self, line: str, **markup: bool) -> None: + """Rewinds the terminal cursor to the beginning and writes the given line. + + :param erase: + If True, will also add spaces until the full terminal width to ensure + previous lines are properly erased. + + The rest of the keyword arguments are markup instructions. + """ + erase = markup.pop("erase", False) + if erase: + fill_count = self._tw.fullwidth - len(line) - 1 + fill = " " * fill_count + else: + fill = "" + line = str(line) + self._tw.write("\r" + line + fill, **markup) + + def write_sep( + self, + sep: str, + title: str | None = None, + fullwidth: int | None = None, + **markup: bool, + ) -> None: + self.ensure_newline() + self._tw.sep(sep, title, fullwidth, **markup) + + def section(self, title: str, sep: str = "=", **kw: bool) -> None: + self._tw.sep(sep, title, **kw) + + def line(self, msg: str, **kw: bool) -> None: + self._tw.line(msg, **kw) + + def _add_stats(self, category: str, items: Sequence[Any]) -> None: + set_main_color = category not in self.stats + self.stats.setdefault(category, []).extend(items) + if set_main_color: + self._set_main_color() + + def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: + for line in str(excrepr).split("\n"): + self.write_line("INTERNALERROR> " + line) + return True + + def pytest_warning_recorded( + self, + warning_message: warnings.WarningMessage, + nodeid: str, + ) -> None: + from _pytest.warnings import warning_record_to_str + + fslocation = warning_message.filename, warning_message.lineno + message = warning_record_to_str(warning_message) + + warning_report = WarningReport( + fslocation=fslocation, message=message, nodeid=nodeid + ) + self._add_stats("warnings", [warning_report]) + + def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: + if self.config.option.traceconfig: + msg = f"PLUGIN registered: {plugin}" + # XXX This event may happen during setup/teardown time + # which unfortunately captures our output here + # which garbles our output if we use self.write_line. + self.write_line(msg) + + def pytest_deselected(self, items: Sequence[Item]) -> None: + self._add_stats("deselected", items) + + def pytest_runtest_logstart( + self, nodeid: str, location: tuple[str, int | None, str] + ) -> None: + fspath, lineno, domain = location + # Ensure that the path is printed before the + # 1st test of a module starts running. + if self.showlongtestinfo: + line = self._locationline(nodeid, fspath, lineno, domain) + self.write_ensure_prefix(line, "") + self.flush() + elif self.showfspath: + self.write_fspath_result(nodeid, "") + self.flush() + + def pytest_runtest_logreport(self, report: TestReport) -> None: + self._tests_ran = True + rep = report + + res = TestShortLogReport( + *self.config.hook.pytest_report_teststatus(report=rep, config=self.config) + ) + category, letter, word = res.category, res.letter, res.word + if not isinstance(word, tuple): + markup = None + else: + word, markup = word + self._add_stats(category, [rep]) + if not letter and not word: + # Probably passed setup/teardown. + return + if markup is None: + was_xfail = hasattr(report, "wasxfail") + if rep.passed and not was_xfail: + markup = {"green": True} + elif rep.passed and was_xfail: + markup = {"yellow": True} + elif rep.failed: + markup = {"red": True} + elif rep.skipped: + markup = {"yellow": True} + else: + markup = {} + self._progress_nodeids_reported.add(rep.nodeid) + if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0: + self._tw.write(letter, **markup) + # When running in xdist, the logreport and logfinish of multiple + # items are interspersed, e.g. `logreport`, `logreport`, + # `logfinish`, `logfinish`. To avoid the "past edge" calculation + # from getting confused and overflowing (#7166), do the past edge + # printing here and not in logfinish, except for the 100% which + # should only be printed after all teardowns are finished. + if self._show_progress_info and not self._is_last_item: + self._write_progress_information_if_past_edge() + else: + line = self._locationline(rep.nodeid, *rep.location) + running_xdist = hasattr(rep, "node") + if not running_xdist: + self.write_ensure_prefix(line, word, **markup) + if rep.skipped or hasattr(report, "wasxfail"): + reason = _get_raw_skip_reason(rep) + if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) < 2: + available_width = ( + (self._tw.fullwidth - self._tw.width_of_current_line) + - len(" [100%]") + - 1 + ) + formatted_reason = _format_trimmed( + " ({})", reason, available_width + ) + else: + formatted_reason = f" ({reason})" + + if reason and formatted_reason is not None: + self.wrap_write(formatted_reason) + if self._show_progress_info: + self._write_progress_information_filling_space() + else: + self.ensure_newline() + self._tw.write(f"[{rep.node.gateway.id}]") + if self._show_progress_info: + self._tw.write( + self._get_progress_information_message() + " ", cyan=True + ) + else: + self._tw.write(" ") + self._tw.write(word, **markup) + self._tw.write(" " + line) + self.currentfspath = -2 + self.flush() + + @property + def _is_last_item(self) -> bool: + assert self._session is not None + return self.reported_progress == self._session.testscollected + + @hookimpl(wrapper=True) + def pytest_runtestloop(self) -> Generator[None, object, object]: + result = yield + + # Write the final/100% progress -- deferred until the loop is complete. + if ( + self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 + and self._show_progress_info + and self.reported_progress + ): + self._write_progress_information_filling_space() + + return result + + def _get_progress_information_message(self) -> str: + assert self._session + collected = self._session.testscollected + if self._show_progress_info == "count": + if collected: + progress = self.reported_progress + counter_format = f"{{:{len(str(collected))}d}}" + format_string = f" [{counter_format}/{{}}]" + return format_string.format(progress, collected) + return f" [ {collected} / {collected} ]" + if self._show_progress_info == "times": + if not collected: + return "" + all_reports = ( + self._get_reports_to_display("passed") + + self._get_reports_to_display("xpassed") + + self._get_reports_to_display("failed") + + self._get_reports_to_display("xfailed") + + self._get_reports_to_display("skipped") + + self._get_reports_to_display("error") + + self._get_reports_to_display("") + ) + current_location = all_reports[-1].location[0] + not_reported = [ + r for r in all_reports if r.nodeid not in self._timing_nodeids_reported + ] + tests_in_module = sum( + i.location[0] == current_location for i in self._session.items + ) + tests_completed = sum( + r.when == "setup" + for r in not_reported + if r.location[0] == current_location + ) + last_in_module = tests_completed == tests_in_module + if self.showlongtestinfo or last_in_module: + self._timing_nodeids_reported.update(r.nodeid for r in not_reported) + return format_node_duration( + sum(r.duration for r in not_reported if isinstance(r, TestReport)) + ) + return "" + if collected: + return f" [{self.reported_progress * 100 // collected:3d}%]" + return " [100%]" + + def _write_progress_information_if_past_edge(self) -> None: + w = self._width_of_current_line + if self._show_progress_info == "count": + assert self._session + num_tests = self._session.testscollected + progress_length = len(f" [{num_tests}/{num_tests}]") + elif self._show_progress_info == "times": + progress_length = len(" 99h 59m") + else: + progress_length = len(" [100%]") + past_edge = w + progress_length + 1 >= self._screen_width + if past_edge: + main_color, _ = self._get_main_color() + msg = self._get_progress_information_message() + self._tw.write(msg + "\n", **{main_color: True}) + + def _write_progress_information_filling_space(self) -> None: + color, _ = self._get_main_color() + msg = self._get_progress_information_message() + w = self._width_of_current_line + fill = self._tw.fullwidth - w - 1 + self.write(msg.rjust(fill), flush=True, **{color: True}) + + @property + def _width_of_current_line(self) -> int: + """Return the width of the current line.""" + return self._tw.width_of_current_line + + def pytest_collection(self) -> None: + if self.isatty(): + if self.config.option.verbose >= 0: + self.write("collecting ... ", flush=True, bold=True) + elif self.config.option.verbose >= 1: + self.write("collecting ... ", flush=True, bold=True) + + def pytest_collectreport(self, report: CollectReport) -> None: + if report.failed: + self._add_stats("error", [report]) + elif report.skipped: + self._add_stats("skipped", [report]) + items = [x for x in report.result if isinstance(x, Item)] + self._numcollected += len(items) + if self.isatty(): + self.report_collect() + + def report_collect(self, final: bool = False) -> None: + if self.config.option.verbose < 0: + return + + if not final: + # Only write the "collecting" report every `REPORT_COLLECTING_RESOLUTION`. + if ( + self._collect_report_last_write.elapsed().seconds + < REPORT_COLLECTING_RESOLUTION + ): + return + self._collect_report_last_write = timing.Instant() + + errors = len(self.stats.get("error", [])) + skipped = len(self.stats.get("skipped", [])) + deselected = len(self.stats.get("deselected", [])) + selected = self._numcollected - deselected + line = "collected " if final else "collecting " + line += ( + str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") + ) + if errors: + line += f" / {errors} error{'s' if errors != 1 else ''}" + if deselected: + line += f" / {deselected} deselected" + if skipped: + line += f" / {skipped} skipped" + if self._numcollected > selected: + line += f" / {selected} selected" + if self.isatty(): + self.rewrite(line, bold=True, erase=True) + if final: + self.write("\n") + else: + self.write_line(line) + + @hookimpl(trylast=True) + def pytest_sessionstart(self, session: Session) -> None: + self._session = session + self._session_start = timing.Instant() + if not self.showheader: + return + self.write_sep("=", "test session starts", bold=True) + verinfo = platform.python_version() + if not self.no_header: + msg = f"platform {sys.platform} -- Python {verinfo}" + pypy_version_info = getattr(sys, "pypy_version_info", None) + if pypy_version_info: + verinfo = ".".join(map(str, pypy_version_info[:3])) + msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" + msg += f", pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}" + if ( + self.verbosity > 0 + or self.config.option.debug + or getattr(self.config.option, "pastebin", None) + ): + msg += " -- " + str(sys.executable) + self.write_line(msg) + lines = self.config.hook.pytest_report_header( + config=self.config, start_path=self.startpath + ) + self._write_report_lines_from_hooks(lines) + + def _write_report_lines_from_hooks( + self, lines: Sequence[str | Sequence[str]] + ) -> None: + for line_or_lines in reversed(lines): + if isinstance(line_or_lines, str): + self.write_line(line_or_lines) + else: + for line in line_or_lines: + self.write_line(line) + + def pytest_report_header(self, config: Config) -> list[str]: + result = [f"rootdir: {config.rootpath}"] + + if config.inipath: + warning = "" + if config._ignored_config_files: + warning = f" (WARNING: ignoring pytest config in {', '.join(config._ignored_config_files)}!)" + result.append( + "configfile: " + bestrelpath(config.rootpath, config.inipath) + warning + ) + + if config.args_source == Config.ArgsSource.TESTPATHS: + testpaths: list[str] = config.getini("testpaths") + result.append("testpaths: {}".format(", ".join(testpaths))) + + plugininfo = config.pluginmanager.list_plugin_distinfo() + if plugininfo: + result.append( + "plugins: {}".format(", ".join(_plugin_nameversions(plugininfo))) + ) + return result + + def pytest_collection_finish(self, session: Session) -> None: + self.report_collect(True) + + lines = self.config.hook.pytest_report_collectionfinish( + config=self.config, + start_path=self.startpath, + items=session.items, + ) + self._write_report_lines_from_hooks(lines) + + if self.config.getoption("collectonly"): + if session.items: + if self.config.option.verbose > -1: + self._tw.line("") + self._printcollecteditems(session.items) + + failed = self.stats.get("failed") + if failed: + self._tw.sep("!", "collection failures") + for rep in failed: + rep.toterminal(self._tw) + + def _printcollecteditems(self, items: Sequence[Item]) -> None: + test_cases_verbosity = self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) + if test_cases_verbosity < 0: + if test_cases_verbosity < -1: + counts = Counter(item.nodeid.split("::", 1)[0] for item in items) + for name, count in sorted(counts.items()): + self._tw.line(f"{name}: {count}") + else: + for item in items: + self._tw.line(item.nodeid) + return + stack: list[Node] = [] + indent = "" + for item in items: + needed_collectors = item.listchain()[1:] # strip root node + while stack: + if stack == needed_collectors[: len(stack)]: + break + stack.pop() + for col in needed_collectors[len(stack) :]: + stack.append(col) + indent = (len(stack) - 1) * " " + self._tw.line(f"{indent}{col}") + if test_cases_verbosity >= 1: + obj = getattr(col, "obj", None) + doc = inspect.getdoc(obj) if obj else None + if doc: + for line in doc.splitlines(): + self._tw.line("{}{}".format(indent + " ", line)) + + @hookimpl(wrapper=True) + def pytest_sessionfinish( + self, session: Session, exitstatus: int | ExitCode + ) -> Generator[None]: + result = yield + self._tw.line("") + summary_exit_codes = ( + ExitCode.OK, + ExitCode.TESTS_FAILED, + ExitCode.INTERRUPTED, + ExitCode.USAGE_ERROR, + ExitCode.NO_TESTS_COLLECTED, + ) + if exitstatus in summary_exit_codes and not self.no_summary: + self.config.hook.pytest_terminal_summary( + terminalreporter=self, exitstatus=exitstatus, config=self.config + ) + if session.shouldfail: + self.write_sep("!", str(session.shouldfail), red=True) + if exitstatus == ExitCode.INTERRUPTED: + self._report_keyboardinterrupt() + self._keyboardinterrupt_memo = None + elif session.shouldstop: + self.write_sep("!", str(session.shouldstop), red=True) + self.summary_stats() + return result + + @hookimpl(wrapper=True) + def pytest_terminal_summary(self) -> Generator[None]: + self.summary_errors() + self.summary_failures() + self.summary_xfailures() + self.summary_warnings() + self.summary_passes() + self.summary_xpasses() + try: + return (yield) + finally: + self.short_test_summary() + # Display any extra warnings from teardown here (if any). + self.summary_warnings() + + def pytest_keyboard_interrupt(self, excinfo: ExceptionInfo[BaseException]) -> None: + self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) + + def pytest_unconfigure(self) -> None: + if self._keyboardinterrupt_memo is not None: + self._report_keyboardinterrupt() + + def _report_keyboardinterrupt(self) -> None: + excrepr = self._keyboardinterrupt_memo + assert excrepr is not None + assert excrepr.reprcrash is not None + msg = excrepr.reprcrash.message + self.write_sep("!", msg) + if "KeyboardInterrupt" in msg: + if self.config.option.fulltrace: + excrepr.toterminal(self._tw) + else: + excrepr.reprcrash.toterminal(self._tw) + self._tw.line( + "(to show a full traceback on KeyboardInterrupt use --full-trace)", + yellow=True, + ) + + def _locationline( + self, nodeid: str, fspath: str, lineno: int | None, domain: str + ) -> str: + def mkrel(nodeid: str) -> str: + line = self.config.cwd_relative_nodeid(nodeid) + if domain and line.endswith(domain): + line = line[: -len(domain)] + values = domain.split("[") + values[0] = values[0].replace(".", "::") # don't replace '.' in params + line += "[".join(values) + return line + + # fspath comes from testid which has a "/"-normalized path. + if fspath: + res = mkrel(nodeid) + if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( + "\\", nodes.SEP + ): + res += " <- " + bestrelpath(self.startpath, Path(fspath)) + else: + res = "[location]" + return res + " " + + def _getfailureheadline(self, rep): + head_line = rep.head_line + if head_line: + return head_line + return "test session" # XXX? + + def _getcrashline(self, rep): + try: + return str(rep.longrepr.reprcrash) + except AttributeError: + try: + return str(rep.longrepr)[:50] + except AttributeError: + return "" + + # + # Summaries for sessionfinish. + # + def getreports(self, name: str): + return [x for x in self.stats.get(name, ()) if not hasattr(x, "_pdbshown")] + + def summary_warnings(self) -> None: + if self.hasopt("w"): + all_warnings: list[WarningReport] | None = self.stats.get("warnings") + if not all_warnings: + return + + final = self._already_displayed_warnings is not None + if final: + warning_reports = all_warnings[self._already_displayed_warnings :] + else: + warning_reports = all_warnings + self._already_displayed_warnings = len(warning_reports) + if not warning_reports: + return + + reports_grouped_by_message: dict[str, list[WarningReport]] = {} + for wr in warning_reports: + reports_grouped_by_message.setdefault(wr.message, []).append(wr) + + def collapsed_location_report(reports: list[WarningReport]) -> str: + locations = [] + for w in reports: + location = w.get_location(self.config) + if location: + locations.append(location) + + if len(locations) < 10: + return "\n".join(map(str, locations)) + + counts_by_filename = Counter( + str(loc).split("::", 1)[0] for loc in locations + ) + return "\n".join( + "{}: {} warning{}".format(k, v, "s" if v > 1 else "") + for k, v in counts_by_filename.items() + ) + + title = "warnings summary (final)" if final else "warnings summary" + self.write_sep("=", title, yellow=True, bold=False) + for message, message_reports in reports_grouped_by_message.items(): + maybe_location = collapsed_location_report(message_reports) + if maybe_location: + self._tw.line(maybe_location) + lines = message.splitlines() + indented = "\n".join(" " + x for x in lines) + message = indented.rstrip() + else: + message = message.rstrip() + self._tw.line(message) + self._tw.line() + self._tw.line( + "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html" + ) + + def summary_passes(self) -> None: + self.summary_passes_combined("passed", "PASSES", "P") + + def summary_xpasses(self) -> None: + self.summary_passes_combined("xpassed", "XPASSES", "X") + + def summary_passes_combined( + self, which_reports: str, sep_title: str, needed_opt: str + ) -> None: + if self.config.option.tbstyle != "no": + if self.hasopt(needed_opt): + reports: list[TestReport] = self.getreports(which_reports) + if not reports: + return + self.write_sep("=", sep_title) + for rep in reports: + if rep.sections: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, green=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def _get_teardown_reports(self, nodeid: str) -> list[TestReport]: + reports = self.getreports("") + return [ + report + for report in reports + if report.when == "teardown" and report.nodeid == nodeid + ] + + def _handle_teardown_sections(self, nodeid: str) -> None: + for report in self._get_teardown_reports(nodeid): + self.print_teardown_sections(report) + + def print_teardown_sections(self, rep: TestReport) -> None: + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + if "teardown" in secname: + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_failures(self) -> None: + style = self.config.option.tbstyle + self.summary_failures_combined("failed", "FAILURES", style=style) + + def summary_xfailures(self) -> None: + show_tb = self.config.option.xfail_tb + style = self.config.option.tbstyle if show_tb else "no" + self.summary_failures_combined("xfailed", "XFAILURES", style=style) + + def summary_failures_combined( + self, + which_reports: str, + sep_title: str, + *, + style: str, + needed_opt: str | None = None, + ) -> None: + if style != "no": + if not needed_opt or self.hasopt(needed_opt): + reports: list[BaseReport] = self.getreports(which_reports) + if not reports: + return + self.write_sep("=", sep_title) + if style == "line": + for rep in reports: + line = self._getcrashline(rep) + self._outrep_summary(rep) + self.write_line(line) + else: + for rep in reports: + msg = self._getfailureheadline(rep) + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + self._handle_teardown_sections(rep.nodeid) + + def summary_errors(self) -> None: + if self.config.option.tbstyle != "no": + reports: list[BaseReport] = self.getreports("error") + if not reports: + return + self.write_sep("=", "ERRORS") + for rep in self.stats["error"]: + msg = self._getfailureheadline(rep) + if rep.when == "collect": + msg = "ERROR collecting " + msg + else: + msg = f"ERROR at {rep.when} of {msg}" + self.write_sep("_", msg, red=True, bold=True) + self._outrep_summary(rep) + + def _outrep_summary(self, rep: BaseReport) -> None: + rep.toterminal(self._tw) + showcapture = self.config.option.showcapture + if showcapture == "no": + return + for secname, content in rep.sections: + if showcapture != "all" and showcapture not in secname: + continue + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) + + def summary_stats(self) -> None: + if self.verbosity < -1: + return + + session_duration = self._session_start.elapsed() + (parts, main_color) = self.build_summary_stats_line() + line_parts = [] + + display_sep = self.verbosity >= 0 + if display_sep: + fullwidth = self._tw.fullwidth + for text, markup in parts: + with_markup = self._tw.markup(text, **markup) + if display_sep: + fullwidth += len(with_markup) - len(text) + line_parts.append(with_markup) + msg = ", ".join(line_parts) + + main_markup = {main_color: True} + duration = f" in {format_session_duration(session_duration.seconds)}" + duration_with_markup = self._tw.markup(duration, **main_markup) + if display_sep: + fullwidth += len(duration_with_markup) - len(duration) + msg += duration_with_markup + + if display_sep: + markup_for_end_sep = self._tw.markup("", **main_markup) + if markup_for_end_sep.endswith("\x1b[0m"): + markup_for_end_sep = markup_for_end_sep[:-4] + fullwidth += len(markup_for_end_sep) + msg += markup_for_end_sep + + if display_sep: + self.write_sep("=", msg, fullwidth=fullwidth, **main_markup) + else: + self.write_line(msg, **main_markup) + + def short_test_summary(self) -> None: + if not self.reportchars: + return + + def show_simple(lines: list[str], *, stat: str) -> None: + failed = self.stats.get(stat, []) + if not failed: + return + config = self.config + for rep in failed: + color = _color_for_type.get(stat, _color_for_type_default) + line = _get_line_with_reprcrash_message( + config, rep, self._tw, {color: True} + ) + lines.append(line) + + def show_xfailed(lines: list[str]) -> None: + xfailed = self.stats.get("xfailed", []) + for rep in xfailed: + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.wasxfail + if reason: + line += " - " + str(reason) + + lines.append(line) + + def show_xpassed(lines: list[str]) -> None: + xpassed = self.stats.get("xpassed", []) + for rep in xpassed: + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.wasxfail + if reason: + line += " - " + str(reason) + lines.append(line) + + def show_skipped_folded(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get("skipped", []) + fskips = _folded_skips(self.startpath, skipped) if skipped else [] + if not fskips: + return + verbose_word, verbose_markup = skipped[0]._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + prefix = "Skipped: " + for num, fspath, lineno, reason in fskips: + if reason.startswith(prefix): + reason = reason[len(prefix) :] + if lineno is not None: + lines.append(f"{markup_word} [{num}] {fspath}:{lineno}: {reason}") + else: + lines.append(f"{markup_word} [{num}] {fspath}: {reason}") + + def show_skipped_unfolded(lines: list[str]) -> None: + skipped: list[CollectReport] = self.stats.get("skipped", []) + + for rep in skipped: + assert rep.longrepr is not None + assert isinstance(rep.longrepr, tuple), (rep, rep.longrepr) + assert len(rep.longrepr) == 3, (rep, rep.longrepr) + + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + self.config, {_color_for_type["warnings"]: True} + ) + markup_word = self._tw.markup(verbose_word, **verbose_markup) + nodeid = _get_node_id_with_markup(self._tw, self.config, rep) + line = f"{markup_word} {nodeid}" + reason = rep.longrepr[2] + if reason: + line += " - " + str(reason) + lines.append(line) + + def show_skipped(lines: list[str]) -> None: + if self.foldskipped: + show_skipped_folded(lines) + else: + show_skipped_unfolded(lines) + + REPORTCHAR_ACTIONS: Mapping[str, Callable[[list[str]], None]] = { + "x": show_xfailed, + "X": show_xpassed, + "f": partial(show_simple, stat="failed"), + "s": show_skipped, + "p": partial(show_simple, stat="passed"), + "E": partial(show_simple, stat="error"), + } + + lines: list[str] = [] + for char in self.reportchars: + action = REPORTCHAR_ACTIONS.get(char) + if action: # skipping e.g. "P" (passed with output) here. + action(lines) + + if lines: + self.write_sep("=", "short test summary info", cyan=True, bold=True) + for line in lines: + self.write_line(line) + + def _get_main_color(self) -> tuple[str, list[str]]: + if self._main_color is None or self._known_types is None or self._is_last_item: + self._set_main_color() + assert self._main_color + assert self._known_types + return self._main_color, self._known_types + + def _determine_main_color(self, unknown_type_seen: bool) -> str: + stats = self.stats + if "failed" in stats or "error" in stats: + main_color = "red" + elif "warnings" in stats or "xpassed" in stats or unknown_type_seen: + main_color = "yellow" + elif "passed" in stats or not self._is_last_item: + main_color = "green" + else: + main_color = "yellow" + return main_color + + def _set_main_color(self) -> None: + unknown_types: list[str] = [] + for found_type in self.stats: + if found_type: # setup/teardown reports have an empty key, ignore them + if found_type not in KNOWN_TYPES and found_type not in unknown_types: + unknown_types.append(found_type) + self._known_types = list(KNOWN_TYPES) + unknown_types + self._main_color = self._determine_main_color(bool(unknown_types)) + + def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: + """ + Build the parts used in the last summary stats line. + + The summary stats line is the line shown at the end, "=== 12 passed, 2 errors in Xs===". + + This function builds a list of the "parts" that make up for the text in that line, in + the example above it would be:: + + [ + ("12 passed", {"green": True}), + ("2 errors", {"red": True} + ] + + That last dict for each line is a "markup dictionary", used by TerminalWriter to + color output. + + The final color of the line is also determined by this function, and is the second + element of the returned tuple. + """ + if self.config.getoption("collectonly"): + return self._build_collect_only_summary_stats_line() + else: + return self._build_normal_summary_stats_line() + + def _get_reports_to_display(self, key: str) -> list[Any]: + """Get test/collection reports for the given status key, such as `passed` or `error`.""" + reports = self.stats.get(key, []) + return [x for x in reports if getattr(x, "count_towards_summary", True)] + + def _build_normal_summary_stats_line( + self, + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: + main_color, known_types = self._get_main_color() + parts = [] + + for key in known_types: + reports = self._get_reports_to_display(key) + if reports: + count = len(reports) + color = _color_for_type.get(key, _color_for_type_default) + markup = {color: True, "bold": color == main_color} + parts.append(("%d %s" % pluralize(count, key), markup)) # noqa: UP031 + + if not parts: + parts = [("no tests ran", {_color_for_type_default: True})] + + return parts, main_color + + def _build_collect_only_summary_stats_line( + self, + ) -> tuple[list[tuple[str, dict[str, bool]]], str]: + deselected = len(self._get_reports_to_display("deselected")) + errors = len(self._get_reports_to_display("error")) + + if self._numcollected == 0: + parts = [("no tests collected", {"yellow": True})] + main_color = "yellow" + + elif deselected == 0: + main_color = "green" + collected_output = "%d %s collected" % pluralize(self._numcollected, "test") # noqa: UP031 + parts = [(collected_output, {main_color: True})] + else: + all_tests_were_deselected = self._numcollected == deselected + if all_tests_were_deselected: + main_color = "yellow" + collected_output = f"no tests collected ({deselected} deselected)" + else: + main_color = "green" + selected = self._numcollected - deselected + collected_output = f"{selected}/{self._numcollected} tests collected ({deselected} deselected)" + + parts = [(collected_output, {main_color: True})] + + if errors: + main_color = _color_for_type["error"] + parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] # noqa: UP031 + + return parts, main_color + + +def _get_node_id_with_markup(tw: TerminalWriter, config: Config, rep: BaseReport): + nodeid = config.cwd_relative_nodeid(rep.nodeid) + path, *parts = nodeid.split("::") + if parts: + parts_markup = tw.markup("::".join(parts), bold=True) + return path + "::" + parts_markup + else: + return path + + +def _format_trimmed(format: str, msg: str, available_width: int) -> str | None: + """Format msg into format, ellipsizing it if doesn't fit in available_width. + + Returns None if even the ellipsis can't fit. + """ + # Only use the first line. + i = msg.find("\n") + if i != -1: + msg = msg[:i] + + ellipsis = "..." + format_width = wcswidth(format.format("")) + if format_width + len(ellipsis) > available_width: + return None + + if format_width + wcswidth(msg) > available_width: + available_width -= len(ellipsis) + msg = msg[:available_width] + while format_width + wcswidth(msg) > available_width: + msg = msg[:-1] + msg += ellipsis + + return format.format(msg) + + +def _get_line_with_reprcrash_message( + config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: dict[str, bool] +) -> str: + """Get summary line for a report, trying to add reprcrash message.""" + verbose_word, verbose_markup = rep._get_verbose_word_with_markup( + config, word_markup + ) + word = tw.markup(verbose_word, **verbose_markup) + node = _get_node_id_with_markup(tw, config, rep) + + line = f"{word} {node}" + line_width = wcswidth(line) + + msg: str | None + try: + if isinstance(rep.longrepr, str): + msg = rep.longrepr + else: + # Type ignored intentionally -- possible AttributeError expected. + msg = rep.longrepr.reprcrash.message # type: ignore[union-attr] + except AttributeError: + pass + else: + if ( + running_on_ci() or config.option.verbose >= 2 + ) and not config.option.force_short_summary: + msg = f" - {msg}" + else: + available_width = tw.fullwidth - line_width + msg = _format_trimmed(" - {}", msg, available_width) + if msg is not None: + line += msg + + return line + + +def _folded_skips( + startpath: Path, + skipped: Sequence[CollectReport], +) -> list[tuple[int, str, int | None, str]]: + d: dict[tuple[str, int | None, str], list[CollectReport]] = {} + for event in skipped: + assert event.longrepr is not None + assert isinstance(event.longrepr, tuple), (event, event.longrepr) + assert len(event.longrepr) == 3, (event, event.longrepr) + fspath, lineno, reason = event.longrepr + # For consistency, report all fspaths in relative form. + fspath = bestrelpath(startpath, Path(fspath)) + keywords = getattr(event, "keywords", {}) + # Folding reports with global pytestmark variable. + # This is a workaround, because for now we cannot identify the scope of a skip marker + # TODO: Revisit after marks scope would be fixed. + if ( + event.when == "setup" + and "skip" in keywords + and "pytestmark" not in keywords + ): + key: tuple[str, int | None, str] = (fspath, None, reason) + else: + key = (fspath, lineno, reason) + d.setdefault(key, []).append(event) + values: list[tuple[int, str, int | None, str]] = [] + for key, events in d.items(): + values.append((len(events), *key)) + return values + + +_color_for_type = { + "failed": "red", + "error": "red", + "warnings": "yellow", + "passed": "green", + "subtests passed": "green", + "subtests failed": "red", +} +_color_for_type_default = "yellow" + + +def pluralize(count: int, noun: str) -> tuple[int, str]: + # No need to pluralize words such as `failed` or `passed`. + if noun not in ["error", "warnings", "test"]: + return count, noun + + # The `warnings` key is plural. To avoid API breakage, we keep it that way but + # set it to singular here so we can determine plurality in the same way as we do + # for `error`. + noun = noun.replace("warnings", "warning") + + return count, noun + "s" if count != 1 else noun + + +def _plugin_nameversions(plugininfo) -> list[str]: + values: list[str] = [] + for plugin, dist in plugininfo: + # Gets us name and version! + name = f"{dist.project_name}-{dist.version}" + # Questionable convenience, but it keeps things short. + if name.startswith("pytest-"): + name = name[7:] + # We decided to print python package names they can have more than one plugin. + if name not in values: + values.append(name) + return values + + +def format_session_duration(seconds: float) -> str: + """Format the given seconds in a human readable manner to show in the final summary.""" + if seconds < 60: + return f"{seconds:.2f}s" + else: + dt = datetime.timedelta(seconds=int(seconds)) + return f"{seconds:.2f}s ({dt})" + + +def format_node_duration(seconds: float) -> str: + """Format the given seconds in a human readable manner to show in the test progress.""" + # The formatting is designed to be compact and readable, with at most 7 characters + # for durations below 100 hours. + if seconds < 0.00001: + return f" {seconds * 1000000:.3f}us" + if seconds < 0.0001: + return f" {seconds * 1000000:.2f}us" + if seconds < 0.001: + return f" {seconds * 1000000:.1f}us" + if seconds < 0.01: + return f" {seconds * 1000:.3f}ms" + if seconds < 0.1: + return f" {seconds * 1000:.2f}ms" + if seconds < 1: + return f" {seconds * 1000:.1f}ms" + if seconds < 60: + return f" {seconds:.3f}s" + if seconds < 3600: + return f" {seconds // 60:.0f}m {seconds % 60:.0f}s" + return f" {seconds // 3600:.0f}h {(seconds % 3600) // 60:.0f}m" + + +def _get_raw_skip_reason(report: TestReport) -> str: + """Get the reason string of a skip/xfail/xpass test report. + + The string is just the part given by the user. + """ + if hasattr(report, "wasxfail"): + reason = report.wasxfail + if reason.startswith("reason: "): + reason = reason[len("reason: ") :] + return reason + else: + assert report.skipped + assert isinstance(report.longrepr, tuple) + _, _, reason = report.longrepr + if reason.startswith("Skipped: "): + reason = reason[len("Skipped: ") :] + elif reason == "Skipped": + reason = "" + return reason + + +class TerminalProgressPlugin: + """Terminal progress reporting plugin using OSC 9;4 ANSI sequences. + + Emits OSC 9;4 sequences to indicate test progress to terminal + tabs/windows/etc. + + Not all terminal emulators support this feature. + + Ref: https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC + """ + + def __init__(self, tr: TerminalReporter) -> None: + self._tr = tr + self._session: Session | None = None + self._has_failures = False + + def _emit_progress( + self, + state: Literal["remove", "normal", "error", "indeterminate", "paused"], + progress: int | None = None, + ) -> None: + """Emit OSC 9;4 sequence for indicating progress to the terminal. + + :param state: + Progress state to set. + :param progress: + Progress value 0-100. Required for "normal", optional for "error" + and "paused", otherwise ignored. + """ + assert progress is None or 0 <= progress <= 100 + + # OSC 9;4 sequence: ESC ] 9 ; 4 ; state ; progress ST + # ST can be ESC \ or BEL. ESC \ seems better supported. + match state: + case "remove": + sequence = "\x1b]9;4;0;\x1b\\" + case "normal": + assert progress is not None + sequence = f"\x1b]9;4;1;{progress}\x1b\\" + case "error": + if progress is not None: + sequence = f"\x1b]9;4;2;{progress}\x1b\\" + else: + sequence = "\x1b]9;4;2;\x1b\\" + case "indeterminate": + sequence = "\x1b]9;4;3;\x1b\\" + case "paused": + if progress is not None: + sequence = f"\x1b]9;4;4;{progress}\x1b\\" + else: + sequence = "\x1b]9;4;4;\x1b\\" + + self._tr.write_raw(sequence, flush=True) + + @hookimpl + def pytest_sessionstart(self, session: Session) -> None: + self._session = session + # Show indeterminate progress during collection. + self._emit_progress("indeterminate") + + @hookimpl + def pytest_collection_finish(self) -> None: + assert self._session is not None + if self._session.testscollected > 0: + # Switch from indeterminate to 0% progress. + self._emit_progress("normal", 0) + + @hookimpl + def pytest_runtest_logreport(self, report: TestReport) -> None: + if report.failed: + self._has_failures = True + + # Let's consider the "call" phase for progress. + if report.when != "call": + return + + # Calculate and emit progress. + assert self._session is not None + collected = self._session.testscollected + if collected > 0: + reported = self._tr.reported_progress + progress = min(reported * 100 // collected, 100) + self._emit_progress("error" if self._has_failures else "normal", progress) + + @hookimpl + def pytest_sessionfinish(self) -> None: + self._emit_progress("remove") diff --git a/.venv/lib/python3.12/site-packages/_pytest/terminalprogress.py b/.venv/lib/python3.12/site-packages/_pytest/terminalprogress.py new file mode 100644 index 0000000..287f0d5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/terminalprogress.py @@ -0,0 +1,30 @@ +# A plugin to register the TerminalProgressPlugin plugin. +# +# This plugin is not loaded by default due to compatibility issues (#13896), +# but can be enabled in one of these ways: +# - The terminal plugin enables it in a few cases where it's safe, and not +# blocked by the user (using e.g. `-p no:terminalprogress`). +# - The user explicitly requests it, e.g. using `-p terminalprogress`. +# +# In a few years, if it's safe, we can consider enabling it by default. Then, +# this file will become unnecessary and can be inlined into terminal.py. + +from __future__ import annotations + +import os + +from _pytest.config import Config +from _pytest.config import hookimpl +from _pytest.terminal import TerminalProgressPlugin +from _pytest.terminal import TerminalReporter + + +@hookimpl(trylast=True) +def pytest_configure(config: Config) -> None: + reporter: TerminalReporter | None = config.pluginmanager.get_plugin( + "terminalreporter" + ) + + if reporter is not None and reporter.isatty() and os.environ.get("TERM") != "dumb": + plugin = TerminalProgressPlugin(reporter) + config.pluginmanager.register(plugin, name="terminalprogress-plugin") diff --git a/.venv/lib/python3.12/site-packages/_pytest/threadexception.py b/.venv/lib/python3.12/site-packages/_pytest/threadexception.py new file mode 100644 index 0000000..eb57783 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/threadexception.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import sys +import threading +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +class ThreadExceptionMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +thread_exceptions: StashKey[collections.deque[ThreadExceptionMeta | BaseException]] = ( + StashKey() +) + + +def collect_thread_exception(config: Config) -> None: + pop_thread_exception = config.stash[thread_exceptions].pop + errors: list[pytest.PytestUnhandledThreadExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_thread_exception() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process thread exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) + except pytest.PytestUnhandledThreadExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple thread exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[threading.ExceptHookArgs], object] +) -> None: + try: + try: + # We don't join threads here, so exceptions raised from any + # threads still running by the time _threading_atexits joins them + # do not get captured (see #13027). + collect_thread_exception(config) + finally: + threading.excepthook = prev_hook + finally: + del config.stash[thread_exceptions] + + +def thread_exception_hook( + args: threading.ExceptHookArgs, + /, + *, + append: Callable[[ThreadExceptionMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the excepthook finishes and before the metadata object is + # collected by a pytest hook + thread_name = "" if args.thread is None else args.thread.name + summary = f"Exception in thread {thread_name}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + args.exc_type, + args.exc_value, + args.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(args.thread) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + ThreadExceptionMeta( + # Compute these strings here as they might change later + msg=msg, + cause_msg=cause_msg, + exc_value=args.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_thread_exception and once by sys.excepthook + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = threading.excepthook + deque: collections.deque[ThreadExceptionMeta | BaseException] = collections.deque() + config.stash[thread_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + threading.excepthook = functools.partial(thread_exception_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_thread_exception(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_thread_exception(item.config) diff --git a/.venv/lib/python3.12/site-packages/_pytest/timing.py b/.venv/lib/python3.12/site-packages/_pytest/timing.py new file mode 100644 index 0000000..51c3db2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/timing.py @@ -0,0 +1,95 @@ +"""Indirection for time functions. + +We intentionally grab some "time" functions internally to avoid tests mocking "time" to affect +pytest runtime information (issue #185). + +Fixture "mock_timing" also interacts with this module for pytest's own tests. +""" + +from __future__ import annotations + +import dataclasses +from datetime import datetime +from datetime import timezone +from time import perf_counter +from time import sleep +from time import time +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from pytest import MonkeyPatch + + +@dataclasses.dataclass(frozen=True) +class Instant: + """ + Represents an instant in time, used to both get the timestamp value and to measure + the duration of a time span. + + Inspired by Rust's `std::time::Instant`. + """ + + # Creation time of this instant, using time.time(), to measure actual time. + # Note: using a `lambda` to correctly get the mocked time via `MockTiming`. + time: float = dataclasses.field(default_factory=lambda: time(), init=False) + + # Performance counter tick of the instant, used to measure precise elapsed time. + # Note: using a `lambda` to correctly get the mocked time via `MockTiming`. + perf_count: float = dataclasses.field( + default_factory=lambda: perf_counter(), init=False + ) + + def elapsed(self) -> Duration: + """Measure the duration since `Instant` was created.""" + return Duration(start=self, stop=Instant()) + + def as_utc(self) -> datetime: + """Instant as UTC datetime.""" + return datetime.fromtimestamp(self.time, timezone.utc) + + +@dataclasses.dataclass(frozen=True) +class Duration: + """A span of time as measured by `Instant.elapsed()`.""" + + start: Instant + stop: Instant + + @property + def seconds(self) -> float: + """Elapsed time of the duration in seconds, measured using a performance counter for precise timing.""" + return self.stop.perf_count - self.start.perf_count + + +@dataclasses.dataclass +class MockTiming: + """Mocks _pytest.timing with a known object that can be used to control timing in tests + deterministically. + + pytest itself should always use functions from `_pytest.timing` instead of `time` directly. + + This then allows us more control over time during testing, if testing code also + uses `_pytest.timing` functions. + + Time is static, and only advances through `sleep` calls, thus tests might sleep over large + numbers and obtain accurate time() calls at the end, making tests reliable and instant.""" + + _current_time: float = datetime(2020, 5, 22, 14, 20, 50).timestamp() + + def sleep(self, seconds: float) -> None: + self._current_time += seconds + + def time(self) -> float: + return self._current_time + + def patch(self, monkeypatch: MonkeyPatch) -> None: + # pylint: disable-next=import-self + from _pytest import timing # noqa: PLW0406 + + monkeypatch.setattr(timing, "sleep", self.sleep) + monkeypatch.setattr(timing, "time", self.time) + monkeypatch.setattr(timing, "perf_counter", self.time) + + +__all__ = ["perf_counter", "sleep", "time"] diff --git a/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py b/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py new file mode 100644 index 0000000..66ca9f1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py @@ -0,0 +1,337 @@ +# mypy: allow-untyped-defs +"""Support for providing temporary directories to test functions.""" + +from __future__ import annotations + +from collections.abc import Generator +import dataclasses +import os +from pathlib import Path +import re +from shutil import rmtree +import stat +import tempfile +from typing import Any +from typing import final +from typing import Literal + +from .pathlib import cleanup_dead_symlinks +from .pathlib import LOCK_TIMEOUT +from .pathlib import make_numbered_dir +from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf +from _pytest.compat import get_user_id +from _pytest.config import Config +from _pytest.config import ExitCode +from _pytest.config import hookimpl +from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest +from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Item +from _pytest.reports import TestReport +from _pytest.stash import StashKey + + +tmppath_result_key = StashKey[dict[str, bool]]() +RetentionType = Literal["all", "failed", "none"] + + +@final +@dataclasses.dataclass +class TempPathFactory: + """Factory for temporary directories under the common base temp directory, + as discussed at :ref:`temporary directory location and retention`. + """ + + _given_basetemp: Path | None + # pluggy TagTracerSub, not currently exposed, so Any. + _trace: Any + _basetemp: Path | None + _retention_count: int + _retention_policy: RetentionType + + def __init__( + self, + given_basetemp: Path | None, + retention_count: int, + retention_policy: RetentionType, + trace, + basetemp: Path | None = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + if given_basetemp is None: + self._given_basetemp = None + else: + # Use os.path.abspath() to get absolute path instead of resolve() as it + # does not work the same in all platforms (see #4427). + # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). + self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) + self._trace = trace + self._retention_count = retention_count + self._retention_policy = retention_policy + self._basetemp = basetemp + + @classmethod + def from_config( + cls, + config: Config, + *, + _ispytest: bool = False, + ) -> TempPathFactory: + """Create a factory according to pytest configuration. + + :meta private: + """ + check_ispytest(_ispytest) + count = int(config.getini("tmp_path_retention_count")) + if count < 0: + raise ValueError( + f"tmp_path_retention_count must be >= 0. Current input: {count}." + ) + + policy = config.getini("tmp_path_retention_policy") + if policy not in ("all", "failed", "none"): + raise ValueError( + f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." + ) + + return cls( + given_basetemp=config.option.basetemp, + trace=config.trace.get("tmpdir"), + retention_count=count, + retention_policy=policy, + _ispytest=True, + ) + + def _ensure_relative_to_basetemp(self, basename: str) -> str: + basename = os.path.normpath(basename) + if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): + raise ValueError(f"{basename} is not a normalized and relative path") + return basename + + def mktemp(self, basename: str, numbered: bool = True) -> Path: + """Create a new temporary directory managed by the factory. + + :param basename: + Directory base name, must be a relative path. + + :param numbered: + If ``True``, ensure the directory is unique by adding a numbered + suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` + means that this function will create directories named ``"foo-0"``, + ``"foo-1"``, ``"foo-2"`` and so on. + + :returns: + The path to the new directory. + """ + basename = self._ensure_relative_to_basetemp(basename) + if not numbered: + p = self.getbasetemp().joinpath(basename) + p.mkdir(mode=0o700) + else: + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) + self._trace("mktemp", p) + return p + + def getbasetemp(self) -> Path: + """Return the base temporary directory, creating it if needed. + + :returns: + The base temporary directory. + """ + if self._basetemp is not None: + return self._basetemp + + if self._given_basetemp is not None: + basetemp = self._given_basetemp + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir(mode=0o700) + basetemp = basetemp.resolve() + else: + from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") + temproot = Path(from_env or tempfile.gettempdir()).resolve() + user = get_user() or "unknown" + # use a sub-directory in the temproot to speed-up + # make_numbered_dir() call + rootdir = temproot.joinpath(f"pytest-of-{user}") + try: + rootdir.mkdir(mode=0o700, exist_ok=True) + except OSError: + # getuser() likely returned illegal characters for the platform, use unknown back off mechanism + rootdir = temproot.joinpath("pytest-of-unknown") + rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + # Don't follow symlinks, otherwise we're open to symlink-swapping + # TOCTOU vulnerability. + # This check makes us vulnerable to a DoS - a user can `mkdir + # /tmp/pytest-of-otheruser` and then `otheruser` will fail this + # check. For now we don't consider it a real problem. otheruser can + # change their TMPDIR or --basetemp, and maybe give the prankster a + # good scolding. + uid = get_user_id() + if uid is not None: + stat_follow_symlinks = ( + False if os.stat in os.supports_follow_symlinks else True + ) + rootdir_stat = rootdir.stat(follow_symlinks=stat_follow_symlinks) + if stat.S_ISLNK(rootdir_stat.st_mode): + raise OSError( + f"The temporary directory {rootdir} is a symbolic link. " + "Fix this and try again." + ) + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + chmod_follow_symlinks = ( + False if os.chmod in os.supports_follow_symlinks else True + ) + rootdir.chmod( + rootdir_stat.st_mode & ~0o077, + follow_symlinks=chmod_follow_symlinks, + ) + keep = self._retention_count + if self._retention_policy == "none": + keep = 0 + basetemp = make_numbered_dir_with_cleanup( + prefix="pytest-", + root=rootdir, + keep=keep, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, + ) + assert basetemp is not None, basetemp + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp + + +def get_user() -> str | None: + """Return the current user name, or None if getuser() does not work + in the current environment (see #1010).""" + try: + # In some exotic environments, getpass may not be importable. + import getpass + + return getpass.getuser() + except (ImportError, OSError, KeyError): + return None + + +def pytest_configure(config: Config) -> None: + """Create a TempPathFactory and attach it to the config object. + + This is to comply with existing plugins which expect the handler to be + available at pytest_configure time, but ideally should be moved entirely + to the tmp_path_factory session fixture. + """ + mp = MonkeyPatch() + config.add_cleanup(mp.undo) + _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) + mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) + + +def pytest_addoption(parser: Parser) -> None: + parser.addini( + "tmp_path_retention_count", + help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", + default="3", + # NOTE: Would have been better as an `int` but can't change it now. + type="string", + ) + + parser.addini( + "tmp_path_retention_policy", + help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " + "(all/failed/none)", + type="string", + default="all", + ) + + +@fixture(scope="session") +def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: + """Return a :class:`pytest.TempPathFactory` instance for the test session.""" + # Set dynamically by pytest_configure() above. + return request.config._tmp_path_factory # type: ignore + + +def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: + name = request.node.name + name = re.sub(r"[\W]", "_", name) + MAXVAL = 30 + name = name[:MAXVAL] + return factory.mktemp(name, numbered=True) + + +@fixture +def tmp_path( + request: FixtureRequest, tmp_path_factory: TempPathFactory +) -> Generator[Path]: + """Return a temporary directory (as :class:`pathlib.Path` object) + which is unique to each test function invocation. + The temporary directory is created as a subdirectory + of the base temporary directory, with configurable retention, + as discussed in :ref:`temporary directory location and retention`. + """ + path = _mk_tmp(request, tmp_path_factory) + yield path + + # Remove the tmpdir if the policy is "failed" and the test passed. + policy = tmp_path_factory._retention_policy + result_dict = request.node.stash[tmppath_result_key] + + if policy == "failed" and result_dict.get("call", True): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(path, ignore_errors=True) + + del request.node.stash[tmppath_result_key] + + +def pytest_sessionfinish(session, exitstatus: int | ExitCode): + """After each session, remove base directory if all the tests passed, + the policy is "failed", and the basetemp is not specified by a user. + """ + tmp_path_factory: TempPathFactory = session.config._tmp_path_factory + basetemp = tmp_path_factory._basetemp + if basetemp is None: + return + + policy = tmp_path_factory._retention_policy + if ( + exitstatus == 0 + and policy == "failed" + and tmp_path_factory._given_basetemp is None + ): + if basetemp.is_dir(): + # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, + # permissions, etc, in which case we ignore it. + rmtree(basetemp, ignore_errors=True) + + # Remove dead symlinks. + if basetemp.is_dir(): + cleanup_dead_symlinks(basetemp) + + +@hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_makereport( + item: Item, call +) -> Generator[None, TestReport, TestReport]: + rep = yield + assert rep.when is not None + empty: dict[str, bool] = {} + item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed + return rep diff --git a/.venv/lib/python3.12/site-packages/_pytest/tracemalloc.py b/.venv/lib/python3.12/site-packages/_pytest/tracemalloc.py new file mode 100644 index 0000000..5d0b198 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/tracemalloc.py @@ -0,0 +1,24 @@ +from __future__ import annotations + + +def tracemalloc_message(source: object) -> str: + if source is None: + return "" + + try: + import tracemalloc + except ImportError: + return "" + + tb = tracemalloc.get_object_traceback(source) + if tb is not None: + formatted_tb = "\n".join(tb.format()) + # Use a leading new line to better separate the (large) output + # from the traceback to the previous warning text. + return f"\nObject allocated at:\n{formatted_tb}" + # No need for a leading new line. + url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" + return ( + "Enable tracemalloc to get traceback where the object was allocated.\n" + f"See {url} for more info." + ) diff --git a/.venv/lib/python3.12/site-packages/_pytest/unittest.py b/.venv/lib/python3.12/site-packages/_pytest/unittest.py new file mode 100644 index 0000000..31be884 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/unittest.py @@ -0,0 +1,632 @@ +# mypy: allow-untyped-defs +"""Discover and run std-library "unittest" style tests.""" + +from __future__ import annotations + +from collections.abc import Callable +from collections.abc import Generator +from collections.abc import Iterable +from collections.abc import Iterator +from enum import auto +from enum import Enum +import inspect +import sys +import traceback +import types +from typing import Any +from typing import TYPE_CHECKING +from unittest import TestCase + +import _pytest._code +from _pytest._code import ExceptionInfo +from _pytest.compat import assert_never +from _pytest.compat import is_async_function +from _pytest.config import hookimpl +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch +from _pytest.nodes import Collector +from _pytest.nodes import Item +from _pytest.outcomes import exit +from _pytest.outcomes import fail +from _pytest.outcomes import skip +from _pytest.outcomes import xfail +from _pytest.python import Class +from _pytest.python import Function +from _pytest.python import Module +from _pytest.runner import CallInfo +from _pytest.runner import check_interactive_exception +from _pytest.subtests import SubtestContext +from _pytest.subtests import SubtestReport + + +if sys.version_info[:2] < (3, 11): + from exceptiongroup import ExceptionGroup + +if TYPE_CHECKING: + from types import TracebackType + import unittest + + import twisted.trial.unittest + + +_SysExcInfoType = ( + tuple[type[BaseException], BaseException, types.TracebackType] + | tuple[None, None, None] +) + + +def pytest_pycollect_makeitem( + collector: Module | Class, name: str, obj: object +) -> UnitTestCase | None: + try: + # Has unittest been imported? + ut = sys.modules["unittest"] + # Is obj a subclass of unittest.TestCase? + # Type ignored because `ut` is an opaque module. + if not issubclass(obj, ut.TestCase): # type: ignore + return None + except Exception: + return None + # Is obj a concrete class? + # Abstract classes can't be instantiated so no point collecting them. + if inspect.isabstract(obj): + return None + # Yes, so let's collect it. + return UnitTestCase.from_parent(collector, name=name, obj=obj) + + +class UnitTestCase(Class): + # Marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs. + nofuncargs = True + + def newinstance(self): + # TestCase __init__ takes the method (test) name. The TestCase + # constructor treats the name "runTest" as a special no-op, so it can be + # used when a dummy instance is needed. While unittest.TestCase has a + # default, some subclasses omit the default (#9610), so always supply + # it. + return self.obj("runTest") + + def collect(self) -> Iterable[Item | Collector]: + from unittest import TestLoader + + cls = self.obj + if not getattr(cls, "__test__", True): + return + + skipped = _is_skipped(cls) + if not skipped: + self._register_unittest_setup_method_fixture(cls) + self._register_unittest_setup_class_fixture(cls) + self._register_setup_class_fixture() + + self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) + + loader = TestLoader() + foundsomething = False + for name in loader.getTestCaseNames(self.obj): + x = getattr(self.obj, name) + if not getattr(x, "__test__", True): + continue + yield TestCaseFunction.from_parent(self, name=name) + foundsomething = True + + if not foundsomething: + runtest = getattr(self.obj, "runTest", None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + if ut is None or runtest != ut.TestCase.runTest: + yield TestCaseFunction.from_parent(self, name="runTest") + + def _register_unittest_setup_class_fixture(self, cls: type) -> None: + """Register an auto-use fixture to invoke setUpClass and + tearDownClass (#517).""" + setup = getattr(cls, "setUpClass", None) + teardown = getattr(cls, "tearDownClass", None) + if setup is None and teardown is None: + return None + cleanup = getattr(cls, "doClassCleanups", lambda: None) + + def process_teardown_exceptions() -> None: + # tearDown_exceptions is a list set in the class containing exc_infos for errors during + # teardown for the class. + exc_infos = getattr(cls, "tearDown_exceptions", None) + if not exc_infos: + return + exceptions = [exc for (_, exc, _) in exc_infos] + # If a single exception, raise it directly as this provides a more readable + # error (hopefully this will improve in #12255). + if len(exceptions) == 1: + raise exceptions[0] + else: + raise ExceptionGroup("Unittest class cleanup errors", exceptions) + + def unittest_setup_class_fixture( + request: FixtureRequest, + ) -> Generator[None]: + cls = request.cls + if _is_skipped(cls): + reason = cls.__unittest_skip_why__ + raise skip.Exception(reason, _use_item_location=True) + if setup is not None: + try: + setup() + # unittest does not call the cleanup function for every BaseException, so we + # follow this here. + except Exception: + cleanup() + process_teardown_exceptions() + raise + yield + try: + if teardown is not None: + teardown() + finally: + cleanup() + process_teardown_exceptions() + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_unittest_setUpClass_fixture_{cls.__qualname__}", + func=unittest_setup_class_fixture, + nodeid=self.nodeid, + scope="class", + autouse=True, + ) + + def _register_unittest_setup_method_fixture(self, cls: type) -> None: + """Register an auto-use fixture to invoke setup_method and + teardown_method (#517).""" + setup = getattr(cls, "setup_method", None) + teardown = getattr(cls, "teardown_method", None) + if setup is None and teardown is None: + return None + + def unittest_setup_method_fixture( + request: FixtureRequest, + ) -> Generator[None]: + self = request.instance + if _is_skipped(self): + reason = self.__unittest_skip_why__ + raise skip.Exception(reason, _use_item_location=True) + if setup is not None: + setup(self, request.function) + yield + if teardown is not None: + teardown(self, request.function) + + self.session._fixturemanager._register_fixture( + # Use a unique name to speed up lookup. + name=f"_unittest_setup_method_fixture_{cls.__qualname__}", + func=unittest_setup_method_fixture, + nodeid=self.nodeid, + scope="function", + autouse=True, + ) + + +class TestCaseFunction(Function): + nofuncargs = True + failfast = False + _excinfo: list[_pytest._code.ExceptionInfo[BaseException]] | None = None + + def _getinstance(self): + assert isinstance(self.parent, UnitTestCase) + return self.parent.obj(self.name) + + # Backward compat for pytest-django; can be removed after pytest-django + # updates + some slack. + @property + def _testcase(self): + return self.instance + + def setup(self) -> None: + # A bound method to be called during teardown() if set (see 'runtest()'). + self._explicit_tearDown: Callable[[], None] | None = None + super().setup() + if sys.version_info < (3, 11): + # A cache of the subTest errors and non-subtest skips in self._outcome. + # Compute and cache these lists once, instead of computing them again and again for each subtest (#13965). + self._cached_errors_and_skips: tuple[list[Any], list[Any]] | None = None + + def teardown(self) -> None: + if self._explicit_tearDown is not None: + self._explicit_tearDown() + self._explicit_tearDown = None + self._obj = None + del self._instance + super().teardown() + + def startTest(self, testcase: unittest.TestCase) -> None: + pass + + def _addexcinfo(self, rawexcinfo: _SysExcInfoType) -> None: + rawexcinfo = _handle_twisted_exc_info(rawexcinfo) + try: + excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info( + rawexcinfo # type: ignore[arg-type] + ) + # Invoke the attributes to trigger storing the traceback + # trial causes some issue there. + _ = excinfo.value + _ = excinfo.traceback + except TypeError: + try: + try: + values = traceback.format_exception(*rawexcinfo) + values.insert( + 0, + "NOTE: Incompatible Exception Representation, " + "displaying natively:\n\n", + ) + fail("".join(values), pytrace=False) + except (fail.Exception, KeyboardInterrupt): + raise + except BaseException: + fail( + "ERROR: Unknown Incompatible Exception " + f"representation:\n{rawexcinfo!r}", + pytrace=False, + ) + except KeyboardInterrupt: + raise + except fail.Exception: + excinfo = _pytest._code.ExceptionInfo.from_current() + self.__dict__.setdefault("_excinfo", []).append(excinfo) + + def addError( + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType + ) -> None: + try: + if isinstance(rawexcinfo[1], exit.Exception): + exit(rawexcinfo[1].msg) + except TypeError: + pass + self._addexcinfo(rawexcinfo) + + def addFailure( + self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType + ) -> None: + self._addexcinfo(rawexcinfo) + + def addSkip( + self, testcase: unittest.TestCase, reason: str, *, handle_subtests: bool = True + ) -> None: + from unittest.case import _SubTest # type: ignore[attr-defined] + + def add_skip() -> None: + try: + raise skip.Exception(reason, _use_item_location=True) + except skip.Exception: + self._addexcinfo(sys.exc_info()) + + if not handle_subtests: + add_skip() + return + + if isinstance(testcase, _SubTest): + add_skip() + if self._excinfo is not None: + exc_info = self._excinfo[-1] + self.addSubTest(testcase.test_case, testcase, exc_info) + else: + # For python < 3.11: the non-subtest skips have to be added by `add_skip` only after all subtest + # failures are processed by `_addSubTest`: `self.instance._outcome` has no attribute + # `skipped/errors` anymore. + # We also need to check if `self.instance._outcome` is `None` (this happens if the test + # class/method is decorated with `unittest.skip`, see pytest-dev/pytest-subtests#173). + if sys.version_info < (3, 11) and self.instance._outcome is not None: + subtest_errors, _ = self._obtain_errors_and_skips() + if len(subtest_errors) == 0: + add_skip() + else: + add_skip() + + def addExpectedFailure( + self, + testcase: unittest.TestCase, + rawexcinfo: _SysExcInfoType, + reason: str = "", + ) -> None: + try: + xfail(str(reason)) + except xfail.Exception: + self._addexcinfo(sys.exc_info()) + + def addUnexpectedSuccess( + self, + testcase: unittest.TestCase, + reason: twisted.trial.unittest.Todo | None = None, + ) -> None: + msg = "Unexpected success" + if reason: + msg += f": {reason.reason}" + # Preserve unittest behaviour - fail the test. Explicitly not an XPASS. + try: + fail(msg, pytrace=False) + except fail.Exception: + self._addexcinfo(sys.exc_info()) + + def addSuccess(self, testcase: unittest.TestCase) -> None: + pass + + def stopTest(self, testcase: unittest.TestCase) -> None: + pass + + def addDuration(self, testcase: unittest.TestCase, elapsed: float) -> None: + pass + + def runtest(self) -> None: + from _pytest.debugging import maybe_wrap_pytest_function_for_tracing + + testcase = self.instance + assert testcase is not None + + maybe_wrap_pytest_function_for_tracing(self) + + # Let the unittest framework handle async functions. + if is_async_function(self.obj): + testcase(result=self) + else: + # When --pdb is given, we want to postpone calling tearDown() otherwise + # when entering the pdb prompt, tearDown() would have probably cleaned up + # instance variables, which makes it difficult to debug. + # Arguably we could always postpone tearDown(), but this changes the moment where the + # TestCase instance interacts with the results object, so better to only do it + # when absolutely needed. + # We need to consider if the test itself is skipped, or the whole class. + assert isinstance(self.parent, UnitTestCase) + skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj) + if self.config.getoption("usepdb") and not skipped: + self._explicit_tearDown = testcase.tearDown + setattr(testcase, "tearDown", lambda *args: None) + + # We need to update the actual bound method with self.obj, because + # wrap_pytest_function_for_tracing replaces self.obj by a wrapper. + setattr(testcase, self.name, self.obj) + try: + testcase(result=self) + finally: + delattr(testcase, self.name) + + def _traceback_filter( + self, excinfo: _pytest._code.ExceptionInfo[BaseException] + ) -> _pytest._code.Traceback: + traceback = super()._traceback_filter(excinfo) + ntraceback = traceback.filter( + lambda x: not x.frame.f_globals.get("__unittest"), + ) + if not ntraceback: + ntraceback = traceback + return ntraceback + + def addSubTest( + self, + test_case: Any, + test: TestCase, + exc_info: ExceptionInfo[BaseException] + | tuple[type[BaseException], BaseException, TracebackType] + | None, + ) -> None: + # Importing this private symbol locally in case this symbol is renamed/removed in the future; importing + # it globally would break pytest entirely, importing it locally only will break unittests using `addSubTest`. + from unittest.case import _subtest_msg_sentinel # type: ignore[attr-defined] + + exception_info: ExceptionInfo[BaseException] | None + match exc_info: + case tuple(): + exception_info = ExceptionInfo(exc_info, _ispytest=True) + case ExceptionInfo() | None: + exception_info = exc_info + case unreachable: + assert_never(unreachable) + + call_info = CallInfo[None]( + None, + exception_info, + start=0, + stop=0, + duration=0, + when="call", + _ispytest=True, + ) + msg = None if test._message is _subtest_msg_sentinel else str(test._message) # type: ignore[attr-defined] + report = self.ihook.pytest_runtest_makereport(item=self, call=call_info) + sub_report = SubtestReport._new( + report, + SubtestContext(msg=msg, kwargs=dict(test.params)), # type: ignore[attr-defined] + captured_output=None, + captured_logs=None, + ) + self.ihook.pytest_runtest_logreport(report=sub_report) + if check_interactive_exception(call_info, sub_report): + self.ihook.pytest_exception_interact( + node=self, call=call_info, report=sub_report + ) + + # For python < 3.11: add non-subtest skips once all subtest failures are processed by # `_addSubTest`. + if sys.version_info < (3, 11): + subtest_errors, non_subtest_skip = self._obtain_errors_and_skips() + + # Check if we have non-subtest skips: if there are also sub failures, non-subtest skips are not treated in + # `_addSubTest` and have to be added using `add_skip` after all subtest failures are processed. + if len(non_subtest_skip) > 0 and len(subtest_errors) > 0: + # Make sure we have processed the last subtest failure + last_subset_error = subtest_errors[-1] + if exc_info is last_subset_error[-1]: + # Add non-subtest skips (as they could not be treated in `_addSkip`) + for testcase, reason in non_subtest_skip: + self.addSkip(testcase, reason, handle_subtests=False) + + def _obtain_errors_and_skips(self) -> tuple[list[Any], list[Any]]: + """Compute or obtain the cached values for subtest errors and non-subtest skips.""" + from unittest.case import _SubTest # type: ignore[attr-defined] + + assert sys.version_info < (3, 11), ( + "This workaround only should be used in Python 3.10" + ) + if self._cached_errors_and_skips is not None: + return self._cached_errors_and_skips + + subtest_errors = [ + (x, y) + for x, y in self.instance._outcome.errors + if isinstance(x, _SubTest) and y is not None + ] + + non_subtest_skips = [ + (x, y) + for x, y in self.instance._outcome.skipped + if not isinstance(x, _SubTest) + ] + self._cached_errors_and_skips = (subtest_errors, non_subtest_skips) + return subtest_errors, non_subtest_skips + + +@hookimpl(tryfirst=True) +def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: + if isinstance(item, TestCaseFunction): + if item._excinfo: + call.excinfo = item._excinfo.pop(0) + try: + del call.result + except AttributeError: + pass + + # Convert unittest.SkipTest to pytest.skip. + # This covers explicit `raise unittest.SkipTest`. + unittest = sys.modules.get("unittest") + if unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest): + excinfo = call.excinfo + call2 = CallInfo[None].from_call(lambda: skip(str(excinfo.value)), call.when) + call.excinfo = call2.excinfo + + +def _is_skipped(obj) -> bool: + """Return True if the given object has been marked with @unittest.skip.""" + return bool(getattr(obj, "__unittest_skip__", False)) + + +def pytest_configure() -> None: + """Register the TestCaseFunction class as an IReporter if twisted.trial is available.""" + if _get_twisted_version() is not TwistedVersion.NotInstalled: + from twisted.trial.itrial import IReporter + from zope.interface import classImplements + + classImplements(TestCaseFunction, IReporter) + + +class TwistedVersion(Enum): + """ + The Twisted version installed in the environment. + + We have different workarounds in place for different versions of Twisted. + """ + + # Twisted version 24 or prior. + Version24 = auto() + # Twisted version 25 or later. + Version25 = auto() + # Twisted version is not available. + NotInstalled = auto() + + +def _get_twisted_version() -> TwistedVersion: + # We need to check if "twisted.trial.unittest" is specifically present in sys.modules. + # This is because we intend to integrate with Trial only when it's actively running + # the test suite, but not needed when only other Twisted components are in use. + if "twisted.trial.unittest" not in sys.modules: + return TwistedVersion.NotInstalled + + import importlib.metadata + + import packaging.version + + version_str = importlib.metadata.version("twisted") + version = packaging.version.parse(version_str) + if version.major <= 24: + return TwistedVersion.Version24 + else: + return TwistedVersion.Version25 + + +# Name of the attribute in `twisted.python.Failure` instances that stores +# the `sys.exc_info()` tuple. +# See twisted.trial support in `pytest_runtest_protocol`. +TWISTED_RAW_EXCINFO_ATTR = "_twisted_raw_excinfo" + + +@hookimpl(wrapper=True) +def pytest_runtest_protocol(item: Item) -> Iterator[None]: + if _get_twisted_version() is TwistedVersion.Version24: + import twisted.python.failure as ut + + # Monkeypatch `Failure.__init__` to store the raw exception info. + original__init__ = ut.Failure.__init__ + + def store_raw_exception_info( + self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None + ): # pragma: no cover + if exc_value is None: + raw_exc_info = sys.exc_info() + else: + if exc_type is None: + exc_type = type(exc_value) + if exc_tb is None: + exc_tb = sys.exc_info()[2] + raw_exc_info = (exc_type, exc_value, exc_tb) + setattr(self, TWISTED_RAW_EXCINFO_ATTR, tuple(raw_exc_info)) + try: + original__init__( + self, exc_value, exc_type, exc_tb, captureVars=captureVars + ) + except TypeError: # pragma: no cover + original__init__(self, exc_value, exc_type, exc_tb) + + with MonkeyPatch.context() as patcher: + patcher.setattr(ut.Failure, "__init__", store_raw_exception_info) + return (yield) + else: + return (yield) + + +def _handle_twisted_exc_info( + rawexcinfo: _SysExcInfoType | BaseException, +) -> _SysExcInfoType: + """ + Twisted passes a custom Failure instance to `addError()` instead of using `sys.exc_info()`. + Therefore, if `rawexcinfo` is a `Failure` instance, convert it into the equivalent `sys.exc_info()` tuple + as expected by pytest. + """ + twisted_version = _get_twisted_version() + if twisted_version is TwistedVersion.NotInstalled: + # Unfortunately, because we cannot import `twisted.python.failure` at the top of the file + # and use it in the signature, we need to use `type:ignore` here because we cannot narrow + # the type properly in the `if` statement above. + return rawexcinfo # type:ignore[return-value] + elif twisted_version is TwistedVersion.Version24: + # Twisted calls addError() passing its own classes (like `twisted.python.Failure`), which violates + # the `addError()` signature, so we extract the original `sys.exc_info()` tuple which is stored + # in the object. + if hasattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR): + saved_exc_info = getattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR) + # Delete the attribute from the original object to avoid leaks. + delattr(rawexcinfo, TWISTED_RAW_EXCINFO_ATTR) + return saved_exc_info # type:ignore[no-any-return] + return rawexcinfo # type:ignore[return-value] + elif twisted_version is TwistedVersion.Version25: + if isinstance(rawexcinfo, BaseException): + import twisted.python.failure + + if isinstance(rawexcinfo, twisted.python.failure.Failure): + tb = rawexcinfo.__traceback__ + if tb is None: + tb = sys.exc_info()[2] + return type(rawexcinfo.value), rawexcinfo.value, tb + + return rawexcinfo # type:ignore[return-value] + else: + # Ideally we would use assert_never() here, but it is not available in all Python versions + # we support, plus we do not require `type_extensions` currently. + assert False, f"Unexpected Twisted version: {twisted_version}" diff --git a/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py b/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py new file mode 100644 index 0000000..0faca36 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import collections +from collections.abc import Callable +import functools +import gc +import sys +import traceback +from typing import NamedTuple +from typing import TYPE_CHECKING +import warnings + +from _pytest.config import Config +from _pytest.nodes import Item +from _pytest.stash import StashKey +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +if TYPE_CHECKING: + pass + +if sys.version_info < (3, 11): + from exceptiongroup import ExceptionGroup + + +# This is a stash item and not a simple constant to allow pytester to override it. +gc_collect_iterations_key = StashKey[int]() + + +def gc_collect_harder(iterations: int) -> None: + for _ in range(iterations): + gc.collect() + + +class UnraisableMeta(NamedTuple): + msg: str + cause_msg: str + exc_value: BaseException | None + + +unraisable_exceptions: StashKey[collections.deque[UnraisableMeta | BaseException]] = ( + StashKey() +) + + +def collect_unraisable(config: Config) -> None: + pop_unraisable = config.stash[unraisable_exceptions].pop + errors: list[pytest.PytestUnraisableExceptionWarning | RuntimeError] = [] + meta = None + hook_error = None + try: + while True: + try: + meta = pop_unraisable() + except IndexError: + break + + if isinstance(meta, BaseException): + hook_error = RuntimeError("Failed to process unraisable exception") + hook_error.__cause__ = meta + errors.append(hook_error) + continue + + msg = meta.msg + try: + warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) + except pytest.PytestUnraisableExceptionWarning as e: + # This except happens when the warning is treated as an error (e.g. `-Werror`). + if meta.exc_value is not None: + # Exceptions have a better way to show the traceback, but + # warnings do not, so hide the traceback from the msg and + # set the cause so the traceback shows up in the right place. + e.args = (meta.cause_msg,) + e.__cause__ = meta.exc_value + errors.append(e) + + if len(errors) == 1: + raise errors[0] + if errors: + raise ExceptionGroup("multiple unraisable exception warnings", errors) + finally: + del errors, meta, hook_error + + +def cleanup( + *, config: Config, prev_hook: Callable[[sys.UnraisableHookArgs], object] +) -> None: + # A single collection doesn't necessarily collect everything. + # Constant determined experimentally by the Trio project. + gc_collect_iterations = config.stash.get(gc_collect_iterations_key, 5) + try: + try: + gc_collect_harder(gc_collect_iterations) + collect_unraisable(config) + finally: + sys.unraisablehook = prev_hook + finally: + del config.stash[unraisable_exceptions] + + +def unraisable_hook( + unraisable: sys.UnraisableHookArgs, + /, + *, + append: Callable[[UnraisableMeta | BaseException], object], +) -> None: + try: + # we need to compute these strings here as they might change after + # the unraisablehook finishes and before the metadata object is + # collected by a pytest hook + err_msg = ( + "Exception ignored in" if unraisable.err_msg is None else unraisable.err_msg + ) + summary = f"{err_msg}: {unraisable.object!r}" + traceback_message = "\n\n" + "".join( + traceback.format_exception( + unraisable.exc_type, + unraisable.exc_value, + unraisable.exc_traceback, + ) + ) + tracemalloc_tb = "\n" + tracemalloc_message(unraisable.object) + msg = summary + traceback_message + tracemalloc_tb + cause_msg = summary + tracemalloc_tb + + append( + UnraisableMeta( + msg=msg, + cause_msg=cause_msg, + exc_value=unraisable.exc_value, + ) + ) + except BaseException as e: + append(e) + # Raising this will cause the exception to be logged twice, once in our + # collect_unraisable and once by the unraisablehook calling machinery + # which is fine - this should never happen anyway and if it does + # it should probably be reported as a pytest bug. + raise + + +def pytest_configure(config: Config) -> None: + prev_hook = sys.unraisablehook + deque: collections.deque[UnraisableMeta | BaseException] = collections.deque() + config.stash[unraisable_exceptions] = deque + config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook)) + sys.unraisablehook = functools.partial(unraisable_hook, append=deque.append) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_setup(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_call(item: Item) -> None: + collect_unraisable(item.config) + + +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item: Item) -> None: + collect_unraisable(item.config) diff --git a/.venv/lib/python3.12/site-packages/_pytest/warning_types.py b/.venv/lib/python3.12/site-packages/_pytest/warning_types.py new file mode 100644 index 0000000..93071b4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/warning_types.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import dataclasses +import inspect +from types import FunctionType +from typing import Any +from typing import final +from typing import Generic +from typing import TypeVar +import warnings + + +class PytestWarning(UserWarning): + """Base class for all warnings emitted by pytest.""" + + __module__ = "pytest" + + +@final +class PytestAssertRewriteWarning(PytestWarning): + """Warning emitted by the pytest assert rewrite module.""" + + __module__ = "pytest" + + +@final +class PytestCacheWarning(PytestWarning): + """Warning emitted by the cache plugin in various situations.""" + + __module__ = "pytest" + + +@final +class PytestConfigWarning(PytestWarning): + """Warning emitted for configuration issues.""" + + __module__ = "pytest" + + +@final +class PytestCollectionWarning(PytestWarning): + """Warning emitted when pytest is not able to collect a file or symbol in a module.""" + + __module__ = "pytest" + + +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """Warning class for features that will be removed in a future version.""" + + __module__ = "pytest" + + +class PytestRemovedIn9Warning(PytestDeprecationWarning): + """Warning class for features that will be removed in pytest 9.""" + + __module__ = "pytest" + + +class PytestRemovedIn10Warning(PytestDeprecationWarning): + """Warning class for features that will be removed in pytest 10.""" + + __module__ = "pytest" + + +@final +class PytestExperimentalApiWarning(PytestWarning, FutureWarning): + """Warning category used to denote experiments in pytest. + + Use sparingly as the API might change or even be removed completely in a + future version. + """ + + __module__ = "pytest" + + @classmethod + def simple(cls, apiname: str) -> PytestExperimentalApiWarning: + return cls(f"{apiname} is an experimental api that may change over time") + + +@final +class PytestReturnNotNoneWarning(PytestWarning): + """ + Warning emitted when a test function returns a value other than ``None``. + + See :ref:`return-not-none` for details. + """ + + __module__ = "pytest" + + +@final +class PytestUnknownMarkWarning(PytestWarning): + """Warning emitted on use of unknown markers. + + See :ref:`mark` for details. + """ + + __module__ = "pytest" + + +@final +class PytestUnraisableExceptionWarning(PytestWarning): + """An unraisable exception was reported. + + Unraisable exceptions are exceptions raised in :meth:`__del__ ` + implementations and similar situations when the exception cannot be raised + as normal. + """ + + __module__ = "pytest" + + +@final +class PytestUnhandledThreadExceptionWarning(PytestWarning): + """An unhandled exception occurred in a :class:`~threading.Thread`. + + Such exceptions don't propagate normally. + """ + + __module__ = "pytest" + + +_W = TypeVar("_W", bound=PytestWarning) + + +@final +@dataclasses.dataclass +class UnformattedWarning(Generic[_W]): + """A warning meant to be formatted during runtime. + + This is used to hold warnings that need to format their message at runtime, + as opposed to a direct message. + """ + + category: type[_W] + template: str + + def format(self, **kwargs: Any) -> _W: + """Return an instance of the warning category, formatted with given kwargs.""" + return self.category(self.template.format(**kwargs)) + + +@final +class PytestFDWarning(PytestWarning): + """When the lsof plugin finds leaked fds.""" + + __module__ = "pytest" + + +def warn_explicit_for(method: FunctionType, message: PytestWarning) -> None: + """ + Issue the warning :param:`message` for the definition of the given :param:`method` + + this helps to log warnings for functions defined prior to finding an issue with them + (like hook wrappers being marked in a legacy mechanism) + """ + lineno = method.__code__.co_firstlineno + filename = inspect.getfile(method) + module = method.__module__ + mod_globals = method.__globals__ + try: + warnings.warn_explicit( + message, + type(message), + filename=filename, + module=module, + registry=mod_globals.setdefault("__warningregistry__", {}), + lineno=lineno, + ) + except Warning as w: + # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message. + raise type(w)(f"{w}\n at {filename}:{lineno}") from None diff --git a/.venv/lib/python3.12/site-packages/_pytest/warnings.py b/.venv/lib/python3.12/site-packages/_pytest/warnings.py new file mode 100644 index 0000000..1dbf002 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_pytest/warnings.py @@ -0,0 +1,151 @@ +# mypy: allow-untyped-defs +from __future__ import annotations + +from collections.abc import Generator +from contextlib import contextmanager +from contextlib import ExitStack +import sys +from typing import Literal +import warnings + +from _pytest.config import apply_warning_filters +from _pytest.config import Config +from _pytest.config import parse_warning_filter +from _pytest.main import Session +from _pytest.nodes import Item +from _pytest.terminal import TerminalReporter +from _pytest.tracemalloc import tracemalloc_message +import pytest + + +@contextmanager +def catch_warnings_for_item( + config: Config, + ihook, + when: Literal["config", "collect", "runtest"], + item: Item | None, + *, + record: bool = True, +) -> Generator[None]: + """Context manager that catches warnings generated in the contained execution block. + + ``item`` can be None if we are not in the context of an item execution. + + Each warning captured triggers the ``pytest_warning_recorded`` hook. + """ + config_filters = config.getini("filterwarnings") + cmdline_filters = config.known_args_namespace.pythonwarnings or [] + with warnings.catch_warnings(record=record) as log: + if not sys.warnoptions: + # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). + warnings.filterwarnings("always", category=DeprecationWarning) + warnings.filterwarnings("always", category=PendingDeprecationWarning) + + warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning) + + apply_warning_filters(config_filters, cmdline_filters) + + # apply filters from "filterwarnings" marks + nodeid = "" if item is None else item.nodeid + if item is not None: + for mark in item.iter_markers(name="filterwarnings"): + for arg in mark.args: + warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) + + try: + yield + finally: + if record: + # mypy can't infer that record=True means log is not None; help it. + assert log is not None + + for warning_message in log: + ihook.pytest_warning_recorded.call_historic( + kwargs=dict( + warning_message=warning_message, + nodeid=nodeid, + when=when, + location=None, + ) + ) + + +def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: + """Convert a warnings.WarningMessage to a string.""" + return warnings.formatwarning( + str(warning_message.message), + warning_message.category, + warning_message.filename, + warning_message.lineno, + warning_message.line, + ) + tracemalloc_message(warning_message.source) + + +@pytest.hookimpl(wrapper=True, tryfirst=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: + with catch_warnings_for_item( + config=item.config, ihook=item.ihook, when="runtest", item=item + ): + return (yield) + + +@pytest.hookimpl(wrapper=True, tryfirst=True) +def pytest_collection(session: Session) -> Generator[None, object, object]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="collect", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_terminal_summary( + terminalreporter: TerminalReporter, +) -> Generator[None]: + config = terminalreporter.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_sessionfinish(session: Session) -> Generator[None]: + config = session.config + with catch_warnings_for_item( + config=config, ihook=config.hook, when="config", item=None + ): + return (yield) + + +@pytest.hookimpl(wrapper=True) +def pytest_load_initial_conftests( + early_config: Config, +) -> Generator[None]: + with catch_warnings_for_item( + config=early_config, ihook=early_config.hook, when="config", item=None + ): + return (yield) + + +def pytest_configure(config: Config) -> None: + with ExitStack() as stack: + stack.enter_context( + catch_warnings_for_item( + config=config, + ihook=config.hook, + when="config", + item=None, + # this disables recording because the terminalreporter has + # finished by the time it comes to reporting logged warnings + # from the end of config cleanup. So for now, this is only + # useful for setting a warning filter with an 'error' action. + record=False, + ) + ) + config.addinivalue_line( + "markers", + "filterwarnings(warning): add a warning filter to the given test. " + "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", + ) + config.add_cleanup(stack.pop_all().close) diff --git a/.venv/lib/python3.12/site-packages/_yaml/__init__.py b/.venv/lib/python3.12/site-packages/_yaml/__init__.py new file mode 100644 index 0000000..7baa8c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/METADATA new file mode 100644 index 0000000..9fc5423 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/METADATA @@ -0,0 +1,209 @@ +Metadata-Version: 2.4 +Name: aiofiles +Version: 25.1.0 +Summary: File support for asyncio. +Project-URL: Changelog, https://github.com/Tinche/aiofiles#history +Project-URL: Bug Tracker, https://github.com/Tinche/aiofiles/issues +Project-URL: Repository, https://github.com/Tinche/aiofiles +Author-email: Tin Tvrtkovic +License: Apache-2.0 +License-File: LICENSE +License-File: NOTICE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: AsyncIO +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +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 :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=3.9 +Description-Content-Type: text/markdown + +# aiofiles: file support for asyncio + +[![PyPI](https://img.shields.io/pypi/v/aiofiles.svg)](https://pypi.python.org/pypi/aiofiles) +[![Build](https://github.com/Tinche/aiofiles/workflows/CI/badge.svg)](https://github.com/Tinche/aiofiles/actions) +[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/Tinche/882f02e3df32136c847ba90d2688f06e/raw/covbadge.json)](https://github.com/Tinche/aiofiles/actions/workflows/main.yml) +[![Supported Python versions](https://img.shields.io/pypi/pyversions/aiofiles.svg)](https://github.com/Tinche/aiofiles) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) + +**aiofiles** is an Apache2 licensed library, written in Python, for handling local +disk files in asyncio applications. + +Ordinary local file IO is blocking, and cannot easily and portably be made +asynchronous. This means doing file IO may interfere with asyncio applications, +which shouldn't block the executing thread. aiofiles helps with this by +introducing asynchronous versions of files that support delegating operations to +a separate thread pool. + +```python +async with aiofiles.open('filename', mode='r') as f: + contents = await f.read() +print(contents) +'My file contents' +``` + +Asynchronous iteration is also supported. + +```python +async with aiofiles.open('filename') as f: + async for line in f: + ... +``` + +Asynchronous interface to tempfile module. + +```python +async with aiofiles.tempfile.TemporaryFile('wb') as f: + await f.write(b'Hello, World!') +``` + +## Features + +- a file API very similar to Python's standard, blocking API +- support for buffered and unbuffered binary files, and buffered text files +- support for `async`/`await` ([PEP 492](https://peps.python.org/pep-0492/)) constructs +- async interface to tempfile module + +## Installation + +To install aiofiles, simply: + +```shell +pip install aiofiles +``` + +## Usage + +Files are opened using the `aiofiles.open()` coroutine, which in addition to +mirroring the builtin `open` accepts optional `loop` and `executor` +arguments. If `loop` is absent, the default loop will be used, as per the +set asyncio policy. If `executor` is not specified, the default event loop +executor will be used. + +In case of success, an asynchronous file object is returned with an +API identical to an ordinary file, except the following methods are coroutines +and delegate to an executor: + +- `close` +- `flush` +- `isatty` +- `read` +- `readall` +- `read1` +- `readinto` +- `readline` +- `readlines` +- `seek` +- `seekable` +- `tell` +- `truncate` +- `writable` +- `write` +- `writelines` + +In case of failure, one of the usual exceptions will be raised. + +`aiofiles.stdin`, `aiofiles.stdout`, `aiofiles.stderr`, +`aiofiles.stdin_bytes`, `aiofiles.stdout_bytes`, and +`aiofiles.stderr_bytes` provide async access to `sys.stdin`, +`sys.stdout`, `sys.stderr`, and their corresponding `.buffer` properties. + +The `aiofiles.os` module contains executor-enabled coroutine versions of +several useful `os` functions that deal with files: + +- `stat` +- `statvfs` +- `sendfile` +- `rename` +- `renames` +- `replace` +- `remove` +- `unlink` +- `mkdir` +- `makedirs` +- `rmdir` +- `removedirs` +- `link` +- `symlink` +- `readlink` +- `listdir` +- `scandir` +- `access` +- `getcwd` +- `path.abspath` +- `path.exists` +- `path.isfile` +- `path.isdir` +- `path.islink` +- `path.ismount` +- `path.getsize` +- `path.getatime` +- `path.getctime` +- `path.samefile` +- `path.sameopenfile` + +### Tempfile + +**aiofiles.tempfile** implements the following interfaces: + +- TemporaryFile +- NamedTemporaryFile +- SpooledTemporaryFile +- TemporaryDirectory + +Results return wrapped with a context manager allowing use with async with and async for. + +```python +async with aiofiles.tempfile.NamedTemporaryFile('wb+') as f: + await f.write(b'Line1\n Line2') + await f.seek(0) + async for line in f: + print(line) + +async with aiofiles.tempfile.TemporaryDirectory() as d: + filename = os.path.join(d, "file.ext") +``` + +### Writing tests for aiofiles + +Real file IO can be mocked by patching `aiofiles.threadpool.sync_open` +as desired. The return type also needs to be registered with the +`aiofiles.threadpool.wrap` dispatcher: + +```python +aiofiles.threadpool.wrap.register(mock.MagicMock)( + lambda *args, **kwargs: aiofiles.threadpool.AsyncBufferedIOBase(*args, **kwargs) +) + +async def test_stuff(): + write_data = 'data' + read_file_chunks = [ + b'file chunks 1', + b'file chunks 2', + b'file chunks 3', + b'', + ] + file_chunks_iter = iter(read_file_chunks) + + mock_file_stream = mock.MagicMock( + read=lambda *args, **kwargs: next(file_chunks_iter) + ) + + with mock.patch('aiofiles.threadpool.sync_open', return_value=mock_file_stream) as mock_open: + async with aiofiles.open('filename', 'w') as f: + await f.write(write_data) + assert await f.read() == b'file chunks 1' + + mock_file_stream.write.assert_called_once_with(write_data) +``` + +### Contributing + +Contributions are very welcome. Tests can be run with `tox`, please ensure +the coverage at least stays the same before you submit a pull request. diff --git a/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/RECORD new file mode 100644 index 0000000..93459bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/RECORD @@ -0,0 +1,26 @@ +aiofiles-25.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +aiofiles-25.1.0.dist-info/METADATA,sha256=a5a5kHMVigDdsBKlFINLSMPsX3Ms4Fn_zecASBdZqLU,6291 +aiofiles-25.1.0.dist-info/RECORD,, +aiofiles-25.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +aiofiles-25.1.0.dist-info/licenses/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325 +aiofiles-25.1.0.dist-info/licenses/NOTICE,sha256=EExY0dRQvWR0wJ2LZLwBgnM6YKw9jCU-M0zegpRSD_E,55 +aiofiles/__init__.py,sha256=DYqUwak6MVosBjbAsgyEnFFP-HUZCG5h7X4owoeyYHw,345 +aiofiles/__pycache__/__init__.cpython-312.pyc,, +aiofiles/__pycache__/base.cpython-312.pyc,, +aiofiles/__pycache__/os.cpython-312.pyc,, +aiofiles/__pycache__/ospath.cpython-312.pyc,, +aiofiles/base.py,sha256=-fvh41PnictTZL3cg98HoN4h6jdebi5d7Mfh81zOBOc,2046 +aiofiles/os.py,sha256=slJ5oUNHVW1xWVuuIWQiYjw30n3L48H7oX4CJvD_1d4,1078 +aiofiles/ospath.py,sha256=c-Kqw4wMCZ-YRt8Jleb697cANwJQM9qux6lq97949C8,678 +aiofiles/tempfile/__init__.py,sha256=twoC7vaQ-JjFzh2Bbd-3-o0hmExH3CYJUmQcuiVwZfg,10207 +aiofiles/tempfile/__pycache__/__init__.cpython-312.pyc,, +aiofiles/tempfile/__pycache__/temptypes.cpython-312.pyc,, +aiofiles/tempfile/temptypes.py,sha256=3_hlc6l9r5wmino1fDrt4TpFlX4IKoR5IP_bBYVVuHg,2037 +aiofiles/threadpool/__init__.py,sha256=-65UURmzUHsGTXUz0TARdSzyXIfkCFtbczAQLEPpEcU,3140 +aiofiles/threadpool/__pycache__/__init__.cpython-312.pyc,, +aiofiles/threadpool/__pycache__/binary.cpython-312.pyc,, +aiofiles/threadpool/__pycache__/text.cpython-312.pyc,, +aiofiles/threadpool/__pycache__/utils.cpython-312.pyc,, +aiofiles/threadpool/binary.py,sha256=hp-km9VCRu0MLz_wAEUfbCz7OL7xtn9iGAawabpnp5U,2315 +aiofiles/threadpool/text.py,sha256=fNmpw2PEkj0BZSldipJXAgZqVGLxALcfOMiuDQ54Eas,1223 +aiofiles/threadpool/utils.py,sha256=VtIJ9KErbcIT9_Yz4V4rZgNEUjBH3cAYxzKQBMpEzik,1850 diff --git a/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..e06d208 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/LICENSE @@ -0,0 +1,202 @@ +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/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/NOTICE b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/NOTICE new file mode 100644 index 0000000..d134f28 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles-25.1.0.dist-info/licenses/NOTICE @@ -0,0 +1,2 @@ +Asyncio support for files +Copyright 2016 Tin Tvrtkovic diff --git a/.venv/lib/python3.12/site-packages/aiofiles/__init__.py b/.venv/lib/python3.12/site-packages/aiofiles/__init__.py new file mode 100644 index 0000000..5f62158 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/__init__.py @@ -0,0 +1,23 @@ +"""Utilities for asyncio-friendly file handling.""" + +from . import tempfile +from .threadpool import ( + open, + stderr, + stderr_bytes, + stdin, + stdin_bytes, + stdout, + stdout_bytes, +) + +__all__ = [ + "open", + "tempfile", + "stdin", + "stdout", + "stderr", + "stdin_bytes", + "stdout_bytes", + "stderr_bytes", +] diff --git a/.venv/lib/python3.12/site-packages/aiofiles/base.py b/.venv/lib/python3.12/site-packages/aiofiles/base.py new file mode 100644 index 0000000..ef1f81d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/base.py @@ -0,0 +1,79 @@ +from asyncio import get_running_loop +from collections.abc import Awaitable +from contextlib import AbstractAsyncContextManager +from functools import partial, wraps + + +def wrap(func): + @wraps(func) + async def run(*args, loop=None, executor=None, **kwargs): + if loop is None: + loop = get_running_loop() + pfunc = partial(func, *args, **kwargs) + return await loop.run_in_executor(executor, pfunc) + + return run + + +class AsyncBase: + def __init__(self, file, loop, executor): + self._file = file + self._executor = executor + self._ref_loop = loop + + @property + def _loop(self): + return self._ref_loop or get_running_loop() + + def __aiter__(self): + """We are our own iterator.""" + return self + + def __repr__(self): + return super().__repr__() + " wrapping " + repr(self._file) + + async def __anext__(self): + """Simulate normal file iteration.""" + + if line := await self.readline(): + return line + raise StopAsyncIteration + + +class AsyncIndirectBase(AsyncBase): + def __init__(self, name, loop, executor, indirect): + self._indirect = indirect + self._name = name + super().__init__(None, loop, executor) + + @property + def _file(self): + return self._indirect() + + @_file.setter + def _file(self, v): + pass # discard writes + + +class AiofilesContextManager(Awaitable, AbstractAsyncContextManager): + """An adjusted async context manager for aiofiles.""" + + __slots__ = ("_coro", "_obj") + + def __init__(self, coro): + self._coro = coro + self._obj = None + + def __await__(self): + if self._obj is None: + self._obj = yield from self._coro.__await__() + return self._obj + + async def __aenter__(self): + return await self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await get_running_loop().run_in_executor( + None, self._obj._file.__exit__, exc_type, exc_val, exc_tb + ) + self._obj = None diff --git a/.venv/lib/python3.12/site-packages/aiofiles/os.py b/.venv/lib/python3.12/site-packages/aiofiles/os.py new file mode 100644 index 0000000..153d65d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/os.py @@ -0,0 +1,61 @@ +"""Async executor versions of file functions from the os module.""" + +import os + +from . import ospath as path +from .base import wrap + +__all__ = [ + "path", + "stat", + "rename", + "renames", + "replace", + "remove", + "unlink", + "mkdir", + "makedirs", + "rmdir", + "removedirs", + "symlink", + "readlink", + "listdir", + "scandir", + "access", + "wrap", + "getcwd", +] + +access = wrap(os.access) + +getcwd = wrap(os.getcwd) + +listdir = wrap(os.listdir) + +makedirs = wrap(os.makedirs) +mkdir = wrap(os.mkdir) + +readlink = wrap(os.readlink) +remove = wrap(os.remove) +removedirs = wrap(os.removedirs) +rename = wrap(os.rename) +renames = wrap(os.renames) +replace = wrap(os.replace) +rmdir = wrap(os.rmdir) + +scandir = wrap(os.scandir) +stat = wrap(os.stat) +symlink = wrap(os.symlink) + +unlink = wrap(os.unlink) + + +if hasattr(os, "link"): + __all__ += ["link"] + link = wrap(os.link) +if hasattr(os, "sendfile"): + __all__ += ["sendfile"] + sendfile = wrap(os.sendfile) +if hasattr(os, "statvfs"): + __all__ += ["statvfs"] + statvfs = wrap(os.statvfs) diff --git a/.venv/lib/python3.12/site-packages/aiofiles/ospath.py b/.venv/lib/python3.12/site-packages/aiofiles/ospath.py new file mode 100644 index 0000000..f47f150 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/ospath.py @@ -0,0 +1,37 @@ +"""Async executor versions of file functions from the os.path module.""" + +from os import path + +from .base import wrap + +__all__ = [ + "abspath", + "getatime", + "getctime", + "getmtime", + "getsize", + "exists", + "isdir", + "isfile", + "islink", + "ismount", + "samefile", + "sameopenfile", +] + +abspath = wrap(path.abspath) + +getatime = wrap(path.getatime) +getctime = wrap(path.getctime) +getmtime = wrap(path.getmtime) +getsize = wrap(path.getsize) + +exists = wrap(path.exists) + +isdir = wrap(path.isdir) +isfile = wrap(path.isfile) +islink = wrap(path.islink) +ismount = wrap(path.ismount) + +samefile = wrap(path.samefile) +sameopenfile = wrap(path.sameopenfile) diff --git a/.venv/lib/python3.12/site-packages/aiofiles/tempfile/__init__.py b/.venv/lib/python3.12/site-packages/aiofiles/tempfile/__init__.py new file mode 100644 index 0000000..b1c32c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/tempfile/__init__.py @@ -0,0 +1,357 @@ +import asyncio +import sys +from functools import partial, singledispatch +from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOBase +from tempfile import NamedTemporaryFile as syncNamedTemporaryFile +from tempfile import SpooledTemporaryFile as syncSpooledTemporaryFile +from tempfile import TemporaryDirectory as syncTemporaryDirectory +from tempfile import TemporaryFile as syncTemporaryFile +from tempfile import _TemporaryFileWrapper as syncTemporaryFileWrapper + +from ..base import AiofilesContextManager +from ..threadpool.binary import AsyncBufferedIOBase, AsyncBufferedReader, AsyncFileIO +from ..threadpool.text import AsyncTextIOWrapper +from .temptypes import AsyncSpooledTemporaryFile, AsyncTemporaryDirectory + +__all__ = [ + "NamedTemporaryFile", + "TemporaryFile", + "SpooledTemporaryFile", + "TemporaryDirectory", +] + + +# ================================================================ +# Public methods for async open and return of temp file/directory +# objects with async interface +# ================================================================ +if sys.version_info >= (3, 12): + + def NamedTemporaryFile( + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + delete=True, + delete_on_close=True, + loop=None, + executor=None, + ): + """Async open a named temporary file""" + return AiofilesContextManager( + _temporary_file( + named=True, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + delete=delete, + delete_on_close=delete_on_close, + loop=loop, + executor=executor, + ) + ) + +else: + + def NamedTemporaryFile( + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + delete=True, + loop=None, + executor=None, + ): + """Async open a named temporary file""" + return AiofilesContextManager( + _temporary_file( + named=True, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + delete=delete, + loop=loop, + executor=executor, + ) + ) + + +def TemporaryFile( + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + loop=None, + executor=None, +): + """Async open an unnamed temporary file""" + return AiofilesContextManager( + _temporary_file( + named=False, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + loop=loop, + executor=executor, + ) + ) + + +def SpooledTemporaryFile( + max_size=0, + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + loop=None, + executor=None, +): + """Async open a spooled temporary file""" + return AiofilesContextManager( + _spooled_temporary_file( + max_size=max_size, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + loop=loop, + executor=executor, + ) + ) + + +def TemporaryDirectory(suffix=None, prefix=None, dir=None, loop=None, executor=None): + """Async open a temporary directory""" + return AiofilesContextManagerTempDir( + _temporary_directory( + suffix=suffix, prefix=prefix, dir=dir, loop=loop, executor=executor + ) + ) + + +# ========================================================= +# Internal coroutines to open new temp files/directories +# ========================================================= +if sys.version_info >= (3, 12): + + async def _temporary_file( + named=True, + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + delete=True, + delete_on_close=True, + loop=None, + executor=None, + max_size=0, + ): + """Async method to open a temporary file with async interface""" + if loop is None: + loop = asyncio.get_running_loop() + + if named: + cb = partial( + syncNamedTemporaryFile, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + delete=delete, + delete_on_close=delete_on_close, + ) + else: + cb = partial( + syncTemporaryFile, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + ) + + f = await loop.run_in_executor(executor, cb) + + # Wrap based on type of underlying IO object + if type(f) is syncTemporaryFileWrapper: + # _TemporaryFileWrapper was used (named files) + result = wrap(f.file, f, loop=loop, executor=executor) + result._closer = f._closer + return result + # IO object was returned directly without wrapper + return wrap(f, f, loop=loop, executor=executor) + +else: + + async def _temporary_file( + named=True, + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + delete=True, + loop=None, + executor=None, + max_size=0, + ): + """Async method to open a temporary file with async interface""" + if loop is None: + loop = asyncio.get_running_loop() + + if named: + cb = partial( + syncNamedTemporaryFile, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + delete=delete, + ) + else: + cb = partial( + syncTemporaryFile, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + ) + + f = await loop.run_in_executor(executor, cb) + + # Wrap based on type of underlying IO object + if type(f) is syncTemporaryFileWrapper: + # _TemporaryFileWrapper was used (named files) + result = wrap(f.file, f, loop=loop, executor=executor) + # add delete property + result.delete = f.delete + return result + # IO object was returned directly without wrapper + return wrap(f, f, loop=loop, executor=executor) + + +async def _spooled_temporary_file( + max_size=0, + mode="w+b", + buffering=-1, + encoding=None, + newline=None, + suffix=None, + prefix=None, + dir=None, + loop=None, + executor=None, +): + """Open a spooled temporary file with async interface""" + if loop is None: + loop = asyncio.get_running_loop() + + cb = partial( + syncSpooledTemporaryFile, + max_size=max_size, + mode=mode, + buffering=buffering, + encoding=encoding, + newline=newline, + suffix=suffix, + prefix=prefix, + dir=dir, + ) + + f = await loop.run_in_executor(executor, cb) + + # Single interface provided by SpooledTemporaryFile for all modes + return AsyncSpooledTemporaryFile(f, loop=loop, executor=executor) + + +async def _temporary_directory( + suffix=None, prefix=None, dir=None, loop=None, executor=None +): + """Async method to open a temporary directory with async interface""" + if loop is None: + loop = asyncio.get_running_loop() + + cb = partial(syncTemporaryDirectory, suffix, prefix, dir) + f = await loop.run_in_executor(executor, cb) + + return AsyncTemporaryDirectory(f, loop=loop, executor=executor) + + +class AiofilesContextManagerTempDir(AiofilesContextManager): + """With returns the directory location, not the object (matching sync lib)""" + + async def __aenter__(self): + self._obj = await self._coro + return self._obj.name + + +@singledispatch +def wrap(base_io_obj, file, *, loop=None, executor=None): + """Wrap the object with interface based on type of underlying IO""" + + msg = f"Unsupported IO type: {base_io_obj}" + raise TypeError(msg) + + +@wrap.register(TextIOBase) +def _(base_io_obj, file, *, loop=None, executor=None): + return AsyncTextIOWrapper(file, loop=loop, executor=executor) + + +@wrap.register(BufferedWriter) +def _(base_io_obj, file, *, loop=None, executor=None): + return AsyncBufferedIOBase(file, loop=loop, executor=executor) + + +@wrap.register(BufferedReader) +@wrap.register(BufferedRandom) +def _(base_io_obj, file, *, loop=None, executor=None): + return AsyncBufferedReader(file, loop=loop, executor=executor) + + +@wrap.register(FileIO) +def _(base_io_obj, file, *, loop=None, executor=None): + return AsyncFileIO(file, loop=loop, executor=executor) diff --git a/.venv/lib/python3.12/site-packages/aiofiles/tempfile/temptypes.py b/.venv/lib/python3.12/site-packages/aiofiles/tempfile/temptypes.py new file mode 100644 index 0000000..8ae5032 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/tempfile/temptypes.py @@ -0,0 +1,70 @@ +"""Async wrappers for spooled temp files and temp directory objects""" + +from functools import partial + +from ..base import AsyncBase +from ..threadpool.utils import ( + cond_delegate_to_executor, + delegate_to_executor, + proxy_property_directly, +) + + +@delegate_to_executor("fileno", "rollover") +@cond_delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "readline", + "readlines", + "seek", + "tell", + "truncate", +) +@proxy_property_directly("closed", "encoding", "mode", "name", "newlines") +class AsyncSpooledTemporaryFile(AsyncBase): + """Async wrapper for SpooledTemporaryFile class""" + + async def _check(self): + if self._file._rolled: + return + max_size = self._file._max_size + if max_size and self._file.tell() > max_size: + await self.rollover() + + async def write(self, s): + """Implementation to anticipate rollover""" + if self._file._rolled: + cb = partial(self._file.write, s) + return await self._loop.run_in_executor(self._executor, cb) + + file = self._file._file # reference underlying base IO object + rv = file.write(s) + await self._check() + return rv + + async def writelines(self, iterable): + """Implementation to anticipate rollover""" + if self._file._rolled: + cb = partial(self._file.writelines, iterable) + return await self._loop.run_in_executor(self._executor, cb) + + file = self._file._file # reference underlying base IO object + rv = file.writelines(iterable) + await self._check() + return rv + + +@delegate_to_executor("cleanup") +@proxy_property_directly("name") +class AsyncTemporaryDirectory: + """Async wrapper for TemporaryDirectory class""" + + def __init__(self, file, loop, executor): + self._file = file + self._loop = loop + self._executor = executor + + async def close(self): + await self.cleanup() diff --git a/.venv/lib/python3.12/site-packages/aiofiles/threadpool/__init__.py b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/__init__.py new file mode 100644 index 0000000..8054034 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/__init__.py @@ -0,0 +1,141 @@ +"""Handle files using a thread pool executor.""" + +import asyncio +import sys +from functools import partial, singledispatch +from io import ( + BufferedIOBase, + BufferedRandom, + BufferedReader, + BufferedWriter, + FileIO, + TextIOBase, +) + +from ..base import AiofilesContextManager +from .binary import ( + AsyncBufferedIOBase, + AsyncBufferedReader, + AsyncFileIO, + AsyncIndirectBufferedIOBase, +) +from .text import AsyncTextIndirectIOWrapper, AsyncTextIOWrapper + +sync_open = open + +__all__ = ( + "open", + "stdin", + "stdout", + "stderr", + "stdin_bytes", + "stdout_bytes", + "stderr_bytes", +) + + +def open( + file, + mode="r", + buffering=-1, + encoding=None, + errors=None, + newline=None, + closefd=True, + opener=None, + *, + loop=None, + executor=None, +): + return AiofilesContextManager( + _open( + file, + mode=mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, + closefd=closefd, + opener=opener, + loop=loop, + executor=executor, + ) + ) + + +async def _open( + file, + mode="r", + buffering=-1, + encoding=None, + errors=None, + newline=None, + closefd=True, + opener=None, + *, + loop=None, + executor=None, +): + """Open an asyncio file.""" + if loop is None: + loop = asyncio.get_running_loop() + cb = partial( + sync_open, + file, + mode=mode, + buffering=buffering, + encoding=encoding, + errors=errors, + newline=newline, + closefd=closefd, + opener=opener, + ) + f = await loop.run_in_executor(executor, cb) + + return wrap(f, loop=loop, executor=executor) + + +@singledispatch +def wrap(file, *, loop=None, executor=None): + msg = f"Unsupported io type: {file}." + raise TypeError(msg) + + +@wrap.register(TextIOBase) +def _(file, *, loop=None, executor=None): + return AsyncTextIOWrapper(file, loop=loop, executor=executor) + + +@wrap.register(BufferedWriter) +@wrap.register(BufferedIOBase) +def _(file, *, loop=None, executor=None): + return AsyncBufferedIOBase(file, loop=loop, executor=executor) + + +@wrap.register(BufferedReader) +@wrap.register(BufferedRandom) +def _(file, *, loop=None, executor=None): + return AsyncBufferedReader(file, loop=loop, executor=executor) + + +@wrap.register(FileIO) +def _(file, *, loop=None, executor=None): + return AsyncFileIO(file, loop=loop, executor=executor) + + +stdin = AsyncTextIndirectIOWrapper("sys.stdin", None, None, indirect=lambda: sys.stdin) +stdout = AsyncTextIndirectIOWrapper( + "sys.stdout", None, None, indirect=lambda: sys.stdout +) +stderr = AsyncTextIndirectIOWrapper( + "sys.stderr", None, None, indirect=lambda: sys.stderr +) +stdin_bytes = AsyncIndirectBufferedIOBase( + "sys.stdin.buffer", None, None, indirect=lambda: sys.stdin.buffer +) +stdout_bytes = AsyncIndirectBufferedIOBase( + "sys.stdout.buffer", None, None, indirect=lambda: sys.stdout.buffer +) +stderr_bytes = AsyncIndirectBufferedIOBase( + "sys.stderr.buffer", None, None, indirect=lambda: sys.stderr.buffer +) diff --git a/.venv/lib/python3.12/site-packages/aiofiles/threadpool/binary.py b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/binary.py new file mode 100644 index 0000000..63fcaff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/binary.py @@ -0,0 +1,104 @@ +from ..base import AsyncBase, AsyncIndirectBase +from .utils import delegate_to_executor, proxy_method_directly, proxy_property_directly + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "read1", + "readinto", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "writable", + "write", + "writelines", +) +@proxy_method_directly("detach", "fileno", "readable") +@proxy_property_directly("closed", "raw", "name", "mode") +class AsyncBufferedIOBase(AsyncBase): + """The asyncio executor version of io.BufferedWriter and BufferedIOBase.""" + + +@delegate_to_executor("peek") +class AsyncBufferedReader(AsyncBufferedIOBase): + """The asyncio executor version of io.BufferedReader and Random.""" + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "readall", + "readinto", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "writable", + "write", + "writelines", +) +@proxy_method_directly("fileno", "readable") +@proxy_property_directly("closed", "name", "mode") +class AsyncFileIO(AsyncBase): + """The asyncio executor version of io.FileIO.""" + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "read1", + "readinto", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "writable", + "write", + "writelines", +) +@proxy_method_directly("detach", "fileno", "readable") +@proxy_property_directly("closed", "raw", "name", "mode") +class AsyncIndirectBufferedIOBase(AsyncIndirectBase): + """The indirect asyncio executor version of io.BufferedWriter and BufferedIOBase.""" + + +@delegate_to_executor("peek") +class AsyncIndirectBufferedReader(AsyncIndirectBufferedIOBase): + """The indirect asyncio executor version of io.BufferedReader and Random.""" + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "readall", + "readinto", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "writable", + "write", + "writelines", +) +@proxy_method_directly("fileno", "readable") +@proxy_property_directly("closed", "name", "mode") +class AsyncIndirectFileIO(AsyncIndirectBase): + """The indirect asyncio executor version of io.FileIO.""" diff --git a/.venv/lib/python3.12/site-packages/aiofiles/threadpool/text.py b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/text.py new file mode 100644 index 0000000..0e62590 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/text.py @@ -0,0 +1,64 @@ +from ..base import AsyncBase, AsyncIndirectBase +from .utils import delegate_to_executor, proxy_method_directly, proxy_property_directly + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "readable", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "write", + "writable", + "writelines", +) +@proxy_method_directly("detach", "fileno", "readable") +@proxy_property_directly( + "buffer", + "closed", + "encoding", + "errors", + "line_buffering", + "newlines", + "name", + "mode", +) +class AsyncTextIOWrapper(AsyncBase): + """The asyncio executor version of io.TextIOWrapper.""" + + +@delegate_to_executor( + "close", + "flush", + "isatty", + "read", + "readable", + "readline", + "readlines", + "seek", + "seekable", + "tell", + "truncate", + "write", + "writable", + "writelines", +) +@proxy_method_directly("detach", "fileno", "readable") +@proxy_property_directly( + "buffer", + "closed", + "encoding", + "errors", + "line_buffering", + "newlines", + "name", + "mode", +) +class AsyncTextIndirectIOWrapper(AsyncIndirectBase): + """The indirect asyncio executor version of io.TextIOWrapper.""" diff --git a/.venv/lib/python3.12/site-packages/aiofiles/threadpool/utils.py b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/utils.py new file mode 100644 index 0000000..fd9767a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/aiofiles/threadpool/utils.py @@ -0,0 +1,71 @@ +import functools + + +def delegate_to_executor(*attrs): + def cls_builder(cls): + for attr_name in attrs: + setattr(cls, attr_name, _make_delegate_method(attr_name)) + return cls + + return cls_builder + + +def proxy_method_directly(*attrs): + def cls_builder(cls): + for attr_name in attrs: + setattr(cls, attr_name, _make_proxy_method(attr_name)) + return cls + + return cls_builder + + +def proxy_property_directly(*attrs): + def cls_builder(cls): + for attr_name in attrs: + setattr(cls, attr_name, _make_proxy_property(attr_name)) + return cls + + return cls_builder + + +def cond_delegate_to_executor(*attrs): + def cls_builder(cls): + for attr_name in attrs: + setattr(cls, attr_name, _make_cond_delegate_method(attr_name)) + return cls + + return cls_builder + + +def _make_delegate_method(attr_name): + async def method(self, *args, **kwargs): + cb = functools.partial(getattr(self._file, attr_name), *args, **kwargs) + return await self._loop.run_in_executor(self._executor, cb) + + return method + + +def _make_proxy_method(attr_name): + def method(self, *args, **kwargs): + return getattr(self._file, attr_name)(*args, **kwargs) + + return method + + +def _make_proxy_property(attr_name): + def proxy_property(self): + return getattr(self._file, attr_name) + + return property(proxy_property) + + +def _make_cond_delegate_method(attr_name): + """For spooled temp files, delegate only if rolled to file object""" + + async def method(self, *args, **kwargs): + if self._file._rolled: + cb = functools.partial(getattr(self._file, attr_name), *args, **kwargs) + return await self._loop.run_in_executor(self._executor, cb) + return getattr(self._file, attr_name)(*args, **kwargs) + + return method diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA new file mode 100644 index 0000000..3ac05cf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA @@ -0,0 +1,295 @@ +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/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD new file mode 100644 index 0000000..a66e278 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD @@ -0,0 +1,10 @@ +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-312.pyc,, +annotated_types/__pycache__/test_cases.cpython-312.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/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL new file mode 100644 index 0000000..516596c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.24.2 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..d99323a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +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/python3.12/site-packages/annotated_types/__init__.py b/.venv/lib/python3.12/site-packages/annotated_types/__init__.py new file mode 100644 index 0000000..74e0dee --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types/__init__.py @@ -0,0 +1,432 @@ +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/python3.12/site-packages/annotated_types/py.typed b/.venv/lib/python3.12/site-packages/annotated_types/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py b/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py new file mode 100644 index 0000000..d9164d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py @@ -0,0 +1,151 @@ +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/python3.12/site-packages/anyio-4.13.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/METADATA new file mode 100644 index 0000000..2de1cd5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/METADATA @@ -0,0 +1,105 @@ +Metadata-Version: 2.4 +Name: anyio +Version: 4.13.0 +Summary: High-level concurrency and networking framework on top of asyncio or Trio +Author-email: Alex Grönholm +License-Expression: 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: Framework :: AnyIO +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +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: typing_extensions>=4.5; python_version < "3.13" +Provides-Extra: trio +Requires-Dist: trio>=0.32.0; extra == "trio" +Dynamic: license-file + +.. 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 +.. image:: https://tidelift.com/badges/package/pypi/anyio + :target: https://tidelift.com/subscription/pkg/pypi-anyio + :alt: Tidelift + +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. + +To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it +`here `_. + +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 +* Subinterpreter support for code parallelization (on Python 3.13 and later) +* Asynchronous file I/O (using worker threads) +* Signal handling +* Asynchronous version of the functools_ module + +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/ +.. _functools: https://docs.python.org/3/library/functools.html +.. _Hypothesis: https://hypothesis.works/ + +Security contact information +---------------------------- + +To report a security vulnerability, please use the `Tidelift security contact`_. +Tidelift will coordinate the fix and disclosure. + +.. _Tidelift security contact: https://tidelift.com/security diff --git a/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/RECORD new file mode 100644 index 0000000..e5f678c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/RECORD @@ -0,0 +1,92 @@ +anyio-4.13.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +anyio-4.13.0.dist-info/METADATA,sha256=F0EYfiPlmTRwmJN2JktNxJg1GNnl0wHhzOWmz7pFvjM,4513 +anyio-4.13.0.dist-info/RECORD,, +anyio-4.13.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91 +anyio-4.13.0.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 +anyio-4.13.0.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 +anyio-4.13.0.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 +anyio/__init__.py,sha256=7iDVqMUprUuKNY91FuoKqayAhR-OY136YDPI6P78HHk,6170 +anyio/__pycache__/__init__.cpython-312.pyc,, +anyio/__pycache__/from_thread.cpython-312.pyc,, +anyio/__pycache__/functools.cpython-312.pyc,, +anyio/__pycache__/lowlevel.cpython-312.pyc,, +anyio/__pycache__/pytest_plugin.cpython-312.pyc,, +anyio/__pycache__/to_interpreter.cpython-312.pyc,, +anyio/__pycache__/to_process.cpython-312.pyc,, +anyio/__pycache__/to_thread.cpython-312.pyc,, +anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_backends/__pycache__/__init__.cpython-312.pyc,, +anyio/_backends/__pycache__/_asyncio.cpython-312.pyc,, +anyio/_backends/__pycache__/_trio.cpython-312.pyc,, +anyio/_backends/_asyncio.py,sha256=kuqlg2sBUsFdgY80xSDAw60Gx_4WNCl9iSL5XlY6lCU,99476 +anyio/_backends/_trio.py,sha256=l9U-TsKRxzmTQxSMvOhn0bNeFn_iRx3Ho30jvR5Bdu0,41366 +anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/_core/__pycache__/__init__.cpython-312.pyc,, +anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc,, +anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc,, +anyio/_core/__pycache__/_eventloop.cpython-312.pyc,, +anyio/_core/__pycache__/_exceptions.cpython-312.pyc,, +anyio/_core/__pycache__/_fileio.cpython-312.pyc,, +anyio/_core/__pycache__/_resources.cpython-312.pyc,, +anyio/_core/__pycache__/_signals.cpython-312.pyc,, +anyio/_core/__pycache__/_sockets.cpython-312.pyc,, +anyio/_core/__pycache__/_streams.cpython-312.pyc,, +anyio/_core/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/_core/__pycache__/_synchronization.cpython-312.pyc,, +anyio/_core/__pycache__/_tasks.cpython-312.pyc,, +anyio/_core/__pycache__/_tempfile.cpython-312.pyc,, +anyio/_core/__pycache__/_testing.cpython-312.pyc,, +anyio/_core/__pycache__/_typedattr.cpython-312.pyc,, +anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626 +anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215 +anyio/_core/_eventloop.py,sha256=c2EdcBX-xnKwxPcC4Pjn3_qG9I-x4IWFO2R9RqCGjM4,6448 +anyio/_core/_exceptions.py,sha256=Y3aq-Wxd7Q2HqwSg7nZPvRsHEuGazv_qeet6gqEBdPk,4407 +anyio/_core/_fileio.py,sha256=CKi1gFNiW2G4knWeBE7He7-rptQwgYjDUWfG8DSlvLs,25665 +anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 +anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016 +anyio/_core/_sockets.py,sha256=RBXHcUqZt5gg_-OOfgHVv8uq2FSKk1uVUzTdpjBoI1o,34977 +anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806 +anyio/_core/_subprocesses.py,sha256=tkmkPKEkEaiMD8C9WRZBlmgjOYRDRbZdte6e-unay2E,7916 +anyio/_core/_synchronization.py,sha256=9G3fvRsPNrrWJ_Z6gD_80wXq8I8qgAyhwM8PvHQnT2c,21061 +anyio/_core/_tasks.py,sha256=pVB7K6AAulzUM8YgXAeqNZG44nSyZ1bYJjH8GznC00I,5435 +anyio/_core/_tempfile.py,sha256=jE2w59FRF3yRo4vjkjfZF2YcqsBZvc66VWRwrJGDYGk,19624 +anyio/_core/_testing.py,sha256=u7MPqGXwpTxqI7hclSdNA30z2GH1Nw258uwKvy_RfBg,2340 +anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508 +anyio/abc/__init__.py,sha256=6mWhcl_pGXhrgZVHP_TCfMvIXIOp9mroEFM90fYCU_U,2869 +anyio/abc/__pycache__/__init__.cpython-312.pyc,, +anyio/abc/__pycache__/_eventloop.cpython-312.pyc,, +anyio/abc/__pycache__/_resources.cpython-312.pyc,, +anyio/abc/__pycache__/_sockets.cpython-312.pyc,, +anyio/abc/__pycache__/_streams.cpython-312.pyc,, +anyio/abc/__pycache__/_subprocesses.cpython-312.pyc,, +anyio/abc/__pycache__/_tasks.cpython-312.pyc,, +anyio/abc/__pycache__/_testing.cpython-312.pyc,, +anyio/abc/_eventloop.py,sha256=39lYnmtvoHaZw22sWBKOTA_zv7bamOnr8O49PqgDXdw,10629 +anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783 +anyio/abc/_sockets.py,sha256=OmVDrfemVvF9c5K1tpBgQyV6fn5v0XyCExLAqBOGz9o,13124 +anyio/abc/_streams.py,sha256=HYvna1iZbWcwLROTO6IhLX79RTRLPShZMWe0sG1q54I,7481 +anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 +anyio/abc/_tasks.py,sha256=KC7wrciE48AINOI-AhPutnFhe1ewfP7QnamFlDzqesQ,3721 +anyio/abc/_testing.py,sha256=tBJUzkSfOXJw23fe8qSJ03kJlShOYjjaEyFB6k6MYT8,1821 +anyio/from_thread.py,sha256=L-0w1HxJ6BSb-KuVi57k5Tkc3yzQrx3QK5tAxMPcY-0,19141 +anyio/functools.py,sha256=5AWM1iYTKkTzptvUhQDdLSh5GvbBW-vcs-SAUfIfA9A,12076 +anyio/lowlevel.py,sha256=AyKLVK3LaWSoK39LkCKxE4_GDMLKZBNqTrLUgk63y80,5158 +anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/pytest_plugin.py,sha256=t6h4KJstqIxfxwTZ1YO8vpUVuB99nfCLltn0NHfatHo,12775 +anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +anyio/streams/__pycache__/__init__.cpython-312.pyc,, +anyio/streams/__pycache__/buffered.cpython-312.pyc,, +anyio/streams/__pycache__/file.cpython-312.pyc,, +anyio/streams/__pycache__/memory.cpython-312.pyc,, +anyio/streams/__pycache__/stapled.cpython-312.pyc,, +anyio/streams/__pycache__/text.cpython-312.pyc,, +anyio/streams/__pycache__/tls.cpython-312.pyc,, +anyio/streams/buffered.py,sha256=2R3PeJhe4EXrdYqz44Y6-Eg9R6DrmlsYrP36Ir43-po,6263 +anyio/streams/file.py,sha256=msnrotVKGMQomUu_Rj2qz9MvIdUp6d3JGr7MOEO8kV4,4428 +anyio/streams/memory.py,sha256=F0zwzvFJKAhX_LRZGoKzzqDC2oMM-f-yyTBrEYEGOaU,10740 +anyio/streams/stapled.py,sha256=T8Xqwf8K6EgURPxbt1N4i7A8BAk-gScv-GRhjLXIf_o,4390 +anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765 +anyio/streams/tls.py,sha256=DQVkXUvsTEYKkBO8dlVU7j_5H8QOtLy4sGi1Wrjqevo,15303 +anyio/to_interpreter.py,sha256=_mLngrMy97TMR6VbW4Y6YzDUk9ZuPcQMPlkuyRh3C9k,7100 +anyio/to_process.py,sha256=J7gAA_YOuoHqnpDAf5fm1Qu6kOmTzdFbiDNvnV755vk,9798 +anyio/to_thread.py,sha256=f6h_k2d743GBv9FhAnhM_YpTvWgIrzBy9cOE0eJ1UJw,2693 diff --git a/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/WHEEL new file mode 100644 index 0000000..14a883f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/entry_points.txt new file mode 100644 index 0000000..44dd9bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +anyio = anyio.pytest_plugin diff --git a/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..104eebf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +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/python3.12/site-packages/anyio-4.13.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/top_level.txt new file mode 100644 index 0000000..c77c069 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio-4.13.0.dist-info/top_level.txt @@ -0,0 +1 @@ +anyio diff --git a/.venv/lib/python3.12/site-packages/anyio/__init__.py b/.venv/lib/python3.12/site-packages/anyio/__init__.py new file mode 100644 index 0000000..d23c5a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/__init__.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from ._core._contextmanagers import AsyncContextManagerMixin as AsyncContextManagerMixin +from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin +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_available_backends as get_available_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 BrokenWorkerInterpreter as BrokenWorkerInterpreter +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 ConnectionFailed as ConnectionFailed +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 NoEventLoopError as NoEventLoopError +from ._core._exceptions import RunFinishedError as RunFinishedError +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 TCPConnectable as TCPConnectable +from ._core._sockets import UNIXConnectable as UNIXConnectable +from ._core._sockets import as_connectable as as_connectable +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 notify_closing as notify_closing +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._tempfile import NamedTemporaryFile as NamedTemporaryFile +from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile +from ._core._tempfile import TemporaryDirectory as TemporaryDirectory +from ._core._tempfile import TemporaryFile as TemporaryFile +from ._core._tempfile import gettempdir as gettempdir +from ._core._tempfile import gettempdirb as gettempdirb +from ._core._tempfile import mkdtemp as mkdtemp +from ._core._tempfile import mkstemp as mkstemp +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 + + +def __getattr__(attr: str) -> type[BrokenWorkerInterpreter]: + """Support deprecated aliases.""" + if attr == "BrokenWorkerIntepreter": + import warnings + + warnings.warn( + "The 'BrokenWorkerIntepreter' alias is deprecated, use 'BrokenWorkerInterpreter' instead.", + DeprecationWarning, + stacklevel=2, + ) + return BrokenWorkerInterpreter + + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") diff --git a/.venv/lib/python3.12/site-packages/anyio/_backends/__init__.py b/.venv/lib/python3.12/site-packages/anyio/_backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py b/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py new file mode 100644 index 0000000..9f1ddc2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py @@ -0,0 +1,2996 @@ +from __future__ import annotations + +import array +import asyncio +import concurrent.futures +import contextvars +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, + Sequence, +) +from concurrent.futures import Future +from contextlib import AbstractContextManager, suppress +from contextvars import Context, copy_context +from dataclasses import dataclass, field +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 CodeType, TracebackType +from typing import ( + IO, + TYPE_CHECKING, + Any, + ParamSpec, + TypeVar, + cast, +) +from weakref import WeakKeyDictionary + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + TaskInfo, + abc, +) +from .._core._eventloop import ( + claim_worker_thread, + set_current_async_library, + threadlocals, +) +from .._core._exceptions import ( + BrokenResourceError, + BusyResourceError, + ClosedResourceError, + EndOfStream, + RunFinishedError, + WouldBlock, +) +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, 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] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """Shutdown and close event loop.""" + loop = self._loop + if self._state is not _State.INITIALIZED or loop is None: + return + try: + _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 # noqa: B904 + 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 via 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._cancel_reason: str | None = None + 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: + 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 + if isinstance(exc_val, BaseExceptionGroup): + cancelleds_caught, remaining = exc_val.split( + lambda exc: ( + isinstance(exc, CancelledError) + and is_anyio_cancellation(exc) + ) + ) + + if cancelleds_caught is None: + return False + + self._cancelled_caught = True + + if remaining is None: + return True + + context = remaining.__context__ + try: + # Preserve __cause__ and __suppress_context__ by avoiding `raise + # ... from ...` + raise remaining + finally: + # Preserve __context__ + remaining.__context__ = context + del context + else: + if isinstance(exc_val, CancelledError) and is_anyio_cancellation( + exc_val + ): + self._cancelled_caught = True + return True + else: + return False + 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("deadline exceeded") + 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(origin._cancel_reason) + 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, reason: str | None = None) -> None: + if not self._cancel_called: + if self._timeout_handle: + self._timeout_handle.cancel() + self._timeout_handle = None + + self._cancel_called = True + self._cancel_reason = f"Cancelled via cancel scope {id(self):x}" + if task := current_task(): + self._cancel_reason += f" by {task}" + + if reason: + self._cancel_reason += f"; reason: {reason}" + + 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 + + +_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary() + + +# +# 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 + + +if sys.version_info >= (3, 12): + _eager_task_factory_code: CodeType | None = asyncio.eager_task_factory.__code__ +else: + _eager_task_factory_code = None + + +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() + self._on_completed_fut: asyncio.Future[None] | None = None + + 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: + try: + if exc_val is not None: + self.cancel_scope.cancel() + if not isinstance(exc_val, CancelledError): + self._exceptions.append(exc_val) + + loop = get_running_loop() + try: + if self._tasks: + with CancelScope() as wait_scope: + while self._tasks: + self._on_completed_fut = loop.create_future() + + try: + await self._on_completed_fut + 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 + + self._on_completed_fut = None + 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: + # The exception that got us here should already have been + # added to self._exceptions so it's ok to break exception + # chaining and avoid adding a "During handling of above..." + # for each nesting level. + raise BaseExceptionGroup( + "unhandled errors in a TaskGroup", self._exceptions + ) from None + 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: + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_discard_from_awaited_by( + _task, self.cancel_scope._host_task + ) + + 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] + + if self._on_completed_fut is not None and not self._tasks: + try: + self._on_completed_fut.set_result(None) + except asyncio.InvalidStateError: + pass + + 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" + ) + + name = get_callable_name(func) if name is None else str(name) + loop = asyncio.get_running_loop() + if ( + (factory := loop.get_task_factory()) + and getattr(factory, "__code__", None) is _eager_task_factory_code + and (closure := getattr(factory, "__closure__", None)) + ): + custom_task_constructor = closure[0].cell_contents + task = custom_task_constructor(coro, loop=loop, name=name) + else: + task = create_task(coro, name=name) + + # Make the spawned task inherit the task group's cancel scope + _task_states[task] = TaskState( + parent_id=parent_id, cancel_scope=self.cancel_scope + ) + self.cancel_scope._tasks.add(task) + self._tasks.add(task) + if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: + asyncio.future_add_to_awaited_by(task, self.cancel_scope._host_task) + + 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[T_Retval | None, BaseException | None] + + +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 + ) + + del result, exception + + self.queue.task_done() + del item, context, func, args, future, cancel_scope + + 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") + + +# +# 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 + _closed: bool = field(init=False, default=False) + + async def send(self, item: bytes) -> None: + await AsyncIOBackend.checkpoint_if_cancelled() + stream_paused = self._stream._protocol._paused # type: ignore[attr-defined] + try: + self._stream.write(item) + await self._stream.drain() + except (ConnectionResetError, BrokenPipeError, RuntimeError) as exc: + # If closed by us and/or the peer: + # * on stdlib, drain() raises ConnectionResetError or BrokenPipeError + # * on uvloop and Winloop, write() eventually starts raising RuntimeError + if self._closed: + raise ClosedResourceError from exc + elif self._stream.is_closing(): + raise BrokenResourceError from exc + + raise + + if not stream_paused: + await AsyncIOBackend.cancel_shielded_checkpoint() + + async def aclose(self) -> None: + self._closed = True + 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 # type: ignore[name-defined] + 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.copy(): + if process.returncode is not 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: + workers = workers.copy() + 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 = 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 BrokenResourceError from self._protocol.exception + 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 BrokenResourceError from 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: + self._closed = True + if not self._transport.is_closing(): + 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: + self._closed = True + if not self._transport.is_closing(): + 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: + self._closed = True + if not self._transport.is_closing(): + 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.Future[bool]]] = RunVar("read_events") +_write_events: RunVar[dict[int, asyncio.Future[bool]]] = 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 < 0: + raise ValueError("total_tokens must be >= 0") + + 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 _notify_next_waiter(self) -> 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 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) + if event.is_set(): + self._notify_next_waiter() + + 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 + + self._notify_next_waiter() + + 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, + ) -> None: + for sig in self._handled_signals: + self._loop.remove_signal_handler(sig) + + 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: + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.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): + if sys.platform != "win32": + import uvloop + + loop_factory = uvloop.new_event_loop + else: + import winloop + + loop_factory = winloop.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( # type: ignore[return] + 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[T_Retval]() + 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, context=contextvars.Context() + ) + 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(set_current_async_library, 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() -> T_Retval: + __tracebackhide__ = True + if scope is not None: + 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: + if scope is not None: + scope._tasks.discard(task) + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + context = copy_context() + context.run(set_current_async_library, "asyncio") + scope = getattr(threadlocals, "current_cancel_scope", None) + f: concurrent.futures.Future[T_Retval] = context.run( + asyncio.run_coroutine_threadsafe, task_wrapper(), loop=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: + set_current_async_library("asyncio") + f.set_result(func(*args)) + except BaseException as exc: + f.set_exception(exc) + if not isinstance(exc, Exception): + raise + + loop = cast( + "AbstractEventLoop", token or threadlocals.current_token.native_token + ) + if loop.is_closed(): + raise RunFinishedError + + f: concurrent.futures.Future[T_Retval] = Future() + loop.call_soon_threadsafe(wrapper) + return f.result() + + @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, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + 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: + try: + read_events = _read_events.get() + except LookupError: + read_events = {} + _read_events.set(read_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if read_events.get(fd): + raise BusyResourceError("reading from") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_reader(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_reader(fd, cb) + remove_reader = selector.remove_reader + else: + remove_reader = loop.remove_reader + + read_events[fd] = fut + try: + success = await fut + finally: + try: + del read_events[fd] + except KeyError: + pass + else: + remove_reader(fd) + + if not success: + raise ClosedResourceError + + @classmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + try: + write_events = _write_events.get() + except LookupError: + write_events = {} + _write_events.set(write_events) + + fd = obj if isinstance(obj, int) else obj.fileno() + if write_events.get(fd): + raise BusyResourceError("writing to") + + loop = get_running_loop() + fut: asyncio.Future[bool] = loop.create_future() + + def cb() -> None: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + try: + fut.set_result(True) + except asyncio.InvalidStateError: + pass + + try: + loop.add_writer(fd, cb) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + selector = get_selector() + selector.add_writer(fd, cb) + remove_writer = selector.remove_writer + else: + remove_writer = loop.remove_writer + + write_events[fd] = fut + try: + success = await fut + finally: + try: + del write_events[fd] + except KeyError: + pass + else: + remove_writer(fd) + + if not success: + raise ClosedResourceError + + @classmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + fd = obj if isinstance(obj, int) else obj.fileno() + loop = get_running_loop() + + try: + write_events = _write_events.get() + except LookupError: + pass + else: + try: + fut = write_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_writer(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_writer(fd) + + try: + read_events = _read_events.get() + except LookupError: + pass + else: + try: + fut = read_events.pop(fd) + except KeyError: + pass + else: + try: + fut.set_result(False) + except asyncio.InvalidStateError: + pass + + try: + loop.remove_reader(fd) + except NotImplementedError: + from anyio._core._asyncio_selector_thread import get_selector + + get_selector().remove_reader(fd) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> SocketListener: + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + transport, protocol = await get_running_loop().create_connection( + StreamProtocol, sock=sock + ) + return SocketStream(transport, protocol) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + return UNIXSocketStream(sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return UDPSocket(transport, protocol) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + transport, protocol = await get_running_loop().create_datagram_endpoint( + DatagramProtocol, sock=sock + ) + return ConnectedUDPSocket(transport, protocol) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + return UNIXDatagramSocket(sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + return ConnectedUNIXDatagramSocket(sock) + + @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/python3.12/site-packages/anyio/_backends/_trio.py b/.venv/lib/python3.12/site-packages/anyio/_backends/_trio.py new file mode 100644 index 0000000..b85a10a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_backends/_trio.py @@ -0,0 +1,1343 @@ +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 contextlib import AbstractContextManager +from dataclasses import dataclass +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, + ParamSpec, + 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, + notify_closing, + wait_readable, + wait_writable, +) +from trio.socket import SocketType as TrioSocketType +from trio.to_thread import run_sync + +from .. import ( + CapacityLimiterStatistics, + EventStatistics, + LockStatistics, + RunFinishedError, + 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 FileDescriptorLike + +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: + return self.__original.__exit__(exc_type, exc_val, exc_tb) + + def cancel(self, reason: str | None = None) -> None: + self.__original.cancel(reason) + + @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: + try: + # trio.Nursery.__exit__ returns bool; .open_nursery has wrong type + return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value] + 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) + + +# +# 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 bytes(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: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @classmethod + def run_sync_from_thread( + cls, + func: Callable[[Unpack[PosArgsT]], T_Retval], + args: tuple[Unpack[PosArgsT]], + token: object, + ) -> T_Retval: + trio_token = cast("trio.lowlevel.TrioToken | None", token) + try: + return trio.from_thread.run_sync(func, *args, trio_token=trio_token) + except trio.RunFinishedError: + raise RunFinishedError from None + + @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, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + 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: FileDescriptorLike) -> 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: FileDescriptorLike) -> 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 notify_closing(cls, obj: FileDescriptorLike) -> None: + notify_closing(obj) + + @classmethod + async def wrap_listener_socket(cls, sock: socket.socket) -> abc.SocketListener: + return TCPSocketListener(sock) + + @classmethod + async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return SocketStream(trio_sock) + + @classmethod + async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXSocketStream(trio_sock) + + @classmethod + async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UDPSocket(trio_sock) + + @classmethod + async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUDPSocket(trio_sock) + + @classmethod + async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return UNIXDatagramSocket(trio_sock) + + @classmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket.socket + ) -> ConnectedUNIXDatagramSocket: + trio_sock = trio.socket.from_stdlib_socket(sock) + return ConnectedUNIXDatagramSocket(trio_sock) + + @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/python3.12/site-packages/anyio/_core/__init__.py b/.venv/lib/python3.12/site-packages/anyio/_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py b/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py new file mode 100644 index 0000000..9f35bae --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py @@ -0,0 +1,167 @@ +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) + # This somewhat reduces the amount of memory wasted queueing up data + # for wakeups. With these settings, maximum number of 1-byte sends + # before getting BlockingIOError: + # Linux 4.8: 6 + # macOS (darwin 15.5): 1 + # Windows 10: 525347 + # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send + # blocking, even on non-blocking sockets, so don't do that.) + self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1) + self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1) + # On Windows this is a TCP socket so this might matter. On other + # platforms this fails b/c AF_UNIX sockets aren't actually TCP. + try: + self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except OSError: + pass + + 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/python3.12/site-packages/anyio/_core/_contextmanagers.py b/.venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py new file mode 100644 index 0000000..302f32b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +from abc import abstractmethod +from contextlib import AbstractAsyncContextManager, AbstractContextManager +from inspect import isasyncgen, iscoroutine, isgenerator +from types import TracebackType +from typing import Protocol, TypeVar, cast, final + +_T_co = TypeVar("_T_co", covariant=True) +_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None") + + +class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]): + def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ... + + +class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]): + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ... + + +class ContextManagerMixin: + """ + Mixin class providing context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via :meth:`__contextmanager__` + which should return a generator. The mechanics are meant to mirror those of + :func:`@contextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractContextManager[object, bool | None] | None = None + + @final + def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__contextmanager__() + if not isinstance(cm, AbstractContextManager): + if isgenerator(cm): + raise TypeError( + "__contextmanager__() returned a generator object instead of " + "a context manager. Did you forget to add the @contextmanager " + "decorator?" + ) + + raise TypeError( + f"__contextmanager__() did not return a context manager object, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__contextmanager__() returned " + f"self. Did you forget to add the @contextmanager decorator and a " + f"'yield' statement?" + ) + + value = cm.__enter__() + self.__cm = cm + return value + + @final + def __exit__( + self: _SupportsCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, ContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __contextmanager__(self) -> AbstractContextManager[object, bool | None]: + """ + Implement your context manager logic here. + + This method **must** be decorated with + :func:`@contextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: a context manager object + """ + + +class AsyncContextManagerMixin: + """ + Mixin class providing async context manager functionality via a generator-based + implementation. + + This class allows you to implement a context manager via + :meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of + :func:`@asynccontextmanager `. + + .. note:: Classes using this mix-in are not reentrant as context managers, meaning + that once you enter it, you can't re-enter before first exiting it. + + .. seealso:: :doc:`contextmanagers` + """ + + __cm: AbstractAsyncContextManager[object, bool | None] | None = None + + @final + async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co: + # Needed for mypy to assume self still has the __cm member + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is not None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has already been entered" + ) + + cm = self.__asynccontextmanager__() + if not isinstance(cm, AbstractAsyncContextManager): + if isasyncgen(cm): + raise TypeError( + "__asynccontextmanager__() returned an async generator instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator?" + ) + elif iscoroutine(cm): + cm.close() + raise TypeError( + "__asynccontextmanager__() returned a coroutine object instead of " + "an async context manager. Did you forget to add the " + "@asynccontextmanager decorator and a 'yield' statement?" + ) + + raise TypeError( + f"__asynccontextmanager__() did not return an async context manager, " + f"but {cm.__class__!r}" + ) + + if cm is self: + raise TypeError( + f"{self.__class__.__qualname__}.__asynccontextmanager__() returned " + f"self. Did you forget to add the @asynccontextmanager decorator and a " + f"'yield' statement?" + ) + + value = await cm.__aenter__() + self.__cm = cm + return value + + @final + async def __aexit__( + self: _SupportsAsyncCtxMgr[object, _ExitT_co], + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> _ExitT_co: + assert isinstance(self, AsyncContextManagerMixin) + if self.__cm is None: + raise RuntimeError( + f"this {self.__class__.__qualname__} has not been entered yet" + ) + + # Prevent circular references + cm = self.__cm + del self.__cm + + return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb)) + + @abstractmethod + def __asynccontextmanager__( + self, + ) -> AbstractAsyncContextManager[object, bool | None]: + """ + Implement your async context manager logic here. + + This method **must** be decorated with + :func:`@asynccontextmanager `. + + .. note:: Remember that the ``yield`` will raise any exception raised in the + enclosed context block, so use a ``finally:`` block to clean up resources! + + :return: an async context manager object + """ diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py b/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py new file mode 100644 index 0000000..59a69cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py @@ -0,0 +1,234 @@ +from __future__ import annotations + +import math +import sys +import threading +from collections.abc import Awaitable, Callable, Generator +from contextlib import contextmanager +from contextvars import Token +from importlib import import_module +from typing import TYPE_CHECKING, Any, TypeVar + +from ._exceptions import NoEventLoopError + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +sniffio: Any +try: + import sniffio +except ModuleNotFoundError: + sniffio = None + +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 + + """ + if asynclib_name := current_async_library(): + 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 asynclib_name is None: + # Since we're in control of the event loop, we can cache the name of the async + # library + token = set_current_async_library(backend) + + try: + backend_options = backend_options or {} + return async_backend.run(func, args, {}, backend_options) + finally: + reset_current_async_library(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) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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_available_backends() -> tuple[str, ...]: + """ + Test for the availability of built-in backends. + + :return a tuple of the built-in backend names that were successfully imported + + .. versionadded:: 4.12 + + """ + available_backends: list[str] = [] + for backend_name in get_all_backends(): + try: + get_async_backend(backend_name) + except ImportError: + continue + + available_backends.append(backend_name) + + return tuple(available_backends) + + +def get_cancelled_exc_class() -> type[BaseException]: + """ + Return the current async library's cancellation exception class. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().cancelled_exception_class() + + +# +# Private API +# + + +@contextmanager +def claim_worker_thread( + backend_class: type[AsyncBackend], token: object +) -> Generator[Any, None, None]: + from ..lowlevel import EventLoopToken + + threadlocals.current_token = EventLoopToken(backend_class, token) + try: + yield + finally: + del threadlocals.current_token + + +def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: + if asynclib_name is None: + asynclib_name = current_async_library() + if not asynclib_name: + raise NoEventLoopError( + f"Not currently running on any asynchronous event loop. " + f"Available async backends: {', '.join(get_all_backends())}" + ) + + # 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 + + +def current_async_library() -> str | None: + if sniffio is None: + # If sniffio is not installed, we assume we're either running asyncio or nothing + import asyncio + + try: + asyncio.get_running_loop() + return "asyncio" + except RuntimeError: + pass + else: + try: + return sniffio.current_async_library() + except sniffio.AsyncLibraryNotFoundError: + pass + + return None + + +def set_current_async_library(asynclib_name: str | None) -> Token | None: + # no-op if sniffio is not installed + if sniffio is None: + return None + + return sniffio.current_async_library_cvar.set(asynclib_name) + + +def reset_current_async_library(token: Token | None) -> None: + if token is not None: + sniffio.current_async_library_cvar.reset(token) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py b/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py new file mode 100644 index 0000000..3776bed --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +from collections.abc import Generator +from textwrap import dedent +from typing import Any + +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 BrokenWorkerInterpreter(Exception): + """ + Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is + raised in the subinterpreter. + """ + + def __init__(self, excinfo: Any): + # This was adapted from concurrent.futures.interpreter.ExecutionFailed + msg = excinfo.formatted + if not msg: + if excinfo.type and excinfo.msg: + msg = f"{excinfo.type.__name__}: {excinfo.msg}" + else: + msg = excinfo.type.__name__ or excinfo.msg + + super().__init__(msg) + self.excinfo = excinfo + + def __str__(self) -> str: + try: + formatted = self.excinfo.errdisplay + except Exception: + return super().__str__() + else: + return dedent( + f""" + {super().__str__()} + + Uncaught in the interpreter: + + {formatted} + """.strip() + ) + + +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 ConnectionFailed(OSError): + """ + Raised when a connection attempt fails. + + .. note:: This class inherits from :exc:`OSError` for backwards compatibility. + """ + + +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 + + +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.""" + + +class NoEventLoopError(RuntimeError): + """ + Raised by several functions that require an event loop to be running in the current + thread when there is no running event loop. + + This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` + if not calling from an AnyIO worker thread, and no ``token`` was passed. + """ + + +class RunFinishedError(RuntimeError): + """ + Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event + loop associated with the explicitly passed token has already finished. + """ + + def __init__(self) -> None: + super().__init__( + "The event loop associated with the given token has already finished" + ) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py b/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py new file mode 100644 index 0000000..3bb8c84 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py @@ -0,0 +1,799 @@ +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, + ClassVar, + Final, + Generic, + overload, +) + +from .. import to_thread +from ..abc import AsyncResource + +if TYPE_CHECKING: + from types import ModuleType + + 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.copy` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) + * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later) + * :attr:`~pathlib.Path.info` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) + * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only + available on Python 3.13 or later) + * :meth:`~pathlib.Path.move` (available on Python 3.14 or later) + * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later) + * :meth:`~pathlib.PurePath.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__() + + if sys.version_info >= (3, 15): + + def __vfspath__(self) -> str: + return self._path.__vfspath__() + + 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: ClassVar[ModuleType] = 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) + + if sys.version_info >= (3, 14): + + @property + def info(self) -> Any: # TODO: add return type annotation when Typeshed gets it + return self._path.info + + async def copy( + self, + target: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Path: + func = partial( + self._path.copy, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return Path(await to_thread.run_sync(func, pathlib.Path(target))) + + async def copy_into( + self, + target_dir: str | os.PathLike[str], + *, + follow_symlinks: bool = True, + preserve_metadata: bool = False, + ) -> Path: + func = partial( + self._path.copy_into, + follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata, + ) + return Path(await to_thread.run_sync(func, pathlib.Path(target_dir))) + + async def move(self, target: str | os.PathLike[str]) -> Path: + # Upstream does not handle anyio.Path properly as a PathLike + target = pathlib.Path(target) + return Path(await to_thread.run_sync(self._path.move, target)) + + async def move_into( + self, + target_dir: str | os.PathLike[str], + ) -> Path: + return Path(await to_thread.run_sync(self._path.move_into, target_dir)) + + 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) + ) + + if sys.version_info < (3, 12): + # Python 3.11 and earlier + def glob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.glob(pattern) + return _PathIterator(gen) + elif (3, 12) <= sys.version_info < (3, 13): + # changed in Python 3.12: + # - The case_sensitive parameter was added. + def glob( + self, + pattern: str, + *, + case_sensitive: bool | None = None, + ) -> AsyncIterator[Path]: + gen = self._path.glob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Path]: + gen = self._path.glob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + 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 + ) + + if sys.version_info < (3, 15): + + 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) + + async def iterdir(self) -> AsyncIterator[Path]: + gen = ( + self._path.iterdir() + if sys.version_info < (3, 13) + else await to_thread.run_sync(self._path.iterdir, abandon_on_cancel=True) + ) + async for path in _PathIterator(gen): + yield path + + 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: + # relative_to() should work with any PathLike but it doesn't + others = [pathlib.Path(other) for other in other] + return Path(self._path.relative_to(*others, 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)) + + if sys.version_info < (3, 12): + # Pre Python 3.12 + def rglob(self, pattern: str) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern) + return _PathIterator(gen) + elif (3, 12) <= sys.version_info < (3, 13): + # Changed in Python 3.12: + # - The case_sensitive parameter was added. + def rglob( + self, pattern: str, *, case_sensitive: bool | None = None + ) -> AsyncIterator[Path]: + gen = self._path.rglob(pattern, case_sensitive=case_sensitive) + return _PathIterator(gen) + elif sys.version_info >= (3, 13): + # Changed in Python 3.13: + # - The recurse_symlinks parameter was added. + # - The pattern parameter accepts a path-like object. + def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block + self, + pattern: str | PathLike[str], + *, + case_sensitive: bool | None = None, + recurse_symlinks: bool = False, + ) -> AsyncIterator[Path]: + gen = self._path.rglob( + pattern, # type: ignore[arg-type] + case_sensitive=case_sensitive, + recurse_symlinks=recurse_symlinks, + ) + 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: + return await to_thread.run_sync( + self._path.write_text, data, encoding, errors, newline + ) + + +PathLike.register(Path) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py b/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py new file mode 100644 index 0000000..b9a5344 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py @@ -0,0 +1,18 @@ +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/python3.12/site-packages/anyio/_core/_signals.py b/.venv/lib/python3.12/site-packages/anyio/_core/_signals.py new file mode 100644 index 0000000..e24c79e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_signals.py @@ -0,0 +1,29 @@ +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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. 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/python3.12/site-packages/anyio/_core/_sockets.py b/.venv/lib/python3.12/site-packages/anyio/_core/_sockets.py new file mode 100644 index 0000000..6c99b3a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_sockets.py @@ -0,0 +1,1003 @@ +from __future__ import annotations + +import errno +import os +import socket +import ssl +import stat +import sys +from collections.abc import Awaitable +from dataclasses import dataclass +from ipaddress import IPv4Address, IPv6Address, ip_address +from os import PathLike, chmod +from socket import AddressFamily, SocketKind +from typing import TYPE_CHECKING, Any, Literal, cast, overload + +from .. import ConnectionFailed, to_thread +from ..abc import ( + ByteStreamConnectable, + ConnectedUDPSocket, + ConnectedUNIXDatagramSocket, + IPAddressType, + IPSockAddrType, + SocketListener, + SocketStream, + UDPSocket, + UNIXDatagramSocket, + UNIXSocketStream, +) +from ..streams.stapled import MultiListener +from ..streams.tls import TLSConnectable, 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, 12): + from typing import override +else: + from typing_extensions import override + +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 ConnectionFailed: if the connection 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, *_, 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] = [] + try: + async with create_task_group() as tg: + for _af, addr in 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 + finally: + oserrors.clear() + + 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 + :raises ConnectionFailed: if the connection fails + + """ + 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 multi-listener object containing one or more socket listeners + :raises OSError: if there's an error creating a socket, or binding to one or more + interfaces failed + + """ + asynclib = get_async_backend() + backlog = min(backlog, 65536) + local_host = str(local_host) if local_host is not None else None + + def setup_raw_socket( + fam: AddressFamily, + bind_addr: tuple[str, int] | tuple[str, int, int, int], + *, + v6only: bool = True, + ) -> socket.socket: + sock = socket.socket(fam) + try: + sock.setblocking(False) + + if fam == AddressFamily.AF_INET6: + sock.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, v6only) + + # For Windows, enable exclusive address use. For others, enable address + # reuse. + if sys.platform == "win32": + sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) + else: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if reuse_port: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + + # Workaround for #554 + if fam == socket.AF_INET6 and "%" in bind_addr[0]: + addr, scope_id = bind_addr[0].split("%", 1) + bind_addr = (addr, bind_addr[1], 0, int(scope_id)) + + sock.bind(bind_addr) + sock.listen(backlog) + except BaseException: + sock.close() + raise + + return sock + + # We passing type=0 on non-Windows platforms as a workaround for a 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 + gai_res = await getaddrinfo( + local_host, + local_port, + family=family, + type=socket.SOCK_STREAM if sys.platform == "win32" else 0, + flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, + ) + + # The set comprehension is here to work around a glibc bug: + # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 + sockaddrs = sorted({res for res in gai_res if res[1] == SocketKind.SOCK_STREAM}) + + # Special case for dual-stack binding on the "any" interface + if ( + local_host is None + and family == AddressFamily.AF_UNSPEC + and socket.has_dualstack_ipv6() + and any(fam == AddressFamily.AF_INET6 for fam, *_ in gai_res) + ): + raw_socket = setup_raw_socket( + AddressFamily.AF_INET6, ("::", local_port), v6only=False + ) + listener = asynclib.create_tcp_listener(raw_socket) + return MultiListener([listener]) + + errors: list[OSError] = [] + try: + for _ in range(len(sockaddrs)): + listeners: list[SocketListener] = [] + bound_ephemeral_port = local_port + try: + for fam, *_, sockaddr in sockaddrs: + sockaddr = sockaddr[0], bound_ephemeral_port, *sockaddr[2:] + raw_socket = setup_raw_socket(fam, sockaddr) + + # Store the assigned port if an ephemeral port was requested, so + # we'll bind to the same port on all interfaces + if local_port == 0 and len(gai_res) > 1: + bound_ephemeral_port = raw_socket.getsockname()[1] + + listeners.append(asynclib.create_tcp_listener(raw_socket)) + except BaseException as exc: + for listener in listeners: + await listener.aclose() + + # If an ephemeral port was requested but binding the assigned port + # failed for another interface, rotate the address list and try again + if ( + isinstance(exc, OSError) + and exc.errno == errno.EADDRINUSE + and local_port == 0 + and bound_ephemeral_port + ): + errors.append(exc) + sockaddrs.append(sockaddrs.pop(0)) + continue + + raise + + return MultiListener(listeners) + + raise OSError( + f"Could not create {len(sockaddrs)} listeners with a consistent port" + ) from ExceptionGroup("Several bind attempts failed", errors) + finally: + del errors # Prevent reference cycles + + +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 + # filter out IPv6 results when IPv6 is disabled + if not isinstance(sockaddr[0], int) + ] + + +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) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. 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) + + +def notify_closing(obj: FileDescriptorLike) -> None: + """ + Call this before closing a file descriptor (on Unix) or socket (on + Windows). This will cause any `wait_readable` or `wait_writable` + calls on the given object to immediately wake up and raise + `~anyio.ClosedResourceError`. + + This doesn't actually close the object – you still have to do that + yourself afterwards. Also, you want to be careful to make sure no + new tasks start waiting on the object in between when you call this + and when it's actually closed. So to close something properly, you + usually want to do these steps in order: + + 1. Explicitly mark the object as closed, so that any new attempts + to use it will abort before they start. + 2. Call `notify_closing` to wake up any already-existing users. + 3. Actually close the object. + + It's also possible to do them in a different order if that's more + convenient, *but only if* you make sure not to have any checkpoints in + between the steps. This way they all happen in a single atomic + step, so other tasks won't be able to tell what order they happened + in anyway. + + :param obj: an object with a ``.fileno()`` method or an integer handle + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + get_async_backend().notify_closing(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 + + +@dataclass +class TCPConnectable(ByteStreamConnectable): + """ + Connects to a TCP server at the given host and port. + + :param host: host name or IP address of the server + :param port: TCP port number of the server + """ + + host: str | IPv4Address | IPv6Address + port: int + + def __post_init__(self) -> None: + if self.port < 1 or self.port > 65535: + raise ValueError("TCP port number out of range") + + @override + async def connect(self) -> SocketStream: + try: + return await connect_tcp(self.host, self.port) + except OSError as exc: + raise ConnectionFailed( + f"error connecting to {self.host}:{self.port}: {exc}" + ) from exc + + +@dataclass +class UNIXConnectable(ByteStreamConnectable): + """ + Connects to a UNIX domain socket at the given path. + + :param path: the file system path of the socket + """ + + path: str | bytes | PathLike[str] | PathLike[bytes] + + @override + async def connect(self) -> UNIXSocketStream: + try: + return await connect_unix(self.path) + except OSError as exc: + raise ConnectionFailed(f"error connecting to {self.path!r}: {exc}") from exc + + +def as_connectable( + remote: ByteStreamConnectable + | tuple[str | IPv4Address | IPv6Address, int] + | str + | bytes + | PathLike[str], + /, + *, + tls: bool = False, + ssl_context: ssl.SSLContext | None = None, + tls_hostname: str | None = None, + tls_standard_compatible: bool = True, +) -> ByteStreamConnectable: + """ + Return a byte stream connectable from the given object. + + If a bytestream connectable is given, it is returned unchanged. + If a tuple of (host, port) is given, a TCP connectable is returned. + If a string or bytes path is given, a UNIX connectable is returned. + + If ``tls=True``, the connectable will be wrapped in a + :class:`~.streams.tls.TLSConnectable`. + + :param remote: a connectable, a tuple of (host, port) or a path to a UNIX socket + :param tls: if ``True``, wrap the plaintext connectable in a + :class:`~.streams.tls.TLSConnectable`, using the provided TLS settings) + :param ssl_context: if ``tls=True``, the SSLContext object to use (if not provided, + a secure default will be created) + :param tls_hostname: if ``tls=True``, host name of the server to use for checking + the server certificate (defaults to the host portion of the address for TCP + connectables) + :param tls_standard_compatible: if ``False`` and ``tls=True``, makes the TLS stream + skip the closing handshake when closing the connection, so it won't raise an + exception if the server does the same + + """ + connectable: TCPConnectable | UNIXConnectable | TLSConnectable + if isinstance(remote, ByteStreamConnectable): + return remote + elif isinstance(remote, tuple) and len(remote) == 2: + connectable = TCPConnectable(*remote) + elif isinstance(remote, (str, bytes, PathLike)): + connectable = UNIXConnectable(remote) + else: + raise TypeError(f"cannot convert {remote!r} to a connectable") + + if tls: + if not tls_hostname and isinstance(connectable, TCPConnectable): + tls_hostname = str(connectable.host) + + connectable = TLSConnectable( + connectable, + ssl_context=ssl_context, + hostname=tls_hostname, + standard_compatible=tls_standard_compatible, + ) + + return connectable diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py b/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py new file mode 100644 index 0000000..2b9c7df --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py @@ -0,0 +1,52 @@ +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/python3.12/site-packages/anyio/_core/_subprocesses.py b/.venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py new file mode 100644 index 0000000..9796f8b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +from collections.abc import AsyncIterable, Iterable, Mapping, Sequence +from io import BytesIO +from os import PathLike +from subprocess import PIPE, CalledProcessError, CompletedProcess +from typing import IO, Any, TypeAlias, cast + +from ..abc import Process +from ._eventloop import get_async_backend +from ._tasks import create_task_group + +StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] + + +async def run_process( + command: StrOrBytesPath | Sequence[StrOrBytesPath], + *, + input: bytes | None = None, + stdin: int | IO[Any] | 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 stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, + a file-like object, or `None`; ``input`` overrides this + :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() + + if stdin is not None and input is not None: + raise ValueError("only one of stdin and input is allowed") + + async with await open_process( + command, + stdin=PIPE if input else stdin, + 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/python3.12/site-packages/anyio/_core/_synchronization.py b/.venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py new file mode 100644 index 0000000..9c6f9a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py @@ -0,0 +1,757 @@ +from __future__ import annotations + +import math +from collections import deque +from collections.abc import Callable +from dataclasses import dataclass +from types import TracebackType +from typing import TypeVar + +from ..lowlevel import checkpoint_if_cancelled +from ._eventloop import get_async_backend +from ._exceptions import BusyResourceError, NoEventLoopError +from ._tasks import CancelScope +from ._testing import TaskInfo, get_current_task + +T = TypeVar("T") + + +@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 NoEventLoopError: + 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 NoEventLoopError: + 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_if_cancelled() + self._check_acquired() + event = Event() + self._waiters.append(event) + self.release() + try: + await event.wait() + except BaseException: + if not event.is_set(): + self._waiters.remove(event) + elif self._waiters: + # This task was notified by could not act on it, so pass + # it on to the next task + self._waiters.popleft().set() + + raise + finally: + with CancelScope(shield=True): + await self.acquire() + + async def wait_for(self, predicate: Callable[[], T]) -> T: + """ + Wait until a predicate becomes true. + + :param predicate: a callable that returns a truthy value when the condition is + met + :return: the result of the predicate + + .. versionadded:: 4.11.0 + + """ + while not (result := predicate()): + await self.wait() + + return result + + 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 NoEventLoopError: + 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 NoEventLoopError: + 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, + ) -> 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. + .. versionchanged:: 4.12 + The value can now be set to 0. + + """ + 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, + ) -> 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, + ) -> None: + self._guarded = False diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py b/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py new file mode 100644 index 0000000..0688bfe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py @@ -0,0 +1,173 @@ +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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + 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, reason: str | None = None) -> None: + """ + Cancel this scope immediately. + + :param reason: a message describing the reason for the cancellation + + """ + 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: + 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`\\] + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_effective_deadline() + + +def create_task_group() -> TaskGroup: + """ + Create a task group. + + :return: a task group + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().create_task_group() diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py b/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py new file mode 100644 index 0000000..75a09f7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py @@ -0,0 +1,613 @@ +from __future__ import annotations + +import os +import sys +import tempfile +from collections.abc import Iterable +from io import BytesIO, TextIOWrapper +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + AnyStr, + Generic, + overload, +) + +from .. import to_thread +from .._core._fileio import AsyncFile +from ..lowlevel import checkpoint_if_cancelled + +if TYPE_CHECKING: + from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer + + +class TemporaryFile(Generic[AnyStr]): + """ + An asynchronous temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager interface to a temporary file. + The file is created using Python's standard `tempfile.TemporaryFile` function in a + background thread, and is wrapped as an asynchronous file using `AsyncFile`. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: TemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: TemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + mode: OpenTextMode | OpenBinaryMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self.mode = mode + self.buffering = buffering + self.encoding = encoding + self.newline = newline + self.suffix: str | None = suffix + self.prefix: str | None = prefix + self.dir: str | None = dir + self.errors = errors + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile( + self.mode, + self.buffering, + self.encoding, + self.newline, + self.suffix, + self.prefix, + self.dir, + errors=self.errors, + ) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class NamedTemporaryFile(Generic[AnyStr]): + """ + An asynchronous named temporary file that is automatically created and cleaned up. + + This class provides an asynchronous context manager for a temporary file with a + visible name in the file system. It uses Python's standard + :func:`~tempfile.NamedTemporaryFile` function and wraps the file object with + :class:`AsyncFile` for asynchronous operations. + + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file. Only applicable in + text mode. + :param newline: Controls how universal newlines mode works (only applicable in text + mode). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param delete: Whether to delete the file when it is closed. + :param errors: The error handling scheme used for encoding/decoding errors. + :param delete_on_close: (Python 3.12+) Whether to delete the file on close. + """ + + _async_file: AsyncFile[AnyStr] + + @overload + def __init__( + self: NamedTemporaryFile[bytes], + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + @overload + def __init__( + self: NamedTemporaryFile[str], + mode: OpenTextMode, + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + delete: bool = ..., + *, + errors: str | None = ..., + delete_on_close: bool = ..., + ): ... + + def __init__( + self, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + delete: bool = True, + *, + errors: str | None = None, + delete_on_close: bool = True, + ) -> None: + self._params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "delete": delete, + "errors": errors, + } + if sys.version_info >= (3, 12): + self._params["delete_on_close"] = delete_on_close + + async def __aenter__(self) -> AsyncFile[AnyStr]: + fp = await to_thread.run_sync( + lambda: tempfile.NamedTemporaryFile(**self._params) + ) + self._async_file = AsyncFile(fp) + return self._async_file + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + await self._async_file.aclose() + + +class SpooledTemporaryFile(AsyncFile[AnyStr]): + """ + An asynchronous spooled temporary file that starts in memory and is spooled to disk. + + This class provides an asynchronous interface to a spooled temporary file, much like + Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous + write operations and provides a method to force a rollover to disk. + + :param max_size: Maximum size in bytes before the file is rolled over to disk. + :param mode: The mode in which the file is opened. Defaults to "w+b". + :param buffering: The buffering policy (-1 means the default buffering). + :param encoding: The encoding used to decode or encode the file (text mode only). + :param newline: Controls how universal newlines mode works (text mode only). + :param suffix: The suffix for the temporary file name. + :param prefix: The prefix for the temporary file name. + :param dir: The directory in which the temporary file is created. + :param errors: The error handling scheme used for encoding/decoding errors. + """ + + _rolled: bool = False + + @overload + def __init__( + self: SpooledTemporaryFile[bytes], + max_size: int = ..., + mode: OpenBinaryMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + @overload + def __init__( + self: SpooledTemporaryFile[str], + max_size: int = ..., + mode: OpenTextMode = ..., + buffering: int = ..., + encoding: str | None = ..., + newline: str | None = ..., + suffix: str | None = ..., + prefix: str | None = ..., + dir: str | None = ..., + *, + errors: str | None = ..., + ): ... + + def __init__( + self, + max_size: int = 0, + mode: OpenBinaryMode | OpenTextMode = "w+b", + buffering: int = -1, + encoding: str | None = None, + newline: str | None = None, + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + *, + errors: str | None = None, + ) -> None: + self._tempfile_params: dict[str, Any] = { + "mode": mode, + "buffering": buffering, + "encoding": encoding, + "newline": newline, + "suffix": suffix, + "prefix": prefix, + "dir": dir, + "errors": errors, + } + self._max_size = max_size + if "b" in mode: + super().__init__(BytesIO()) # type: ignore[arg-type] + else: + super().__init__( + TextIOWrapper( # type: ignore[arg-type] + BytesIO(), + encoding=encoding, + errors=errors, + newline=newline, + write_through=True, + ) + ) + + async def aclose(self) -> None: + if not self._rolled: + self._fp.close() + return + + await super().aclose() + + async def _check(self) -> None: + if self._rolled or self._fp.tell() <= self._max_size: + return + + await self.rollover() + + async def rollover(self) -> None: + if self._rolled: + return + + self._rolled = True + buffer = self._fp + buffer.seek(0) + self._fp = await to_thread.run_sync( + lambda: tempfile.TemporaryFile(**self._tempfile_params) + ) + await self.write(buffer.read()) + buffer.close() + + @property + def closed(self) -> bool: + return self._fp.closed + + async def read(self, size: int = -1) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read(size) + + return await super().read(size) # type: ignore[return-value] + + async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.read1(size) + + return await super().read1(size) + + async def readline(self) -> AnyStr: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readline() + + return await super().readline() # type: ignore[return-value] + + async def readlines(self) -> list[AnyStr]: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.readlines() + + return await super().readlines() # type: ignore[return-value] + + async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + self._fp.readinto(b) + + return await super().readinto(b) + + async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + self._fp.readinto(b) + + return await super().readinto1(b) + + async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.seek(offset, whence) + + return await super().seek(offset, whence) + + async def tell(self) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.tell() + + return await super().tell() + + async def truncate(self, size: int | None = None) -> int: + if not self._rolled: + await checkpoint_if_cancelled() + return self._fp.truncate(size) + + return await super().truncate(size) + + @overload + async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ... + @overload + async def write(self: SpooledTemporaryFile[str], b: str) -> int: ... + + async def write(self, b: ReadableBuffer | str) -> int: + """ + Asynchronously write data to the spooled temporary file. + + If the file has not yet been rolled over, the data is written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param s: The data to write. + :return: The number of bytes written. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.write(b) + await self._check() + return result + + return await super().write(b) # type: ignore[misc] + + @overload + async def writelines( + self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer] + ) -> None: ... + @overload + async def writelines( + self: SpooledTemporaryFile[str], lines: Iterable[str] + ) -> None: ... + + async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None: + """ + Asynchronously write a list of lines to the spooled temporary file. + + If the file has not yet been rolled over, the lines are written synchronously, + and a rollover is triggered if the size exceeds the maximum size. + + :param lines: An iterable of lines to write. + :raises RuntimeError: If the underlying file is not initialized. + + """ + if not self._rolled: + await checkpoint_if_cancelled() + result = self._fp.writelines(lines) + await self._check() + return result + + return await super().writelines(lines) # type: ignore[misc] + + +class TemporaryDirectory(Generic[AnyStr]): + """ + An asynchronous temporary directory that is created and cleaned up automatically. + + This class provides an asynchronous context manager for creating a temporary + directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to + perform directory creation and cleanup operations in a background thread. + + :param suffix: Suffix to be added to the temporary directory name. + :param prefix: Prefix to be added to the temporary directory name. + :param dir: The parent directory where the temporary directory is created. + :param ignore_cleanup_errors: Whether to ignore errors during cleanup + :param delete: Whether to delete the directory upon closing (Python 3.12+). + """ + + def __init__( + self, + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + *, + ignore_cleanup_errors: bool = False, + delete: bool = True, + ) -> None: + self.suffix: AnyStr | None = suffix + self.prefix: AnyStr | None = prefix + self.dir: AnyStr | None = dir + self.ignore_cleanup_errors = ignore_cleanup_errors + self.delete = delete + + self._tempdir: tempfile.TemporaryDirectory | None = None + + async def __aenter__(self) -> str: + params: dict[str, Any] = { + "suffix": self.suffix, + "prefix": self.prefix, + "dir": self.dir, + "ignore_cleanup_errors": self.ignore_cleanup_errors, + } + if sys.version_info >= (3, 12): + params["delete"] = self.delete + + self._tempdir = await to_thread.run_sync( + lambda: tempfile.TemporaryDirectory(**params) + ) + return await to_thread.run_sync(self._tempdir.__enter__) + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + if self._tempdir is not None: + await to_thread.run_sync( + self._tempdir.__exit__, exc_type, exc_value, traceback + ) + + async def cleanup(self) -> None: + if self._tempdir is not None: + await to_thread.run_sync(self._tempdir.cleanup) + + +@overload +async def mkstemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, + text: bool = False, +) -> tuple[int, str]: ... + + +@overload +async def mkstemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, + text: bool = False, +) -> tuple[int, bytes]: ... + + +async def mkstemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, + text: bool = False, +) -> tuple[int, str | bytes]: + """ + Asynchronously create a temporary file and return an OS-level handle and the file + name. + + This function wraps `tempfile.mkstemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the file name. + :param prefix: Prefix to be added to the file name. + :param dir: Directory in which the temporary file is created. + :param text: Whether the file is opened in text mode. + :return: A tuple containing the file descriptor and the file name. + + """ + return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text) + + +@overload +async def mkdtemp( + suffix: str | None = None, + prefix: str | None = None, + dir: str | None = None, +) -> str: ... + + +@overload +async def mkdtemp( + suffix: bytes | None = None, + prefix: bytes | None = None, + dir: bytes | None = None, +) -> bytes: ... + + +async def mkdtemp( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + dir: AnyStr | None = None, +) -> str | bytes: + """ + Asynchronously create a temporary directory and return its path. + + This function wraps `tempfile.mkdtemp` and executes it in a background thread. + + :param suffix: Suffix to be added to the directory name. + :param prefix: Prefix to be added to the directory name. + :param dir: Parent directory where the temporary directory is created. + :return: The path of the created temporary directory. + + """ + return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir) + + +async def gettempdir() -> str: + """ + Asynchronously return the name of the directory used for temporary files. + + This function wraps `tempfile.gettempdir` and executes it in a background thread. + + :return: The path of the temporary directory as a string. + + """ + return await to_thread.run_sync(tempfile.gettempdir) + + +async def gettempdirb() -> bytes: + """ + Asynchronously return the name of the directory used for temporary files in bytes. + + This function wraps `tempfile.gettempdirb` and executes it in a background thread. + + :return: The path of the temporary directory as bytes. + + """ + return await to_thread.run_sync(tempfile.gettempdirb) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py b/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py new file mode 100644 index 0000000..369e65c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py @@ -0,0 +1,82 @@ +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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + 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/python3.12/site-packages/anyio/_core/_typedattr.py b/.venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py new file mode 100644 index 0000000..f358a44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py @@ -0,0 +1,81 @@ +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/python3.12/site-packages/anyio/abc/__init__.py b/.venv/lib/python3.12/site-packages/anyio/abc/__init__.py new file mode 100644 index 0000000..d560ce3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/__init__.py @@ -0,0 +1,58 @@ +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 AnyByteStreamConnectable as AnyByteStreamConnectable +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 ByteStreamConnectable as ByteStreamConnectable +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 ObjectStreamConnectable as ObjectStreamConnectable +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/python3.12/site-packages/anyio/abc/_eventloop.py b/.venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py new file mode 100644 index 0000000..ae06288 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py @@ -0,0 +1,409 @@ +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, + TypeAlias, + TypeVar, + overload, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if TYPE_CHECKING: + from _typeshed import FileDescriptorLike + + from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore + from .._core._tasks import CancelScope + from .._core._testing import TaskInfo + 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 = 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 an object that allows other threads to run code inside the event loop. + + :return: a token object, specific to the event loop running in the current + thread + """ + + @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 + 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, + ) -> Sequence[ + tuple[ + AddressFamily, + SocketKind, + int, + str, + tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], + ] + ]: + pass + + @classmethod + @abstractmethod + async def getnameinfo( + cls, sockaddr: IPSockAddrType, flags: int = 0 + ) -> tuple[str, str]: + pass + + @classmethod + @abstractmethod + async def wait_readable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wait_writable(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + def notify_closing(cls, obj: FileDescriptorLike) -> None: + pass + + @classmethod + @abstractmethod + async def wrap_listener_socket(cls, sock: socket) -> SocketListener: + pass + + @classmethod + @abstractmethod + async def wrap_stream_socket(cls, sock: socket) -> SocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream: + pass + + @classmethod + @abstractmethod + async def wrap_udp_socket(cls, sock: socket) -> UDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket: + pass + + @classmethod + @abstractmethod + async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket: + pass + + @classmethod + @abstractmethod + async def wrap_connected_unix_datagram_socket( + cls, sock: socket + ) -> ConnectedUNIXDatagramSocket: + 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/python3.12/site-packages/anyio/abc/_resources.py b/.venv/lib/python3.12/site-packages/anyio/abc/_resources.py new file mode 100644 index 0000000..10df115 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_resources.py @@ -0,0 +1,33 @@ +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/python3.12/site-packages/anyio/abc/_sockets.py b/.venv/lib/python3.12/site-packages/anyio/abc/_sockets.py new file mode 100644 index 0000000..feb26bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_sockets.py @@ -0,0 +1,399 @@ +from __future__ import annotations + +import errno +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 typing import Any, TypeAlias, TypeVar + +from .._core._eventloop import get_async_backend +from .._core._typedattr import ( + TypedAttributeProvider, + TypedAttributeSet, + typed_attribute, +) +from ._streams import ByteStream, Listener, UnreliableObjectStream +from ._tasks import TaskGroup + +IPAddressType: TypeAlias = str | IPv4Address | IPv6Address +IPSockAddrType: TypeAlias = tuple[str, int] +SockAddrType: TypeAlias = IPSockAddrType | str +UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType] +UNIXDatagramPacketType: TypeAlias = tuple[bytes, str] +T_Retval = TypeVar("T_Retval") + + +def _validate_socket( + sock_or_fd: socket.socket | int, + sock_type: socket.SocketKind, + addr_family: socket.AddressFamily = socket.AF_UNSPEC, + *, + require_connected: bool = False, + require_bound: bool = False, +) -> socket.socket: + if isinstance(sock_or_fd, int): + try: + sock = socket.socket(fileno=sock_or_fd) + except OSError as exc: + if exc.errno == errno.ENOTSOCK: + raise ValueError( + "the file descriptor does not refer to a socket" + ) from exc + elif require_connected: + raise ValueError("the socket must be connected") from exc + elif require_bound: + raise ValueError("the socket must be bound to a local address") from exc + else: + raise + elif isinstance(sock_or_fd, socket.socket): + sock = sock_or_fd + else: + raise TypeError( + f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead" + ) + + try: + if require_connected: + try: + sock.getpeername() + except OSError as exc: + raise ValueError("the socket must be connected") from exc + + if require_bound: + try: + if sock.family in (socket.AF_INET, socket.AF_INET6): + bound_addr = sock.getsockname()[1] + else: + bound_addr = sock.getsockname() + except OSError: + bound_addr = None + + if not bound_addr: + raise ValueError("the socket must be bound to a local address") + + if addr_family != socket.AF_UNSPEC and sock.family != addr_family: + raise ValueError( + f"address family mismatch: expected {addr_family.name}, got " + f"{sock.family.name}" + ) + + if sock.type != sock_type: + raise ValueError( + f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}" + ) + except BaseException: + # Avoid ResourceWarning from the locally constructed socket object + if isinstance(sock_or_fd, int): + sock.detach() + + raise + + sock.setblocking(False) + return sock + + +class SocketAttribute(TypedAttributeSet): + """ + .. attribute:: family + :type: socket.AddressFamily + + the address family of the underlying socket + + .. attribute:: local_address + :type: tuple[str, int] | str + + the local address the underlying socket is connected to + + .. attribute:: local_port + :type: int + + for IP based sockets, the local port the underlying socket is bound to + + .. attribute:: raw_socket + :type: socket.socket + + the underlying stdlib socket object + + .. attribute:: remote_address + :type: tuple[str, int] | str + + the remote address the underlying socket is connected to + + .. attribute:: remote_port + :type: int + + for IP based sockets, the remote port the underlying socket is connected to + """ + + family: AddressFamily = typed_attribute() + local_address: SockAddrType = typed_attribute() + local_port: int = typed_attribute() + raw_socket: socket.socket = typed_attribute() + remote_address: SockAddrType = typed_attribute() + 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`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream: + """ + Wrap an existing socket object or file descriptor as a socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket stream + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True) + return await get_async_backend().wrap_stream_socket(sock) + + +class UNIXSocketStream(SocketStream): + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream: + """ + Wrap an existing socket object or file descriptor as a UNIX socket stream. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX socket stream + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_unix_stream_socket(sock) + + @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`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> SocketListener: + """ + Wrap an existing socket object or file descriptor as a socket listener. + + The newly created listener takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a socket listener + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True) + return await get_async_backend().wrap_listener_socket(sock) + + @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`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket: + """ + Wrap an existing socket object or file descriptor as a UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must be bound to a local address. + + :param sock_or_fd: a socket object or file descriptor + :return: a UDP socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True) + return await get_async_backend().wrap_udp_socket(sock) + + 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`. + """ + + @classmethod + async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket: + """ + Wrap an existing socket object or file descriptor as a connected UDP socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UDP socket + + """ + sock = _validate_socket( + sock_or_fd, + socket.SOCK_DGRAM, + require_connected=True, + ) + return await get_async_backend().wrap_connected_udp_socket(sock) + + +class UNIXDatagramSocket( + UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider +): + """ + Represents an unconnected Unix datagram socket. + + Supports all relevant extra attributes from :class:`~SocketAttribute`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> UNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + + :param sock_or_fd: a socket object or file descriptor + :return: a UNIX datagram socket + + """ + sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX) + return await get_async_backend().wrap_unix_datagram_socket(sock) + + 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`. + """ + + @classmethod + async def from_socket( + cls, + sock_or_fd: socket.socket | int, + ) -> ConnectedUNIXDatagramSocket: + """ + Wrap an existing socket object or file descriptor as a connected UNIX datagram + socket. + + The newly created socket wrapper takes ownership of the socket being passed in. + The existing socket must already be connected. + + :param sock_or_fd: a socket object or file descriptor + :return: a connected UNIX datagram socket + + """ + sock = _validate_socket( + sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True + ) + return await get_async_backend().wrap_connected_unix_datagram_socket(sock) diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py b/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py new file mode 100644 index 0000000..186e3f5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py @@ -0,0 +1,233 @@ +from __future__ import annotations + +from abc import ABCMeta, abstractmethod +from collections.abc import Callable +from typing import Any, Generic, TypeAlias, TypeVar + +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 from None + + @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 from None + + @abstractmethod + async def receive(self, max_bytes: int = 65536) -> bytes: + """ + Receive at most ``max_bytes`` bytes from the peer. + + .. note:: Implementers 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: TypeAlias = ( + UnreliableObjectReceiveStream[bytes] | ByteReceiveStream +) +#: Type alias for all unreliable bytes-oriented send streams. +AnyUnreliableByteSendStream: TypeAlias = ( + UnreliableObjectSendStream[bytes] | ByteSendStream +) +#: Type alias for all unreliable bytes-oriented streams. +AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream +#: Type alias for all bytes-oriented receive streams. +AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream +#: Type alias for all bytes-oriented send streams. +AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream +#: Type alias for all bytes-oriented streams. +AnyByteStream: TypeAlias = 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) + """ + + +class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ObjectStream[T_co]: + """ + Connect to the remote endpoint. + + :return: an object stream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +class ByteStreamConnectable(metaclass=ABCMeta): + @abstractmethod + async def connect(self) -> ByteStream: + """ + Connect to the remote endpoint. + + :return: a bytestream connected to the remote end + :raises ConnectionFailed: if the connection fails + """ + + +#: Type alias for all connectables returning bytestreams or bytes-oriented object streams +AnyByteStreamConnectable: TypeAlias = ( + ObjectStreamConnectable[bytes] | ByteStreamConnectable +) diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py b/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py new file mode 100644 index 0000000..ce0564c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py @@ -0,0 +1,79 @@ +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/python3.12/site-packages/anyio/abc/_tasks.py b/.venv/lib/python3.12/site-packages/anyio/abc/_tasks.py new file mode 100644 index 0000000..516b3ec --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_tasks.py @@ -0,0 +1,117 @@ +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, overload + +if sys.version_info >= (3, 13): + from typing import TypeVar +else: + from typing_extensions import TypeVar + +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, default=None) +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. + + The target callable must accept a keyword argument ``task_status`` (of type + :class:`TaskStatus`). Awaiting on this method will return whatever was passed to + ``task_status.started()`` (``None`` by default). + + .. note:: The :class:`TaskStatus` class is generic, and the type argument should + indicate the type of the value that will be passed to + ``task_status.started()``. + + :param func: a coroutine function that accepts the ``task_status`` keyword + argument + :param args: positional arguments to call the function with + :param name: an optional name for the task, for introspection and debugging + :return: the value passed to ``task_status.started()`` + :raises RuntimeError: if the task finishes without calling + ``task_status.started()`` + + .. seealso:: :ref:`start_initialize` + + .. 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: + """Exit the task group context waiting for all tasks to finish.""" diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py b/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py new file mode 100644 index 0000000..7c50ed7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py @@ -0,0 +1,65 @@ +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/python3.12/site-packages/anyio/from_thread.py b/.venv/lib/python3.12/site-packages/anyio/from_thread.py new file mode 100644 index 0000000..837de5e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/from_thread.py @@ -0,0 +1,578 @@ +from __future__ import annotations + +__all__ = ( + "BlockingPortal", + "BlockingPortalProvider", + "check_cancelled", + "run", + "run_sync", + "start_blocking_portal", +) + +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 functools import partial +from inspect import isawaitable +from threading import Lock, Thread, current_thread, get_ident +from types import TracebackType +from typing import ( + Any, + Generic, + TypeVar, + cast, + overload, +) + +from ._core._eventloop import ( + get_cancelled_exc_class, + threadlocals, +) +from ._core._eventloop import run as run_eventloop +from ._core._exceptions import NoEventLoopError +from ._core._synchronization import Event +from ._core._tasks import CancelScope, create_task_group +from .abc._tasks import TaskStatus +from .lowlevel import EventLoopToken, current_token + +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 _token_or_error(token: EventLoopToken | None) -> EventLoopToken: + if token is not None: + return token + + try: + return threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "Not running inside an AnyIO worker thread, and no event loop token was " + "provided" + ) from None + + +def run( + func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> T_Retval: + """ + Call a coroutine function from a worker thread. + + :param func: a coroutine function + :param args: positional arguments for the callable + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the coroutine function + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_async_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + token: EventLoopToken | None = None, +) -> 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 + :param token: an event loop token to use to get back to the event loop thread + (required if calling this function from outside an AnyIO worker thread) + :return: the return value of the callable + :raises MissingTokenError: if no token was provided and called from outside an + AnyIO worker thread + :raises RunFinishedError: if the event loop tied to ``token`` is no longer running + + .. versionchanged:: 4.11.0 + Added the ``token`` parameter. + + """ + explicit_token = token is not None + token = _token_or_error(token) + return token.backend_class.run_sync_from_thread( + func, args, token=token.native_token if explicit_token else None + ) + + +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. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + """ + + def __init__(self) -> None: + self._token = current_token() + self._event_loop_thread_id: int | None = get_ident() + self._stop_event = Event() + self._task_group = create_task_group() + + 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: + 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("the blocking portal is shutting down") + + 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(): + if self._event_loop_thread_id == get_ident(): + scope.cancel("the future was cancelled") + elif self._event_loop_thread_id is not None: + self.call(scope.cancel, "the future was cancelled") + + try: + retval_or_awaitable = func(*args, **kwargs) + if isawaitable(retval_or_awaitable): + with CancelScope() as scope: + future.add_done_callback(callback) + retval = await retval_or_awaitable + else: + retval = retval_or_awaitable + except get_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. + + :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 + + """ + run_sync( + partial(self._task_group.start_soon, name=name), + self._call_func, + func, + args, + kwargs, + future, + token=self._token, + ) + + @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, + *, + name: str | 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 + :param name: name of the thread + :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_: + if name is None: + current_thread().name = f"{backend}-portal-{id(portal_):x}" + + future.set_result(portal_) + await portal_.sleep_until_stopped() + + def run_blocking_portal() -> None: + if future.set_running_or_notify_cancel(): + try: + run_eventloop( + 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, name=name) + 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: + token: EventLoopToken = threadlocals.current_token + except AttributeError: + raise NoEventLoopError( + "This function can only be called inside an AnyIO worker thread" + ) from None + + token.backend_class.check_cancelled() diff --git a/.venv/lib/python3.12/site-packages/anyio/functools.py b/.venv/lib/python3.12/site-packages/anyio/functools.py new file mode 100644 index 0000000..f1d6c7c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/functools.py @@ -0,0 +1,409 @@ +from __future__ import annotations + +__all__ = ( + "AsyncCacheInfo", + "AsyncCacheParameters", + "AsyncLRUCacheWrapper", + "cache", + "lru_cache", + "reduce", +) + +import functools +import sys +from collections import OrderedDict +from collections.abc import ( + AsyncIterable, + Awaitable, + Callable, + Coroutine, + Hashable, + Iterable, +) +from functools import update_wrapper +from inspect import iscoroutinefunction +from typing import ( + Any, + Generic, + NamedTuple, + TypedDict, + TypeVar, + cast, + final, + overload, +) +from weakref import WeakKeyDictionary + +from ._core._eventloop import current_time +from ._core._synchronization import Lock +from .lowlevel import RunVar, checkpoint + +if sys.version_info >= (3, 11): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +T = TypeVar("T") +S = TypeVar("S") +P = ParamSpec("P") +lru_cache_items: RunVar[ + WeakKeyDictionary[ + AsyncLRUCacheWrapper[Any, Any], + OrderedDict[ + Hashable, + tuple[_InitialMissingType, Lock, float | None] + | tuple[Any, None, float | None], + ], + ] +] = RunVar("lru_cache_items") + + +class _InitialMissingType: + pass + + +initial_missing: _InitialMissingType = _InitialMissingType() + + +class AsyncCacheInfo(NamedTuple): + hits: int + misses: int + maxsize: int | None + currsize: int + ttl: int | None + + +class AsyncCacheParameters(TypedDict): + maxsize: int | None + typed: bool + always_checkpoint: bool + ttl: int | None + + +class _LRUMethodWrapper(Generic[T]): + def __init__(self, wrapper: AsyncLRUCacheWrapper[..., T], instance: object): + self.__wrapper = wrapper + self.__instance = instance + + def cache_info(self) -> AsyncCacheInfo: + return self.__wrapper.cache_info() + + def cache_parameters(self) -> AsyncCacheParameters: + return self.__wrapper.cache_parameters() + + def cache_clear(self) -> None: + self.__wrapper.cache_clear() + + async def __call__(self, *args: Any, **kwargs: Any) -> T: + if self.__instance is None: + return await self.__wrapper(*args, **kwargs) + + return await self.__wrapper(self.__instance, *args, **kwargs) + + +@final +class AsyncLRUCacheWrapper(Generic[P, T]): + def __init__( + self, + func: Callable[P, Awaitable[T]], + maxsize: int | None, + typed: bool, + always_checkpoint: bool, + ttl: int | None, + ): + self.__wrapped__ = func + self._hits: int = 0 + self._misses: int = 0 + self._maxsize = max(maxsize, 0) if maxsize is not None else None + self._currsize: int = 0 + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + update_wrapper(self, func) + + def cache_info(self) -> AsyncCacheInfo: + return AsyncCacheInfo( + self._hits, self._misses, self._maxsize, self._currsize, self._ttl + ) + + def cache_parameters(self) -> AsyncCacheParameters: + return { + "maxsize": self._maxsize, + "typed": self._typed, + "always_checkpoint": self._always_checkpoint, + "ttl": self._ttl, + } + + def cache_clear(self) -> None: + if cache := lru_cache_items.get(None): + cache.pop(self, None) + self._hits = self._misses = self._currsize = 0 + + async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + # Easy case first: if maxsize == 0, no caching is done + if self._maxsize == 0: + value = await self.__wrapped__(*args, **kwargs) + self._misses += 1 + return value + + # The key is constructed as a flat tuple to avoid memory overhead + key: tuple[Any, ...] = args + if kwargs: + # initial_missing is used as a separator + key += (initial_missing,) + sum(kwargs.items(), ()) + + if self._typed: + key += tuple(type(arg) for arg in args) + if kwargs: + key += (initial_missing,) + tuple(type(val) for val in kwargs.values()) + + try: + cache = lru_cache_items.get() + except LookupError: + cache = WeakKeyDictionary() + lru_cache_items.set(cache) + + try: + cache_entry = cache[self] + except KeyError: + cache_entry = cache[self] = OrderedDict() + + cached_value: T | _InitialMissingType + try: + cached_value, lock, expires_at = cache_entry[key] + except KeyError: + # We're the first task to call this function + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + + if lock is None: + if expires_at is not None and current_time() >= expires_at: + self._currsize -= 1 + cached_value, lock, expires_at = ( + initial_missing, + Lock(fast_acquire=not self._always_checkpoint), + None, + ) + cache_entry[key] = cached_value, lock, expires_at + else: + # The value was already cached + self._hits += 1 + cache_entry.move_to_end(key) + if self._always_checkpoint: + await checkpoint() + + return cast(T, cached_value) + + async with lock: + # Check if another task filled the cache while we acquired the lock + if (cached_value := cache_entry[key][0]) is initial_missing: + self._misses += 1 + if self._maxsize is not None and self._currsize >= self._maxsize: + cache_entry.popitem(last=False) + else: + self._currsize += 1 + + value = await self.__wrapped__(*args, **kwargs) + expires_at = ( + current_time() + self._ttl if self._ttl is not None else None + ) + cache_entry[key] = value, None, expires_at + else: + # Another task filled the cache while we were waiting for the lock + self._hits += 1 + cache_entry.move_to_end(key) + value = cast(T, cached_value) + + return value + + def __get__( + self, instance: object, owner: type | None = None + ) -> _LRUMethodWrapper[T]: + wrapper = _LRUMethodWrapper(self, instance) + update_wrapper(wrapper, self.__wrapped__) + return wrapper + + +class _LRUCacheWrapper(Generic[T]): + def __init__( + self, maxsize: int | None, typed: bool, always_checkpoint: bool, ttl: int | None + ): + self._maxsize = maxsize + self._typed = typed + self._always_checkpoint = always_checkpoint + self._ttl = ttl + + @overload + def __call__( # type: ignore[overload-overlap] + self, func: Callable[P, Coroutine[Any, Any, T]], / + ) -> AsyncLRUCacheWrapper[P, T]: ... + + @overload + def __call__( + self, func: Callable[..., T], / + ) -> functools._lru_cache_wrapper[T]: ... + + def __call__( + self, f: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T], / + ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: + if iscoroutinefunction(f): + return AsyncLRUCacheWrapper( + f, self._maxsize, self._typed, self._always_checkpoint, self._ttl + ) + + return functools.lru_cache(maxsize=self._maxsize, typed=self._typed)(f) # type: ignore[arg-type] + + +@overload +def cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def cache( + func: Callable[..., T] | Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: + """ + A convenient shortcut for :func:`lru_cache` with ``maxsize=None``. + + This is the asynchronous equivalent to :func:`functools.cache`. + + """ + return lru_cache(maxsize=None)(func) + + +@overload +def lru_cache( + *, + maxsize: int | None = ..., + typed: bool = ..., + always_checkpoint: bool = ..., + ttl: int | None = ..., +) -> _LRUCacheWrapper[Any]: ... + + +@overload +def lru_cache( # type: ignore[overload-overlap] + func: Callable[P, Coroutine[Any, Any, T]], / +) -> AsyncLRUCacheWrapper[P, T]: ... + + +@overload +def lru_cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... + + +def lru_cache( + func: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T] | None = None, + /, + *, + maxsize: int | None = 128, + typed: bool = False, + always_checkpoint: bool = False, + ttl: int | None = None, +) -> ( + AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T] | _LRUCacheWrapper[Any] +): + """ + An asynchronous version of :func:`functools.lru_cache`. + + If a synchronous function is passed, the standard library + :func:`functools.lru_cache` is applied instead. + + :param always_checkpoint: if ``True``, every call to the cached function will be + guaranteed to yield control to the event loop at least once + :param ttl: time in seconds after which to invalidate cache entries + + .. note:: Caches and locks are managed on a per-event loop basis. + + """ + if func is None: + return _LRUCacheWrapper[Any](maxsize, typed, always_checkpoint, ttl) + + if not callable(func): + raise TypeError("the first argument must be callable") + + return _LRUCacheWrapper[T](maxsize, typed, always_checkpoint, ttl)(func) + + +@overload +async def reduce( + function: Callable[[T, S], Awaitable[T]], + iterable: Iterable[S] | AsyncIterable[S], + /, + initial: T, +) -> T: ... + + +@overload +async def reduce( + function: Callable[[T, T], Awaitable[T]], + iterable: Iterable[T] | AsyncIterable[T], + /, +) -> T: ... + + +async def reduce( # type: ignore[misc] + function: Callable[[T, T], Awaitable[T]] | Callable[[T, S], Awaitable[T]], + iterable: Iterable[T] | Iterable[S] | AsyncIterable[T] | AsyncIterable[S], + /, + initial: T | _InitialMissingType = initial_missing, +) -> T: + """ + Asynchronous version of :func:`functools.reduce`. + + :param function: a coroutine function that takes two arguments: the accumulated + value and the next element from the iterable + :param iterable: an iterable or async iterable + :param initial: the initial value (if missing, the first element of the iterable is + used as the initial value) + + """ + element: Any + function_called = False + if isinstance(iterable, AsyncIterable): + async_it = iterable.__aiter__() + if initial is initial_missing: + try: + value = cast(T, await async_it.__anext__()) + except StopAsyncIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + async for element in async_it: + value = await function(value, element) + function_called = True + elif isinstance(iterable, Iterable): + it = iter(iterable) + if initial is initial_missing: + try: + value = cast(T, next(it)) + except StopIteration: + raise TypeError( + "reduce() of empty sequence with no initial value" + ) from None + else: + value = cast(T, initial) + + for element in it: + value = await function(value, element) + function_called = True + else: + raise TypeError("reduce() argument 2 must be an iterable or async iterable") + + # Make sure there is at least one checkpoint, even if an empty iterable and an + # initial value were given + if not function_called: + await checkpoint() + + return value diff --git a/.venv/lib/python3.12/site-packages/anyio/lowlevel.py b/.venv/lib/python3.12/site-packages/anyio/lowlevel.py new file mode 100644 index 0000000..ffbb75a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/lowlevel.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +__all__ = ( + "EventLoopToken", + "RunvarToken", + "RunVar", + "checkpoint", + "checkpoint_if_cancelled", + "cancel_shielded_checkpoint", + "current_token", +) + +import enum +from dataclasses import dataclass +from types import TracebackType +from typing import Any, Generic, Literal, TypeVar, final, overload +from weakref import WeakKeyDictionary + +from ._core._eventloop import get_async_backend +from .abc import AsyncBackend + +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() + + +@final +@dataclass(frozen=True, repr=False) +class EventLoopToken: + """ + An opaque object that holds a reference to an event loop. + + .. versionadded:: 4.11.0 + """ + + backend_class: type[AsyncBackend] + native_token: object + + +def current_token() -> EventLoopToken: + """ + Return a token object that can be used to call code in the current event loop from + another thread. + + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + .. versionadded:: 4.11.0 + + """ + backend_class = get_async_backend() + raw_token = backend_class.current_token() + return EventLoopToken(backend_class, raw_token) + + +_run_vars: WeakKeyDictionary[object, dict[RunVar[Any], Any]] = WeakKeyDictionary() + + +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 + + def __enter__(self) -> RunvarToken[T]: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self._var.reset(self) + + +class RunVar(Generic[T]): + """ + Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. + + Can be used as a context manager, Just like :class:`~contextvars.ContextVar`, that + will reset the variable to its previous value when the context block is exited. + """ + + __slots__ = "_name", "_default" + + NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_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[RunVar[T], T]: + native_token = current_token().native_token + try: + return _run_vars[native_token] + except KeyError: + run_vars = _run_vars[native_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] + 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, RunVar.NO_VALUE_SET)) + current_vars[self] = 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] + except KeyError: + pass + else: + self._current_vars[self] = token._value + + token._redeemed = True + + def __repr__(self) -> str: + return f"" diff --git a/.venv/lib/python3.12/site-packages/anyio/py.typed b/.venv/lib/python3.12/site-packages/anyio/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py b/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py new file mode 100644 index 0000000..a5183f0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py @@ -0,0 +1,363 @@ +from __future__ import annotations + +import dataclasses +import socket +import sys +from collections.abc import Callable, Generator, Iterator +from contextlib import ExitStack, contextmanager +from inspect import isasyncgenfunction, iscoroutinefunction, ismethod +from typing import Any, cast + +import pytest +from _pytest.fixtures import FuncFixtureInfo, SubRequest +from _pytest.outcomes import Exit +from _pytest.python import CallSpec2 +from _pytest.scope import Scope + +from . import get_available_backends +from ._core._eventloop import ( + current_async_library, + get_async_backend, + reset_current_async_library, + set_current_async_library, +) +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 current_async_library() is None: + # Since we're in control of the event loop, we can cache the name of the + # async library + token = set_current_async_library(backend_name) + _runner_stack.callback(reset_current_async_library, 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_addoption(parser: pytest.Parser) -> None: + parser.addini( + "anyio_mode", + default="strict", + help='AnyIO plugin mode (either "strict" or "auto")', + ) + + +def pytest_configure(config: pytest.Config) -> None: + config.addinivalue_line( + "markers", + "anyio: mark the (coroutine function) test to be run asynchronously via anyio.", + ) + if ( + config.getini("anyio_mode") == "auto" + and config.pluginmanager.has_plugin("asyncio") + and config.getini("asyncio_mode") == "auto" + ): + config.issue_config_time_warning( + pytest.PytestConfigWarning( + "AnyIO auto mode has been enabled together with pytest-asyncio auto " + "mode. This may cause unexpected behavior." + ), + 1, + ) + + +@pytest.hookimpl(hookwrapper=True) +def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: + def wrapper(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: pytest.Module | pytest.Class, name: str, obj: object +) -> None: + if collector.istestfunction(obj, name): + inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj + if iscoroutinefunction(inner_func): + anyio_auto_mode = collector.config.getini("anyio_mode") == "auto" + marker = collector.get_closest_marker("anyio") + own_markers = getattr(obj, "pytestmark", ()) + if ( + anyio_auto_mode + or marker + or any(marker.name == "anyio" for marker in own_markers) + ): + pytest.mark.usefixtures("anyio_backend")(obj) + + +def pytest_collection_finish(session: pytest.Session) -> None: + for i, item in reversed(list(enumerate(session.items))): + if ( + isinstance(item, pytest.Function) + and iscoroutinefunction(item.function) + and item.get_closest_marker("anyio") is not None + and "anyio_backend" not in item.fixturenames + ): + new_items = [] + try: + cs_fields = {f.name for f in dataclasses.fields(CallSpec2)} + except TypeError: + cs_fields = set() + + for param_index, backend in enumerate(get_available_backends()): + if "_arg2scope" in cs_fields: # pytest >= 8 + callspec = CallSpec2( + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + _arg2scope={"anyio_backend": Scope.Module}, + _idlist=[backend], + marks=[], + ) + else: # pytest 7.x + callspec = CallSpec2( # type: ignore[call-arg] + funcargs={}, + params={"anyio_backend": backend}, + indices={"anyio_backend": param_index}, + arg2scope={"anyio_backend": Scope.Module}, + idlist=[backend], + marks=[], + ) + + fi = item._fixtureinfo + new_names_closure = list(fi.names_closure) + if "anyio_backend" not in new_names_closure: + new_names_closure.append("anyio_backend") + + new_fixtureinfo = FuncFixtureInfo( + argnames=fi.argnames, + initialnames=fi.initialnames, + names_closure=new_names_closure, + name2fixturedefs=fi.name2fixturedefs, + ) + new_item = pytest.Function.from_parent( + item.parent, + name=f"{item.originalname}[{backend}]", + callspec=callspec, + callobj=item.obj, + fixtureinfo=new_fixtureinfo, + keywords=item.keywords, + originalname=item.originalname, + ) + new_items.append(new_item) + + session.items[i : i + 1] = new_items + + +@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_available_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] + + +class FreePortFactory: + """ + Manages port generation based on specified socket kind, ensuring no duplicate + ports are generated. + + This class provides functionality for generating available free ports on the + system. It is initialized with a specific socket kind and can generate ports + for given address families while avoiding reuse of previously generated ports. + + Users should not instantiate this class directly, but use the + ``free_tcp_port_factory`` and ``free_udp_port_factory`` fixtures instead. For simple + uses cases, ``free_tcp_port`` and ``free_udp_port`` can be used instead. + """ + + def __init__(self, kind: socket.SocketKind) -> None: + self._kind = kind + self._generated = set[int]() + + @property + def kind(self) -> socket.SocketKind: + """ + The type of socket connection (e.g., :data:`~socket.SOCK_STREAM` or + :data:`~socket.SOCK_DGRAM`) used to bind for checking port availability + + """ + return self._kind + + def __call__(self, family: socket.AddressFamily | None = None) -> int: + """ + Return an unbound port for the given address family. + + :param family: if omitted, both IPv4 and IPv6 addresses will be tried + :return: a port number + + """ + if family is not None: + families = [family] + else: + families = [socket.AF_INET] + if socket.has_ipv6: + families.append(socket.AF_INET6) + + while True: + port = 0 + with ExitStack() as stack: + for family in families: + sock = stack.enter_context(socket.socket(family, self._kind)) + addr = "::1" if family == socket.AF_INET6 else "127.0.0.1" + try: + sock.bind((addr, port)) + except OSError: + break + + if not port: + port = sock.getsockname()[1] + else: + if port not in self._generated: + self._generated.add(port) + return port + + +@pytest.fixture(scope="session") +def free_tcp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_STREAM) + + +@pytest.fixture(scope="session") +def free_udp_port_factory() -> FreePortFactory: + return FreePortFactory(socket.SOCK_DGRAM) + + +@pytest.fixture +def free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int: + return free_tcp_port_factory() + + +@pytest.fixture +def free_udp_port(free_udp_port_factory: Callable[[], int]) -> int: + return free_udp_port_factory() diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/__init__.py b/.venv/lib/python3.12/site-packages/anyio/streams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py b/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py new file mode 100644 index 0000000..57c7cd7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +__all__ = ( + "BufferedByteReceiveStream", + "BufferedByteStream", + "BufferedConnectable", +) + +import sys +from collections.abc import Callable, Iterable, Mapping +from dataclasses import dataclass, field +from typing import Any, SupportsIndex + +from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead +from ..abc import ( + AnyByteReceiveStream, + AnyByteStream, + AnyByteStreamConnectable, + ByteReceiveStream, + ByteStream, + ByteStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@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 + + def feed_data(self, data: Iterable[SupportsIndex], /) -> None: + """ + Append data directly into the buffer. + + Any data in the buffer will be consumed by receive operations before receiving + anything from the wrapped stream. + + :param data: the data to append to the buffer (can be bytes or anything else + that supports ``__index__()``) + + """ + self._buffer.extend(data) + + 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) + + +class BufferedByteStream(BufferedByteReceiveStream, ByteStream): + """ + A full-duplex variant of :class:`BufferedByteReceiveStream`. All writes are passed + through to the wrapped stream as-is. + """ + + def __init__(self, stream: AnyByteStream): + """ + :param stream: the stream to be wrapped + + """ + super().__init__(stream) + self._stream = stream + + @override + async def send_eof(self) -> None: + await self._stream.send_eof() + + @override + async def send(self, item: bytes) -> None: + await self._stream.send(item) + + +class BufferedConnectable(ByteStreamConnectable): + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the connectable to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> BufferedByteStream: + stream = await self.connectable.connect() + return BufferedByteStream(stream) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/file.py b/.venv/lib/python3.12/site-packages/anyio/streams/file.py new file mode 100644 index 0000000..79c3d50 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/file.py @@ -0,0 +1,154 @@ +from __future__ import annotations + +__all__ = ( + "FileReadStream", + "FileStreamAttribute", + "FileWriteStream", +) + +from collections.abc import Callable, Mapping +from io import SEEK_SET, UnsupportedOperation +from os import PathLike +from pathlib import Path +from typing import IO, Any + +from .. import ( + BrokenResourceError, + ClosedResourceError, + EndOfStream, + TypedAttributeSet, + to_thread, + typed_attribute, +) +from ..abc import ByteReceiveStream, ByteSendStream + + +class FileStreamAttribute(TypedAttributeSet): + #: the open file descriptor + file: IO[bytes] = 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: IO[bytes]): + 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(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(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/python3.12/site-packages/anyio/streams/memory.py b/.venv/lib/python3.12/site-packages/anyio/streams/memory.py new file mode 100644 index 0000000..a3fa0c3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/memory.py @@ -0,0 +1,325 @@ +from __future__ import annotations + +__all__ = ( + "MemoryObjectReceiveStream", + "MemoryObjectSendStream", + "MemoryObjectStreamStatistics", +) + +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 from None + + 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, + stacklevel=1, + 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, + stacklevel=1, + source=self, + ) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py b/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py new file mode 100644 index 0000000..9248b68 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +__all__ = ( + "MultiListener", + "StapledByteStream", + "StapledObjectStream", +) + +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/python3.12/site-packages/anyio/streams/text.py b/.venv/lib/python3.12/site-packages/anyio/streams/text.py new file mode 100644 index 0000000..296cd25 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/text.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +__all__ = ( + "TextConnectable", + "TextReceiveStream", + "TextSendStream", + "TextStream", +) + +import codecs +import sys +from collections.abc import Callable, Mapping +from dataclasses import InitVar, dataclass, field +from typing import Any + +from ..abc import ( + AnyByteReceiveStream, + AnyByteSendStream, + AnyByteStream, + AnyByteStreamConnectable, + ObjectReceiveStream, + ObjectSendStream, + ObjectStream, + ObjectStreamConnectable, +) + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + + +@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, + } + + +class TextConnectable(ObjectStreamConnectable[str]): + def __init__(self, connectable: AnyByteStreamConnectable): + """ + :param connectable: the bytestream endpoint to wrap + + """ + self.connectable = connectable + + @override + async def connect(self) -> TextStream: + stream = await self.connectable.connect() + return TextStream(stream) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/tls.py b/.venv/lib/python3.12/site-packages/anyio/streams/tls.py new file mode 100644 index 0000000..e2a7ca5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/streams/tls.py @@ -0,0 +1,421 @@ +from __future__ import annotations + +__all__ = ( + "TLSAttribute", + "TLSConnectable", + "TLSListener", + "TLSStream", +) + +import logging +import re +import ssl +import sys +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from functools import wraps +from ssl import SSLContext +from typing import Any, TypeAlias, TypeVar + +from .. import ( + BrokenResourceError, + EndOfStream, + aclose_forcefully, + get_cancelled_exc_class, + to_thread, +) +from .._core._typedattr import TypedAttributeSet, typed_attribute +from ..abc import ( + AnyByteStream, + AnyByteStreamConnectable, + ByteStream, + ByteStreamConnectable, + Listener, + TaskGroup, +) + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") +_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] +_PCTRTTT: TypeAlias = 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() + + # External SSLContext implementations may do blocking I/O in wrap_bio(), + # but the standard library implementation won't + if type(ssl_context) is ssl.SSLContext: + ssl_object = ssl_context.wrap_bio( + bio_in, bio_out, server_side=server_side, server_hostname=hostname + ) + else: + ssl_object = await to_thread.run_sync( + ssl_context.wrap_bio, + bio_in, + bio_out, + server_side, + hostname, + None, + ) + + 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, + } + + +class TLSConnectable(ByteStreamConnectable): + """ + Wraps another connectable and does TLS negotiation after a successful connection. + + :param connectable: the connectable to wrap + :param hostname: host name of the server (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 server does the same + """ + + def __init__( + self, + connectable: AnyByteStreamConnectable, + *, + hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + standard_compatible: bool = True, + ) -> None: + self.connectable = connectable + self.ssl_context: SSLContext = ssl_context or ssl.create_default_context( + ssl.Purpose.SERVER_AUTH + ) + if not isinstance(self.ssl_context, ssl.SSLContext): + raise TypeError( + "ssl_context must be an instance of ssl.SSLContext, not " + f"{type(self.ssl_context).__name__}" + ) + self.hostname = hostname + self.standard_compatible = standard_compatible + + @override + async def connect(self) -> TLSStream: + stream = await self.connectable.connect() + try: + return await TLSStream.wrap( + stream, + hostname=self.hostname, + ssl_context=self.ssl_context, + standard_compatible=self.standard_compatible, + ) + except BaseException: + await aclose_forcefully(stream) + raise diff --git a/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py b/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py new file mode 100644 index 0000000..694dbe7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +__all__ = ( + "run_sync", + "current_default_interpreter_limiter", +) + +import atexit +import os +import sys +from collections import deque +from collections.abc import Callable +from typing import Any, Final, TypeVar + +from . import current_time, to_thread +from ._core._exceptions import BrokenWorkerInterpreter +from ._core._synchronization import CapacityLimiter +from .lowlevel import RunVar + +if sys.version_info >= (3, 11): + from typing import TypeVarTuple, Unpack +else: + from typing_extensions import TypeVarTuple, Unpack + +if sys.version_info >= (3, 14): + from concurrent.interpreters import ExecutionFailed, create + + def _interp_call( + func: Callable[..., Any], args: tuple[Any, ...] + ) -> tuple[Any, bool]: + try: + retval = func(*args) + except BaseException as exc: + return exc, True + else: + return retval, False + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter = create() + + def destroy(self) -> None: + self._interpreter.close() + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + try: + res, is_exception = self._interpreter.call(_interp_call, func, args) + except ExecutionFailed as exc: + raise BrokenWorkerInterpreter(exc.excinfo) from exc + + if is_exception: + raise res + + return res +elif sys.version_info >= (3, 13): + import _interpqueues + import _interpreters + + UNBOUND: Final = 2 # I have no clue how this works, but it was used in the stdlib + FMT_UNPICKLED: Final = 0 + FMT_PICKLED: Final = 1 + QUEUE_PICKLE_ARGS: Final = (FMT_PICKLED, UNBOUND) + QUEUE_UNPICKLE_ARGS: Final = (FMT_UNPICKLED, UNBOUND) + + _run_func = compile( + """ +import _interpqueues +from _interpreters import NotShareableError +from pickle import loads, dumps, HIGHEST_PROTOCOL + +QUEUE_PICKLE_ARGS = (1, 2) +QUEUE_UNPICKLE_ARGS = (0, 2) + +item = _interpqueues.get(queue_id)[0] +try: + func, args = loads(item) + retval = func(*args) +except BaseException as exc: + is_exception = True + retval = exc +else: + is_exception = False + +try: + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_UNPICKLE_ARGS) +except NotShareableError: + retval = dumps(retval, HIGHEST_PROTOCOL) + _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_PICKLE_ARGS) + """, + "", + "exec", + ) + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + self._interpreter_id = _interpreters.create() + self._queue_id = _interpqueues.create(1, *QUEUE_UNPICKLE_ARGS) + _interpreters.set___main___attrs( + self._interpreter_id, {"queue_id": self._queue_id} + ) + + def destroy(self) -> None: + _interpqueues.destroy(self._queue_id) + _interpreters.destroy(self._interpreter_id) + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + import pickle + + item = pickle.dumps((func, args), pickle.HIGHEST_PROTOCOL) + _interpqueues.put(self._queue_id, item, *QUEUE_PICKLE_ARGS) + exc_info = _interpreters.exec(self._interpreter_id, _run_func) + if exc_info: + raise BrokenWorkerInterpreter(exc_info) + + res = _interpqueues.get(self._queue_id) + (res, is_exception), fmt = res[:2] + if fmt == FMT_PICKLED: + res = pickle.loads(res) + + if is_exception: + raise res + + return res +else: + + class _Worker: + last_used: float = 0 + + def __init__(self) -> None: + raise RuntimeError("subinterpreters require at least Python 3.13") + + def call( + self, + func: Callable[..., T_Retval], + args: tuple[Any, ...], + ) -> T_Retval: + raise NotImplementedError + + def destroy(self) -> None: + pass + + +DEFAULT_CPU_COUNT: Final = 8 # this is just an arbitrarily selected value +MAX_WORKER_IDLE_TIME = ( + 30 # seconds a subinterpreter can be idle before becoming eligible for pruning +) + +T_Retval = TypeVar("T_Retval") +PosArgsT = TypeVarTuple("PosArgsT") + +_idle_workers = RunVar[deque[_Worker]]("_available_workers") +_default_interpreter_limiter = RunVar[CapacityLimiter]("_default_interpreter_limiter") + + +def _stop_workers(workers: deque[_Worker]) -> None: + for worker in workers: + worker.destroy() + + workers.clear() + + +async def run_sync( + func: Callable[[Unpack[PosArgsT]], T_Retval], + *args: Unpack[PosArgsT], + limiter: CapacityLimiter | None = None, +) -> T_Retval: + """ + Call the given function with the given arguments in a subinterpreter. + + .. warning:: On Python 3.13, the :mod:`concurrent.interpreters` module was not yet + available, so the code path for that Python version relies on an undocumented, + private API. As such, it is recommended to not rely on this function for anything + mission-critical on Python 3.13. + + :param func: a callable + :param args: the positional arguments for the callable + :param limiter: capacity limiter to use to limit the total number of subinterpreters + running (if omitted, the default limiter is used) + :return: the result of the call + :raises BrokenWorkerInterpreter: if there's an internal error in a subinterpreter + + """ + if limiter is None: + limiter = current_default_interpreter_limiter() + + try: + idle_workers = _idle_workers.get() + except LookupError: + idle_workers = deque() + _idle_workers.set(idle_workers) + atexit.register(_stop_workers, idle_workers) + + async with limiter: + try: + worker = idle_workers.pop() + except IndexError: + worker = _Worker() + + try: + return await to_thread.run_sync( + worker.call, + func, + args, + limiter=limiter, + ) + finally: + # Prune workers that have been idle for too long + now = current_time() + while idle_workers: + if now - idle_workers[0].last_used <= MAX_WORKER_IDLE_TIME: + break + + await to_thread.run_sync(idle_workers.popleft().destroy, limiter=limiter) + + worker.last_used = current_time() + idle_workers.append(worker) + + +def current_default_interpreter_limiter() -> CapacityLimiter: + """ + Return the capacity limiter used by default to limit the number of concurrently + running subinterpreters. + + Defaults to the number of CPU cores. + + :return: a capacity limiter object + + """ + try: + return _default_interpreter_limiter.get() + except LookupError: + limiter = CapacityLimiter(os.cpu_count() or DEFAULT_CPU_COUNT) + _default_interpreter_limiter.set(limiter) + return limiter diff --git a/.venv/lib/python3.12/site-packages/anyio/to_process.py b/.venv/lib/python3.12/site-packages/anyio/to_process.py new file mode 100644 index 0000000..b289234 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/to_process.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +__all__ = ( + "current_default_process_limiter", + "process_worker", + "run_sync", +) + +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( # type: ignore[return] + 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) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :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/python3.12/site-packages/anyio/to_thread.py b/.venv/lib/python3.12/site-packages/anyio/to_thread.py new file mode 100644 index 0000000..83c79d1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/anyio/to_thread.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +__all__ = ( + "run_sync", + "current_default_thread_limiter", +) + +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 ``abandon_on_cancel`` 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) + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + :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 + :raises NoEventLoopError: if no supported asynchronous event loop is running in the + current thread + + """ + return get_async_backend().current_default_thread_limiter() diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/METADATA b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/METADATA new file mode 100644 index 0000000..7b7bb30 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/METADATA @@ -0,0 +1,78 @@ +Metadata-Version: 2.4 +Name: certifi +Version: 2026.5.20 +Summary: Python package for providing Mozilla's CA Bundle. +Home-page: https://github.com/certifi/python-certifi +Author: Kenneth Reitz +Author-email: me@kennethreitz.com +License: MPL-2.0 +Project-URL: Source, https://github.com/certifi/python-certifi +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.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 :: 3.14 +Requires-Python: >=3.7 +License-File: LICENSE +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: requires-python +Dynamic: summary + +Certifi: Python SSL Certificates +================================ + +Certifi provides Mozilla's carefully curated collection of Root Certificates for +validating the trustworthiness of SSL certificates while verifying the identity +of TLS hosts. It has been extracted from the `Requests`_ project. + +Installation +------------ + +``certifi`` is available on PyPI. Simply install it with ``pip``:: + + $ pip install certifi + +Usage +----- + +To reference the installed certificate authority (CA) bundle, you can use the +built-in function:: + + >>> import certifi + + >>> certifi.where() + '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' + +Or from the command line:: + + $ python -m certifi + /usr/local/lib/python3.7/site-packages/certifi/cacert.pem + +Enjoy! + +.. _`Requests`: https://requests.readthedocs.io/en/master/ + +Addition/Removal of Certificates +-------------------------------- + +Certifi does not support any addition/removal or other modification of the +CA trust store content. This project is intended to provide a reliable and +highly portable root of trust to python deployments. Look to upstream projects +for methods to use alternate trust. diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/RECORD b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/RECORD new file mode 100644 index 0000000..c69462d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/RECORD @@ -0,0 +1,14 @@ +certifi-2026.5.20.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +certifi-2026.5.20.dist-info/METADATA,sha256=HDiCa8lfd0TGlQFongDjoaLJOhWJiJN84y707Vwd8PY,2474 +certifi-2026.5.20.dist-info/RECORD,, +certifi-2026.5.20.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91 +certifi-2026.5.20.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 +certifi-2026.5.20.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 +certifi/__init__.py,sha256=-8ljc7w7fyi5Kyi06c1_L0lG_kD_J0FvbUTa8mEpdzQ,94 +certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 +certifi/__pycache__/__init__.cpython-312.pyc,, +certifi/__pycache__/__main__.cpython-312.pyc,, +certifi/__pycache__/core.cpython-312.pyc,, +certifi/cacert.pem,sha256=fO9UDGrzvwo8fQBwvT_XquNPiqgFtAjRwnoByK6zBd8,236095 +certifi/core.py,sha256=XFXycndG5pf37ayeF8N32HUuDafsyhkVMbO4BAPWHa0,3394 +certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/WHEEL new file mode 100644 index 0000000..14a883f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/licenses/LICENSE new file mode 100644 index 0000000..62b076c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +This package contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/top_level.txt new file mode 100644 index 0000000..963eac5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2026.5.20.dist-info/top_level.txt @@ -0,0 +1 @@ +certifi diff --git a/.venv/lib/python3.12/site-packages/certifi/__init__.py b/.venv/lib/python3.12/site-packages/certifi/__init__.py new file mode 100644 index 0000000..004dd55 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi/__init__.py @@ -0,0 +1,4 @@ +from .core import contents, where + +__all__ = ["contents", "where"] +__version__ = "2026.05.20" diff --git a/.venv/lib/python3.12/site-packages/certifi/__main__.py b/.venv/lib/python3.12/site-packages/certifi/__main__.py new file mode 100644 index 0000000..8945b5d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from certifi import contents, where + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--contents", action="store_true") +args = parser.parse_args() + +if args.contents: + print(contents()) +else: + print(where()) diff --git a/.venv/lib/python3.12/site-packages/certifi/core.py b/.venv/lib/python3.12/site-packages/certifi/core.py new file mode 100644 index 0000000..1c9661c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi/core.py @@ -0,0 +1,83 @@ +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem or its contents. +""" +import sys +import atexit + +def exit_cacert_ctx() -> None: + _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] + + +if sys.version_info >= (3, 11): + + from importlib.resources import as_file, files + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") + +else: + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the + # file in cases where we're inside of a zipimport situation until + # someone actually calls where(), but we don't want to re-extract + # the file on every call of where(), so we'll do it once then store + # it in a global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you + # to manage the cleanup of this file, so it doesn't actually + # return a path, it returns a context manager that will give + # you the path when you enter it and will do any cleanup when + # you leave it. In the common case of not needing a temporary + # file, it will just return the file system location and the + # __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + atexit.register(exit_cacert_ctx) + + return _CACERT_PATH + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/.venv/lib/python3.12/site-packages/certifi/py.typed b/.venv/lib/python3.12/site-packages/certifi/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA new file mode 100644 index 0000000..67508e5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA @@ -0,0 +1,68 @@ +Metadata-Version: 2.4 +Name: cffi +Version: 2.0.0 +Summary: Foreign Function Interface for Python calling C code. +Author: Armin Rigo, Maciej Fijalkowski +Maintainer: Matt Davis, Matt Clay, Matti Picus +License-Expression: MIT +Project-URL: Documentation, https://cffi.readthedocs.io/ +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 +Project-URL: Source Code, https://github.com/python-cffi/cffi +Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues +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 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta +Classifier: Programming Language :: Python :: Implementation :: CPython +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: pycparser; implementation_name != "PyPy" +Dynamic: license-file + +[![GitHub Actions Status](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++) +[![PyPI version](https://img.shields.io/pypi/v/cffi.svg)](https://pypi.org/project/cffi) +[![Read the Docs](https://img.shields.io/badge/docs-latest-blue.svg)][Documentation] + + +CFFI +==== + +Foreign Function Interface for Python calling C code. + +Please see the [Documentation] or uncompiled in the `doc/` subdirectory. + +Download +-------- + +[Download page](https://github.com/python-cffi/cffi/releases) + +Source Code +----------- + +Source code is publicly available on +[GitHub](https://github.com/python-cffi/cffi). + +Contact +------- + +[Mailing list](https://groups.google.com/forum/#!forum/python-cffi) + +Testing/development tips +------------------------ + +After `git clone` or `wget && tar`, we will get a directory called `cffi` or `cffi-x.x.x`. we call it `repo-directory`. To run tests under CPython, run the following in the `repo-directory`: + + pip install pytest + pip install -e . # editable install of CFFI for local development + pytest src/c/ testing/ + +[Documentation]: http://cffi.readthedocs.org/ diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD new file mode 100644 index 0000000..6f82298 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD @@ -0,0 +1,49 @@ +_cffi_backend.cpython-312-x86_64-linux-gnu.so,sha256=AGLtw5fn9u4Cmwk3BbGlsXG7VZEvQekABMyEGuRZmcE,348808 +cffi-2.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cffi-2.0.0.dist-info/METADATA,sha256=uYzn40F68Im8EtXHNBLZs7FoPM-OxzyYbDWsjJvhujk,2559 +cffi-2.0.0.dist-info/RECORD,, +cffi-2.0.0.dist-info/WHEEL,sha256=aSgG0F4rGPZtV0iTEIfy6dtHq6g67Lze3uLfk0vWn88,151 +cffi-2.0.0.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 +cffi-2.0.0.dist-info/licenses/AUTHORS,sha256=KmemC7-zN1nWfWRf8TG45ta8TK_CMtdR_Kw-2k0xTMg,208 +cffi-2.0.0.dist-info/licenses/LICENSE,sha256=W6JN3FcGf5JJrdZEw6_EGl1tw34jQz73Wdld83Cwr2M,1123 +cffi-2.0.0.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 +cffi/__init__.py,sha256=-ksBQ7MfDzVvbBlV_ftYBWAmEqfA86ljIzMxzaZeAlI,511 +cffi/__pycache__/__init__.cpython-312.pyc,, +cffi/__pycache__/_imp_emulation.cpython-312.pyc,, +cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc,, +cffi/__pycache__/api.cpython-312.pyc,, +cffi/__pycache__/backend_ctypes.cpython-312.pyc,, +cffi/__pycache__/cffi_opcode.cpython-312.pyc,, +cffi/__pycache__/commontypes.cpython-312.pyc,, +cffi/__pycache__/cparser.cpython-312.pyc,, +cffi/__pycache__/error.cpython-312.pyc,, +cffi/__pycache__/ffiplatform.cpython-312.pyc,, +cffi/__pycache__/lock.cpython-312.pyc,, +cffi/__pycache__/model.cpython-312.pyc,, +cffi/__pycache__/pkgconfig.cpython-312.pyc,, +cffi/__pycache__/recompiler.cpython-312.pyc,, +cffi/__pycache__/setuptools_ext.cpython-312.pyc,, +cffi/__pycache__/vengine_cpy.cpython-312.pyc,, +cffi/__pycache__/vengine_gen.cpython-312.pyc,, +cffi/__pycache__/verifier.cpython-312.pyc,, +cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908 +cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055 +cffi/_embedding.h,sha256=Ai33FHblE7XSpHOCp8kPcWwN5_9BV14OvN0JVa6ITpw,18786 +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=QUTfmlL-aO-MYR8bFGlvAUHc36OQr7XYLe0WLkGFjRo,44790 +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=78J6lMEEOygXNmjN9-fOFFO3j7eW-iFxSrxfvQb54bY,65509 +cffi/setuptools_ext.py,sha256=0rCwBJ1W7FHWtiMKfNXsSST88V8UXrui5oeXFlDNLG8,9411 +cffi/vengine_cpy.py,sha256=oyQKD23kpE0aChUKA8Jg0e723foPiYzLYEdb-J0MiNs,43881 +cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939 +cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182 diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL new file mode 100644 index 0000000..e21e9f2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt new file mode 100644 index 0000000..4b0274f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[distutils.setup_keywords] +cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS new file mode 100644 index 0000000..370a25d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS @@ -0,0 +1,8 @@ +This package has been mostly done by Armin Rigo with help from +Maciej Fijałkowski. The idea is heavily based (although not directly +copied) from LuaJIT ffi by Mike Pall. + + +Other contributors: + + Google Inc. diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..0a1dbfb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE @@ -0,0 +1,23 @@ + +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: + + MIT No Attribution + + 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. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + diff --git a/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt new file mode 100644 index 0000000..f645779 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_cffi_backend +cffi diff --git a/.venv/lib/python3.12/site-packages/cffi/__init__.py b/.venv/lib/python3.12/site-packages/cffi/__init__.py new file mode 100644 index 0000000..c99ec3d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/__init__.py @@ -0,0 +1,14 @@ +__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', + 'FFIError'] + +from .api import FFI +from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError + +__version__ = "2.0.0" +__version_info__ = (2, 0, 0) + +# 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/python3.12/site-packages/cffi/_cffi_errors.h b/.venv/lib/python3.12/site-packages/cffi/_cffi_errors.h new file mode 100644 index 0000000..158e059 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/_cffi_errors.h @@ -0,0 +1,149 @@ +#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/python3.12/site-packages/cffi/_cffi_include.h b/.venv/lib/python3.12/site-packages/cffi/_cffi_include.h new file mode 100644 index 0000000..908a1d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/_cffi_include.h @@ -0,0 +1,389 @@ +#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/python3.12/site-packages/cffi/_embedding.h b/.venv/lib/python3.12/site-packages/cffi/_embedding.h new file mode 100644 index 0000000..64c04f6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/_embedding.h @@ -0,0 +1,550 @@ + +/***** 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: 2.0.0" + "\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/python3.12/site-packages/cffi/_imp_emulation.py b/.venv/lib/python3.12/site-packages/cffi/_imp_emulation.py new file mode 100644 index 0000000..136abdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/_imp_emulation.py @@ -0,0 +1,83 @@ + +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/python3.12/site-packages/cffi/_shimmed_dist_utils.py b/.venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py new file mode 100644 index 0000000..c3d2312 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py @@ -0,0 +1,45 @@ +""" +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/python3.12/site-packages/cffi/api.py b/.venv/lib/python3.12/site-packages/cffi/api.py new file mode 100644 index 0000000..5a474f3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/api.py @@ -0,0 +1,967 @@ +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/python3.12/site-packages/cffi/backend_ctypes.py b/.venv/lib/python3.12/site-packages/cffi/backend_ctypes.py new file mode 100644 index 0000000..e7956a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/backend_ctypes.py @@ -0,0 +1,1121 @@ +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/python3.12/site-packages/cffi/cffi_opcode.py b/.venv/lib/python3.12/site-packages/cffi/cffi_opcode.py new file mode 100644 index 0000000..6421df6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/cffi_opcode.py @@ -0,0 +1,187 @@ +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/python3.12/site-packages/cffi/commontypes.py b/.venv/lib/python3.12/site-packages/cffi/commontypes.py new file mode 100644 index 0000000..d4dae35 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/commontypes.py @@ -0,0 +1,82 @@ +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/python3.12/site-packages/cffi/cparser.py b/.venv/lib/python3.12/site-packages/cffi/cparser.py new file mode 100644 index 0000000..dd590d8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/cparser.py @@ -0,0 +1,1015 @@ +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 in between, 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/python3.12/site-packages/cffi/error.py b/.venv/lib/python3.12/site-packages/cffi/error.py new file mode 100644 index 0000000..0a27247 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/error.py @@ -0,0 +1,31 @@ + +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/python3.12/site-packages/cffi/ffiplatform.py b/.venv/lib/python3.12/site-packages/cffi/ffiplatform.py new file mode 100644 index 0000000..adca28f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/ffiplatform.py @@ -0,0 +1,113 @@ +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/python3.12/site-packages/cffi/lock.py b/.venv/lib/python3.12/site-packages/cffi/lock.py new file mode 100644 index 0000000..db91b71 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/lock.py @@ -0,0 +1,30 @@ +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/python3.12/site-packages/cffi/model.py b/.venv/lib/python3.12/site-packages/cffi/model.py new file mode 100644 index 0000000..e5f4cae --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/model.py @@ -0,0 +1,618 @@ +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/python3.12/site-packages/cffi/parse_c_type.h b/.venv/lib/python3.12/site-packages/cffi/parse_c_type.h new file mode 100644 index 0000000..84e4ef8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/parse_c_type.h @@ -0,0 +1,181 @@ + +/* 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/python3.12/site-packages/cffi/pkgconfig.py b/.venv/lib/python3.12/site-packages/cffi/pkgconfig.py new file mode 100644 index 0000000..5c93f15 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# 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/python3.12/site-packages/cffi/recompiler.py b/.venv/lib/python3.12/site-packages/cffi/recompiler.py new file mode 100644 index 0000000..7734a34 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/recompiler.py @@ -0,0 +1,1598 @@ +import io, os, sys, sysconfig +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)) and + not sysconfig.get_config_var("Py_GIL_DISABLED")) # free-threaded doesn't yet support limited API + +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 = '(size_t)(((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/python3.12/site-packages/cffi/setuptools_ext.py b/.venv/lib/python3.12/site-packages/cffi/setuptools_ext.py new file mode 100644 index 0000000..5cdd246 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/setuptools_ext.py @@ -0,0 +1,229 @@ +import os +import sys +import sysconfig + +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._shimmed_dist_utils import log + 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 + + if sysconfig.get_config_var("Py_GIL_DISABLED"): + if kwds.get('py_limited_api'): + log.info("Ignoring py_limited_api=True for free-threaded build.") + + kwds['py_limited_api'] = False + + if kwds.get('py_limited_api') is False: + # avoid setting Py_LIMITED_API if py_limited_api=False + # which _cffi_include.h does unless _CFFI_NO_LIMITED_API is defined + kwds.setdefault("define_macros", []).append(("_CFFI_NO_LIMITED_API", None)) + 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/python3.12/site-packages/cffi/vengine_cpy.py b/.venv/lib/python3.12/site-packages/cffi/vengine_cpy.py new file mode 100644 index 0000000..02e6a47 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/vengine_cpy.py @@ -0,0 +1,1087 @@ +# +# 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('#if Py_GIL_DISABLED') + prnt(' PyUnstable_Module_SetGIL(lib, Py_MOD_GIL_NOT_USED);') + prnt('#endif') + 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/python3.12/site-packages/cffi/vengine_gen.py b/.venv/lib/python3.12/site-packages/cffi/vengine_gen.py new file mode 100644 index 0000000..bffc821 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/vengine_gen.py @@ -0,0 +1,679 @@ +# +# 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/python3.12/site-packages/cffi/verifier.py b/.venv/lib/python3.12/site-packages/cffi/verifier.py new file mode 100644 index 0000000..e392a2b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cffi/verifier.py @@ -0,0 +1,306 @@ +# +# 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/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/METADATA b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/METADATA new file mode 100644 index 0000000..6b5a360 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/METADATA @@ -0,0 +1,808 @@ +Metadata-Version: 2.4 +Name: charset-normalizer +Version: 3.4.7 +Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. +Author-email: "Ahmed R. TAHRI" +Maintainer-email: "Ahmed R. TAHRI" +License: MIT +Project-URL: Changelog, https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md +Project-URL: Documentation, https://charset-normalizer.readthedocs.io/ +Project-URL: Code, https://github.com/jawah/charset_normalizer +Project-URL: Issue tracker, https://github.com/jawah/charset_normalizer/issues +Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +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 :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient +Classifier: Topic :: Text Processing :: Linguistic +Classifier: Topic :: Utilities +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: unicode-backport +Dynamic: license-file + +

Charset Detection, for Everyone 👋

+ +

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

+

+ Featured Packages
+ + Static Badge + + + Static Badge + +

+

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

+ +> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, +> I'm trying to resolve the issue by taking a new approach. +> All IANA character set names for which the Python core library provides codecs are supported. +> You can also register your own set of codecs, and yes, it would work as-is. + +

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

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

+Reading Normalized TextCat Reading Text +

+ +[^1]: They are clearly using specific code for a specific encoding even if covering most of used one. +[^2]: Chardet 7.0+ was relicensed from LGPL-2.1 to MIT following an AI-assisted rewrite. This relicensing is disputed on two independent grounds: **(a)** the original author [contests](https://github.com/chardet/chardet/issues/327) that the maintainer had the right to relicense, arguing the rewrite is a derivative work of the LGPL-licensed codebase since it was not a clean room implementation; **(b)** the copyright claim itself is [questionable](https://github.com/chardet/chardet/issues/334) given the code was primarily generated by an LLM, and AI-generated output may not be copyrightable under most jurisdictions. Either issue alone could undermine the MIT license. Beyond licensing, the rewrite raises questions about responsible use of AI in open source: key architectural ideas pioneered by charset-normalizer - notably decode-first validity filtering (our foundational approach since v1) and encoding pairwise similarity with the same algorithm and threshold — surfaced in chardet 7 without acknowledgment. The project also imported test files from charset-normalizer to train and benchmark against it, then claimed superior accuracy on those very files. Charset-normalizer has always been MIT-licensed, encoding-agnostic by design, and built on a verifiable human-authored history. + +## ⚡ Performance + +This package offer better performances (99th, and 95th) against Chardet. Here are some numbers. + +| Package | Accuracy | Mean per file (ms) | File per sec (est) | +|---------------------------------------------------|:--------:|:------------------:|:------------------:| +| [chardet 7.1](https://github.com/chardet/chardet) | 89 % | 3 ms | 333 file/sec | +| charset-normalizer | **97 %** | 3 ms | 333 file/sec | + +| Package | 99th percentile | 95th percentile | 50th percentile | +|---------------------------------------------------|:---------------:|:---------------:|:---------------:| +| [chardet 7.1](https://github.com/chardet/chardet) | 32 ms | 17 ms | < 1 ms | +| charset-normalizer | 16 ms | 10 ms | 1 ms | + +_updated as of March 2026 using CPython 3.12, Charset-Normalizer 3.4.6, and Chardet 7.1.0_ + +~Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload.~ No longer the case since Chardet 7.0+ + +> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. +> And yes, these results might change at any time. The dataset can be updated to include more files. +> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. +> Chardet claims on his documentation to have a greater accuracy than us based on the dataset they trained Chardet on(...) +> Well, it's normal, the opposite would have been worrying. Whereas charset-normalizer don't train on anything, our solution +> is based on a completely different algorithm, still heuristic through, it does not need weights across every encoding tables. + +## ✨ Installation + +Using pip: + +```sh +pip install charset-normalizer -U +``` + +## 🚀 Basic Usage + +### CLI +This package comes with a CLI. + +``` +usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] + file [file ...] + +The Real First Universal Charset Detector. Discover originating encoding used +on text file. Normalize text to unicode. + +positional arguments: + files File(s) to be analysed + +optional arguments: + -h, --help show this help message and exit + -v, --verbose Display complementary information about file if any. + Stdout will contain logs about the detection process. + -a, --with-alternative + Output complementary possibilities if any. Top-level + JSON WILL be a list. + -n, --normalize Permit to normalize input file. If not set, program + does not write anything. + -m, --minimal Only output the charset detected to STDOUT. Disabling + JSON output. + -r, --replace Replace file when trying to normalize it instead of + creating a new one. + -f, --force Replace file without asking if you are sure, use this + flag with caution. + -t THRESHOLD, --threshold THRESHOLD + Define a custom maximum amount of chaos allowed in + decoded content. 0. <= chaos <= 1. + --version Show version information and exit. +``` + +```bash +normalizer ./data/sample.1.fr.srt +``` + +or + +```bash +python -m charset_normalizer ./data/sample.1.fr.srt +``` + +🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. + +```json +{ + "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", + "encoding": "cp1252", + "encoding_aliases": [ + "1252", + "windows_1252" + ], + "alternative_encodings": [ + "cp1254", + "cp1256", + "cp1258", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + "mbcs" + ], + "language": "French", + "alphabets": [ + "Basic Latin", + "Latin-1 Supplement" + ], + "has_sig_or_bom": false, + "chaos": 0.149, + "coherence": 97.152, + "unicode_path": null, + "is_preferred": true +} +``` + +### Python +*Just print out normalized text* +```python +from charset_normalizer import from_path + +results = from_path('./my_subtitle.srt') + +print(str(results.best())) +``` + +*Upgrade your code without effort* +```python +from charset_normalizer import detect +``` + +The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. + +See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) + +## 😇 Why + +When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a +reliable alternative using a completely different method. Also! I never back down on a good challenge! + +I **don't care** about the **originating charset** encoding, because **two different tables** can +produce **two identical rendered string.** +What I want is to get readable text, the best I can. + +In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 + +Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair Unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. + +## 🍰 How + + - Discard all charset encoding table that could not fit the binary content. + - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. + - Extract matches with the lowest mess detected. + - Additionally, we measure coherence / probe for a language. + +**Wait a minute**, what is noise/mess and coherence according to **YOU ?** + +*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then +**I established** some ground rules about **what is obvious** when **it seems like** a mess (aka. defining noise in rendered text). + I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to + improve or rewrite it. + +*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought +that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. + +## ⚡ Known limitations + + - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) + - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. + +## ⚠️ About Python EOLs + +**If you are running:** + +- Python >=2.7,<3.5: Unsupported +- Python 3.5: charset-normalizer < 2.1 +- Python 3.6: charset-normalizer < 3.1 + +Upgrade your Python interpreter as soon as possible. + +## 👤 Contributing + +Contributions, issues and feature requests are very much welcome.
+Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. + +## 📝 License + +Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
+This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. + +Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) + +## 💼 For Enterprise + +Professional support for charset-normalizer is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme + +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7297/badge)](https://www.bestpractices.dev/projects/7297) + +# Changelog +All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [3.4.7](https://github.com/Ousret/charset_normalizer/compare/3.4.6...3.4.7) (2026-04-02) + +### Changed +- Pre-built optimized version using mypy[c] v1.20. +- Relax `setuptools` constraint to `setuptools>=68,<82.1`. + +### Fixed +- Correctly remove SIG remnant in utf-7 decoded string. (#718) (#716) + +## [3.4.6](https://github.com/Ousret/charset_normalizer/compare/3.4.5...3.4.6) (2026-03-15) + +### Changed +- Flattened the logic in `charset_normalizer.md` for higher performance. Removed `eligible(..)` and `feed(...)` + in favor of `feed_info(...)`. +- Raised upper bound for mypy[c] to 1.20, for our optimized version. +- Updated `UNICODE_RANGES_COMBINED` using Unicode blocks v17. + +### Fixed +- Edge case where noise difference between two candidates can be almost insignificant. (#672) +- CLI `--normalize` writing to wrong path when passing multiple files in. (#702) + +### Misc +- Freethreaded pre-built wheels now shipped in PyPI starting with 3.14t. (#616) + +## [3.4.5](https://github.com/Ousret/charset_normalizer/compare/3.4.4...3.4.5) (2026-03-06) + +### Changed +- Update `setuptools` constraint to `setuptools>=68,<=82`. +- Raised upper bound of mypyc for the optional pre-built extension to v1.19.1 + +### Fixed +- Add explicit link to lib math in our optimized build. (#692) +- Logger level not restored correctly for empty byte sequences. (#701) +- TypeError when passing bytearray to from_bytes. (#703) + +### Misc +- Applied safe micro-optimizations in both our noise detector and language detector. +- Rewrote the `query_yes_no` function (inside CLI) to avoid using ambiguous licensed code. +- Added `cd.py` submodule into mypyc optional compilation to reduce further the performance impact. + +## [3.4.4](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.4) (2025-10-13) + +### Changed +- Bound `setuptools` to a specific constraint `setuptools>=68,<=81`. +- Raised upper bound of mypyc for the optional pre-built extension to v1.18.2 + +### Removed +- `setuptools-scm` as a build dependency. + +### Misc +- Enforced hashes in `dev-requirements.txt` and created `ci-requirements.txt` for security purposes. +- Additional pre-built wheels for riscv64, s390x, and armv7l architectures. +- Restore ` multiple.intoto.jsonl` in GitHub releases in addition to individual attestation file per wheel. + +## [3.4.3](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.3) (2025-08-09) + +### Changed +- mypy(c) is no longer a required dependency at build time if `CHARSET_NORMALIZER_USE_MYPYC` isn't set to `1`. (#595) (#583) +- automatically lower confidence on small bytes samples that are not Unicode in `detect` output legacy function. (#391) + +### Added +- Custom build backend to overcome inability to mark mypy as an optional dependency in the build phase. +- Support for Python 3.14 + +### Fixed +- sdist archive contained useless directories. +- automatically fallback on valid UTF-16 or UTF-32 even if the md says it's noisy. (#633) + +### Misc +- SBOM are automatically published to the relevant GitHub release to comply with regulatory changes. + Each published wheel comes with its SBOM. We choose CycloneDX as the format. +- Prebuilt optimized wheel are no longer distributed by default for CPython 3.7 due to a change in cibuildwheel. + +## [3.4.2](https://github.com/Ousret/charset_normalizer/compare/3.4.1...3.4.2) (2025-05-02) + +### Fixed +- Addressed the DeprecationWarning in our CLI regarding `argparse.FileType` by backporting the target class into the package. (#591) +- Improved the overall reliability of the detector with CJK Ideographs. (#605) (#587) + +### Changed +- Optional mypyc compilation upgraded to version 1.15 for Python >= 3.8 + +## [3.4.1](https://github.com/Ousret/charset_normalizer/compare/3.4.0...3.4.1) (2024-12-24) + +### Changed +- Project metadata are now stored using `pyproject.toml` instead of `setup.cfg` using setuptools as the build backend. +- Enforce annotation delayed loading for a simpler and consistent types in the project. +- Optional mypyc compilation upgraded to version 1.14 for Python >= 3.8 + +### Added +- pre-commit configuration. +- noxfile. + +### Removed +- `build-requirements.txt` as per using `pyproject.toml` native build configuration. +- `bin/integration.py` and `bin/serve.py` in favor of downstream integration test (see noxfile). +- `setup.cfg` in favor of `pyproject.toml` metadata configuration. +- Unused `utils.range_scan` function. + +### Fixed +- Converting content to Unicode bytes may insert `utf_8` instead of preferred `utf-8`. (#572) +- Deprecation warning "'count' is passed as positional argument" when converting to Unicode bytes on Python 3.13+ + +## [3.4.0](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) (2024-10-08) + +### Added +- Argument `--no-preemptive` in the CLI to prevent the detector to search for hints. +- Support for Python 3.13 (#512) + +### Fixed +- Relax the TypeError exception thrown when trying to compare a CharsetMatch with anything else than a CharsetMatch. +- Improved the general reliability of the detector based on user feedbacks. (#520) (#509) (#498) (#407) (#537) +- Declared charset in content (preemptive detection) not changed when converting to utf-8 bytes. (#381) + +## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) + +### Fixed +- Unintentional memory usage regression when using large payload that match several encoding (#376) +- Regression on some detection case showcased in the documentation (#371) + +### Added +- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) + +## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) + +### Changed +- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 +- Improved the general detection reliability based on reports from the community + +## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) + +### Added +- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` +- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) + +### Removed +- (internal) Redundant utils.is_ascii function and unused function is_private_use_only +- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant + +### Changed +- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection +- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 + +### Fixed +- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) + +## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) + +### Changed +- Typehint for function `from_path` no longer enforce `PathLike` as its first argument +- Minor improvement over the global detection reliability + +### Added +- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries +- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) +- Explicit support for Python 3.12 + +### Fixed +- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) + +## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) + +### Added +- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) + +### Removed +- Support for Python 3.6 (PR #260) + +### Changed +- Optional speedup provided by mypy/c 1.0.1 + +## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) + +### Fixed +- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) + +### Changed +- Speedup provided by mypy/c 0.990 on Python >= 3.7 + +## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it +- Sphinx warnings when generating the documentation + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' + +## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) + +### Added +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Removed +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) + +### Fixed +- Sphinx warnings when generating the documentation + +## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) + +### Changed +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Removed +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) + +### Deprecated +- Function `normalize` scheduled for removal in 3.0 + +### Changed +- Removed useless call to decode in fn is_unprintable (#206) + +### Fixed +- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) + +## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) + +### Added +- Output the Unicode table version when running the CLI with `--version` (PR #194) + +### Changed +- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) +- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) + +### Fixed +- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) +- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) + +### Removed +- Support for Python 3.5 (PR #192) + +### Deprecated +- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) + +## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) + +### Fixed +- ASCII miss-detection on rare cases (PR #170) + +## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) + +### Added +- Explicit support for Python 3.11 (PR #164) + +### Changed +- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) + +## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) + +### Fixed +- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) + +### Changed +- Skipping the language-detection (CD) on ASCII (PR #155) + +## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) + +### Changed +- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) + +### Fixed +- Wrong logging level applied when setting kwarg `explain` to True (PR #146) + +## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) +### Changed +- Improvement over Vietnamese detection (PR #126) +- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) +- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) +- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) +- Code style as refactored by Sourcery-AI (PR #131) +- Minor adjustment on the MD around european words (PR #133) +- Remove and replace SRTs from assets / tests (PR #139) +- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) + +### Fixed +- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) +- Avoid using too insignificant chunk (PR #137) + +### Added +- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) + +## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) +### Added +- Add support for Kazakh (Cyrillic) language detection (PR #109) + +### Changed +- Further, improve inferring the language from a given single-byte code page (PR #112) +- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) +- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) +- Various detection improvement (MD+CD) (PR #117) + +### Removed +- Remove redundant logging entry about detected language(s) (PR #115) + +### Fixed +- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) + +## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) +### Fixed +- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) +- Fix CLI crash when using --minimal output in certain cases (PR #103) + +### Changed +- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) + +## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) +### Changed +- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) +- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) +- The Unicode detection is slightly improved (PR #93) +- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) + +### Removed +- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) + +### Fixed +- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) +- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) +- The MANIFEST.in was not exhaustive (PR #78) + +## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) +### Fixed +- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) +- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) +- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) +- Submatch factoring could be wrong in rare edge cases (PR #72) +- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) +- Fix line endings from CRLF to LF for certain project files (PR #67) + +### Changed +- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) +- Allow fallback on specified encoding if any (PR #71) + +## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) +### Changed +- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) +- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) + +## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) +### Fixed +- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) + +### Changed +- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) + +## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) +### Fixed +- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) +- Using explain=False permanently disable the verbose output in the current runtime (PR #47) +- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) +- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) + +### Changed +- Public function normalize default args values were not aligned with from_bytes (PR #53) + +### Added +- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) + +## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) +### Changed +- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. +- Accent has been made on UTF-8 detection, should perform rather instantaneous. +- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. +- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) +- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ +- utf_7 detection has been reinstated. + +### Removed +- This package no longer require anything when used with Python 3.5 (Dropped cached_property) +- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. +- The exception hook on UnicodeDecodeError has been removed. + +### Deprecated +- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 + +### Fixed +- The CLI output used the relative path of the file(s). Should be absolute. + +## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) +### Fixed +- Logger configuration/usage no longer conflict with others (PR #44) + +## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) +### Removed +- Using standard logging instead of using the package loguru. +- Dropping nose test framework in favor of the maintained pytest. +- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. +- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. +- Stop support for UTF-7 that does not contain a SIG. +- Dropping PrettyTable, replaced with pure JSON output in CLI. + +### Fixed +- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. +- Not searching properly for the BOM when trying utf32/16 parent codec. + +### Changed +- Improving the package final size by compressing frequencies.json. +- Huge improvement over the larges payload. + +### Added +- CLI now produces JSON consumable output. +- Return ASCII if given sequences fit. Given reasonable confidence. + +## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) + +### Fixed +- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) + +## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) + +### Fixed +- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) + +## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) + +### Fixed +- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) + +## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) + +### Changed +- Amend the previous release to allow prettytable 2.0 (PR #35) + +## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) + +### Fixed +- Fix error while using the package with a python pre-release interpreter (PR #33) + +### Changed +- Dependencies refactoring, constraints revised. + +### Added +- Add python 3.9 and 3.10 to the supported interpreters + +MIT License + +Copyright (c) 2025 TAHRI Ahmed R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/RECORD b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/RECORD new file mode 100644 index 0000000..b2f7f04 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/RECORD @@ -0,0 +1,36 @@ +../../../bin/normalizer,sha256=sIQLF5kUgKnaENC8Hr1-ThrvkgdsHeHT4LEp9mkO85I,218 +81d243bd2c585b0f4821__mypyc.cpython-312-x86_64-linux-gnu.so,sha256=xPTGB-9iuOqJ5RfI3qaB1WzuFAm1oYWbgN1Jz9U1wn0,433312 +charset_normalizer-3.4.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +charset_normalizer-3.4.7.dist-info/METADATA,sha256=K8lK8L8LaZ1YmKvWLt3zEkpIxiCOC58xNhzFQrfQJxQ,40931 +charset_normalizer-3.4.7.dist-info/RECORD,, +charset_normalizer-3.4.7.dist-info/WHEEL,sha256=Tc3fF66yn9Kh-hkUUsdKQyuB9Lw0CDoeANnEbSVc3f4,190 +charset_normalizer-3.4.7.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 +charset_normalizer-3.4.7.dist-info/licenses/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071 +charset_normalizer-3.4.7.dist-info/top_level.txt,sha256=c_vZbitqecT2GfK3zdxSTLCn8C-6pGnHQY5o_5Y32M0,47 +charset_normalizer/__init__.py,sha256=OKRxRv2Zhnqk00tqkN0c1BtJjm165fWXLydE52IKuHc,1590 +charset_normalizer/__main__.py,sha256=yzYxMR-IhKRHYwcSlavEv8oGdwxsR89mr2X09qXGdps,109 +charset_normalizer/__pycache__/__init__.cpython-312.pyc,, +charset_normalizer/__pycache__/__main__.cpython-312.pyc,, +charset_normalizer/__pycache__/api.cpython-312.pyc,, +charset_normalizer/__pycache__/cd.cpython-312.pyc,, +charset_normalizer/__pycache__/constant.cpython-312.pyc,, +charset_normalizer/__pycache__/legacy.cpython-312.pyc,, +charset_normalizer/__pycache__/md.cpython-312.pyc,, +charset_normalizer/__pycache__/models.cpython-312.pyc,, +charset_normalizer/__pycache__/utils.cpython-312.pyc,, +charset_normalizer/__pycache__/version.cpython-312.pyc,, +charset_normalizer/api.py,sha256=387F3n23MlMu-xfSbFULW2DLGsBmVrZVGhnkiGXeKBo,38844 +charset_normalizer/cd.cpython-312-x86_64-linux-gnu.so,sha256=gOe65H__3O8_4a-aSVMB8gxHsRxVyQDUqqaIurPmIhE,15912 +charset_normalizer/cd.py,sha256=v0iPJweGsRegXywrM1LzUgqW9bJ1KFvIblQHP1jm5FQ,15174 +charset_normalizer/cli/__init__.py,sha256=D8I86lFk2-py45JvqxniTirSj_sFyE6sjaY_0-G1shc,136 +charset_normalizer/cli/__main__.py,sha256=E9FFSV1E2iOE_B2B1tJHQT9ExJqc60Ks_c-08sNawh8,11940 +charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc,, +charset_normalizer/cli/__pycache__/__main__.cpython-312.pyc,, +charset_normalizer/constant.py,sha256=yvLAWDrdSC743Cu4amhwHLIO-FGuRTOTZouCzZKGikc,44431 +charset_normalizer/legacy.py,sha256=yBIFMNABNPE5JkdKOWyVo36fZtV9nm8bf37LrDWulz8,2661 +charset_normalizer/md.cpython-312-x86_64-linux-gnu.so,sha256=iYaQbya7NVRR7xg5FtK1yAKS5shmTFwmtkqqQbbvEWs,15912 +charset_normalizer/md.py,sha256=AYCdfDX79FrgoId3zXqmbCuDcbGr1NRuGqgJN94Rx9Q,30441 +charset_normalizer/models.py,sha256=FbaQnI6ECmVmyHRSvVM5fHNeMAQ3KSGdwLjGcQqWDws,12821 +charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +charset_normalizer/utils.py,sha256=9cpi-_0-vC9pGDfuoarhC6VlF_Jxwx5Jsa_8I4w2D8k,12282 +charset_normalizer/version.py,sha256=2LxFuGp3BBuIwt95cp64y7v8bCNHcMAi08IfXt_47Co,115 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/WHEEL new file mode 100644 index 0000000..0493eaf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.1) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/entry_points.txt new file mode 100644 index 0000000..65619e7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +normalizer = charset_normalizer.cli:cli_detect diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE new file mode 100644 index 0000000..9725772 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 TAHRI Ahmed R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/top_level.txt new file mode 100644 index 0000000..89847be --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.7.dist-info/top_level.txt @@ -0,0 +1,2 @@ +81d243bd2c585b0f4821__mypyc +charset_normalizer diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py new file mode 100644 index 0000000..0d3a379 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py @@ -0,0 +1,48 @@ +""" +Charset-Normalizer +~~~~~~~~~~~~~~ +The Real First Universal Charset Detector. +A library that helps you read text from an unknown charset encoding. +Motivated by chardet, This package is trying to resolve the issue by taking a new approach. +All IANA character set names for which the Python core library provides codecs are supported. + +Basic usage: + >>> from charset_normalizer import from_bytes + >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) + >>> best_guess = results.best() + >>> str(best_guess) + 'Bсеки човек има право на образование. Oбразованието!' + +Others methods and usages are available - see the full documentation +at . +:copyright: (c) 2021 by Ahmed TAHRI +:license: MIT, see LICENSE for more details. +""" + +from __future__ import annotations + +import logging + +from .api import from_bytes, from_fp, from_path, is_binary +from .legacy import detect +from .models import CharsetMatch, CharsetMatches +from .utils import set_logging_handler +from .version import VERSION, __version__ + +__all__ = ( + "from_fp", + "from_path", + "from_bytes", + "is_binary", + "detect", + "CharsetMatch", + "CharsetMatches", + "__version__", + "VERSION", + "set_logging_handler", +) + +# Attach a NullHandler to the top level logger by default +# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library + +logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py new file mode 100644 index 0000000..e0e76f7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py @@ -0,0 +1,6 @@ +from __future__ import annotations + +from .cli import cli_detect + +if __name__ == "__main__": + cli_detect() diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/api.py b/.venv/lib/python3.12/site-packages/charset_normalizer/api.py new file mode 100644 index 0000000..50cb955 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/api.py @@ -0,0 +1,988 @@ +from __future__ import annotations + +import logging +from os import PathLike +from typing import BinaryIO + +from .cd import ( + coherence_ratio, + encoding_languages, + mb_encoding_languages, + merge_coherence_ratios, +) +from .constant import ( + IANA_SUPPORTED, + IANA_SUPPORTED_SIMILAR, + TOO_BIG_SEQUENCE, + TOO_SMALL_SEQUENCE, + TRACE, +) +from .md import mess_ratio +from .models import CharsetMatch, CharsetMatches +from .utils import ( + any_specified_encoding, + cut_sequence_chunks, + iana_name, + identify_sig_or_bom, + is_multi_byte_encoding, + should_strip_sig_or_bom, +) + +logger = logging.getLogger("charset_normalizer") +explain_handler = logging.StreamHandler() +explain_handler.setFormatter( + logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") +) + +# Pre-compute a reordered encoding list: multibyte first, then single-byte. +# This allows the mb_definitive_match optimization to fire earlier, skipping +# all single-byte encodings for genuine CJK content. Multibyte codecs +# hard-fail (UnicodeDecodeError) on single-byte data almost instantly, so +# testing them first costs negligible time for non-CJK files. +_mb_supported: list[str] = [] +_sb_supported: list[str] = [] + +for _supported_enc in IANA_SUPPORTED: + try: + if is_multi_byte_encoding(_supported_enc): + _mb_supported.append(_supported_enc) + else: + _sb_supported.append(_supported_enc) + except ImportError: + _sb_supported.append(_supported_enc) + +IANA_SUPPORTED_MB_FIRST: list[str] = _mb_supported + _sb_supported + + +def from_bytes( + sequences: bytes | bytearray, + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.2, + cp_isolation: list[str] | None = None, + cp_exclusion: list[str] | None = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Given a raw bytes sequence, return the best possibles charset usable to render str objects. + If there is no results, it is a strong indicator that the source is binary/not text. + By default, the process will extract 5 blocks of 512o each to assess the mess and coherence of a given sequence. + And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. + + The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page + but never take it for granted. Can improve the performance. + + You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that + purpose. + + This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. + By default the library does not setup any handler other than the NullHandler, if you choose to set the 'explain' + toggle to True it will alter the logger configuration to add a StreamHandler that is suitable for debugging. + Custom logging format and handler can be set manually. + """ + + if not isinstance(sequences, (bytearray, bytes)): + raise TypeError( + "Expected object of type bytes or bytearray, got: {}".format( + type(sequences) + ) + ) + + if explain: + previous_logger_level: int = logger.level + logger.addHandler(explain_handler) + logger.setLevel(TRACE) + + length: int = len(sequences) + + if length == 0: + logger.debug("Encoding detection on empty bytes, assuming utf_8 intention.") + if explain: # Defensive: ensure exit path clean handler + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) + + if cp_isolation is not None: + logger.log( + TRACE, + "cp_isolation is set. use this flag for debugging purpose. " + "limited list of encoding allowed : %s.", + ", ".join(cp_isolation), + ) + cp_isolation = [iana_name(cp, False) for cp in cp_isolation] + else: + cp_isolation = [] + + if cp_exclusion is not None: + logger.log( + TRACE, + "cp_exclusion is set. use this flag for debugging purpose. " + "limited list of encoding excluded : %s.", + ", ".join(cp_exclusion), + ) + cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] + else: + cp_exclusion = [] + + if length <= (chunk_size * steps): + logger.log( + TRACE, + "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", + steps, + chunk_size, + length, + ) + steps = 1 + chunk_size = length + + if steps > 1 and length / steps < chunk_size: + chunk_size = int(length / steps) + + is_too_small_sequence: bool = len(sequences) < TOO_SMALL_SEQUENCE + is_too_large_sequence: bool = len(sequences) >= TOO_BIG_SEQUENCE + + if is_too_small_sequence: + logger.log( + TRACE, + "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( + length + ), + ) + elif is_too_large_sequence: + logger.log( + TRACE, + "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( + length + ), + ) + + prioritized_encodings: list[str] = [] + + specified_encoding: str | None = ( + any_specified_encoding(sequences) if preemptive_behaviour else None + ) + + if specified_encoding is not None: + prioritized_encodings.append(specified_encoding) + logger.log( + TRACE, + "Detected declarative mark in sequence. Priority +1 given for %s.", + specified_encoding, + ) + + tested: set[str] = set() + tested_but_hard_failure: list[str] = [] + tested_but_soft_failure: list[str] = [] + soft_failure_skip: set[str] = set() + success_fast_tracked: set[str] = set() + + # Cache for decoded payload deduplication: hash(decoded_payload) -> (mean_mess_ratio, cd_ratios_merged, passed) + # When multiple encodings decode to the exact same string, we can skip the expensive + # mess_ratio and coherence_ratio analysis and reuse the results from the first encoding. + payload_result_cache: dict[int, tuple[float, list[tuple[str, float]], bool]] = {} + + # When a definitive result (chaos=0.0 and good coherence) is found after testing + # the prioritized encodings (ascii, utf_8), we can significantly reduce the remaining + # work. Encodings that target completely different language families (e.g., Cyrillic + # when the definitive match is Latin) are skipped entirely. + # Additionally, for same-family encodings that pass chaos probing, we reuse the + # definitive match's coherence ratios instead of recomputing them — a major savings + # since coherence_ratio accounts for ~30% of total time on slow Latin files. + definitive_match_found: bool = False + definitive_target_languages: set[str] = set() + # After the definitive match fires, we cap the number of additional same-family + # single-byte encodings that pass chaos probing. Once we've accumulated enough + # good candidates (N), further same-family SB encodings are unlikely to produce + # a better best() result and just waste mess_ratio + coherence_ratio time. + # The first encoding to trigger the definitive match is NOT counted (it's already in). + post_definitive_sb_success_count: int = 0 + POST_DEFINITIVE_SB_CAP: int = 7 + + # When a non-UTF multibyte encoding passes chaos probing with significant multibyte + # content (decoded length < 98% of raw length), skip all remaining single-byte encodings. + # Rationale: multi-byte decoders (CJK) have strict byte-sequence validation — if they + # decode without error AND pass chaos probing with substantial multibyte content, the + # data is genuinely multibyte encoded. Single-byte encodings will always decode (every + # byte maps to something) but waste time on mess_ratio before failing. + # The 98% threshold prevents false triggers on files that happen to have a few valid + # multibyte pairs (e.g., cp424/_ude_1.txt where big5 decodes with 99% ratio). + mb_definitive_match_found: bool = False + + fallback_ascii: CharsetMatch | None = None + fallback_u8: CharsetMatch | None = None + fallback_specified: CharsetMatch | None = None + + results: CharsetMatches = CharsetMatches() + + early_stop_results: CharsetMatches = CharsetMatches() + + sig_encoding, sig_payload = identify_sig_or_bom(sequences) + + if sig_encoding is not None: + prioritized_encodings.append(sig_encoding) + logger.log( + TRACE, + "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", + len(sig_payload), + sig_encoding, + ) + + prioritized_encodings.append("ascii") + + if "utf_8" not in prioritized_encodings: + prioritized_encodings.append("utf_8") + + for encoding_iana in prioritized_encodings + IANA_SUPPORTED_MB_FIRST: + if cp_isolation and encoding_iana not in cp_isolation: + continue + + if cp_exclusion and encoding_iana in cp_exclusion: + continue + + if encoding_iana in tested: + continue + + tested.add(encoding_iana) + + decoded_payload: str | None = None + bom_or_sig_available: bool = sig_encoding == encoding_iana + strip_sig_or_bom: bool = bom_or_sig_available and should_strip_sig_or_bom( + encoding_iana + ) + + if encoding_iana in {"utf_16", "utf_32"} and not bom_or_sig_available: + logger.log( + TRACE, + "Encoding %s won't be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", + encoding_iana, + ) + continue + if encoding_iana in {"utf_7"} and not bom_or_sig_available: + logger.log( + TRACE, + "Encoding %s won't be tested as-is because detection is unreliable without BOM/SIG.", + encoding_iana, + ) + continue + + # Skip encodings similar to ones that already soft-failed (high mess ratio). + # Checked BEFORE the expensive decode attempt. + if encoding_iana in soft_failure_skip: + logger.log( + TRACE, + "%s is deemed too similar to a code page that was already considered unsuited. Continuing!", + encoding_iana, + ) + continue + + # Skip encodings that were already fast-tracked from a similar successful encoding. + if encoding_iana in success_fast_tracked: + logger.log( + TRACE, + "Skipping %s: already fast-tracked from a similar successful encoding.", + encoding_iana, + ) + continue + + try: + is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) + except (ModuleNotFoundError, ImportError): # Defensive: + logger.log( + TRACE, + "Encoding %s does not provide an IncrementalDecoder", + encoding_iana, + ) + continue + + # When we've already found a definitive match (chaos=0.0 with good coherence) + # after testing the prioritized encodings, skip encodings that target + # completely different language families. This avoids running expensive + # mess_ratio + coherence_ratio on clearly unrelated candidates (e.g., Cyrillic + # when the definitive match is Latin-based). + if definitive_match_found: + if not is_multi_byte_decoder: + enc_languages = set(encoding_languages(encoding_iana)) + else: + enc_languages = set(mb_encoding_languages(encoding_iana)) + if not enc_languages.intersection(definitive_target_languages): + logger.log( + TRACE, + "Skipping %s: definitive match already found, this encoding targets different languages (%s vs %s).", + encoding_iana, + enc_languages, + definitive_target_languages, + ) + continue + + # After the definitive match, cap the number of additional same-family + # single-byte encodings that pass chaos probing. This avoids testing the + # tail of rare, low-value same-family encodings (mac_iceland, cp860, etc.) + # that almost never change best() but each cost ~1-2ms of mess_ratio + coherence. + if ( + definitive_match_found + and not is_multi_byte_decoder + and post_definitive_sb_success_count >= POST_DEFINITIVE_SB_CAP + ): + logger.log( + TRACE, + "Skipping %s: already accumulated %d same-family results after definitive match (cap=%d).", + encoding_iana, + post_definitive_sb_success_count, + POST_DEFINITIVE_SB_CAP, + ) + continue + + # When a multibyte encoding with significant multibyte content has already + # passed chaos probing, skip all single-byte encodings. They will either fail + # chaos probing (wasting mess_ratio time) or produce inferior results. + if mb_definitive_match_found and not is_multi_byte_decoder: + logger.log( + TRACE, + "Skipping single-byte %s: multi-byte definitive match already found.", + encoding_iana, + ) + continue + + try: + if is_too_large_sequence and is_multi_byte_decoder is False: + str( + ( + sequences[: int(50e4)] + if strip_sig_or_bom is False + else sequences[len(sig_payload) : int(50e4)] + ), + encoding=encoding_iana, + ) + else: + # UTF-7 BOM is encoded in modified Base64 whose byte boundary + # can overlap with the next character. Stripping raw SIG bytes + # before decoding may leave stray bytes that decode as garbage. + # Decode the full sequence and remove the leading BOM char instead. + # see https://github.com/jawah/charset_normalizer/issues/718 + # and https://github.com/jawah/charset_normalizer/issues/716 + if encoding_iana == "utf_7" and bom_or_sig_available: + decoded_payload = str( + sequences, + encoding=encoding_iana, + ) + if decoded_payload and decoded_payload[0] == "\ufeff": + decoded_payload = decoded_payload[1:] + else: + decoded_payload = str( + ( + sequences + if strip_sig_or_bom is False + else sequences[len(sig_payload) :] + ), + encoding=encoding_iana, + ) + except (UnicodeDecodeError, LookupError) as e: + if not isinstance(e, LookupError): + logger.log( + TRACE, + "Code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + tested_but_hard_failure.append(encoding_iana) + continue + + r_ = range( + 0 if not bom_or_sig_available else len(sig_payload), + length, + int(length / steps), + ) + + multi_byte_bonus: bool = ( + is_multi_byte_decoder + and decoded_payload is not None + and len(decoded_payload) < length + ) + + if multi_byte_bonus: + logger.log( + TRACE, + "Code page %s is a multi byte encoding table and it appear that at least one character " + "was encoded using n-bytes.", + encoding_iana, + ) + + # Payload-hash deduplication: if another encoding already decoded to the + # exact same string, reuse its mess_ratio and coherence results entirely. + # This is strictly more general than the old IANA_SUPPORTED_SIMILAR approach + # because it catches ALL identical decoding, not just pre-mapped ones. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_hash: int = hash(decoded_payload) + cached = payload_result_cache.get(payload_hash) + if cached is not None: + cached_mess, cached_cd, cached_passed = cached + if cached_passed: + # The previous encoding with identical output passed chaos probing. + fast_match = CharsetMatch( + sequences, + encoding_iana, + cached_mess, + bom_or_sig_available, + cached_cd, + ( + decoded_payload + if ( + is_too_large_sequence is False + or encoding_iana + in [specified_encoding, "ascii", "utf_8"] + ) + else None + ), + preemptive_declaration=specified_encoding, + ) + results.append(fast_match) + success_fast_tracked.add(encoding_iana) + logger.log( + TRACE, + "%s fast-tracked (identical decoded payload to a prior encoding, chaos=%f %%).", + encoding_iana, + round(cached_mess * 100, ndigits=3), + ) + + if ( + encoding_iana in [specified_encoding, "ascii", "utf_8"] + and cached_mess < 0.1 + ): + if cached_mess == 0.0: + logger.debug( + "Encoding detection: %s is most likely the one.", + fast_match.encoding, + ) + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([fast_match]) + early_stop_results.append(fast_match) + + if ( + len(early_stop_results) + and (specified_encoding is None or specified_encoding in tested) + and "ascii" in tested + and "utf_8" in tested + ): + probable_result: CharsetMatch = early_stop_results.best() # type: ignore[assignment] + logger.debug( + "Encoding detection: %s is most likely the one.", + probable_result.encoding, + ) + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([probable_result]) + + continue + else: + # The previous encoding with identical output failed chaos probing. + tested_but_soft_failure.append(encoding_iana) + logger.log( + TRACE, + "%s fast-skipped (identical decoded payload to a prior encoding that failed chaos probing).", + encoding_iana, + ) + # Prepare fallbacks for special encodings even when skipped. + if enable_fallback and encoding_iana in [ + "ascii", + "utf_8", + specified_encoding, + "utf_16", + "utf_32", + ]: + fallback_entry = CharsetMatch( + sequences, + encoding_iana, + threshold, + bom_or_sig_available, + [], + decoded_payload, + preemptive_declaration=specified_encoding, + ) + if encoding_iana == specified_encoding: + fallback_specified = fallback_entry + elif encoding_iana == "ascii": + fallback_ascii = fallback_entry + else: + fallback_u8 = fallback_entry + continue + + max_chunk_gave_up: int = int(len(r_) / 4) + + max_chunk_gave_up = max(max_chunk_gave_up, 2) + early_stop_count: int = 0 + lazy_str_hard_failure = False + + md_chunks: list[str] = [] + md_ratios = [] + + try: + for chunk in cut_sequence_chunks( + sequences, + encoding_iana, + r_, + chunk_size, + bom_or_sig_available, + strip_sig_or_bom, + sig_payload, + is_multi_byte_decoder, + decoded_payload, + ): + md_chunks.append(chunk) + + md_ratios.append( + mess_ratio( + chunk, + threshold, + explain is True and 1 <= len(cp_isolation) <= 2, + ) + ) + + if md_ratios[-1] >= threshold: + early_stop_count += 1 + + if (early_stop_count >= max_chunk_gave_up) or ( + bom_or_sig_available and strip_sig_or_bom is False + ): + break + except ( + UnicodeDecodeError + ) as e: # Lazy str loading may have missed something there + logger.log( + TRACE, + "LazyStr Loading: After MD chunk decode, code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + early_stop_count = max_chunk_gave_up + lazy_str_hard_failure = True + + # We might want to check the sequence again with the whole content + # Only if initial MD tests passes + if ( + not lazy_str_hard_failure + and is_too_large_sequence + and not is_multi_byte_decoder + ): + try: + sequences[int(50e3) :].decode(encoding_iana, errors="strict") + except UnicodeDecodeError as e: + logger.log( + TRACE, + "LazyStr Loading: After final lookup, code page %s does not fit given bytes sequence at ALL. %s", + encoding_iana, + str(e), + ) + tested_but_hard_failure.append(encoding_iana) + continue + + mean_mess_ratio: float = sum(md_ratios) / len(md_ratios) if md_ratios else 0.0 + if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: + tested_but_soft_failure.append(encoding_iana) + if encoding_iana in IANA_SUPPORTED_SIMILAR: + soft_failure_skip.update(IANA_SUPPORTED_SIMILAR[encoding_iana]) + # Cache this soft-failure so identical decoding from other encodings + # can be skipped immediately. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_result_cache.setdefault( + hash(decoded_payload), (mean_mess_ratio, [], False) + ) + logger.log( + TRACE, + "%s was excluded because of initial chaos probing. Gave up %i time(s). " + "Computed mean chaos is %f %%.", + encoding_iana, + early_stop_count, + round(mean_mess_ratio * 100, ndigits=3), + ) + # Preparing those fallbacks in case we got nothing. + if ( + enable_fallback + and encoding_iana + in ["ascii", "utf_8", specified_encoding, "utf_16", "utf_32"] + and not lazy_str_hard_failure + ): + fallback_entry = CharsetMatch( + sequences, + encoding_iana, + threshold, + bom_or_sig_available, + [], + decoded_payload, + preemptive_declaration=specified_encoding, + ) + if encoding_iana == specified_encoding: + fallback_specified = fallback_entry + elif encoding_iana == "ascii": + fallback_ascii = fallback_entry + else: + fallback_u8 = fallback_entry + continue + + logger.log( + TRACE, + "%s passed initial chaos probing. Mean measured chaos is %f %%", + encoding_iana, + round(mean_mess_ratio * 100, ndigits=3), + ) + + if not is_multi_byte_decoder: + target_languages: list[str] = encoding_languages(encoding_iana) + else: + target_languages = mb_encoding_languages(encoding_iana) + + if target_languages: + logger.log( + TRACE, + "{} should target any language(s) of {}".format( + encoding_iana, str(target_languages) + ), + ) + + cd_ratios = [] + + # Run coherence detection on all chunks. We previously tried limiting to + # 1-2 chunks for post-definitive encodings to save time, but this caused + # coverage regressions by producing unrepresentative coherence scores. + # The SB cap and language-family skip optimizations provide sufficient + # speedup without sacrificing coherence accuracy. + if encoding_iana != "ascii": + # We shall skip the CD when its about ASCII + # Most of the time its not relevant to run "language-detection" on it. + for chunk in md_chunks: + chunk_languages = coherence_ratio( + chunk, + language_threshold, + ",".join(target_languages) if target_languages else None, + ) + + cd_ratios.append(chunk_languages) + cd_ratios_merged = merge_coherence_ratios(cd_ratios) + else: + cd_ratios_merged = merge_coherence_ratios(cd_ratios) + + if cd_ratios_merged: + logger.log( + TRACE, + "We detected language {} using {}".format( + cd_ratios_merged, encoding_iana + ), + ) + + current_match = CharsetMatch( + sequences, + encoding_iana, + mean_mess_ratio, + bom_or_sig_available, + cd_ratios_merged, + ( + decoded_payload + if ( + is_too_large_sequence is False + or encoding_iana in [specified_encoding, "ascii", "utf_8"] + ) + else None + ), + preemptive_declaration=specified_encoding, + ) + + results.append(current_match) + + # Cache the successful result for payload-hash deduplication. + if decoded_payload is not None and not is_multi_byte_decoder: + payload_result_cache.setdefault( + hash(decoded_payload), + (mean_mess_ratio, cd_ratios_merged, True), + ) + + # Count post-definitive same-family SB successes for the early termination cap. + # Only count low-mess encodings (< 2%) toward the cap. High-mess encodings are + # marginal results that shouldn't prevent better-quality candidates from being + # tested. For example, iso8859_4 (mess=0%) should not be skipped just because + # 7 high-mess Latin encodings (cp1252 at 8%, etc.) were tried first. + if ( + definitive_match_found + and not is_multi_byte_decoder + and mean_mess_ratio < 0.02 + ): + post_definitive_sb_success_count += 1 + + if ( + encoding_iana in [specified_encoding, "ascii", "utf_8"] + and mean_mess_ratio < 0.1 + ): + # If md says nothing to worry about, then... stop immediately! + if mean_mess_ratio == 0.0: + logger.debug( + "Encoding detection: %s is most likely the one.", + current_match.encoding, + ) + if explain: # Defensive: ensure exit path clean handler + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([current_match]) + + early_stop_results.append(current_match) + + if ( + len(early_stop_results) + and (specified_encoding is None or specified_encoding in tested) + and "ascii" in tested + and "utf_8" in tested + ): + probable_result = early_stop_results.best() # type: ignore[assignment] + logger.debug( + "Encoding detection: %s is most likely the one.", + probable_result.encoding, # type: ignore[union-attr] + ) + if explain: # Defensive: ensure exit path clean handler + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + + return CharsetMatches([probable_result]) + + # Once we find a result with good coherence (>= 0.5) after testing the + # prioritized encodings (ascii, utf_8), activate "definitive mode": skip + # encodings that target completely different language families. This avoids + # running expensive mess_ratio + coherence_ratio on clearly unrelated + # candidates (e.g., Cyrillic encodings when the match is Latin-based). + # We require coherence >= 0.5 to avoid false positives (e.g., cp1251 decoding + # Hebrew text with 0.0 chaos but wrong language detection at coherence 0.33). + if not definitive_match_found and not is_multi_byte_decoder: + best_coherence = ( + max((v for _, v in cd_ratios_merged), default=0.0) + if cd_ratios_merged + else 0.0 + ) + if best_coherence >= 0.5 and "ascii" in tested and "utf_8" in tested: + definitive_match_found = True + definitive_target_languages.update(target_languages) + logger.log( + TRACE, + "Definitive match found: %s (chaos=%.3f, coherence=%.2f). Encodings targeting different language families will be skipped.", + encoding_iana, + mean_mess_ratio, + best_coherence, + ) + + # When a non-UTF multibyte encoding passes chaos probing with significant + # multibyte content (decoded < 98% of raw), activate mb_definitive_match. + # This skips all remaining single-byte encodings which would either soft-fail + # (running expensive mess_ratio for nothing) or produce inferior results. + if ( + not mb_definitive_match_found + and is_multi_byte_decoder + and multi_byte_bonus + and decoded_payload is not None + and len(decoded_payload) < length * 0.98 + and encoding_iana + not in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_be", + "utf_32_le", + "utf_7", + } + and "ascii" in tested + and "utf_8" in tested + ): + mb_definitive_match_found = True + logger.log( + TRACE, + "Multi-byte definitive match: %s (chaos=%.3f, decoded=%d/%d=%.1f%%). Single-byte encodings will be skipped.", + encoding_iana, + mean_mess_ratio, + len(decoded_payload), + length, + len(decoded_payload) / length * 100, + ) + + if encoding_iana == sig_encoding: + logger.debug( + "Encoding detection: %s is most likely the one as we detected a BOM or SIG within " + "the beginning of the sequence.", + encoding_iana, + ) + if explain: # Defensive: ensure exit path clean handler + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + return CharsetMatches([results[encoding_iana]]) + + if len(results) == 0: + if fallback_u8 or fallback_ascii or fallback_specified: + logger.log( + TRACE, + "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.", + ) + + if fallback_specified: + logger.debug( + "Encoding detection: %s will be used as a fallback match", + fallback_specified.encoding, + ) + results.append(fallback_specified) + elif ( + (fallback_u8 and fallback_ascii is None) + or ( + fallback_u8 + and fallback_ascii + and fallback_u8.fingerprint != fallback_ascii.fingerprint + ) + or (fallback_u8 is not None) + ): + logger.debug("Encoding detection: utf_8 will be used as a fallback match") + results.append(fallback_u8) + elif fallback_ascii: + logger.debug("Encoding detection: ascii will be used as a fallback match") + results.append(fallback_ascii) + + if results: + logger.debug( + "Encoding detection: Found %s as plausible (best-candidate) for content. With %i alternatives.", + results.best().encoding, # type: ignore + len(results) - 1, + ) + else: + logger.debug("Encoding detection: Unable to determine any suitable charset.") + + if explain: + logger.removeHandler(explain_handler) + logger.setLevel(previous_logger_level) + + return results + + +def from_fp( + fp: BinaryIO, + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: list[str] | None = None, + cp_exclusion: list[str] | None = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Same thing than the function from_bytes but using a file pointer that is already ready. + Will not close the file pointer. + """ + return from_bytes( + fp.read(), + steps, + chunk_size, + threshold, + cp_isolation, + cp_exclusion, + preemptive_behaviour, + explain, + language_threshold, + enable_fallback, + ) + + +def from_path( + path: str | bytes | PathLike, # type: ignore[type-arg] + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: list[str] | None = None, + cp_exclusion: list[str] | None = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = True, +) -> CharsetMatches: + """ + Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. + Can raise IOError. + """ + with open(path, "rb") as fp: + return from_fp( + fp, + steps, + chunk_size, + threshold, + cp_isolation, + cp_exclusion, + preemptive_behaviour, + explain, + language_threshold, + enable_fallback, + ) + + +def is_binary( + fp_or_path_or_payload: PathLike | str | BinaryIO | bytes, # type: ignore[type-arg] + steps: int = 5, + chunk_size: int = 512, + threshold: float = 0.20, + cp_isolation: list[str] | None = None, + cp_exclusion: list[str] | None = None, + preemptive_behaviour: bool = True, + explain: bool = False, + language_threshold: float = 0.1, + enable_fallback: bool = False, +) -> bool: + """ + Detect if the given input (file, bytes, or path) points to a binary file. aka. not a string. + Based on the same main heuristic algorithms and default kwargs at the sole exception that fallbacks match + are disabled to be stricter around ASCII-compatible but unlikely to be a string. + """ + if isinstance(fp_or_path_or_payload, (str, PathLike)): + guesses = from_path( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + elif isinstance( + fp_or_path_or_payload, + ( + bytes, + bytearray, + ), + ): + guesses = from_bytes( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + else: + guesses = from_fp( + fp_or_path_or_payload, + steps=steps, + chunk_size=chunk_size, + threshold=threshold, + cp_isolation=cp_isolation, + cp_exclusion=cp_exclusion, + preemptive_behaviour=preemptive_behaviour, + explain=explain, + language_threshold=language_threshold, + enable_fallback=enable_fallback, + ) + + return not guesses diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cd.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/charset_normalizer/cd.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..dffe3e48b59c960aa602778742e128867f0f0901 GIT binary patch literal 15912 zcmeHOYit}>6~4QU>E?m$mO3?Y)hv}GInZo(9XlA(H0$^^20L+_7^#ZV@$AmnyVX9d z-PzPzg%Zh0g$YruNQj5{5m1Ce1r_k4ywu8RLQ_>p7=+aPL0yD`)|9lVpr}Q{%Q^R+ zvmOt-X?`GtV6L@u&iT&co;h=8eP`$1FARIQq%{f=Vx3G)&1eb^3h>T$Xn!zvX@F z=@j*sAj*CyqFqMY2&g_P=EF65~lo_(&@_cyB9iZfY0#3N%8D=W;WdO)z-(g!~VUE@4nIe)7QIK zF&A{-2p{xQ`9+23^Gj?KVBbCaV&#W7UKj?pX2U##H8cgl_#B+-Z`HxE|De;5cpYf1 zIA5-VKLmIXKJGRIz=LiOZ0MSZ|BQmJg5JUViqU+)Tag?jMBee3@GQeymdVEEb;wk2U0 z`Ni*1361YxzTcu$-|KXLGA;-&xp0WfKmsH2wpWzT?n$wSV+qc@mVA&m?$VY7*iI6t%7V)+(G4v zaHafXgcp9HoozU?G`CTkFZ;BG2D@83bJKnVEYV+Ec)PreW@jhGncvK9(&qmJMw7x0 zVZ5zyf!dB8uap+Gg*VD`vX`5MtB6LHjvc=;9ctcw8q0)nw1t6=K>3Ia2Hcx3O|PjJ zSB?P=cGYoEz4FrCJ9riFD&SSXtAJMluL52Lyb5>~@G9U{z^lOjNCkQ$W>@#_nAsKY z>FtSyr@MQ*BD$U~&KBcBzSWB-j9lKb^-MOGmTwbtx{hxWsolmu^g;f<_J+Mdh=V^4 zTztDyc?9ThZ&fN7{Pa7O$~S@HJ7#RYrzV7NuEp2B<*vXY{Myg~*!}RC0>4=_Xlfa1 zYWqy{<|hNE#Qtr2Kfb%;L4>>EGXS3i#Jz|>UsKC7eyyo(zG0v#?0~_;08HuY+{2 zd*ro^FH({$UvtU0<`UZ$MXC!hKTL9f)~kM$WL}5a&U&KTi1K<&d{)E8YZ2dFtz=Q2 zqD7JXjI~IMA$cw17fFw6g|XdK1Xk3l)<%@vM48rMlABm0JwHd{I!SEzDI%t*W5$27 zK`A)TOJv7uF1bH=Ybx{q8rj1eU@3E5;jvN5Pm}(Ak~vQb`yV5`Uftc{9$H{KMD%k+ zr-_~*`V7(G{{Bw~J7LB5&S0b)7V$=cUE!{ta3m5Ac1~DkP%~^&?21Hos_QLXp)kXJ z9lqU0L@ZJ)rdVsY4KMuNO18CZcr3Qin5<>PeT>jL;953~yaxL=aqh&1b8Wv(&YWv2 z_2s@EEaN!ZAl|`O^XnSkmwp)id|s@CB?A&)qP`jb7~%YVz&LK_@QJM=990O9+kT1P zE%?4wS|OeQ2K%jkzmVdDv~Q`d$7cJl08=afdgt{G8K+r1gbBM^tvHv!#t+|8JO37sGiq%GyZdle?++NgR9b@sk+~S?eTliCt9oZVf+t-^Y;wn?*k)uHMr`J7cj_1 z4%!ZdaG6SNR~`IOz-z@Bt79Jr9QPf#_ZQ>|ckf5|e4RMw0k75X_W+kausP02vhkdq zw+pbuBrevrE$DVykK@LLJnU~Uv-(Ub8#7Y6X=ihJ-6+h7cs4zovTVzQy#h5Ya5FwsA)PLQi$hYo5uB!R6SC3up^3h+L4EMx0Pd^6c-U1wt0#<% ziQ6d#9y{1KHqs9$e81^~8aZeK69B=^3w9kIJ#?UNR6jH{bY$?Tezfnv=pY2dUpsHR zU8Bw}3+ILk*XECUyER7Sb_?Eh;oRL(Yr_WaAi0BGBGGj_GeY@d+BRY$l(%z=CYZ}) zZ7VdBDTHE$WXjx?G=-EBMm`}zW-$YvirP7)Ibr4U$!unwpo1=Fr3`c+ZZ>5L`J1r_ z+14EJ@)Ct|S<|o$5wa3=krO8PvZOrJz)&8{qZf7o0c1KxIvIx)vJe;!A=z6jpBEt* zdubR_Dm{5lApjeSlOzh9|Dy9LrpRr-T$g)4rccOq{Z1(4Q8dIK^fr)GsjzysXhjS$M=Xnv+ ztz^mhvmI01M}>1PmU-UAG)VsL{0SB&FsNgHp64;WM26hI`}{NHe}L?G-pF*(;ol0p z+kXx)tO?I+t6%=~Pk`so6h`LCJW?EpPX8eJF`ac915fHIecI*E^9QB{*9BvL4tv_= z&;NIs^78@PW38R-OCVquT?__^AzEadDA$kEIuAJVeEm)al_Ap(C038_{x5*W>3<=p zOqnh!v3lhG8F&sDoMZ9$;O8FRH|e%#nduAQ+wAb?`PKDZ077FsC9TPfzYGe@pZn+e zP3Z&tIo_{;!Li5wJP+c1ttAW&jqAq!@;JB-I%og9Z|>EHrJD#UQa!Ri)8B!{>Cf}0 zGWly&PsOkw(?0@?Jm=5z)^K>ey^>U4%p-jhKG;8*BP1L9QyL6QTNKa-+h5_s&xk1Z zPT^6ADGC^5Lj_msNTQ6xW0x_cry@#PCM7;M list[str]: + """ + Return associated unicode ranges in a single byte code page. + """ + if is_multi_byte_encoding(iana_name): + raise OSError( # Defensive: + "Function not supported on multi-byte code page" + ) + + decoder = importlib.import_module(f"encodings.{iana_name}").IncrementalDecoder + + p: IncrementalDecoder = decoder(errors="ignore") + seen_ranges: dict[str, int] = {} + character_count: int = 0 + + for i in range(0x40, 0xFF): + chunk: str = p.decode(bytes([i])) + + if chunk: + character_range: str | None = unicode_range(chunk) + + if character_range is None: + continue + + if is_unicode_range_secondary(character_range) is False: + if character_range not in seen_ranges: + seen_ranges[character_range] = 0 + seen_ranges[character_range] += 1 + character_count += 1 + + return sorted( + [ + character_range + for character_range in seen_ranges + if seen_ranges[character_range] / character_count >= 0.15 + ] + ) + + +def unicode_range_languages(primary_range: str) -> list[str]: + """ + Return inferred languages used with a unicode range. + """ + languages: list[str] = [] + + for language, characters in FREQUENCIES.items(): + for character in characters: + if unicode_range(character) == primary_range: + languages.append(language) + break + + return languages + + +@lru_cache() +def encoding_languages(iana_name: str) -> list[str]: + """ + Single-byte encoding language association. Some code page are heavily linked to particular language(s). + This function does the correspondence. + """ + unicode_ranges: list[str] = encoding_unicode_range(iana_name) + primary_range: str | None = None + + for specified_range in unicode_ranges: + if "Latin" not in specified_range: + primary_range = specified_range + break + + if primary_range is None: + return ["Latin Based"] + + return unicode_range_languages(primary_range) + + +@lru_cache() +def mb_encoding_languages(iana_name: str) -> list[str]: + """ + Multi-byte encoding language association. Some code page are heavily linked to particular language(s). + This function does the correspondence. + """ + if ( + iana_name.startswith("shift_") + or iana_name.startswith("iso2022_jp") + or iana_name.startswith("euc_j") + or iana_name == "cp932" + ): + return ["Japanese"] + if iana_name.startswith("gb") or iana_name in ZH_NAMES: + return ["Chinese"] + if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: + return ["Korean"] + + return [] + + +@lru_cache(maxsize=LANGUAGE_SUPPORTED_COUNT) +def get_target_features(language: str) -> tuple[bool, bool]: + """ + Determine main aspects from a supported language if it contains accents and if is pure Latin. + """ + target_have_accents: bool = False + target_pure_latin: bool = True + + for character in FREQUENCIES[language]: + if not target_have_accents and is_accentuated(character): + target_have_accents = True + if target_pure_latin and is_latin(character) is False: + target_pure_latin = False + + return target_have_accents, target_pure_latin + + +def alphabet_languages( + characters: list[str], ignore_non_latin: bool = False +) -> list[str]: + """ + Return associated languages associated to given characters. + """ + languages: list[tuple[str, float]] = [] + + characters_set: frozenset[str] = frozenset(characters) + source_have_accents = any(is_accentuated(character) for character in characters) + + for language, language_characters in FREQUENCIES.items(): + target_have_accents, target_pure_latin = get_target_features(language) + + if ignore_non_latin and target_pure_latin is False: + continue + + if target_have_accents is False and source_have_accents: + continue + + character_count: int = len(language_characters) + + character_match_count: int = len(_FREQUENCIES_SET[language] & characters_set) + + ratio: float = character_match_count / character_count + + if ratio >= 0.2: + languages.append((language, ratio)) + + languages = sorted(languages, key=lambda x: x[1], reverse=True) + + return [compatible_language[0] for compatible_language in languages] + + +def characters_popularity_compare( + language: str, ordered_characters: list[str] +) -> float: + """ + Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. + The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). + Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) + """ + if language not in FREQUENCIES: + raise ValueError(f"{language} not available") # Defensive: + + character_approved_count: int = 0 + frequencies_language_set: frozenset[str] = _FREQUENCIES_SET[language] + lang_rank: dict[str, int] = _FREQUENCIES_RANK[language] + + ordered_characters_count: int = len(ordered_characters) + target_language_characters_count: int = len(FREQUENCIES[language]) + + large_alphabet: bool = target_language_characters_count > 26 + + expected_projection_ratio: float = ( + target_language_characters_count / ordered_characters_count + ) + + # Pre-built rank dict for ordered_characters (avoids repeated list slicing). + ordered_rank: dict[str, int] = { + char: rank for rank, char in enumerate(ordered_characters) + } + + # Pre-compute characters common to both orderings. + # Avoids repeated `c in ordered_rank` dict lookups in the inner counts. + common_chars: list[tuple[int, int]] = [ + (lr, ordered_rank[c]) for c, lr in lang_rank.items() if c in ordered_rank + ] + + # Pre-extract lr and orr arrays for faster iteration in the inner loop. + # Plain integer loops with local arrays are much faster under mypyc than + # generator expression sums over a list of tuples. + common_count: int = len(common_chars) + common_lr: list[int] = [p[0] for p in common_chars] + common_orr: list[int] = [p[1] for p in common_chars] + + for character, character_rank in zip( + ordered_characters, range(0, ordered_characters_count) + ): + if character not in frequencies_language_set: + continue + + character_rank_in_language: int = lang_rank[character] + character_rank_projection: int = int(character_rank * expected_projection_ratio) + + if ( + large_alphabet is False + and abs(character_rank_projection - character_rank_in_language) > 4 + ): + continue + + if ( + large_alphabet is True + and abs(character_rank_projection - character_rank_in_language) + < target_language_characters_count / 3 + ): + character_approved_count += 1 + continue + + # Count how many characters appear "before" in both orderings, + # and how many appear "at or after" in both orderings. + # Single pass over pre-extracted arrays — much faster under mypyc + # than two generator expression sums. + before_match_count: int = 0 + after_match_count: int = 0 + for i in range(common_count): + lr_i: int = common_lr[i] + orr_i: int = common_orr[i] + if lr_i < character_rank_in_language: + if orr_i < character_rank: + before_match_count += 1 + else: + if orr_i >= character_rank: + after_match_count += 1 + + after_len: int = target_language_characters_count - character_rank_in_language + + if character_rank_in_language == 0 and before_match_count <= 4: + character_approved_count += 1 + continue + + if after_len == 0 and after_match_count <= 4: + character_approved_count += 1 + continue + + if ( + character_rank_in_language > 0 + and before_match_count / character_rank_in_language >= 0.4 + ) or (after_len > 0 and after_match_count / after_len >= 0.4): + character_approved_count += 1 + continue + + return character_approved_count / len(ordered_characters) + + +def alpha_unicode_split(decoded_sequence: str) -> list[str]: + """ + Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. + Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; + One containing the latin letters and the other hebrew. + """ + layers: dict[str, list[str]] = {} + + # Fast path: track single-layer key to skip dict iteration for single-script text. + single_layer_key: str | None = None + multi_layer: bool = False + + # Cache the last character_range and its resolved layer to avoid repeated + # is_suspiciously_successive_range calls for consecutive same-range chars. + prev_character_range: str | None = None + prev_layer_target: str | None = None + + for character in decoded_sequence: + if character.isalpha() is False: + continue + + # ASCII fast-path: a-z and A-Z are always "Basic Latin". + # Avoids unicode_range() function call overhead for the most common case. + character_ord: int = ord(character) + if character_ord < 128: + character_range: str | None = "Basic Latin" + else: + character_range = unicode_range(character) + + if character_range is None: + continue + + # Fast path: same range as previous character → reuse cached layer target. + if character_range == prev_character_range: + if prev_layer_target is not None: + layers[prev_layer_target].append(character) + continue + + layer_target_range: str | None = None + + if multi_layer: + for discovered_range in layers: + if ( + is_suspiciously_successive_range(discovered_range, character_range) + is False + ): + layer_target_range = discovered_range + break + elif single_layer_key is not None: + if ( + is_suspiciously_successive_range(single_layer_key, character_range) + is False + ): + layer_target_range = single_layer_key + + if layer_target_range is None: + layer_target_range = character_range + + if layer_target_range not in layers: + layers[layer_target_range] = [] + if single_layer_key is None: + single_layer_key = layer_target_range + else: + multi_layer = True + + layers[layer_target_range].append(character) + + # Cache for next iteration + prev_character_range = character_range + prev_layer_target = layer_target_range + + return ["".join(chars).lower() for chars in layers.values()] + + +def merge_coherence_ratios(results: list[CoherenceMatches]) -> CoherenceMatches: + """ + This function merge results previously given by the function coherence_ratio. + The return type is the same as coherence_ratio. + """ + per_language_ratios: dict[str, list[float]] = {} + for result in results: + for sub_result in result: + language, ratio = sub_result + if language not in per_language_ratios: + per_language_ratios[language] = [ratio] + continue + per_language_ratios[language].append(ratio) + + merge = [ + ( + language, + round( + sum(per_language_ratios[language]) / len(per_language_ratios[language]), + 4, + ), + ) + for language in per_language_ratios + ] + + return sorted(merge, key=lambda x: x[1], reverse=True) + + +def filter_alt_coherence_matches(results: CoherenceMatches) -> CoherenceMatches: + """ + We shall NOT return "English—" in CoherenceMatches because it is an alternative + of "English". This function only keeps the best match and remove the em-dash in it. + """ + index_results: dict[str, list[float]] = dict() + + for result in results: + language, ratio = result + no_em_name: str = language.replace("—", "") + + if no_em_name not in index_results: + index_results[no_em_name] = [] + + index_results[no_em_name].append(ratio) + + if any(len(index_results[e]) > 1 for e in index_results): + filtered_results: CoherenceMatches = [] + + for language in index_results: + filtered_results.append((language, max(index_results[language]))) + + return filtered_results + + return results + + +@lru_cache(maxsize=2048) +def coherence_ratio( + decoded_sequence: str, threshold: float = 0.1, lg_inclusion: str | None = None +) -> CoherenceMatches: + """ + Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. + A layer = Character extraction by alphabets/ranges. + """ + + results: list[tuple[str, float]] = [] + ignore_non_latin: bool = False + + sufficient_match_count: int = 0 + + lg_inclusion_list = lg_inclusion.split(",") if lg_inclusion is not None else [] + if "Latin Based" in lg_inclusion_list: + ignore_non_latin = True + lg_inclusion_list.remove("Latin Based") + + for layer in alpha_unicode_split(decoded_sequence): + sequence_frequencies: TypeCounter[str] = Counter(layer) + most_common = sequence_frequencies.most_common() + + character_count: int = len(layer) + + if character_count <= TOO_SMALL_SEQUENCE: + continue + + popular_character_ordered: list[str] = [c for c, o in most_common] + + for language in lg_inclusion_list or alphabet_languages( + popular_character_ordered, ignore_non_latin + ): + ratio: float = characters_popularity_compare( + language, popular_character_ordered + ) + + if ratio < threshold: + continue + elif ratio >= 0.8: + sufficient_match_count += 1 + + results.append((language, round(ratio, 4))) + + if sufficient_match_count >= 3: + break + + return sorted( + filter_alt_coherence_matches(results), key=lambda x: x[1], reverse=True + ) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py new file mode 100644 index 0000000..543a5a4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .__main__ import cli_detect, query_yes_no + +__all__ = ( + "cli_detect", + "query_yes_no", +) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py new file mode 100644 index 0000000..ad843c1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py @@ -0,0 +1,362 @@ +from __future__ import annotations + +import argparse +import sys +import typing +from json import dumps +from os.path import abspath, basename, dirname, join, realpath +from platform import python_version +from unicodedata import unidata_version + +import charset_normalizer.md as md_module +from charset_normalizer import from_fp +from charset_normalizer.models import CliDetectionResult +from charset_normalizer.version import __version__ + + +def query_yes_no(question: str, default: str = "yes") -> bool: # Defensive: + """Ask a yes/no question via input() and return the answer as a bool.""" + prompt = " [Y/n] " if default == "yes" else " [y/N] " + + while True: + choice = input(question + prompt).strip().lower() + if not choice: + return default == "yes" + if choice in ("y", "yes"): + return True + if choice in ("n", "no"): + return False + print("Please respond with 'y' or 'n'.") + + +class FileType: + """Factory for creating file object types + + Instances of FileType are typically passed as type= arguments to the + ArgumentParser add_argument() method. + + Keyword Arguments: + - mode -- A string indicating how the file is to be opened. Accepts the + same values as the builtin open() function. + - bufsize -- The file's desired buffer size. Accepts the same values as + the builtin open() function. + - encoding -- The file's encoding. Accepts the same values as the + builtin open() function. + - errors -- A string indicating how encoding and decoding errors are to + be handled. Accepts the same value as the builtin open() function. + + Backported from CPython 3.12 + """ + + def __init__( + self, + mode: str = "r", + bufsize: int = -1, + encoding: str | None = None, + errors: str | None = None, + ): + self._mode = mode + self._bufsize = bufsize + self._encoding = encoding + self._errors = errors + + def __call__(self, string: str) -> typing.IO: # type: ignore[type-arg] + # the special argument "-" means sys.std{in,out} + if string == "-": + if "r" in self._mode: + return sys.stdin.buffer if "b" in self._mode else sys.stdin + elif any(c in self._mode for c in "wax"): + return sys.stdout.buffer if "b" in self._mode else sys.stdout + else: + msg = f'argument "-" with mode {self._mode}' + raise ValueError(msg) + + # all other arguments are used as file names + try: + return open(string, self._mode, self._bufsize, self._encoding, self._errors) + except OSError as e: + message = f"can't open '{string}': {e}" + raise argparse.ArgumentTypeError(message) + + def __repr__(self) -> str: + args = self._mode, self._bufsize + kwargs = [("encoding", self._encoding), ("errors", self._errors)] + args_str = ", ".join( + [repr(arg) for arg in args if arg != -1] + + [f"{kw}={arg!r}" for kw, arg in kwargs if arg is not None] + ) + return f"{type(self).__name__}({args_str})" + + +def cli_detect(argv: list[str] | None = None) -> int: + """ + CLI assistant using ARGV and ArgumentParser + :param argv: + :return: 0 if everything is fine, anything else equal trouble + """ + parser = argparse.ArgumentParser( + description="The Real First Universal Charset Detector. " + "Discover originating encoding used on text file. " + "Normalize text to unicode." + ) + + parser.add_argument( + "files", type=FileType("rb"), nargs="+", help="File(s) to be analysed" + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=False, + dest="verbose", + help="Display complementary information about file if any. " + "Stdout will contain logs about the detection process.", + ) + parser.add_argument( + "-a", + "--with-alternative", + action="store_true", + default=False, + dest="alternatives", + help="Output complementary possibilities if any. Top-level JSON WILL be a list.", + ) + parser.add_argument( + "-n", + "--normalize", + action="store_true", + default=False, + dest="normalize", + help="Permit to normalize input file. If not set, program does not write anything.", + ) + parser.add_argument( + "-m", + "--minimal", + action="store_true", + default=False, + dest="minimal", + help="Only output the charset detected to STDOUT. Disabling JSON output.", + ) + parser.add_argument( + "-r", + "--replace", + action="store_true", + default=False, + dest="replace", + help="Replace file when trying to normalize it instead of creating a new one.", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + default=False, + dest="force", + help="Replace file without asking if you are sure, use this flag with caution.", + ) + parser.add_argument( + "-i", + "--no-preemptive", + action="store_true", + default=False, + dest="no_preemptive", + help="Disable looking at a charset declaration to hint the detector.", + ) + parser.add_argument( + "-t", + "--threshold", + action="store", + default=0.2, + type=float, + dest="threshold", + help="Define a custom maximum amount of noise allowed in decoded content. 0. <= noise <= 1.", + ) + parser.add_argument( + "--version", + action="version", + version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( + __version__, + python_version(), + unidata_version, + "OFF" if md_module.__file__.lower().endswith(".py") else "ON", + ), + help="Show version information and exit.", + ) + + args = parser.parse_args(argv) + + if args.replace is True and args.normalize is False: + if args.files: + for my_file in args.files: + my_file.close() + print("Use --replace in addition of --normalize only.", file=sys.stderr) + return 1 + + if args.force is True and args.replace is False: + if args.files: + for my_file in args.files: + my_file.close() + print("Use --force in addition of --replace only.", file=sys.stderr) + return 1 + + if args.threshold < 0.0 or args.threshold > 1.0: + if args.files: + for my_file in args.files: + my_file.close() + print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) + return 1 + + x_ = [] + + for my_file in args.files: + matches = from_fp( + my_file, + threshold=args.threshold, + explain=args.verbose, + preemptive_behaviour=args.no_preemptive is False, + ) + + best_guess = matches.best() + + if best_guess is None: + print( + 'Unable to identify originating encoding for "{}". {}'.format( + my_file.name, + ( + "Maybe try increasing maximum amount of chaos." + if args.threshold < 1.0 + else "" + ), + ), + file=sys.stderr, + ) + x_.append( + CliDetectionResult( + abspath(my_file.name), + None, + [], + [], + "Unknown", + [], + False, + 1.0, + 0.0, + None, + True, + ) + ) + else: + cli_result = CliDetectionResult( + abspath(my_file.name), + best_guess.encoding, + best_guess.encoding_aliases, + [ + cp + for cp in best_guess.could_be_from_charset + if cp != best_guess.encoding + ], + best_guess.language, + best_guess.alphabets, + best_guess.bom, + best_guess.percent_chaos, + best_guess.percent_coherence, + None, + True, + ) + x_.append(cli_result) + + if len(matches) > 1 and args.alternatives: + for el in matches: + if el != best_guess: + x_.append( + CliDetectionResult( + abspath(my_file.name), + el.encoding, + el.encoding_aliases, + [ + cp + for cp in el.could_be_from_charset + if cp != el.encoding + ], + el.language, + el.alphabets, + el.bom, + el.percent_chaos, + el.percent_coherence, + None, + False, + ) + ) + + if args.normalize is True: + if best_guess.encoding.startswith("utf") is True: + print( + '"{}" file does not need to be normalized, as it already came from unicode.'.format( + my_file.name + ), + file=sys.stderr, + ) + if my_file.closed is False: + my_file.close() + continue + + dir_path = dirname(realpath(my_file.name)) + file_name = basename(realpath(my_file.name)) + + o_: list[str] = file_name.split(".") + + if args.replace is False: + o_.insert(-1, best_guess.encoding) + if my_file.closed is False: + my_file.close() + elif ( + args.force is False + and query_yes_no( + 'Are you sure to normalize "{}" by replacing it ?'.format( + my_file.name + ), + "no", + ) + is False + ): + if my_file.closed is False: + my_file.close() + continue + + try: + cli_result.unicode_path = join(dir_path, ".".join(o_)) + + with open(cli_result.unicode_path, "wb") as fp: + fp.write(best_guess.output()) + except OSError as e: # Defensive: + print(str(e), file=sys.stderr) + if my_file.closed is False: + my_file.close() + return 2 + + if my_file.closed is False: + my_file.close() + + if args.minimal is False: + print( + dumps( + [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, + ensure_ascii=True, + indent=4, + ) + ) + else: + for my_file in args.files: + print( + ", ".join( + [ + el.encoding or "undefined" + for el in x_ + if el.path == abspath(my_file.name) + ] + ) + ) + + return 0 + + +if __name__ == "__main__": # Defensive: + cli_detect() diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py b/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py new file mode 100644 index 0000000..e1297d2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py @@ -0,0 +1,2050 @@ +from __future__ import annotations + +from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE +from encodings.aliases import aliases +from re import IGNORECASE +from re import compile as re_compile + +# Contain for each eligible encoding a list of/item bytes SIG/BOM +ENCODING_MARKS: dict[str, bytes | list[bytes]] = { + "utf_8": BOM_UTF8, + "utf_7": [ + b"\x2b\x2f\x76\x38\x2d", + b"\x2b\x2f\x76\x38", + b"\x2b\x2f\x76\x39", + b"\x2b\x2f\x76\x2b", + b"\x2b\x2f\x76\x2f", + ], + "gb18030": b"\x84\x31\x95\x33", + "utf_32": [BOM_UTF32_BE, BOM_UTF32_LE], + "utf_16": [BOM_UTF16_BE, BOM_UTF16_LE], +} + +TOO_SMALL_SEQUENCE: int = 32 +TOO_BIG_SEQUENCE: int = int(10e6) + +UTF8_MAXIMAL_ALLOCATION: int = 1_112_064 + +# Up-to-date Unicode ucd/17.0.0 +UNICODE_RANGES_COMBINED: dict[str, range] = { + "Control character": range(32), + "Basic Latin": range(32, 128), + "Latin-1 Supplement": range(128, 256), + "Latin Extended-A": range(256, 384), + "Latin Extended-B": range(384, 592), + "IPA Extensions": range(592, 688), + "Spacing Modifier Letters": range(688, 768), + "Combining Diacritical Marks": range(768, 880), + "Greek and Coptic": range(880, 1024), + "Cyrillic": range(1024, 1280), + "Cyrillic Supplement": range(1280, 1328), + "Armenian": range(1328, 1424), + "Hebrew": range(1424, 1536), + "Arabic": range(1536, 1792), + "Syriac": range(1792, 1872), + "Arabic Supplement": range(1872, 1920), + "Thaana": range(1920, 1984), + "NKo": range(1984, 2048), + "Samaritan": range(2048, 2112), + "Mandaic": range(2112, 2144), + "Syriac Supplement": range(2144, 2160), + "Arabic Extended-B": range(2160, 2208), + "Arabic Extended-A": range(2208, 2304), + "Devanagari": range(2304, 2432), + "Bengali": range(2432, 2560), + "Gurmukhi": range(2560, 2688), + "Gujarati": range(2688, 2816), + "Oriya": range(2816, 2944), + "Tamil": range(2944, 3072), + "Telugu": range(3072, 3200), + "Kannada": range(3200, 3328), + "Malayalam": range(3328, 3456), + "Sinhala": range(3456, 3584), + "Thai": range(3584, 3712), + "Lao": range(3712, 3840), + "Tibetan": range(3840, 4096), + "Myanmar": range(4096, 4256), + "Georgian": range(4256, 4352), + "Hangul Jamo": range(4352, 4608), + "Ethiopic": range(4608, 4992), + "Ethiopic Supplement": range(4992, 5024), + "Cherokee": range(5024, 5120), + "Unified Canadian Aboriginal Syllabics": range(5120, 5760), + "Ogham": range(5760, 5792), + "Runic": range(5792, 5888), + "Tagalog": range(5888, 5920), + "Hanunoo": range(5920, 5952), + "Buhid": range(5952, 5984), + "Tagbanwa": range(5984, 6016), + "Khmer": range(6016, 6144), + "Mongolian": range(6144, 6320), + "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6400), + "Limbu": range(6400, 6480), + "Tai Le": range(6480, 6528), + "New Tai Lue": range(6528, 6624), + "Khmer Symbols": range(6624, 6656), + "Buginese": range(6656, 6688), + "Tai Tham": range(6688, 6832), + "Combining Diacritical Marks Extended": range(6832, 6912), + "Balinese": range(6912, 7040), + "Sundanese": range(7040, 7104), + "Batak": range(7104, 7168), + "Lepcha": range(7168, 7248), + "Ol Chiki": range(7248, 7296), + "Cyrillic Extended-C": range(7296, 7312), + "Georgian Extended": range(7312, 7360), + "Sundanese Supplement": range(7360, 7376), + "Vedic Extensions": range(7376, 7424), + "Phonetic Extensions": range(7424, 7552), + "Phonetic Extensions Supplement": range(7552, 7616), + "Combining Diacritical Marks Supplement": range(7616, 7680), + "Latin Extended Additional": range(7680, 7936), + "Greek Extended": range(7936, 8192), + "General Punctuation": range(8192, 8304), + "Superscripts and Subscripts": range(8304, 8352), + "Currency Symbols": range(8352, 8400), + "Combining Diacritical Marks for Symbols": range(8400, 8448), + "Letterlike Symbols": range(8448, 8528), + "Number Forms": range(8528, 8592), + "Arrows": range(8592, 8704), + "Mathematical Operators": range(8704, 8960), + "Miscellaneous Technical": range(8960, 9216), + "Control Pictures": range(9216, 9280), + "Optical Character Recognition": range(9280, 9312), + "Enclosed Alphanumerics": range(9312, 9472), + "Box Drawing": range(9472, 9600), + "Block Elements": range(9600, 9632), + "Geometric Shapes": range(9632, 9728), + "Miscellaneous Symbols": range(9728, 9984), + "Dingbats": range(9984, 10176), + "Miscellaneous Mathematical Symbols-A": range(10176, 10224), + "Supplemental Arrows-A": range(10224, 10240), + "Braille Patterns": range(10240, 10496), + "Supplemental Arrows-B": range(10496, 10624), + "Miscellaneous Mathematical Symbols-B": range(10624, 10752), + "Supplemental Mathematical Operators": range(10752, 11008), + "Miscellaneous Symbols and Arrows": range(11008, 11264), + "Glagolitic": range(11264, 11360), + "Latin Extended-C": range(11360, 11392), + "Coptic": range(11392, 11520), + "Georgian Supplement": range(11520, 11568), + "Tifinagh": range(11568, 11648), + "Ethiopic Extended": range(11648, 11744), + "Cyrillic Extended-A": range(11744, 11776), + "Supplemental Punctuation": range(11776, 11904), + "CJK Radicals Supplement": range(11904, 12032), + "Kangxi Radicals": range(12032, 12256), + "Ideographic Description Characters": range(12272, 12288), + "CJK Symbols and Punctuation": range(12288, 12352), + "Hiragana": range(12352, 12448), + "Katakana": range(12448, 12544), + "Bopomofo": range(12544, 12592), + "Hangul Compatibility Jamo": range(12592, 12688), + "Kanbun": range(12688, 12704), + "Bopomofo Extended": range(12704, 12736), + "CJK Strokes": range(12736, 12784), + "Katakana Phonetic Extensions": range(12784, 12800), + "Enclosed CJK Letters and Months": range(12800, 13056), + "CJK Compatibility": range(13056, 13312), + "CJK Unified Ideographs Extension A": range(13312, 19904), + "Yijing Hexagram Symbols": range(19904, 19968), + "CJK Unified Ideographs": range(19968, 40960), + "Yi Syllables": range(40960, 42128), + "Yi Radicals": range(42128, 42192), + "Lisu": range(42192, 42240), + "Vai": range(42240, 42560), + "Cyrillic Extended-B": range(42560, 42656), + "Bamum": range(42656, 42752), + "Modifier Tone Letters": range(42752, 42784), + "Latin Extended-D": range(42784, 43008), + "Syloti Nagri": range(43008, 43056), + "Common Indic Number Forms": range(43056, 43072), + "Phags-pa": range(43072, 43136), + "Saurashtra": range(43136, 43232), + "Devanagari Extended": range(43232, 43264), + "Kayah Li": range(43264, 43312), + "Rejang": range(43312, 43360), + "Hangul Jamo Extended-A": range(43360, 43392), + "Javanese": range(43392, 43488), + "Myanmar Extended-B": range(43488, 43520), + "Cham": range(43520, 43616), + "Myanmar Extended-A": range(43616, 43648), + "Tai Viet": range(43648, 43744), + "Meetei Mayek Extensions": range(43744, 43776), + "Ethiopic Extended-A": range(43776, 43824), + "Latin Extended-E": range(43824, 43888), + "Cherokee Supplement": range(43888, 43968), + "Meetei Mayek": range(43968, 44032), + "Hangul Syllables": range(44032, 55216), + "Hangul Jamo Extended-B": range(55216, 55296), + "High Surrogates": range(55296, 56192), + "High Private Use Surrogates": range(56192, 56320), + "Low Surrogates": range(56320, 57344), + "Private Use Area": range(57344, 63744), + "CJK Compatibility Ideographs": range(63744, 64256), + "Alphabetic Presentation Forms": range(64256, 64336), + "Arabic Presentation Forms-A": range(64336, 65024), + "Variation Selectors": range(65024, 65040), + "Vertical Forms": range(65040, 65056), + "Combining Half Marks": range(65056, 65072), + "CJK Compatibility Forms": range(65072, 65104), + "Small Form Variants": range(65104, 65136), + "Arabic Presentation Forms-B": range(65136, 65280), + "Halfwidth and Fullwidth Forms": range(65280, 65520), + "Specials": range(65520, 65536), + "Linear B Syllabary": range(65536, 65664), + "Linear B Ideograms": range(65664, 65792), + "Aegean Numbers": range(65792, 65856), + "Ancient Greek Numbers": range(65856, 65936), + "Ancient Symbols": range(65936, 66000), + "Phaistos Disc": range(66000, 66048), + "Lycian": range(66176, 66208), + "Carian": range(66208, 66272), + "Coptic Epact Numbers": range(66272, 66304), + "Old Italic": range(66304, 66352), + "Gothic": range(66352, 66384), + "Old Permic": range(66384, 66432), + "Ugaritic": range(66432, 66464), + "Old Persian": range(66464, 66528), + "Deseret": range(66560, 66640), + "Shavian": range(66640, 66688), + "Osmanya": range(66688, 66736), + "Osage": range(66736, 66816), + "Elbasan": range(66816, 66864), + "Caucasian Albanian": range(66864, 66928), + "Vithkuqi": range(66928, 67008), + "Todhri": range(67008, 67072), + "Linear A": range(67072, 67456), + "Latin Extended-F": range(67456, 67520), + "Cypriot Syllabary": range(67584, 67648), + "Imperial Aramaic": range(67648, 67680), + "Palmyrene": range(67680, 67712), + "Nabataean": range(67712, 67760), + "Hatran": range(67808, 67840), + "Phoenician": range(67840, 67872), + "Lydian": range(67872, 67904), + "Sidetic": range(67904, 67936), + "Meroitic Hieroglyphs": range(67968, 68000), + "Meroitic Cursive": range(68000, 68096), + "Kharoshthi": range(68096, 68192), + "Old South Arabian": range(68192, 68224), + "Old North Arabian": range(68224, 68256), + "Manichaean": range(68288, 68352), + "Avestan": range(68352, 68416), + "Inscriptional Parthian": range(68416, 68448), + "Inscriptional Pahlavi": range(68448, 68480), + "Psalter Pahlavi": range(68480, 68528), + "Old Turkic": range(68608, 68688), + "Old Hungarian": range(68736, 68864), + "Hanifi Rohingya": range(68864, 68928), + "Garay": range(68928, 69008), + "Rumi Numeral Symbols": range(69216, 69248), + "Yezidi": range(69248, 69312), + "Arabic Extended-C": range(69312, 69376), + "Old Sogdian": range(69376, 69424), + "Sogdian": range(69424, 69488), + "Old Uyghur": range(69488, 69552), + "Chorasmian": range(69552, 69600), + "Elymaic": range(69600, 69632), + "Brahmi": range(69632, 69760), + "Kaithi": range(69760, 69840), + "Sora Sompeng": range(69840, 69888), + "Chakma": range(69888, 69968), + "Mahajani": range(69968, 70016), + "Sharada": range(70016, 70112), + "Sinhala Archaic Numbers": range(70112, 70144), + "Khojki": range(70144, 70224), + "Multani": range(70272, 70320), + "Khudawadi": range(70320, 70400), + "Grantha": range(70400, 70528), + "Tulu-Tigalari": range(70528, 70656), + "Newa": range(70656, 70784), + "Tirhuta": range(70784, 70880), + "Siddham": range(71040, 71168), + "Modi": range(71168, 71264), + "Mongolian Supplement": range(71264, 71296), + "Takri": range(71296, 71376), + "Myanmar Extended-C": range(71376, 71424), + "Ahom": range(71424, 71504), + "Dogra": range(71680, 71760), + "Warang Citi": range(71840, 71936), + "Dives Akuru": range(71936, 72032), + "Nandinagari": range(72096, 72192), + "Zanabazar Square": range(72192, 72272), + "Soyombo": range(72272, 72368), + "Unified Canadian Aboriginal Syllabics Extended-A": range(72368, 72384), + "Pau Cin Hau": range(72384, 72448), + "Devanagari Extended-A": range(72448, 72544), + "Sharada Supplement": range(72544, 72576), + "Sunuwar": range(72640, 72704), + "Bhaiksuki": range(72704, 72816), + "Marchen": range(72816, 72896), + "Masaram Gondi": range(72960, 73056), + "Gunjala Gondi": range(73056, 73136), + "Tolong Siki": range(73136, 73200), + "Makasar": range(73440, 73472), + "Kawi": range(73472, 73568), + "Lisu Supplement": range(73648, 73664), + "Tamil Supplement": range(73664, 73728), + "Cuneiform": range(73728, 74752), + "Cuneiform Numbers and Punctuation": range(74752, 74880), + "Early Dynastic Cuneiform": range(74880, 75088), + "Cypro-Minoan": range(77712, 77824), + "Egyptian Hieroglyphs": range(77824, 78896), + "Egyptian Hieroglyph Format Controls": range(78896, 78944), + "Egyptian Hieroglyphs Extended-A": range(78944, 82944), + "Anatolian Hieroglyphs": range(82944, 83584), + "Gurung Khema": range(90368, 90432), + "Bamum Supplement": range(92160, 92736), + "Mro": range(92736, 92784), + "Tangsa": range(92784, 92880), + "Bassa Vah": range(92880, 92928), + "Pahawh Hmong": range(92928, 93072), + "Kirat Rai": range(93504, 93568), + "Medefaidrin": range(93760, 93856), + "Beria Erfe": range(93856, 93920), + "Miao": range(93952, 94112), + "Ideographic Symbols and Punctuation": range(94176, 94208), + "Tangut": range(94208, 100352), + "Tangut Components": range(100352, 101120), + "Khitan Small Script": range(101120, 101632), + "Tangut Supplement": range(101632, 101760), + "Tangut Components Supplement": range(101760, 101888), + "Kana Extended-B": range(110576, 110592), + "Kana Supplement": range(110592, 110848), + "Kana Extended-A": range(110848, 110896), + "Small Kana Extension": range(110896, 110960), + "Nushu": range(110960, 111360), + "Duployan": range(113664, 113824), + "Shorthand Format Controls": range(113824, 113840), + "Symbols for Legacy Computing Supplement": range(117760, 118464), + "Miscellaneous Symbols Supplement": range(118464, 118528), + "Znamenny Musical Notation": range(118528, 118736), + "Byzantine Musical Symbols": range(118784, 119040), + "Musical Symbols": range(119040, 119296), + "Ancient Greek Musical Notation": range(119296, 119376), + "Kaktovik Numerals": range(119488, 119520), + "Mayan Numerals": range(119520, 119552), + "Tai Xuan Jing Symbols": range(119552, 119648), + "Counting Rod Numerals": range(119648, 119680), + "Mathematical Alphanumeric Symbols": range(119808, 120832), + "Sutton SignWriting": range(120832, 121520), + "Latin Extended-G": range(122624, 122880), + "Glagolitic Supplement": range(122880, 122928), + "Cyrillic Extended-D": range(122928, 123024), + "Nyiakeng Puachue Hmong": range(123136, 123216), + "Toto": range(123536, 123584), + "Wancho": range(123584, 123648), + "Nag Mundari": range(124112, 124160), + "Ol Onal": range(124368, 124416), + "Tai Yo": range(124608, 124672), + "Ethiopic Extended-B": range(124896, 124928), + "Mende Kikakui": range(124928, 125152), + "Adlam": range(125184, 125280), + "Indic Siyaq Numbers": range(126064, 126144), + "Ottoman Siyaq Numbers": range(126208, 126288), + "Arabic Mathematical Alphabetic Symbols": range(126464, 126720), + "Mahjong Tiles": range(126976, 127024), + "Domino Tiles": range(127024, 127136), + "Playing Cards": range(127136, 127232), + "Enclosed Alphanumeric Supplement": range(127232, 127488), + "Enclosed Ideographic Supplement": range(127488, 127744), + "Miscellaneous Symbols and Pictographs": range(127744, 128512), + "Emoticons": range(128512, 128592), + "Ornamental Dingbats": range(128592, 128640), + "Transport and Map Symbols": range(128640, 128768), + "Alchemical Symbols": range(128768, 128896), + "Geometric Shapes Extended": range(128896, 129024), + "Supplemental Arrows-C": range(129024, 129280), + "Supplemental Symbols and Pictographs": range(129280, 129536), + "Chess Symbols": range(129536, 129648), + "Symbols and Pictographs Extended-A": range(129648, 129792), + "Symbols for Legacy Computing": range(129792, 130048), + "CJK Unified Ideographs Extension B": range(131072, 173792), + "CJK Unified Ideographs Extension C": range(173824, 177984), + "CJK Unified Ideographs Extension D": range(177984, 178208), + "CJK Unified Ideographs Extension E": range(178208, 183984), + "CJK Unified Ideographs Extension F": range(183984, 191472), + "CJK Unified Ideographs Extension I": range(191472, 192096), + "CJK Compatibility Ideographs Supplement": range(194560, 195104), + "CJK Unified Ideographs Extension G": range(196608, 201552), + "CJK Unified Ideographs Extension H": range(201552, 205744), + "CJK Unified Ideographs Extension J": range(205744, 210048), + "Tags": range(917504, 917632), + "Variation Selectors Supplement": range(917760, 918000), + "Supplementary Private Use Area-A": range(983040, 1048576), + "Supplementary Private Use Area-B": range(1048576, 1114112), +} + + +UNICODE_SECONDARY_RANGE_KEYWORD: list[str] = [ + "Supplement", + "Extended", + "Extensions", + "Modifier", + "Marks", + "Punctuation", + "Symbols", + "Forms", + "Operators", + "Miscellaneous", + "Drawing", + "Block", + "Shapes", + "Supplemental", + "Tags", +] + +RE_POSSIBLE_ENCODING_INDICATION = re_compile( + r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", + IGNORECASE, +) + +IANA_NO_ALIASES = [ + "cp720", + "cp737", + "cp856", + "cp874", + "cp875", + "cp1006", + "koi8_r", + "koi8_t", + "koi8_u", +] + +IANA_SUPPORTED: list[str] = sorted( + filter( + lambda x: x.endswith("_codec") is False + and x not in {"rot_13", "tactis", "mbcs"}, + list(set(aliases.values())) + IANA_NO_ALIASES, + ) +) + +IANA_SUPPORTED_COUNT: int = len(IANA_SUPPORTED) + +# pre-computed code page that are similar using the function cp_similarity. +IANA_SUPPORTED_SIMILAR: dict[str, list[str]] = { + "cp037": ["cp1026", "cp1140", "cp273", "cp500"], + "cp1026": ["cp037", "cp1140", "cp273", "cp500"], + "cp1125": ["cp866"], + "cp1140": ["cp037", "cp1026", "cp273", "cp500"], + "cp1250": ["iso8859_2"], + "cp1251": ["kz1048", "ptcp154"], + "cp1252": ["iso8859_15", "iso8859_9", "latin_1"], + "cp1253": ["iso8859_7"], + "cp1254": ["iso8859_15", "iso8859_9", "latin_1"], + "cp1257": ["iso8859_13"], + "cp273": ["cp037", "cp1026", "cp1140", "cp500"], + "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], + "cp500": ["cp037", "cp1026", "cp1140", "cp273"], + "cp850": ["cp437", "cp857", "cp858", "cp865"], + "cp857": ["cp850", "cp858", "cp865"], + "cp858": ["cp437", "cp850", "cp857", "cp865"], + "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], + "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], + "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], + "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], + "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], + "cp866": ["cp1125"], + "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], + "iso8859_11": ["tis_620"], + "iso8859_13": ["cp1257"], + "iso8859_14": [ + "iso8859_10", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_15": [ + "cp1252", + "cp1254", + "iso8859_10", + "iso8859_14", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_16": [ + "iso8859_14", + "iso8859_15", + "iso8859_2", + "iso8859_3", + "iso8859_9", + "latin_1", + ], + "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], + "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], + "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], + "iso8859_7": ["cp1253"], + "iso8859_9": [ + "cp1252", + "cp1254", + "cp1258", + "iso8859_10", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_4", + "latin_1", + ], + "kz1048": ["cp1251", "ptcp154"], + "latin_1": [ + "cp1252", + "cp1254", + "cp1258", + "iso8859_10", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_4", + "iso8859_9", + ], + "mac_iceland": ["mac_roman", "mac_turkish"], + "mac_roman": ["mac_iceland", "mac_turkish"], + "mac_turkish": ["mac_iceland", "mac_roman"], + "ptcp154": ["cp1251", "kz1048"], + "tis_620": ["iso8859_11"], +} + + +CHARDET_CORRESPONDENCE: dict[str, str] = { + "iso2022_kr": "ISO-2022-KR", + "iso2022_jp": "ISO-2022-JP", + "euc_kr": "EUC-KR", + "tis_620": "TIS-620", + "utf_32": "UTF-32", + "euc_jp": "EUC-JP", + "koi8_r": "KOI8-R", + "iso8859_1": "ISO-8859-1", + "iso8859_2": "ISO-8859-2", + "iso8859_5": "ISO-8859-5", + "iso8859_6": "ISO-8859-6", + "iso8859_7": "ISO-8859-7", + "iso8859_8": "ISO-8859-8", + "utf_16": "UTF-16", + "cp855": "IBM855", + "mac_cyrillic": "MacCyrillic", + "gb2312": "GB2312", + "gb18030": "GB18030", + "cp932": "CP932", + "cp866": "IBM866", + "utf_8": "utf-8", + "utf_8_sig": "UTF-8-SIG", + "shift_jis": "SHIFT_JIS", + "big5": "Big5", + "cp1250": "windows-1250", + "cp1251": "windows-1251", + "cp1252": "Windows-1252", + "cp1253": "windows-1253", + "cp1255": "windows-1255", + "cp1256": "windows-1256", + "cp1254": "Windows-1254", + "cp949": "CP949", +} + + +COMMON_SAFE_ASCII_CHARACTERS: frozenset[str] = frozenset( + { + "<", + ">", + "=", + ":", + "/", + "&", + ";", + "{", + "}", + "[", + "]", + ",", + "|", + '"', + "-", + "(", + ")", + } +) + +# Sample character sets — replace with full lists if needed +COMMON_CHINESE_CHARACTERS = "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞" + +COMMON_JAPANESE_CHARACTERS = "日一国年大十二本中長出三時行見月分後前生五間上東四今金九入学高円子外八六下来気小七山話女北午百書先名川千水半男西電校語土木聞食車何南万毎白天母火右読友左休父雨" + +COMMON_KOREAN_CHARACTERS = "一二三四五六七八九十百千萬上下左右中人女子大小山川日月火水木金土父母天地國名年時文校學生" + +# Combine all into a frozenset +COMMON_CJK_CHARACTERS = frozenset( + "".join( + [ + COMMON_CHINESE_CHARACTERS, + COMMON_JAPANESE_CHARACTERS, + COMMON_KOREAN_CHARACTERS, + ] + ) +) + +KO_NAMES: frozenset[str] = frozenset({"johab", "cp949", "euc_kr"}) +ZH_NAMES: frozenset[str] = frozenset({"big5", "cp950", "big5hkscs", "hz"}) + +# Logging LEVEL below DEBUG +TRACE: int = 5 + + +# Language label that contain the em dash "—" +# character are to be considered alternative seq to origin +FREQUENCIES: dict[str, list[str]] = { + "English": [ + "e", + "a", + "t", + "i", + "o", + "n", + "s", + "r", + "h", + "l", + "d", + "c", + "u", + "m", + "f", + "p", + "g", + "w", + "y", + "b", + "v", + "k", + "x", + "j", + "z", + "q", + ], + "English—": [ + "e", + "a", + "t", + "i", + "o", + "n", + "s", + "r", + "h", + "l", + "d", + "c", + "m", + "u", + "f", + "p", + "g", + "w", + "b", + "y", + "v", + "k", + "j", + "x", + "z", + "q", + ], + "German": [ + "e", + "n", + "i", + "r", + "s", + "t", + "a", + "d", + "h", + "u", + "l", + "g", + "o", + "c", + "m", + "b", + "f", + "k", + "w", + "z", + "p", + "v", + "ü", + "ä", + "ö", + "j", + ], + "French": [ + "e", + "a", + "s", + "n", + "i", + "t", + "r", + "l", + "u", + "o", + "d", + "c", + "p", + "m", + "é", + "v", + "g", + "f", + "b", + "h", + "q", + "à", + "x", + "è", + "y", + "j", + ], + "Dutch": [ + "e", + "n", + "a", + "i", + "r", + "t", + "o", + "d", + "s", + "l", + "g", + "h", + "v", + "m", + "u", + "k", + "c", + "p", + "b", + "w", + "j", + "z", + "f", + "y", + "x", + "ë", + ], + "Italian": [ + "e", + "i", + "a", + "o", + "n", + "l", + "t", + "r", + "s", + "c", + "d", + "u", + "p", + "m", + "g", + "v", + "f", + "b", + "z", + "h", + "q", + "è", + "à", + "k", + "y", + "ò", + ], + "Polish": [ + "a", + "i", + "o", + "e", + "n", + "r", + "z", + "w", + "s", + "c", + "t", + "k", + "y", + "d", + "p", + "m", + "u", + "l", + "j", + "ł", + "g", + "b", + "h", + "ą", + "ę", + "ó", + ], + "Spanish": [ + "e", + "a", + "o", + "n", + "s", + "r", + "i", + "l", + "d", + "t", + "c", + "u", + "m", + "p", + "b", + "g", + "v", + "f", + "y", + "ó", + "h", + "q", + "í", + "j", + "z", + "á", + ], + "Russian": [ + "о", + "е", + "а", + "и", + "н", + "т", + "с", + "р", + "в", + "л", + "к", + "м", + "д", + "п", + "у", + "г", + "я", + "ы", + "з", + "б", + "й", + "ь", + "ч", + "х", + "ж", + "ц", + ], + # Jap-Kanji + "Japanese": [ + "日", + "一", + "人", + "年", + "大", + "十", + "二", + "本", + "中", + "長", + "出", + "三", + "時", + "行", + "見", + "月", + "分", + "後", + "前", + "生", + "五", + "間", + "上", + "東", + "四", + "今", + "金", + "九", + "入", + "学", + "高", + "円", + "子", + "外", + "八", + "六", + "下", + "来", + "気", + "小", + "七", + "山", + "話", + "女", + "北", + "午", + "百", + "書", + "先", + "名", + "川", + "千", + "水", + "半", + "男", + "西", + "電", + "校", + "語", + "土", + "木", + "聞", + "食", + "車", + "何", + "南", + "万", + "毎", + "白", + "天", + "母", + "火", + "右", + "読", + "友", + "左", + "休", + "父", + "雨", + ], + # Jap-Katakana + "Japanese—": [ + "ー", + "ン", + "ス", + "・", + "ル", + "ト", + "リ", + "イ", + "ア", + "ラ", + "ッ", + "ク", + "ド", + "シ", + "レ", + "ジ", + "タ", + "フ", + "ロ", + "カ", + "テ", + "マ", + "ィ", + "グ", + "バ", + "ム", + "プ", + "オ", + "コ", + "デ", + "ニ", + "ウ", + "メ", + "サ", + "ビ", + "ナ", + "ブ", + "ャ", + "エ", + "ュ", + "チ", + "キ", + "ズ", + "ダ", + "パ", + "ミ", + "ェ", + "ョ", + "ハ", + "セ", + "ベ", + "ガ", + "モ", + "ツ", + "ネ", + "ボ", + "ソ", + "ノ", + "ァ", + "ヴ", + "ワ", + "ポ", + "ペ", + "ピ", + "ケ", + "ゴ", + "ギ", + "ザ", + "ホ", + "ゲ", + "ォ", + "ヤ", + "ヒ", + "ユ", + "ヨ", + "ヘ", + "ゼ", + "ヌ", + "ゥ", + "ゾ", + "ヶ", + "ヂ", + "ヲ", + "ヅ", + "ヵ", + "ヱ", + "ヰ", + "ヮ", + "ヽ", + "゠", + "ヾ", + "ヷ", + "ヿ", + "ヸ", + "ヹ", + "ヺ", + ], + # Jap-Hiragana + "Japanese——": [ + "の", + "に", + "る", + "た", + "と", + "は", + "し", + "い", + "を", + "で", + "て", + "が", + "な", + "れ", + "か", + "ら", + "さ", + "っ", + "り", + "す", + "あ", + "も", + "こ", + "ま", + "う", + "く", + "よ", + "き", + "ん", + "め", + "お", + "け", + "そ", + "つ", + "だ", + "や", + "え", + "ど", + "わ", + "ち", + "み", + "せ", + "じ", + "ば", + "へ", + "び", + "ず", + "ろ", + "ほ", + "げ", + "む", + "べ", + "ひ", + "ょ", + "ゆ", + "ぶ", + "ご", + "ゃ", + "ね", + "ふ", + "ぐ", + "ぎ", + "ぼ", + "ゅ", + "づ", + "ざ", + "ぞ", + "ぬ", + "ぜ", + "ぱ", + "ぽ", + "ぷ", + "ぴ", + "ぃ", + "ぁ", + "ぇ", + "ぺ", + "ゞ", + "ぢ", + "ぉ", + "ぅ", + "ゐ", + "ゝ", + "ゑ", + "゛", + "゜", + "ゎ", + "ゔ", + "゚", + "ゟ", + "゙", + "ゕ", + "ゖ", + ], + "Portuguese": [ + "a", + "e", + "o", + "s", + "i", + "r", + "d", + "n", + "t", + "m", + "u", + "c", + "l", + "p", + "g", + "v", + "b", + "f", + "h", + "ã", + "q", + "é", + "ç", + "á", + "z", + "í", + ], + "Swedish": [ + "e", + "a", + "n", + "r", + "t", + "s", + "i", + "l", + "d", + "o", + "m", + "k", + "g", + "v", + "h", + "f", + "u", + "p", + "ä", + "c", + "b", + "ö", + "å", + "y", + "j", + "x", + ], + "Chinese": [ + "的", + "一", + "是", + "不", + "了", + "在", + "人", + "有", + "我", + "他", + "这", + "个", + "们", + "中", + "来", + "上", + "大", + "为", + "和", + "国", + "地", + "到", + "以", + "说", + "时", + "要", + "就", + "出", + "会", + "可", + "也", + "你", + "对", + "生", + "能", + "而", + "子", + "那", + "得", + "于", + "着", + "下", + "自", + "之", + "年", + "过", + "发", + "后", + "作", + "里", + "用", + "道", + "行", + "所", + "然", + "家", + "种", + "事", + "成", + "方", + "多", + "经", + "么", + "去", + "法", + "学", + "如", + "都", + "同", + "现", + "当", + "没", + "动", + "面", + "起", + "看", + "定", + "天", + "分", + "还", + "进", + "好", + "小", + "部", + "其", + "些", + "主", + "样", + "理", + "心", + "她", + "本", + "前", + "开", + "但", + "因", + "只", + "从", + "想", + "实", + ], + "Ukrainian": [ + "о", + "а", + "н", + "і", + "и", + "р", + "в", + "т", + "е", + "с", + "к", + "л", + "у", + "д", + "м", + "п", + "з", + "я", + "ь", + "б", + "г", + "й", + "ч", + "х", + "ц", + "ї", + ], + "Norwegian": [ + "e", + "r", + "n", + "t", + "a", + "s", + "i", + "o", + "l", + "d", + "g", + "k", + "m", + "v", + "f", + "p", + "u", + "b", + "h", + "å", + "y", + "j", + "ø", + "c", + "æ", + "w", + ], + "Finnish": [ + "a", + "i", + "n", + "t", + "e", + "s", + "l", + "o", + "u", + "k", + "ä", + "m", + "r", + "v", + "j", + "h", + "p", + "y", + "d", + "ö", + "g", + "c", + "b", + "f", + "w", + "z", + ], + "Vietnamese": [ + "n", + "h", + "t", + "i", + "c", + "g", + "a", + "o", + "u", + "m", + "l", + "r", + "à", + "đ", + "s", + "e", + "v", + "p", + "b", + "y", + "ư", + "d", + "á", + "k", + "ộ", + "ế", + ], + "Czech": [ + "o", + "e", + "a", + "n", + "t", + "s", + "i", + "l", + "v", + "r", + "k", + "d", + "u", + "m", + "p", + "í", + "c", + "h", + "z", + "á", + "y", + "j", + "b", + "ě", + "é", + "ř", + ], + "Hungarian": [ + "e", + "a", + "t", + "l", + "s", + "n", + "k", + "r", + "i", + "o", + "z", + "á", + "é", + "g", + "m", + "b", + "y", + "v", + "d", + "h", + "u", + "p", + "j", + "ö", + "f", + "c", + ], + "Korean": [ + "이", + "다", + "에", + "의", + "는", + "로", + "하", + "을", + "가", + "고", + "지", + "서", + "한", + "은", + "기", + "으", + "년", + "대", + "사", + "시", + "를", + "리", + "도", + "인", + "스", + "일", + ], + "Indonesian": [ + "a", + "n", + "e", + "i", + "r", + "t", + "u", + "s", + "d", + "k", + "m", + "l", + "g", + "p", + "b", + "o", + "h", + "y", + "j", + "c", + "w", + "f", + "v", + "z", + "x", + "q", + ], + "Turkish": [ + "a", + "e", + "i", + "n", + "r", + "l", + "ı", + "k", + "d", + "t", + "s", + "m", + "y", + "u", + "o", + "b", + "ü", + "ş", + "v", + "g", + "z", + "h", + "c", + "p", + "ç", + "ğ", + ], + "Romanian": [ + "e", + "i", + "a", + "r", + "n", + "t", + "u", + "l", + "o", + "c", + "s", + "d", + "p", + "m", + "ă", + "f", + "v", + "î", + "g", + "b", + "ș", + "ț", + "z", + "h", + "â", + "j", + ], + "Farsi": [ + "ا", + "ی", + "ر", + "د", + "ن", + "ه", + "و", + "م", + "ت", + "ب", + "س", + "ل", + "ک", + "ش", + "ز", + "ف", + "گ", + "ع", + "خ", + "ق", + "ج", + "آ", + "پ", + "ح", + "ط", + "ص", + ], + "Arabic": [ + "ا", + "ل", + "ي", + "م", + "و", + "ن", + "ر", + "ت", + "ب", + "ة", + "ع", + "د", + "س", + "ف", + "ه", + "ك", + "ق", + "أ", + "ح", + "ج", + "ش", + "ط", + "ص", + "ى", + "خ", + "إ", + ], + "Danish": [ + "e", + "r", + "n", + "t", + "a", + "i", + "s", + "d", + "l", + "o", + "g", + "m", + "k", + "f", + "v", + "u", + "b", + "h", + "p", + "å", + "y", + "ø", + "æ", + "c", + "j", + "w", + ], + "Serbian": [ + "а", + "и", + "о", + "е", + "н", + "р", + "с", + "у", + "т", + "к", + "ј", + "в", + "д", + "м", + "п", + "л", + "г", + "з", + "б", + "a", + "i", + "e", + "o", + "n", + "ц", + "ш", + ], + "Lithuanian": [ + "i", + "a", + "s", + "o", + "r", + "e", + "t", + "n", + "u", + "k", + "m", + "l", + "p", + "v", + "d", + "j", + "g", + "ė", + "b", + "y", + "ų", + "š", + "ž", + "c", + "ą", + "į", + ], + "Slovene": [ + "e", + "a", + "i", + "o", + "n", + "r", + "s", + "l", + "t", + "j", + "v", + "k", + "d", + "p", + "m", + "u", + "z", + "b", + "g", + "h", + "č", + "c", + "š", + "ž", + "f", + "y", + ], + "Slovak": [ + "o", + "a", + "e", + "n", + "i", + "r", + "v", + "t", + "s", + "l", + "k", + "d", + "m", + "p", + "u", + "c", + "h", + "j", + "b", + "z", + "á", + "y", + "ý", + "í", + "č", + "é", + ], + "Hebrew": [ + "י", + "ו", + "ה", + "ל", + "ר", + "ב", + "ת", + "מ", + "א", + "ש", + "נ", + "ע", + "ם", + "ד", + "ק", + "ח", + "פ", + "ס", + "כ", + "ג", + "ט", + "צ", + "ן", + "ז", + "ך", + ], + "Bulgarian": [ + "а", + "и", + "о", + "е", + "н", + "т", + "р", + "с", + "в", + "л", + "к", + "д", + "п", + "м", + "з", + "г", + "я", + "ъ", + "у", + "б", + "ч", + "ц", + "й", + "ж", + "щ", + "х", + ], + "Croatian": [ + "a", + "i", + "o", + "e", + "n", + "r", + "j", + "s", + "t", + "u", + "k", + "l", + "v", + "d", + "m", + "p", + "g", + "z", + "b", + "c", + "č", + "h", + "š", + "ž", + "ć", + "f", + ], + "Hindi": [ + "क", + "र", + "स", + "न", + "त", + "म", + "ह", + "प", + "य", + "ल", + "व", + "ज", + "द", + "ग", + "ब", + "श", + "ट", + "अ", + "ए", + "थ", + "भ", + "ड", + "च", + "ध", + "ष", + "इ", + ], + "Estonian": [ + "a", + "i", + "e", + "s", + "t", + "l", + "u", + "n", + "o", + "k", + "r", + "d", + "m", + "v", + "g", + "p", + "j", + "h", + "ä", + "b", + "õ", + "ü", + "f", + "c", + "ö", + "y", + ], + "Thai": [ + "า", + "น", + "ร", + "อ", + "ก", + "เ", + "ง", + "ม", + "ย", + "ล", + "ว", + "ด", + "ท", + "ส", + "ต", + "ะ", + "ป", + "บ", + "ค", + "ห", + "แ", + "จ", + "พ", + "ช", + "ข", + "ใ", + ], + "Greek": [ + "α", + "τ", + "ο", + "ι", + "ε", + "ν", + "ρ", + "σ", + "κ", + "η", + "π", + "ς", + "υ", + "μ", + "λ", + "ί", + "ό", + "ά", + "γ", + "έ", + "δ", + "ή", + "ω", + "χ", + "θ", + "ύ", + ], + "Tamil": [ + "க", + "த", + "ப", + "ட", + "ர", + "ம", + "ல", + "ன", + "வ", + "ற", + "ய", + "ள", + "ச", + "ந", + "இ", + "ண", + "அ", + "ஆ", + "ழ", + "ங", + "எ", + "உ", + "ஒ", + "ஸ", + ], + "Kazakh": [ + "а", + "ы", + "е", + "н", + "т", + "р", + "л", + "і", + "д", + "с", + "м", + "қ", + "к", + "о", + "б", + "и", + "у", + "ғ", + "ж", + "ң", + "з", + "ш", + "й", + "п", + "г", + "ө", + ], +} + +LANGUAGE_SUPPORTED_COUNT: int = len(FREQUENCIES) + +# Bit flags for unified character classification. +# A single unicodedata.name() call sets all relevant flags at once. +_LATIN: int = 1 +_ACCENTUATED: int = 1 << 1 +_CJK: int = 1 << 2 +_HANGUL: int = 1 << 3 +_KATAKANA: int = 1 << 4 +_HIRAGANA: int = 1 << 5 +_THAI: int = 1 << 6 +_ARABIC: int = 1 << 7 +_ARABIC_ISOLATED_FORM: int = 1 << 8 + +_ACCENT_KEYWORDS: tuple[str, ...] = ( + "WITH GRAVE", + "WITH ACUTE", + "WITH CEDILLA", + "WITH DIAERESIS", + "WITH CIRCUMFLEX", + "WITH TILDE", + "WITH MACRON", + "WITH RING ABOVE", +) + +# Pre-built lookup structures for FREQUENCIES (computed once at import time). +# character -> rank mapping per language (replaces list .index() calls). +_FREQUENCIES_RANK: dict[str, dict[str, int]] = { + lang: {char: rank for rank, char in enumerate(chars)} + for lang, chars in FREQUENCIES.items() +} + +# frozenset per language (avoids rebuilding set() per call). +_FREQUENCIES_SET: dict[str, frozenset[str]] = { + lang: frozenset(chars) for lang, chars in FREQUENCIES.items() +} diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py b/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py new file mode 100644 index 0000000..293c1ef --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any +from warnings import warn + +from .api import from_bytes +from .constant import CHARDET_CORRESPONDENCE, TOO_SMALL_SEQUENCE + +if TYPE_CHECKING: + from typing import TypedDict + + class ResultDict(TypedDict): + encoding: str | None + language: str + confidence: float | None + + +def detect( + byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any +) -> ResultDict: + """ + chardet legacy method + Detect the encoding of the given byte string. It should be mostly backward-compatible. + Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) + This function is deprecated and should be used to migrate your project easily, consult the documentation for + further information. Not planned for removal. + + :param byte_str: The byte sequence to examine. + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + """ + if len(kwargs): + warn( + f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" + ) + + if not isinstance(byte_str, (bytearray, bytes)): + raise TypeError( # pragma: nocover + f"Expected object of type bytes or bytearray, got: {type(byte_str)}" + ) + + if isinstance(byte_str, bytearray): + byte_str = bytes(byte_str) + + r = from_bytes(byte_str).best() + + encoding = r.encoding if r is not None else None + language = r.language if r is not None and r.language != "Unknown" else "" + confidence = 1.0 - r.chaos if r is not None else None + + # automatically lower confidence + # on small bytes samples. + # https://github.com/jawah/charset_normalizer/issues/391 + if ( + confidence is not None + and confidence >= 0.9 + and encoding + not in { + "utf_8", + "ascii", + } + and r.bom is False # type: ignore[union-attr] + and len(byte_str) < TOO_SMALL_SEQUENCE + ): + confidence -= 0.2 + + # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process + # but chardet does return 'utf-8-sig' and it is a valid codec name. + if r is not None and encoding == "utf_8" and r.bom: + encoding += "_sig" + + if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: + encoding = CHARDET_CORRESPONDENCE[encoding] + + return { + "encoding": encoding, + "language": language, + "confidence": confidence, + } diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..87d31c7e9c670f6288a511726c3d3a4c3cd8d58b GIT binary patch literal 15912 zcmeHOeQX?85r1dL^zwo2LYx!Idhd4ayXroy zyS>yGg%Zh0g-Ih>k&p^PB2*EIDyV=z3Ierq>d;gb5~f0se-H;K=uAnQss*(Oe9XKz zb6ziZY5qV6!9MGDW`6VWW@q1?-`>9WrGb&bI-gH)s~3-mbxDW_DLOvI7KrtT&2Zc) z+NABKb=ALcw;B}0R5U9Y`;aP<8E4v3r|g(Aj()PgfYJ*W4N>w;oqitzmzBNBZ{@&7 zIz|1(iLxJx=vN}Y68SMLQh!YSDy+;A^X#E`ZrV}{kv|ns25!psOxf>I z@WVXsv~k(TslQrv2~qw`>2&4!-2Dz%Z&4 zze6Q7zJK|Ci%@;9)BVY~AiU_pS$_4Piicwp#|XC-2hR5x>aIKRyVw|L(ScX`MtuYi zLZB_(N>cVmB#}7YL)@ApibMO#m;k=jf#dzeEf7&0+B@(64hOy=A7$BiaTJQ$T6%L0&It0J*3Nf752&_qOlKpTKBR*g=f9 z9WGGovE$|9qPFnc(wyw&X8tOo;iY58m#2eGyUt*lFpjp+-|jCRmBD~}~_#df2PuT42+7~rDW8FR7 z(a>~PPiI)yQ-#?=Ovtx-vAB`VS+<_eWK;5OVpa#e$)|rC|KJDt``YRs3P2$Iap2Nh zi|}hhJ77EEGX;LLXwcX^*x35n zrmau-Pm6;)9{R+-_WKd;f=@qu;t=-|0=oCbpERvp|BXON1wtE#3Q`9lzKiZ@e zoabe-<29GuAG|e{`9DMU@CI1QTvvE(l=73Lf1hN|lfwST2(MRncetMx*bWl?Jke>Q zCx|{pbf~ZIGl33R@x3Py?t(?U;Xr4ovpWE4jv<|qMO#`pNzD=AvvEf|XZj&?T+CqJ~ zuLsLGjy8$6@zwl>hWBM1jD9{ZR>G12i7!##jDMVP{ytzFw{!T!b`gpwgvV{4#P1e- z-zu#Tj{}4KR=!_IaYEWRSJq>*{a1mhmVd4D`nrtMBp$$oU9DQ2%V1Ln-%>mV*yVX( ze6XmV*LO4i6N!IRxbK6j(x9=j--7M&d(bCZD)nLf4}|mg4CC(uQ-`}6T=mBb7+@m@ zZG%F%Or^H72L1@()#8lSu#W*=ZQntPaQD80FV={20q|=5eg|;*1DoTVBpb`xIXe$a zOk!ev+k$SV^cZej$ie;wGo#NWGf^X{n|3Cf(~bO`h-FftKMoM=ykOU%kt2tCNAx3ugGUD@^oia>BLff+f9<^Oc8wal zESwuET$?{??baAx-_0R6Uhw{l>Kit22gx1m5{YcsnGwttQnnEl!JM5{G|pT)V_U(Q zbUql(Cz9sggejyHH*#?iGz)3)RMgHY%?T@;OJvd;1RZo)D`}tuakEKV$lr`b(6;7) zmzOA*&6tL5h@cgxiySw>mnG$)28Qxr9=)&&2q4ojQi&L(kb%H(2+H1~xts{X*h|5X zQt8Qa3L3BpCK1isFkb&n4d+KV$KZKMt^Hu!9Zefp6hYwr3UdhdpCX272U+s`0{_oB zTMr00XJUVzS26X&{frI!MV9?}z7qiqW3xZc)0pzS3`}IBa4ZT9IGiJ~KhKMpZYN95 zpY539J}R7ZvCQ)>rUCMI=TEROfk7Sn^E{8~WisUc-RGYs|3hTQ^G2qN4*wS5-Trfc zVNG~mTlw;*e*!#zrZ6&B=8@t+bovL#kLj$_7 ziIpSw&%krQ;2ev`2S4}lzDc(|%S>MY-zJAY&#$iU1rQqBDQR71{AEyJ{@g#$Z;Bt_ z&+&c^430ha=XntCYb|1MXk0h$m&d_%&^i0(eRHoqDBVOrk;;+%nf?YePJf<1mB?SK zcq)ednEnx9^tGlfSX zrYK;54HaCeBZ)E&k6p%)o(?N%g_QW*jL)ik-u(TG&!2qm%0nH;@C7HdYp(uXqMZI$ P^S^h$^55ceaEaod10Yub literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/md.py b/.venv/lib/python3.12/site-packages/charset_normalizer/md.py new file mode 100644 index 0000000..b41d9cf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/md.py @@ -0,0 +1,936 @@ +from __future__ import annotations + +import sys +from functools import lru_cache +from logging import getLogger + +if sys.version_info >= (3, 8): + from typing import final +else: + try: + from typing_extensions import final + except ImportError: + + def final(cls): # type: ignore[misc,no-untyped-def] + return cls + + +from .constant import ( + COMMON_CJK_CHARACTERS, + COMMON_SAFE_ASCII_CHARACTERS, + TRACE, + UNICODE_SECONDARY_RANGE_KEYWORD, + _ACCENTUATED, + _ARABIC, + _ARABIC_ISOLATED_FORM, + _CJK, + _HANGUL, + _HIRAGANA, + _KATAKANA, + _LATIN, + _THAI, +) +from .utils import ( + _character_flags, + is_emoticon, + is_punctuation, + is_separator, + is_symbol, + remove_accent, + unicode_range, +) + +# Combined bitmask for CJK/Hangul/Katakana/Hiragana/Thai glyph detection. +_GLYPH_MASK: int = _CJK | _HANGUL | _KATAKANA | _HIRAGANA | _THAI + + +@final +class CharInfo: + """Pre-computed character properties shared across all detectors. + + Instantiated once and reused via :meth:`update` on every character + in the hot loop so that redundant calls to str methods + (``isalpha``, ``isupper``, …) and cached utility functions + (``_character_flags``, ``is_punctuation``, …) are avoided when + several plugins need the same information. + """ + + __slots__ = ( + "character", + "printable", + "alpha", + "upper", + "lower", + "space", + "digit", + "is_ascii", + "case_variable", + "flags", + "accentuated", + "latin", + "is_cjk", + "is_arabic", + "is_glyph", + "punct", + "sym", + ) + + def __init__(self) -> None: + self.character: str = "" + self.printable: bool = False + self.alpha: bool = False + self.upper: bool = False + self.lower: bool = False + self.space: bool = False + self.digit: bool = False + self.is_ascii: bool = False + self.case_variable: bool = False + self.flags: int = 0 + self.accentuated: bool = False + self.latin: bool = False + self.is_cjk: bool = False + self.is_arabic: bool = False + self.is_glyph: bool = False + self.punct: bool = False + self.sym: bool = False + + def update(self, character: str) -> None: + """Update all properties for *character* (called once per character).""" + self.character = character + + # ASCII fast-path: for characters with ord < 128, we can skip + # _character_flags() entirely and derive most properties from ord. + o: int = ord(character) + if o < 128: + self.is_ascii = True + self.accentuated = False + self.is_cjk = False + self.is_arabic = False + self.is_glyph = False + # ASCII alpha: a-z (97-122) or A-Z (65-90) + if 65 <= o <= 90: + # Uppercase ASCII letter + self.alpha = True + self.upper = True + self.lower = False + self.space = False + self.digit = False + self.printable = True + self.case_variable = True + self.flags = _LATIN + self.latin = True + self.punct = False + self.sym = False + elif 97 <= o <= 122: + # Lowercase ASCII letter + self.alpha = True + self.upper = False + self.lower = True + self.space = False + self.digit = False + self.printable = True + self.case_variable = True + self.flags = _LATIN + self.latin = True + self.punct = False + self.sym = False + elif 48 <= o <= 57: + # ASCII digit 0-9 + self.alpha = False + self.upper = False + self.lower = False + self.space = False + self.digit = True + self.printable = True + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = False + self.sym = False + elif o == 32 or (9 <= o <= 13): + # Space, tab, newline, etc. + self.alpha = False + self.upper = False + self.lower = False + self.space = True + self.digit = False + self.printable = o == 32 + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = False + self.sym = False + else: + # Other ASCII (punctuation, symbols, control chars) + self.printable = character.isprintable() + self.alpha = False + self.upper = False + self.lower = False + self.space = False + self.digit = False + self.case_variable = False + self.flags = 0 + self.latin = False + self.punct = is_punctuation(character) if self.printable else False + self.sym = is_symbol(character) if self.printable else False + else: + # Non-ASCII path + self.is_ascii = False + self.printable = character.isprintable() + self.alpha = character.isalpha() + self.upper = character.isupper() + self.lower = character.islower() + self.space = character.isspace() + self.digit = character.isdigit() + self.case_variable = self.lower != self.upper + + # Flag-based classification (single unicodedata.name() call, lru-cached) + flags: int + if self.alpha: + flags = _character_flags(character) + else: + flags = 0 + self.flags = flags + self.accentuated = bool(flags & _ACCENTUATED) + self.latin = bool(flags & _LATIN) + self.is_cjk = bool(flags & _CJK) + self.is_arabic = bool(flags & _ARABIC) + self.is_glyph = bool(flags & _GLYPH_MASK) + + # Eagerly compute punct and sym (avoids property dispatch overhead + # on 300K+ accesses in the hot loop). + self.punct = is_punctuation(character) if self.printable else False + self.sym = is_symbol(character) if self.printable else False + + +class MessDetectorPlugin: + """ + Base abstract class used for mess detection plugins. + All detectors MUST extend and implement given methods. + """ + + __slots__ = () + + def feed_info(self, character: str, info: CharInfo) -> None: + """ + The main routine to be executed upon character. + Insert the logic in witch the text would be considered chaotic. + """ + raise NotImplementedError # Defensive: + + def reset(self) -> None: # Defensive: + """ + Permit to reset the plugin to the initial state. + """ + raise NotImplementedError + + @property + def ratio(self) -> float: + """ + Compute the chaos ratio based on what your feed() has seen. + Must NOT be lower than 0.; No restriction gt 0. + """ + raise NotImplementedError # Defensive: + + +@final +class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): + __slots__ = ( + "_punctuation_count", + "_symbol_count", + "_character_count", + "_last_printable_char", + "_frenzy_symbol_in_word", + ) + + def __init__(self) -> None: + self._punctuation_count: int = 0 + self._symbol_count: int = 0 + self._character_count: int = 0 + + self._last_printable_char: str | None = None + self._frenzy_symbol_in_word: bool = False + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if ( + character != self._last_printable_char + and character not in COMMON_SAFE_ASCII_CHARACTERS + ): + if info.punct: + self._punctuation_count += 1 + elif not info.digit and info.sym and not is_emoticon(character): + self._symbol_count += 2 + + self._last_printable_char = character + + def reset(self) -> None: # Abstract + self._punctuation_count = 0 + self._character_count = 0 + self._symbol_count = 0 + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + ratio_of_punctuation: float = ( + self._punctuation_count + self._symbol_count + ) / self._character_count + + return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 + + +@final +class TooManyAccentuatedPlugin(MessDetectorPlugin): + __slots__ = ("_character_count", "_accentuated_count") + + def __init__(self) -> None: + self._character_count: int = 0 + self._accentuated_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.accentuated: + self._accentuated_count += 1 + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._accentuated_count = 0 + + @property + def ratio(self) -> float: + if self._character_count < 8: + return 0.0 + + ratio_of_accentuation: float = self._accentuated_count / self._character_count + return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 + + +@final +class UnprintablePlugin(MessDetectorPlugin): + __slots__ = ("_unprintable_count", "_character_count") + + def __init__(self) -> None: + self._unprintable_count: int = 0 + self._character_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + if ( + not info.space + and not info.printable + and character != "\x1a" + and character != "\ufeff" + ): + self._unprintable_count += 1 + self._character_count += 1 + + def reset(self) -> None: # Abstract + self._unprintable_count = 0 + + @property + def ratio(self) -> float: + if self._character_count == 0: # Defensive: + return 0.0 + + return (self._unprintable_count * 8) / self._character_count + + +@final +class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): + __slots__ = ( + "_successive_count", + "_character_count", + "_last_latin_character", + "_last_was_accentuated", + ) + + def __init__(self) -> None: + self._successive_count: int = 0 + self._character_count: int = 0 + + self._last_latin_character: str | None = None + self._last_was_accentuated: bool = False + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + if ( + self._last_latin_character is not None + and info.accentuated + and self._last_was_accentuated + ): + if info.upper and self._last_latin_character.isupper(): + self._successive_count += 1 + if remove_accent(character) == remove_accent(self._last_latin_character): + self._successive_count += 1 + self._last_latin_character = character + self._last_was_accentuated = info.accentuated + + def reset(self) -> None: # Abstract + self._successive_count = 0 + self._character_count = 0 + self._last_latin_character = None + self._last_was_accentuated = False + + @property + def ratio(self) -> float: + if self._character_count == 0: + return 0.0 + + return (self._successive_count * 2) / self._character_count + + +@final +class SuspiciousRange(MessDetectorPlugin): + __slots__ = ( + "_suspicious_successive_range_count", + "_character_count", + "_last_printable_seen", + "_last_printable_range", + ) + + def __init__(self) -> None: + self._suspicious_successive_range_count: int = 0 + self._character_count: int = 0 + self._last_printable_seen: str | None = None + self._last_printable_range: str | None = None + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.space or info.punct or character in COMMON_SAFE_ASCII_CHARACTERS: + self._last_printable_seen = None + self._last_printable_range = None + return + + if self._last_printable_seen is None: + self._last_printable_seen = character + self._last_printable_range = unicode_range(character) + return + + unicode_range_a: str | None = self._last_printable_range + unicode_range_b: str | None = unicode_range(character) + + if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): + self._suspicious_successive_range_count += 1 + + self._last_printable_seen = character + self._last_printable_range = unicode_range_b + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._suspicious_successive_range_count = 0 + self._last_printable_seen = None + self._last_printable_range = None + + @property + def ratio(self) -> float: + if self._character_count <= 13: + return 0.0 + + ratio_of_suspicious_range_usage: float = ( + self._suspicious_successive_range_count * 2 + ) / self._character_count + + return ratio_of_suspicious_range_usage + + +@final +class SuperWeirdWordPlugin(MessDetectorPlugin): + __slots__ = ( + "_word_count", + "_bad_word_count", + "_foreign_long_count", + "_is_current_word_bad", + "_foreign_long_watch", + "_character_count", + "_bad_character_count", + "_buffer_length", + "_buffer_last_char", + "_buffer_last_char_accentuated", + "_buffer_accent_count", + "_buffer_glyph_count", + "_buffer_upper_count", + ) + + def __init__(self) -> None: + self._word_count: int = 0 + self._bad_word_count: int = 0 + self._foreign_long_count: int = 0 + + self._is_current_word_bad: bool = False + self._foreign_long_watch: bool = False + + self._character_count: int = 0 + self._bad_character_count: int = 0 + + self._buffer_length: int = 0 + self._buffer_last_char: str | None = None + self._buffer_last_char_accentuated: bool = False + self._buffer_accent_count: int = 0 + self._buffer_glyph_count: int = 0 + self._buffer_upper_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + if info.alpha: + self._buffer_length += 1 + self._buffer_last_char = character + + if info.upper: + self._buffer_upper_count += 1 + + self._buffer_last_char_accentuated = info.accentuated + + if info.accentuated: + self._buffer_accent_count += 1 + if ( + not self._foreign_long_watch + and (not info.latin or info.accentuated) + and not info.is_glyph + ): + self._foreign_long_watch = True + if info.is_glyph: + self._buffer_glyph_count += 1 + return + if not self._buffer_length: + return + if info.space or info.punct or is_separator(character): + self._word_count += 1 + buffer_length: int = self._buffer_length + + self._character_count += buffer_length + + if buffer_length >= 4: + if self._buffer_accent_count / buffer_length >= 0.5: + self._is_current_word_bad = True + elif ( + self._buffer_last_char_accentuated + and self._buffer_last_char.isupper() # type: ignore[union-attr] + and self._buffer_upper_count != buffer_length + ): + self._foreign_long_count += 1 + self._is_current_word_bad = True + elif self._buffer_glyph_count == 1: + self._is_current_word_bad = True + self._foreign_long_count += 1 + if buffer_length >= 24 and self._foreign_long_watch: + probable_camel_cased: bool = ( + self._buffer_upper_count > 0 + and self._buffer_upper_count / buffer_length <= 0.3 + ) + + if not probable_camel_cased: + self._foreign_long_count += 1 + self._is_current_word_bad = True + + if self._is_current_word_bad: + self._bad_word_count += 1 + self._bad_character_count += buffer_length + self._is_current_word_bad = False + + self._foreign_long_watch = False + self._buffer_length = 0 + self._buffer_last_char = None + self._buffer_last_char_accentuated = False + self._buffer_accent_count = 0 + self._buffer_glyph_count = 0 + self._buffer_upper_count = 0 + elif ( + character not in {"<", ">", "-", "=", "~", "|", "_"} + and not info.digit + and info.sym + ): + self._is_current_word_bad = True + self._buffer_length += 1 + self._buffer_last_char = character + self._buffer_last_char_accentuated = False + + def reset(self) -> None: # Abstract + self._buffer_length = 0 + self._buffer_last_char = None + self._buffer_last_char_accentuated = False + self._is_current_word_bad = False + self._foreign_long_watch = False + self._bad_word_count = 0 + self._word_count = 0 + self._character_count = 0 + self._bad_character_count = 0 + self._foreign_long_count = 0 + self._buffer_accent_count = 0 + self._buffer_glyph_count = 0 + self._buffer_upper_count = 0 + + @property + def ratio(self) -> float: + if self._word_count <= 10 and self._foreign_long_count == 0: + return 0.0 + + return self._bad_character_count / self._character_count + + +@final +class CjkUncommonPlugin(MessDetectorPlugin): + """ + Detect messy CJK text that probably means nothing. + """ + + __slots__ = ("_character_count", "_uncommon_count") + + def __init__(self) -> None: + self._character_count: int = 0 + self._uncommon_count: int = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if character not in COMMON_CJK_CHARACTERS: + self._uncommon_count += 1 + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._uncommon_count = 0 + + @property + def ratio(self) -> float: + if self._character_count < 8: + return 0.0 + + uncommon_form_usage: float = self._uncommon_count / self._character_count + + # we can be pretty sure it's garbage when uncommon characters are widely + # used. otherwise it could just be traditional chinese for example. + return uncommon_form_usage / 10 if uncommon_form_usage > 0.5 else 0.0 + + +@final +class ArchaicUpperLowerPlugin(MessDetectorPlugin): + __slots__ = ( + "_buf", + "_character_count_since_last_sep", + "_successive_upper_lower_count", + "_successive_upper_lower_count_final", + "_character_count", + "_last_alpha_seen", + "_last_alpha_seen_upper", + "_last_alpha_seen_lower", + "_current_ascii_only", + ) + + def __init__(self) -> None: + self._buf: bool = False + + self._character_count_since_last_sep: int = 0 + + self._successive_upper_lower_count: int = 0 + self._successive_upper_lower_count_final: int = 0 + + self._character_count: int = 0 + + self._last_alpha_seen: str | None = None + self._last_alpha_seen_upper: bool = False + self._last_alpha_seen_lower: bool = False + self._current_ascii_only: bool = True + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + is_concerned: bool = info.alpha and info.case_variable + chunk_sep: bool = not is_concerned + + if chunk_sep and self._character_count_since_last_sep > 0: + if ( + self._character_count_since_last_sep <= 64 + and not info.digit + and not self._current_ascii_only + ): + self._successive_upper_lower_count_final += ( + self._successive_upper_lower_count + ) + + self._successive_upper_lower_count = 0 + self._character_count_since_last_sep = 0 + self._last_alpha_seen = None + self._buf = False + self._character_count += 1 + self._current_ascii_only = True + + return + + if self._current_ascii_only and not info.is_ascii: + self._current_ascii_only = False + + if self._last_alpha_seen is not None: + if (info.upper and self._last_alpha_seen_lower) or ( + info.lower and self._last_alpha_seen_upper + ): + if self._buf: + self._successive_upper_lower_count += 2 + self._buf = False + else: + self._buf = True + else: + self._buf = False + + self._character_count += 1 + self._character_count_since_last_sep += 1 + self._last_alpha_seen = character + self._last_alpha_seen_upper = info.upper + self._last_alpha_seen_lower = info.lower + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._character_count_since_last_sep = 0 + self._successive_upper_lower_count = 0 + self._successive_upper_lower_count_final = 0 + self._last_alpha_seen = None + self._last_alpha_seen_upper = False + self._last_alpha_seen_lower = False + self._buf = False + self._current_ascii_only = True + + @property + def ratio(self) -> float: + if self._character_count == 0: # Defensive: + return 0.0 + + return self._successive_upper_lower_count_final / self._character_count + + +@final +class ArabicIsolatedFormPlugin(MessDetectorPlugin): + __slots__ = ("_character_count", "_isolated_form_count") + + def __init__(self) -> None: + self._character_count: int = 0 + self._isolated_form_count: int = 0 + + def reset(self) -> None: # Abstract + self._character_count = 0 + self._isolated_form_count = 0 + + def feed_info(self, character: str, info: CharInfo) -> None: + """Optimized feed using pre-computed character info.""" + self._character_count += 1 + + if info.flags & _ARABIC_ISOLATED_FORM: + self._isolated_form_count += 1 + + @property + def ratio(self) -> float: + if self._character_count < 8: + return 0.0 + + isolated_form_usage: float = self._isolated_form_count / self._character_count + + return isolated_form_usage + + +@lru_cache(maxsize=1024) +def is_suspiciously_successive_range( + unicode_range_a: str | None, unicode_range_b: str | None +) -> bool: + """ + Determine if two Unicode range seen next to each other can be considered as suspicious. + """ + if unicode_range_a is None or unicode_range_b is None: + return True + + if unicode_range_a == unicode_range_b: + return False + + if "Latin" in unicode_range_a and "Latin" in unicode_range_b: + return False + + if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: + return False + + # Latin characters can be accompanied with a combining diacritical mark + # eg. Vietnamese. + if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and ( + "Combining" in unicode_range_a or "Combining" in unicode_range_b + ): + return False + + keywords_range_a, keywords_range_b = ( + unicode_range_a.split(" "), + unicode_range_b.split(" "), + ) + + for el in keywords_range_a: + if el in UNICODE_SECONDARY_RANGE_KEYWORD: + continue + if el in keywords_range_b: + return False + + # Japanese Exception + range_a_jp_chars, range_b_jp_chars = ( + unicode_range_a + in ( + "Hiragana", + "Katakana", + ), + unicode_range_b in ("Hiragana", "Katakana"), + ) + if (range_a_jp_chars or range_b_jp_chars) and ( + "CJK" in unicode_range_a or "CJK" in unicode_range_b + ): + return False + if range_a_jp_chars and range_b_jp_chars: + return False + + if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: + if "CJK" in unicode_range_a or "CJK" in unicode_range_b: + return False + if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": + return False + + # Chinese/Japanese use dedicated range for punctuation and/or separators. + if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( + unicode_range_a in ["Katakana", "Hiragana"] + and unicode_range_b in ["Katakana", "Hiragana"] + ): + if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: + return False + if "Forms" in unicode_range_a or "Forms" in unicode_range_b: + return False + if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": + return False + + return True + + +@lru_cache(maxsize=2048) +def mess_ratio( + decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False +) -> float: + """ + Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. + """ + + seq_len: int = len(decoded_sequence) + + if seq_len < 511: + step: int = 32 + elif seq_len < 1024: + step = 64 + else: + step = 128 + + # Create each detector as a named local variable (unrolled from the generic loop). + # This eliminates per-character iteration over the detector list and + # per-character eligible() virtual dispatch, while keeping every plugin class + # intact and fully readable. + d_sp: TooManySymbolOrPunctuationPlugin = TooManySymbolOrPunctuationPlugin() + d_ta: TooManyAccentuatedPlugin = TooManyAccentuatedPlugin() + d_up: UnprintablePlugin = UnprintablePlugin() + d_sda: SuspiciousDuplicateAccentPlugin = SuspiciousDuplicateAccentPlugin() + d_sr: SuspiciousRange = SuspiciousRange() + d_sw: SuperWeirdWordPlugin = SuperWeirdWordPlugin() + d_cu: CjkUncommonPlugin = CjkUncommonPlugin() + d_au: ArchaicUpperLowerPlugin = ArchaicUpperLowerPlugin() + d_ai: ArabicIsolatedFormPlugin = ArabicIsolatedFormPlugin() + + # Local references for feed_info methods called in the hot loop. + d_sp_feed = d_sp.feed_info + d_ta_feed = d_ta.feed_info + d_up_feed = d_up.feed_info + d_sda_feed = d_sda.feed_info + d_sr_feed = d_sr.feed_info + d_sw_feed = d_sw.feed_info + d_cu_feed = d_cu.feed_info + d_au_feed = d_au.feed_info + d_ai_feed = d_ai.feed_info + + # Single reusable CharInfo object (avoids per-character allocation). + info: CharInfo = CharInfo() + info_update = info.update + + mean_mess_ratio: float + + for block_start in range(0, seq_len, step): + for character in decoded_sequence[block_start : block_start + step]: + # Pre-compute all character properties once (shared across all plugins). + info_update(character) + + # Detectors with eligible() == always True + d_up_feed(character, info) + d_sw_feed(character, info) + d_au_feed(character, info) + + # Detectors with eligible() == isprintable + if info.printable: + d_sp_feed(character, info) + d_sr_feed(character, info) + + # Detectors with eligible() == isalpha + if info.alpha: + d_ta_feed(character, info) + # SuspiciousDuplicateAccent: isalpha() and is_latin() + if info.latin: + d_sda_feed(character, info) + # CjkUncommon: is_cjk() + if info.is_cjk: + d_cu_feed(character, info) + # ArabicIsolatedForm: is_arabic() + if info.is_arabic: + d_ai_feed(character, info) + + mean_mess_ratio = ( + d_sp.ratio + + d_ta.ratio + + d_up.ratio + + d_sda.ratio + + d_sr.ratio + + d_sw.ratio + + d_cu.ratio + + d_au.ratio + + d_ai.ratio + ) + + if mean_mess_ratio >= maximum_threshold: + break + else: + # Flush last word buffer in SuperWeirdWordPlugin via trailing newline. + info_update("\n") + d_sw_feed("\n", info) + d_au_feed("\n", info) + d_up_feed("\n", info) + + mean_mess_ratio = ( + d_sp.ratio + + d_ta.ratio + + d_up.ratio + + d_sda.ratio + + d_sr.ratio + + d_sw.ratio + + d_cu.ratio + + d_au.ratio + + d_ai.ratio + ) + + if debug: # Defensive: + logger = getLogger("charset_normalizer") + + logger.log( + TRACE, + "Mess-detector extended-analysis start. " + f"intermediary_mean_mess_ratio_calc={step} mean_mess_ratio={mean_mess_ratio} " + f"maximum_threshold={maximum_threshold}", + ) + + if seq_len > 16: + logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") + logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") + + for dt in [d_sp, d_ta, d_up, d_sda, d_sr, d_sw, d_cu, d_au, d_ai]: + logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") + + return round(mean_mess_ratio, 3) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/models.py b/.venv/lib/python3.12/site-packages/charset_normalizer/models.py new file mode 100644 index 0000000..382de15 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/models.py @@ -0,0 +1,369 @@ +from __future__ import annotations + +from encodings.aliases import aliases +from json import dumps +from re import sub +from typing import Any, Iterator, List, Tuple + +from .constant import RE_POSSIBLE_ENCODING_INDICATION, TOO_BIG_SEQUENCE +from .utils import iana_name, is_multi_byte_encoding, unicode_range + + +class CharsetMatch: + def __init__( + self, + payload: bytes | bytearray, + guessed_encoding: str, + mean_mess_ratio: float, + has_sig_or_bom: bool, + languages: CoherenceMatches, + decoded_payload: str | None = None, + preemptive_declaration: str | None = None, + ): + self._payload: bytes | bytearray = payload + + self._encoding: str = guessed_encoding + self._mean_mess_ratio: float = mean_mess_ratio + self._languages: CoherenceMatches = languages + self._has_sig_or_bom: bool = has_sig_or_bom + self._unicode_ranges: list[str] | None = None + + self._leaves: list[CharsetMatch] = [] + self._mean_coherence_ratio: float = 0.0 + + self._output_payload: bytes | None = None + self._output_encoding: str | None = None + + self._string: str | None = decoded_payload + + self._preemptive_declaration: str | None = preemptive_declaration + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CharsetMatch): + if isinstance(other, str): + return iana_name(other) == self.encoding + return False + return self.encoding == other.encoding and self.fingerprint == other.fingerprint + + def __lt__(self, other: object) -> bool: + """ + Implemented to make sorted available upon CharsetMatches items. + """ + if not isinstance(other, CharsetMatch): + raise ValueError + + chaos_difference: float = abs(self.chaos - other.chaos) + coherence_difference: float = abs(self.coherence - other.coherence) + + # Below 0.5% difference --> Use Coherence + if chaos_difference < 0.005 and coherence_difference > 0.02: + return self.coherence > other.coherence + elif chaos_difference < 0.005 and coherence_difference <= 0.02: + # When having a difficult decision, use the result that decoded as many multi-byte as possible. + # preserve RAM usage! + if len(self._payload) >= TOO_BIG_SEQUENCE: + return self.chaos < other.chaos + return self.multi_byte_usage > other.multi_byte_usage + + return self.chaos < other.chaos + + @property + def multi_byte_usage(self) -> float: + return 1.0 - (len(str(self)) / len(self.raw)) + + def __str__(self) -> str: + # Lazy Str Loading + if self._string is None: + self._string = str(self._payload, self._encoding, "strict") + # UTF-7 BOM is encoded in modified Base64 whose byte boundary + # can overlap with the next character, so raw-byte stripping + # is unreliable. Strip the decoded BOM character instead. + if ( + self._has_sig_or_bom + and self._encoding == "utf_7" + and self._string + and self._string[0] == "\ufeff" + ): + self._string = self._string[1:] + return self._string + + def __repr__(self) -> str: + return f"" + + def add_submatch(self, other: CharsetMatch) -> None: + if not isinstance(other, CharsetMatch) or other == self: + raise ValueError( + "Unable to add instance <{}> as a submatch of a CharsetMatch".format( + other.__class__ + ) + ) + + other._string = None # Unload RAM usage; dirty trick. + self._leaves.append(other) + + @property + def encoding(self) -> str: + return self._encoding + + @property + def encoding_aliases(self) -> list[str]: + """ + Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. + """ + also_known_as: list[str] = [] + for u, p in aliases.items(): + if self.encoding == u: + also_known_as.append(p) + elif self.encoding == p: + also_known_as.append(u) + return also_known_as + + @property + def bom(self) -> bool: + return self._has_sig_or_bom + + @property + def byte_order_mark(self) -> bool: + return self._has_sig_or_bom + + @property + def languages(self) -> list[str]: + """ + Return the complete list of possible languages found in decoded sequence. + Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. + """ + return [e[0] for e in self._languages] + + @property + def language(self) -> str: + """ + Most probable language found in decoded sequence. If none were detected or inferred, the property will return + "Unknown". + """ + if not self._languages: + # Trying to infer the language based on the given encoding + # Its either English or we should not pronounce ourselves in certain cases. + if "ascii" in self.could_be_from_charset: + return "English" + + # doing it there to avoid circular import + from charset_normalizer.cd import encoding_languages, mb_encoding_languages + + languages = ( + mb_encoding_languages(self.encoding) + if is_multi_byte_encoding(self.encoding) + else encoding_languages(self.encoding) + ) + + if len(languages) == 0 or "Latin Based" in languages: + return "Unknown" + + return languages[0] + + return self._languages[0][0] + + @property + def chaos(self) -> float: + return self._mean_mess_ratio + + @property + def coherence(self) -> float: + if not self._languages: + return 0.0 + return self._languages[0][1] + + @property + def percent_chaos(self) -> float: + return round(self.chaos * 100, ndigits=3) + + @property + def percent_coherence(self) -> float: + return round(self.coherence * 100, ndigits=3) + + @property + def raw(self) -> bytes | bytearray: + """ + Original untouched bytes. + """ + return self._payload + + @property + def submatch(self) -> list[CharsetMatch]: + return self._leaves + + @property + def has_submatch(self) -> bool: + return len(self._leaves) > 0 + + @property + def alphabets(self) -> list[str]: + if self._unicode_ranges is not None: + return self._unicode_ranges + # list detected ranges + detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] + # filter and sort + self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) + return self._unicode_ranges + + @property + def could_be_from_charset(self) -> list[str]: + """ + The complete list of encoding that output the exact SAME str result and therefore could be the originating + encoding. + This list does include the encoding available in property 'encoding'. + """ + return [self._encoding] + [m.encoding for m in self._leaves] + + def output(self, encoding: str = "utf_8") -> bytes: + """ + Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. + Any errors will be simply ignored by the encoder NOT replaced. + """ + if self._output_encoding is None or self._output_encoding != encoding: + self._output_encoding = encoding + decoded_string = str(self) + if ( + self._preemptive_declaration is not None + and self._preemptive_declaration.lower() + not in ["utf-8", "utf8", "utf_8"] + ): + patched_header = sub( + RE_POSSIBLE_ENCODING_INDICATION, + lambda m: m.string[m.span()[0] : m.span()[1]].replace( + m.groups()[0], + iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] + ), + decoded_string[:8192], + count=1, + ) + + decoded_string = patched_header + decoded_string[8192:] + + self._output_payload = decoded_string.encode(encoding, "replace") + + return self._output_payload # type: ignore + + @property + def fingerprint(self) -> int: + """ + Retrieve a hash fingerprint of the decoded payload, used for deduplication. + """ + return hash(str(self)) + + +class CharsetMatches: + """ + Container with every CharsetMatch items ordered by default from most probable to the less one. + Act like a list(iterable) but does not implements all related methods. + """ + + def __init__(self, results: list[CharsetMatch] | None = None): + self._results: list[CharsetMatch] = sorted(results) if results else [] + + def __iter__(self) -> Iterator[CharsetMatch]: + yield from self._results + + def __getitem__(self, item: int | str) -> CharsetMatch: + """ + Retrieve a single item either by its position or encoding name (alias may be used here). + Raise KeyError upon invalid index or encoding not present in results. + """ + if isinstance(item, int): + return self._results[item] + if isinstance(item, str): + item = iana_name(item, False) + for result in self._results: + if item in result.could_be_from_charset: + return result + raise KeyError + + def __len__(self) -> int: + return len(self._results) + + def __bool__(self) -> bool: + return len(self._results) > 0 + + def append(self, item: CharsetMatch) -> None: + """ + Insert a single match. Will be inserted accordingly to preserve sort. + Can be inserted as a submatch. + """ + if not isinstance(item, CharsetMatch): + raise ValueError( + "Cannot append instance '{}' to CharsetMatches".format( + str(item.__class__) + ) + ) + # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) + if len(item.raw) < TOO_BIG_SEQUENCE: + for match in self._results: + if match.fingerprint == item.fingerprint and match.chaos == item.chaos: + match.add_submatch(item) + return + self._results.append(item) + self._results = sorted(self._results) + + def best(self) -> CharsetMatch | None: + """ + Simply return the first match. Strict equivalent to matches[0]. + """ + if not self._results: + return None + return self._results[0] + + def first(self) -> CharsetMatch | None: + """ + Redundant method, call the method best(). Kept for BC reasons. + """ + return self.best() + + +CoherenceMatch = Tuple[str, float] +CoherenceMatches = List[CoherenceMatch] + + +class CliDetectionResult: + def __init__( + self, + path: str, + encoding: str | None, + encoding_aliases: list[str], + alternative_encodings: list[str], + language: str, + alphabets: list[str], + has_sig_or_bom: bool, + chaos: float, + coherence: float, + unicode_path: str | None, + is_preferred: bool, + ): + self.path: str = path + self.unicode_path: str | None = unicode_path + self.encoding: str | None = encoding + self.encoding_aliases: list[str] = encoding_aliases + self.alternative_encodings: list[str] = alternative_encodings + self.language: str = language + self.alphabets: list[str] = alphabets + self.has_sig_or_bom: bool = has_sig_or_bom + self.chaos: float = chaos + self.coherence: float = coherence + self.is_preferred: bool = is_preferred + + @property + def __dict__(self) -> dict[str, Any]: # type: ignore + return { + "path": self.path, + "encoding": self.encoding, + "encoding_aliases": self.encoding_aliases, + "alternative_encodings": self.alternative_encodings, + "language": self.language, + "alphabets": self.alphabets, + "has_sig_or_bom": self.has_sig_or_bom, + "chaos": self.chaos, + "coherence": self.coherence, + "unicode_path": self.unicode_path, + "is_preferred": self.is_preferred, + } + + def to_json(self) -> str: + return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed b/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py b/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py new file mode 100644 index 0000000..0f529b5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py @@ -0,0 +1,422 @@ +from __future__ import annotations + +import importlib +import logging +import unicodedata +from bisect import bisect_right +from codecs import IncrementalDecoder +from encodings.aliases import aliases +from functools import lru_cache +from re import findall +from typing import Generator + +from _multibytecodec import ( # type: ignore[import-not-found,import] + MultibyteIncrementalDecoder, +) + +from .constant import ( + ENCODING_MARKS, + IANA_SUPPORTED_SIMILAR, + RE_POSSIBLE_ENCODING_INDICATION, + UNICODE_RANGES_COMBINED, + UNICODE_SECONDARY_RANGE_KEYWORD, + UTF8_MAXIMAL_ALLOCATION, + COMMON_CJK_CHARACTERS, + _LATIN, + _CJK, + _HANGUL, + _KATAKANA, + _HIRAGANA, + _THAI, + _ARABIC, + _ARABIC_ISOLATED_FORM, + _ACCENT_KEYWORDS, + _ACCENTUATED, +) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def _character_flags(character: str) -> int: + """Compute all name-based classification flags with a single unicodedata.name() call.""" + try: + desc: str = unicodedata.name(character) + except ValueError: + return 0 + + flags: int = 0 + + if "LATIN" in desc: + flags |= _LATIN + if "CJK" in desc: + flags |= _CJK + if "HANGUL" in desc: + flags |= _HANGUL + if "KATAKANA" in desc: + flags |= _KATAKANA + if "HIRAGANA" in desc: + flags |= _HIRAGANA + if "THAI" in desc: + flags |= _THAI + if "ARABIC" in desc: + flags |= _ARABIC + if "ISOLATED FORM" in desc: + flags |= _ARABIC_ISOLATED_FORM + + for kw in _ACCENT_KEYWORDS: + if kw in desc: + flags |= _ACCENTUATED + break + + return flags + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_accentuated(character: str) -> bool: + return bool(_character_flags(character) & _ACCENTUATED) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def remove_accent(character: str) -> str: + decomposed: str = unicodedata.decomposition(character) + if not decomposed: + return character + + codes: list[str] = decomposed.split(" ") + + return chr(int(codes[0], 16)) + + +# Pre-built sorted lookup table for O(log n) binary search in unicode_range(). +# Each entry is (range_start, range_end_exclusive, range_name). +_UNICODE_RANGES_SORTED: list[tuple[int, int, str]] = sorted( + (ord_range.start, ord_range.stop, name) + for name, ord_range in UNICODE_RANGES_COMBINED.items() +) +_UNICODE_RANGE_STARTS: list[int] = [e[0] for e in _UNICODE_RANGES_SORTED] + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def unicode_range(character: str) -> str | None: + """ + Retrieve the Unicode range official name from a single character. + """ + character_ord: int = ord(character) + + # Binary search: find the rightmost range whose start <= character_ord + idx = bisect_right(_UNICODE_RANGE_STARTS, character_ord) - 1 + if idx >= 0: + start, stop, name = _UNICODE_RANGES_SORTED[idx] + if character_ord < stop: + return name + + return None + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_latin(character: str) -> bool: + return bool(_character_flags(character) & _LATIN) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_punctuation(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "P" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Punctuation" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_symbol(character: str) -> bool: + character_category: str = unicodedata.category(character) + + if "S" in character_category or "N" in character_category: + return True + + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Forms" in character_range and character_category != "Lo" + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_emoticon(character: str) -> bool: + character_range: str | None = unicode_range(character) + + if character_range is None: + return False + + return "Emoticons" in character_range or "Pictographs" in character_range + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_separator(character: str) -> bool: + if character.isspace() or character in {"|", "+", "<", ">"}: + return True + + character_category: str = unicodedata.category(character) + + return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_case_variable(character: str) -> bool: + return character.islower() != character.isupper() + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk(character: str) -> bool: + return bool(_character_flags(character) & _CJK) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hiragana(character: str) -> bool: + return bool(_character_flags(character) & _HIRAGANA) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_katakana(character: str) -> bool: + return bool(_character_flags(character) & _KATAKANA) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_hangul(character: str) -> bool: + return bool(_character_flags(character) & _HANGUL) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_thai(character: str) -> bool: + return bool(_character_flags(character) & _THAI) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic(character: str) -> bool: + return bool(_character_flags(character) & _ARABIC) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_arabic_isolated_form(character: str) -> bool: + return bool(_character_flags(character) & _ARABIC_ISOLATED_FORM) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_cjk_uncommon(character: str) -> bool: + return character not in COMMON_CJK_CHARACTERS + + +@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) +def is_unicode_range_secondary(range_name: str) -> bool: + return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) + + +@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) +def is_unprintable(character: str) -> bool: + return ( + character.isspace() is False # includes \n \t \r \v + and character.isprintable() is False + and character != "\x1a" # Why? Its the ASCII substitute character. + and character != "\ufeff" # bug discovered in Python, + # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. + ) + + +def any_specified_encoding( + sequence: bytes | bytearray, search_zone: int = 8192 +) -> str | None: + """ + Extract using ASCII-only decoder any specified encoding in the first n-bytes. + """ + if not isinstance(sequence, (bytes, bytearray)): + raise TypeError + + seq_len: int = len(sequence) + + results: list[str] = findall( + RE_POSSIBLE_ENCODING_INDICATION, + sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), + ) + + if len(results) == 0: + return None + + for specified_encoding in results: + specified_encoding = specified_encoding.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if encoding_alias == specified_encoding: + return encoding_iana + if encoding_iana == specified_encoding: + return encoding_iana + + return None + + +@lru_cache(maxsize=128) +def is_multi_byte_encoding(name: str) -> bool: + """ + Verify is a specific encoding is a multi byte one based on it IANA name + """ + return name in { + "utf_8", + "utf_8_sig", + "utf_16", + "utf_16_be", + "utf_16_le", + "utf_32", + "utf_32_le", + "utf_32_be", + "utf_7", + } or issubclass( + importlib.import_module(f"encodings.{name}").IncrementalDecoder, + MultibyteIncrementalDecoder, + ) + + +def identify_sig_or_bom(sequence: bytes | bytearray) -> tuple[str | None, bytes]: + """ + Identify and extract SIG/BOM in given sequence. + """ + + for iana_encoding in ENCODING_MARKS: + marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] + + if isinstance(marks, bytes): + marks = [marks] + + for mark in marks: + if sequence.startswith(mark): + return iana_encoding, mark + + return None, b"" + + +def should_strip_sig_or_bom(iana_encoding: str) -> bool: + return iana_encoding not in {"utf_16", "utf_32"} + + +def iana_name(cp_name: str, strict: bool = True) -> str: + """Returns the Python normalized encoding name (Not the IANA official name).""" + cp_name = cp_name.lower().replace("-", "_") + + encoding_alias: str + encoding_iana: str + + for encoding_alias, encoding_iana in aliases.items(): + if cp_name in [encoding_alias, encoding_iana]: + return encoding_iana + + if strict: + raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") + + return cp_name + + +def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: + if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): + return 0.0 + + decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder + decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder + + id_a: IncrementalDecoder = decoder_a(errors="ignore") + id_b: IncrementalDecoder = decoder_b(errors="ignore") + + character_match_count: int = 0 + + for i in range(256): + to_be_decoded: bytes = bytes([i]) + if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): + character_match_count += 1 + + return character_match_count / 256 + + +def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: + """ + Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using + the function cp_similarity. + """ + return ( + iana_name_a in IANA_SUPPORTED_SIMILAR + and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] + ) + + +def set_logging_handler( + name: str = "charset_normalizer", + level: int = logging.INFO, + format_string: str = "%(asctime)s | %(levelname)s | %(message)s", +) -> None: + logger = logging.getLogger(name) + logger.setLevel(level) + + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter(format_string)) + logger.addHandler(handler) + + +def cut_sequence_chunks( + sequences: bytes | bytearray, + encoding_iana: str, + offsets: range, + chunk_size: int, + bom_or_sig_available: bool, + strip_sig_or_bom: bool, + sig_payload: bytes, + is_multi_byte_decoder: bool, + decoded_payload: str | None = None, +) -> Generator[str, None, None]: + if decoded_payload and is_multi_byte_decoder is False: + for i in offsets: + chunk = decoded_payload[i : i + chunk_size] + if not chunk: + break + yield chunk + else: + for i in offsets: + chunk_end = i + chunk_size + if chunk_end > len(sequences) + 8: + continue + + cut_sequence = sequences[i : i + chunk_size] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode( + encoding_iana, + errors="ignore" if is_multi_byte_decoder else "strict", + ) + + # multi-byte bad cutting detector and adjustment + # not the cleanest way to perform that fix but clever enough for now. + if is_multi_byte_decoder and i > 0: + chunk_partial_size_chk: int = min(chunk_size, 16) + + if ( + decoded_payload + and chunk[:chunk_partial_size_chk] not in decoded_payload + ): + for j in range(i, i - 4, -1): + cut_sequence = sequences[j:chunk_end] + + if bom_or_sig_available and strip_sig_or_bom is False: + cut_sequence = sig_payload + cut_sequence + + chunk = cut_sequence.decode(encoding_iana, errors="ignore") + + if chunk[:chunk_partial_size_chk] in decoded_payload: + break + + yield chunk diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/version.py b/.venv/lib/python3.12/site-packages/charset_normalizer/version.py new file mode 100644 index 0000000..a93d367 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer/version.py @@ -0,0 +1,8 @@ +""" +Expose version +""" + +from __future__ import annotations + +__version__ = "3.4.7" +VERSION = __version__.split(".") diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/METADATA new file mode 100644 index 0000000..fbea0aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/METADATA @@ -0,0 +1,110 @@ +Metadata-Version: 2.4 +Name: cryptography +Version: 48.0.0 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +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.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 :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable +Classifier: Topic :: Security :: Cryptography +Requires-Dist: cffi>=2.0.0 ; platform_python_implementation != 'PyPy' +Requires-Dist: typing-extensions>=4.13.2 ; python_full_version < '3.11' +Requires-Dist: bcrypt>=3.1.5 ; extra == 'ssh' +Provides-Extra: ssh +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-email: The Python Cryptographic Authority and individual contributors +License-Expression: Apache-2.0 OR BSD-3-Clause +Requires-Python: >=3.9, !=3.9.0, !=3.9.1 +Description-Content-Type: text/x-rst; charset=UTF-8 +Project-URL: changelog, https://cryptography.io/en/latest/changelog/ +Project-URL: documentation, https://cryptography.io/ +Project-URL: homepage, https://github.com/pyca/cryptography +Project-URL: issues, https://github.com/pyca/cryptography/issues +Project-URL: source, https://github.com/pyca/cryptography/ + +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/actions/workflows/ci.yml/badge.svg + :target: https://github.com/pyca/cryptography/actions/workflows/ci.yml?query=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.9+ 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/python3.12/site-packages/cryptography-48.0.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/RECORD new file mode 100644 index 0000000..99dc725 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/RECORD @@ -0,0 +1,196 @@ +cryptography-48.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cryptography-48.0.0.dist-info/METADATA,sha256=Q_COX-Utpsgl5e4Ym9mF7hU3hgAgMy3Ly8XaVnneyQY,4343 +cryptography-48.0.0.dist-info/RECORD,, +cryptography-48.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cryptography-48.0.0.dist-info/WHEEL,sha256=59dZFnCZ9Ah4-4Hov4_Ij4OuERDplrmrGrO0EnwpCG0,109 +cryptography-48.0.0.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197 +cryptography-48.0.0.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 +cryptography-48.0.0.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 +cryptography-48.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json,sha256=1fzfm56UYqOyUDjSaZ_PR1FgbE0pmZk5bxNk6uJedaI,45613 +cryptography-48.0.0.dist-info/sboms/sbom.json,sha256=hj41wZWnr0WU1kaHtI0VS_cPesX716EgqQjDnxMp0yI,1206 +cryptography/__about__.py,sha256=4Ay52_5LrlJ_oTPcjtV4NXOcNzJhpo6iDZPEYbndzJI,445 +cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364 +cryptography/__pycache__/__about__.cpython-312.pyc,, +cryptography/__pycache__/__init__.cpython-312.pyc,, +cryptography/__pycache__/exceptions.cpython-312.pyc,, +cryptography/__pycache__/fernet.cpython-312.pyc,, +cryptography/__pycache__/utils.cpython-312.pyc,, +cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087 +cryptography/fernet.py,sha256=3Cvxkh0KJSbX8HbnCHu4wfCW7U0GgfUA3v_qQ8a8iWc,6963 +cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455 +cryptography/hazmat/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/__pycache__/_oid.cpython-312.pyc,, +cryptography/hazmat/_oid.py,sha256=p8ThjwJB56Ci_rAIrjyJ1f8VjgD6e39es2dh8JIUBOw,17240 +cryptography/hazmat/asn1/__init__.py,sha256=AZVmLA09Q9-bq27G8fYr3dof-iRkh5KSX3zEPmNb-9w,744 +cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc,, +cryptography/hazmat/asn1/asn1.py,sha256=FZ6nZr3N1kKqNBQfh0qKtJ0cLUjwtGFemG82KNWOcfk,14724 +cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361 +cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305 +cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc,, +cryptography/hazmat/backends/openssl/backend.py,sha256=7A8JKJlQjG3-Fqdcyat2Eb7kHLYAAtGF0j9ooaWyaE0,10520 +cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/_rust.abi3.so,sha256=UVbEIvZcX5Eb4cvajr3IhuE7RL74hDZf3cObn80DKeY,13844864 +cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=uUnnvOq_uameYnBNpmsv3SRAVO0WQGpjI58aR1lL4Kw,2285 +cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=T_5TpYO8bghIPjAQ3BtAABqw-CcYskHp92wm5PNY6dc,228 +cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354 +cryptography/hazmat/bindings/_rust/declarative_asn1.pyi,sha256=iSeRyyd3hN0VIUKg_p424AzQ-b7hW1b_53Yx50yniYY,3661 +cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640 +cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=VPVWuKHI9EMs09ZLRYAGvR0Iz0mCMmEzXAkgJHovpoM,4020 +cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=JDCef29vXnrEMvF13JH9z1-W9ahiTZQAk5xhu_KH6po,1550 +cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=7d--xdc0vuzRe4S2uuQaGm-9GGKv4NcSLQcLyWcaD6s,4582 +cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=LhPzHWSXJq4grAJXn6zSvSSdV-aYIIscHDwIPlJGGPs,1315 +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=VXfXd5G6hUivg399R1DYdmW3eTb0EebzDTqjRC2gaRw,532 +cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=Yx49lqdnjsD7bxiDV1kcaMrDktug5evi5a6zerMiy2s,514 +cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=bFe3T13UeyRInUJMD-R5ArGTGLqWDiWo2Lv2KGzUbf4,1076 +cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=BXZn7NDjL3JAbYW0SQ8pg1iyC5DbQXVhUAiwsi8DFR8,702 +cryptography/hazmat/bindings/_rust/openssl/hpke.pyi,sha256=wrz8c12gBNBAItz9jorr_W4s8rFYDE1Yel1z2I-Wd2s,2877 +cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=HiiLdEB9nqMsTvMwiFppQpfPz6fHUz9_gpjEB_XAOXA,6619 +cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=teIt8M6ZEMJrn4s3W0UnW0DZ-30Jd68WnSsKKG124l0,912 +cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi,sha256=HChNsjW0C_vO9OVD4FhEZrXO3_FiEq2ZTF59hlfcEdM,1073 +cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi,sha256=byoeus4-lIOlWlEiRQdCc00qCkXpOB5peBY6yo9ik8s,835 +cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=_SW9NtQ5FDlAbdclFtWpT4lGmxKIKHpN-4j8J2BzYfQ,585 +cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364 +cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=ewn4GpQyb7zPwE-ni7GtyQgMC0A1mLuqYsSyqv6nI_s,523 +cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=juTZTmli8jO_5Vcufg-vHvx_tCyezmSLIh_9PU3TczI,505 +cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=vEEd5wDiZvb8ZGFaziLCaWLzAwoG_tvPUxLQw5_uOl8,1605 +cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=txGBJijqZshEcqra6byPNbnisIdlxzOSIHP2hl9arPs,1601 +cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=PPhld-WkO743iXFPebeG0LtgK0aTzGdjcIsay1Gm5GE,757 +cryptography/hazmat/bindings/_rust/x509.pyi,sha256=rmt4enscdvDQ7lBEHb2cASwN0d3TP3ymEVZcEJ-Rf3A,10152 +cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc,, +cryptography/hazmat/bindings/openssl/_conditional.py,sha256=vfzi-xHdStAcJQjLqqOlSos94L8D_tqFtBe7Ovw-KtY,5611 +cryptography/hazmat/bindings/openssl/binding.py,sha256=GnoZLYvCzXbKf4KldUXec-oz-hdVSa_sinZnqXgdS2s,4096 +cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 +cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc,, +cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=yqHt7k5OzF_KuvO8M9vTlNLtG7EotbvShyYKIaUJqhg,3566 +cryptography/hazmat/decrepit/ciphers/modes.py,sha256=Oq_PEwCke5OLczOfr_vzAOJ6wPx-rlsvHAXPBuh5b9o,1649 +cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc,, +cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532 +cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=Eh3i7lwedHfi0eLSsH93PZxQKzY9I6lkK67vL4V5tOc,1522 +cryptography/hazmat/primitives/_modes.py,sha256=_EiAOD8Jb6WpFXluwkcUT3ECz_TH8xjGvbQlGkNm59Q,3075 +cryptography/hazmat/primitives/_serialization.py,sha256=hi0xJblBAZ8pmZx2lWa-lruphxvvvG3g9DNK0G8Odfs,4440 +cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc,, +cryptography/hazmat/primitives/asymmetric/dh.py,sha256=klvHVaxAFkUyaWXLytqYjBTh81Zx1ByCJNZVRcSDYX8,3912 +cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=kQzTViqAI9PiELNOBco_n6MfP3N9ehIPkSduOpupt0M,4482 +cryptography/hazmat/primitives/asymmetric/ec.py,sha256=LklVCLLxbbQzqiE6HRaYTRauLMphYUw3tE6c8oBIxYQ,10183 +cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=mbffLvs-3PgCDeg1rQXVXh7GLntlVL4ATjkIEc4pt5M,2998 +cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=n0dK6y-VLxuw-zvrDxuPT4598hymgX-1Ry_jq9aUQhE,4002 +cryptography/hazmat/primitives/asymmetric/mldsa.py,sha256=49DPIhoHEyfGVwL6DRc3WlJ20Rinq-ldtjsp91NpyMM,12226 +cryptography/hazmat/primitives/asymmetric/mlkem.py,sha256=wvVkeFT-4of-VcUAER2-zI_5hXT6rwTJPMCQ8gF9GTA,7901 +cryptography/hazmat/primitives/asymmetric/padding.py,sha256=vQ6l6gOg9HqcbOsvHrSiJRVLdEj9L4m4HkRGYziTyFA,2854 +cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=lIE7lGe449W5URLTKCpvFGerITouKoZV9GwuTzScsSo,8492 +cryptography/hazmat/primitives/asymmetric/types.py,sha256=q3glpax4EHgNpkRSEXlhK43a07F3Dsk-rQ3bbjL9ZnM,3309 +cryptography/hazmat/primitives/asymmetric/utils.py,sha256=Qs8Re9GFPjW_tNp_73IeJEjPCf0slOIsWOxo6qymT6k,821 +cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=lQcgUk-Piubj8ynFNWQeYme3tYZMgHQqkRKkIDOHLzo,3888 +cryptography/hazmat/primitives/asymmetric/x448.py,sha256=b37ig7k7poG6SJDry42j-ElCj5q4eQ0X5CFmN0Sn8z8,3913 +cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680 +cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc,, +cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634 +cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=IGfCycYJOh1TvoI34rfw8KfJ_xmt1p_DpBht2Fghceo,3496 +cryptography/hazmat/primitives/ciphers/base.py,sha256=aBC7HHBBoixebmparVr0UlODs3VD0A7B6oz_AaRjDv8,4253 +cryptography/hazmat/primitives/ciphers/modes.py,sha256=pNI0cSLCLxr7lz7ApfUpVpFH5q_bzOpKW-QggmgQB74,6007 +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=M8BrlKB3U6DEtHvWTV5VRjpteHv1kS3Zxm_Bsk04cr8,5184 +cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423 +cryptography/hazmat/primitives/hpke.py,sha256=RsHissC5l-dTPn1p2JbcrIrAnDBrLqM5ugBFSGpmKu4,865 +cryptography/hazmat/primitives/kdf/__init__.py,sha256=v3yiYBGU272EojNXbwfYZdbbfI9cVOCCG3nXhTDda3k,1037 +cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc,, +cryptography/hazmat/primitives/kdf/argon2.py,sha256=ZJx-enUlAA4o7b46C1wlzG_XAQQW0wIkFgUKkrM_wA8,632 +cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=BFnOKS72txKF0yQvASr0Na7uhzvLHpUCckK5iNAC_aQ,591 +cryptography/hazmat/primitives/kdf/hkdf.py,sha256=M0lAEfRoc4kpp4-nwDj9yB-vNZukIOYEQrUlWsBNn9o,543 +cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=C3w99vjFqhqzAxzFOH4zzvVoHkb5AC6Lz6AEQGV7d_g,737 +cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xchkk99s-Mk6b6KSCouqdk8FVyiNVOSFIenmnW3tLgQ,468 +cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590 +cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=QKjhRehuTsAstL384bKfvjuv1yfdamAHVhsBRWen2Vw,456 +cryptography/hazmat/primitives/keywrap.py,sha256=UI-0UESQxBXTKU1HrrRoUwDiFsrWif81qyXlV1e7kyY,5776 +cryptography/hazmat/primitives/padding.py,sha256=QT-U-NvV2eQGO1wVPbDiNGNSc9keRDS-ig5cQOrLz0E,1865 +cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355 +cryptography/hazmat/primitives/serialization/__init__.py,sha256=Q7uTgDlt7n3WfsMT6jYwutC6DIg_7SEeoAm1GHZ5B5E,1705 +cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc,, +cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615 +cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mS9cFNG4afzvseoc5e1MWoY2VskfL8N8Y_OFjl67luY,5104 +cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=mFM7OFuZ8cCxUGUoo4Cof6BnCHc_Ibx1AQjjfxnPlxo,13998 +cryptography/hazmat/primitives/serialization/ssh.py,sha256=HV6ZqIjqNNaNUL6M4gQyNJJtZ75EsiSZs6yxddqBrWI,53789 +cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258 +cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc,, +cryptography/hazmat/primitives/twofactor/hotp.py,sha256=ivZo5BrcCGWLsqql4nZV0XXCjyGPi_iHfDFltGlOJwk,3256 +cryptography/hazmat/primitives/twofactor/totp.py,sha256=m5LPpRL00kp4zY8gTjr55Hfz9aMlPS53kHmVkSQCmdY,1652 +cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cryptography/utils.py,sha256=wCnykfmWmp6L5zUFou715snYL9_YLLHjpj4_NHmlhwU,4281 +cryptography/x509/__init__.py,sha256=zciyadRgnCp2DNPPm6ujEKd6EX-8_zOnwtCjJbZBatg,8273 +cryptography/x509/__pycache__/__init__.cpython-312.pyc,, +cryptography/x509/__pycache__/base.cpython-312.pyc,, +cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc,, +cryptography/x509/__pycache__/extensions.cpython-312.pyc,, +cryptography/x509/__pycache__/general_name.cpython-312.pyc,, +cryptography/x509/__pycache__/name.cpython-312.pyc,, +cryptography/x509/__pycache__/ocsp.cpython-312.pyc,, +cryptography/x509/__pycache__/oid.cpython-312.pyc,, +cryptography/x509/__pycache__/verification.cpython-312.pyc,, +cryptography/x509/base.py,sha256=8322G4opJ1a2juXU14Qi2y-G1V-cQHaudo7gAFbTPlM,25998 +cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797 +cryptography/x509/extensions.py,sha256=0AUgutLe26SLg1KaxSeo0fG7fUBAyK2tEgJl9c_AQRM,77968 +cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836 +cryptography/x509/name.py,sha256=H0Bix4uAxk5Ndkmee3Lxv3UdXbdph2XBr-nTPxHo54U,15356 +cryptography/x509/ocsp.py,sha256=Yey6NdFV1MPjop24Mj_VenjEpg3kUaMopSWOK0AbeBs,12699 +cryptography/x509/oid.py,sha256=BUzgXXGVWilkBkdKPTm9R4qElE9gAGHgdYPMZAp7PJo,931 +cryptography/x509/verification.py,sha256=gR2C2c-XZQtblZhT5T5vjSKOtCb74ef2alPVmEcwFlM,958 diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/WHEEL new file mode 100644 index 0000000..4e7840d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: maturin (1.13.1) +Root-Is-Purelib: false +Tag: cp311-abi3-manylinux_2_34_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..b11f379 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +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/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.APACHE b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000..62589ed --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,202 @@ + + 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/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.BSD b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000..ec1a29d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,27 @@ +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/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json new file mode 100644 index 0000000..0e28622 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json @@ -0,0 +1,1362 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "version": 1, + "serialNumber": "urn:uuid:b0f888ff-baac-404b-ad7b-a394cb3cc7f7", + "metadata": { + "timestamp": "2026-05-04T22:46:52.633241040Z", + "tools": [ + { + "vendor": "CycloneDX", + "name": "cargo-cyclonedx", + "version": "0.5.9" + } + ], + "authors": [ + { + "name": "The cryptography developers", + "email": "cryptography-dev@python.org" + } + ], + "component": { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust#cryptography-rust@0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-rust", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-rust@0.1.0?download_url=file://.", + "components": [ + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust#cryptography-rust@0.1.0 bin-target-0", + "name": "cryptography_rust", + "version": "0.1.0", + "purl": "pkg:cargo/cryptography-rust@0.1.0?download_url=file://.#src/lib.rs" + } + ] + }, + "properties": [ + { + "name": "cdx:rustc:sbom:target:all_targets", + "value": "true" + } + ] + }, + "components": [ + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-cffi#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-cffi", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-cffi@0.1.0?download_url=file://cryptography-cffi" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-crypto#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-crypto", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-crypto@0.1.0?download_url=file://cryptography-crypto" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-keepalive#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-keepalive", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-keepalive@0.1.0?download_url=file://cryptography-keepalive" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-key-parsing#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-key-parsing", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-key-parsing@0.1.0?download_url=file://cryptography-key-parsing" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-openssl#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-openssl", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-openssl@0.1.0?download_url=file://cryptography-openssl" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-x509", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-x509@0.1.0?download_url=file://cryptography-x509" + }, + { + "type": "library", + "bom-ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509-verification#0.1.0", + "author": "The cryptography developers ", + "name": "cryptography-x509-verification", + "version": "0.1.0", + "scope": "required", + "licenses": [ + { + "expression": "Apache-2.0 OR BSD-3-Clause" + } + ], + "purl": "pkg:cargo/cryptography-x509-verification@0.1.0?download_url=file://cryptography-x509-verification" + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "author": "Alex Gaynor ", + "name": "asn1", + "version": "0.24.1", + "description": "ASN.1 (DER) parser and writer for Rust.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1" + } + ], + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ], + "purl": "pkg:cargo/asn1@0.24.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/alex/rust-asn1" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "author": "Alex Gaynor ", + "name": "asn1_derive", + "version": "0.24.1", + "description": "#[derive] support for asn1", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a" + } + ], + "licenses": [ + { + "expression": "BSD-3-Clause" + } + ], + "purl": "pkg:cargo/asn1_derive@0.24.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/alex/rust-asn1" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1", + "author": "Marshall Pierce ", + "name": "base64", + "version": "0.22.1", + "description": "encodes and decodes base64 as bytes or utf8", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/base64@0.22.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/base64" + }, + { + "type": "vcs", + "url": "https://github.com/marshallpierce/rust-base64" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.11.1", + "author": "The Rust Project Developers", + "name": "bitflags", + "version": "2.11.1", + "description": "A macro to generate structures which behave like bitflags. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/bitflags@2.11.1", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/bitflags" + }, + { + "type": "website", + "url": "https://github.com/bitflags/bitflags" + }, + { + "type": "vcs", + "url": "https://github.com/bitflags/bitflags" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.61", + "author": "Alex Crichton ", + "name": "cc", + "version": "1.2.61", + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/cc@1.2.61", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/cc" + }, + { + "type": "website", + "url": "https://github.com/rust-lang/cc-rs" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/cc-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "author": "Alex Crichton ", + "name": "cfg-if", + "version": "1.0.4", + "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/cfg-if@1.0.4", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/cfg-if" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", + "name": "find-msvc-tools", + "version": "0.1.9", + "description": "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/find-msvc-tools@0.1.9", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/find-msvc-tools" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/cc-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "author": "Steven Fackler ", + "name": "foreign-types-shared", + "version": "0.1.1", + "description": "An internal crate used by foreign-types", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/foreign-types-shared@0.1.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/sfackler/foreign-types" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "author": "Steven Fackler ", + "name": "foreign-types", + "version": "0.3.2", + "description": "A framework for Rust wrappers over C APIs", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/foreign-types@0.3.2", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/sfackler/foreign-types" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "name": "heck", + "version": "0.5.0", + "description": "heck is a case conversion library.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/heck@0.5.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/withoutboats/heck" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18", + "author": "David Tolnay ", + "name": "itoa", + "version": "1.0.18", + "description": "Fast integer primitive to string conversion", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/itoa@1.0.18", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/itoa" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/itoa" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", + "author": "The Rust Project Developers", + "name": "libc", + "version": "0.2.186", + "description": "Raw FFI bindings to platform libraries like libc.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/libc@0.2.186", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-lang/libc" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "author": "Aleksey Kladov ", + "name": "once_cell", + "version": "1.21.4", + "description": "Single assignment cells and lazy values.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/once_cell@1.21.4", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/once_cell" + }, + { + "type": "vcs", + "url": "https://github.com/matklad/once_cell" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "name": "openssl-macros", + "version": "0.1.1", + "description": "Internal macros used by the openssl crate.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/openssl-macros@0.1.1" + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115", + "author": "Alex Crichton , Steven Fackler ", + "name": "openssl-sys", + "version": "0.9.115", + "description": "FFI bindings to OpenSSL", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/openssl-sys@0.9.115", + "externalReferences": [ + { + "type": "other", + "url": "openssl" + }, + { + "type": "vcs", + "url": "https://github.com/rust-openssl/rust-openssl" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79", + "author": "Steven Fackler ", + "name": "openssl", + "version": "0.10.79", + "description": "OpenSSL bindings", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" + } + ], + "licenses": [ + { + "expression": "Apache-2.0" + } + ], + "purl": "pkg:cargo/openssl@0.10.79", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/rust-openssl/rust-openssl" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", + "author": "Jonathan Creekmore ", + "name": "pem", + "version": "3.0.6", + "description": "Parse and encode PEM-encoded data.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" + } + ], + "licenses": [ + { + "expression": "MIT" + } + ], + "purl": "pkg:cargo/pem@3.0.6", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/pem/" + }, + { + "type": "website", + "url": "https://github.com/jcreekmore/pem-rs.git" + }, + { + "type": "vcs", + "url": "https://github.com/jcreekmore/pem-rs.git" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", + "author": "Alex Crichton ", + "name": "pkg-config", + "version": "0.3.33", + "description": "A library to run the pkg-config system tool at build time in order to be used in Cargo build scripts. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pkg-config@0.3.33", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/pkg-config" + }, + { + "type": "vcs", + "url": "https://github.com/rust-lang/pkg-config-rs" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1", + "name": "portable-atomic", + "version": "1.13.1", + "description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR MIT" + } + ], + "purl": "pkg:cargo/portable-atomic@1.13.1", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/taiki-e/portable-atomic" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "author": "David Tolnay , Alex Crichton ", + "name": "proc-macro2", + "version": "1.0.106", + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/proc-macro2@1.0.106", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/proc-macro2" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/proc-macro2" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-build-config", + "version": "0.28.3", + "description": "Build configuration for the PyO3 ecosystem", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-build-config@0.28.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-ffi", + "version": "0.28.3", + "description": "Python-API bindings for the PyO3 ecosystem", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-ffi@0.28.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "other", + "url": "python" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros-backend", + "version": "0.28.3", + "description": "Code generation for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros-backend@0.28.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3", + "author": "PyO3 Project and Contributors ", + "name": "pyo3-macros", + "version": "0.28.3", + "description": "Proc macros for PyO3 package", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3-macros@0.28.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3", + "author": "PyO3 Project and Contributors ", + "name": "pyo3", + "version": "0.28.3", + "description": "Bindings to Python interpreter", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/pyo3@0.28.3", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/crate/pyo3/" + }, + { + "type": "website", + "url": "https://github.com/pyo3/pyo3" + }, + { + "type": "vcs", + "url": "https://github.com/pyo3/pyo3" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "author": "David Tolnay ", + "name": "quote", + "version": "1.0.45", + "description": "Quasi-quoting macro quote!(...)", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/quote@1.0.45", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/quote/" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/quote" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2", + "author": "Lukas Bergdoll ", + "name": "self_cell", + "version": "1.2.2", + "description": "Safe-to-use proc-macro-free self-referential structs in stable Rust.", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 OR GPL-2.0-only" + } + ], + "purl": "pkg:cargo/self_cell@1.2.2", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/self_cell" + }, + { + "type": "vcs", + "url": "https://github.com/Voultapher/self_cell" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@1.3.0", + "author": "comex , Fenhl , Adrian Taylor , Alex Touchet , Daniel Parks , Garrett Berg ", + "name": "shlex", + "version": "1.3.0", + "description": "Split a string into shell words, like Python's shlex.", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/shlex@1.3.0", + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/comex/rust-shlex" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117", + "author": "David Tolnay ", + "name": "syn", + "version": "2.0.117", + "description": "Parser for Rust source code", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/syn@2.0.117", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/syn" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/syn" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5", + "author": "Dan Gohman ", + "name": "target-lexicon", + "version": "0.13.5", + "description": "LLVM target triple types", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + } + ], + "licenses": [ + { + "expression": "Apache-2.0 WITH LLVM-exception" + } + ], + "purl": "pkg:cargo/target-lexicon@0.13.5", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/target-lexicon/" + }, + { + "type": "vcs", + "url": "https://github.com/bytecodealliance/target-lexicon" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24", + "author": "David Tolnay ", + "name": "unicode-ident", + "version": "1.0.24", + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "scope": "required", + "hashes": [ + { + "alg": "SHA-256", + "content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + } + ], + "licenses": [ + { + "expression": "(MIT OR Apache-2.0) AND Unicode-3.0" + } + ], + "purl": "pkg:cargo/unicode-ident@1.0.24", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/unicode-ident" + }, + { + "type": "vcs", + "url": "https://github.com/dtolnay/unicode-ident" + } + ] + }, + { + "type": "library", + "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15", + "author": "Jim McGrath ", + "name": "vcpkg", + "version": "0.2.15", + "description": "A library to find native dependencies in a vcpkg tree at build time in order to be used in Cargo build scripts. ", + "scope": "excluded", + "hashes": [ + { + "alg": "SHA-256", + "content": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + } + ], + "licenses": [ + { + "expression": "MIT OR Apache-2.0" + } + ], + "purl": "pkg:cargo/vcpkg@0.2.15", + "externalReferences": [ + { + "type": "documentation", + "url": "https://docs.rs/vcpkg" + }, + { + "type": "vcs", + "url": "https://github.com/mcgoo/vcpkg-rs" + } + ] + } + ], + "dependencies": [ + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust#cryptography-rust@0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-cffi#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-crypto#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-keepalive#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-key-parsing#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-openssl#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509-verification#0.1.0", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115", + "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-cffi#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.61", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115", + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-crypto#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-keepalive#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-key-parsing#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-crypto#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-openssl#0.1.0", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509#0.1.0", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115", + "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-openssl#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1" + ] + }, + { + "ref": "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509-verification#0.1.0", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "path+file:///__w/cryptography/cryptography/tmpwheelhouse/.tmpXdQ78X/cryptography-48.0.0/src/rust/cryptography-x509#0.1.0" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.11.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.61", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", + "registry+https://github.com/rust-lang/crates.io-index#shlex@1.3.0" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.61", + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", + "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", + "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.79", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.11.1", + "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", + "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", + "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", + "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.115" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", + "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", + "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3", + "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@1.3.0" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117", + "dependsOn": [ + "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", + "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", + "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + ] + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" + }, + { + "ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" + } + ] +} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/sbom.json b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/sbom.json new file mode 100644 index 0000000..f8e47c1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography-48.0.0.dist-info/sboms/sbom.json @@ -0,0 +1,43 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "version": 1, + "serialNumber": "urn:uuid:7b8d231f-c05f-4fe0-9e12-5dba4b373c50", + "metadata": { + "timestamp": "2026-05-02T09:34:54Z" + }, + "components": [ + { + "type": "library", + "name": "openssl", + "version": "4.0.0", + "purl": "pkg:generic/openssl@4.0.0?download_url=https://github.com/openssl/openssl/releases/download/openssl-4.0.0/openssl-4.0.0.tar.gz", + "hashes": [ + { + "alg": "SHA-256", + "content": "c32cf49a959c4f345f9606982dd36e7d28f7c58b19c2e25d75624d2b3d2f79ac" + } + ], + "externalReferences": [ + { + "type": "distribution", + "url": "https://github.com/openssl/openssl/releases/download/openssl-4.0.0/openssl-4.0.0.tar.gz" + } + ], + "properties": [ + { + "name": "build:operating-system", + "value": "linux" + }, + { + "name": "build:architecture", + "value": "x86_64" + }, + { + "name": "build:flags", + "value": "no-zlib no-shared no-module no-comp no-apps no-docs no-sm2-precomp no-atexit enable-ec_nistp_64_gcc_128" + } + ] + } + ] +} diff --git a/.venv/lib/python3.12/site-packages/cryptography/__about__.py b/.venv/lib/python3.12/site-packages/cryptography/__about__.py new file mode 100644 index 0000000..c81901c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/__about__.py @@ -0,0 +1,17 @@ +# 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__ = "48.0.0" + + +__author__ = "The Python Cryptographic Authority and individual contributors" +__copyright__ = f"Copyright 2013-2026 {__author__}" diff --git a/.venv/lib/python3.12/site-packages/cryptography/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/__init__.py new file mode 100644 index 0000000..d374f75 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/__init__.py @@ -0,0 +1,13 @@ +# 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.__about__ import __author__, __copyright__, __version__ + +__all__ = [ + "__author__", + "__copyright__", + "__version__", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/exceptions.py b/.venv/lib/python3.12/site-packages/cryptography/exceptions.py new file mode 100644 index 0000000..fe125ea --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/exceptions.py @@ -0,0 +1,52 @@ +# 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/python3.12/site-packages/cryptography/fernet.py b/.venv/lib/python3.12/site-packages/cryptography/fernet.py new file mode 100644 index 0000000..c6744ae --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/fernet.py @@ -0,0 +1,224 @@ +# 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 collections.abc import Iterable + +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: 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/python3.12/site-packages/cryptography/hazmat/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py new file mode 100644 index 0000000..b9f1187 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py @@ -0,0 +1,13 @@ +# 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/python3.12/site-packages/cryptography/hazmat/_oid.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py new file mode 100644 index 0000000..4bf138d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py @@ -0,0 +1,356 @@ +# 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") + PRIVATE_KEY_USAGE_PERIOD = ObjectIdentifier("2.5.29.16") + 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 HashAlgorithmOID: + SHA1 = ObjectIdentifier("1.3.14.3.2.26") + SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.2.4") + SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.2.1") + SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.2.2") + SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.2.3") + SHA3_224 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.224") + SHA3_256 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.256") + SHA3_384 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.384") + SHA3_512 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.512") + SHA3_224_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.7") + SHA3_256_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.8") + SHA3_384_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.9") + SHA3_512_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.10") + + +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") + BUNDLE_SECURITY = ObjectIdentifier("1.3.6.1.5.5.7.3.35") + CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") + + +class OtherNameFormOID: + PERMANENT_IDENTIFIER = ObjectIdentifier("1.3.6.1.5.5.7.8.3") + HW_MODULE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.4") + DNS_SRV = ObjectIdentifier("1.3.6.1.5.5.7.8.7") + NAI_REALM = ObjectIdentifier("1.3.6.1.5.5.7.8.8") + SMTP_UTF8_MAILBOX = ObjectIdentifier("1.3.6.1.5.5.7.8.9") + ACP_NODE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.10") + BUNDLE_EID = ObjectIdentifier("1.3.6.1.5.5.7.8.11") + + +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: "rsassaPss", + 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)" + ), + HashAlgorithmOID.SHA1: "sha1", + HashAlgorithmOID.SHA224: "sha224", + HashAlgorithmOID.SHA256: "sha256", + HashAlgorithmOID.SHA384: "sha384", + HashAlgorithmOID.SHA512: "sha512", + HashAlgorithmOID.SHA3_224: "sha3_224", + HashAlgorithmOID.SHA3_256: "sha3_256", + HashAlgorithmOID.SHA3_384: "sha3_384", + HashAlgorithmOID.SHA3_512: "sha3_512", + HashAlgorithmOID.SHA3_224_NIST: "sha3_224", + HashAlgorithmOID.SHA3_256_NIST: "sha3_256", + HashAlgorithmOID.SHA3_384_NIST: "sha3_384", + HashAlgorithmOID.SHA3_512_NIST: "sha3_512", + PublicKeyAlgorithmOID.DSA: "dsaEncryption", + PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey", + PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption", + 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.PRIVATE_KEY_USAGE_PERIOD: "privateKeyUsagePeriod", + 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/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py new file mode 100644 index 0000000..ac3d4bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py @@ -0,0 +1,43 @@ +# 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.asn1.asn1 import ( + TLV, + BitString, + Default, + Explicit, + GeneralizedTime, + IA5String, + Implicit, + Null, + PrintableString, + SetOf, + Size, + UTCTime, + Variant, + decode_der, + encode_der, + sequence, + set, +) + +__all__ = [ + "TLV", + "BitString", + "Default", + "Explicit", + "GeneralizedTime", + "IA5String", + "Implicit", + "Null", + "PrintableString", + "SetOf", + "Size", + "UTCTime", + "Variant", + "decode_der", + "encode_der", + "sequence", + "set", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py new file mode 100644 index 0000000..aa18d7e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py @@ -0,0 +1,419 @@ +# 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 builtins +import dataclasses +import sys +import types +import typing + +if sys.version_info < (3, 11): + import typing_extensions + + LiteralString = typing_extensions.LiteralString +else: + LiteralString = typing.LiteralString + +from cryptography.hazmat.bindings._rust import declarative_asn1 + +if sys.version_info < (3, 10): + NoneType = type(None) +else: + NoneType = types.NoneType # type: ignore[nonetype-type] + +T = typing.TypeVar("T", covariant=True) +U = typing.TypeVar("U") +Tag = typing.TypeVar("Tag", bound=LiteralString) + + +@dataclasses.dataclass(frozen=True) +class Variant(typing.Generic[U, Tag]): + """ + A tagged variant for CHOICE fields with the same underlying type. + + Use this when you have multiple CHOICE alternatives with the same type + and need to distinguish between them: + + foo: ( + Annotated[Variant[int, typing.Literal["IntA"]], Implicit(0)] + | Annotated[Variant[int, typing.Literal["IntB"]], Implicit(1)] + ) + + Usage: + example = Example(foo=Variant(5, "IntA")) + decoded.foo.value # The int value + decoded.foo.tag # "IntA" or "IntB" + """ + + value: U + tag: str + + +decode_der = declarative_asn1.decode_der +encode_der = declarative_asn1.encode_der + + +def _is_union(field_type: type) -> bool: + # NOTE: types.UnionType for `T | U`, typing.Union for `Union[T, U]`. + # TODO: Drop the `hasattr()` once the minimum supported Python version + # is >= 3.10. + union_types = ( + (types.UnionType, typing.Union) + if hasattr(types, "UnionType") + else (typing.Union,) + ) + return typing.get_origin(field_type) in union_types + + +def _extract_annotation( + metadata: tuple, field_name: str +) -> declarative_asn1.Annotation: + default = None + encoding = None + size = None + for raw_annotation in metadata: + if isinstance(raw_annotation, Default): + if default is not None: + raise TypeError( + f"multiple DEFAULT annotations found in field " + f"'{field_name}'" + ) + default = raw_annotation.value + elif isinstance(raw_annotation, declarative_asn1.Encoding): + if encoding is not None: + raise TypeError( + f"multiple IMPLICIT/EXPLICIT annotations found in field " + f"'{field_name}'" + ) + encoding = raw_annotation + elif isinstance(raw_annotation, declarative_asn1.Size): + if size is not None: + raise TypeError( + f"multiple SIZE annotations found in field '{field_name}'" + ) + size = raw_annotation + else: + raise TypeError(f"unsupported annotation: {raw_annotation}") + + return declarative_asn1.Annotation( + default=default, encoding=encoding, size=size + ) + + +def _normalize_field_type( + field_type: typing.Any, field_name: str +) -> declarative_asn1.AnnotatedType: + # Strip the `Annotated[...]` off, and populate the annotation + # from it if it exists. + if typing.get_origin(field_type) is typing.Annotated: + annotation = _extract_annotation(field_type.__metadata__, field_name) + field_type, *_ = typing.get_args(field_type) + else: + annotation = declarative_asn1.Annotation() + + if annotation.size is not None and ( + typing.get_origin(field_type) not in (builtins.list, SetOf) + and field_type + not in ( + builtins.bytes, + builtins.str, + BitString, + IA5String, + PrintableString, + ) + ): + raise TypeError( + f"field '{field_name}' has a SIZE annotation, but SIZE " + "annotations are only supported for fields of types: " + "[SEQUENCE OF, SET OF, BIT STRING, OCTET STRING, UTF8String, " + "PrintableString, IA5String]" + ) + + if field_type is TLV: + if isinstance(annotation.encoding, Implicit): + raise TypeError( + f"field '{field_name}' has an IMPLICIT annotation, but " + "IMPLICIT annotations are not supported for TLV types." + ) + elif annotation.default is not None: + raise TypeError( + f"field '{field_name}' has a DEFAULT annotation, but " + "DEFAULT annotations are not supported for TLV types." + ) + + if hasattr(field_type, "__asn1_root__"): + root_type = field_type.__asn1_root__ + if not isinstance( + root_type, + (declarative_asn1.Type.Sequence, declarative_asn1.Type.Set), + ): + raise TypeError(f"unsupported root type: {root_type}") + return declarative_asn1.AnnotatedType( + typing.cast(declarative_asn1.Type, root_type), annotation + ) + elif _is_union(field_type): + union_args = typing.get_args(field_type) + if len(union_args) == 2 and NoneType in union_args: + # A Union between a type and None is an OPTIONAL + optional_type = ( + union_args[0] if union_args[1] is type(None) else union_args[1] + ) + if optional_type is TLV: + raise TypeError( + "optional TLV types (`TLV | None`) are not " + "currently supported" + ) + annotated_type = _normalize_field_type(optional_type, field_name) + + if not annotated_type.annotation.is_empty(): + raise TypeError( + "optional (`X | None`) types cannot have `X` " + "annotated: annotations must apply to the union " + "(i.e: `Annotated[X | None, annotation]`)" + ) + + if annotation.default is not None: + raise TypeError( + "optional (`X | None`) types should not have a DEFAULT " + "annotation" + ) + + rust_field_type = declarative_asn1.Type.Option(annotated_type) + + else: + # Otherwise, the Union is a CHOICE + if isinstance(annotation.encoding, Implicit): + # CHOICEs cannot be IMPLICIT. See X.680 section 31.2.9. + raise TypeError( + "CHOICE (`X | Y | ...`) types should not have an IMPLICIT " + "annotation" + ) + variants = [ + _type_to_variant(arg, field_name) + for arg in union_args + if arg is not type(None) + ] + + # Union types should either be all Variants + # (`Variant[..] | Variant[..] | etc`) or all non Variants + are_union_types_tagged = variants[0].tag_name is not None + if any( + (v.tag_name is not None) != are_union_types_tagged + for v in variants + ): + raise TypeError( + "When using `asn1.Variant` in a union, all the other " + "types in the union must also be `asn1.Variant`" + ) + + if are_union_types_tagged: + tags = {v.tag_name for v in variants} + if len(variants) != len(tags): + raise TypeError( + "When using `asn1.Variant` in a union, the tags used " + "must be unique" + ) + + rust_choice_type = declarative_asn1.Type.Choice(variants) + # If None is part of the union types, this is an OPTIONAL CHOICE + rust_field_type = ( + declarative_asn1.Type.Option( + declarative_asn1.AnnotatedType( + rust_choice_type, declarative_asn1.Annotation() + ) + ) + if NoneType in union_args + else rust_choice_type + ) + + elif typing.get_origin(field_type) is builtins.list: + inner_type = _normalize_field_type( + typing.get_args(field_type)[0], field_name + ) + rust_field_type = declarative_asn1.Type.SequenceOf(inner_type) + elif typing.get_origin(field_type) is SetOf: + inner_type = _normalize_field_type( + typing.get_args(field_type)[0], field_name + ) + rust_field_type = declarative_asn1.Type.SetOf(inner_type) + else: + rust_field_type = declarative_asn1.non_root_python_to_rust(field_type) + + return declarative_asn1.AnnotatedType(rust_field_type, annotation) + + +# Convert a type to a Variant. Used with types inside Union +# annotations (T1, T2, etc in `Union[T1, T2, ...]`). +def _type_to_variant( + t: typing.Any, field_name: str +) -> declarative_asn1.Variant: + is_annotated = typing.get_origin(t) is typing.Annotated + inner_type = typing.get_args(t)[0] if is_annotated else t + + # Check if this is a Variant[T, Tag] type + if typing.get_origin(inner_type) is Variant: + value_type, tag_literal = typing.get_args(inner_type) + if typing.get_origin(tag_literal) is not typing.Literal: + raise TypeError( + "When using `asn1.Variant` in a type annotation, the second " + "type parameter must be a `typing.Literal` type. E.g: " + '`Variant[int, typing.Literal["MyInt"]]`.' + ) + tag_name = typing.get_args(tag_literal)[0] + + if hasattr(value_type, "__asn1_root__"): + rust_type = value_type.__asn1_root__ + else: + rust_type = declarative_asn1.non_root_python_to_rust(value_type) + + if is_annotated: + ann_type = declarative_asn1.AnnotatedType( + rust_type, + _extract_annotation(t.__metadata__, field_name), + ) + else: + ann_type = declarative_asn1.AnnotatedType( + rust_type, + declarative_asn1.Annotation(), + ) + + return declarative_asn1.Variant(Variant, ann_type, tag_name) + else: + # Plain type (not a tagged Variant) + return declarative_asn1.Variant( + inner_type, + _normalize_field_type(t, field_name), + None, + ) + + +def _annotate_fields( + raw_fields: dict[str, type], +) -> dict[str, declarative_asn1.AnnotatedType]: + fields = {} + for field_name, field_type in raw_fields.items(): + # Recursively normalize the field type into something that the + # Rust code can understand. + annotated_field_type = _normalize_field_type(field_type, field_name) + fields[field_name] = annotated_field_type + + return fields + + +def _register_asn1_sequence(cls: type[U]) -> None: + raw_fields = typing.get_type_hints(cls, include_extras=True) + root = declarative_asn1.Type.Sequence(cls, _annotate_fields(raw_fields)) + + setattr(cls, "__asn1_root__", root) + + +def _register_asn1_set(cls: type[U]) -> None: + raw_fields = typing.get_type_hints(cls, include_extras=True) + root = declarative_asn1.Type.Set(cls, _annotate_fields(raw_fields)) + + setattr(cls, "__asn1_root__", root) + + +# Due to https://github.com/python/mypy/issues/19731, we can't define an alias +# for `dataclass_transform` that conditionally points to `typing` or +# `typing_extensions` depending on the Python version. We work around it by +# making the whole decorated class conditional on the Python version. +if sys.version_info < (3, 11): + + @typing_extensions.dataclass_transform(kw_only_default=True) + def sequence(cls: type[U]) -> type[U]: + # We use `dataclasses.dataclass` to add an __init__ method + # to the class with keyword-only parameters. + if sys.version_info >= (3, 10): + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + # `match_args` was added in Python 3.10 and defaults + # to True + match_args=False, + # `kw_only` was added in Python 3.10 and defaults to + # False + kw_only=True, + )(cls) + else: + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + )(cls) + _register_asn1_sequence(dataclass_cls) + return dataclass_cls + + @typing_extensions.dataclass_transform(kw_only_default=True) + def set(cls: type[U]) -> type[U]: + # We use `dataclasses.dataclass` to add an __init__ method + # to the class with keyword-only parameters. + if sys.version_info >= (3, 10): + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + # `match_args` was added in Python 3.10 and defaults + # to True + match_args=False, + # `kw_only` was added in Python 3.10 and defaults to + # False + kw_only=True, + )(cls) + else: + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + )(cls) + _register_asn1_set(dataclass_cls) + return dataclass_cls + +else: + + @typing.dataclass_transform(kw_only_default=True) + def sequence(cls: type[U]) -> type[U]: + # Only add an __init__ method, with keyword-only + # parameters. + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + match_args=False, + kw_only=True, + )(cls) + _register_asn1_sequence(dataclass_cls) + return dataclass_cls + + @typing.dataclass_transform(kw_only_default=True) + def set(cls: type[U]) -> type[U]: + # Only add an __init__ method, with keyword-only + # parameters. + dataclass_cls = dataclasses.dataclass( + repr=False, + eq=False, + match_args=False, + kw_only=True, + )(cls) + _register_asn1_set(dataclass_cls) + return dataclass_cls + + +# TODO: replace with `Default[U]` once the min Python version is >= 3.12 +@dataclasses.dataclass(frozen=True) +class Default(typing.Generic[U]): + value: U + + +SetOf = declarative_asn1.SetOf + +Explicit = declarative_asn1.Encoding.Explicit +Implicit = declarative_asn1.Encoding.Implicit +Size = declarative_asn1.Size + +PrintableString = declarative_asn1.PrintableString +IA5String = declarative_asn1.IA5String +UTCTime = declarative_asn1.UTCTime +GeneralizedTime = declarative_asn1.GeneralizedTime +BitString = declarative_asn1.BitString +TLV = declarative_asn1.Tlv +Null = declarative_asn1.Null diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py new file mode 100644 index 0000000..b4400aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py @@ -0,0 +1,13 @@ +# 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/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py new file mode 100644 index 0000000..51b0447 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py @@ -0,0 +1,9 @@ +# 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/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py new file mode 100644 index 0000000..ad64a2a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py @@ -0,0 +1,312 @@ +# 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 + if rust_openssl.CRYPTOGRAPHY_IS_AWSLC: + return isinstance( + algorithm, + ( + hashes.MD5, + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA512_224, + hashes.SHA512_256, + ), + ) + 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): + # FIPS 186-4 only allows salt length == digest length for PSS + # It is technically acceptable to set an explicit salt length + # equal to the digest length and this will incorrectly fail, but + # since we don't do that in the tests and this method is + # private, we'll ignore that until we need to do otherwise. + if ( + self._fips_enabled + and padding._salt_length != PSS.DIGEST_LENGTH + ): + return False + 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 + and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC + ) + + def dh_x942_serialization_supported(self) -> bool: + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + + def x25519_supported(self) -> bool: + return not self._fips_enabled + + 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 + and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC + ) + + def mlkem_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_IS_AWSLC + or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER + ) + + def mldsa_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_IS_AWSLC + or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER + ) + + def ed25519_supported(self) -> bool: + return True + + def ed448_supported(self) -> bool: + return ( + not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL + and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL + and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC + ) + + def ecdsa_deterministic_supported(self) -> bool: + return ( + rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER + and not self._fips_enabled + ) + + def poly1305_supported(self) -> bool: + return not self._fips_enabled + + def pkcs7_supported(self) -> bool: + return True + + +backend = Backend() diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py @@ -0,0 +1,3 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so new file mode 100755 index 0000000000000000000000000000000000000000..2ab847abcc4e1c78f5bcab95916da34bb9979f96 GIT binary patch literal 13844864 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⪻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

btE* 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

?xB=eR0ttcLokB9OluU)LM=rv{sk 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¥nG_K&L4nj=ZvLasA#||%3fVmT*K|rVD_VLCo$@0 zas0TxP8guBu#FuW@*ocqUc**j32oV1qQdyK0`%>+1d;{0a<>~ydR1CgrGlw zoe-0?pY@LS%=c7d2?vPck3&0~y7Mtv#webW+S%$x8^fGmsx!B#3okr1@>=lAN6y}* zf~=wy_v84A@dF}3rhCa?5WxuqRj

QLjb?Eb3N9f%5aOjl%kr^q!SEwEBl%5XIC% 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 z%0A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 z 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 z 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%0Ad~$?Nogwec)D#c-ed z$dq#|SoiHb!HNqU@EmJ~rTDp&L#|-UadylYzeawUa4$0+V9O&cUhAGSe4TmwdM$3Z`{JhP4zp+maxP$F)#mSCCCaruiV{kw{S2DTQIA+|zoCOEJbB?Tj?>yKtc)M{N zVR**#$AT$`+{fq-o_oeT#DvFK|IvNBO#T142Q0apEeFj1Bp)2|ER#Q*{~gMlX8KqA zX3Zfx`iC6fGv>Kt$?a@dGWxr9nXzTZk$0N6vu_sM!J7M-{zHFeoM*?8cbWH}_Q#sr znf}ZEIN&_Pe~aVY=DW`NV3OH&*Zcgl@jTvpz4bmjEsu-V2h+?iTp!GF$OV=ct@n9n z^MvdDo+tD0Ami(=_iuF8Vf-HR+)z97#oC$NXuaQ2Y21z12aDfp-J7frrdV?aTkc`B zWPNaeIgc>C>3W}YmM89Hax?L;y!m>cUl!*r*8BXj_FIbkeb(hD>u7zjzc1fLKSsCJ zkNNG^2dl0yp3{u(V!qzHuMbYMxW{_mS!Ex~*9S|k?&iEnggS;76 zaKMH&PcfWa@9&tc!{to4iUl*4+{tRgdfz)?Jm(p1v@Q!q1N&vdY|45}9wmN8kJkPn zad8#1T;71 zFLggyvSiDHjNfP+n>Ra;k6Q0?=fU9>^7}FUIKuEs=fI3BnRAK-cd%r^iu*a>92*{C zd{BOw@Dx*??aLo9?_u+Biroi|V?0+Ur>77QEvV#K-L zZ@900{nhUGr|kRN>w|5~POcBmG5oH)e_GsJ$?SW^^?u*JSwhC}pH*(f$Kkc>gG0S%#2lt2($Cxnf%V*_> zIV-k2*4O{Z`SkT%^m+3!VarK|f0kdy+|7g~Q_eEyVU|43ibD=~hBbqu_RA5rj2Zq# zp89%jXMRq6O#bQ~aKN)1a`6}R`1cQ z>-?Dn6F%pvp7RW^GZCC+#id^o&vhq)an{_<^m-G)UJiMLgGKuH?Ob$BT#Q&=FcB>0 zfU9~hoCx+ay=WqsV}2v!nB90H$iFO(n@j{J*f9EE?Kig$2e+9BI@U`kf^A8;Ae#tAt`^^8CxR)qkDCZ)`tsu^f`%Qt-X~53**DGmWbF)}X1^Rf zU4Bj|=km*xtC&AyBG|$1IpXNscbSLP^Thcr`+dPgFwJWBL{PGM;Y4tZ!xv2i=NJ~| z`?mT^oD;KGikBr9eaE_tIOK9>ud*&HPP2Qp{4grDpH$C++3P2Qn)MsZ^IiSkWZ#V6 z?A%#$52Lr(KU1Dz@mA;dJ?p-0!skql=NYyKCVaPsINv@I?CQO2A~?YKo$fC?b`0M= z5sb|{pIQA_^B}9s^<(iK@%+Gk-X|W0SI8ghE1eg+_siQ4#r1)SU9~`?+~J%A5%YoMg+K;cq8` zeSJG?M(sp!y7zC+X+gf2GUYZF+{NVY&X+k4v*t;Lo%NY=@h|L;%UE-aEmL;f#^gWp z!|Z~|;54IfG8p@%b#6H6bExWXG8xP>zv*PK_*eGN<&17V8SG)qnZEv3lfe;ox19_Y z`u5vR2BW8~fBQ*)M`^x0Oa|LHST-5#?Y+-ru;|y~yzgYNg2e+TgQ|DsWN?DzBPN6B zH}aWUhsorm@5U1k7g+LKU)~^|-)evCWN@JOarVLL@smN^n*WKD!A^G1o%Cm;`JXo# zEW1|R&z}s&*)Z?RFBHe`%*Sz-g>@OdWYYf!GVZ13<&aB$ul^GIVE9_`u;b_%{a$B0 z<8m@6nQ)fT8zzI`57xVM()Sg}=YI3|WzH~r<79BKuYZR;{n7YJJ)?I{`dtCaJj3wa zlfJJ&JttYuT8GKy;$-z+d0{wb|7VSRzjme{G!JWb><0GxC-omP51S89`kn&)KVn^G zJlOjQ=k#ai^_fZEQ()X@CxiXWzF}~55&P{XkW~J>|Xrcyg!)?b}+tXGMHoaQ|H5$ zQD>c>%P+$P_l6zM^yOc>kN?o0yEx!JHk@V0BTUYS@1OGZ2l27|k9wxp*${M07HtTo z|0SLaHUzUA-gH9{oVPwl7~O0`u!<>j=G@DYvmD-HLvXCGzvYHtp2e*<1f%~p{&wbN zy=+6UpTkl8*xY49aGKd&t=df7&N%1lNq5Hw^l=>T)|EI<=;v@&$ z#Ub~xWySDk^2V6QnDG<~c76MTbGh+FKHsW8bJi?)f&+FOa@kF+&xFw*of8x8;^3V5 znf}drEYXkC4FB$4Fyud}TFuv}_;0O~oOnHhqJ9bP)#C^Suz6)8tILC02aV*&O^;~)j<1g44 ztl*H_7+$zB*u|Lp`*zMT;}H(o_T`H<24lChE>m{g%_!XH^Q8J+e`7Gq?1meI!z_86 zHHU0&ywPWV#Br02K4)s(CC0O4$pL43Z@Mu!!{}xkgV9JloMOS9EV-8*YeqNU7@T0v z;MUgTD5DYkW6WJlxQ{6-W<12^7V^mOmK%e`w^5G7#ddUKaGv3PjJvJ%R>}jzbsK}p zrRIO6eX`iTF&Md>_ZOG5X3FTv*6HhctS>*sc!p0C|Lw(*i<>o<-@&?E#r&Dhfd%JS zKTAHCJ$GZUa+!8cvEnXvJiu_*M*q!YpPXmT^DH^?Kk~{IjGwnL*u#oP`g%5OxWJYj zqvvl7mfTT)j=rEcAu0_rnUQZH)S4V zd~9QIjxCeBtN-#wzYAPkU-dlP!+acL$&~5W2 zJj;@c?kyfhY&g!2I~ji0J{a);V-A>co+Zz-;*u5O;V5e+Y`KHc_uN;;oMXZxOxdvD zIS#n=KE`te8;*0xjN$h^XH0mADUUJZJae|JIC5X(xspRpvE>eSEEvx_e`Y+voC_>? zjwQqUiHBosxs4rnG5mpe7_nl?<1BcFC4({XaD)TK9CDiB58ZP{oMFs^OjtALX;xf( zf8)7~HOJU6Wyjr&f8_nhlt-BJBnzHq$&NKgA7DS6WH^);#@x$<2bl91E4Hi|JW%{x z%9bk_{a9X@avyWfvSiJQC)luK$7P9eKXFf(aGEJ|X57P^Gc0(JC2LkZ!G;|>E_;yp zudyFyESPgY3(m3Rk=|3zhYf=V8~0QDVauJ&eQfj7M0qVZ~D%@GNUCT4g*V4!N8y zSFz(X!{3{q5%)0W3=1A&$zy%_jPqpp2YG(DxVef2r&%&*c-DPj_g8T+{+svDBlPDA z793~I9Sr~OJ;<1|OnI0&8x}mpisu-0p3{q+3s*Ab6vKab?iq8YujfGyxzLyY=^l-n zhbuVbIP-sbf3f2}M(4dRnDP)a9%Ih=zWi_Roz>#vQYQa#PR#ymJvPCn;4I5vF2Vzi#G*x%x<(P7)#B^ZEQHhjz?MFWK%G@ zPCXNLoMN?VZ(#W zZ@DR0G@+hL*>H?Qrfj*59d|Lg)uvz{Q&!A)h&hjO$d2W0jGHw6cG?-Eace3GLwmiV_PMiE&oAINYf?y->EKf|hgV9~|>+3n_>v@zZPcq|a z=ImH-@h0nX83!C=&6EwdamZb4xsM$yhIh3uMm)xt^Gw(><#}dYGG(2+8OQb>*4wOn zFZYA-eU!H--_Jf6Jz!ICf;ks$6~_bJKMpy|jz<_J`Z3{Y<~+}eBh%J-h`5HU)Lx&NJ*7J<2{W-V`in#8n(HW5b;sa&O;0E+6dJGF~nI zN1K-sQ!Z!5Rm?ffk~s(5$7YRt($}xEk4%{{JB~A2Z+%vr>B|$=?aOT0a^x}kGiErs zDOkyvQ%t#o84Kpz&ysT-@Ca)*Y z|5$N`*|Y7huYa-k%y#Q@1xs#Y!(AM_%yY|PkN4S=tow53&EggI&6e|Q_FCu3=6|(u z3}0s)+c${wDdM?Q-dXRLUxsgSUOU8bnR8)SIcGNS6!%k&dzX8{j#CWZ?Vj{y9%ekd z$>(*&!P9In_r7?VcBV|;vnklkk|k@-F@3M~`!YLLbLM@zbA6xlWz32h4{^YTL!M@I zh5ME(a|M$t#n1SI_S=_vlFdLqo}vGT#Lpr3viPv|8Gpq7pDB*3tk2}5&V}X2jAwdS z9-gKA@lC-bD{kk2yP18$ewgzJ3pT8HiVZssxp=4ia2c~t+9xY+XZ{xQiv+d(E&VvIM%xe9aepZ~%(eHEi!Q}Jq16#(=RsMqez~YPgGyjtK`ZCXQz(u>n z$%yT7&o8sDxi>7X_WW}2O?hU+J#0C{=!E#0vSz^(9B_d{o@4Ya_xE|?`Htt1)pzx0 z`+fKS`S$%od1b``TOMb}A)_COmoXQ;z`R_=ZfJd$KQ$y~uo=WXGLs|Lncrm#;S!EPb*0uRrDY5X;LArh?NP-efA6EacqNC;Y-cKrHn?Wf)z|S&Yas=ayMI+ zjPE=Z9AwIx9Z&T2cQHS6o@2}KW%hkn^E14g`B`$D6*D&6$szal<-41YN|+`&yqdXIZgfbU*7{A}&rd9h>smSL<>Q!$drc807$=i&7r~NR0mvdzCZujbdd1l>1=9fE1 zR`2otVffyuAbLCJv~&1A`DSya_%1V!yOsr+3KDVegmTkDHIlC){hMpOml4 zIId##IrommmyKui75BcczuLO*GiNOdh*Am|?bUv)|#X{aKrXGpt^|Iau*t{Vov~i%T~L`x#Z6gOjY@wb^&i z8GrfaU~EqLip{~^-h-QibId-wIXJ@lbDM*%um8&CVDT0D zAKx4-W5*pFp4jaBI?ey>&B0MtKe8_4;bxyZH4nSKo{O(E{u<+%aEuwJ*l=H8|5M{R z{F(NH%0IU+4!D~gXBaMQ4h}NqvA&+?ng2?@-tRf#cGkb%>^tE-f7d$K4_N>`%_0IZrbF^X6dbK)lRZ{l$4OIk(yGqZKz-d`S7P z)@RK*R)3QZrhga5ht2a3`(V!Fefgi(|A;s^$>`skeV$a_x#*C2xst_y#KkJu;&&UU z=NYyPt}^aATl`z4xVefYGdA4IArG+SfYEig1V@?jBrA3tTyIM-{!#re+7iq#xxRL$ zH`o#^`($GUHwrJiw9zRy@i9PqOA|Htaa$;v@FYW$ZY{@B#M0 zh})QPFLNGX#ba#PvSsio;~uyrSjvbim~tBn?qkJS)~wm^1Uq(&6LEanI3~s8p`g$H?&UqGWS@Jw9F8PA>ILev{qupD44%K+h zvEUIVFWeHG>&smHMdLZjdUW|=i~Y_X3J@I%$e+SZY+41 zC69B!Q;c5Y-ZSH}uNwDS&oiUabHbR1nZ8+mzUDr?MVyRyh$)YAz*DSwmJJtu-MwPO zmdn|36~niRlM!>q+{1)3>^R?--!87kIvitnne`dJLtIRFoW(ok;T!VGm;u4|mZuoKN8F6L=xXyaV*Fn3L$)k9nDZWEeT8_wDITT_uXJBn9`s&ecG&rz z;K!XWqfe;k@H6KBmi^T7!IIlKV8Q0I-W$C~?c>|l|AKqX0c$oq!Ilf`c#ioO<>@=( zIOhDAvts;Zd1S(t)&F{~PMU|Sm>-uP7Mx?rBdpkPz*7vrDj(mqE_0@heyqOXz56}= z7_sJZwpZH^t8Y1Zn{>1afmM7S8f#o&gV8hWL ziRYB_W5xZ9e=2WGc#0{{GUKA5^%=3^at^qPHK*Bf7n7g)bA%~pS@JL|9%sDZ&$%BP z$GuE`;r+rPTV}s@KYwD}Z{(8$9$?&xp9z;-qy6{JlNI+cKI8qygf(-X?%TQelzF&} zHOJWg!TKEjQGOZypFFYTlAr3w-RI`ILeqw@86vxOCIHr3k?5Z z-#?d^f9l7T|*F183RtFzXv_4HlT(*!sV+K36c~ICEw! zxU+A+$yUFI)_gbH8Z0?&{hRB@^cGu#8J4X3dY)i%%dNp#7Pk_|ueEa(2b^ZjoI~zm z%Nce&$S^V=Bc5Qw1?KG7aQSbH=PJgx-Wu#+!h#w1v)~*Xp5TxRYP~s-D&Dw)*_C{qj6(F1c17Zof5{WX0_qa5rm~ z?0A^r9pt-jXUlfk)?oB^>i=hJkTBvTV{T{4-ON}r=PU~zX2W?7*|Oz%c3kp%^WM?? zjF>RtBuf??@-RCdXLzTr{_asf24}3x5f)tDm+xwQw#&B$i~peiJ>``V#~CwY!kvBn zeYOTC`!dh=;ZBs*?rwpKow zq~>S1&c6D3&iD1}oyTA7chYmhjyu_G(4Wah_n1RQ=ae^DhXr@Ao)QPcE#m#F{cW`_ zqiOE}4jyS8W{+~-f78x{1t(c@J1g$yfF*0rvf*J4d7LeW?0AOZqn*d!t;Z3@jG1y3 zb8chBk_`{DWy3IYu8i0*=Hkvixr`~t7(Lm3SUkme=FgFLw$HV%e~4$7_*p-1YjB9w zZu4>Q;;r7z+PR(8OT2HGy>x4Eipd`L_g~6f#fsZ{U+$dPy+S;xwx&GV!F10Qr?q zXlKpE*A4w_v%Ik57^C-!i|PBs(br$${loI0_Ul>a{o-WyA?MDTixz4BhJ? zU~`rC^aaWv7YFN4Z4Hia_!-Z`g`uCR^ydj{HY~p29$sYqFM2<-;SB39Id8UKlfO{E zuge3oW@~Va={M}>`sTaZdx+sT-4o^$Vz=zqd_GX9QrIXvn7*z!0#4%vLy`QOmI z-*casaTk+$dFktUm^qI#{DE`s+qrnLJaL@a59Nj7kDL>eAA4W2{)zi~Bm20<`LjNy zy)XaN`Yd^d6@wc)H;%Ao%!VsD{F(O+Tkd7@bLY(P7vi{yymJ|2jxk}%jN4dn7c1`L zfK^}rE9c9S9XpOLG4E;bAI6+y&h35uuf@-TbzlCC@hrLcru?mZFlxOQnR3wA^Eew0 zSzIgcH#7cs)?va)7H7r9mM5A1#l5<@c$u={j=ua?^E3XNIQlYAG3(@E#Jv9yFROo= zkLACt+n0HU0|vJ+A4fQ3%=o-~G5xo3%z21||9Fov`mg(Z%l}W&{m1u_&xr#c)S6vI zQ4ETp7!*Y@h!qsWc5SS*owjM4c6$tpA{Yd*iVwO3#h}Phd{7j{pa{whSA?rGT)(bP z&&{14If6m)ea>Ic$NTr|{d&LWlkN7Csdlbo&M_98;D9+x&Tz;%hQUN|lo2b&Jk5;f zSg>Pvy$Rp_(f|4r!6Z8#?#nlr@ZV_0^CV*~GU0g^ToGBH5lgOP$Bj%@Oa%LwvS7wT z%sJ13Cpciul4m*O5-YB_t$Z!G2!cD=?_I>t>aO<7jz<}<)t?DZGvztvTxM}MagEpy z_p{|87I!y}$vxa74q|z_v-5kPefMsVM^?PR>_PH=7yG!}`b?f=9K#9m-&Gu(Vz}A- zj5y!d^8}ME?pRxCkqzaCxTNP?oxj@?p4q58tX86n*A_;mh-;5am*M$d%}B| z#mzxqevW*zJm5UpnAQcu(WG zmNmy1zS48ompRwBzsk9>Iw)WF64$HchXYQrWx@0{`g6!3D=x6%8Fp-Y2iCi{`q!C< z9VZ#R-aH(>LH;;6JQHSzUIAj zv+v8Rzt!d-zMu6t%8uhqZoN6!$9T;d|7f(dh`oMFZ}<~+*i{+oRtT|X|fdVui{w7&;#4i0g!VYBxiJ71n-#bq`O zH(2Mv;$`!Y&A}|ghwIPe5u1bPLCTkJ4%RW|7*kHNdE{n)XJEcD^D=qN=3vc(ozs)V z%jzoe_T}B1{T+a~r#1&mOs=usL&W#g&B1`xGdFv0vij$4_I+>jzg~Usb(@2hgOAwn zL+!UR?qT|WQ9ElMVawx;zqHx+udQ=R`@@~vx9o$1?`#fMjarXuS#ylpY58Hn`QGo! zKZm@~xBu9AJ;FZEZuY%uc{sN@SmclwSY6P5xp6nx5*+EhsI)jw7|@7og0vYOu#9P7)+ zxA?xWaVNF}M_GJsOR&U2wIvvRynDoPR@}#q1*5NQ3DQ)azGi-=JkG(lw|LL5b2_~x zIKc3G^2OqZTY|Rtms|W@jQ#$XdD(Cu(^kLU3;Ho*aD{fRW%<9>WAW=P!G3m}>&w5{ z5}fGEY?%Gd{F~(C5BA0GPvT?rSM7a0FREgE5wzWp@3ozPD??E4Iq5`7hZT zob0{P*5Di)F8A%>R`1R+Ki9CjbZfAlff}{&i%|*n!m5-;l7?jMys|4 zOMUxojJwMGYqok%uQ>0p)q8sN=LvSKncZ<~aF)fLw+3s+ts8F*3P$(c8Z5AUz}8^p zllfrlGkKVL4tb6>FZT5h7x&fXW5R+H9P$98(XGKz4%o2dg}!{b{!bC-Be(i~uXE-R zhGScUbK~(Mf@CW+ZvqV zaJTrjns2YXu)9V)96Wt%Fq&z9ws_gGVfY;FEDwlxoB5w7UM4)kjK^8>JcsQ1_UCW) z{#IqNayF=kv~&ND37a=?o$xoXP%T+50H8*XCuW^pii%hq6y z>0$Sn6~jHsZ+E`Tm^0@L3(j%KV=Uj{-tJXjx;LzNm>o|re5dDx5zjK_5{Hbh(VwHN zInIXr*dB2on7+rpSp1`W>@$u9Tb4}TuOA1j+3+0W56EZV&XxaQ9*%IplpPPV{Gj~x zWv+ax{vUGBm~kUZ=Bzlw;fJlma40TDyvUfVo@PAPGG)Syo7l2o_z~;&?H@C)_n7wF zJfARc?C@tQdbvJbjrfo?$;+%jzrEWz9ouIM0?R*s*5% zRp-N;OAHs}-DnW2;=K#{w;a)u<7e>kOiw=sNae#NIAG+=67_( zc}W(`G3L>}{zh4FhUv;I7=Mv`tjhd9MeE-@3znJPG7H8E^{cbsBs(rMyj2#2FSbtP zPvRUhXT=%ToMXphjBlF-HKRLZ!RnVNGiJh3rW|L+eau;~;2{n;&yputbCDg-Gr6O9 zUaCLWv*JExcgljp?0AZ4Z2cMY-&>pv?<-F)Q_qden6cy`hWC?K4%xDOK;~T#*54ow zmJi8-4X@D7l=(yLvoG@^TdtiI->CetWbjJsJ;Ht&U2b2@c#=66dmm|h@0dJYYyTW& z`6zkj@X_|w`c% zA?KOyH1F%o$DH|;=YBsKrS+MR6;@j^zV9jIfSTlTf7Mx}D zoXq=a#l;nejAz7@>zHwjIVV^!=YTUTImaQ7vSP)Wr`hlvTQ0L>c%AhR*dHUVXUvUE zm@(ykW}Ict!z?)DfG1gUkwczm#f~*szR~&|VapBdm@<5>d@$BrxCV*P37!iXCfGiAa_raZum2br^E!DAe7fh8Lbd4Uy!w_2a8*)V3yQFa_> z_yYN2#DXyoG2uK@o?ynBInT1-5(ivy*!qk($%{QF ztT^&E@yzJYgfq-I$C5`mWW|c7S@RszSGZ?ySI>Edv+~1;XZres=6{EHxPkGjohLi) zXZjlPF}Y4YdFGgNWXre|$8qPutT|x*Px8{&^E4YSF{<5zBhKyH z_RESh48J2!%y^15FS6t6_sGvbTZb7pamXpwoMyuTqtl*WW<14?=NNz2e0}+Q^8H?U z`Mz^y^Fz-Chd-95_nD9T*>ILE4>M`(pDCA^amBoQ#}N*>fi+V$Ke2zdKXqRJXkCu7 z{dfCibk6g`n8#Rhi8WWgpFh{H_dlF3!(aGZV9M1WFyDFmXY`-;&-#Bn*DRLY^P}S7 zG#egf@qhNo0nf8$@ImwbPP~jb$#mI0WzMtg{v_WYvfn?;FH2TzxX6y@+5W};KCH}f zCVzLHy}`DiVa^MEdBrw=rm+s!G3OWyPH=F;ZNYI4d5Sd~cI+5lvMpHo5%F?_F*h(_ z%9NALcz`(%a(JU{-qmYdX#7XrJFaJTsdf7@5B25Ctj~^TSlrmUeVJ=MX8o1MamWd# zH`x{x%y@{&O}F`;x_X{telz<$W*)BNfMcwfF&K2WtncXDSd3`@ zlyO|g{4Uy=+*Q69-fdg3%=Yf?!Kdx>9`eaBb`H$h_4W7M7Hs^CI5^4Sy^Qb6Jjt4i zYJ4W}muM^@}w=LMjh6h>S$NWsz+vjJ^&uQiyaL9SKtl0516UM4I# z!7$ktoM8Ph_xtn4Gvjbnyi70O7Obv}XUt^GIHt_|dd@KC919-hfE9;aWX+Z>gD+V3 zQS!{32?rc!$$e~jq_2Opacns9MLuR*u%F>Z`{s}*S#gmy&$D62mMg!+Cv5Y+W_jT> z>nA!l=2zH1!%gmgU*-}ASJ~%D=P~a2X2OH4InV6L)@93v;nhBO7&G{?{BSjM#%we3 zvfj2WSm^7wi;v|F_vkC)+9_{rcWv{1cjKo#zsxwnoHU z**9}8u;3XE*s|h^T0AfJxx#`Qna+xf$+h;+k}JP$9gZ+L=y_oHYUj!9HTKW)wfcQW z{lGlTUMCJFueUF@Z*cGaSv-ft$>cigu;OxG|3>%gwD>sL`zCo}@@9Ev&a-_zmsoPe zcdhpp>oH=+hKHFQ_W8t|%Pij}?(fMD3s!G89~&;Q<%;h+mv`7F6K-J2l-WBy4;*lT z6`Q{P-OhQ@yzg;u8NJszv)~EF?{j~CAb(6aoOhr5GLQDoNPbpFfPhWB)8!GyG@w;H>#h+dqfj z^?Wk^p14?k-}?Vg`=WNH%vo@@um6Gk^ktr9!xcZ{5A|bl#yre^WS)OF{$HIllOM}J z!^SwaTz$@bKeZ0ypE=LI{O>*&I6UY6|J=CN^UL}-#xwt;y#I%FxRy01*mUkA+rRp} zV0yjnLGlap+-bY-gvm3Hu)fRopygogcJHIM?%lQr8I${M_YQAybAdS<4j-^Ri2qZ+ z4cmkL%#!WCH*UR$Zx1fA9NiwQ`K5Kaf%WCvz3bcjW7~sCR-9q+=w`t$BOU2 ztaHWo;2;N_@4a$+aF)aI?ZHT^eD(HV6H88UII%rA-q&y09-LyBZ4b_|;hO(eziqqs zSBq!+_TU)HUE94=OdMSEKgv_uS#!W}kA6&eiUrTJWbiBd-nTv2*!!&Q!5ow4Z4b^c zd$Igou+B@x#lg$vm-#Ej$L?DFn7(nle_O&jZ;}sobK8U2zFcnieQfi5)INW0y-&+8 z+t0}_ho9f>{nqZokLBq%`f;4q@2$h+kMhWzE0)CFZ4btn{CRsY&FG?e`g&eq$>9I2 z|5x*|{JXsOWtPl>9l^ z9sX>mo+C^y*%2fha-7kPb_6*GoMjm92&%rGr~CRF@9_O=adI7p9Am`^wmiUS<&NMG zo15(jmiu~!zgK^Y9l=J%%oyHsM^Lc4^^TzK%eUDPoMpr%#$53S`{f#zOjvUh!^nC} zS+e2j-rMT`N8@j&Kcn042&Vfo2TXW`DUWl&nkCP2$R##h(WzgvBUsOh8(G}JJgi4{ z1ZUW?Wq9WuzK?AkuKJUFGiJ_Fwwz{kcjw8JXPI${4dXwX=N>zJUt65q$DFeqa-J<0 z7{xn+b8Hy=MV$9^u8i&_zl^z`>3y9eGoEJ7b1b;bAy-{A57+fRa7Qr1j&lq*>{xQ;U(Lr6HXLR6Aoq(Yr#WEBlE;`o*gSoi!QbSSs~J8--Z|h5%fvb? z9wtA37tg5s!;;6?@jUZKSUaVBFtoJcHe4qPLe=o8lSY*o! zj5fNj*S|DKxt19d=G?@V2idV?_*m;R;sRryVZxRvFLJ=uH@GyYxSlOHvc5u|8E+B? z6P{sfOnJMLq2rTsHy+505VJ9A#(fWZxoW5Sl37+vLFFySn7&a>bN4q3C| zS%%~8Cv&d9L_FNcjt3b(+4@X)oGEK&Jjp0{XD^9Rx&W1B=ImeDi z8D8xkGU6i3?eY{F$5D2iWW2+8W<0`Zr#M*d691**+GAc8*LV&XJym`#v+mREgJmv{ zY#H9z{7-jp*l<6~XV^EhXYTO*b@B5oT_u3^FTEV+^4bL5-xbLHhG`ZHq6 zb<8-%k{N66XFKhlvEvyQFYw&mRKFMN$C}e@IAHb?{h9L&OD?nKs+*aI>zKUM{#kH8 zha9lskl~DdGT}MqTxQ8|m3bI*@G|*l`f~S*8BZ{Jh3A(sSKi!suH}%M*m9cjtn=#I zS+n3-X0JB?EtHvZz)4mdu;p=vuQ84>&odjy`z_7KjSOGs+!=Am^bO+b+qq`7eQ=uT z8{IGFTw?QP_w!cTSulNz=bl5JV#S6nJ0@?nzgx=>H*&~~4GSiR?UMs8_T{%(|2Fz@ zl+oMmj|mSj=Rp=MIp8riJk|RSpEtdw{6^0AUG6_y&avZBhVSi^~eBL^D68{(Mo6VQx zm+49EeLYu<$j4XB!}jYwC-1Dx)l9zWJ~8DH=C%HJQUA}*kI`xSX2H{Jc%g6quDst> zJY2`{d+s%J?qk7%Lze7#yl?-$_}FlX>7sqERnN6-INFzg;B%234>S4~>vG8GZq`5J zp0VO28y;Z(BmJ5F*z>@s@pJL+^7|9_fzeN`cMs#amMte){+n?eoZS(e?92bpIWzm2 zJjKrCmwtZS(>`0D2dw_vb9OK5{K~#rT#zSrJl~i9*Zsb?GAG#l+WO3Y<9z#iu3o2p zX-6={?EmDcFLSXk|JLWseUv%I!SCD;Cd=**TduvYIDc>7O#dKmb`0+)Pk*#7v(D!d z8#XNeWZZi7e>RTMUyNhJ@c!B_8prIf#xeSvyt85Q00xu65ti4R^xY5ru0QF$;QHNQ zGMHt<(=1m^`fn)hH=GRiF}h?jIL?L_dv7!uY2PzkxAb8VB}%eeduJ6v3mHV?~XdZM@$AwEV<(0>KU=UeA2tr%=gI2 zV1^AV4j(lcgroX#gy~}@gZ<1n+m|0V87#73@CfTa-ac5SlfL_6{1fzN^~A|wj`bCj z!3j2-CjBm~b-BcjD=s(gO8aEZDYl$pc$IOC$0vgeOs<{`)<4pEOxbXXEvMOW!0;*Z z$b`pPaf$K7WUy+?J~mH!kG1g}FyA6i9B_f**2&-u(`+&rf0Vp#H(&1#`)19H%yx<6 z(eg0m99ZsgU)b)Gm&fS;50k+fW>1wjR!?(IOr9=|jmpn(FPJ@Z(tD%D&()8${npV)H`h@p$82q#wItGC0iiCGyYmrQ%Ap&&VH> zmrn*qIe3M8%Vu^mSoZ|^y4L(mIp}?r{PZ52^#AzzYUlApzQ#RbIIus~Os+5wH?idu zi`O|P);z}i_2%i@xqg#4xsl-;++RlA&zQ4Jc$g`N%y^PH7dhYsR$O_d@rUGrH7A)~ z=Uy@ATwl-QEZ^w7o@D+vO$MVZIn8{|^VXNyu;K+a46YI{SF?PJd&20g^2C@&`u6w9 zC&PL7rElk|aqV2ojAI;dk~OCp4&|9K&oE)jloy$C)sv0mI#wKG$9+sbA`YfJ!i>k6 z^ArmI2mz*B@eOwym;910y_qq%~!dn%$Ts?CRUtf%VP|`AYY7mhB+^=WVpq? zxQ5Xet`KgR{c4}j?;`5 zu+6wr^2Gtyvt!Qqo9-i19%aKbY}vBoMTXxJ*LHDmEfXe8xrqg*IplzK?Q@**xBVRH z+fQ46hq%9IUo3tgZ%lvaIhj;{-o9A<#(mmpeQsj1G#M0Zc#7Ho`FXHQI}i2#R-8*ioO?C!j`^0zCoxUG#K2Eb)y)#%~ zce|aw|82fI>CZ0%2wC$>D?m!D{VY#2VrJXhEU zD-QbdCi!N3<<8(DizkWyfcmR;dOxypoMtgDUo5Y-E^D4=xY_wVSAUMM;s(ZBb_U}d zX7<6Fhx__%JA)H!*fQL{GYFn%Uan!yQFfeUyu)~AJi?X>eR;goza;}R>bdx3M>V_xPw&YBk)@0HIN+CL|m^8gDT zWXA;#_Sr9oyvT~HUSuA|%%3XHjGiVxEZFqz?AUT;puW4u>z(zptN_ub0~w*Ro*3 zAt%`JP+xzoeD@wS-z)5!G2>U8ulF^cAErFb;hWvNS>@8c*}luZ8NS>8n6T~Zx#E@9 zVZ`W&{W9T3rp%aeKXcA&Jw!FxWtFE>Fd+d`D6LuVD^?sj!ta*YB zYqmVg#;lq0EORcgX7mR8`i^s9!-?LX z8qes0{wy!q73@DGo|U_TSynvEnnN}`$(D=kc%I=+b_E?HuDnhh9AUx@Oqnv{By%2M z!Gj#IWXWS3a)A}kux87K7uj;v8?Dc^3~#zCNEmSwV@@&QG*b?kah~1H%-^@K+U5P% z;$ZM5>v4n~$C%vQJS;fdw{yO4=R)6p3*-BC`XAiB$7eh{jxo8V@hmvow{yO4=R)7U z+W5Ym!JF+n+7*nmz3r}Gh8^b^-foxoX=}gzF5flLe~o=J9I?ONJMRikvE=Hv+W%dw z&xW&Xd6>hyT9@hCUB0hvp1bYx-4yja$c9JQ-F;Wk^zHYs-rK~@m<`96-%~ss+)Ml0 zjprJs_udsuFkH7QIMkOp&w?j7P8s7bDIxzQTM= zc#au6mR$Q@{Wp0|nD8LWD?JY!vSs}w_x*kL$tfmRxi=iLWXlr_$9MUE%i`r(mRw@Z z74zbGvVE}OL|?wzJiSkGUl{XD?}T&tN8>i{@^6vI>sI%L@pkdEVDx_N9AmV@{bJ5} z4kmX67g#d*fbvfL*m9cTF3%}5o?yeKZ|4OjyXEDmdXBT@K9+m!kKr})%>mbcko)Ah z_aEH5-luv#Kg6fWYj19Uy-)Xf@?kzh9($kZKKAYx=aA2`UhlK*gXweRk+M|1YqQ-WQ6S(M#;}WBM^?#W{vA-4#?U*fE~j6>L0a9`0xPGI6tbx##cW z)_sNjGM@E(F}v3MtPZ*dpD^yV)@8$rEl)EXI3FflX2$T7=H(ieT+fOd8Nb2$OgPKv zI-mc2Jx?;_VqZV!^SH0)s^iLU*%gd3<~URCT!!KEfYuIr;!$tR#5i`cz&z8s8@l@ab1Lw#gSD!R5V`e{e{w)4QURm;V z-_EwL|B-w5W#hPs;lFxLS+L=N7nn8X`-=LXI#&*OlG)Gw_s>`DhY34QF!~ScGUf~u z&N1atW~?~mIo4ce%W%OwT*L4e^1vad`g)cu&+FgUGyIzMxrQ~@v*AXz%-C^1!~gU= zG2&sy95Ufard(vk^UT??;L5MtH%C}<1BXl*{?g|nBOYMPgG^ZV^}lxSzoFl6+&jig z&X37&Jx|P+?eCQMIpFa3^26c}?iV|*{-%9(K4+NxNnV-%S=@a+ml<7jp5L;MzuGUO zzsWaCHXQ!lKK{vk+`uTv1H3r?AS26-tOQ6QwFv9ufIFk#D@DA z-(Ytz*VnJu9h_msmfa0^`!|ctd+F|Az`>2Rv*HC7H_`qb^Dtv{^WDMGzRa`ixYXC* zVt26OpY5Ls2i(LVr&x2EEeC!1me%25_3mKhX>oCcLvCQrv@hSve*5-Y?+#9~;bLFD z&F_T{_m4o>&&cirvZRJIN$nBQ%;_jt=Q zYqmVgDwek&>c;`2`#A63`--12FEZh(e=+WU)@QO_f2KUbjK|sCUw;lBu-m_RtUo6h zK5%!ipD_91$mpiAv{gJza(Vr-fIS*!!@;tG8wEOf^@o*z6 zW~{lN4QJW%Fq6l)AIw;@;8~Vj>gzW;hkrAk<7~N)9Sg>fmG{2=amKNHytvO=C)LjK z3GN4Lo?`e!{rYyU{(su9FfWJP$BG4O9%93JwmiX(HRDa5Pgd;MaOKambA-*6_Rr!; z=4Hi#=~d!o#-kjLJ6G1cz?LiiU7Sxg4)jpUq`VVnGOI{WX_BQ_jABmmORWMhpc##H5b|OBIDi@CM3p_`RnKI)v2Rye`LizrcbvVkL84K>`kR=-~uw%pM zMeZqMh8L{I4IFTiLr$~i92*{GSXiG4J7!$=?aFzJFsK?ql?F@%Ck&W;tuUCHrB@mN`4lFnp!{ zjChnWD<(Y6l;@annK{G%Ge6gG!1Ww5WyML>Jivwr*|KEEV+^l#9*lT~F*g)OYY;41uGt6&3U%0*zq*OSJ?+6E;DBMJNx7srd-dA8<{iXfCpF{bpQJL zSIax2*U0O#dge@DD}QWxx-Sp({p;RsufG5UbGSg>TnMTSRxjxyn@zgeGa zSuo*{85!j2ZW{;4FunXTt?19~M6|t_&{o{ZH|8 z$T2pYVloszGah5X1rB+R4TI}l=KW>HGvOFBPOxCkAqQ+YWb#qtnKQWlWkJai)*NHU zDMrWSpB2wD`?zzvfp+d^`3d)f$tUfHIajZ+E@KWk!G<&Jc$m>~@RrZlvGW?Thg@%-5H>E;P?6>$BxNv+p~9 z=3H^9I2PSI4me=)FVSbkv&Q{n{uAr6V{&8Tf9mHq6HYPbEKAO_;t4ja z8UCB+fZfmBla=!G@7h`O3_C6}J|`bHu`V|-{JH0W6)!OV5Bt8Ub(nL=88#d;{DnL) zX3LZ-Ze|=K4!E8rH?sbvIN1HS=VFz8{g3=H<``QZV#oQu{a1co_3d0_e!+YAJ%5aNj4>CO@d5{2eQWD;9XqCs zJDW|vHP7qtCfHsw9($~?;Q##6yLRt)Z-{w7nw zBn!^6yXjQ0!0={M!5PMEncjTL`_RpIi>crsGah4l%c!;JM4Y^4KFNJ!#8Vt{ z#l7|C8irT7CoDM4A&;=;iN1Z@zBypH&bnOBhMO2Z**P=eJTq1-c!om;_tBp*JB~5B z+C65<0Sg{y$(j|i-n`X2Nl1+|L0^R$O4ih8>p~PfP_P_tT##3r?}*3~LVA zagotx`Delv>zxPJvt-7K2iWis!!7p9gy)#CW5Lz;7awDm9A(3TEf4kenf3beHs|mF z^KbVYvfUvM3@6RYjNt>#yVE{6{0IAH_*~-|J>R|Gpq&LPmP}r%y{~8ZAp2#^a#lY! z*G>gz8NW*VgZ1ME4w$m!6tjcw1uNFfU*n!X#C#lQ{#yBD%bFd}vV5I>i8x-bAHzfL z33E0q89Y?{*IAeG8?DEJ2RUHLWX|)zl&cDzrSFnNbK`Z6!{?OZ!*o_9KLChzima==r}-!0xpi2I23+3*0P_qa!_x#Dv3 zy;oe!IK`aPEI8=P@AG`HIcmK}s{fFASn?3VkBE!ur?ijB`={Lt)+`u(PM(>uWzLH% zxav{j8$@w5CF@v{E0JUr35KXG3<_&4!iVZQ(8o-jXWf4#quzfI;j zZyuJ+`|>Z%$Bt+F@~^~qrFpoKEem!$#OQ)}neYTt)*P~B#fz-D>Phy&wQQNN<0eM` z>s~YFG!qW`cAn_lf8#m1O8x)IKWmn3e`kIc%iP`p2;8le0s8T`lEI> zY}oMvqt5(Si~CQ0jxheSpTkTzWESiRmRWJ-Q>@FF9YVGg;#@P>Q*et~$na80jpec7I1Y^(KeY(7>z#D?=MR?0(P=K9RMH`x=6Gv+>) zJjnXydxA5pR$FhIxNfz_{{uIk1sj%3Z!JG;xybA`^08gN+gg_;=X-B&zdOvwQRZv* z_%4lk?;uYcux2@8{G@!{SzeglWlwOF6uucQ_YutdxIMu!4j(VSPt!k@ceYR16P#rC#6AA*Li;B3GrdxNSU*YJjK+<7y7{j* zA8Q_DIU!$+HoLdXwmA1^C~w^p%rWLsW<1FO&#+_gO!H-Xg4K)|Gv+8W<{WU2Lk?N< z6kDES$7ROb+^hY@F=fFimYiYDAv-QI-Y!qfxb|84bBraYIOH@d4p{RD8y;tRjeGuV z^X+pVm_E(?z0VL2bFO+0pQW7@3l5&`^NHz!J;AyIe6D-M!Ass`*@Gf73N&= zLUA$Tkn31+j5R0NFlY2$`|o|9`I*i8dBB`2USyw)SaKaJj4jp&utX_>kwUcWB?NxbnruafBTcW*>3x9PnUY{-}G+oDB^X2c)@uw`I>Red5R^^G5xypn6-~@_<6~c2bl37YaV0qUGcwC zd|b!ud-lot$IgevS@B*g&U40da9&(2d7eXdthn-3#&LulM;ZO6cscl`e6anMJoNP! zJm&|+#r2GT?|JFVoM+1gmVeN{Z~vqHznXvYxySO);%CJLMt`%9*C=1I*Pj6y?)40+ zzjSY~+?P4_TIYPTy}=wi4jHc68=PcytG(WrubpezGGTP4CoMORgmK?C+5jH%| zj;9#iX0PAPWAAMN$K!Rk53=5~97v#f5f-|OXd&E8;^9p{!IiQ_u)J-~V_STcU_ z-e9S(f5_fo-5brHScmaL#ow17w%0qy%+C?F+`x`0^M~&ZCK-?J4Gu89Tzq}~*xsPy zkkOp|a6O|(?+s=+c+6gZRyNN@d3m$(kCSIskKY>{W6cFNPjEhOu`WkgJaKQZzb|ic z&J3@VZw?v1)p(9FeUf#UT;;s`GEZ_azBgEN*t$$Pyn3(qrfYwSxEW4}_ifhY3|r2z z<54D?&BK(7eLYvcUHumKhVfSSkOLm(aEEhY&8}~sl!tf7&o1Z6c*;DiuTfuGkK>H? z$tM#YWcCl%WzIzwJkKh3Pv5Ej>E>g{6AYgzukSL?e)pEev)zk#yJw6z(!zJ7YI_nw=NBkxguf#;ABGiEH9 zbB+a%_Vq6`p6QGB2CLp{-Wm6TEoXaQ=Gx1qMTb^O|YVk9C zjr%cgUXHWk48zwtC+0lCnr9dfe134i)&FQdZeaSly}<-C=B!_D|9v};Gkk;30p>i* z0hib@e82r4a{pLxlqJX6@j%~xo%8P7d9-h5)wi?h+u!K9?Ay8W1KK&lj$=&c_6CPo zaL9^_tlw&obu{hg|hZ z{kfJMH!=B!=f5xW1jBFIH-{WKE^el*zGa_G{>gL6lr0Bb@hSZnGpqGy&dI+1wEN5C zyYj>4d-C{cF3LLxKXjiMow43$jOQ40PO#%_U;iWPvuJ#7o-i*ru=S z#ihQTD^6->#P&~~XGVV(4`Uu-^%whN_c#0avbg^4K5)pAEh~0hWEfl%oM*=1EB14} zYl5|GnK8cMHU6Kjc`vypxWJmhSH;8CY#FoTD8n0F6O1$BJ|-+!aE=v^F%Pc^RxF5% z2`g^m;L>Y?qpW$F-DTGVBVRM`jjstdFk;GtlgxO4<<0GbLv{>rVcgfXbCk&~ukp+} zPtGv9wf?NR>KpdMm@P+{-|m`Vz=B5@-r*YW{4oC=#e0f(vOWhR`myAEU(XXP?qPr5 zv~DaPOu3I43zqk~#_!PBH!m>0_ch)DqMn-=uhWkS4>7v0c$xBIUw?n=|9^_^KTeNs zJRI<-#iN3y!5~;|=(Iu?!CJ%ERLb|7k3X)@ocsK^&wb83$s2jHpVXf@vwL3?Y^&94=QTmW z!F{d?&M}(MpD{;2WuF`}e~^7KdGIyCh2DqQ*Qf29yIDTecoq*c?lYbrZar3ybRQTG z<$KY5kF_u6kJFz8FR*08c#nJXpX$oJjHk47@MQPwzwC=Ko2Oh8oMiU2Yl2nA)6V;| z_RAp?PBY~!GoEDr40U10ZL8+p>zo;KjuB6>e)cuNrT+Oj>dl-ZpR*r^Y`KGjYpw|< z8FPvaXE~g?COFTEgU{=?&;4S?lr7IO+3)@_<(4mqbBsB6vfz*;!!OB~<1D$G z4Kog&>z*@W)wj>-|7G>(PFByqCYWciImL!42lLiv%=1imi5WLpaOA7zeUbBK$$3^UwjNtHef%Xp@4lu# zV}6Po+8gfi+;WB;GY(FAUm0?V)l2nf!@-*P%hZW6r@BD>l5y>=oAM;Faq24eN4}8E2R;xIe6Tku}#@yvqCZ-`4*d`(*NJ z{aC$59oEhBT6wd6-8I2V|Ga1&4qoqb^qcn238tK3!BebRvE?cUZ!nI-H`?d7tivHw zPBVL}`@?ccU08AC+s1R8;oJQ>#h9~9c#0{{Fk{7>7g=zfC9kmJ;Ii}M7zgjT#^2Ak zZ|2N+jx`$=XS`ScW1V;E$3f|SF?)}Bz9WzKUK8wM%2^gHSn(XA_qk8swO?*y%9sUb zSaX3b&oKUgeKX~@RzL1y#c4LoIQXFcjCqj>FEeBCJ>x#){&4tD?m07_Wywpdxxto$ z@2l5G)RhGfv*bMMk2-J0=hcrX$9|w6hpf1l4d^)x2!~&F9Du&HwM#@BPFzLCx}$^7@fFe9Gr4bDm?xh7C8_am$ab z^J(Y6gnOAWW5LD#`7`Rn{Bz!iP4jS$EzfZ9dG~-38zvn2iT>QdlDk-c!Tsdml5woQ zZ2h0AXXCwO_7(ZE{i=O1`I^s5M(fu7nfbnB|Lnf6ox`8X`-=HF%lc==ad^eA1FRVT zTswEO;a;{p!j3ryKQ}K6wrqak^Yj<;`jyWymRIec4Z~mR|9|pj#Dqg0Va6qvtXXrN zEw8e>-b^s;jQgFL;0P0*X2xX}TxHFx?700`-t+6v`1ji7!-NeNIlRG4aD^=={#RZ% zpYiXpd*%kStusOVYx!_DlUo_rKXaLbTh9cW3^{Vue7Bhi##u07%XvoOOmLPlS2*M) zCTx3eYux{7zuio5s`vIY!5WJ@&IHkKtj7sf+{2nPY?!g-0y{2oxLsb%xb3&ba~Eq) zvt`EcE;GRy4tbF&FEeKl+$5;DgDs~xxa&-Cm=SZ9Tw%paY}j(}hsIy;CjQ=``Z0{0 z7t_1x&%urv{~o(`Zew+K^HAq$eQQb@)E=Q+wTqaXT+3~%sI`1 zv#eOK=2^D9#Evb)2h0ScH&S12=a9RZa4&P_EO?p~FR*6Ami}YMpFht8TNypjyJDG7e3#KeN$Bt(h zJ;Zy;gdH=E-b8=yU^^*Kc3j}#q0WmTml?5U%r$nqjN7995$*#M&NE}dg3GMA&W?kd zn(vV_!5CvsFlEYur&zIM!wVezsroQxcr)v8oEdksWX6U|>{u}zIv+;dV9em=#&auE zMr^p3EswBc&hSyrn-R}3<^>MfFyRKf$2jLv{dTK26P{tl^DNk~=EyDd=MILC^*%A? zUZz}N!DUvw$c8OjUS-G8TdK?B+*gK-8F6>tzDK@{xyFjK zIOGa5Zm{Imt@7s%wj45if_ibt(@eR{oU5#Ol`Xg5$~sSU-i(=W$iqxH&y&(L;4>RFBGcGb`$%5xua+NhNvtifAU*uli-ufr)hxJR{`)%Uf z$-&E<92`88{VZ|eCd5Y18tizP6%y^kOyZ-r~ z?eh<g*zp|0%K99#W5%(_`7&n3gf$Pd;XH>QQzv$e{>b_(_Qm0UxL0gH;XVFi z_4uUuSboZQHW!^UtN(PKcQY@yvH7gLI9N3=V{S0v$PRgNj5&9*;E)xkS#y>RPcr?S z{jvDG`@`%D?#tbc=L`#Gthm6MOKiBzmNla<+TT6o!71jPW64FYc8~=UMVB8(w0^4Te8- z-aD=9uh9}u^iP4YUXC}PDj9cz&9Y(A< z#g20fH=QG6o@2^2796>sb-0ZUV-9{|zl^!SglCv>g(WxGaBFNG?qK*+_mvT+8FQA& z75in!j)R}e_x}2Gj1hM-`=xVX$(ju}*>Ugy{X6?(#p&K(d7oJGEE}%$?f>gs`e(Lm zd6oIE{r>8K=Hrm@Z`FfMurCNE^c&e1oMv)^eg16@dEIbdu=PRuGhsH^=g+FeZ@kaX z7vN3y1rrbEP4@*$9Nc_gF!B)N7;?xR{qyL)V1|QRn4jG(_XX=reowzi?Y9zV%2RA_ zvoE;9INTSEJ=8qh$>g^Cf?0Mf+1`F%F#0h4w(SdI7Mx*vhke0?{+a8ndA0YB_VIAz zd8GGF`+|$C?ra~s)c5!I1@p|f()$Pd{C#?L+iqP(ciHD>Q+ehlYmPor{kWYiV|Lul z!Cm(Sdl~WwBj${Gnkj1*++@YUpPG-`*>RHLAIh6Ck1*i^GnOp4!isBbc!eEDhsHZrmNl=k;plF8-orQ+JjLOk=+BP9W5w@je#ZB5f7o!fZ@;(q;c@1>&%R)agZp`J zm~hLVnI|?cJ1+On_qRTy2RNra#_=S>2fBAmxxt*lhoYnE(yo-J4V_@w*zMC(7yePPK34j*nG{WCkJyR1KDJm=W*6g!^b z;1Tj?!3~xSo@9P*W%@|WboMgi(woKV^j^Pvb`McNV;WBg9 zEV;&to2)tVbn`G|%N^`E$-xuN&xnT^bDl#MOn8r;Xe0@8YnXqQcHD=so$&qKsmm$L^n~yPv%s9=Qvn+X%H4C;p%Z@7?JVm|?*>d<) z_wkv0ntQ;M8Iz~GzwEfdc-pxYd>Jz54mM2LzDPdoc!7f#`&?wgEzh+M zcQZYyA2VKH&V~gySaakrjDM+hm@sC|ImRzDo?))uf9X8Ag9#^@a*7#KHm`L5*>aKL zg1WO{*T=bI);?e5^Mf%n7Cg)J)#}NL(O;Q|6AVvThskTyjU9vMX@9M}IC!0S|ICyz z=UB65!!@?texj9=$_->u%) zigORc_jupgz0cdN`^1t9thm&-e^$QiIJnMwtMX>Zab}-0ALDPEuYdlo_2=|!?UyM}v-+X^vF24a z9DTleaXUN49Q??=V#vLWc!V)?4tbg>&oSc#=4@DSgC&C(SeIK_Gh)LDCY$!jluOLG z%)w8bA4873(7FuSFlO{q_o{Da#vvD&aEU3GnXzWhH5S}t$&q>UaR*yYvf~s7KXV@# za*h#CG3FT#Sux>7rd(&nE6h1~k@dKZCC6ED7i%VLc$h8c*>REC6?wDh?EA&~bCM0G z*m17+S3c*MUG?k9ORUSCjDMpp9CCpPm-;xDnf_KC*lpP#OrEsgo9+*$7&2wdIi_4> z!DS9^wm%47svg|R;pqNgFB_I@dA@JI#r|NO`7QSc<1aIwyI3+|&BF|TZ@)hSw$5$# z`*|4pvFqdEet+g`KJI3CTkA095hl!;vt-5dY`Dsfml@v9e2lmyH=bkcZol8Z8Ls^f z`-7`YIr?(*aXSmfEV-K%_i}K@{lNlrHcZC$`!isYTb=u7ZhN(PcDhH5nR3WE zCOpNIXV`MBZ@-WFv*N@l?VMr9jD!2yCqpi=&5j+%-eTOJ?f2bI>vNVl z3sziV%a-9D^?a*1ceCOVMvr$tn6T;N4BjTrh~X2QKZjgk#$}dVW5bap`7mVkMCZyO zr)?8x8n$dIYg9*dZccQ-gdD;BAJl^a1FVu}S&$4-e`f&I{_vx(s z=e;M)c#`?cydQo0E9CPJ@?X%-_*KTUdyVsXpY>kn92mdJd`!6I{l>rDd@TOXxv+eP z`*F_x&)8S*yL=8adbfJBeXsU^l;2tVW$_Qrk>&fW{{j2wkm38)lMxr0aD_QrmJB{< z9&TgHnAth`_s<{Hj~%aauG2=KJ&avaEKK>E+ zi#gX=aFZoRD*ODXy0ZIs_mRU3;!HU5Vfiv-!CkC*m@Q9o@E^{JF)uRZ6&4)*h;_J~ z6=T-i&6X(#pKy*0xyXnm3pUI@X`dhU%x$bWWXD;CwavED6r`U3y z;i~x=bA<`lSbts}KBgb{F#dx5vA*PBR?c88Pf6>Z+?|2_rUvX}1nfC3R=-e+-hp6>3Fnw`kp<7Od;|TNah(;fvgYWj^%=3_1cx^~5bWXLMhEpCI|c-XZi3VTduR*V!xlaF1Ing=>gw=kr(Hf^Av|Sv(G-xx{q_M zk8_hH2VaoasChW#I8*Lo&V&_DvSGo&E!2S#8zu}cnV(x(aGVu)vE?iWw=@qU7EHLz zjCCLXy#v8z)(pO=-&XZt%beY9u(+Rlw5I+3>d%-Nhg@L7CFZPH@lyZ%0QczY=6#^N z8F7X&GZqsEf-5W^B;Rj1PiCwi>|Sv25Ow%(=g&!olk#H2mMyRL?GKgTx_r2YDO2V= z$?#$FV#kqhTJLe<%%6I|_f6#WboKj|@tkDBDW*)BbB-lXvE~`JtT>oM?8%vI}<}S8OIC#c^;4mZ3bI3)eESd8>ORlnG$LN{v_hohDkb`H*lNryk;8u zux7j{qvmqFy}=UTxWI4 zdH+z}9I|fP4nF;4Q83$H4TgOQH=aH$OgP{LZ>h~D^eM$Yi1LriC2EotF%dM;!F?@a+Ofcgfwwz(|g4CbY zdgekOf1&mJ_Ic|wev$P#d~q7A_3bZ7gH2X>8jM_V?k`V+kl`!slj&j_>}K=kG&s!a z9p+{HF8Q&0cN(lSd`}u&VZp)Ac{cTX8~xs=9!%b^9;|t?Z~t%_oMFlH3_qUw&bEHs zV6&R~HxjjfOMk}SRTmC6sCS!9m}Pk=OMO2Frc>^$!MDdT(|xnCjHy z<_Cj$R-*@l6}G&@=oSZqO~xGjiuXV0yEyWA=)qvR_c;fH1?De17_749We)!Opg*fN zo?HLdy7@uB7t;Q6{n@e0VP?!(vt;}wb?BcN{Kh;C8GYG! zrj0x~{OZBrOz*eUl|_5d_hiifqhHux|IChYClCK?eswSyWBlub!A>R|GQ6t3 z9CEhz{|*KxnXzESvuwD+j+dDK=AhpLZV74z*V_`b+{)o^of|t&GYbv{bF8??h9$%6 z9rC>zdGJ#2$RXds-{Pwvhy1*REkVSs95Q0c3Fh3xmRTR)a>#e6w)lP8p`ha6ric7& zIsF-2-+GML-uzIoz-&~%zMbo=ZXrHm{jG=m-qU`##Nn+D1sD5gj@&@Lw>}h%vAWG6 zKR>}bq4k+?fhCvv_-&12cZWm4wi}9%>E9b23YHmcA9EI6A$^fJFU;@{`qOfGueA6 zILYGKhl2AAuQ?Q~_U#7_1%uz!?=R)sn;i5>|*CY`Ub)Fn!vh!$g znC*R!2A4UwpMH$Ea$2)xyj_wM}vu5$dh}RJw`uPk2@Nyvf<@EzUQdlV_T17EFOO} z*u{p^>^RHt3G!vkf(g$tzCp^H&}X)>yM+yx?5!AWt4<_A2YK`j3y$8| zd2l-$#!UWJU6?Ut&N+5(RzHTPoeN`LWx~z1a2NIF zIFom&FKf;-e)my7TV36_%E5c=>+^Y_b>gMYLhn-3rL--@0;;`|wYRGm0HukNhaGWoo^_s_w#eog{! zcdeh3AfKsg{asu6aChI%y=-`d9dm|Hx;8k?nCF=A0y8!&xWS6S-Hhi}c8nN4`PyKD zG50Xx3`;JsVaefBt_{{$ade0JB-aMJSTN(@sn-T)nQ@&hx8B`+PrKIN)pahMWBhb^ zvgBok)7ScLtNL*_JI*tG#c*>aA9XI~qfV#G5XvSPtY?0ALAbIf~Bd2lBO*O-q9XZv;*ta+~YpmBZs z5$oT}{KuRdhdj-c=a};XOEw&2_Hl1qvP%YbLMP$j>8kiG2tdNj_g$5>)azo z+|8JKIph&0%$f2uGoEA4ng!Qba+5Vj?qgks9L!xC>|n%64mrh?DRa)TJR(v$tLwtT5*# z7Hrup+3!QF`*!EcSj=-8uZ8dHOgnGiAr@N9y%( z`}?W)g(cUR{7gQ(?C*;E#)50?7(PP(pF4LBej#5bJjIS@Sp3p_jIVzz*!oE8Fk-_A zw%o&xGfYN~1@nFT4UPpD`}P|i3xYqD|Ba0A9UKd$*fC{x<72^G?@f*crx@S#Sg^|Q zX2*Qr#QL{B7VKbrn`6NwyU_f-w>=iDFuc<-KOaIoI_CQ&^5T|9neUH}`F@G`y^i^Q ziFka>_e;b%&Efrz`F@FWX5P0y@K_K&TEB-J3(hj-3R_-gvFli{>oMxegcT37<~-X+ z9P@n->kZXmw|ST`;UY7hWx<-mN7>h7t;0!{+{>Cr*fQtf(Z_<*jChViUSPou)*L*} zdXI4)EIG??_p#s%b2jWa`e)i7do0+_jJsGcVats1dbGv^vhZnEac)6B<^ zgIBnxj5x`ZDNCMW%c_sR(mmsMK4T3)XWYA7{&sS2_5B{mi%@+|Haas||Hy!)11?S^QA`eLI7F z+PRhCk9@wf;>o_9XBhw3d(gLU%5T5={>1yink(#nYTNmSmyf!_VMs6SZ4Hy%y&?{AKbx$yIFBB zqetq`Ay2X7MYh~zJrm}JH&#!phOzMUso zafKb%nLXJ)o@afIvt`QYDb9yYqE3A~xBNfjIL7d)S+J9Xr)R-g4xi$@3ipRC*ZcUZj6bekf1@AkSF1PE*SJ^x z^K0$*gz+4*~W57IAr)O_lz-TneZfQF7@r-_WrPB!|<|wv*T!PzW5AVW5!Ju9C^9^oMikxb!5hQR$S`iTDdEteVoMt>Hg$Ai&VdmcOPdjtC2^LSA74vz;rPs#hS$Ne|5c{$C3vn+X% zHJ8|OnS;k2_wy~}%{30W$&@3nF%LtQ+`*cY{qy6k&z7?sJi+|Td7cGV*>Qu(6ORXj z*V;d~Gn_IHGfuN%#`sCcgGClR$K)x;eMX8W_Q~+6$AgQ^*|OuvqI{orJQ!oc36{?| z9_(TC%;Ujn4tb6#FED4rk{kW=vyTVC>&0h|2U|HjU|mLs%*VlV&ByjHj|VfXpC@0I zM~?>!%rkXmdY!s*_yToh_#$;>`x13!^)mUsLI0PlE0YCvW%O$GVD}nzWxaUZ&*xBw zHy#h>nEvf?-$ziNw;cCxCYhh-*>L2I+TSKW=A2>2g3;3PV1>iCTdxr3PFCE*@b8=- zyED#@**lL1mzccEIlW09OgMP=@!&9HE->LzALn_tT;;Gl9&Gtr>u{Xm=jG4z((&Lj zOLi>2B#$@CpF_4^wO@9eRom+VS8DGY(Gc_jTvOju#ky!#Oiy z$NF39{1)qRn$frQXUavEJlDs0v5#MN&-yq=-YU*9wwz$}KkCDjNBTHV^>Lo<$9biXbM)`T zxr5P&tl>dzg_ILU(3EIG@X z3v9T=mdor|bMSNZ=a3`sa&NhV-7oBm)vxqp!^=$mSN`v|E@PJ5%a%E#U#n*y=ehp* zs&iq&4Hop53H-aS&Y9u=sVfssu;89P&Y3>`8+Ga9Tf>DQ=Cq|FMbla3AMKgY&>SRUNY;r-ngb`Nm> zm_N||Ihb&NKjN9Qj2>hktQmY%J`Yw8c3fcj5bp(RwrqHn!%63QUY?v{@lfv<^Wh2K z-B*`CbABJwKII&kKH2?Z{*)8JTL1hs@qaPzwDIgt+9&gu*(cL8?*GU6F874dyWNle znGM_bSbs$xf3KeG-fMowXYH5eKe#9Vs+|$5_o)Y)_q%T_J|O;Y=KX|r=AU$~{d2AT z-}V303IDBWzE3-M4ta(tD`uZD?*;2{7l)tqdBSK_9sk2Kx3c73);z+&m+XTv*O~Gv zbB3Rg@0Zns^;gt`$(r+J!Bv*ua-TkFp6|GqjJ~Jd41Z{RZQdWB2xeGbu@1vuct84f zj(*B~jF@qf1rrW_>Ad@Pt}y?V&p}4Nc7C5Wp3`iuinHY<7Qb;$pRpcyGx@D^V8(OI zd4V-AGrit*L3&X;=UB1e@P^j~qyK3_*oGC0kx$^}E*vlm8_TPBFa6b-^qX zE-+)soab3=xh~l8S>rg#meb5{t{w;j_{w-0Qzh~XgdERPWhMZ!=lriTx>US!U77QDiegU?%++gNj)4R^6+ z!j6YIxYc#RJVP!rV#%22Ipiu6US`UU8Ml1F`W$1yoh&(I#c9@@Wy_Ns+}e3E;#m&4 z!jzYovt`MvtT}qgcy8z5HqM<9cXP**vIeYUU9fXz1QS>cm0{( zLp|7Wo%x?Q@2^{T-2Pc|mhnB+nKf6L+{--QFh9qc-CLbG*eQP&Tw!z{d9&i!e_Q{) z&XW!2ncmO(?6}T6w*I>H?{9sUoMrp~>$B!6lLuP=o7U$zvkB{S@F456;0mJ$Tb~uj zzGeM~Sf35&nNC`t9oLyZ)cW7H{==-#lCz8-Zhh8VWwOiqm#xonW{kkKhgT%Gk(hFAj>D2hw+om!$B$A53Re` zJ!i*x4xa6vGi1q#=NWUALtbXWjw!eNNZuS{&YdhcWXWk(oMp|EY*?`6S$16E;5puF zhHM$}Dr1iR*!tYggfUa@X2!kDd4vUXmORah=UDRs8#ZjY!H&VE^{?^y$&e8vPB7*k z4mrbw8B;DW;}UZ&vtZ4VYpl4*nj=54K0~(L!H$z0%=nyS$dnQ181oc|Ji~+)Q(k1o zb>_Unf`gx0pW9e*oHci`VZxS&*>RqOea@dDOGZ4;n5!J}G81-8x#efp=NNPDWWgaz zPP5`HYo27of-TRo;|d4+oj*gijChqXN3U3)+nF$C%H7PkmpPBHV9t`KS@9feUSPw9 zEjQRP___5DIDdwW7;%Cz_i)G=Cd`;}ff<*WbD0HemRw`SP1YRwg>?>^k0ECmF=NaH z4!OjH%S>4_;~H~rvf#)st;3KNcd+Ip8&0uh%8qj!9CDu+@(d$ZjCqkmt~22krW|zE z=QidXXTe=8nXuwv)|_X+{=VVm@;R^)698}1uw8Y*=xF4TG!Z<5q@$VSdJ(V8T7jIKzS&D=x6% z5<4z4{7d(XG1r)IlNm?;&v=HcxPuKR*>Q^Dtoy~7b4+-O8PBj_#flf%aGf2mF#Id` z>o>-88xxK*<1Q9VSn)6$&a>kp!{@nQjCr03SDEoL3wEry<+sLjj2(9}{D1BjV@@;S zEHj>D!Gaafvf&ClUSfE}{bJ0kOgI|cG|0G}1!Gp+&4zo~@d(4C?iXX8X2NsKc!32Q zR@`92;CjY$E5mD@KVwcX;T~q3VZn?Q7uax#9hVs%bN-CE#)O;9IPyEjGi1dbY&gk| zQw%fb&zN&ec#0X%uwccC7uj%~9j`Dv?)xMTCS`L}LNc}l@u{>F@Ve}IFzLC5bb9ho6*|1>xQtLD48cS}n=E%VO3^{n2b790u z4mrh?DRa)T!puQo_n?n+vyXG+HsTB!oppXpImwb! zeVl0@{|BD~eVnKIIM4KPR(go2%DsJ@NBTJPKK{?%n?BBSeViBiIGa9Rxqp3} z!R^Jll`SJiA9nssxu=hFrjIl0;~#PUeVj{uoXdTjbsztz^Y7!_?Bg8SCeD!2dFRiR zlPo#a$C>u=k2(K7&QpDyXZkp+KK?JxzmIdhkMl|&=imcF}-<^LS=WHM6$v)1ak6&>9 zeVi+OoR|7I+dlpu&cBay^iJa3&XzHwPdI<3+}p=_q>nT2d_HmBfS)3uG zi_V`ZCs}f;k2CG#|LOeuI8XI)p6TPP`uKl2|31$3KF%wBoP*yN|E%+8%5j$5#g<7Q zUv>U{ob!F0i+!ABAOD>5@8ewU!kDbpSS?4G6XZKU* z%l69ur|oRuBO9;B|KtT1>Bz3Ey}QzON~%MHXoD$1c7>fK2!cTn1cM+5E+)z3eX2o9 zs-sfU3P!DpN_7wf!5|2t22r8Wj-RwbLx-mA|9kGdB$J)&ChDKhXZJhzJm;L}Jm-12 z&%JkE=)c{l=X3f8+P|Q`q5ofupYP-6-{co+|D}EyQEru!k6pT(&xzu13%44Afvwys zdw1f|-2C1b{pN~$_yh8|6XnppGvzRn=jL;n)VGV9&tnq*_uTxR7V+EMY7mBZrMwWm-6)4zKIKr~ zopNZ~gYv!cwF6e`9=!cy!0DEBw24DmZ%kdhw z%G(dWYu((lM-Drob};%PhXH7V!_W@Lp%YF+18V!z-V(Q}fj-y)gU~O>hY)8Geh#G` z=z|XEUFYVzuQ-MqpdSvxARL8Z7=|GSaUH-hEQJwR3AOd~r^pX?^PO49;iSk*sqaAQ zgE`O!ZO{&jpaYgcC#-@lXg~vYKrifpKG+ZaFbD&13AkHs$xpNKs)D$zR_J6Hvc)5)V8!vQ%y z13fv0IVBvQNk6~{td`>{{K+vKlH;@JM>&SMhj4s0?Sv6nE63+B4&)dP%dv|*$uZ13 zl;d+5M=%22a(o_nmSZ?7$LBK+*TD;E7mPqdjxVC$<%f&N<7PZ%+X7kaP6?_nI*(=HhFFkfI8PC{o3c{&`u zR>nW{Uqd+z`~<(H^j|0a0bMt`RUb6q5cI+jjNHV$I)d_FVh@9G2-<@kd|&oLZS9K!+Ve4Kf89PNZ9&@o6Jpda=@?Fr@~bV2=i+5tQpHVX?1ECy0)$3JGiMQyN=(7LVtN3zXL^m+t=|-k@mwv7|y9vT~O1}KaFx& z4sEOIR1b8mu2W;syJMZQR&tzMr>ddjn{_Gx4QrjMJe_h_3w_WFojcX3>@$e_yLGAq z26m}a6VU!Wvd$x(Q|fpILp-n=8qg!h zr_xU7hY_fsMtjaDuF5*)h5_h@&a;T48lF#{p$pbR1A67SntZ?z%({U3FQz`|xCA{I zytGaYLZg;^K;KpL?}gOUR;Naw15Q9Ej6i=o^;|?e-a4KcAcy_JpDgc zwNA$0#puIaXoCg9pJERKa0Eu+B=p}3^*@pi=zIfx=z{&wfI%1uk?%VE zyvaO)wznA#j^U{AGv-$#{$LLDe@>pDD?-1)Aev z1K+GyGth3WSB2M*H&_a_@6@Xn=!1jM4@Y4DhG7WKK<{_!`P>)!yVR>b=!Fw7^!<8O z(1G9ldOmwY9IzTXp$A6bFtqKCKWM=0Yw-{BpmWc9?m3gsy{H$4e^Afwy`cBQdQ}0n z!g|#SZLk;GVE{VdFm%Fk=z`PGfZFx=hea?9YoLE`+6VQ0>eVE4L2D;^`%(@acKi!} zRIjpr3J;)u;UV-lbixpHIf(BD{H(86<14EvNsWaUAy02Sd<* zJmaE^{GEV*=yEbXU=RkO?ZkRD1p_D5tKy%LUsw*q&;=vV4YiZW3v|FCXut>z!u*@a zQw9A2y{FJ$Fm!6YQa`7@)98Pwom0;}b=mgaFih11YkPu#zxyn*~dMtR~Uw)Fz^`j@*e7WoIDGkpr3_L zF&>3aliv-DJ6H-Muoi01&_3vZ-Ovg9paDmq4^F@UjKCnwx|cYi6^3CUj6es}o~3?a zkhr1o9P0rLK9AmS;S0nKy)V|QF{q8u?)&f$4XC}se3N4sf%>bAk3Rgo#=L;GQSt?y zuQPw|r~Hk2H2__30(#*L^uwIrk*^T#g}ygwC-lQ^7=(i`3`b!EhN1Qr_RtOs9zgy! zdQclf4|<`spZMOx9}K-uIrM%&UVe{1Xoud9$RiBGFw`d*Uk}m_SPh+jqyM4)ch>tq z&@Pw@?XUnkUgF{0#qc{7?J~Bjn*B;{1a11k}G| zTtEYkK<^Cw_Aqvt4XO_Y;S>yP)1ZnTp&nQZz1udZK^Xi-gUTG>*wUcNpkuoR)eW`m z2KH{mzpO!(Jc=Fcg4*&1o}ExH9DsH>0-bOIx?ltvFzYe=LaT8522~2fISr}>YC8}Y z^uhecIsPX8p#evs{ae@#68ER2B4puYunIA^u$(RPK}5 z?a{!sD*pDwAG8&(E2Pq zph1-j4{T8VFak%Qeh~FS1L{HIDQ@6*L5OE<1D^*W&Vw8H90)9--=Ggx!!YcEp+g$f zIE=t)s2$p%wCC{)b76QLdeG)*V2)EiR4>pjm;)W_8&oB24U8V)N>5+KqqX00T_f47>4#^iQ^^wK^qJ}2aG@$w3pMLQV#u64uet-!%}`6 z_AgToZBRR&IH3>vpzQ?m2Ln(Wp&d^835K8nT_-YMU<8Jt_ayq^74mX&gK|PY?1TZ> z3xhBK9j7p_Ud0}kK|id9Vdxe48H{t0L)&Y}VF?UW5f==?4KM@;q4R9YM`;IiLi;() zS2>2AFmgWQNaWQGs^E3xuoOBjY~XVY*u#G4f)9s6`HKmyxFs*uRB-9rZ##?1TZ> z3xhBKef8+Qjb0<;0tTBn9>ZTV?ScW=0_`n~Td21pe}{OX0rhtLKpPx}b~p}$Ue<*_ z(SB$^va0u!*HK;LYhf~miS?^;9Z7}?E+6Dc-2F^Li;Uo+~^=IPw1^I&ZTkr#2 za2k4{Hi7?c`VBf^F*I&tUO-0=;}`mV#rXXIz1tg91q}2uzMyeOgPMU}nEe;@U>*!W zJJjzaZ_o)n(7S6gD@KSjJS^eO%DcgkT7j6fR<{DU~5_9gj- zz8U;MKb((zw1Cb?1xS`1_Lm28aN)4qYUV`5#N^_=Mx8X!A|If zz0eN>Fbqefe0PtUg8CjFmHjX3gGJD`C+!#Rg&lOl0QCP5I~au1(6=x3%%EqdUC<7@ zp%eB&7aW3K7=nH{34>7mn>b(&)P6+!g!@wuG~hV&AK+1y|Dm1(sTbN0@~DFUk_YI9 z{$lzS1`kHAEc}j$M|Hsv^uq`ofWdY2JJi>cr!|}5kK^n;XlK|c^|*fdo1z5KsoKmwD5bI9+eOE6By6X;ba~_<3#F#{*xG=Fm$p< zXHT)PD){3;Hf4KQLHJzr)C7 z%wuS~g8V_JK|gIx{Z}$iVBjj|D-2&vJE7A}KDMD=s6#Kzhdx*g{jeMcp$mqg8)|io zQ)q*|&;bKb_b|@4#SR+6CdMnYw~)6i+5YNMWS5Z8~%3v^yf{w%};^PnHv zVfZ@M4d}a`yg^4N<=b)mQ~bi<4fF@pyT}&|{fvA;@6SD|HXHq$8Mn*u`wQxW{##hT zV5r-}doO6ut&AIJzm2${tA}-PIsSjeIst>f#t)3#&N>Edcd*_;=bfxG(0dp0Y)?D< zv>%4=CQnemhw%p;8yJ7kxR-qAQ2%eqGYs9w_=DR0j6Z1q9sL4b4>10qub=U^g8F~Y z_=AxLX)m+|XfJd=MBbtIVa6W}JVO3|rSkK@SYh&@XcQZ~AW+^6?+ygpvO;9-vJ%ssU(+BhZo7 z$g?)uyJe&5fi4(?VVGs3UJZR{+lKO8>2DZ@+O~~+zdrS3HL4b9|3;(Q0DW*8Mquvu zv9~m;5*XYLIkYX~I3GXD8&#KZd*XmTH~|AN0)sGXcjACnsO2=OLTG~y=ztZ_1#6&x zMWgD5_LYrlQsg@A+JpFD8T78A96EMtRGE9?H?L8ZL!YgY@4cs9I1EE@97f=@9Piqw z3JQp4H|m3d?>F+@o79)zsQRIAcl4om58~a6`u9fSW1;gcyynl+gjw6pScsz0J zL;WYvZ_w`~-_Um=`Gnq+7%$K`nS4Q41^u-z^`FwHN}=P_M!wU7`cG?Az0g)k9-w}D zqnd%*8I9}-s2`TY@R^Og&x-o17*8;GRwLheP5o!nZ$F~`bLbD~bCGB0J(vD~#(DGy zbe&KBptG7h???R?Fb<&o!bVjEZ5K82{wnIPVO&G)V#YI!z?}W5|B^;k3`3VTs%jXl zrT<{yvPQlyp879uR8!D*1>?Jj`VGb-G_Gt^wa|4H^8`AtW;{WMoAC_ob@cZE)L+kd zgnC1xazoA2$le9Njg%jVpLW^hhpa^KB(VK{-O38`e_~chZQh< z5B&~98yFujcrWdDQ2%ckZ_s}q;}ZJ%X!m;Ze?Rp@<9EabBM&sH{KN475Po1_fbnuT zd<;7n9K>HKc0-ISXnTfuk09P>={M+t{m_6x=!0W00H0%c*hBv?{V4L6 z7;i^m|1$Fs>Lbi27=)Q+_};^pBc(w2o6KvziICY znv@&*zlT5QvQd5ta##Spumt*`6Z&BdbmljyE~phW@!R&~Z|^3hor-*)Ce}*)zzP_G z9+BIr9|nKaq^zgWepm>#{m_RtSOFcd26|x!3_!n>@887xa)<+FS7KL0T+o09^d8Wp zhF}1WLHmL9%jv{%5dNU8m~v=`A?SdU&ClJL;ER=6X-Y<|7TGSi=pi_+68^E zPvme2hF}OfDyipeQdZ7=#5d1WRBTI$;D>L+xDZg-+N5eQ;2C9)8Y4?|k%O_$um!+SN^J z06Jh8y5I~9y2No{}z z9EV|;a{+O-(O&3kXZ*qt3_|V4lwU|a&;@PRkauW@L(l<3&`Sr#9pe=0zh}LI zwg>SKop2I{U|}tB1?YDegrhJ7!!QhIU<77ghTcQu2ihOTFZ4Y^o?z$++6yB?=v~h7 z)AS>BKHH=^q46C327PcE2B3BY`mhMvpQk<0fE~~e{ZM;>`3PMwBIPj4AP>+A!!MI} zsK3hiz7jvM02;6g2A~gy;RdL`Mx0U(r(kFl{i}%MP4r;!E&2}{Z#Su7=!4@>d!Kr* zrW{(K@6W6QFz^rZ2E)(?^=ZZ#w80>Bz%l58Q_u@D-LwztFboT!{u$>Z=!GpX0t3+X zIsF7f&{~K7Kbfaci=YR+a2N)@z+XLae@Qv?LJ#!8F6f7T7=VK?3_~K%XjWMb)R*0? z%An(0&B_P0?>6)PDfD-1R(22d!!oFUzgg8l?;g!+P~>nNy7p;S`Hj@SU$g3j&i#=? zADo6knA1eP&<34F&8h?%&aI?y8#;&AU)xrq$Lg%6AK^Gi` zUN{c@>zertGy2dCH3#uP2iyQ%a1aLJjFhi$R@PR=&Ed_e8oHndI!l|?7z{zJjpHMl zRW9_xBIt)@B0my4Xh07P!2zfrMLbdtr=j;);%~=)IrYQ9@zf9X6UeWZcAbVj)GEmX z)S+LFPjBY?y@?-MVE{T|@Qh~FEAlg&)d-AKk*8~ryPA~&jq{uNzHahgjr=FX3-e$A z+U58H>VdWki3j>FYUVeiu&ZfSRnT#9v+9Sw+GbUFEq*U2@6cg1^VwgHuOu(mVSg3* zfL>Sw!>|`Ru5MN#sJrR+>&Zhs`GTQF{6cS2vzn4)XzN6;g?2$ZtcFhLfiBnu4d{nn zH~@Wc1P0&)48aJDz^tF*ua$hk04#;xHsXgt=!YRVAiNg+8;BcPVFVUJ?K;L0bi!(p z-$*?$a1-(yX`hdI0t0XuhHq|G1zqIzmS%oC0R3B=`3-B1d+_@+;^}QxEzoyovr;#a zx4WnZy8QTo{<|3uF#MZlRrz!Buz~)AemD-bdl`Q|j$t_rK^F}Cwpn#R*L}=0h(mhR&DBKXkzX=pSWV-irU%=^v=Q(X1w65N6$m z-kZ&;0_txuPN5x!pc76(Kg{kSE?5AKw}~5uVXqvIp$CI7=U4c7hw%zsunKDL(r)O5 z)?XtZr@vtc`k~`J%Axap{NGM}Vb&Gs_&a%k`X|VHDTfZIPm?bgfMd}3oN<2#@-LYG z&A=nSYFbJK^EovIN zpmrB=!(8ZvE%@=av~cfo8S|_~O~62Fi?Y77O!?Y~7d@@LMOAPdf_W8}Ucx2R5N zyQW1A!2pcF5X|yZKO9Bh@sk#%UdAu9B6oGPsB##AUg*2FMHNyWhLzB99q}Xg!XOO5 zFtlCYqH^x0f1nMzU=fVKDyVn1@SZfr5$u4*Pa*!CH;`Yb-AFr;+u$&Cz;V%oGcXAA zenZ^Q4uf6f4LkeK$S=oEXd79kPP?gv??JYxF4zs9g8``hoc6--%`GbP9`evlyP$C= zd4{gLXcr7TLcJn?f_!YCov;Y{U^)K7Pm&jC8zOFKJk7X(foF*Cm1W!qVEmvT2||wj z&!G=p&l3lUZP| z=0N+ot*Qj-=e4R@=sF*N(0Kv*#Ll>=mG||bcX6umlk{(Wb1>meJux z7(MS=^xmYM2e)xeXHhS~B94QHw5b7(^+VhE{!{$HP8e~tsUa9xPh6C1hhslLKA{6T zOWRZpG++nx!zuB1L>r%(L;uJ&mHQU$IfraH0k=w2&9~`^t={Jr84YY@2pQnxQ$ELoGont>NhYw$eKd4>brfg%x1@Kp&idei(rPnDsdM zfo|-y8`{(c7`l--pwWe2DTmqb5bw|0_}*6h-$eb$UC;%+&<&kF>_vVv_RtTfp#7FM zRWL|JsA1Wro%FB#8IFfL#YG@uRoVG#_$G8lnX&~+>Rpcnd} z4{m_QZM0jCVFZSt{v>(r!4I^_z`ikrQXxvTz zh#l0PA|6-(UB98dFaT>{2zEm49_9~p!XapEpuI2*^&$M;+otlN?LO?G3%a59`!;^d zg!3kBfx!o9AM!99g!&&Cufm7h)C{yg#`r`}8zgT}BZo!M4$GhuRzZJ|cB1Eau1$4u z9E5&oJkR)n-WTZqcbQkij3bUia6l${(TZ|*9y-k0N za}0~1ZH#_H&kL(L_QMdz+B?j*AohPE|FC_Wexm#_n9uPCund0d{WfL5qu~JMkv}uO zp=W}5{@yY*@d5J!?)g{dH(dWAdhi1{2`~PLII;f__P{+S=^uD548hys47~Pl^w)D1 zzB?9wa2Q(OU#6bBU1={`)I(ak z%7uqywX1UY3uwS=SGM!rEEaW*-mXUAZmZha_gmC!Pd^UG3kTzgY5%={zs9_rB1-p>1#@$YR{)i4MPvD2=h|2Ve64Wb7Jp%;$BTi`Stf!S|R z?@#Dg=(v{tfvy|cl?UoK5*PG$F&?4zGsXe5-_)-1LevLKVfg3msuo6|7wR{YN9cqj z&R;Fo|`dU;t{b(|%}!<1p|?^7$TdzRkKO9HZUBcgQ0QK_7Jd z344*lVUfd0;k)$X`;?CpKXko^J=Ffp{Dl4q#tDqTVQ74S9rQx=XWI7{@&_ZZ1nObt z1$06W48R^ao}}H-fD=&v8|(1|^+P*!{GEDX02ym542T>KLw$<+p#if#z#is9 z`zPcb%3qH8KVQRo)iEl~$WGfaYb&iYP4QkTrE>W<=-Ey+h|XF|_8N;`VySG-xGufM zqOY-JueE4vER}qf5ycMv)n2omx)WQ`T`L;a5{oCTIrF-V>(kR8v{OR(~Yn%H^x zH~Ew8R9ZSO=Gb+nwv*6Iy8z7VUV42~*meJM>Gj#Dc=X<-*H?*M>3vJD&xc*+qf4)E z1iRcPmR4ULInl^}?{nL!|L|JTx9cpO>CNfs4@j@mU!BrD(q9FGC$+~hLCAHrS>;YSrKJ> zh(5J6r=Mcc4z*;L;8E&RnN&mh4-(}>Lpi;!WL#J&ucmzOMEM!?_)0=h!d)DLYfj$Q5fT>#MBA$5?EJrC_bazSdH_#=`sC$_|UK&Zy?_ZvtBY6|@|tTC||)>(SeB#89&@jBNa zp;-J^pr8Hd;`KYwFL-Qm`d0i8pkMZQwt87AG)E&j%?PHMQ6rKV#?f&PEzYOtXK%&% z;pxTe7o$J$%;NNAoYkN|_H4GQ=5;jYO2)xiX2??BNqK%STeT*Y*PG=MU!TNB`C|KE zT;h9f@%q_J=&|P)uV0M*%nOUtw-SF1`nfM|y3e}MalE=XpOUj7^j&W(oYM))1C%e6 z)6A{e?}W0|eksOv$-Hqb^%bITe{*hqWr;q_qr9B*@L0CGl-IF&BGX-_i7W0-{J7C8 zekYsn&gah@zcLmsNc43#G6>tU6&1Xr>|M2o=&0LoY3WDLb=Q-Kl*vMEmMc{I_5ie{!2eN z?pg9Tg5HM5mZ|^7&IyV9t(9$qy~I-1yj91R*KToL=JgqE=@|oQ=KhL3M`DX%?sv;1 zf4Se_eDM6z^H+{tnCH<;y{75FZjk3~OTC5~!p_I@jHTAcM-WvF_c@nZUm=%{z7LmH zpY&lhcEz7AQ{RjA%{t4VTw^?KE_iD=Ku7Z4g`VRdOOK0NlB(ukOOGpaJH>AhFW+Qb z`NWlvUhy`|)p2vr6Es1__d#j#Q5bEA=vSa$v+Z)e*NcPbp3Q8s=&zlpFYWF?e{h%O z{5A@I=J`O*D;LMoQ+8VXMLK2CQL0o4szca$_gStEW3MlEi80SUnhG;zFC822uJq*H zv*~6qGdjnz*r=D4FW=-|U;42SJr{b#yq5Z!(~q!dhgh;_uPm516jkt_2R+%_%Ad$; z_^;!*S((h)7GyqTU&nR82)?q8U!L-MN9v#Azg)`WG0D2c@~&*i%(caAPr13bKQ+F? zM;5Cf4t}<}5heM%j4%&cVDJU)D31C+*uM-hSkEcnBj47^Lm8>dyHRxrXw|PAudL`(cgiWIEGdK1( zh@N{9{=(?>qvxFK@APDUGA6ROr~l5M?ca*nwYlUkpR!8Ic8&Rv>nPb{lpr^d9~G0! zNhUqrVLF#KS5Y3M{BkLet^@J?;Q8@YL;Q50XRThYE)hL5hq6~W-}F<2XanUQ%FRAX z_H(7hSjSDZwH9w0H*3-kv(#J__q@hZgRQJDlh_wruv}dlTaQ>L7&mk7(LJ#7Jvxai zkH#ApV#n**xT4pt;wY~b){B=0G_h2bbOWJ%~k)C?Br7_e$JBT%_Mex#?tG{V`0>otV`KnT4`SecAc#`>H}U!=Z$$!`E7IB zAd>Tr2c7&sFXk7R%Cs=j;#ySf7xvH7e!ktb-U2*ontRDH?p32 zvQA{(b7WFaFEZaE>lr~e_o3M~N}eW=>BttFr<~Qa|KV955~~ea-aJ_mGV4598M54Y zvMOY*c`^f8)f}0$vjbTrvc=>xfNs|#voT6LhmnmVTWmXL(C>S6)`zq+dq?&sk0s0U zkZE&da!#-#%X>U$lY3?HSBYK`dJH}DPh_>o9LVDL!ew3YB5QdfN9`$EoMW2Pqq|Sp zcRCRHDernR=WE#`Pva}{R8Go0vc!_ZfBMs@%Q=OrLdvy~oTPn_#8`^liu`Y}`Ixv4 ziC=ryB%S{17P$7Vk+I;z*U&3D>J47U_Dt(6K0SenmN3m6~RcVjz(?W^3!kufnZpX>s*nA@<72e(btGUg-ZVSH7M z=S26Q=J-mlifwJpJ%IKt_Hh$A{O%^o@oUxCz5V(3KxMAk(J%jC@%k?GtNyZheINR@ ze_gzO5dD_$;`JlwcYU~cecQKLhyO1}{b-T-aiZV<(c=6||9H_KVuM=EYw2s*G{m0^ zNWT4)=lpGP{zN~7e%{{~r!VzuR_67`IVtahia#6Wy_7F@d^pkXpIV&0tfyY|hd#+s z9$v@BUu@sR36;wf+1g4ReOA`fPZ#G`^vBUJ{m0__E&Ds{Tc#K1U-DLr{$wO|ze#x| zxW&BrdtWd@B+SJ?lUY?wbZ0NOI zxnkaVY@=uOhi~q_miVg0m;Y<ZW_&ZHfF zeD&9_NbDmej|0dCkv+0$|C@dPL1%y*sI=pVWXU0n_#2Z&(fx-D z|9PH;bMB?QaDj5Vi|_Yh{!sq?SbSVp$8vr~BIlwvf?g?lo9Y|Ui=bD9-j6o&mw0cR zIX|r5W1gb7ZX><=q_|HMvF)<&^8|mxl?Jmdc{cX`5 z~J}cZ#(MO&;?ug~{q=8gN~`8?mrTB#0LC~xD*?Ywb;c8*>4+PLHJaJ`WpD2AjjI01zv)FIE9?X01L;88CMxyLbbJ$E7_&H4c zlv=V~tZE|9N8X2geM~OTzeHYwJdAvu$j`9wRaa4Y1#;_2$@&s!EpjLFi$#C)aXJ@@ za{xb`_%Z7}Vz%B<r*ZwJ5K-ld`j-h z@##BrrE+ev?WuDoYhV}lfwPzgtQ9H72lu^_*P0g=IInrIm%f<5pY7bQuZ`l5`7p_{VZvBdRb7Eh`1HP|o{>wVM0iQMXE2H-o*pg=> zE0p#l_akqUd`8zQ_RH%mBN?%qKwD-c-2@tmor7nvEB38aXDoD1@6?j^E@fM$T8VkT z0rnC{DgRaWtW;|wayKkjB zWu6^dhiH8-CExs#y@J#?&40!NE7gM>&&?OhO4RQ0WII_$WZdS<&6Gc^RKM8*ZhYQi zzny$P{+tE&ES=bw;KGf+%7K;Y)7kjf#y|gnGjq=p{R~9LPV@G(=3|0R>}0GC;YWXB zrFvzyJ@}b_57CkG9-`zUlZ&;$&`R|gucck7)~#s# zwb;2nU#b4M(B6M4c^*X;SpTQUhdKW77x_2q*v4!1?E7RgUfgJ}6c}uLjUnJ)(zV6my=g@TZf!L*5M_4fDUW5K+f%eDO&msJUvvjrpyf_l; z;D3|jkabYT(F}G|d?Kx8{xx;{8f-lIJRDfy{*kQP;;(c+?pLqWRYBGQ?dsbmsa>Zm za6akVCfYCE_?y~ES6|G|tK@O{R&u-I<8lbQA% z9E{I{>E!wbiK_&=66~A>y0Xf7A#tq`pZ|k%*YTS(Qq2EB>XZ6Ar2f5^HV);waR@u@ zN4nZ|_8OD7s72quwPWHUGg`+Kx>@__YQK4H+}v2PG0}1k)YVoCjg@(Ow%te(R#bF^K_@Im!0Xt^~!#f?p9;iTYFO4k8HUK z`w_;tjG5enxW9b6u4WcGPjzmY)F<1e%pv<@{K@>T#$U}ny881H#=OMQi=FL0T^&!m zQugJ%d#PCq#3A=m#osjk?0?YJZW2eTvB3#!?)hh*1nSIb_~bM=buZHpQmm7S%EyvgZRqB7ncHNpEjp&{Jhsj(G+^Quj!lptb<%z=B{P@ zzo93dok~s$kWC_6o))bq@k}%R{3_}5Rp}q3>ghu*WhJTPl9NvSZ+J^rZM@z@PR`m~ zPQ=$3zAE3=)jgZ`b>jx@u4I72Vs+Y2(?OYa?59RgJHy_jUE-P5a^o=-Q+=v`c>c*iQdn>iiT? z9;CeGBVDoNn{A$bKAiZ-?V%QH{4RR5Z8O+(V)OHuO=2udo@VFY<;n4EJpUx;jzhT5 zh_4$q?d#&r`I5fw!k6t6J^HzP`fs)^=^gPFun{)j=Rb(1YccmL2co*e=oX|cPB(&X?ILya*3rI2 z>XxB9vq)Vxy2a^>i`S2C%OZ70(G4zAS2l}T8HN?TwTBL3Zx}int_Mw}b zxwv@8(5+mguC|_i)*^Ka(G4$Bw-Vj_EfyDV2f8(j(3QPp0Ns9c@0*=VESmGy_}h|K zbeV%w63>>aRKM6=Wj@z3=U{Pc4qh;44nFnL%jP~hj|YJPYOyr6sKghM_|WB)FlQ}}#&`9c_(V64iv|PTt5WF7SAm($)1v3pGIS@i zSE;T=?GH1qPPqlA9_hnwxme-DIDd)N%i7OURrx(O!i{u-SK~`U;1$pKh|%pO1wuc>3vm)&-SC_UO$q3lTH?Ef{EGM#F>@l{Z;O6@K&(2w!&L(1VWKv@Z8yT;1o zy$IR79zkA#d}opKC$b4-#vGYk$3&2IA$wDlHXb8uEX8v#fz$n|<{V0;spK+jokuZr z_gbag;(y-!PcMt-jHQ=vuZZT4*L;am^z|Wg_?-v_Q7wn~feE68a$6LHk(Lb^mopXyK{q|J-aEPVys8sR- zTt@I;wePCvJ*h<|3p6vvcXKUl=oBaJ$aS7KdJmT zX1NvFDCI`Ys_1)p6ZQ|rI;qlp))YZ!`qEYE+Soj$6?_**#(8Nmtv=R1smpo{``MP* z+Mn=ssi`GxE2g}nb(Q*r*Yn$UUy8QXi2n7f)bB(;;V=4;%{)Y%QXl2!`Ij7<`~B#5 zCk@Bl$yydbZ{nw%>tbVUj$R8yfd~0qM2rJus+WInh`{bDv>iF4`XY{Gh^SoHOB&O0xx zQtM)|nCD&+Cf{9>ojz+I=SVC14)n`kXFnkh;{BSjM|?O{N}pAuGxG5&wc~6IvF}01 z;L~%lGdP_lx;}JkKU<~LZ2fWF+PS(iMu*VV(^sp9c%7&_{dZ>j&Hkr6K>71hK4;!0 z`ah7||Kd|Wj`n1%p7Y%o@_DT2xR-Hir@Wu?O^s9WQ;A;pma9{acPaN!UbI!}dZZtE z#Gdj?W^-l^TvvgVHr^d!l z;+zrRv#~tQS@7libL?Dxun9}tE8?0Xdc84$~aRe zkdKpAN3VzC>rGVmj`_Oz=w|bXs<}Rw6_(}!wTY_6Bx?SjAGMk6Q`P%#-~69j+j5W|kdGBv zuQCtLGGC{^7cIHmJc?-_W6H`*QS|?)a;&)6EUunYY~H?()fH8Wnnho`=hkPwCqAY+ zw}v`XDf)jbT-8ETrfHXP$LhrSb>3K;xA!~U;;6Tjt+hDUS}NCATx%^gYc0mw*q5!W z<*dEd;^SXWn#1CUef$gXZ;<04|3=b|NHtO=4)=-VmpJ-(ExF%x9H&|w4VJPsW*lqh z#^K;gUnGt-n}}oWLUG6#$UKSbW#V{J;*hU{Nqddc5AYG7Z1?I5*KVZTX5ZO<)`Nnq!i4S5XD~75tKF zvwts@`DVWRJGv*Rpxo+Tt$r3Omv7`nfwz-K)g;#*bh^@J_1Xl8b5PDV%@qUY&UM5)JTRa}G#7d6fGoe`TS1lAn16Qq^O{ zwi-W`gR9lvv2$GdzsyXqWDz{&qSJ|v`^inO+5PC)hBjWa#m5L`-IRTs*YYRrn?UAA zwxOgx+J-eHy+(K%!^N_-$zz2>bYz+x;5ywZ@S*iK1-Fn z1@Kk!+G@4G_~K96HH>V0^ndp+V_83gb>ofM>x9&6!n~VHtQU*@Jg|ZPp|RODckc6U zS+jhUk1kLyZ4FRf_0H=5`FX4O%H-q|z*iHmH!&Vg-`seRwXO_bx$m;Zi!XDnGdHr) zJ-4iNh)elrpjU{VD~*H0Hn;c7SfBE8=c(ZrUwZi&^ExJozw+_birYG78_a7H^BK#P zvzIXi%qxng(ek{>}#?2V*ge9sbA{<%U7#k+C44x zW53jV<)6bj@vlp>llE3&=fm!+wzn61J^a<$EA>xE{n+2k>)G{)2inloD@GW@85>-Q8>9;Jt00g51BW6)6*+r zcYki%cj!KV?XgR*9e&$updvH@1ytM=6!bfc#HPbGjj4dSiVoaEj|8?>KT!g zZ~Km&r*d#zLp@plSgj7`wcJyx=Zq-VfnKQ}d5y@;M^Uoo%Q@+ac)Ol@34M_yPEt?3 zWS&chur;PvtM|lqv**(E$5UOO#X>aqyg5Ad)IW>uZ*eJc+mO{FD~Z(+|BQ1n@(sw> ziG2O+XQiFUC+5k^QK>=R{l(XNf53|0AbyAN`-V(fbKjAEn|YCyv3HtmIwZ%F=y?9M zT2=BoHm+hhK5CP{AtU=u8=L)JY|VDcG5?B?jU(f6Vtic@f6;R^-xy+Ut*Kk`=R!A= zFI?PJbcs)%*UDV;AkRhqlF0e2Ib%H*Q+oME9iM*stO#{!(;)U6u)kzu`(tO7Hy$`_ zh13kTp8u?l{$^2hp2g};)Wd_=#Aq|eTEPWer($!Dp6SMqCl2i{h`%82YiXs~w}yCd_BA@tmC+*GtTmlV>IPnLm4? zd(q4b=^y+V5<^lSB)&v?(f*`Q%JJvJU#0jg_&-Ta=3GlB9yU3P1sxLPhGjrd=XidC{g?jnq9DN$y7hBJq^Lnn* zLa%vyM#46!HFFITKN3Z4iJ8Xh#DIy84J!+Bk+!3HDm~HW=>3=YbD7TM&k8K`S?$h@ zsJ|NgW$mz|IyJSw68Y%q+~+*oEb!i(K5{AJVi127JMO4fF657khPnRubtwDB&yAuN zA3FZBFQ%Wr85@6OG5&o!GB)!mE2eA>ujSmuLkRPHQG(n+zO%^r%jcgHSr@Wi5#VQY z@3HWrpYyHx`l;VzA!}~{{h_^fRG&(_qvMx>7=1oFkscoajB$!5(DlE@+=-39`1KTD z4;5Xj7x-<)To%>N(2goCR!7_}aUGUym$(ip$1a$WyX5*hup3{NyQKQ$95RHRvm#e* zKO1NCdXqOb%zhsDj7MdpuA}hRX zwhn367_tIn+eoFv&sPzebx$J?&e4^4v|92wM`lHqi);|tLhTp3VsvKa*omwhna!Au zMPx2ydGlm$WY&4IPGsHlWWC6I^JD>JUGrqa$U5iA#*uZ*lT9P@&XZ}E@qA^TEEkz) zjx2|E6d-dWo1aq$^4dAN(&h?eHS=UO$f}W@NxA%0@UI1#|H|C|FZZQ$@jroo+f}&> zexG6H<;)+-_m+SdC#gO=Byr}WSA*UsqQ{!UdJsJqd=1YDBwro)@2Sg8+<%tXdywVU zr;go0`5@(`l;6YaO~kaQd&OC7!us&FllbCK{9BO?H{|}$-?1p|tHxKcC%JtdWTnW= z{f?RY+3&BEb-su43d#@Xb+m6%f9|J{;$d|A8gmuh9nS?P^w=6-8Q+g@{GQEBHh#gT z-2eH$wDeaQzPf*$x;+Jy*HE6{k-O;6>ByYx#m0AiZql`bFS2#QUz1B;JCFjo4{dvi@PWl)aq`yJGAry6GP|w{l6Hm=p2qW=^tm_aE6@ zjm0mWrD*or`RG&|llWz+IVUzySFz<=>L998Yz>-uBdPXObiWa#tctRIr7Y2Ic?4>n z;uAYlPJHJ6f2#XSntFs&HPTQ;w3*ER~>ar{AqGC>piAgmo zDJ2>zB^BxDl2V~kQc_V-kx^1nQBhG*Qc;mnkzrzyQBhGBi;8a4{XXC4%%5{Ub7tlo z*Y4x@4_)h?IVO2gVzM_xsndUpIPa# zpJ42iCilO^ue;&3m7Y}4uf?ZE;r7AZpSGF#My-U;5CuGMv2;T01kAsgm;IrUM4)`*7 zX?eE&P4Gqsyts$)&H=9kpK`$Kz&9N5X7KQd+3|LO#~tuq@F53$7<|D2p9Bv+E!+M) zc)bI@3f}F2Z-Y-b;AL;%{)Gb`0S`SrJKhHH76-f)d_coP_*ED9Si?!`2V6@VN$$QEVX@XOFW`0g5oOw9kAdi;3 zPh0ao2&en3{C1|`)SR80BYn{_oDDd}vy(R|0-R|$124>PX9Z5vdHFek1nuek{G2eH*#ev< zIEm)`b~@qIU67wM2xqGRX9~{bi}Kr9hSParevZ0{@v|jArxMOu0Zt>F(Tnoi>3|b| zaemGKoQjL{b0*;|<>81gF2NaWE$oYO(|i3Tx#JUCm2k#hn%|b;|L{~iKQ9EO3tr15 zx#JYh5S+40^K+)*EWpXvm#o0)xh%h(z!COS+wyb5a5nRBjQqoyc=<`{DMe(!=x=7E zzj+|p-^>Usye@cSmlrOlVR(uUr{o(aX5dWc;YfU|aJpYnIKJXu%JY@^-kMXBEy?0Z#B1`qNkEw-bTW`I`Kk7@WxU`8i#1)(UWj z;EZ^4kgA%KEneCk&_e_4(s!f)nk@A6F-w^*o$Pe0~tl*c%GB zpJ{mN#@z9Vtra-aaPrMbgct0izr86xF9a_NujA(2aSEpyPWVWEPB)y@0-RwuBfYup zNV;d>w7#)$y4T=M-jdsvq`UY{`1h^(IZ-%~zWkhKIBNws-EcaS%ouKfD?Q({@tJ7P6STwXnp z-&=FrlDZGRg3xQFrF&#NbTl;Yhl>;B*fbPWLdpg}3LAZw5~PJMwc@;WXZr zpA+n7eKVAw6M-{RfD?n$^UnNsy5Q8lD?eul&SnA5G@OZd=e8qdy#l9WxNunqZ(}@u zPi|WwY(?NC-kYBjgH!ju{G2X0TLm~na3)9c+nI*b`TqQz6*!R(FohS9A|ouuZtw#Jv-)AzmH7G2zU zCTjsr&*x84XV^4m`g&5gfC!UxZNnXb>oq?$Z(~031@F2JbQ@pvt{X*H{w43aWpwpl z_O2_tlV$f;yz83K4a|7g^`e{lsyChZ$27W?uetXR(QKgUx!1i${G@V_@}KpVKGC(J ztN*%p-5|R5Z+O?uqZ|09cU|D^l>eM}T^+iWZ+X*69dw}!e#cuKjG-Atpqrof zt}A~B^RMrE*Tv9Pe9ybC4_(88cijxSj_-TZNgZsV8~lN{I*8oGc=SW>akZgqS@agy z5SmRiUh8`SUB&(0bW$F{AxvJNJGfb=QZcVclC?qMJe2@q2GNN#ho}!9RFQW8^)Q|9^XnOVZee zu4TiUPSQAxF7b%BG%ld2_@lSDB#puMQvQGPrjs<*qwCo8mc}kL!9RPCYaCtlFW%x> zK{JoWYu{S_KKlQ^de_C!Rs7Ant`A+q|9I0$`Ol#1_`A0<*+diE_7<1;NMwZa|A%*7 z8@iT%de;r3OZ=~Q-6Fa%PQdU|=Ter%?`J=$=w#RHZ7Gv_G#x%~afz-Q-JsvQZUWtO zz`Je@-Rd#kb)gSXF2{M-wVYd(}_>7pld4i=F?@P>=&Z((tgCJo6wa$$-Ax>UG0;-=_GH{=;CGG^0t9y z8;#ezg+E03Kh?V~j;`qh@46v$UFF_&3+RSV^sWou&Haz3dDqpWTYtKDT{pV2fAg-J zKv#E?H=X#(8oIWVz4=PThY^On$JLB3ywiPLlD>?lG zej}Poi}=H$N$>w(YZGPm|Iw|5z3Yb2h4y;WN!}LGwLHsP-hv-x{(0)j>ILYNc{AUk zlXrWPM?~CUe}T@phft5M)uXPQD7w*gp*vOL<5{`XdzPjAIY?L^VPz7=Pb9|wxkc`;hA*poy_8b0*d;weiFFIMhH|e9ikDNX)_V(0kp$9nn z7_RWuIQ_weC###R`~~AZQa-qqevf!T_+fsd@LMlBS-sfGAMwkJ<^HDh3-G4xR=5K% zc9$0M+kW_}^<=fcf0_1WB;UNh_GL@^f>Out44Ppy4U#~!O`3jdJ8S6IcF(n(wXHse z{*sf;cj@sf^Q`U6`PL!Aw+Vj)^J1U!G%S-}pMJiz6(*0otqs) z(|Ut@O+@mKW-M{C`X|@PGB#d`kTM>~_~3{Xsq_hx@Tb0v4+@`iq)Lif#r{rxSB`h!SJ2D2G>g63H-oA>xS(5<}PTYMqn8%4J^5>mh9+I+5hfV1j-52oH35g(d^Gx`3I zdZ)Et;>>92m-v2Rx+6=SYrh4*_j4h2UD77@E-}ta;cuy<@=r3}d^MyV%2h|aYj1h4 z&-W{*xA!;$LR=%nZ#(t^_l5GlC*BWd<@+IZ9(?(czF`C`@`F&$cXww9ZzcTMBEXlN z-rW^DYw(&EL+SC>dfr|s;lWSgM?VUs-@O&A0&L*^klM+${0J5W>-ceoC$<~Fy1~vA zl97h))XAR4JG5|JKF*8*qucZKaqnf1aA|30L?nzWZS|FwPV zCC4|TICx+B?FDR&{~@II9mgNp<(~zQ zJ*^z+Td>XbuX7$&7K+5yG`2<_xAUlNg|QW!qThJxPV;OVvkfqpG3PNHuw)=Xcs241 zcMC4vjHSJBo}gu|$mwI5V={Rht`=$sy2J@PJ)iLwLOX`G=IJ|go^K}QF%M^>VyCJV zKQi->wTAVVYsbKFjJ5vZQ^{lb=V>?pwo|=>>s+>cH{0`A2{neU>7VA$@bO1FPv*EF=h0+7Bf(e9qSOI7HrkJz z9fd^lvWktd&`veXb;gEPUw!txM4(oDf$?qUPI*N#r6Y}*?{!-DNF<&n_?_@)GW@La z_f6R25nV63Ep#8qr+b@SR|j`m;@h?JQJ)>Rjb1%tr{}Zdr2cBZNPDT=nf}%u^_TiC zDMw2f9ZxU{FAlGG_s;Y_R;HdWNzrU)%t?6+qA5pnG1ocki_wV6NLRItuk+~Y(fzyV zNP>LRi*v|I&%qOcU#yS#a_3h<0#L(3(?;+ehGiT-Z|1v)8kGmU&*6`UuwyhaUe$Y1$Kna zs4EM}=42y$Gdb-ISjJ8^#|?62mL2QYC``*7Dd#ydW8L2IWy)snPIC=nIoq!nyd3;) zsf(jL+fRIK5qsU(^KzC8Lx(E<3jJMl=l}1{>63nDj3mC*q^Kqd^WWQTR~X)4C4l2Z{-?$@fNWV=L1*_Dn|cDc-O!0RPU8= zbDT?lpUZb=s$-Kl;_%ktogzG=Pm+^UvCnVM4F5bQ6aNw4%OsXksly?{$_O*QPiOWM z#@LhIYk!YzYb7Ds7Tp56*8kY4Zq1CB=q&qhB}F#j3PMRs@T=?}{AYGL#bza8bA)+K zODnq4=X*70jg!WIbgnuk?%YfawsP;oEg+qvHJ(2TD9 zh0b~CBrHOh*F21&t8Dg`2l0s&bki?#PRll73xs*~>zY~ecab;0mb7%B>%7=GEq#Rb z66Q56Gw9}D;yo=}=qlsRX({_U^E<-4rlkp8;!hB4 z^(bY~iZ1q!M_&fR=m+ldRt8d^i|E4da?Z;JVReLg?K8sPq5m58)=niYadgY?bxunU zVH<>bP0J*@+V^`;%R0K1yPeY#oTon_%xhZe(bav#TUx|7y3sX%cc+SF+N!x%WPBhz z^-Yp_OZzb2sg>p+`YH5n_ht4nEbS}dOFgjSXHW?ruQ zGj8nDeK^&Bvfwe+I+fo=bN^1ovNAa)vI_C}K5Pt%n3OC;_-%r>@xV^&H;at{PB#Dk zkuka={04q|;Lkj`Q~gJ#jT^gnBsppnLuPaDn4+xBy-Z$au`~71P9?X$?PI~UnOHl` zd~d+5`>8u_#eJmDCASf7!_VB=?Seb!k~;#oX~mu0dAJKMxm$3Xf9}q1<@XqmTykS@ zTOW33w+HTuOYRulwqLljy9js9C08D{=~#7VHv)IVCAS4`*Du}K?S;GLk~;yn=U0X8 z%G_re?#f#JxsUV(0X8GMf4fsXB<+W>nq%XoeP5Yu-xEdbqw?TSP;5rw);&_#W(&O0 zKNn7?#NP`y{FlO98Q&-1j>Ek-({Iwj$wzyWXAn5-4Tx?HUBi|)os3_hA5e~ea~?D5 z2-_gc%e+9+(uHpAAKupjWA!u`F45QFA_hfMK`!( zSKj$|1-v-Cb$C~DZLX)}JMwL5e@YNuSDfFD)WaCO$|pJJeU`9V!o22v8(pHzd){jn z*`GSJOVvxB%)LK+P{z*mH*1VNwN`iuc+U_X?dpi{lJwqRH(`T>>2)dXVUV!Kvv#TF zq>mf-R*bRP|J|a@h!(GbI|H|(X_x0`pSC3ab9Q-t_9^-!`uAq<@wKCyZS@vki2RM9 zYr1?_&S#&byk_C_UA;@qa-AOk&AH5HEX8Hadp5-0t9GgK?6Q5FwQR+1#r^niXJNZC z_i2Q?c>OMQmDn}be|eHMQ{?+``Z9$5 z@Q?AAH|)y!{8j^;CO8u}>{7=i%Ql@SV?M`?{q%g!cZHRluP1zoYw;UvdNXN#bl*d6#AGXmXRv=& zUi&^iJZMaNlMfkWe`i8Us_)^86Ah_rY5QGhrrxwm)g{|r+V4_r z-~Mc+|H+QeNSO~;)?#lKdt-0jrLN#Qw;%g1u=^S9Rrn9mPSEbkl@=Z~wLGuo`~nH?w>bPs*+bZtV75>Zdkt=J|?*ZM!OJeD7`o%{H3L zk{ZUwo zq_3?5OZ>1hZ=c)>uMOT1yhpgs(>_09Z=XEF@M1fXJu;YSo5gHI-nedRb)%09~{q6>)eyRO9AAOX4 zJ8nei_5Aj&5dVXfYLM%UKGipMTDqpQv%01OC7s5e;5;@4u<>ZpAbBYMIrH6z9-Y1( zeG~fnpH`|jaGf^~uXoIXk%s|n^!%Yxea&V=FUQfG<+y;p_`fSvYY_(cMxpr#Sm=D2 zEZP3OT6(!8VqWtvb0TDA57XW^DsA6essWFH*MmPdWxL6+@!G6^dBlT8M4V=y-G*-N zkxIutJ3-hoVa6I!;^21>Yzu6ZYw_Dc-%gx`-|{D&@BF@M$$e`7vbN7`_S>y!qJlOegaKV;f8M zoy zm`_|`6w85<7$ds5{EYbsGZvyT(hYSBUCqh6)9=igb5PnDPl#vgm2W3&9o7tKYC>Q8 zOWH|vw;Gc)VmoUN%UuY|ywTsDYs~eTKHrDE)sEffoY*YO^gD3r$+Ik@iB9T!3jXF* z?)V}2Yw!cF+N~Co={4U0U^_)hC26zxSCnVxZuK9+agl~P_zTy#OM|4j1OCdj?)c)P z!|*$=-)+8^VbrbcotaB?37?kq68=W6GwsWqn@N1jgh#vG#V7pYU(>(6);+%te(ZJb z`5o}%z3$6*SkiN+JNqHhKQHMS%wNYFaEjmI&Zd+}XpMS!mwSE;{`kAy@udzE@TZ4! z*YOD9#qV*hW6AT3q;DjDo>$?te87FVm;HwR=Y#I@AZ645f9u2U_>$)?_`#3l&hsGQ zErdJybrbqY!pmz;F`uPz>U($}+uHYx=IDDy{Pbhk3;dS#&;zHW-($?x8aek86@ur;s} z+gyXW3j4~bM~VE(I#n^pbkVM$o&NJFszZ!f=dXsg-#C-tdVP`;HRl)=f8hQe+ELNs z(}m{YP4GMaqVt{S;Z2zAfgdm0qh3lsK`(}=N0EuKjHZSo9_NXT?r?TbM zxILPhtN)L+pp2KKWF8s(Z~Cpr?@{+B=RVBm@CD0#)nC~*;}Sxvih|5 zdkOTBCzIyPoGbMl{aZ8h;!eX4#^H{fzejCn(#SFkKd|NRH(B|Uc-GLiU9d;7Rh05| zs?#{PgIgoX7nTT;zN~D6@#RH(%=?K(z4Icy&;QDzaW-^`*zYhyD4u}gj@LLamdcxLjjQum|n+})E_%jp0*ge$Z=uMY3oB5cwD$(+Kl!Ox34*@ex0lvs)4n#dDh1RzEnMrps`m(Hb`ikv+>l`navyMa_`7`s4SA@0tqJjI9J~3$Z33Hz%j;8L)usT=D zocN9V9eiL~mfe!R1mXRJ&r5iEd^Y_)({_CK*ms<>cfzGy=CKw3aM;yz09$Y?$HE1l zu?S(el1=pRJz?|y8hJS4Gu{R(HIC&99Z)=!p1#M{xs@-8Egb>T7cYTtL^4zmu)5T zFdb~!H%YtLGRh$P{Ih+Uw`xx9{f(>GLw8y zz#WErtJun&Z&&A}Y-1}iA68TOZQWIC|3-g_t)um|3TzExYwCN>GD zM6Wh3q;1Z^n}T<-m1jO;$1EhnTZgw%z+O2v%KlD!e<)M_srO=~?4yL$5%yljdr6Zv zp2$A6b?fCkztg@1Hy65*_h4(_=V5iPjAI3Daok)j*uuTxXQ6NOP9F6#tX?L)-)Y zXR20bKjBS5`#r?VbCmH0`daMGW3N84H$Bc~_VM#S8z$p@&{w3o&|3HLE5JIz4im1J z%ZKt}ha?Y#qoOJ_f+s2;i*sE@oylQI+!&v%xNdJRQdro3V z^4M@pk*dA==<_JH2Cy~VxmVrqT>g67`Ga#7&9<|Qt=M&sjlYMFEmDK8@$T=^x3r-R zckfN#6B4W&tkQys@AZR)z*5g2r0bVyZ)PvVIKeGF1!e<3d%=z^Da&Q-mA-bb`V!ZA z{bua>hAlH{M{}|!3-eHaFScGFwrFRpeGa7_K#v185BKgY4zoKX^QaH>%3 zOB%+BW9IdH(`&UOzIZALO42Y-_yXbQI=2tmda_Ih{kt6dH*UM;N`sVXG>Cup>{ajN zI!~E)7b??Mx@$KlU6?p1rlmJ!!W({V{&+X=5B+<5OMyPwwT#`ilfd6m4(VSf|* z!xDe)yu7|pUgF5h9$%yy6MM~b6bq*H9H*r6?3Lf==wH~E^5`ax#hdo3C%LP8$2mf- zx|hCv3HyUL?@d3ymhMZ;cMP=W4SXX`nUHjbb`+`NBmWv7m3}aR{dw#^k8AmnI0wPD z{zY+$uWVv}s`u#Ia2Q+l#gxw*_ZHmmlz2PfCT`h#)aMdcU~39ny|?boxu>{9_-x-^ z^+LF2d*uAzGm={-rDofdlh7nx0dBr-!F`W#bGDuAXT^P-)n^@ur0!zaYI@VZ#;?n< zKY{&<{=Mdzm9)*58@Xb9B5abOY$bhWtqR4S-F9EecRDUA~Vxwc9gF>K%tL zvA==+x;yro_csgMzt@X>$y1EUY3p0}s?#L>{79T_VBKKRPbjx&!F^u=PYj0bO9#vdDd;D_l<{YRuqMKunGCyv zHYxZJcpJFk%RI-DXebeEbzp1Zi+f!^PcsZZ`X!z3{5*}({^4)H@0L8% zk7d^XOcaxkc+fD7`}|vQ2Yb!!#*<>3+6WtKH)e|FXD2Gm- z1P@v6zl%_G3+R@2dyg;pWcH^{^A?}@M?JcV=S0*ma?SgB`^DP!zU?^iH``r5w$$?? zd`}X?**=@KAM|HOU*ixd^x_Z8*bDtvL{%lrFyjw?>$~0|@y%cv^V9l>Y7;JT8|z=6 z@B7ISbD@YPV%~39D3W$o3%~bZ#JsP4Zb|4y+Ghwn20jhm%(dxP_WQu~z+G^*;CvvN zuJqiDb)NWPX4(Cg>}5B4$=eh*+8QG2XIwJ?g?PJ@dm++I@UQu{i}5~-?A5HoEk6`_ zRQqC~r_ev4eX_*EkMuV+U@c&$7>J2Af^}(FE7*Xh?F1XP@T9Nm1)Bo9M(kzRyS_GP zaq6oiUGv!4z|K>|4nKALu7GWW@mNf%-5OJtuwK@DP9@6rm32NNHbYOPKF`SHRnl7n zwgh&x>FvQz>0x`>4uDlyun_T$g0);4$+_n!oH;nXFVF3Fm2lSK%v^~buBDuvzLg?v zy6gnTtE*^-!pk`y^tt*TL>ybKkAW?zi*an#UmZz58zAYM0c!%|QHMNf&7QkHn)e)K zE)!(|yG(q~O!gD$wrj4dbYIW#C+a9cYSN$e#?$iY{jtEimk`uxr||K&0uR6cI79^ z?*!QLH4#tml&+!)UmJN$p5=%>js6Q;xt`^S5k5|Mp=ZgZta{)sz`a~Dmo9fYA=&Rs z9RYX9zK2e!>z`{d>TnKQo7Y7iljlOpo=*GkiX8PiRq`0eR>w_`O}fUhHG!?WY~_{p ztXRSC`c$Cb!dCCi&h;qmyu3p8Pa^-4{TV5jZtR!c5;^K;SR}2p*lKxGFXBzV` z37;msGf((3;UhW1C12ZwFA#p8*iW}Vr+j_RF<*5jp&yBOI%mI~sJhU#-%TGOW5~G< z&(QE@t#!^^%F{*neq;W-_kHhlIp^%STYUY;-ew%3`a-AS4dlr>ZNF2;wsi1yxuxtHkcOg*T_TFZC3>@y)bEZX|n7vt8Yv40{qm#{f|KkNCtHoxj%v&SCI z8RnVPQV)^JBDJyd)B^{oIaC9cWvolPu{HQWM14nWrSqIMml?Ou^O+bzm$7dO zedSLg={-gE4e_+o*@ZCIy-S4G5dInd&SU=_PWC0Q<-56`i~d)l&-P)>=X{R)#FAGT z58ANR@*wR}`pRruS?%EjM+Y{#GNYYOVlVhJ`piHMd-nckKF9FV*^XSD!u_X*BPvjw z!xr!U*v8@<#s-RV-0wSNes5RGp@Xn+%-EZ(P1l1YYBo!f2wm*ch|wq%SP#n#sG z`_w?L{e8Qwr{}1mPXEUpV>!fYb!Ps<>=&N6@2LGs%CZGps}=jynmZpolq=0*YZP03 zJNK!7xT}}xT(-n#Hn3Hrtr)gC>i4N|uDP$>SAXZ|k;yhRjIG$A$0l8C*cxnnY;4s$i~Y~DUE9j}9HEp~ z0$Ua5y0(?K?B}r+KX0FEkul$?&#}Fm;7d4rV%+Ep$|LM&H}6v)a%U^oGt9)2Yj=;&?9{J3@>{oBRf^CWG& z3+-I%KE>9pQ#~2CUMzivZ_v4-7DO&SG>g5VmmGb*WUMJYmHoSy?NfVm^|f{%%JuxX zJTU1v`->BYltDM~bY05$kgE(kZf4Xv?`k_Rey+5PReQ!za&K#1Bh|K0pj&obh?U~cDVLC^c@$7)MHhC=8 zg5qD@#IZ>nM>~g?zGxO(Q`hWMFXB4ST8Dje>)kzbexUZt{e*ov-|3UIR>B#&Zl8LY z_=8j1(w>1h-LdVPeN`v6#=G{Zd!C-dmi-xS{~b?vs%3KxIE}5+fqkk9Q<5&@e0lzt z@7>k|EfXFk{C)|~sz=LxX5ZZo%`Rrx2vxH_Itm-oH??7-{jK}dUviZnFOXaMd;fio z2f#rGdP(lHQ-yneZ}4dXWL13L_dhW{XP5Cg#y_1oCuRT$L08} zy^92V(JxAVMjoAB%A@SrMQZKi`_#40b6Cz*w3UZ{PmVeG1o@Zrv|+D&a-aEq+jM?X z`|R<|^A5f{viwleGm5?s{r%2sjI8~Gz8q_e1pX)Oc~jE!dFC#;?Ahn&Kg_kB5?l5E z&ig>qM{i5=*^jN6nSJV6xzeTWoBt|D!czHM#@@*6(Wg!F8Ga7)mARw0)qt%oY&FjB zQ$FYZpuzqQvF|rdJ)op%8at)mJ^FRA*b3INU$}seI{UnwuVd|^&d%ZT#-4UFw!-)C zQ&%|mPr9vBa#Uld+&de=R?`Ff)VH~|^##^@#9y$l9i)tx(5<4Yly$V$=IGFl%``gy z51j5#B+yCxq5q)&|JgouTBa{z)7y9y%Qy$dw_<+`E!79MpqqUdf63?=eDyx?5qrWd zec%xK(O>RUPs!FZTBQ2WKk9lJB8)y~34QNca$T9uzY)Ks4?Q(!A6ougp3ncCWBM)e zXQkhWU&_A){p25*L)p@wn)5QCr02Z;Qyj}b*>{d$XZw+T>d7`cMm~5uGF1*6dO64= za7H<-pkLnHr>@D?Tgu_2oaGSuPuAyK?7ijHKR2g7j=tmX`&2NW-nXfzUxqRx{Uhk> z{<%+Go=-0`Z62Bk84u%dBmsk|7^uX?io{=Zs<{u&+{id6$H%uB%!m0#Qsoq^<9Tr3 zsp|E4;|V*)BlX>gzTuct)#q*cRDCl+%+z<#slMfYpp2CR*qIKVsvc*vb3pEeSmq8{ zes9bjO3^Q%@85B%?On4~us#jj0_)YV;^%RH#)8Q`n-EwJm~_>tA1T*b@GkK83TTuo z8|6M;IW)_4K=_iLIJ|XuLE-TuSQpqD*bW0Ru|BX>Fu7$WKf)UZTLF85ftc6?*s{i( z1zXavC9p*eTL)XvF!g-;Hw`NTo71o`*sO-tfz4=G3~XA%+Q6nXtQ%}n!}`G{G;9QH zT*D^8#=xxQHwQMV@s`0xG;9NGSi=JU#rj6W%E1OTECM#5VfA3*Z`OKi1`~g?V(nn! zZ&s`aO#ID?4S!P+$}SkLnh8dd=o*RUv9 ztA;gzwP;uiShI$8fWS zHU~DUVas468nyv8tYLu$`acaT2OHF|2-tvz)r0kGSTk6khP8wBYFH0gLc<2YdNgbl ztXspTz`8VS9;{QtR=_$mY!j?q!-9wC|1_)uEUsZuuvReZIT;OLEgIGW)~sP2U@;9# zfHi5@AXuY@je#|2*fdzZhAn{AY1k@Qt%hxZ)o57p8T5Y|76OZCSPfVh>_p4Fq!CQF z*Xm%eGik5HqU}xEd!hv!0;?nrnFq;_{67vB0<-dFz$!Fs5v*Lp*1*a%Y#Xdp!%7d+ zpMqJ_R|yu>c(q^wjn@RGz+?$6KeEpp2itz!spcNEu}680@r;V(r-v}fPl_+=%>gjU z&y8Hmy0zcO*B@^2Gr2z!=y|*xiYxQ`!#iG4QhvH|2~GBZ=g^7&K3qWOy9pzv@_@_} zBprc9bNorAL*~8ZU^4zxh&>{Wk!r!C;4*&ccq6!se^y*<$H8U%OxYH!3rxmOE7q6P zTd`p<89%Mq1enBc#b&`|{Ip_AV3K|-whkuarxjCYn&YPxD+819(~5<`Wc;*Zbzm}n zTCo_|wxv!bzinV!8rBWAsbT$K8yYqOwr;`1M<&76z~08StUYcKA35Tex1e5Nx(E-o zHSCaUYMN_mTGFwOPUc_M^s2MW`Ii+d18dZHVXy`bs{^aouozgKhP8po{L30gH<--7 ztXMyo%)hMI2$=3alVH04%z=sjS+&bxy8mo|>HZVoL1f*3%E5I1iGb<;Qx7KdFKfKb zV7mXbgX#X$1E%}W0GRGSqhPxKOo8eCGY_Ww&kC5;f5Zb8rA`}pkWEHc?}x`o71o{uvrb82Ak2a1+ZxiTLqiaur07j4J$s! zoPSx%AOtq9@oK=vG^`P9RKr@qMl`GwY*@p3!G<(!2y9Tp#=!)hV4WHk2kX$VF0ghD>jRVV%Np-6SX{##(6B(voG)3+z8tJf<3+$qHLM=2Si_pZf*RHi7SOOB zFr{GwU@~8_rezdtOT(tXHZ^P>Y(v9Vz}7Wv6KqYxf-j{1)36G#6%C7mEo)c<*ph~| zfGuiR2iSs!CBWu2Y!GbDg2}vV3~Uzc1g>R1c5ZS$Hj~s_ap~ii!1ego!SwjmdD-!o zfm!1hdtoq%-&!wqU^5yP1Dn>cHn1rT>js({Vmus#iI2kX_a95$=-hrSQzX%T$}T6PF67H-&Yt5G-lk! ze7hB%p1w{nJ$=1kdisXI^z@B`>FJvRv!+kl>LQq=&)Qbkz$!It8!V(@r7vQ9(6CCd zat*5mE7Pzhuu=;aB`tBVVzA57WtCd1ye7R=Y1;9E)JnxDw?Q;n^$(U+|6qFgE`aIf zy9%b4?-rO|zQq@2mv0D6FW(w4y?h(N^zv;5)62ILOfTPFFui<-!1VGR2eX#1)b$LQ zl&`g}7r}yH)_sySuz-ebgDDLwZDIW7{+m@>3AUwSwP2eX)&#bpVR5i^4eJ71)383U zRSg>kTLH7~#Y})LYrI*oB@J5wTLiO~%R1NsnDu^|x`^>l!^*(sG;J7cR>SJRW;84Y zCigF`@wS0YX;?Sdq=xl_O=#E%*tmvGf{kg|9N4IaErX3{*aq0Jh6P^C_@`mzV1pVK z0UOY;da!;CYX2HYmH=zgutBg!4I2Y%(6DK+dJS6utJAPmuv!h< z0;|!m;#T@U4GV!qG^_?JtYM8{l^WIx7SgazunG<91uNIEA+RzH8wV@3U@{iZfE9xk z8jIK9>FL`B)6-Y_lI-+Vg6Zk21=G{l1ZGX2^v`iHNuRYp=mHCBSRYtG!-m0>hE0HN zAD6KwWiSi21t$Bz_W4M=Z9cLN&ze5jOH?m4)90|4cy@Y#KAgRmSc|TUv>h)t_>s7q zz!G4?0%&{JzL#K)y-GH(&)gxFwLR!Qw5t|-Qg*{&8t zo-i5DtYy6dwr#N?b~nMcESQu-@MY$B=CvH^(Y0FQ6kE+;Enr@4^`o1y*pjr4fK6-I zB-oUO&4Ep7*fQ9J1&d;D18f}3%Y3Xdp1oeK1zWY)mb5m3t!P*rY+1v)z?L+u4{TAx zhQSuVtmE|r*t~@&<<-cgPriK`&W{VUNG6e@{)cj?`d>1mi-e+#{$^2 zi*#(kTeN5;9mSVY-j5<34d|@<`_fKZz+`{lshxHarr%%a1GC;=kn{|L$^8X~a>?5_ zEutIK_T<;VMm203Y(&FKFJpeBVU=J*8deK7s9{ZD0~!_w>({U@us#jz1MAhWVX%aT zO@Q@i*eqDLhAn}0Y1legr-sRae;pcD2G*`&VX!t0s{@N`SPZOH!`i@FG^`t}8O-|p zQa@Ns!$!cGG;9(~{Ku-D0~7zTu3MJD#DA>V23Q@Kb&LqS-0r{9w#vc8f33U-SX9I6 z!6F*g3>Ma~cCbnf>j4XC*Z^3ChK+)iYuFT6nTE}Sm1@`uSh0p}f(11!czO2ysR}Tq z@uFbcmVQIZssU_E!&<;LHLL?{1MFpJgcM@)+OYREX zewW6_?yKxRpu+4_HXU2EZybY!s|q!=}K>G;AKMRKr%liZyH#EU00@PWnGE zYdfz1QyLZp+h+W*@*2RlG^_<|Q^PvIHZ&{&wyt4=U~3vS2DYkU(_kwawg9%QVXI(E z8ny+tsA0v|(En*z2y9-%YQW|+tPyNh!&<>+G^`U$#$Rjs^@2@l*bvyHhK++wXxI$c zxP~o)jcM2#*r9zEK8deE5q+zvSgBsQZHlSf~uzn5e0_)STKCoU58wN{g z*aTRQhRuR?YuFN4mxir_b!wQpj{Z->%D~z+EDYABVRc|}4U2)bYFHasi-vWBHEUQu zSWLr4z?w8{60A|f=D->>Y#FRx!#2R`G%V0X|EFQ)U^N;R0gGx_Jy=A;n!&;v)(%#w zVLf0W4I2Qf(6CXkat)gTD+9Bx&*#BPHEac}Si?5Kf*KZlHT|E4Re&iCi-K)4hquPt z0Jf!JEnu4()&VB-f2%eDwyt4=U~6F3cLvA6RxOyUou|Q8z&=AV&9(FCPdN7M+x@o% z`j0tstZ(h{#mBMPZmzS};L3c!4Y%wyl+TmhaqHm@m$~D1z>PlD9d`)s>M%;1LYndMN|HI=7;s(G)-ta4|>xi&=fa#(@dfnJLFBXhNj`L zH%&zk<4dDAO$<%XSp_vWOIe?hEbCr0&CCS_HMX+eK+}3*L5;24DsQ0wZ}FySK{NPbZ<>BI(N=Go88q`R z@ut~C)BZAVn(&SE=a+cXw4xcg)SG4iOp+HTiOQEG1vR#KqBpTVxyGBO4b9ZG z-ZVpKn!5^WY~?nOX5-b~H0oyNYu6Xl*vg;=P58CmH0@|+U*}CTj3(aWO|yVz`wiYS zfg{YnZuF+9MN@NAL5;0`I?ycKTu@^x>k%{^z1}p7Xo9zR(*%2&Pu=QGQ-`MhO{c2+ z^Utepl{P2qP?=YCqFH~ldyTAfN6`%RpQ?W39M6{3{sP>l+Y8#iMKot6(-e3k!W?Pvzy?M*X`rv5$NGz)0f-|I~ixE22zDX6iPTP>P~4;0k&Ng1TZn+`M^A1tUz z$79q#n&A(fs=nsv`+ij<5%7MuaOdIHf7l&&3-0QeJ8oqk^VyHOyFzCckFZSxD#-jKJSjZ40q!T z?zq7>(>|x&ackh#e%T#24tM!0?zsJM6JK@5oq}6=uRHE4+__nI+|su&-hRU!w+?RU zoI7qi+{tgb;|{`Y`Hnm84BYK`cieTjBlo%EmiN=YEx6-0z+L;kJ8mc3{vW#I4#SQ9 z$Q^eM?&AIKxSMdhm)vnfw=v)Ui92o++?fa6al7HRJ>-r%3OD#OciaWI<16mC+i+tK zyW@s$=YGa7+;N-X4*k*{HvzZq*Y3FEa97scahKrs{?;8ga0ly|b$8q--1*-> z{BL*MKDcF%xZ_U3o%*9Y?h4%2O?TYl0VWxLamTHNJNnncT$%H?!EOB8sp?nhIm|^* zIQIPXeX3EJ!W`b=dzJj3S|<#l3H;rgW**JVwl|G>EBlrI^ropnQ_GX>Uh>n9X0ynf zW*E(c-JEM_ok^w zQ};A)nl3b3Pxq!7Lo@kr-ZaZ-I#2edDSbQZmz~};4QSSOdDC>G8LjlD8AlU8#hYdY zO-0z7rtBT;CqL7hrV-8Hv%G0~(8Tt6(@dZ#KJ7I1GQcwfLamVDbBLwr}s|EDMS z9#+w8MZIat@1lR(?@iN$W}(KLCV^)9-@R!j(Tvr4)2yKxe6BZ5#Sr~Loi|MkO~>=S zX?oGL)O*uRp=mhiO|y(u&;-sp zO+7uWx$p_cUYMREC7h=8klulXb~9nw2ZPX`=68y>RtuYS}rS{mK1U->aoC*!(uuKWM_& zc+(7_S-j4hW*$xAHQqGpz07xB>rGRGX6g0bH0@}5Z}6rWMiafsn`Qyc@)2*E!29SA zZtrGSqe(s;V-Bqfk z8qhR;(wn9m&E}`QX~xlveAb(01x?fEy=lrm$a>+6-ZYJ9M!)P$(}O1VRd1RJG~4%j z)2yNy`-V47`6$nye9N1r35}Zfrb(a~zt5Xy5>3nZy=m6a1Qxw%Dn7*i+x^}&F*L0Y zc+>Qv2`+onOre?lsW;6!n)uJXX+n21e_QpYX+|^kD{u3vJ~Yk0@urzZv++A`Y1=?E z{0DEE$`8}OJ>pH%f@bqi-ZcGaM*iYWGlQn-Z{9SUXtw_DO%ooYfBUC5O)HvMQPk61 zc>v9}-vIE`1v%Kp@(Jh_oO(*NlWppc5 zQT3PfTGh5*|FkSo&)DHVA?fSJTD5eX@wmpDrU6a&bG&J~(KI~Qn`RtM>GQm4R?sZ} zmp4t>$LLQEdebzb={n?1(}SkI(VJ!hP4U^@G^=Qq&he%x|2X}9%$ueOP3QUEGzm0y zFY>0DL=(Kon`RBo;>F%H6`!Dge5p503{A%+-ZZ^vYTLYNrqBeg@TOTuv+#PJ(5tvAgKnx5i(XhqQ%1$$a7U-ZZUfVmElx44`ScDXJEo)AkXwko~tB z<;KGjQf{+owtBs3w$P0BMb)ZvJSkte%UVB?PqBXJ_oj)XsTlC48AP*mr+bY&`#Oha z@UEyjHNYRne9k^5`|g#JYuF*&O}InvF3gR<4SkyR!uz7?sX6TWuk)FP;R-(nzyE{o z_)>QX_&pzvswY_OXSd03rs_e`KZ&O0o~Y`#YAkKin%5P$)1Pq1EuJh=p-Fe#TDZMa z?znAm*QN_|rThlqHhnd!%5&t+U!5s$DZd%`bzhIF-&y&Y^82_{&gnb+&oNZV_Ogj) z>swLvcV|t?_YWHyo~@R6!k=M&eV;c?E1H2HM%7+<;Xmb*nY5+ixzHNV0Gj?KZ<<*& zeGj?Uh!1U{>3uk=zHBW62Op9;sr)R@)BHNB2Cez9aQ&D0ZEc_#e&4!#elPs3N22PM zT=tJ->`Prt!f*L&RGlv0cF!)?FGykzGp{vN)Vbt$4NccS=p(HfOCEebG_xT2E&Ckz zYXVg{xqg2pJz~27{?g;C)UgF@OWHf(PM21x7jkW;{Ve%*L{mxhMVucH zN}-LSSwoXLH$v=8fvtkM?Z3s&I+~HPj2*$$6#bh8ll+u{4S}^wxKX~TGa2n?95xeN z4|f^v!@@Prd_Fh*y~2i)dhUajg!tkERyg_jj2%tZKM(DAF@GLTg*M3CL4L~k9l~zc zQ>u(>1LU7ZekZ_tz>iJNM~qLNhxpF;!IJ2KlG=un_>trKkG<`f+XHv_Z}Iu>FDj`$ zP!cu5VU?3YDd3X0e5Y_5TlFVYsWyp#6dv()Nm!|zC|wU!@p;-W;jflw=}@9SAi3#0 z61d%ehtK!Pk`M)B1OyM2be>`P^FT=_=OH7DV`IL&igT>_!zcv=ne-kmnef>X91A)p zc!K;&-ArJwV`r5LOa6`ANScoLZ}<71;7gSxh}7*e+|FlIsdsTLKT@t6VB28E_e)97 zVe(oB9{d9PRp4LAH5^kNRYrV6ZxtN|*g+0VVWN-ZS&XR{p~E*UA3%Du(sYfn|K(YCnr#yp7M zR&=pTs?^2Vb-a=Bsn2Is=tfbGz2eU(*$|u3o+hx_dRdkFZF1gf`c1n1A1;|W#@3$t z{LbxZhP;S9^(ETF8OcDB6(CG}Qw zzHujOXOz@8IN?VBhy7{nUqXBi_N8;O_J3bE*2xgPQwaGswpMzo)Q^*Xe#GZ{pV0yO zpIsz}>dQIap)WH&+)$;?5Kco$%zwZLGWN6T2wS+ZN`0E3jE~_!w6|23fkpnzc$nm& z3tda1O4W)@vtPyK>^gtB<`##f4ckl^X_&!Q(@pdv`OV2f z`w8hj(ORB;_Pj{nJBzLx-A!C4%agn`PzmNZb#i$d#4%?1VyQwj@(-`{mJH7^|7Y9ni*6QOExPaL zOV?NJx=MU+8{G=JbEMAFZ8hbG<_yC3Qj5;$-l8wCBRFPP7Lv`$My`8Qs%$$~X4yF_ z3-O(i1!Wu35GhS#%&Ghu^Ut?bsb>jbn{&J@Rek37Ki~}vR;fL zK3bLj9*ayaeZE#HlwgSP5yI>4sZxDhOCM#-x_rJH3<8kMIcDL^J;*qoz13t@O zd~fzT-~bHPwb-fnChO&mD#em1HLe;{V{=T8Wz6^<$r{oRl*Ej*#IaHF$0~J_%vX&> zCf9$A>BizOvnFv&i>&)3Eo0c(+^kaHE0h-haZXu^Ngb_YBk>pZ43hg4nxE)%$1mra zJ4&BfJIC|G|EQABN2T&beX!y;tHgh9k!8{WMaJn4G_ikHsjFUO*RawtmzNJ+U=dLd z*bYG*Mc3E7U+thQ#Q$g@SDW+HcNvpW;mk>TUa>!?k4igPhg1K`{r{47BIVokE#5!G z{s?|3=`2vbFUzsi@09Uu2wP>>?N|THb;fT}a}#qp?t9oWsMFcwQpUP#5$zJ%^Q64e zK8N-&>!{Sk-0!sRvQ9~4zs6j5)nO+5ZT6d9y%Xb zCUf6n4E^Zq_N#NSrF`{9zau_11HKGi65tOrulOOoq(A9Xzw_CAYJ_$u>DfXzdBcA7 zb6dMK%4jB#E~B%3dVXezN5-()@36kUdA~YQHd3C7GlTesO%9wZYlZb6MJ{; zS0C2$v69NiF;$%j4vpUlp`=(f=H-nC!d?IAwNcXXch^1Df= z^ZJu6ZHF!2|IE>eOt9G}^^m|`>j(F%uVm71u0`mmGxcyVs~!fD^)Q2e`R@H{L-OHR z4*^?x-X3rsc}CDnxs-mF{gr$6tCvf^hCQRn_0C|5ejjdfxEwjAk@d`UWnu-EX({p#d&Iu?rP_Xd)YFBGvbG1IY7beSa`3+N)B z+OIw-9~W>ckHiVKbUak-Tpo!N%yfkAqkg`DjDCUco- z%dOe-jYB1I7Wh;aP1zU(W%FNVcjj8H3)_wVvtK<&eA_xcVLRRRJ}qk~iB>?LK-={9 z{pOki|85t+|a`IaQJL5gphW!Ok2cNSom?mDfZ|Ti4SjF6v^+^;FZ{;GSF2x%uN6$AZ_Bw<;>LdE0=CwRt5xc|gK?u4 z%{9t8Vd@Fh>Q9+;rN)}fyvX-WyK_jNZ{1J9lUvc({*e9&TOSi!*=g2%;zdqLKE&;@ zYn=j&{tx?sl4^CKl(n^u94<-tQ;zIEKC6)=;ES&tM zwYrDvOuo&2C)4TRLbkpo`@V(PU&hu3wxoT_kMtiKVB26<3t+VK*U3D$w1XZ*>b3Mo z^e?AX|4Y^gV!spn9nosFl#E}i7tL>cA9wPPTx<1uY%XBGqPkkOO5e&}jqh@40HO0G zleT}26;u*2uld*PL(A@GyoWICQ}+O`vb6hq zxz`qkmOxYY>}qwpXo!m;Ikh)@U1pmlYV0wL!;PO_{g{kzTi9AXRBfJvFR3&7N^2t4`X6%%oS*@-} z+BxEDF?!+al6|qX&2Bhta83~pKZ5mxB`lb<*AcLev#V8=kc{?viDBg&oK-Y45+18o z&ya9_#HSX)`oJy}!1O68e|DW;lX0n#xvo?X@I38#)#^;)vgdgPcEv_Hcmw#)1ebQi z<|RgYjDOA6WV9DaLj&9`xMjlSrsX(T;8w-JJ7y#C==)a}cZy9K7oQG_%c2VXvX3TAi269@~7DeDMI7 zvpw1C6WUjgK;`(Ye z!L>ts(8u3*In|D|(b|WYU){(&!j?~?Gi2eD^&HaKx#rbks~20rBS+ufCC>}kYQk1I z*YYFwSHW7qdhscTJj=?++Ek9s;gYcs^5{>QpWRxmmbn&N>;)KoVriqphSS(r zt!i?`H(;N8{yazF3{bX}*c-)O?VGFB`N=qC(L$`u73h-VFEB17pY!lK;oU5}wEuYT zH$%wFf5!YCTX!Y%z`W#$JWzJ0&xp?GGwR?(Z>v_i+!AdQ z+6A41H#mvjeyj=zF?&9lT) zkG2Qxlev~3!J5JPG^`zL5G>U$h1UZ%V!#fzJ*l*G1rR zv&QEeHJnkx=Lm0pXSI59QYVvWpYMDlT=!)!NkRDQNv#WT+ontL(E8YMYT7|Pn-mEtkx zp;PF~zEZ75ljCoyf9AMdL+#|^0~ zv#f7Qt=G(bkSN?9xE~O%wVWm0Y>FnI8hV3eZf*=yl5mNu6a6~+SBXB=*PG8mHKiN_ zQ-*I0!wo%9t)3=a;*xrv0E>WiCNXpWAT>uy#(u74l0@u0q10$cOrP1tRt#IO$!F^V zyVA*)^e>IS;(k&pt!I~%zJNwq4;}~a2Y<5E6ZuW=caTZu)s}nlzQ1KntC2{Zjbfwy zCz&=Pf<=;Hw zga;bQ3z}=9iZ#~b=)UbP{!co^FZpQ0PVk|lvm<@RICciGbAs%jTg#EvV`fkETh>m) zSR%~$&9=LazVjE=>b)`$*!2?`{TEKO*TjL63G{79BEMmN^S|`y$v)@*QFs1vm6g^0 zf94z<(L+Q7GjrU&YyX}-*L#imB#~M2f9(H0ZF6OIAam;U z&6PQW%-J(GS7!8AjK3qBE3+M$$DgpdGN+Nb^2E)RS-ga@J$dtFN*iuNW?o@j9bQ*I zgZGh)!ryq-EE$8 ziB|;ErlZk z^&mNAzhS)cPp6h1F}>}`NfgHe*Kqp@PZ9oANw3p}Im>IzU0!97&cL?--#apVnSSm% zt8bH9G3FK4pilpE+&&}YsKa}$I+XG&`7QMa-KmtHq^}h@ZO@FWcHYf=Wzu)KJAL9e z3_?E%z2j^{hMuWxT{NO)j{T2PzH{)EKFhy+WxTsWc!F@vWMN}D?X{n zjssc60>VWoS>PJhcA9*YIrmNweQR zL7V@zpZ-TlYuTgtAfMxkyZGj_$jZQNxI^{W54{_o0G0iWKFyzjvo_{N7s4I&|`jeBRfkZT{Fmv zZi}m@Z%9_SdcCqQ`aS(=xlbFBZ4Bmd$k5&1Hd4ckODPRfYnXHaI$h1k8cD>}!_w!S z*pB~Zy*!R0D_Ip+cWx|?9}CK34M|x;R%dlw-7f9Iwgbj|shC=Ok5A~wSwl0~-z;Tb zvqJst#7A(A;~n$hZXMklu+2d0XfLv6YfoKAC2#Y{s^1@1|9PUk$#IPs<3?s5>{=sp zlDFbNke*arJ+iT~&JAdPx-GXLtGzC+-nKDW*Q_UN3|ZqB(9dm5))#|xK=P>mh`qiz zu0FakS>IkyRvoh1ULIG^4zgFSwjBQJda?$PRexFhl=m&g$l@Zh1}=}QJ46@sa)G_a z;5}A9FUMl^Gz=tCE=7N0zVV8>h%i)^?C&v9oL*cK%S)(_{v)8IAS!=GyoEbdR zlQ|087~{_(WAK)^`W|Up-==*ztJycgQt)T&>#gyB$Xt;4B5lZ@K>n%B>4=`gtq zEpLo(_&i|}o*sDG;dz2LKG&HIt{QN;7sjR^X@}GB*LKF$+J=0L@SQ>JP}XS**BCF} z8Q-+Cjm^kw=#HP%*~S5QhT++)vyI1*H;23q^0ygh8xwzJzrmZ$F_z{p4&RckVG;Ct-iS)b^yC~zfPL<^GNN zd&2*kV}xgokz9Tp4xvwX#~mo)?*%B9*q3qs+K8w49r6F~&T2}#DEm9}G4G74>nKwn zKc4DvpM42hWb5SYOA{gvA+Pg&aaG|j&wCz^`(1nreP4dt?S43SJQNVe0&z5cB(B6g zmq%>P3RnZ!i~xpCF=ol^&u(-qFS2kLy2SBY#`tj+Qpe%H(Zz4BmvQ}%7KwI62^ZVc z41MXacbtLG?HEgi>Cru=t%j4|E=b}YMONcS{|c)W`NH3;ZsW*nvpj?;y)iFZLgc~ zX~GX~%m;s}Z*(j`1vb7WW4&OBct$^&(FN(NR4Am5fMuV3w@*d0Fl680q~~nn@QlNA zQ21^AG3tO#yLR_lmmddLfh)H1YhJA)s7<8;ryC3S{h!`sWb)qQ>e`ar9+hj!vA znvJXdQYP8)c-QJFC7*T=mE?I6S;gPXHuWqiM~^PXq7Q+GI-|Mtx4($1YeZ)ZTW{>sag9sv&+5Fh?(lM~C$}TBCz0L`A1 zWEn%ZyD{YCu551k<(?T*C4bz!@)Lb*L}qMtbMa=2KcVeRp)DGPrs z{3CyhKd!qG3gPdDf1I6-=kcx_cN+|XFN5Eg18(RGz7xZY9K+WN-{Za$K+YgYpECzv z$7$PCI-_TrkE`*Fv(GPa^Gz~15Wc*;km}3braqEs>qdMblaE=>C>@wi3SNNp13Lfk zgkn8E-fIMK9c@QbGAF&T+>JowUyh_+l^Kras1a%EynQ zeY)@Ndnn*M?J{F)$wwZOFMSo;)LSGSwqM3LMc*oDojGrD?l01lyjJ&v>sVKn5<2jwN@-VI+%-O2GudMDspxL}*HK4IGvon9uutUEjZC+r?Y;f6BzCFQp! z`Mr3Xx`2Af zpNPM3%{KK3-#Hg9gv{9Kub<{)r-w{^YD7+7?>6=J?DF8Gw^bfDg}n76=ewmm2H{`6 zXPf$XhW~`^QOaWuzLvLdQ}5YedHi{u@{n|t6ol032Pu!B_W2c$UPD&~Un6`q_ia=A zOuK;T&@D+Wt9?;8| zNjlL3t-zshamd^N+;>{|mdtyTjY^s2J}IQ;?%$lYAo3cJm-D@C>fxX|K&hR3sDsX5 zTRJ`>>3d|G`hnj}g+te3=%D{7f zLSH}`SnX>~(zCivy)%;@qkUQSdeEcS&`I6KpTc3J&ATM^)Lb7==SX=b%&4et-x1WwOu_us6D^i?H^cY8_No5U;DSKFHj%8 z`CfLx4u4tfsk6IG*FS&$_=J zBHt4K8ggb|esbj}@yGe#cA{y!y4_n2I{wKsq<=8uZ`kS+|0MHEVjmA7r|sJ9>JG^V z{heVSbLHSy_%%-Z$o#Oxdj$H)8@8)=l76#)q<^KK)A7za`=wS-zV({dEd7$Mf8-Rk zZ9geLMeK9gS@dVOZdWzZFWTj-ZNaFNuE%IqA1Z7@?d_U&2MlsI-g|PM3T5t@n^|O_acpOTBc%KR-si znex^3Vvko|;I0?(7o=V$;9LIB?dl5A3)+vpW~JN1@@Wn~?J7S%Z5cTdf6mjX-`SJX z3(0p9zUU*{A6I`S@g9PI@-N%fCo<{R`CiU7{oSv+$35~50m=86q&HNq3Pq2y^6gp^ z{I#b&XgeTlf+A-PIR&SetG`OQ+wIx1CwQdm_o&@ow}yx9_PRB6v(1%<%a;k#(%9+^-GM zp;=?3Y@g8T%O>HUxS(8p#A7?SB*~cT@bTe9CsyGryrlf3Y@O6a?4Ls_?}l>qO>bXh z)kSBX%l8`z@PRt>%={y7@aA&AG5qp+W1eMnLF#f6{<_=B)o!!x(TU_zm)otn9Lfz} zV$~(@@6Eg$wog5i`Q6u-tGtXY(QRWiV2){&cyU`*0=}uva{InJ!Ro-$Zz@;M;awil zl_s!OFvr)MUTT18g8yWHOU@9aAq zvQGGY3`tk*vuN*!%k}<-b<*{ojijp&S*>?%P9BdTYqWQBWR*slU%#hZy?@=Z=TaeE zyY>`ejGH1|P$B&7$Qr-5+&HTqI#1u_DtIrr>dVH5!Bal?1bCkhJ_kPUgD-(c-<~Z$ z=h-q}l8qOGkNV(o@KqnY4!q_a*>ak}dwlRi;Ilq>KX}nQv*jECZ}P#Xz(;)WW8ljk zT;}Chz!UxDo3^G?hRoD+SkHZTx$&(bqiteG3>zHk%FG!@p_R4Q4(O+#7h5cS@+*y{ zR~^<**m0k*Vn{~`%X?3`y>XS#^ zb8UYJ@IOaQf41;nNZ+T+Rc@d>_c*OMNFIHvq^}uy17qcza_^_e8$({l7dKB{&hwZ* zpDb7PLHX@G(^Vf=2IRN%Oe4Sb$eW+tob*M>?=bSlA1+__+|@KRbKfmj_ispd!|xB6 z@@|qgTKIg*;|JxNa_*`LS%-dHzA5Lfjvz}tQobo?5>}Bl_p|a-K3^f_Ra;7b^~-W~ z(0_d5-N#tqI~-uYqR8q)R`IXP)fYEbj|~B`bUhwJR^RgGgzrb^zWqPe9)e{RaJw{(yXF9qLBb zYUPH@WIRBYE|Xbg9jQ)i)Zh5IfAKcyxuxP5`)hV3jPZg!rnAfB)s7-zH!>f_h}xTj zbUSN=e+d3T|F-QNPy8t;En@dZkhQWqq24I6jI?0jSbK7u$Kq59OJPwxoM2GFz|c6O zya;ciHlfbmSi4$XuY6-K!9PhQ)Vns8r{$nL>G2=3+72XCTP9B=Uzh(P~ zCz^Ay8@L}|<|P}DwSp{t&pP#mX2@`prA_$UgjHu|gAC)E2lCaXC}Rx?RyMOML`iGTR~${g`bcPG?e z%r$|m`ISkRK88OPFrJ^J{-r)kwljaxmr$XMoic1M`k2wDEaR*7N$g59{4H-!1m9!i z{uV{(D}L<*Invffk+p`b$9PxV?C8mxm_Nt1Dn3Zz<38vwePcXDo+`&4yd$AL5Y#vH zxqafx1J>61C>zOZ8hLB)PN>F>>C4$c`XaIhkyY?s`iCG{ZvUn>Kvt8~;c;Ze-=9#g z6j>gdV7KWo9>3QpKrj5IiI8d@@@-xI3PC1;oH@#SMSmTk$(hu*R#{ILqgD@U*`vgg=c zjx3p3>y__v?tw7|Jm{CyX&d~-YFF;DOf0}R z*FNnqvU+AV?9=|tugzO`1T7;g?;9IV*VETaSF#HK_}ht~Z_#_}^1guf*;YfD_8@ES z;l$&?(RRrUl01G9X49hvVy$TWpv-`-#Eq+ z;SYt}%5SD6wuAZH-zU_uAiega+PO5K);~{rj%|oI%Ko0oli1)fuu`z%Oq%s%7sg88!F;zKJ%yIt zb6^C*D|)>Gf3B)9{Nod+yDO;OimaNQoJS2+826BzSk~Kp%K&>WdftnybZ&*}_17)$ z{=m?6{^cgy19d)+A+Im5Lj7=K^~0VYU)|DuO=%6|v4RTKk8H1=g!oE9Q2UKS-VARK zyxXL{C?mh`NB2N8{G^JF&Z7n1nDcPz!ItF7)lJ3iVc(e_;6;xVxFN zA4j+NjIKLP+u4Ae_NP~LX2}UrWe| zJ*Pr_hILNg_Uf@+pE`lqg2Okbdgp3bF zH`|bxS6N}~opbtZ&c5d*p1#MtkM0P(v1En%)W*vDJ?oYCDzf@_X3AUgnpaDG3Ko=C zkA9q}Z)-$edS8Wl#m4%!(g54oB)TwytmagO`lR^XC$_UUL;`GC4eTq(n!dO~J?PVa z$fTs*#6u7HwD2Z9kCfPpKD{hcer3q5151N_miLT}wC79tT1V8LAl37K&@VvWWYRly zQokB=nC|@_(pJXc%YAt!ZsDH>%K>ZNm~OQB>c%>KvE;98AO7H_73#lv&*aba;Xdx@ z0|=IMHbOrHy)<8Wr2lOL8wA@g0Q#TFv;Db0*L@?>W`^Mzhvy-ahcSP~cXZyH%SYNW z9g&fS1$gT&%cMc%t$?M#9u>U|YJ+EFw?Qd`*nZmYRw9jVtJNnT{f_?CJ{R+jeivFOYJc0_y;?z9AymJXp5}TLS9?JBx66 zgifWX4-ZxdHsZlb!NxpT5^NHzP~`B4yfoO1hpriH9?VLg)IkT>BAD-8zK$9EXotH@ zC9W}eQ`c0u&PzwZXTUqb*Yh)$p&4wh*y!3{m2W#S*Zz_R@VBq4*yvizy5rIwWUaia z;*{^fm9m{fR>O@IimSE)^*c1`9^P1MB?SkWpLq3#{jk6>pZG0}$ZEQUwl4jN-Oemu zYV^#Ep$+@`Eu-uwlKLJ-PRr{m^qB>9eSX2E?4N4_p8y{Mukj!6dVH*AzaBxZEE@@^ z25R9E|0@I1+O!0Ym#2D@A}&&Px*zz0sXTcAEfH& z4-Z$UYVYNn{Ie>@5-c2@;U&P12(swjlSPwqv6x~5AVoZD%96A zY022=PDj~rC6QK3@%*5baw<5F_58PH(js+O0+s+<3o0k~7(HlCw@LJ=4OvTfSEze= zclUpx*ITZqq$Wo2AhhXwDpWMnjtLS+(O4d2c%E@6Xuwf;W+naiu5SZZplRsa9J^I> zKJDXO*zpau!SK03_diR$^&)HbeHF&H1-*UisH3jjQxUq}rr~WL%+#Csv&X>Nz-EH{ zSjaKKp&@!h39$}V6(^+)LM{Of}cle?Oq4HjMpv0s{Q}R?LZG!6N*sfVrz7 zGYzB2Tl6-Z2;c6}3DN0Acv?Q2DI3x0Rj_8TABsWnOP71@=56cMTWy;8wDF8i zyX;bPaJ@-7I^mu8Vuf)hj$b<5ZR?`}HmFJJdj?sfU!rV+Y^`Vf^5Ottos_M}E4~nY z{7Qv-T97=C{!9lLZ)bdQN;CSvlW2^|>9XgPe<4-}?^Z zw4i(M+`90ZfFZ8#2hzwo@+15hsf15Yd1$RcJ>g2{nJs@^JLJ$i_@wURFJt_XQ|a=hQed@U-wU!8E;|9qAD zYkq5`8X_)l8{#~bofYp|iKJ~7-sI_(>S@BuBiN#(7wq{0aR0qvt6D;Q#(kQ%?z`vVUxEMIL4BIX-n=cK?-pM`(o=dV`F&BPdbQ}MogR1F2=Dcb zM~o#W-9NR!KX!Jdz0a9NxDV>1Yhk`vihUJ-qZgXCb1Kynf_yShnrGIrky5Y6kQa-a z?aub6Gxa*=Q?CV=VL!HI>a__uC144#2ZP2xZe8p3Ey{KJH7Va7;uuamzWNs1H4T4# zMWyrJU}KMxOz2wMI_~n0Kbv&9=P*G%4e!7B=Z`z9Tyu2cgT5Ku1)0&e)FW>sS*f0p zX-`I49An|E`>x4Dl)pZBdv~mFE5}5B_2$?&)yRC^?#;1Db;zpOS9!|cIjW%?29PzE zs(jr0A*3HThKz!Pr*0#qk1V~M{_^}vb+=ik%v;O+h+Nfk=N8L}f=>F@dT6IFaJQir zuqm*AWE+|xj@ZSIuMJ7M^R8e$0)A_bs0l0zb`0!88?))#d`;3iZD0J;ZsI7ttkRy> z%B%^57(HsoQ)Xzg{L=~eI^nxK$X>ebQeHr_Z4>*GdnNw-m6hD9!VlZNd;6O(`+>6k zNh!OO@L!cFJF(}DV0~bLb0Z#mepkT2O8OwNaYvB1a9yQ}tYcSLTW88GYR?A3rtQjc zc*k0T?TXCT<~1=sdR3+Ryf9_^`|R;|PF47CW7hhQoYeJ|YKO?-5v&EQ9qb&97-xtM zf%SnsBD&=%^Zoe?&b9V$`K`4tP*;-fDdZ$>#@?EBr`u+YZkwOB+a{@ZC{Rtv`2!hJ zUwKzyFK)$$6^AxE|5;;{Z#~W53zhnkJU1e5`K^`qw*>@i16y)nVyC*nj)T2=V{Pmi zzHN+gmb9f=WVQZVrMi^&Oqsgz8xMOBy+? z@2gaQlKB1Pq+L4nO+PuZ79nXGL{@aLQvE<=W$DMV^NiV;&qD38G!v=UMdXYl=MDj~ z)1+^*cFw^a2rv{PP|_8@hWdIx^~Jl7Ja0R>)Gv=xR)>&N@PSHorhmG;dzlygMAgA!-m1@F2J>IiKzTaqCZAnq;y-3K zC!Fw}8|gC^khS!g%9B1XD!j$lVxPwW_aF-d=iCB&MAZkFQ7Txq=<>k8e-OMI#Fq`r?e4o^2coArIH z+!oq9@;anUTyp$p$o{+f+;De5x74KVKe9^huT<6kbI*)nx4P{A zgT9l`jA8LXL|)PL*xTvNlh=a0oNsKNym92Md}s6I<=%k49@{*54al4O>E_8BLEhB= zZJxYUN3F<*N0 zp>OE5lNxw>;khR}UG|1XnFbbLxE0!EXuD*dF>5^I+FQ41fUKC^u0Bt5rQscKhxjJX`0B-@7$vjxDvd?a1PH&Z^ZsZ#$Dl&(?tIogVV zbd8$nYDM1Y>#9^^4#KnRU*BVGoLve3OV}@6@QEIcA!qvbD)kg!IkV1rlSaS%&6??1 zL(b5fs?>*k%Zbco%IR!BIaa=EZ(;w|o2&S)oo~LJa%%nLSbE)yoSL30wTtv+mydJ2 zALdIm+4W|P&*zYndTW*XrEhv%`8v-pJ(4fc>*8B!uXk4&cYN6OrS+OGKw4)#|D2`Q zeZ(vJ(+Gd(+c!^tj!1g?tJIfHoG(-{yL=>HlCBlxv}zGd{_cUgKMb172Z&G2`8s!B}@zn^`ZbeA_@N=ndYV6g113;JH_Ni$z#$XgvH zJ^apYCmYL`q-za1O`ok&-wH|>HxN7LjJVL>-%)H>Nbv@Civez=7bAOc@lKk2B!0K0A zcEwkpr2UDUcH~S?RXM-wqbr(8S^GQpW~2m6{>1-Q zsUBZB#`iSL^t{opoyhkzqSK6&g_rzU484F3FA!v{}aD@x9nT( z>+#=yx;g%v=utoNQa^uu_Ez}k;2-~Gm3r^Gdib0`JuG|!`B^%(@|8KTM&v|)OTF_h z_x@hQMvr1%+reAFs|2S%Fh0ee(zC}hpV?1%58>BHICds8Z+uaHX@_(SiQV}%yYf@J z^V0{d!4IUezBCK(1ia4`ULL^~!REnUpb-OG1zQ2T#DV1KiN z?hy;ZPYCH^yGj3xcq#WeXvhD^IE!}`dX0U*_!4;RPgTY@hq;GJ%0~WjZl}D#^*p|b z6@iU`ZR58*QU1!n7Qh}AfPAor=gm6a(5rM_%Yf6+Osq2I6&jDX2xZmC`b8N8; zXU!_R!Z8Ha#YyD+rwZRldB9tJqB>{j?ea3U5aZ6Z24r-vRjJFoGIk3~!n0lzes0j* zrpyNoBWvKVRqEizWJTALwT!I6zg4NSjmZkFCo6de{oUWI)Y%)86%LXmZN3Lt&Ge%E zyl2{oyS|!z>Pz%}3f^gW^*!RD!?wO315a;B8fWZ6Z_#)S^ef==;9uc=ef>(i>({(f zPxLl{^Uxehs_R4sk6?9RlVI0tgf)Nu-vm}1PO81q$ApeJE^MOlGb^nZaSaO+`h9-x z(FbN8`o>KfJ{&SkOS?M_=-5$c9$=~(UW-W2`^*rR+L}~HC4QIP487Yjlg`7XG)&Ex z^Ct8QzWYo*d%noIcg!EVa8RXt_m90&Ysz+n(6evwy?yxb=y)sdif;o0|cXF$V{eH2B=8%ADBLDI0hB%WKn zDBqYa(@DbWo|H89iR!#b9dubGZ+*D1M)Y$D+JYx1)th*iN89Kw=I_AvYyG^z1E#^|!1Q`6>vw#EK+`UO=be>QPtlYH{|Zh=D&rYf0cr;?k+)I;bHoR+N5g8j{7_-Zqd0UbOn2pMmeG%=bJiL51tFo zQlfrp__qZt2W(Lg!!NSOyl$W7GPk`q`y7ey2r@>IA?2g$b$6!Yo&ui+f3Dy>Qm^x1 ziymwVOzq30OLS7*#d;93gS2$lwWzVr_~VvF0r zT2o0qURKo1maD~<^=-M3vXgxE5#CGq`-H*h!}O73+4*p{Rrmxo@izgjI*?R%^PXL% zHJRbtCHX1#yQO#KH|)-D+MVCBJHKsre#dUk-0aTp;jeGY!K^|@WEp?a9_||;jz^>| zSm~kXm5r~Tb!>K+BwX4~8v3P!N%b+4-rdA|^}*Gf94%Z|Z(F!=iyKpikTr;`bX`(? z+$#&kx29|OTzaB8KlYmZM93Z!?at08`D@8f+?Zd(ORYO9gx%ssh+6AV@muik&U5Qk z3~Uh0(rwYpTCf2yJ-$MR#P&9UH-e9X|D)(fbfJQtbS?SyX9 zJ%`y6^vxDjR?^*pyp@KevA>MpQjWb~ZI`ad)*G2S8U1_?txnnYNEwuMo`juEEM`~tS>pwK<~ zf9Dx=QP@(@%{5XFW*S3((_v|Y!mz8Y&@5b@G`^vQENT8YD?O&OBF98^oc+)>T#-~C z;34d;+Q7E+ZrU2ENHp{&OB=OK8iTF}x+h41 zSvJX3<=TV$cnqAbnEi4bTlk_bG|j)*EX$(zhjaqgkGmy3qGPUvfv%xU)jjYkjOSsfM9zj@eX`rs1LE!qHQ4$jY3n1rk#N0lknl#+fJ!?)LRcGN))+;~VG`^RSiadV zY=1-h2ATDNOJh+$I_Z)biB-hAZ9Bf3!s zHhNdmm^0LQbGDmW9k1l09lC~|r0#o!SFj$iW)C(9HuTn{`ez~Hk@6b_I|62vPglrj zZz5gf%o099_!+{$BRq>>rM*dGf2>Xyr|ESMXViJlJ819kGRG){8~qLAGt;@3Y~AsW3Y+)?gUIRpFmp$| zE2b-&i9zr&i67kQZ%N)~z-PgQMjpWyz}7yJRB~;cJc6x&t%8ZZ$s>8neJA#NIBDF= zVwBNg%jijXDdFve`;~i&@D$)oN|n>L88-I zj?~i#G{ewndrut3Hx`Zk(^G^^6IKJA*}s{#yUnuk{I*_=`d)=+?qUD>l=?1u7vslu z!V`qof6weA@h7j4v_>z}|3=|&AiVI0Np%tLM%mn^TZysCW2E5_G-+tkCQX~BI9LB0 z0~;i~lW={OhB#g;AyG!z{7ZaHWggJ~cIbbNxw2V!CVrA>8(sLsq6eY_ON7l5Rxj}( zPwW4?vGB-x7=^KjPgFgW8IPX#}hE2TAwuxMsU|GjZ zr*7u09^Rrw_xxH5SRBmCo0Q8Tuo@3tA6O%pbG~jEti^+kgZ2N+ZNp~3hQR!6l<4#l z;iCcJlIOeu=I;qV!MT&$+&0Y6yA-@*zhEvLIZ|HMz(?#!GhrRSWR6Y33_X&Wb#Xs! zr%rUS7rLR}Ce<^A4jC7ix+q~Igbfq+Pi9!1BW#MWkpR90!j2H8`%q-+)ONVxRkJ* zzpw8TNV`cxR}7t>4T=)pMtB+FK|X_&Yd4;)BgV6ASOy z6d$bU{n(qQ>@e=lM|We$F9TZu)8*r;TgO&!$62vBJWcSd!K2UPQl@r$c8pI#=Q`Rm z_C?BI0KSsK9mai%{7&$91gzj`JDg`1F#~d@?r;T?w8 zxjrQIsy;ybDB7{EuP5m#g>D!+t4^e@lVGDy-{D)=BD<0BNy5+P-Q5;4*K9`#>V|G4 zx+Cyz6p3pDnw&EKxTJ2T2`?o4wk`ZHY%XP^k8B(BD%Wmt%&QpVwKaH}&fc-^oR;Lb z#^Tb(rr^yze}^##&&mM|icM@LKutU$iNZsL+Z***w3g#>7Onlz&)ec| zIdbclNwukmje(v}7YUm@HYGYan!u%=djJ3LKTGhx=e zm(U*r>%4J?ukXJ~So=-cVbb0UKZ-y4>J#TH30*;(J6~zA;+y^RC249S{4n7!2%CQG4r3h?RE&Sb2Txdwp@NlO~q*4OV)!@OtgLFit) zQ2>|n*8$xMbj?C%_)>UTdQ9j#&nL9Q&?ernG1^&Z4{eZk4cf^K(w2T4|Kre!(;)p( zJ+vukKV#<2?em%AKhJ(LX^*||O~UtK;iH@lK4bhQ<12HU-0hC>x2|B+N0;dg{IT12 zobW87_zbJi_Cwnu3)#-Nto@npyOX{ga5a(sMpNww{z2Ccdu}6v$Of<}u*WlR(+}@J z_sRPLVmD^toq<<;UU>vt1e*ujBLLfhMc-G!3g5iL*#Auat@|n^tnlBNpC_zN=pA9w zU&jdRAxw_}>hfbZYG>-8o?{c$1Ii{7_?DSrWR!F?x_V63}-;e-7`WBR5h` zwY;XmC&qTDn@!wq!&bX;?HyzB%jZOu$R9vX{TDN7J!ICyD0nZpHAf(2Fa`|ZijlJ$Rtj- z^5{91g3plO@9Z%4uLSvE^uQPgtunG9K^BEYW*xlghut!zJvD=;z;~E&b>WDMcO`L3 zd+8>;g>b9A2+sgmGg#W>G0Mc4f0nq$3GXM|xdtluEcg*{%f?xD<2d*txYKT|fvNAh z?MA^U_7*H-ms!&yt&+|%!eWG3=}dstfR%y$jCZqctT9ApzIw=8Ud9=bL+~!ZTOXub zZd>^YzqTav7NV!q$Xb~{by-nl75)eP)%SN8=O%1@??L{U zf7+Y$cO|-!)$jvzUe8Cr+dcX%ZDJgr6?neLyI-4d-c=!OA`00OJe|jO7 zBRVWJ1^1!<&o+j4x*BI*Z(tca-p9!g~l0k|FkV8k!Yoym~A07YQr+vA=zh_LcKl%AfG0 ze_c|1&Ta18Nt#0I4(8Wf<6o&Y(oZxXvv$Fpi>9oz`~bIpusM|)(A|h>SIE1xxgq5A zBFAcTf{lUoc(7@(ZVz@0tkZ)ngB|iY2v~^+n*fV?uvxHT54H$aXsubI7;MOc#lQv~nB=7vYyeE} zF(WVR%F%gg1n&cXDm3y)dfH6>=V`>iy1}G;oO?b7z@)q_tP%PnU{ZhI72fPV#oJGP z%CAq^mtRxshf6;4zKH#NB-?i^0q+5K`i=>(VKC`?<&l224r~<6&yO!7yp`~#pKff7 zExzR-^s%40)}2g0rC?!Sf>Y5|3!T|Fe!&ZuyL>hyvtpf)>*nT@MUmo zj#=_t1GWU#BDB;A0~fYKvTcq2Tk5Qd@Z`_`F?AM0{v`4zkWbcJeqfU^ZxMcG$mKjX z$(+^-JgHyqPr||6wUpE=^z!VC`Va->34fNnYuv zlN?!M8?%pNgnX%|3F1({+hLpsBOlk&?n=Suz;pjZ{&<)C8Rv3&(f&KvfSWIpcJ(Fv z4QMSp5(O#*YXGz6a5VoLEG{GN1T9OBIz$DZz5|H{1|xBmSvns5m^#GOZXb$dY?UfT_N`6@_#FQnegJ(;P8U~ zMqdfH_LLaD_$4~?CCHBvzO=SOz1)nSvqeH8I}nVsft0bNqXD{{zxj_}B~KlM#|gLm zbz_u?e8Tz(Yar|`5{F|v6f@`In7Sic9YEG%#`q7OF?h}i(l?ewoH~7$??i>6&xQYk zJ^y<~pQS!Z!TQ1SB~6qk`_HuQ*MRqeTlyn9RS(wVz=XdAtlNjb6Z{akWmAN|AFRW{ zFW3lJy9b*9Yx7{UV66@;4(}pZ3s@iTl6J>j7h`mDRpAcD7@ay4{VJZM|JqWmzAt=6 zJutR6)+1!jFtg?BXx5f5(UnH{W1(vIz5!#6$KE#}>Fb7WAY83}ZpNc^$~>Q9-#)At zF!|WCPQTI4C*VuxRIA@O`8thrEkCs>nWrZFIJAYQRoiEV?C}^1w1lq_K1%pau5e?ItArPS z8T*=FtzP8{zgj0!!jpuj3HP&WwS+ek-bVP-g4(2ee#iHlUeb1kk&}92bynM(0Ivae zw!K-fqz79BOL(wVu($`yJBq$~uqarW1C#W`!AimOz8%U{&O%8Y)Oq-Yw#nr8+KmqI z5{FzV&t97 zT1oF3^li|~yF8NKf+_3=nAa~a1#bt}d!U4lza&_v1Cuw0K&&#G2$7uti`CwyamZt7?aKidfFCG0$D6NDX> zw04E}b26Ef?$K>46$$+xKMX%*e}uC-M;}OGY3O8i|2T$>fu~ig?IJ^`C6nL%T#xaC zGj#_^ZkU|j7tuV&;TbNfHuhItYUgu*eze(+YFC8o_KjGzE)+k&_!u5-!zdBfNVOiu z!B@d8AD}CAQGS&Ba^(z?=s-Q;iKkZ^_vl_F`ENQiKX-qGD<5*>gtrquPxxzjH_F^B zgJ@_UiK*3vus3pmck)0D)J9Yk(j)LipHZzUCEtW}ai~2O8aY$Pyf>mIfM=me{!_JD zlTEpl&An`jExIR6zjb#GJy-8(*!#mP zef$mZH^kovf1^1EkZ0(8-&gSm{-xSpXO%WN4Auem0^Vs;1^7x@Cnv!Bz@KE6hpkJ| zHoNF37Jb*qacH}rS8d$+L)qE1HKF&~WuA)E@@^@iZiCTj{Ke;28|Q|IUfN(BtQ1Vz zv^**K11kZ0)YM7;a%bq-bvnL(==Z+mzCWVV+K;UH(rR_J2=}%%(Gb#)W~U;k4eH9P zk**o|3SLmHZK1GAS{J~|z+NH%kCf*MSOb`4%LEtNysyz;f?N3#EDF}>!Qx=T@1#qC z)qAi;u(Stj1FQ33-C!vXHUL)Z!H$5{c(6&Zqz9V=OL(y3U~vz&1{U*R1^;W5moslA zV5J_q1Xzg&s{@OAuqLo#57rJ=s88frx0h(FXTLuFg+fm|ycU zJqT{G@pB{Ehnzrm+Y76WwLaaZ*#zDdK0iNtm1Q}#UN1s70G;mBNt5XHD%ddCS-i`m z^?wF`&_S01kAh8r-7Q44#k*zecCYl0hjX|{qw6$s6534>fY2PFpa^zHxTP!EzDW4{ zTkK!85l(I&eZb^T!C(90YV};<_xNBU0fp8kJN!dag8s!RF&wf!SbE@($KaO=*)MGn zJ-a*18Ar1XiZi?wHz{~O*yqfTfPRman$WLFZe}!(ff_CTEkOc9ki@(8qYh|@@ub%efI8Sjq?grJrSGr31 z*3+&C$2O0uPU!2Puc@k5x1TOQq^*#|UR(sLt%!4`htHX&QY&dsk+;(h=k$i}4ux>X zN{Pm9OS>Xmcb35Y!xUhL4&)~;u-?O8XJU?DtV8NOq`Lk|LvnYu`Wi`)N3cb(Rj?;$ z#L%TxuobW?1*3Y=iTtLQktOQ+0Ci9bTs%wr+EcCjccULBRfnbRcjeq}^}^7Jok&4j z53P0P?OypT!#z@ecjxRCzl*$^e%HDAHLvmQ{&x|tq_-bgJ;<`=&87W}fc1kN982gt zBL*8RRbx0H7=Ikw%x~OO-InT+kvhouP;9=|8{BLRhm8AF_Mvs@3m#Uq|+pL9+Lm zvW-^F$lR1I<+_CI*m>1D9ueOv|9l&N8|)Ez*KO9*zeyP}G;?Jj6_HlzE+3=ZolW^- zHb0qSjYI|=_Z^+H_E+-F0 zBaY}aJQMH?@h)jH$J5&K*CzwS@P=jexS6*?(Tqh5{iv-}iWI7WEUW!3im(Osd}XB>PFE<$zekH#_IG3Qhz)KB|6gUDEJtX5N!UQ&6B_F6(I zZTNu_{7phLczLz@h0y4D9P={WMyhZ%E{a@bB$szanMT`RgExIewHh?*Hp@QBNcCY_ zI&s_mF$R1n8#i}o`O~l;qsUlmu2$ztr6R-G&+7RN-Pi3i#@S8b9Dn<8$}<0&4@?XWBs1 z$JFCI#xmwO4<9oeq1a-3wEecd%zY7&8~Yyp#Z8&Ak@VGqb%1%&r|VVHcY`y1XrHvx z4tN&f(R1hI6&hSgW3Hy3u-sQytB*tH$gh?cdb6$5yCN^o=!W?4Gw^l7_aDNi`(^Ea zQch;SEQxuGyH`5b>>tHm}m&^E( z|v z(@VxHEK)vYKVZHM8DFwxXn$DO%Nq?DOo1MVn6qz~n!UQ8ZicVuHgk?SW1}e7uAB?( zxOYYBklP1s5?UFT8Fi@r5#n&vAp5uG}Y88E5K;8uM-XijN1X}@1ye6|& zlm^Q^#`p{DUTMsv(SHskG-lW~yHOmBgqvwA;vJDr_{cQ99j+9AjmT{4s8)YlSLS7~ zlXP3;-bna;BGQS~M#R=1L0o{)q8eUeugsVVq}+{*dwXp=#qiw9}vA1|G9X=rT<~R|1`wWqG3fHG)OK&J`df zW0qIY%J(wMo?9pNTf$!_G<6%0KLTC7L%!r`0xS)7GI5wi{xa%~Gn3un2VY z0jbA!=m((xNH%?qO@APgshCuxMR+A`qwwavapQ3ZryLRAZzMPnKmc(`ViP8Sh3w7)!FloTnSawBxOd@<@b!|wY^Pb42(%r~TVTGUyIS4qmZxoRCcoO(Js;0# zFi4t|T?4Xm?y6Q>OkZc6F*(cc;d1E{vXfS;?dLGErr(;`M_{hKY1@snW%@_w>T;fi zHt%iKdY+$T=)QIltO#ts0JPz*ka&{kMLOm8PiTLHz2B|_?3g+4q_>Aqb@TB z7#q7H_C>Du0~sv}CCRfXSx}lXdL}2o7)tR4x=BMzueokP)44o9dQkdS2^%8p5MeKp zv=Y{6hKU`YAgqtDaS7v*a-RiD-i^I7^K-Avqh*aRQ^xvNI`kvot{5Ms5Lv~KV88A$ z`z?upN8*ixH-M8w{lxe;1y&EX#f;ZEKf(QywB?M8Ci5h<@En3Cb}#dp_PotONh`Bo z=2{(Pe|YO2No)8u=&qEiyJyG7B4-LYss3tJVdt5Gcjj5kvDcntjFx@z-hoJ+p9YG( zD)}k%G4HBY518_u;{-hi&VVL-i}YF4uHEq%8z&m^(NubMakU!_T2Dx%70Gj4xgN=`KCjwT3+P z;cDZ)0MgbgTe!^my-d5>9l4n{W;j9zBix3Hw@NQh5}Np{S!BNeGTv#Iy`R3BHZmQ0 zpKsYS$9ZKy#zo(TiDTp=nR$L`7vo@KV0w;Fw`tkoX0(|(!o~^HcO5#yY6xE@Y=SU7 zp4EBI_<^AtZDX=8LQR;N-W{>;$ENQ>M*L^!|8TYXoaEWh&o&0QE{BZ4+#jffbC2dn zQ;wOoI%{6dF$GMY>GJOM__2Ygju7wAN9iwQp+M_WM$bL=d4GO7{LC%+4cOLHe#@Tx zwhQyqwG=TL;%P)kmom=%Ipg(DR_l3Rerl{3tP{++ZWIISbzo6wYrzJf*l8Yr@LRnlw|D*z*L2PMLA_p57SFZ`eMJpzJA)B z=31}>m~;K60W1z?r9*gI!D1e)6Rga^E4tkeRthG0mvL}c=o+otB4d;=iC^C3k^fJD zN&GuCVqo)N&0wE!U`t@?Gu6h~Mw3?^r#`{n=%6bETLqgjG3g6R!Nx|bje9GY!Isu0 zao2z!1Gnx#i-FaH&4XQI+5~rBP{Y7YtRxK+Ga||@K4}j;h4-0x;Kt%EbI*vZv`IW8 zgvSY&HZD&yvM0b|U>~<_YhZgg$fYIW>qGe?Tdv?<@O}B?eDz>*%K?7Ng|z4LTfRlK z(ckAoU%L+de!^4hgr7`&`$%7%oxY(hmyqT3Haj+FI)J;Evt${6riCK_Gu!3J< zKRj3o*l`b*09*86bzlo%mK_kA-2`?F>?0BZ<>d4ou*7=(9B+&U-*LKe- z>T;MscI>}Rzxd+(=(YK|m&mlLz9dlqJvmb08 ztVU!u=SN>bdYgGY0-u0+GcYVKwD*Q__gQ0q;5-pgZ=%oyGRdapvo&HRY)FZXDWLXwMZ|`XiYuPVqVmeh6HzBW3m>=&_>AwHcYJjfBUs zNYV!}W5?)B&o5+lh8k-N#lNL}OfesA_hZ^u#L4f<34IWD(x~UVoDMQ+kha$V@9FyeYRw|)_4P66a=&KUz06v;u49=rGU|9&1V_WHqFU^ytOR4Ph-U@<1pHS> zz3Q~GdLZp*_Vf^LrJ2AzkH4NL4c$AyG@XbP^`c%GYt~nO?ZPNoS`RH!05-NJr5H; zK=}K3m+_`^{!5lYOjmjz<5aEWX~^c`$@xZRAB8#Z;95sH%sPtR2VE2T58p9QZDhZe z$j1&Pon{5+g!BBJRXy)2bsYZ#^)>Hq z`zf$`FiQ`e?MZlA2=6Cc+Lt_1o`=Bt!2HTn!UqYTC48?i(8n=P&p|xdWpCKA3BuL) zs*U@Ov)Vp=8_jNiq(chCvy=r*YSi2ftv3lc}hZnHKe?bwh(|9Fi<$hl4- zQP^scF^vrEkK?ahW#~8mvXO|?mO;}3P0sf-YZcZWMA_NEMsB7FxKY0=gq6zG`*Q}M zuZ4aJUWea|jW_q5G6fL2kakH*9aC>81c#5WM9)KT7_@K3=l zpI2z3V8=YPVmA`tIX`gjjUg4*d64rcEyFk1AGy^iM*IX;rj2>~c6i6()!#GAj0tq! zOg}aj!U(whSdOnSf#Vt#aI{1Fms7}_KejpY;-s-~mGRRLtJUSadwoRd%+U&#fA&V~ zuRuU1e0A_ee{^zuqUYW4rQoyL0qbK-9vSve!iNd(A^Z~F6}y^f2Uj}7rwE^R@H^MD zq+S;Z&;4;`+#=X2SPq!gf9v}HGwlh?YNNXT!Pby{gTzgrE`Fw@DFI#&ZM}&*`yDBZ z2EzLZ*K6^*U1=Xo<{?BojD|&>-f6!deL&LC2Ve1mdA5w-l7?ZhQm|+7F21Dn(n($? zz+>Q_6dZ}>nGQzYp*I;ChymEG&vY0a?|x~yM${sA4gU9iXs0oE!_={yFOs|!K1O*X z_ZpGQ`ruU@Wp|c$obY+VpD*Ev;~PY@;@!H(68?I^j|cEe*|Za$_(`?;w(w`lhL$W_ zYb+;0(z?fR9q<>TkRE|=0ltq5A32h7f6DZ!rC!8`oQ0H&3P6L@6re* zjjPbr|1^_FzNg~SsiME&-w=K)v{LVE^1aGAwoVY9vuN(eynqtN(7jT&9HTr^j}3&k z6K?If6|5Dk&4EceI>B1ObXgM*zW=4pc!mg{2ndhDKS_Ar&rJUio6{A-;9&Kc65V^0 zaH;1-XcEx8h<7Oy_KWK={~CB2oN9NDOPLpSo_k;lS*EVN+hO)b&|@!%$43puFk%dn zn2f`?nR9%lMA5oN{(pfUoBg&q_GHwaF_JPLC}|#mw)wc}N0DZ>pXzcQ1#bn{XD^{^ z;%^G91MJK&KQi`|B-nc}w0R?fE8ru#-iQTOOIYvY~G@=1i5Dy)5nR#@ARQ}=7mnwVwoTodhnB`B!z}moO!1UcCjNSSABO@XPb;y^RsEx1)M}34}9pe9c^_u=LJVje}s(*%G z9+5o`RstsaD34$>U@tZ z)oh$&C=P{HI=s`~i*MR%($JM7qXUDMX-b?Qd!mJVWMvTAL|l#V7v$_zcZyC0mXTiH zm%Z*!!nFAm(*6izsYLcb&9DuV#F0FWa$)P zZaA#!x9&8~!_nW!ZLCtyQSe4^eTRW351Fy1xwoJ#B) zmvzi&XxEv#{mSTF25ittoeegF&vfWL4rnhe(={YPV{Ue*fcX#j>yg!xOIezAVy=1U@r{-xmir3`qZ6|`T85?` z@w<$Z@5VmH5oE^C*lB!6gE*r6O@O7ruH;?%7G0j#8pSCcu5SO(^g{DQq2ZBwSOMz+ zyGj6y&!-;h367qIy@K{Wp{4%)_eDw*=LCh<@sZ&+E}^TlMobfb9mtKJxl_G|_u#f+ zuAkN5#rmk#0okpZNM|kSpKs><7;&@_hh_VuJeI)@fr*~UBUsMXuSmL2~ zGU7-QwoKSdMGj9Le`&Ddyq#)FbRT_juJ^Gq**GuzJCE+y%5D?U5^NVdrmLm%F(mOn z!QH2gfekpYMxvSq>jzV2o6FKuG_h;zEk-o9XU-fqE!er!cAsnKi?+XUa*6L;G8>>FdngOTe^J?SL;+3(zGtjkIh zhNqOj0q~N7oyOVA^YWv3NcQ>&Q=a$nY-!kw;j5u(o%;OeP6?X#Yer&+KGu$y^TTpTPbzI(NgTdUWdmI*ID zYp2rtJ@j}W#GIKi-F)z+hy8EWR`I6QQaovg!K?6)7Zbt|5-^mAf+rwJ>3 z`cCyZNtDM=$?`{=!Uu!Sz)C!YdE_s;)42DEc_Bl6rcqFaI;Aw-$N|#{EV28kD%aA;RchJSbiom+Tv~F@zF|!u=YTJzH ziEx}?u_$t0Ky*6|-voS8Csuzby4(W30`8~F!q-E1{5jctL*T98etb3XO%i^D@Za)o zj14I_WAFW4c0!FgKr^MrUV3YMDKc{M=|7+Q_+?0ar;w3TvQyh`qrR`z_F3j2nh7f+ zY#Z-J`%9T=WPRVT&bl6Yp^HK1teYXQG7mNeRto0q+o!=wJlHX?D43}rgC+Nzv=vaX*V&F=pJEl{$ygGO!K5HGMNw*t(G;@BT6LvDY6=SHbp3QV}MCTuERB&7FcKWTy+z*XQq4drUvX>HlcoU)F8zF*0~E zvUTo|FKuazI2uZW`;9rmM*_m7t*sEgO1QI)iXF&%68iT7w|^T2D+BY=Ccv9Lv~^%j zKD5o?10LEAuznxfKJXa_t)y!hY#Pitmpl$O<-umaCOy~!*n|gL0UHOy9P3Bo$bB;N z86K<{Y}A2?e;Wfk0`?u@)Vkv{F1^^Jz_k&!le=_BpE;3uJCT=sVJ0nNd;cGM?;l^; zQP=;U+?$q#Y-5lW11@@jfB_Z>5;SNe_ugc8?`G5OZ5v8T1Eg(Q(h}NG0t6Z$lDGuO zwgI994H|Wod~lJd3q)Nt+O@8_K+vdBS6%FaQKBxeepoc<_xXCCIX~{XC)-y2{`WbL zM|RJB&CGk|%)IA4?|ILAX6C@!z&e!&^5JO4Y#+#a{A1jpApI{GxdYjImULy(#oJ2! zashlAd?y)N`xNSz@ZVQ{W?yqMp(P_ThcBTDbuyLsGPKioCvmn$fBc6=?+c>uP`>?O zWiT5K7U~^!>SfaHq z-&QTFpT8Dzia2Y;3G_?x!W!5)u>a(}!d8suF#L;U%LcB@@t`Ah%e86I%){Dh{tyu! zcJ%i?mHE$$7$-!F$NE3m0@#Zs-{ZFE<(kZIhQ0HEIqmqJ(UuzLrThLpqZw^uPGJrFeLa@NmUh|E3;K%aop$u%{c*$5i(^}0Bjk})9z3c~?`h~OU}-Wz;W@%j5I(86 z$RJMI7>cjY<_X{XgHbq5d{;btfbf}EI0u2gJ{~?n`1TIr{badF_;6c?_d3VxRldk( z*U9eAz-)0lP62bWxGyyI(9e^GopycG(`la{>hQi5_GdTt<$MoWfAEtZj~n}#=>&5J zwOgL_?d=`jFN+rPKxcC1+~uHJX5@$?(~XCScba%s&k&u=Xi2RnT&EVT8RB?9(&61N zS{COvlX_aFywSx}d!Hm;AMxTma0aY5hKcXbftA4ys_txDjP|Cb%!$9Q^@nQXebLU8 zY5p$gcXW6Us-6;KYzi&y227#l@9`gn)_lIhJL+hq+k`Uj_I0MH1?w`%tcpfO5urGz zp|??BP9erlD;qrGeEqbTPo<_54O`Pbk~i$d9=hMzrL7IFt1r`38aCubYpb%fYngUc zTjYs_ekT7#`)f`x%UPU|2cr$?=anUlk)W8oUNN!V|~a^*F~Jh zgOz3K1KR@@jLpr^8V1XOX&aXMifjtG%n848_iO!HUF&E@Wtk`Lg+m<`XB(aXy9gGS zX{%scV1bRlg|zEnm%-L~7eBcVFq3?uyP2+Ew%N{J?yPScL`51GkxVe^m?xA@O~O{s*9@gE4%TW@MUMX6vb|k zd{q`C*EhC&5Bd-E?h-w#2h=Kz%WtAG)ECQyZxQ~^&<C&O@jXS081zyh>#uXOfjXp8%8UKJ>73xOUR!hdTUozOA3Zt8V{vrWh)5 z2kPD&^fiAzG)lT9(%qpvkpT@{gsFZz1Mc1BWKD$*gcaXqFQbb(F5S+{k!F=NJ*0UL z@3ILqNu}4al*-tunUvyOAkNAa;z)nn^(^G&-Ok?3*`m0To4%I=Z%^QoPi^46;KAHL zSP$3$So}=c0kGjjyiu^R1U3yemB1FjX2G5UZ9U3k8EhdIPqxR?U`t^74&U~u+d2IB z=LtKJpm7OoC5DN{u4kj4q|i`U6Jcjk!c?9-VP^@8e{(O`2AI<7QM@6r3yFB+U>CvS zbvO&QoxqNR?RtG>J*XR-b^B<1GPx7$H$CwKm{f-+Naqb#*25~;?gX|D z)|kLH!J5G0^|cMw62nyHhUdWVV7AV{sW^decr*A^EUvzz9c&it@q9;)bfctOA>Hrt zE`Ln*Su_gvxv*3R)WY99aR%Se;qR|8S%8$2UB5+&v(v@**WDh-1(oM4X)coHCwceB zebNN-=#73V1bSy3rf!BVUVYxIeJ=X+JyG2#|3{`Y7~hv|jpt~WY+K|<#U?dx-iQkfHHV(fyJ=pBdN{QnX? z)$=9jHQ(pj7VkT)BX-wyGjP=B6uzgK{B{U$AiS0EU2p2}UckE^VI{Dp7$$z~2O9vp zLG5Mz-1yP@dG^I zFc_JcYuon|ckwM9-mg@CKm5x6(aU_k-w9dD`DjoTwr6vWbj@QO-fb>jx_!&}1`TAk zQLU=C))(sTPc!rOQ|wF7Z+lyZ_uEvDuS4<;Rl1w$sNMA!ug+}MU&cdW;t4nMe?h<} zQjmgDlO6s(OMa{U_JYlVeTH|R=a@7F@|49r*M1ygU{cWJ zX&X^{N!K{l;on1#b=>()viEMXmPItj^|sF!ql|1Mz9(%TX=h10Oxly7&Eg7mHCJcnS8e1&pcF%y7cKQ^bPKJxhDG&u5G!dRDb@OJNTb#Tdv7`hL-~XH}XF>yIhm` za%vrR`g`|tFGPQw?eP9Q{H`<}bNeXI*JPVAsZ54-1$zg7BhYLAP>1&wIWwn~-{yzL zvz*p}i+=qXENW})l|I8wL94oLBBgfNZ19CA5rcbV*uQ#)L+0^|{l8h>s06Mj^3#gX zFY0@ioi7o;2l%V~KJ+oLhr&8FJ6YPfZ(M4V$(~F9Mhfw{n)=NQ7v3%LJ_GMf>MZ%#USE^M2t_70>VFHQStHFOy!$#xunw4+pd0zi3x&FO`aO|E z@M1er*GV`1nGWx-T${z$pxyYpme2kU{9HZWs*d)&2>JUPk$q8px*4nq?At48M!JVw zKo=7fY4v8(4w81)sSdBeyT+5~yaxOquv)mIR4wi-arY7TXBF4Rm*5P9%uqbC%6W>o z%fw}9vPZhY8rTW24+u=-ugE??$9r=2>uSsD?j{#U(>-!6%tualc<<7oCRe7{rITd# zrOR^L`>1*xf!@A9cDe`jj%h(?CM&yFZIAPhJA;(l<$anzXknt+fG%dYPXLPVccc(D=CL#kAMi4zKOe zznMPz6!3Mz!;aC~<@x&VOA$;-s)7heAcB9tshyGu7 zq~1TK^H&;~FEAJ$j2#?j{M1W1Mo2sSSJ-M@KT5ZKb9`Cm8)^QK%zdhzB*ph z!z(+Tsmji${`o+ne;$ad4N zXeCa1rYdbNvQGYEUy9}r>Jyt_qhPyv*CT8jYy>QBPj7fB{0SDUH!EHf*fdxmXIsG9 zz^1@#ectBB?5Ua5*_BkNTV|I9PJKbPI@RegX_iUT7q)RE`?~!$F3UTkLio`h(yx|C zyZ+@4?>Ai9c)vkAc4t3|$zNMvqWiqsztsl)u!UfnEo8y2IxN;n&}4_ctKkQb&zl|o z9YgkAHcrDcSjpXYb#uLw8+~nT?;=g#<4iL~nyJT;W)EdML7HXK*cvfw&)Ub@41OB? z47mB1m~6-9ZnB--WxmHlr+kFe1{aCDP24tDk8W+m_&S5%QmE7G)DgZP9yggFKG^qz z^rwI7@b6aUx3E0ez690_)(7??!u2TMA+S-ft}tJl3;T1yGvtLfLtb5Wc(^|oHd{~= zX^)e3`YUc-%GxCOK2!q#`)u#?S>i1|N<7K7OT=3tp4sb2FW-_zOgtrpj zxRsth5B*-kON9Rm?-jPAT(n)0lSD&)GLBTm(@|8}s;_xyT!O~OM8n4VRQdYhO5XO< zu9J4?pC5l+8~?wI_3(drocvFkrN^0Oj5OH=w|3%Wg`CB8L zJy=&J%?N3RzuMvb2k&avNH?FfyBt_})A+_IpEg=TB!>|72hVQXX&4QElJ_ddX=pZn z?eUf4GHFhd=6|ak`nH}Q#@_Yy|6Q7k(A<6Le~+eg!AsCwfoA<<(LeK<4cMW22@&bk z4y}AU{pA}S{+$TXc!6AF;mr+xg*vQr?wunq!c(;dNIyaPZPGu)yLdJ}t}$10=yJ5SA^CC{Y!uAS@U7s(=w8EYji!hU4WYu- zRkx3{tv|y0`ZqhgkMQoVn_@o;>_V?+skG-|*w>ayZHyQs{?Na>wRMuHOk-eEV1ci@ z=DRas6JWuf!1&pG!AV;5io&*sDIjA$Ikn;xsJ_&gFU<1 zlBTTH4Bk~InVRds|09$4zNGrtX&rj7zU@eTe!#BbV{f&7lubd+TwhsHRk!^ij5%BW zMvLs_$zqoL+H3RP?n*tu^b=`!XqRF~Uo+thJ=_i@ApPI;N!8~%GJ#72@eA^IOZJwwX zNZ0(tytiLl)m=ADt~E%iUeqLY9N1 zE$z-H*V14)`I)$Kvp+F-Rhj2WbDT7h?b+(w_&`^@IeoDd+>&9VWz1Prb$t#Rxoh&? zt5eDx@IkgdS!Tbk8;i_;zb)^}Wvgpvd&`UET$vA6*0tKGOuBQVYf@cXI;(4a>o1ZH z1XZA@d8?yAuT@#6Nw?mR_x7ZeHIzFaO|S*+tNJ}n+LdpQ*Kg3i<6$-wHOSF;`Z8(u zJSFda=T2iFB@X>)%J@R1NQhwXT~{aa_dD|5D|ez5+1%uRr}diZ)ebk$n}ybPW8V7? zR}WS8?TT_`uaWGkHs2u_Sfxz~kji9o_%ivNe0tvAflO}f?qYrgmI>{^@OXTEApRZL z`o57qJrDEfhu+Bd>o^ynwFIr`Y*Hkq%@6S;3t2MrFTNr0fZX3rx7oX&`pD(IN!PB<-iRJDQ_B%N zN3Y^+NbXy>jcU+v7^)(gxHVAhe$snQd2d~Pfk)T~*xBdg!!c2M{S?>+Sm3J|Ul&)J zK_yTym{kp)G5?CMDNw6m)m!aB z^t0#Zz5A4wF+Q$$WuICTq}3=EF4n0H+DW(kf_$jUG=Y`DPJtcby<&{YjfHb{jnQMo zyFk2O2zd+kiQ5n9=aW}!Q$bwGWd=m?DdFY*iUe}BA-h%snSGUHibP9K_lpS#k zCsdn%Mxe2~gud32_g+!U5BO*2_F*NpnndlZ}5n=urdh4X!B<%~7mPc*1 z30C_9mDn~|4ou=sk9eWs5dM{54+u!~AvUooGdRaKLxwCR`X$nh#%Kxa2O9xX6YCK+ z0yYd5$U0$DU_%LP9&8Zo8YSdWyc1vpv3Sj3t6=?LX8W@8vR>qLvv&oY>(Xx#cb2&I z{MI9yUN`k0qbY0;*fdz6qYG;Wn*$5_v#@qB(TmHavZELHS+x>x5Nw`!Q@pEAkLhk` zXNP9p#=nR7mP$=o^cP5Xk#zC8S_a#SVS9*o8f+76TlC$T)JlI2eC-GG;h8bQE-F3P zOB9z!SnXltYg@z{lF5x=tzfq+5-Us+KA14%QfYp`ZG=zl5Z+IcKEl@t{|lv0%xkSb zq5(`^jpePPGjo>GZ8n019VKPkhBpAo73lZAEbo0>%tf7EK~F%SG8b>F))MfIVnsv& zlql|BajkzY)!rWDL3`9ca%9y4wg6^h72h?PxsHlCP=f8U0$T$cPhc0o#uC_Nu+apz z8;6w4$(a*A}z zq;l+I0=o=$66|}FkVo-$AHjbwhN&Oqz|MkwpCU1&a+y8%T65hz z7d_;F@`Rt;AzVp%8IJ~uvq_wnXxv9~PiD}&7=5|>d>30u_+8?6q3-|v`eFy3X%63(D=2P)793k(i@9U`_DA<){ zh59jW#in@E{LNB4-C!0`+Hdx`Rq}4{b~d4C989cvI9uOr%{_bvnl-bC_;`1??se47 zLF(qPyAeutgAKq(9m#tGVY{Oq>@+VQ(DC_0-Ss0q^<$KPR`#n; ztuGfld3Wq^ zz=CykLrv6#o3q;Q54n1_tme=IH z&#T;K`?9t3>;1L!V9H`Qc>DHAEG4rC(tSg+1Dz_a%SHBTyVIT4V;vrwagwE& z*scVtbLIMh_@xOlyrnQT;A}0a^`%dG8eOs!vWVzSWdXd>qAt1N^NL=LB(D zRdhUE`Fr56(?qL;Kt|Y0D!~-)*Eg4Sm`Vk@xq~TAllM%`_3VkFWv4uIJs?@wKPV%Qj1)0-kp z-!>z9U^h8Fdr&ry2eRp7Svm8G)UHG8s{d|4Yj8C0-vLEE#r@&v7n4^V!pxShoO7!T z4X?r;K)S@;Dz%Y>iK>T2?dZ;NaO5`KivSzTR*P^T)SiM|>>uYq=)-hv_zO>+5;w z4M1<}9eM9rPPWJQVk5=zNqqu-sx-+{NBso3RvsA^4{xs zce0UV!J-+}n7EhP-OoLuV&> zdq3m%WZrw?PHhs=k{oMJZ(sjDv}tG!{#f4YRC#TVsD5A4^|+;nfW*@$h%-x^e^4B% zEIgC1#n%5o5G-Bh5AMHX9ajBaAYJKb-robxZ(*0g*1^8XyB^i)?pGrZ-s9vJXMNnK zJ0VzI-Q~jf5uTe)56=^)hwur)gLBp5_EWR@P#=?5%f3BTZONwJN|&C6M*n;BUXAKB zp$jDX3}klR{)2hnpN+b8w=bCPsJx~LzeM;?J(h1N zXZ|ZSFIMb_ptTOI(gzsVMT=9#uh7TompCM+unHsQ&!pyYFgTU_bDvi~$}=h1BCj{` zu1DX}3)c6+y!T6?T)@xO%~!DZ&gh5R|OsUFDEp^w#eSGL!+2(JUF?FFB=le`SA{*UCnhaIi>cxPviR9FKJ)xE1e#~0Bd z_@V*Q%#wbK^grBPei0JHI z7)#TEaMoC;lRRs=o$p)7d!KY^tJc{<8NXgFHow&l z>9lXBT(CvRR&4g;1ZPo%%YFHY_8}eM9P}D~+S%$kyIlM7_kmvm?*l)sd~B?RP4Ox5 z-8JtrF@FHVtj=yyA8#5&{~-RiDL#*Aw1KUI{k{NlozWe%N3%Ve9|of_O-hY3m*j7V z_@h6QkL{^_rR}K|{VBrc39IE@PoBSdusN{b60l=k=<;qBe3CUv645;ejfO>MUvSt( zuzd-v_73D_3{#(I1RDf<#uNBK{^>H**?HD#GN)iTnx3YO8sX&B%*HkD_mXm5s=L#4!8XF%2=DrRdBk$tY>3N&AV)`nB)Cjx}=?=UIp2<61W3cDq#ZmT0X)YwJutwl6*!H@Fe1 zx4z||S|{ICUnC}Jv zNOysBKf=2&w-_4k@na}_jPTk&Ne?eEw$2mYMELS(n3_iVRP|XwphQxr=I71llP0++lZWff4cNLh(&Y>6X!CNR>*3V z_cQ$YREkhJ=%^g5zYN6*jPy|ZwHi|Ok_yv3d97w|RDpgmr9ej@)lGp!s-i$QWr+QX z7>QSi@V7)*XcvT8smUGzebwWjOVyp=@51YmH-CeFSI95M_mNG<>e9vr_5^3}V4lZG ze~}S=32l3szm?1pc9eJ9&hoWPp?(8aI?ujxA|Ficucf+IL!=8LJrLOs5xxP|StX&i zr?2sQgTM3qZSwmfCS5ZVY22P0W_|5%Bfmk(lqIlBU=KLG%Z&(5ocrs~-Jay@d_!+!Jo^eh z;+3)_A%jhRQDtmJNKvU}Eb67GC!y80mG@Rdd2ZwX4z#poBt_hCSJR35zQgqchpe~c zpu77%><9mn_wH61O%BM59JN@;bVl45`LI>E)T&T#!4d6hV_=7hk$bYEvP(?5@(e-XCEcb1>#)3 zLLBvhU2md&zUpKyy02Ckh3CNcrQmJgttogJydC_hq}BS7?hzE7A@Hu0_!HngiTIkE z%!BtPaFJ9WJV{s|VQPLo!p^w(aqJveIT7z7nDUFqs~u(j3no=dkLWdmsk{N!3bqd{ z5B7OQGP!Q{CFVS`2{nR95Nc0QxrMX}i;U&U9t#H0CzSc%@C$GW}Ba@k{6vRYr;ctSp zXG!~7rH$$ok5ji`OO@)jG)*XZwKb}fq`6F*Q721|q1{yY(;}EMpYhE^q2TYb6>r)q zM8fs$+PCn1|CaaO;pjaedTvcMI#(t0cRs~Xn(o&UZiOj|_SQG`La*=t=Dl0PZz9i0 zwAfskO7_=$J2{@rsREW}nly8yF&l~1Ly!hv!H}IYuL&w*0NSTWbCEPZ;>ulRqlxAS z*txO1rI-X+bJIF1F%yi%EKhBldn@hpjXZbA^26E|c^BVXX>I#36%qH&JYf6P`boQb zIq&^ANqv6fn}R(F>M_2l=KzO_s48GR>4ub&@raCy2sCo4Nb20;esBb?i%BK9MV16jVW;+wtEYeCnHJ4XVO!C z7KjlzWGWf_2BFsny}9kY_fL-AgJ|5$-{5O!k76|^wAGY#rlr~-Vd7x(KzI67&Gsg$ z+^giX_rF*l2+IwBR-P93vEEQq@ZKvui}sGM;ZeHqe(r($ z(=>wYcS=zp9=sm*6SEhH_AsDTO2)qkCm?x>Pj)UdF?hp-=Eyla_k#H~U2y`o0SGv_n z(v6WWuXN-sC2c>4SW0*9+|*4&8Im9r^Bpt*fgZ z+}fdy_ZY4jjMVrDNw}+vEx$QTxV}jC_c82!2kSBa&Gc%ge$rng{SWf4c47qL4pUzT zR`@vK4d1hK_yXZAgul?Gr$^Av_)76xe0PfQJmHTh-1vun8kc>D61RgaWOBqv5;FND ze%$>|=6lfl3DL8$16#fJT#XDdTrfdG0Xb5~AmGO1BJx6Wf-cgPas}^}%5>W6N{u>y zFT#e2H$l9;uAQsK>-c*5L=ELk5i9gbwe1SDdY@VF&pnTqRn6-1JfJIh++BTf9ZKmh zlm7UBGrhjK?OoWTNPneoR=fe??R(ahn<9kwOe5;gIIssL(Uf z590x$ZM<)S{Uyzeb(BTCq^l9mH zen&$G*BHJ*x^>dcKZzg6hN`|sV>|uJG*~HMG-pBqJ5Xoi-@DOAUs?$F2TPY}18V~N zXr-RiUFlsHXjk~IcN{H6_R^n|HrgdpBJHh>W2&7t0jY?`ebm? zJOeEpwq#7vK~??_>Qm{OCt1&EFLexQ%*TLwti|MG(O#Vd|& zQi5DP#kc#?&9-)^C$;e@XkY9ocz?uutc@d^xXDwSS4(E3o1IbE#q+zSm@nsDpJ1$# z_gyhIRy7kob%pQ};d56AA0qrX;V)8ItUfEx3027qKgR ziSRuIbPt7F`f`2w&34q3mH!OkU4-8nh97|lml5EHCi%Ka_#ENe3b(N`(i?4lLrc;j zEA|Oc{qF&aPwZqqw!Yo=W8_yXgmdt?ZlQti$LY+du9VcbC~!bYE>t$?+@WDfUTOV2;(-G&8Ai6YYE_-D z60O}wu@@aEcr1&@`N`%G(U@WUr2R0NrBq@E%m=FvTBC;w-kTlY$LEuL+vHU$jcMia z>McsUP=zBG`53B>aXW!vccpkM(4Xrrct3i@y3^2>y+d;{<{IdI;Q(f`MzY*{5B{({ zP6v<6gMrKqfe29DG!w7s+Jf(UYV{2N#P{IPR^fRecGqG`Ju^B>mW)E{476--aFnM% zd^z#K)Ve~ZE8PjwjT|X>|IB+#cEKQ#tq7hy9LgYcr94qDkgoZq-VCBie`q5|U6SbVeYNkFuT=#4?| zbZ@~s=j!u8smgKv%*JPD6J4&!J^&M^4A7xGIs?7ln+h?%J9PJO4_=jC#Bg6x<$nKv zANoHuCfqkZ$mBC9Q{=1T*5Foa`ck_;<7FG)*9WaVHy6B(u)d66s2n#>mv^~5!Yk^l z+wLNpBkjs91^;YSCS;Sc1uz&4;xVSN;3VOf34f4xe=Nwnl?a@LRKIHwXr1t({(^V6 zo>Ecnnx^a5IZpKKlR2K2}N|7nhmwY-y9&-cH96DX!3-e%)U^*?y#62^V z_trwpCT;UTb=B~a^nJu@C*CUYUdMY{zb?mkOgukKeDAh`cRZ9K(2dVmyUF2m36?;h zp^H>lf+W0E*QcO2aC=nO%3}?zAME|S`(!flsO8b+3 zxvfW?g5=60cMN+|EDvF=VDn&NFFkwsD}l{{JwpJRV{l&1fJ-05Rr=9H`WCQhm;NM4I)-kw*P|fiw+wNA?j$Cstj2`-%AP z6yYs|+uDiAdh;%|zgu!R8 zrhaq0o&i4%ZhOp3W?swu8Yf<)XTXbF#5o_!Lv`rQqW^#$aB{lh?4u>*v+FAiTUO2% z;%~pPvYcIDm%-F#dPKJmtoFWwe?I!FH0E1Bv~gHlu)-mjxc2?TKTZ6AhWhdX*c#YF zp&gntwjaVr5>3T0GuR*mVcchy48EtApl#w^B;MaE9%YI9n8fE_6E&CvWEzRjAP(2v%g*{cPz(z0T!lku zfHoHNeu(k&ody5gJNl?@BUIfFgU^ERR(?FfCctLEo^FVb&4JAa>0P`fu;Vez+8=Bg z?4?S}cTQ$>JAqGQgRphNg7xn{@GY=4u&+7ZPG8$H2LFxf4`xR4U)88Om&#I_Lw-%T z@z2U~OIVgc!o~;-%A)TcbMfnW*CT8OY=n6F20g+S!G^(V4e_y)U_*&`XTSzynEKv1 zumP~hcb@4lmVP9o`J8PNK0)}`G&o!PqZ+t%5?hb6`8VE!HQKYEWB}?Tg)Ok-;l!Ja z+iCj%hJT##Y^o6YYRBiqHiu%3m6?8m^?iH85^1MM`$w*qu?2&Lxu)N@Y?u8{UspAoG&IP9)qTg5} zd*Z)KI#@Oe)+w>EpR&p3$#a@Kr{0JBulzpC6Sq+ruOb?(-zFAs?EB6_W8hdJJl`w_ z-Sc4mU@zv~-|KRtcB>z(E19UYwV~P%!=J?YlW-iz{~H{ZnF z#KsQzGP?JTq=K7_qljSWA?=K!V;Msvz;Jc-}zJc+O8 z|02IJ%BReNxYcPxe#qD3hkStl3-V0XLASoYHB0nAcUAC%#*|#`okxECe8GEy;~zKw zitLh6IZ(+l^rZ~i#k%Y#Ime`@e&j&?5IX|~pnC$kf|9d3~t2I6h>GzYQaXGl)XnD;NZDUACLd zxbUh*wv4bJA|j^E;ShZYa>LL*|FMGiU8)!MWyW|-4rklHCySoHQco_0a9R9P+u`shWqp2;? zg;%^^j3ZGoQd;bA-3>0@jV|6B+;9JOH!@|G?o(o^-M#fw_Nkvl|M(@=I8^6sX*x!a z3UmUebG6vrJ#j}9&Cc7{FM^Up=t9h$SQ|5pD&VDA#+kVo# zg8#X)u}1D~KHdFwtceA$nSK}VCKoTeo^gQ(9FqAIFUmduFhw~;1+pSG2;sWH%q#g{ zMSKJQEBm0xzKkDUK>zr?f_J0ZnY(q}Ign-(Vu4Y9axJLimF-!lTeQq+*UOV^85&y; z6}+DpjUD`;--SKNz%|D*naU6yO*MKYBE0^X2`WA_04^Or?=_4tv`eQ z`NswGL(m)?6EB8x0SfTJo+f|D4L`C z<|$1I6?dJueZ-CS!!SHJy77H^D!=!$@DFk0bCW$_N*~NA6|WhrH-WW-mBEb9iKBT? z8SDhub9omJPcr4yH;BeCVXFxm6JVzj*c{jj*wtz`9@Wnh*vZeao+iNhV$`SDJK=;t zSKVE)cjbAWblaqhx6dW8%V6>E+x2s-x5O~bqxXVc1bd$HjQS&Tr#En`vG3|4p6JEv zu+Py8Fb#{tj@~mx&pj&R6!@;sN97p@n+K}}JF3d#d*l8vkxUKt17+TkI-YU`V zxLrGQQ~a_?eftD)&l2~$6_-c+I|sG_)+qqHPI%5u?v7v@VE37+jaG@fP28Im*V+hP z_s_}37z%)(RJ={%wf^Om8rqy}CLj}|GRd_`Mm@?^0@ewWUnm-Ft|Y4Dv|?)1NtX(2Xl_oL(! z!<}<7do*Kxa|;@af8*+!B)(A>ElOzpn|MxU`4=YnvQRWn5_joQXe!=0;w?Xhc(uQX zydmBTm3LG|wC&0=?jznR@gA*=gOKSX?rG&+%Ma_niTQ}Fmpgyt*_!M*DMi(u2~2W6 zPC)fh9+)&t-&rM}nJ*T+t`yxXq3;~wd;wXsQ*^IR^C{Tt6HpO7k5jiVFaRqy6e=Iz0euD;QUUlK3UtH%w9|XXU#NZR^yKe;+`VztCXk3 zrQ1$syX$klRu$+uOT@cGJjqc#CH_ulPH;DUO z_aZZ*cR&JVv*Q&t%zfq7w0a|1Lsn z3tG=o;rUMgEb4BK=GkTQi-!M$JSF^V3Ma2Bxn?#&_5@}=jQ~s7gc&_@B9}or3KQEU%LbP z1EgvDIOYEeK6+vQzQTNWKnHxMG%pTw52OZ^Y_H)0c`W{G!QTVw*U>WHaIkJa|6+F3 z-nu+xIZeFzi}=`vb%pzoKL-9@to(SVxNkXNYSJOAENVnHh+V&ge)ZLYciG7iXM>3I zSozIWJeIv0zOCRpRE{63(TN#3`VaggsV(|L(C@ue@cz^Fhj?Fg{xjvw*Av9-62%Jg z=;l~yD##zH8;AZG=wJG`g7+1T$K)Hg{YCxF=}UHoY;|v=&peXa9893Ust#K1BVqic z{7Wa0AK!%EcFI4J_pB3RaYOy)%Zx(C@ zEU+)+!Hzq6Kj(NeetrUpfOMryW!hHV1TG+_E zUSx;EZ*C`BOlO_SzxR`r|Ei+*Roh@m!D9q@H3kQI|24@VLw6-bZg#K^BydHeHnU8wR&XZh{oab(Ce=& zdhZJB1I~!sC8PQkD>bTmt0lu>(`)uE!>_xOeDH3X6Q7Kd-Sf&z(dw>`xTDt;{r+fe zFP_2eOmX2!`+njK)E6skC9`1t3G6snAK0_`9zCk}710MX-y^#ph~dd({&j(rKm5?c zPW!((7`gmDc8Ro0F?#LL+x07qhhU#~_2lFSI@^rypmgU*XYZqav=kpo2UWCmvJ&Zg z8;ah`dG`jR^HvAI$HAF;Ji1&S$^Jrm{rmQp6VN*Ulwx=eo9b{C>|6|M0b2*#0K0>C zkDGLzynq8IYp=u3ukOp}OJBpz`DQpR>HG!Q1Ye`^h zVEe%0GW7yja{{{z)&wTk5@rBu&(Q};KbZ5@DWS_@~ zkLyLQ@GxWkS;X#V&E}*2H$eLSrxv}}g#FL-q-ZTHJ}&09&YUvJIv=4E&>H#9qGx`O z(B8}6D%ciSjp$puWsajAnG7xiohSTUW6{6+4!ywf0vs?|%R+!-0+cD%blndIMaF0M zqa>x(y&r+sRZp!a84sRTthhU-1hx+LEW-6DUO(7HuA6-Ylq_gDH+;%7J_ zRa^DIh2-+jS=c0>)|R60<7E7V|MnzDb>$qdn+Ah_r9Xu-KHB>k?B}HU95nRZ$K?Jx zljRDeqxeCvUWLB~t`8NiSvZmUgMFy}AE?Wdd?Gq$-Zp=Q zGPx9}a1<03?m9j~de{kQT>So`fA7qW-=Mjs`wARo`$>0!bQ>=zdOyayzQfJMIgsrb z*N+6}4ccBnijuCIAFR72O)?q%t`+RJt^fbf@1@Acq2C7mUlVV$ZeBHa(%A&RnyB`Y z&H=A5bimpZZY9xQkKcyw;!BI(eXdQa=KAoH_DQckDRZKlh4V|cc$ z<{?DN!!U&JKb$A>1XSy`COcIAQ{=Py@}gJZU5~IeuY830nbcN?>Qfa$w&!KT5~yn2L{z^1^0GB$zrJ9PejggPi3z zBG>fADdNqOwo~z7vUfWETmWAKSNGJTe3rq^Ca}|BXA;;3*y#ke1-1$nlu`7&-@^V1 z_HBCS5w-_x1uR&T5f3$kiJtiqa-Nm$Q)mItgKvRbo7*>b`}2?yt;G3;Q`RNu0R9I{ zKN%%WsSu4T>2fD!5%$i?X{5@!3azuyir3LP*qH>j33fVxZG)}GFwLtPew+FLTjkv! z^HR0DDlRK7o83IRziH;ZI-YF z!k**gPn9o_`q1AdWPPy?XSuj@kiQmt4q6kPPX9Fj=(mP_>M~)c2@7~Z@^bg@P#<76 zKYfE*8gR(Ql?G5%_&i{mUpQlNotCsQH?3*?w_-hp> z$)oRJF}7C)n}*~{IrI9UpGjXjN4#a?P3l{sbyV$RLW9<74iacTIk|qS^YAq;?D<{v znNrdFhSJeCai4rfd-*BJf?1(l(8t+?nXuK8T>WAYdMnU-hv-?qaOEAygo1F8pc&#d z94dNWP(1kS3geM3#$(|#r5P!R({et?wzY}Gwl({{OXM?6KF5@geIIQUKVOQQ0PXml zc$ZQ=ReZ4$`|`5u_mDTxQyZ4c9)E>(|;Gbc?3C*eQNTy46Z-dQ(-N1Wnd}3}KEz|AC`!Sdk=(}1T zqP(PezS8hWZghcdfJtu?9sf>T9G#JB8aoCF-y}R3LlrI0-#GY1@Ze0y2h>-p?y0~` z$s{8wPr~(2XzcDm?+g1|G=H+WuGtr?zhQURYG4Y_FxQn#+I<@P610A>mLDdQSc`Oe z8*Y{}S4A?3bES7>CR2$WG&#}-t@U!z`^&6P%k4Qf{TI7QU{AXR^JPUN>bulu=1D)= z%e-FS)vY?k#{=noa~;R`x?5w_9;?I~enqju_hcPx2uz=$NAWhn2EpPyx(zk}7K{Ok z*YNwuuSC2iu)YM=2G*NM+XGfk#2WzXNnoR3r9|3ku&xBQ0G5woEtGE=tR3v-uDvUL zh|NtlS_*JBj0?)|ref$9ukXC9{9>4me-D!%*s}=t`4}mOp+V;y=7IJRUMBn=<&B?i zd=G-AU@WCXV*~by@z~&?yG}%71RCS7Ec$nx8I8!#K|?!ZZZx7G%YH9LQfJqy>?ffy z-&fggXTavb4im0N@y>y*fx%MtsDEArI}3JH2s|FQjmGY+kXhGgyYr+WJND}nWb-6=zq^_|4HXZ_H|;A`tAuF)htr6aO>n60UwzUKP+*=r8+f03xZ zUbF=L!MlrIn-sk()3=@IC!tT4m|f!Q>1=x-`6X!e4Hv!AW2E)j)Up=pTA-CbLw~ua z==}!ov9S#q3hy-fIxp6l4+rl9L>nc|CTX4-$}F27M*51?l{=5T%$zl)rkKpnr3NsA zmYm%nkCXSh`LM}k{|s&E1(yh0A#8#7O1ZD|0GwXHdV2T)aD)2CzCUDsH4>krVOC?| z;|AoC_@amKJmGP!e$5ycwE>z*mMHh1e*e@kbhtkU~ylL1~wRtg9YVnhF%lc7}(E*`OB4ss**{O zyL0`QPAgBDxLd@H&!+~#Ho>k|dLGr~7}y0c+q-+K)<`cSIm_Bi#9ah)whijZ5(bFuL?(5e#Q2}|P46gr zKT2{>dJwv&JZGo*d0hDs_7h^tG}#yDh1HtO-R?r`SMq;>y`A)BOgBzeRl07kld)UO|BBa16Y5vT{tG0rS8DKXJ*YMX=pquc{BeJ?aP1da%>) z7BiVG_M{E5Crx|PiflK_cCwj;Z6M2>FS5kOGmwAMl#41$j13If?CF0i0(gq;E_gGtBMBWw+*n^Y%teptZoJ7_{6^FeTo$pv&W+xveuwalbDs^VDgIj57>}UQ=5%{E zPfc6XJj5m`o!^ndZ)gSYV}I3cxcZj3Z5{p!Z(FxiuP2aORp@^!){FPkH+2=e0+~Be zj#HGQ;m3=y`#JcQxUC`5Bcv0t`z*!Mu)pp`w}nspxO*YaP3-)sR1-A}@To;NIo|#S z`0po*-rrCb@y#)Ifd*^C=F^2;LzAQm(^SD-=@S}&{dKTuXkCDo?K?CXcg1hX;LO%m zJ>DIxdmA5v_E>WrA9FDAF>Ms{BKZu@7rp% z_)R^e`f2!6|17JO1LEp}Sje?cHOiy4; zV3uu4x<-(|W-w2jLE?Nb@jc>DK$COJ;N#$;BUz@iJz97@13m>_7H;y@{D{%Rk#^3$ z)up{t&X-qdwn?){npcKtn2Sy74nMbx1cim&+ktd8S=F+Rd?U>lm4;`Uzb>$)pDlVH z-OUe<+^e!%8DH2KrPk(5Z`!+u_P){D9pt$g-gGrjKmQjLXDoe?V$p4&##d8h-dfJY zPi5*S`!&jP>Jvrp6B?irJR12yy7BVjld^xr*2O1XS3gnL$N%hJZ8UY&h1#jmhy1pC8ANWb@2qA^1}G6mKMCf?K|Y#yvB z5$^<8E`hD4&|7!(o?_H|dYdWqwjDjG7J4)$HT=0>KC@-PUpg_N;Knb7w-COMGHBcs z{~_CPL-x;}R=LZB_YfZNyRbp9QmouLuraVMuov+zI><9-G`_7}P>H_<;*^Q=(y%QP zwyj9-yOx`^vQNmQ>3P_GptY&|ey!-={f3{O<5AY;+!}Z!yX?S+eEmkvtIz;%Km+9J zG)A4Zn#^gy>i~;@U4Y!D#-FymuGQR}$~OC1nb=??@d?G#a2Ht3NGwd-W&?T(do^Kr zJhB&f6TC7EVVPbKzpdB*0)6>Y&K}9Ps|_~6PJ%s~cRj+k!PdY6UsGWX8~D>CuqLog zFm-P|iq{5q0W99mJz(d-;=VltU|R`#qhOc80voF6O@r$XmIu2aV8_19 zKJUpV$o4Nazj{by@aiG?CbTX<>z8-prKtV6@U15MP062XYUWoZdTkgsyMDdsoet@B zOZQ+V;P@%xO&wp6-7R|6gQ132li$NM3$Z9D;;-M>}zz9q`EvGeV)HFMLUAeEV&uH$!uPzb3FvuxLz) z=3q9aSUuX9qN5a0ol`i6>E<61P`%W3(B|>eNNXM58MWw)G3L0GI%5HKbBiI3D}KO#apy``7`MF=VG2lhDtBbyI!EJGa^G+MSC zfr9j1#I{}eJM+#2@b=hmk$;E#E!|WE?oWh|1i2mIIvAh!PEk3yrJ^j^e^lx2;9Cl& zP_6P^d{1|bj7pGwi$1xJXaQjg;#0bj|IJ$P{GfV+?=*vg>RV~7nm+E*1i;<=Ry_tO z&pgyFZ#H<`hZSZ@!x`| zv7j4Vka$F?Zgs!Q{4c6#N5KbT9PnR@vJ-@;ds78l?P2VwingEAP<$}RCtlik>4PH1 z!&R=ENFQ(5dmOzWKKCa1MxgNXT?Eas;u!?h$t|Pwu!vJ)b)qCU#)5*fC@+;I_#I{y zd~sCMDM1p>ejP-$fkZI*cKkg0<6lMgTj`!FU|nGMDw2&G%thm8i6Ydh`x4)#VY}K^=G&Fqzo`AS&Q}K7L;RJ&`v11*Wi*q`nS2FbW9BA~%}T^!vgdn{FeX9%6nS}nSM={^WX`8M7ILgw zG%#-~a!QT<*Z6ROc+JE^+}fjhxD3_=_DMHZR?XQW{YCpi4yV_Hzb~Qd@3Ehquk=Cc z1Di@J);&7{5H=v}L?r*_?% zpEifo4%;qlF7F{o^RI?S@INB`^@@ugHko+~D-!5C{M<*_dBS>KogKqW5%8SpDyk{l z5s1nWa-F~CHbmOmzb9=t9!Iv5#Jp5z^>e&R3fr+bo~Ilqq1AW+d#1|4T&B`TXCU)a zaXsgN)@LF;M+&THT!zM;FBQG_x^GFJ$KrdC;=C;yz0hdCko5+>?EF=ty-(EZOl^0# z#&63rx8lK2b)nVmRnD`%&3q2Jo6uF!f=4>{3GnmaYhfMYkNp7m(^Q`aDm^UQB&J0U zBn(itXE6u8ZI%BYBb^&N&=G%wmy~S!GI4TWLLVf~2UHl&(X2Wrq*zz)E1j-m5F;lF`RAf{GUSuQT6|o#E!W7k zu6m}2dHNFsRBOdurmwgC1K)cwYAbzj4_FQ?Sl1Oc0Jay5Wqf;b{5uNP2zHAQd>|$> zZ#OFni$?olJFCxm;`9^N9pE6{j0 zH~?LR6W^87(GyTDJ$#_(uvbA;LBa+)Yr8MdJ_@wI4P zYx4ge8IQnTEZR1v-fLrn$$!EY3A>;$(>c%`;(l|?^fTg`3g_4^+dDZ#+TO1hyAhdqbHv>wZeW*F+b@A#0JC<&u+MD8#`ab4E%1Q9^bPAS{r4#^ zbdX7=ak^Dk-+Ymy@CQU6>*@ z`fijulU;qeOx$7OzRc-Y$1>QIEB6`W_4zyW;0n<~Z$ojZfc$$t7NOVwji}D!W1SpX zl|9-Lk!2;ZQ^>BXJHoraP-n7U-@OT)+ROMA>l=8i{lRiz&+s9`8oq*j0&8`c>az)~ z56srr{Q5DTP@FtrFi?=fT#&G`{H3_niP+1G~)-A6o_6_a9CN23MP`gAIZGxZ+tq zP0S7CtTtXbkg2@8{~7wEGd{QP!nSW`vC!LG`>CB;iPyMY^w0ERZRTd$sRg_TyaoL2 zyer>?Zyq{Aq=(7RQM@%lnsL(D9vwTa{cgJ6LpGi-5VlBId<{tTa1wk4TwP3$urpvM z!QyM?=fIZ1YAu!@?;_X(B;=Ms1%G%-;=2bFD z{$;A3UyunQMJK^WhdmTF1^hhZyZBpGe*abUtWm67?t5&Pg!>>|J8({SKM=LI@xvEK z65GZsEv;k)<)Qh@`|@{E;C=>+*{wmR=R>t z!p&i(iFo+#u$7Dc86aEGS$KksX-}TD+2Hzi3@^p&uX~Lh)yVe4Aa(p|l z3*d|34+~GoQ#nBBQ*G>L07?7)0=QSGlNMKUl;c8PS$2myDz3g zVZuHdQlm$x%#B~i-tg_6u{+=CG`p^~bD~P3Pxu_+=EI8J8aag#;edbPhSp(9b+>CS zsXJ!)ucyw(UZ)?2+z+z0b*?FBjXkB)--oypFHyn@^{Bh9-}Fp|D3b8${v2=(THDb2 zVag~y^R=!nH^H0sblQ4-B@f-lr^8eX?}q~=%Irt?g!>-$L8|!@^+EbiiLSK;86VZ$ zo4Z!t?UM-Vzf6=q;@5shr@wca`bpm_m09q=DztiaQNfjFFlX>mpF07~3245;^||=? z$|?ggv*K>A57zhHQa^BgeP6*Yyw1Vwd?$s}*eiP5(3^c~r}wYw`+QUSywhxfS`*G5 zgk{>Qje+zozmN3%8~paP&Wd%?0kC~wEyU5IelrSI0<&@5VbfrPV1XXr3%&q00A{v_ z9rz-9RgEU8`9giER9`OXV+Tq)F?|FlQoLj;rXtW3?_PrL($hP=Px7wst~y5@*)@6f z5KR*|NHQwE(pWHTEcLPc|3!X&SEu*0qGkRvc3(<&=udP5#pq4kS&3UkPuut&r%LV2QZohbDHq6u3xfP|EzfsbxkjCcu)J43UZ!)Q4;>>t;k+|oG zn|FQQ=~I!fVceFJ=N!Q_iywK4@3)|}R_^p(5ccB z-}!#hYMj<%-w$>kO#M}lumP|wu%OSWPmF?Xg1xv>j-wYbmMn%mW9R9Lua6V|@+&$+ zyGwjsKjUVnaEylWkg+v}pCjG!O-`qU_a~(VGs@kAOi|gk2|rJGoW~pf1AYSwY*nJu z1hxTYKC`9=%d5A@q~5>7sf+ODS9W?g5?4C^jBEhz?!`zoDR6>@>w!fEHKM8v6Qu1S zZJ;j+n*$pIi|@-=0vnBC;^$LfBVcb2c`L!s@xD>Ukl$84crm3n*|lpM`PA3x-40z} zKY&kdt}ahtXS=9lA2<3L>Vc(cC(U`%*mqOEM_C81PQzS3mS%)ByKjE%X;dG_Nz+D} zx9U52TKHR0d0*A(-6g>Cwz;}y_6L0q@)eq(?Bb&>m6tST3o*Gvd7Z3sUuoLGYpk98 z^Xw(Bn!c6wcHPqH?}@JX&ilixqy$LLmx()0++inU+}O%I#jXF*1d~^@*sp;(97!2l z|J>Jv=z)}s8Ap$Qcj_Ya7GC|>zf*J7b<%8*M&Df7mIL->YCnZ-6YnDNY|a3Dt^Y}VyvFtYc-`mT5Ykt?e&Xeb_h{vqB3>Ku9&b5Lk*@qG}Ho&;5H0WT=@2t)H!OP(96K-W8=cBs6 zZ?aZr*85X79HXE{R>oi&m*z;fM7qZ-Pu13Iq&-VoTcfq|yE$%|H5TP!V!_u3wu$TA zM&IH;l`pPaL^_$ftaQxhgz>kgdx+-JgWL`UY*WblUwrH*)B<4A3$*4u=}qN5St zFBX&?ubztC|46KxE5F94RfbmkP^b3?JB>-EV>!RT9rku=OcF0IKx_4`PVXL9uK2hQ zFH@w5d5i3Ph!`8alr-l^f06VbbLk&%-KY$?Y^~RScc*`TPQ=4Of4VhZhQo<6DE$EG z7fAo#{f0%-pC$eOAeIHr3X8W28Ug#{+~P{a*LA3LgU>OyGOLXTit83yP1p3eN13wQLY*neY>Y z|Ea=R+jBAtoxN&4L8r$am?E;U@ZFb&cR9cq1=s`D+d#Zks7r$-8JmZzs{`; zs_z_OX9@dsr96w~pC-?Oz3RG_u&vscpj4OLa|PRx>U)}en%>GhH|%3J7o#TKzAT;I z@pB0wOhf)8P~0J-Gx&b!?Ri@?S63ceU^%ezmGb!cL>~BB%Zwe*BjES;UC_Hf%0pvG z8EhZe`_+i>jkBpG>MQUI&ptK1T1s_!k}p7OWU|x4bZNBWx^%_ZmiguCPRY0|J~*%P zyrl2YKVe@<(qO#dLWn8G{6kD&B%$Nic=$Y2t4iY#FRWK%^_dbJ2eFKE-_!`U&x_ zb>CJc8*NS_M}0icS`z0W9)kDz)};5B=1T+6o`CkL4|RI?R`##7dc(VJhs!`vjNhAD zorOBJ_XU-I&gql<*3-=2Hh9C2leYkqaqO8ps{L=1wMaD8GV{Tb-QC(;hx{V$PrA5q zUbORUtPb3r6QPFX%a=jYu8{V6rM0z!aTWm{@<#YMLD)87U)aqL>n}Ub;cZ{a33Zri zqZBUc&#qxp&@Jp3I>i44*{eS;{|DY&c=vA8ynLBe=0!Z;-{yT zQNN|74~yQOYtV;}cWQQEkFaL23t&%nnE0w4?Bf4N+#7($byRoXSG%&UWSxy#3>Bc# zB@v*4T0}8LfZDFC-PKA~l1-u{B2i$SD8YaL1BM7NAdqVe$cZQ}sMMt@7%;_v0Jpec zKy6GAtrJA+wr=Z!THIoa%WH~T+~U&r_dj>`-n)0NZ1cU(_w+u`+E@RXIWu?W%$YN1 z&dh+B?S^>|E;*Kd1ibA-j6E)Hh5xsLZ2+?~^kd^G&8a1n92N{}%}fw@vqZP5U`5Jd zQ}ROtpSvT_T!yAZt)3jeWw2vl#%s2QO6|SLhS|b{gsYv-5vS$D&ep?hA1emNGnL*C z-;hOrS|xmpI2#E+K=?~t-A466vm2ryyq+;5IxaKI!!A=djF@L?T_hY!B}?~0|L{lp zy}wt3UaC(_txI3E+8{#mE`Fz=SN?dv_jkPe{oK~BkaO_DmVg%|l1wgcxVqUpPyBBv zzR5-NZ=oVne^!&dQv~%AZ{ZWj6*0)g%c;8tel3-qWVc`w4)>F+&!pz)$&Cg%cDQbA zCZy5vu*gM=lP`ckzoqxvYLPN{Uw1Zu?6&B3SmVVN!Y95M+V(qWvvXiu!G^@ltCriw zXzny$o6a<6cdspFXVmFSiGUeco-r5@jOR1v*dY_JSC_7z3JN#q5iV6G-I*aA>y=s zE%en8wgff;wod7I+W4IWtAJGmm^{9Zp6=^b=Ly?M*o#~rj*jW!eFt)AZ>4v2qi)fg z`Fz;L#O5p{dw&}KU&g-Y^hq}tiH+UHN32lZmMt@+3}i>qWCyZaIVN);J1OVzO&nRE z>yP0MJF;Lfo5$rRVUWYX{}X39Oqs_1yx;qtjAF*yRB!Z52zuNu z7?wSw*Js(6`KDyqArpkzAaajld_L)PMd(Gx{ZI!@>8xogC>Z$7uOOEaIZqsu+01}mL5%AtfOue@u-qpUc3iC(zVo9Q#Lx!V;z((^G z(3s!7fA7k9=yrUw-BqcJ(_nt!aBfCfL(8hkM)3UI9H!`7_1en)(4fg3br1sW}_9h9jql5Z#P(T z4BH3hfvGR+QQ8GZ&*X!{j)IL6PtznligyBRBtmZ^*cq@2*gvb!+qh=;(hV`LeTq>K zZGp)+zGB;bjfg+|d$>9tLwWzh={c6pa+7_n2(XBrfYRlj$W{{VDIPMuTMKa!!9}6T{B1# zB%Nt1amW9&-+Q(z??vau`P7#Zd&fZLz9hv!b-x7t!hiL9pK$aW2+OVJe2)m=0%H$qt8qT@U_%N^WA!b=F*nD6N8m<4+@1M13vQL^fhYK+w z$U$}o;ZubF4(~cQ1RttN!iRJWL?6m(9(bNO7lHW#V zrR3lL2(5Cu;NNLO-9`PB!n=BhY;HP5+zZ5g4Q22+OC+}~+cM7U9Qb&1!TS?e|H)&k zl}Gy&zm;4bl*yk_NTV{T0ZM%1Jq~%duHd~|171vS$Ig7Ua}0h@x@n`_4#v9*^e+5# zA#&#zc3PVJwbrn{Y}!to$ww9HeyVI~)QZrV(S~Nxj^_By^E<%bIu5Jy=Iki>qidE;*OU%b zi+yjla#v!hwAR!`xz19qvBwp>5A&}5Y*F8v)LUx(NVgjJwz6D1`Y7>jj|6>3=hsz9 z+5>(H+-#|rs;f%sB!z0Ub`8nlFo8K~Nb%-A@>qC$!Qa;>k>4b&0hD<7Q9ymG6O->aL+}} z)|vX(#BW6c%Szu^ImL{|jd}7I{<(s;9PlJ|VW-cAdYY3%d7HE_(i|X7M|Z*deozK%647~$$&(td zanfcYAN1MY#dG?eUn+R29zEiNmK^#v*y9cHv39VPSiEkqlaY9mw*|1%V1J{s+4nek zo71j-C!7Z|v{UeIcK98nn|)Tn#$S#9vJ-YdVlVg-_~Y$IX)j>azSLoB3G;^e>>4L(_V0*!$y80N{JXq9Lxee?fn9}Ic|2x4B#IQNA{a{hw z`h#E#V9`7l!H&ez9tS%d!%l-80*ki!1xGK6wO-?wujVJH@^yfTUJc8E^?)4(`wZ`X z8>QGT%y8@kLObvnajMUb>H;|JQ=6Rq*V04y4#HOmUltAeX;inbL2YK1 zQKEM9J51W`Jq5dq-fy?soP=UHHbWb%jrLY&XGpU^8nY`KU%klY6Vl~duH}0O`&Zs0 ze8AX{LZx&%1Cn3$v5~mDpTjt!xI9W<2HOkvcR?Sp{c}psh~f%_L{ZyqCvLg75cm#F zr~La*6gErP#42G237cIdY>BW#gxQ`?`?d-DHkJ1jVM~PF&buC!_dM8!Un%%&MBIJk z`Uf)Ybv0f`2|$k_Zbt|D8gVfjTRSDsEf8Pn?;%O!3qV+YS)`565VVGjKm1(QFO}9~ZHdVco^au73ZrdgI$Oq+K9ww7+{#roV$-BYHgImknS`VDC1> z$2!4AilI(WCaf2%3icz#3&+`HUxcd8Jn3m1Wnl#6xf@y=N(J8+nZHf{1Dgj6$MGZ` z+QvN0!A2723u3*NVQ_sbijhdMI7w!$)_7QPtSv1CTHN_186b`;b# zHRT(!l|kER#t_UrI2!KjiQ`XUd?ep*u9mNj-GL85^4>p;pSLCH%Ft9>ERo0fb&j_= zizBMrVgQ_8!?)QAOmtC316!T4%U&&LZRlh?g4XY(`NP^Ztf!$s-?8#mSl4)Tvc85U z_fCa0>Uip7YL6Y{vr;K|zbb=SET2$Mv-ah3$>y}zl$~qt%g#5m{?N?Hu+29MePIfe zb9F15WJtX8=k z@jAgs!3M6lFw{Y`b4rUu=5+MkT?&$<`*-DyK*;-Y4l0XakbNYq6V|F>QHu3i*~ z3i;sllE>K7knb-k;G^l6Io6I4ikhl8*2J2Tu?4=6KDv+e`H6xzuQJje7qJ0pFY)gs z)t~NNc~~dmRqB=ot$Y`tKlO5FU#aJj(7;j%l_*ZtL$*(+cTt`miqzXh`Cd`*ZgAfh zT?aNBtQ?NNL*+ze4$@fdIZ4`)ZH2&o>*}9ASa(j=XZ_Sc)za%UL65dbe@1hVCFt#c zWx+eidyL=5ea=NeC}bwZxV4W^I`0>lU%#s0oznZL#?zpmI8$AiPM5Xnzjbwn!0aOJLDE)VQ@~EiAEb@)la7!HM2gDC?pU~bRkD8sa_HlW&{UZAC>k1M7iEvL#>UfZs*+NFit4#b;uP=D*QZ6W4 zv_FOZlUy6MR>xq*wZTD|Y~rBJztxZSL;JuK_B?%elux@;+Pvy3O86PAYpQ8x#;M(E ztXYzrBK_!F3*OtV@XL0Qeq=K0Y@#G$Jex=!&yZeQxV49?BxX4)c#h?#`KFsO%B7#k zigkbvIMlaMPCgy}e`movN0H<+AK|-q1^im_PkXKLtJ-vdbnP?DGcMBS(oK}Jj)4X< zIntgX?cyG%|1f%BZy$`#*+jp!8-LyV3;z8~A&=36r<>pCSGG;VYP)RsL&LerlVQ>> zJV2jtWlQD(`AF6+Q(AiUsTtB&-i3~-^roK&eX$(0Gph$d{lFpW7e`6g^BeRj-rbtH z%`->*o!6R?vZ|H0Grc5N&k_GT@&8@C2h;XdJvn~8 zVBWs4z2GP8>#W~68$g~oRpMMA&Rg9&ZnUmVE?8YNchsHpaS4Dzq_JW@G|C?+_;=hV z$riu8?x2mL!!Tn-oYnk(a1MGcA1rw9aC|nc6-bU4am6mbsve>dP;tv!f)h$4R3Nt%|A6uc)l=Jos_FWl&Y z^zglOyd$LZ4*Xr>SpUs^7UZ-bA9UJMCRmvvq!DX zo?nlcy{g3j9Js&U*U5K7f8(bL-uy*0F{Ic_x8Q(vPF2TFnC#vh2JmI?%&gs6{bTz~u zNf;{pabi2P+71`IFFIQHYu~6_TaL)z7sX_*Oh#6gL!_T0z3uU`v;5Ib+`5a^14l0I zT6=?hd~g<$T98nuj+QRFJorZi|ISOJwW?j6BPP;=$+MKAt>q3wYyJxbvum-}qm@%6 zjZenG4}-rz-^-)=oCItBV!@lfD4#f+K>p@Lw$^+x2d$&fDu2n@HOZGz6CID5;0TBt zDZ@76oF>i)al-XF^NTXR917<2j(4h@X1x^`#j+d?r*I*zGX5siVPA$F9ErHi@5g%R zAB&fr=Nm37&%)|?@|jlO$H{kzd^J_nqxwD#b`;F!wkBUzT@#Y8=1;H2Gm_UEpMyU7 z$A!olQh}aweITO?_3}DLx(U)%{-ogjfOqKyjF(#&czl~VMo#Ivv&89ou;7g*%uB=Z zQrkG|^bymca-?4-{qUCy-dA{cd9cil(cRaAY+i7VIOmD8&hcKfFWVeMefdT|hoI}i zY|<;bpNoCE9I#%#HlsAubmnzsku-|v}m8}2BLSCo4ut>r*scxRTDKiee1?XLP zxDdG$CA7^oi~*JObCab#i_Z89CXApqpVQdZ*2{SRwQyX_fpvkcfSDh)&%ba9{F{=; z;$UVsaVo^ww_J$a5tXDTn4B48?KSo8#0nkCq(7GCauu#hWt*ukSvkx5`KBTMzLWpa z8=7<$nID6zdkm??xQ)h@&T^8ncz?}WK%ld%T37hCT&t;YqA*KuOJ|p^y(_zX?PmV( zBdV{r_5KREW;Z3F4 zqBOz0Y`!jySq_lJM9cje^5+a|xb-%;P@iM&;rxl^6ZEU-x4Mmi=r$N&AZYwDK8I+LZ0;v==}%p{+g5<54&0Kb&ZC^L7#Jd zg0j`{&CG(tOS_@5|Nl5S&%V}3d!qa}W}q)wy%1k%j*zBu*3J7bk_J7@=305uoF&cr z@4K>E8pi{*}_HXnQ=V~N^mj<~0YJ4F|a=-XzybMyDSH1@jbJMN7Bpb;Il{WYD_(A)6Cf;Xdi zIDIvF9FmQSGX>-Ga5eeEkCX|VUR$$tq<=T%rWIUTVJ0v8#>yt z+xeZE(OP=c4&p8o*X}a6`amZ&9}S5DKPpXr7k~K50Uk7J_d}#R@!tjS^UC8gXU6DPC3r(W?fX9k?-$&dojezH>yG)G ze*t@Gs_2!Rd_Ygd2hQn`I3~$CGJMBPOxY2uBVM(8Egbh zI0wG_SozVyBf^Y4AN^n^z9fa%xz_U;n)qemC(_Ba&4 zPJroK^(fvb*eS4IFvOpyPJnrjDSDq3h9sk7V=UB$FRmB_Qu?2Oxk45cwkgx^9 z`gvFTGBDsxArEe!e~uBpLb&bIApF)yxM-gty!B^_CVTugdR5T&Ed$t}2pbe_9(_YQ z*fy}JZKxY;2iP?h%a2z8n*&oh^k|G81=|bu%+>6k{`%sN&==|^a_SH2Q*%n+Ui2R2 zbl0f74s9fI2u`=sWO0dY^FeEW=uBK&^bWhe8NH*{jU(OZ7rIhJh7ER^YxWT!^1%_k z?lSHFq$1~<@Q2N#O{X?}C^rAl4#4zYNcW&4kyq3)A}HS!^v-l5JL>7h?20yDK>1In zKPvugq-XoJG^Q*=Z%0?rKlj1pYE90wK{^;ninUdDX_&TGp=FT%`-?^YK6>k;Ti_n1 z=5UC;s#&b1H5<6Tr*)OWa;VM9q>H3t1?QuLl4@=T6AX252_-mNj; z%8>d43PBfnfdBezZ@~g_)^99&EO*x8;4GxW==_*$uVE(U`w^TZZfg&6M{*qfxb3Vc_!*`Ocw;6deT=dV& zvhUM2U(_g#J8~d&=aGyPwTrl|*U|Scx+l%q3RwQrC9ny+N_pRs%*E(_-Q=^Ed`{e0 z^nR>1re31@Io6Ey7on@3NpWoiEfZ~tH2ZHYdQEQq^&&b82IusTRkWn4tU_zUF#gG7 zMQ?LJE3}oxe9m=OksThL@SQA+zMQ+9SrVOZ8be29Zpn?NvrWGJlRQT5DtcoV?e|W{ zs;;sr7%8%W@*Y)r?{VWR_D|iFES5S6z75>knZG3;&VtQ=J~|C~&U8O-~0x}9AGXBa>y zrb^r&Wm_+AzX5%7chNt4i&)dC7gH_Q*F>1fxjb=B66alfqc0z23oB<>oRxU=jslMp zXY}nw?soAmX=ROV}Ga}b^NBe#(`-!`ixV1I4DF4X` zPq`onZQWTPA|nM=Z~cCSv@;JBgYz&#A6riO9Db0df5>#e3F5E65&JZ8F%w;CJmQpt zRO$=a(_C17mXY-|mZX(b`U%MbA7k#@Mce@&3Hp-izgAo-=F zZ%yWWjz)L}Cp0wOhHSl_|FvXeeg7o%bMK4JF-;eu%i@9DGn&m`Z=xvltte|$54wQO z$#OSg?}vu1ds;hTahDG>ldC+{lqbCFx5GZ-*1OT}Ww!CB?d1&8(2->eYk)J{nVjAU zjX7wvKu=GV-yGOpF!OU^vgz(!!#{n7^T%rEv1$Kl8+*OoMKO6FOO5Ns+^!uKhIX~h3MejYlCydcD=!ZhlPDBetqz+hyDZ{dXj{9V=zAWevk41I|?@E=tuppgZTk2E_H)61BB+aJ4xI5@uK%?-aQ@L zuZe9zyP*BD?tCSq~_;bpg(4RY0 z^d6(}G5o%$t+QKfT&MF4Ns!caOtT@VK956h>mL>)>nk!}6>Ki2G3y-RhY9a-bCX>u zI|o6EKg)ySFPPM}lDkSkP|1GKbsOWuXN!^hR$l|%A=4Gggq0K`y@%=ahcpB=D)s}J^i$H= z6IA{Rm4AZXz04gH%48J%aFK6HXxmUP3+6O-hj}CUP3H4H46VwSu^ZO6Bff)?{k8P0 zx;rT_c&Hq^Njv@(bfkp&Ky2?Q`z_etk|5KhYgVCk99olqTJ-iiT2b2xeR0VAf(*kO5P8GPg8mhrW|>mYfa{TjRw$T0JJFu1E2Dz>b66ZHTW6 z4uf_7Ir9Wz)M?ZXY@WKls4ir-)KoTqZm^Sd2S`_|%jn!S*7ow5`g6Xeab{4~pdzNg zI!28I&4ph7>co%l%gCPsj)_i8D?gQ zIK@z(yFj^C{-)^twBzB1IW6BFS=)TAL`$P4QOJyl-Wcn#e}^4=wLVwt542MJL?ULz zpJ?n0MQfjV=pOk7y1%2lN3wbb8HP-;_R+S({ILR&qca{@ zd92e55M+7M?j!Bgw~F39fsBEx_i!}~I`HPePgy$f5ET#?8$~#;RWb7q}d$E8S-`hbwkpXXv>fwvpw|)#hEo2x^^Ky@!^v!yPG^3haKuT2d$&f>iKrj_y4v%n3_eha@fy*wCN~u zP7|m3bkS>deb9{wEM!Obn~0gJs>_&MMp0#2ALm<5GCQDMcp>ufyG3uBcb}(N>UO41 z*#PuNHcSvNcP5^#X?)g zLR;d0X0EC9k2T9}Y-<$!yuKg$$I_)>JtI2C$kU6?7@ZVo4K}*T%6Q=%G|FpB-nZ-f z71B0(M+XNSHmAE1n4y??eIqYO_s|@svITokOUZkTtIs-H1r~5=l;|4ej5JNg!=h$E z$VB&U>ljt&%t7bS6(w(KJ%6lPk51>N)($jNYmL2)O)HOLYYH~+S!&m}fnQH88$Vtk zkL5>~f_b-it@SSI7wjp#E8pn8#an8VErP`Ny~I`eCn+x963iuXtm6-vL^WO>C*BF- z*_mqaxK`|>PwxON8G_9L4A^$>NE9!};q>$Y9c%iWEB++OmwNpAS%_#FzcNjG#d2W8;k z-_#@gcayE_SBO88D|tu7Thsyb06uESUoU@^Hl$piN&jM#UKphIkZ!Zd>D{F3xRyFn zKDVnb-FX$=DNev8V+~IaKEx>R@4w3t?FeaFpHlM9h}ZnG`D-(pKO*;3M=QiR*;(?( zJN{N1p935H`I7Y~zl~|HU1?-5=N?DeSaEaXtRE8B=H%oNj33=~XM5OLg~Pc>BN|Ka|r_$XBfYs3Ck@zdz)_Lf7WY!JJx>a?tW&;yVdpAXrEG_N52bg zEeZYw<9x?yXzYJh$vbdSxn%uG=P5BFC#X71VSiEmtWgQFbv^N4?_+48<|BiJS;7Q2SZx8W&DT4KcAPYwx0Sqm zT^e|h1ugirkAH8lD=bYnxj?*a#QROvtF^83J?Kt9o??l*S!>Kn?f+rr(s%aUi~j%o zlJ^C5*o%LsG@SHHl5Lk}w#h?cH}nd3l>$8}DtCry<7%{x8Go*?m-ALv$Dq-AcgZV2 z&;K55WmZ>P_!?iqktt60Vm{3G)p!x(A8CHg$?FFFUkr9Mrk^3Db7av=+V1gEuwRXO zmOKa!JF5_knrO5>eF&OM(EOIl%p;aw0^9$hlD9=|$&=@I z63lx^$-lF6;3}_gI@Px;y=kU-)1Eb(?q9p;j}#s* z94kCjc(DH}{0>Anx0UsDXnZ}A*G91NM9KSyNM2>Ij#rfYvvMz<*YlI|lCC@hjd^I; z*uy$C#g$yLaT&dvLT9@vcDCmMbi(}5L&XREbprc-((Wbg+aqN<3D)|mlJ`Vm7pK>U z;`f85uN5CDK3qIjbdZ9YAj%G`a`+ThQhO|5Uv_eW~EuzsB}BBV7Q&X8C4>)beLyyB0C`o05% zZ6vIACRaDu5wJYiD|z?%-Od6*iDdwAV}`Wg8t<2hc9t|VqhP$cqf7#D1AsD)Ro=9<4# zzrp-{kPaJH$04IWegwL?*O$C6xxO167i1{-c2q{NWPxu)j`pKw(ZCoV^=OQ1ekt~c zHJv-WI^-zy<_ZyD3RI!?c{W zRo{P2I{gIk7K!&_<&!#YmyK#}vJ<@RttIcBbk~d54~B?%{jjC(7b#~P*4#wxy#npw zT_x}Hu8yK}*+$!n?Ho=}zBikVvulF#zYSTb{zN_5?Hvp&GIUB|5)W61bMoya?~T06 z$1AnT56toB1Y!Aixb{*#AG2ywa=90ot~jgo?<`r_Ej{C#@jv*6nXulK*AcK*u;=p5 zy$U%_|KhM$#wPweLD)vZYWMj#J`Zh?aFx6o>uiyXZt7PXUWRP))~)LcqdHg$>q0p1Vw6`unSjR1 zZmF0!eN4Jwc^g>?Pjexsty(-eeTgAnF4lQNbLnuI>7@elCRZoisMb zf|lr=g5K8ODtXNpoijUo4!X)i(p4H~pth#0w%Yg#^sC=31wN2furgQ&SnZtJhJF^c znXotWfyC*Wod(mx5)eyLQRClcJGbv3dF19xo|-_9WXd8~JJ{0%(5LQ2P1wlm3Gj{J zpAybIB+%P!U6#s+msy;+C)+moV79evi{O$MtxR-|knVYEBkJ)1CgzRvCs$Yaxv&C~ zP*sIYp5$ZDTKGW8_xFTWRHu_=yb_hiY=4Qej1*1vfq7_jeX!&`op-)3{}NvfGFg0t zusmV4KBKl-2J4Mrs-sh2JrPW#MEe4GH~5nUpc78x)7MgY(8AhYi9Sl$FDndZxM}S? zxZy$Lr;UVdCG4uer`G(VkUj1Wu42SKw$POBOy(yWQ&i_Wq1U-k!mhy|R_CF;Ewt;~ zoPoWGsc$5*4%{hTUxwDf&+tt_*~0x-AuZQ_lw*@slN+^f(R7O|CFUfE73 zQe&(ght>qNx{o6N)nMUw!syZc%Wkb{HYIPH1m0qrxI=|*oP=o%QkjpF&+f05ymtis z-P$L~M|BXNz|_|g^YQy!?$y{szE-O93Ev2|6v1-jSq3`__BP&YV@6&lsjH(Q|HPXU z#OYpk`YnG8n*tjK)4Lx1zZa|wCf?N}>;TvZn5LC_#J@+ts$i0>ex7$(?@-tY!nPBZ z3&LJtVd9ZTY}fiw z$@`Un=R*E9Eg4z5Yi;PqGL%kiKAQ__opdks4t%5J-wA{`r{R1jZ^Bd;hg^6I?|M4< zErIPPjm;e>UR2h^?N;Yp`X?zdqHs04Rr_m@U(k%(tu}%muHiP0`FgtQy+YWLgfP|9 z1YwJWz5LRADPPI-O-#ZQ*xYOlBhWhny_HkU-vj-Z%5wKkYlBCi>Ao!6xkXAei$VEY z7^j0E){fqo?YyZL2F)IR8(xb%`e$czpe`?Sj$mz5A?-fWM&|_MU~^!0hBWlM`AvfD z2YU|h^=)t+rSm7~w(o&+T{Tx%nuDZSAkCc8SR31!MDXo&v!*R*-C|B!1m<+Q+zM&h zzg-Iax`T1B8 zVRo<)(od59B}z{@-F(N^ZES6r{xH23`*IT_V0=-b#Ki;M#G<1Gb0EkZ<#!Oev(Wvw z1YJJtXHI@HHeZJ`7^ZbD1@oQ4v`pA2H8)`p+TDaPlrxL`c+`yIF%hfRK{#PDAw6cy z7n6`(qY#~zPr{KGTZAh;&y+hN&7x;r;Hn_27dK`TItiUxz)E zbOXv1Zqn+JExPlk`a=idy9xh2=|nG*Y%#v0g}6WJw``+kupzSF&&kx!E}~aSd*Qn! z&o)b8)1^>0Nl(9-&fSVPe#<@1EzK{>Eb+&_SBjibi(3l&Tg98IqXohz2(QULVMoEX zg4OgO$)OWq<6u_DCZpI*1up(R=i)!WyWfvYZdgCUz^py>VLuwm+#dPM(r?TdvpZVTtts$#F`?97lGhSm6%e(CjjOV1eRr&CU z7rVe(&z8KM0z&y?c5{{o-4_>3S05$rcH&;ExIAi;39wl(8&9lFLi^5c!uHm}h^uzp z2R09OCGUFd`@!~s{UkP@Q=~afn%9cR<;(L#n@{%+Vqh+DG{={t9V?FKt60l_0|4s7EOO8(ui4m$`of36hlDO0>fup<#na;4*qj9))w{8IUt3(Uv|VHfAM)Bj(^_aRQ`?--cA zXgoll_P=3_bW`W+zjYhf4A`3>rN_P>>^xZQoN#@^99Z*zmi)U&t<5OZOq#+4zEs^> z->%cq_yS~CosOnS>qh9EhTb9Q-K|VbHd-Ix8_-c*A28dvZ|5&dHmYrNZ(==zbdqI0 z&)c_~yt}!^@&t*$hlzWZxW@Cw-&;)P=o_~Z*8E=~FRM&Dz&x-my!&OU?R~_$AN1ur zqf=C?uS2BkAzf`>UN^~?zzSfhyU3VLeX*3|1io!McjJ7HXctIRB~72wP$$#)>ai|2 zoWU=gCbW9Xy_xnR&W(y=^XOW=g=O4~dDIox#uR<;DCrK7PAsTL*aX-@44VQw2u4lV zqj-D44uI9cYw1TN?^%{3_F4XK znZkb&?6bkVpLT9oH#Pc0+?#e!c8Sq`8K3jTru6fg_=}78*yXd>bT3Lm<9yO|R>{q6 zEr97~=<6B{G)z(6LiAgRezUvr zH2oRffqFYgh5vOcD%q+W=PAdjD+avB*OwzU7q#!TavUc4-H_Sj%5kU4@p2L040;#; z>w?wPTX`7)xQYLjTY5o7ZLE*r9NMY3AP*ls;QcfGG}5lCmgA2h+)$3cOeba{i&_=3 zr^ut_i348dC-~O8;@|r1N_`T~V%cC04<2x76;~;rddj`Ba6Y45v+t6NS z4scvT69BF#jKx7&O^%q(oI{Z(ZBlP3*My*-ijoUZ0iIc2iLpsn@vKv`7dEx z3A@Sl&FFjyCEA})Gp(t!S0bxXlwy#-p0F2slZAm`jMte*ISi_;EO(VN+wK1jigAQ^ z%fx$~%4G7ED<^S1QoI2dZ!bZoh}T(!$AZ3X^K;{|*cyN~6sHbk52MB}Hr>kqnuywF zwj>^&Ai`!Mj`Dw>A{ODp#u*07wa<4k@8tg?G0jf1V<+R|z<~GMpj@GyCRQ#j=X@`* z(<51AA76sj_Q3&9vdr&CtI}c;jOxCZ2}Y(ai(i`!pbh;v)`b*h%&J%AOnsM7s>)V)L)$oXpuJ-66Y#U*dt{lntW~-w|k0C8_V~w=5 zK&dpnViH>Wp|!=)YLFTD9lW_-yE8snAl*sQ)pP^NtfOEjz?{r#m247z5_}~AKL>s~ zhO152?{3C`71q7JIR|!uxW{-`-A8SOHQC0@t4<9y2Z%Z?lywb~Pe5x5`u8O8V93vA zzYq_m_9ymI7H09EN^)%O*RKAAE+FNjO8#faf7{Ii-me7tqsy&&Ul&}7y(}&akY)dp zSS&j7R`RI49sTCk0q^T#EbC{huAd-lQ~u=%jbeeNng7lnW@q6du6s-Tmie9Fx01S% zWPV>cLSBn69SH8_X+cgbgB=4qed)1Yd$;Yru*PoASH2v(4c2kg8hK(ZAhi$k|00^z zY!lne2(1vxO$-*w?a)mxiD5U2{v!^&&PBXK;QIh?jL_x+=YTfnD$g;f%%kniF{lvx z4|o{@;MQo%s|v{Z{-C~H$&Z%)2#W%9q?&KiST5c@`405$`v$yE@*bHJuv_YGyM?)E zH|Ul8+nTvoZ*wr~=+k0UFauHF=wxwz!(=G`kAU@pwSuivXN<|zke{7S4nMyRlNgSe z{QOPv1wKgQtITDo_Y$ZBS}X1&YW0q{W|QvdOyKKM<{lEsa(7)Mk%sUamFRXC7^SGd z8wfNRc7gI&cMNz>x}5UkwAZI@*T^gbnU^Hu!LP7JPq-!9!%i9hM2dp;rBt;Z8Z<|ru3Knxvs5mvcdev0ItI-uG;6Xk54Hj}2KGVT z@ex3N(dX;elDoB7T_>*k{BzfjaY%spsACWFC1{+We_f)Tbzg|&Lt~Fd2u+ZD8??rz z2fTNR7W2vIJkG{pR;gYtXl9&B5hN)keaM25heZ)ct9#s|o&PFBco$Ib!onY`-} zb`ICn^Ao9g>yV%rrm$;;d&x%l*4*Dy%1l9i_zV-GO?-ap`djCYkOYKLO%bmz>o5{@A| zz6_wn;bD@^Lu2>>>T;b=BYBUG8#C#nZ&e>n)B=5MV`us^GvK}1$-xAjHLsmIF|eX7vg0ln_` z4|s11^s#V$)6i!t+Mk@Dkg@zkQ4?y;Ka+cfJcfU7z+Y>i9bfI*@jUo;a4lKuQ9ZAJ zH~JyidP97hW)5ruOnk3-zkg$(AHSEdDZ*;)A{rI29TBV(Y#eM7>~iKWvo8IUl%L7Y zn!PdHQ=xng6K^LpYik3-j)AE>wee5bilbL6qsns*>~hPK+v|VNrOHz#Uc5YGVDa*7 zbM&I+*$H;JY5x#d}Q=`U5D^TdmnXZ?E`%aa3(^Yccq%PmjUrN2~pb`vjNo_%2P`dM)FqV;nW z>~hO<)}_BxdD`CFSe`DhczN<*@%kA7yWH|jx%8JR&jI4a>*okqygbW}UbKErfn9ES zTIVjWzxNO?UY=pFczLRhUbH;h!7jHv^Dh0R>Su{~@$#Gmi~hOfap^Br zo=M`x%QFKOFVDQA7p49u?G*72)XESk+Xe828XhZK+wZWR6)Rg8 znBref*+yOZOOhwh?H#({Adl2mF*B=OR=&ox%ih;wsS81rOMX+e%2pqWr&n* zBlw9L9xGdgu#=Ipi3i7B{2C7mn*=)^i#G$N{G##Z!H&hSLtqWx52pIMtnYW}FZTW7 z#rEIz%X2B3id!bGzUN{ziDvVR?Zj36F`c$Y^|9Nfk7D~={#h4KHkk!5)yH{Z)O~1M z(I#@=EXEA)*5D5&?V^90bbCL*TAR}GXdUwc*dmzuHkkhd=c2asJ*c@}+dTdn9~|)h zP?61U#@sA!H^V9tGyHQbMcuI|Iym0uPFtb39eTD_$$jI|{Uh39z^BNEhWLIR%@O}7 z@uR$O5Nt7m$$qs6b_8rdb5d+nZcd6{&#q>7Hda3M&iV-`&Mn6t#>P0kW>@n}dXElj zs+*u{!mB!K|2_Kq{`xX>gB^@uYWo7%0kBK)iELxrT>6XgiOGNB9fD?q{0Bc$!y|o9 zGL(5$J&V`{ zwZ5Ho2728eayCfpC{g=@o%MoMmA&3|YvzSa!xdxJb|;`U0c7`A&XOS#vJ0@arbKLd1cZZBF$%%W+>>F7-b^I zYnnrI=wQ`D)G5;Set5upmD1TaH}*63LDcsu_%lcJBcz=o?aP$b`tRM?Z2V(fxAQIS zz$?T#MjXiweap12Q)sy^qiq-n0VPQE7ojY#cKx$J4M(>>TOTf&y6=q z*@}20wq*hLyt=Nfr9h*19(sk34tV9Cj9y$+X!+h`Y57CY`~AkU>$}DY8ztypo6k!tyVXEJKgiR4hHU^l z5W_mb_Q$YZu=yBP0aN*+Wf=#Xi(!*sd%+w#Ssl)R&4N|*5!l+`Kr=q-dkzqGl(4H6 z#v^_{0=5WtS^#~=`DMZr_+@|AXIXQ^_D|5@)t3Y3NH_U0x2_S&0dh>?fQ*ke$Aqfy zw11@8TOr=4=v&|M?cA~fs!Y9v=ML7}+bUq~5lnq-9IOrOP0r6Hx()#Ex^)0Nh{dXb zo4}?_nY8ny9s9(9cWMoPm~GkCNMgP_wqAsf40}@(jQ`pfrSCZdE$`4kuvc(|`YpTt z$qHY8fb~qmtxuxwadcZEusXIk25z8VmL4|ASz&U{4T0Uh>US+@s)g;Om9+DDDZcysj!9;N}Z$ZI2wE*4Za^qcKtx;XWYY?#pS>@AxSG5;a=nYnOa} zrq{}QBg%LF9l&kB&wAP)4sb(_|EPRjU{$c^MX)^B4A|Qp*2(`PU@e~=@P1vG?VV(S zn01Fy7{%g{_nsht?TrEq{)6s&)KfvSiiu7Gy~}pH5s?xgnE!Phuu= zyRf<&If%dEpA7it*}}6kQt)@B&1amGf?d`5y1u?~C-EnV|AEW-#)=&;+xUr$>}tzp zXq|j8Y)kRPDX`OECEg{+-4+~lGnS^@9x&7A{4Q^F+CI+w=V&PFI!M|D=7AYs8Xa4k zLFGVPz~|bdqj)34Yaw1=ke{99#Q4JS;!7Bl2Rn$@O}yJ&JkF0{5(iJAoXvvmBYYd- zy{Z@HU{QZF8#k=~>R3p(X@N6N;7PWl#kzDcgCvq_6bvpyItwV~ruK52-#d{ZIb=v1 zGNhHoRfsX#+4W!k*xCIF#!9YdAJ&2De3W<{#Orn67S_4useWaZB4_bPZp@IT zn>3f2M~RmY6L%wVKV8o=w&ogJI}|UkQ@_Bq0v8L0&Y&7t7tSX#xv9D0r`#uzCl7^u zps{5m*lw_&=UwB_xEqIt!RNuF<5(5!U@b1uMRPmY64+;zo<}rygKhtc(~-ygJ#nhX zdGLMUHhvn<* zf;$JDJltLr?o&?1b_0o4FX>iDXJagX3#)*g0ke6f!^Xi{z8dDKc#~kwF>D6R1GDxg z&KSRWuy(LHr#nUEU#y*Rqq>4!`n#{-Ap9$a`JdwiuSk75QC7*l!q+W6g?{yCflh>n zitjtw2EILptK8k-GvGB{Ls$W9H<)PZ5jG08GloroO@aNg#q#4#fgOy++Y5F8ESkpw zu>BEC?RErg9?Zsfle5SfxBpkCgZV?$KnA~uHu`ld{ko+}x^tvELAnDMtyAZBlea5c z8$C)`cmLhQt}|fgwvK+9{{INFEm+$ReS2cUE7(S`pE1P8c7Ww$@n*q>Bk@LvzaMNA%+8cd@}IFX z2mUi}r^JmuW1O;rAGS$y`G%=3E2$#~5#RvU5nI-;?zpkHC z?FXBTVTZxCgQ*$yDF0(%+hW)X*hCCF2evhawfq78;9#0&=@Gqluqs%z&AP!vZ>`1h z;}yU}FN%#ideQPtIC{}MrohHx`R)Z9jbR7CMq=0zuu2SD1{;oHr@+c!QQi4GSOH9` zpq`!l*8d^=2zHI+0&Uf>ZyhP%pJ|TAja1C&w4pyUkS%Wx4mZCsTOP_*ZpxPX>@>1& z{N19lnHAxLWZc+~AvU;G)XK4!a-2Rf5S;fIEr~}KMePNbHKY@-yhXIx>HB8J;T{N zTU#hR-=I#6_N(f~9WuY!aBrMsRcN&T!+d@^UfER-KYo1Mh<>73Frh-}65+Y8C z95S*aCT})#dvF<_y+Mml9vTam$fxkF0q+LhWeat-Aat1NW?a&wcAF^(x@h@k3i75D z_mAj*q#JQMc0>748dEAkg$Z^kGQlpmZymO8<;aA(QsV`QH)8FE9jA{6-^AJ2KJ)wX zV<%V?(u|^p7dsd{k!w!wz(8ikyXm9)WfOm(}*^_Cf zUPfKAh!iO4n=kFn7vl76wua!WMqEopC};>{Seoeu zYs6a!joOd3cREi#9iL}B|1LVX$_=Ma(}#09vR*u2$lRFi z^U(O5ld;KjBs+fz|LXJ;q|!n=W+U>?t!Vus`px&lwHlSb18ffLsk{g4OuVXH^MoBF zthN__BiIPoVX)WpuC{W#M%S8d4(BEPnR~63eOau0W=QA#8~(*g2kY{&SPfnK6E;rR zc7<8{52^jl4$7jl>8cZr&2MW-r$~E%w0%lze1>k9jc(?cB1+2|KJ+%8DHIpEo&gl{E0T9+rl#=&ZH67|b7U{$b3yKyWy z_be|vsBiqlB6eS(&oKbzN!Rfw$n$^4hdxmHHL~Gw?d=v zANA`cJHYZ_HxusH56T~3_Ikdo8)zTlQ-nV?$k*)1Xou6yvZS*<7z#&p0?EQ<((L%* zfcGyhO~d*B2)@8xzR;99k=V9_8Szj1gUpZr)9quT-nH&0d?R=dcn9xl4@q;??=W}; z{MkYMG7?R5W=pq|dbE0QNhR+xZWKtjlXRp1<$Pn1PtIO$---H(ZmA)yR-YKt!#^1x zkEs+U=Z-;V7CP@25XxN?p6TYh{6?jPHf8bzpC@kfj|RLkH)h1foz!ncGqu~L!%Sry z6^-7b)X#s1``_a<(6+)v%zQ9RgnzROL~T3;jU7J@_oc^aupuiR2Wx%vQuq&7D`t!Ss%oI1{DUu2Jt{LKGWVZNH21n}FWpy0UjW@BTOnr@L!+ z)wgE|_gc!{q5Ay7eMDyGF}oaRuk@z>Mn>(%{A^9C2YM%=x9zI3_qS4RY|IS(b1369 zckCdJq+CapCSMO|dkB5!XUf4^uj;)EtQG7zyvO;=Ke@{v%hlgUiQ7TkUd826-<$xe zfc=fr*Q0t$IG=WQ$CainiE4`L=ZB$n>TzXHwk*GG&o-H%Ea| zy*Gaa`T6*=_w58)VY!SJbA{9^6KRLw>fQT4tbdP~q7cjcSmWWU;4B!zL3q0jsUmWjLmXUpDS1@bSfm+)JxjyW?Ty*8PJ zHkl2#eF^*W6UyF033_FW7Pit<`T-GZ94PHOto@;vdt%wUD`@}Fm!W~)aw>)YL4uyz zmzkm-4nwQZQTD#+XeG}ju@S*V>W~OiDuPbnk5Rwbh;n++cq3ZSs&yj)^PTGbD)sY} zvUkR{%N`x==XBEHn*CI^12PZhB*-xF+$3p7I?LWkrM0y^inqtV>wx0AwfWSi>r38m z7BiM(A{H~hZ!JOZ6!ZoVA*=TX*SF5)_(l4?&N2CLV%ZohMQZ_C!_O>xi0&r(09p zMuD!16#awH%Kx$(%lKPk$|Bep*e2fnF$MpMqVEH!wp<~6hVYjwr~lhHYkTnP+9b6%u~Aq-RQ=~4X1?>BvOl-MlD%%%+L<+b zn(t3PkTU(IN_ZFHQ!X#}4OTAs2f2c|(Zg@&y`2`FSqWOQS-n>T`FP>ZWzNB~4rA%kVHv$o% z!Z|%v`fV3+=ZX75#kD#{-nlx4lia7+reXJ9%2bFtPP!%1eb342XdNJ-Q-`BE9BM8^d4ccNXn_ciWlXDWH!0D&Cr zf?oUA_}*W0yc+V9>C;i08Bs0%F!2|OU%NLlI!q30EgZPzvIndYl$Un=; zAJY>sC8k)_sM89HnLFzNC;y?fA6g443}ub_!5b}Iq9lL)`mqSBWz-(()7r3%{?b?W zb_U}=}`Pth1PI?+4lviv+?qg)u$nDO^5O*8A+V<^A?`M~KT`WXjdO%8#b~trIrbQ^7RBeu@oNWL1T&dxGKT|* zY7-X1l~0~H>j%o-ukx<%F*~(zeq)4n6ZRb;gb3)O#^xRBWirm_s)y{ zU|25I;UTaVu*(^5R*2h9+~|074r~-GKHjYV3+7AUml|)RJM<9uIC0;>yWihTcc2}E zeJ9;g{i3}~ez@ZE?WFA-D*JXJ>#L#OVSROo8qzvgG_i55Z!b7R`Z>~v^Ocw$ZG7u= zy?pA+NjgQ9a$JB;elzvB8vjSf!OH4KMX?0dsP#jCf?xGG^*`+RJ(NpUPxh^pHSSd3 zC%vRjQ1$6Vi#nvpsd9DmC-SQBTPD9T^80<>@1>p$|v(#MEaPqs%Eq(+kY><2GdZ?sx_1>_%q-I*Zqp{qy`{dzGwgsSiZy zu#IahO4jJ)D9<64=lZhu9p1fR^|{n826enz(VTFkP`^1roSiq6>&~}012zQ~-A~~C z75Zrm+W>YL>`G@)6unNcLnFun@d}TyUa;0%%ia^34Kl0()_!~0dvYY+I9Sge#4JZ3|uqi)K+4Z1UTm%X>}?vDxd8@r>C-8*g+7|LKxa=y44 zWA>BgG-;&c`C|-<5(AZZaFMX~@p9nDqWQ#eur{#ADL(5+wX=K9fafA{CC9zL@zZ}? z?|kPToayP?=)Zw~LU-zwjsEpjqW6+^7J7CrIF)*lc`a%#QYWE?iaKH^0k7H3=gDLE zp0f8gm5*nH-yyIiu$ql0Sx=J60&O*W)c8@%FvQB?B+3i@X767AxAeak(dL$u|Iax8 zb%1XLKdF9h-)Z)MB)en*8*KXRvIjJJp!H=~%}iuC$m+Xh$Yyx~HIfV502ZAVtc`AM&!D)5@5z3*US@b8V_A{SGm6WBIvD z`SndnAD}ua{~h}iUV&{lsH3n?#_W-tWtdJKS*0rM^sC0auS0e4?H}Yb^J?a-Rb5P%^ng&6}p6E(gkScUQ_ly#JlRn)i<z+bg=0e4o+ht{b=V?n&j}U1Ig@^dKBTc+n#hik&lLGwcvso`GVh6P zz(6y-_Qfunh&yS)``@Dvr#+j3Ih9%73fd=M%jB!{da`?V!EQUZJ$l8 z!Y~6F>=}Fqn^;l+;BJn6*^9uUeN5eUmur{!b zV4qhc9${TzD_}K$SdF!Lurpv!QzWXQ=086Q-u(M6F1Y;5L}MHHdhi<+pGV{SPOwq1 znjg?~ij~%WUyq$9YzJW`SBNVbhro7&eT;WK!j`}~J{H={gq;M-gXMYGBkU~L1lTJL z@v-KAK%WA;KZ3P^jU2=V647J3z?MH=_U?zmD&xP!nY`Wn=>P|SKmJ9 z{Ui1J$+CBi(wW@Gz1y7H{B!SfgwGHj_g(5HdM{zKg!Q?!fj@!mtAZFadO2$a6O859IOmh+eazvG}s8( z6BUU^*afg{U>dUZ2y6Wo`~g<;o3Q>5wiWCXiZrD87V;Py>W#WBLTG+Fjv-VyS!c>gGI zmx#Mvajl$Y!=^rEP4eY)$?j|O-!=ADhjtf`~%H5sVqGDzHzVQTJbe?~ro>0OWh?*MB7i?&%0 zSaXctFqjvKS0-&0?80Zu-e@paHQhTlcX0aZw$#5T3ON{3&L-5WARRdZ3K&c!v(PJSiIJ6BhMr8G{$v+^?+IXu{UoEvaAEV7rY8?`?-woP{2@o zCG3U}wEAE6zH#CmAl^rLujfy0YigtMiD%3IZN3#0!w-{Ue*!H2-cS0r&zJrCdcwW{ ze@{1)ono5V39kT3!7FJbi%ya*N4l$nx{v9wru*6WGQ`z7>EBA8#M_#SzPIZ%yn`h;(Jr{Ur6%fOauKRN=X*`ts7vvdf{!M8pV{|;TGk)Nq6zj>c0c{M#_fsPcB{!cW$%AdsYhQHs z45Mdb6ujZ~nsS=94sEG7eUHn}f;YS_nU>-UY6G?=UZOhBvq%T5Og$0>vco8q0lOLK_5 z#Oop6J-q9?8s_Ex90HR_bQJRQSSIbnCGwCgJx{#t#G`2TNDp6s27L(3&gr3i(-^)a zOBB{g*xrP&cESpT9VTp$cYkeq3p&bRhAlHKs>5-@PZR#>dO2k66!sY#14_*8V8(>z zK+zQsFF@*X9i8`?E{J7Ytc3F)I-xxe z-CpRL&)|(w9SnYUeO85#Y&%BWDdPTx%Yf%@n>Z(o51U) zlh}CS+Q|;G=xLla_e=Z^+2=$p!HdwJfc`S{U#UTuO1_BSweAx!;n|o09?Jj2tp7S@ z{iFOZi{m)|+f^ZNfpr>FaO_qh>k+*F`2GbtC`lZjN-bMit-A0XN`>z?h5xlQBTw z(EWcHKbFhh(}QmaWll^U=o`|HQ3kh>fv)IKSzl!T&sv!W`1{Q+<5iA@_sh=Wea!BH zMW!Iq7!#XbS0i!11Mc8|)yI9YzbXGXzjJ)U`Qw-A*H=ybFuQe>{fybIT*YDbnMMg` zhunm*$V2jO>@51xUte6_6^OT!cw32ggm<4WE>frY)Lc>&utgC>Z0lzum z4;wGSF)8drx{HN{&V)%l`au<1`=I50qwLwGh1OrJUP65+N~>!A35`ut%INcm8Dk(O zZqgCGe`CD(CToI$tt_nja2{fHrt+r$M7I5;Tp(4^4|>Dj3GH9ec}>_qYI~7x&W>)* zR<~F)l#`m(&ZO86%}Ho}C6G(8GS<~Y`q}EiNm5|7ijZ_@)C`K5D&Ld)0sdY8QufZ) z=N)SUTRV_l^CPR&&T3g3f9)iX(KGe)usN_1FtL#y@!UbM3RrD_k#O(E9nczs*3`e3y+2K;yKvk$Uf}e;6k}@wXK`-9 zzE#>s9y19IZMGBTf0F!{|D)`Eu0DV3i{|HNd~=EXSy|Y-TK?+2jWrqMKlbm;SI?Kd z=LLFCs5^$^skKWfyDcRJwXtVmZaBJH{#x>A%-?hp`+o9Y{?Grt`XBob^obw+@6^BK z_DS-uUI_b;u(M!eVA>L)M_BU@>3^|!ZD1p@cwJzXSiC&ga0F8y8UZVV;R1T;{NY0)*FOr*OqTn9wF=mVdkF|wOKMB=qAo~!p{?Kexx=JV%}>$ z0B+XHJhYs7Ma><+@;E@6j`Uz~M$u|>q9pxXZE~9Q&)(m>)GZ5;rMk|zSfeql6L|R%f5-ol^q1Uo@&^s!?>Z3L%W%CswG^uplTTIBUm*Rqb%Wk36XatkpK5htHwh(- zlQ`@q)fM#B*46)le_N<`-XrrhRO{P=&naZ^rgigQwbfqIERiN@&fPGcRZ`zcl!9=) z@?U}0!7F~U{5SlF`6_AtTV)R0H`L)mUO|bJ@0cb$%<`fW9#-sUNnVB4zAFd4V?iB< z{Wts;t7Dy0_PEt_m+grkIkf7`X0;iQ>h~OFI*}QS+u6eJV@wJ5wXqN95%O+SAp0_% z(8^!Ho`2P)<%#;u1aYT`drJ#{g!-)I8_F{Cx77(LDS6B^=4;bc>xW06H~Z*8@BImV zI@S;6w{=)`+ept$Q>Oc@|8InB+n~2TXsei>8P*H*l2v!DoydkW3az8iI?ucIGz4S- zBJM7K4->q-XALKA+`o2Hw&#I$>Cv@cD?U07TE^#1NL(tkXb zUi$D6<=;8zO{l%xBf7`Id!9Pzy?z~kT%ugV>l(|o;eVKKJYcNys)iltY%Mo8cD zoI$S?E7t`0{BsArw@dUFuJZb*Fsf^&c@tGdMLn>tbk*AQXyz)f@U_Atg@+5r3J(03#1hVcb+IJb3qS8#>&B$BSEj$0N~TpH`AKLkZyEG{N%dv=%c}f2z%}vd z<}22FEwQQ>osOn8-qK43y(g$WFHI+Pwvh~Vg#3ff{7VPDaq;`5>7;*oZ6hJkIRu>( zucVJ&x;*R?Zc6tzCDXY8oz_}}1a!I{>!#b%VApt+dwv!4|-BE}n47 zu;WVq`a$ovUHyb-kETH%Q z^>qdsiys{H*8}-mPo7_MGwt^w`1>+w{{ZQfRK7Aa_J0bUUv+kgZ_HlVNJHhDfyVsj z2fb$37C=_M{on__fbM@8^>^JW^>-Q?r;ZJJpZ%#zmv82$lFQe%W{p?<#-Q)Rt-|r- z`4zwqtPFbhO0Hkx+n={e{q2UvsqYSYZ@Y{(7+a-%k3l1Ue$abSr2ZtIPpN$WHR!!Z zcS%qgIgwVLsh{>IjfbhdSFgFRlOmWifEEJEX0ddPdc%9BiE ztD}+bR8dm*)Dt0@9?@E_i@dKG@=B7=eA|}BZ@a$k+xD+n_1i|Fwc+X^@3e?BRwvZc zoGaH*5)sM4eb88j#z`pvJb8W#VEcY%$h*$f*;r#4-&d=%7bOrD-E+`5+&1KWtc5># zgtfG+@y4Gx>|^p6I^SzW<6f-cGP7*LQ@q zgPnc$P~cl4J*XS(0+{5LKW56a(Vy!M6Xx{{d0P~Q-D4MLFZ*_laYmit2bsrn(9{q+ zmr(SHBA%NfO)qI)p}L68iDL6Z-HrU!#AzUN-73kCL96)4IROX~F2Njw9R8b!>%~mieuyTh1b*qi}y!&U) z4-@@7Y0r`NrD|_vcJf-OW=^;yR(j>vX?XT_g@_f_D-lS#=vr5 zPg5S0?;`s{hO#TIHzw9Q4hp%Znk!eCXmwl6&4>Ib`JDc>A@3JeapAr)^QmCf$IctE z*>9aoMlqwWi~3pg)vmpl-lruP@Lo~woZ zBy)8)$#+9*q&(!GWoMOjOiq9fd|Jb{7?ER7$j|rkaY36bltyTv2D043oZ*VUb~y zQK6zu`F+05=Y0O$&;2kv{qp;}j|aP-d7bxppMUT3KJW8B|JpR|mVYO_YOc4OqQ_|9 zBU$LZ7W?sJr@yaG{d^Ft1?-|tD}G(f`hm&IPr{9K6nj_=}~|xCA~4J}1Dm zhw(L;z5Yg*u*l`#eO1u)S@Awj@v5&m=u~ueI`6f-!ux)x33K0G;&?=WxF@PqqTG4w z0KEF(^|0kNp?gx!>WtMW=S-Tln>M(|t37pZ=wGH>=ZU}deaK5I>F>bnNUqbF^7CTm zgtY8vg?8g1dZwQTcSg95+QZNve)K_ShPdXwvfy0{6Zc*(s=bj*^Ywn{SD=5R(t`ee zZR@))iJy3)f_`cDog{886)fqfhdjwxk++VFMl);bC;7;bF=P?I>@+H0zF?fXB&l9@1ltwFjO0u1@DmR~%PtKOai| zOuP@XJw-dm^nc@yHon>k(wXe;bk_}x4lsUj6fj4S?3)o?WYO~#t34NqGfkX7;alhT z!ec<Gv9-gz)B%S>M z=0EUytaN>#3*&fctutN~vgJsA-tx6*w)1k}Cq^R};cOwvWzx^w(CMD5ApLNE_1@xp zb)<7Dp7VnTom2nAcmtbQWCUvIy2BK{&A zG`GfzIe4wz*lET9*WU2fj~GI&oP^&Nkxt$s&L(j%9n7ze|6T^$0J}~IJwo^RHS$^a zL-K!hx^HC~Ii%m|e!1p5eA!d{Bhc0MmdEb}Zvy{m@!coB@^l)yHt4)O37Z5v0;Z{? ze!|X!wSqMp#Kq>pdPA7|{mkD%7+} zC}=CQL9VGYw3cDc$`)a<4!<+^bo%#uJs`Woo&z-NL%EPIxv<~1Q{Db<^*)m1PX!pZ zhDoFCvGmthbUNSF{MOWUaIT*_;3f_tv({gIvS#r>^3UVju055!H&L_9+W{+_ZCqog z;Kl(%|HK0peIfrp3I~!&^EXerORwql&$yd>rRgQKqwcuuhh`m`MQHvlrau__16sZ3 zFq=+%tb9`{^QK}sY8Hy%UF7dbJ^k%+j@tmX7@=zhTZmv8u=x;{CNF(p zb6`Kix5m~%b4k8;`>LDY)6l5=s1oyQ;=hw%%I}kf(4WHR7;o~d3W7A=E!ghFg|$5c|ekY2kCzTx4-*WGx^i|Vo-hvH@=Z->D|e>HjQv+*A( zZ)c#}_VN(Q+d1%E50B(+j)#Pp0#($)|H8da%Zz-?07Vs($59LjLra)LV zVRt3@htY>|{nfMcl24BxdRPo6`ZT710YQ6sz;i z+g5+JpU$`Z#^6^qf$vd%XH#cEIV1BBY^Y@NP4WE9_^*0fgPi)-r&xR-L_-g%o=pTDOv?;f}Kdz`}=3}OI9lGE! zrv4fKRJSvvaq8WS2Ywo6ZGE5mNW2+dq#D`lO^H;VZFn`m$NDIdbIciEAxqY))7amH zd$MliAM6O&O9$?RtcCcHeJ(s@%^fgIaYm310o1o$lRm zW=zB9Y4%34->4pdt$^BpBbi))b`n~1Ud!yWyaRjK-v33|9APcc>8JMC2HS{W&XY** ze5d~=q4ZlFSS#4KWS9i?Y3!az9boq)lHb&sfN~?sICfN~0r+kGDgGnA#m|oE-SR=% zljL+e_ub)1Xs16I$e?8AJlMGqCN?V10{CPI7q$X60T!0cO|bC@wg)yA!K!}@eG$RZ zV5dWv^hyiZ2v|SgrFI8y3)@Cn9lNmK?RrkyZ8~GO3=?1F`Ki+Qh<2YpPghx{p__%y zj7$7mY0rW!fc>fBzCbz+y=8rhW{yicpwmwClxyZ8rnEfe+F_q^HMYw|K5L`aPt5#9Jla z-zc8BSBrdx?>1#M3xBI7;(mWVk<#S^;T4H&s-JQ^2+c$2Ui-AQ8JFha-#g#w-zz#Q z2N@IhH`P?#@E}R1`CEr(78-93&OLcA)U^pYoPoMKHw%q(J@Wry=C;0mjf`DpEVpyz zw#5I5)g4H>+A9yQ4S0opH$|{@u&_K1gROy8DSdv5I}WxQi8~Fp0%n&u4c#o*B``B4 z5l{VV$?`Mj(~ZtRxf`9q(C*8lo6Zij+r+NG#u_%mR6 z;`9kIc{BQuyh-rgyh(nSp&NrPCpviY6qS+d4e*5sF8}2o_%67)HzXq0JQ#Sl5lhlT zxJ?~3JsJPypLZHxZ&ZibUJq%9*%R9fZ6CD5%1fZ{7>`CP0@_SU{p$?07omN93~itr zPo!RB8dAA&_t?)9ZBc{wdoq+!wbRKVE3O=v=@3J!5V>U&sH>{ zAEEuE%|qCC?5!W-SuGiRnYZ)8VF_gPy>wJB{Hi|E>9kvZrT0A-@ZhM=yOpcc`oX_k zzrqyp>xqABX`hT6e`LREJTD(;&Bf6%Lo?&N?2SFrsQYNAbJj1j(NAQ?o(pdDPI>ZkYsZJ`f=#PvMJd<4ZaBe1pGK*lyy2u;A=dxZwQy*#o2OZ9j8bhnh!Hxe!A27P;&%jWHbPebyBNU+z%E3v5wPU^8HIe7oZYa%p9R!;$rL2d3QAUa-E#=&&t^_qK3}wVnm0V1^{t1k&jfZw{+hw6!Djbs|FFGO(7nX1`l-B&V5h*IS6W`vCj#Gzov#&=cf^Z#*1iAq8R)+i z>r*!=xb55sJ_zpVG|iD(!3My-mgFBnJ;(J$on=Y(#LtmTzf#%8;a9O5q%CY3tQHKl zWq!hD!Kx$J5?B?Omp{?1S$@~KQBD1AgQcPK^rz^YX5=M=H38Lu)r0-L!qJbTbSfSH z&{H8N^cZU`uhU@=HWSCCMOb_`}M+a4}KrB{K9@Wv+jh$ zWV90NwqzL=y~YHV!ZWB*C`?^#rFr;shWPBkiHiG@_a{uBpM!tjKXy7Fl)y*IU)IjW z#1|=jxr>ILCmYOpCiAI(?sNwAZDgJ8CbuKC8SNZ=wfVo~ya~FxuiJUEu^Ei5#<64O4rBQNi0T6)t;;H--7=) z{eBSmr_1UG-SkAsN4b_Z`{Y{T*Vux;;v3<8Pl3Iw8&IhZTB|+SV#9+Tmxx#3uSnbl z;u_o4?13ULc8?vh?(Ko-q> z{~Gj5>9!rP%h27zP-Qi_|)Eirl?-=;M;v7j~xVVTHK)e+zsD z;&M6$ zYw`9jYGVs>ugB;$+PnN$vGs!%QohIGwfY}HzJ*PLt$>BcwOO!BVBvcvm%x@obSm>2 z*b>++e5($_I>^YKQ7iYBsvt7_rIxHL=IYMk!ac*30CF0kCh_X&X?ge z`R%}$qvERG>spzwfvcVMQyH4T#=x5Vw!XsLqs{D@DAy>qK8E2nxW}GEzdrUoUq!!4 z-VJ(u^gyj_Wa(B&Bc^Ct~03OckIk59!W)CeiqPqyT5~Aw`TLilVcAwS%@%k4X&K}R` zaSgVDvGyKScqdau_|`w4_P7SVe1~)<^ZSy$`*Mqirz||&9zjm>)M5#V7`n1`nlze8 z<23@1t^2h<@jl7n1bg$bjghUj}uYR@(?H06W zVtf^y+M^hZF+KP(aVp2m1{k~B;MLrQzaW)!uJ`LZ@7qSfc&BL#)9dJ+mM2`Bau^;} z)j8K^Yh*;X=egtIBw^Kr-D~-motX;qil020l5J2v@O7kKA7}TFhU9RUG@7mrWFE+j zlhsGjr{IN{dNT7S+QXk$7ZUf@^mWw~Z*uPt#3dJFXq_-Vg8}%~*XDeCN@L3iSRI(r zO{NaG{tMq8OAdODwM!KunV*5~bSQ2U*c{jh*wyUy+OYAjC!Rajd255g9yz5`bqxCs zo?+e40CpyXNtT+ylpoIy4Et_qe^+P!hbywokdbOluEBJ2xuu@DC{g!jo0HkkvOxUWAGBkdsY9&+cj$bj(Ov6=HxoXnvXgN5 z{aBsAu$mspcK1_MA4fkyZ|c?XqgBbg^jZ^PV}zOY@sP|aEJN6MOqk~SMZzWs`&Yi* z@dMi{d?!Ehd=DkWX!2`^wHz1mD=;GiuVfgcE15BL5&jK7M80f)3AcxL?n6fw@oM&6G$$nq=pRw?w=G-#S}0n&31e4#!s-P2%d;b!c{=`H7giF|sP& z@1!ci*2HmFN7c8pU+{4`=Xr{Y`h64J+ijh<5LWg0ob!S!+U%zMoEy4nAI;#(+!=T^ zJR#@&p?Hyw<|<04jekxPHv7b!|2}Nku9DvcGmg&HlX`UWGvbh0|>NIV| zxA<#?ShAzA%Y^L^)}SyW(^^$=<*NP!_B3Iho)51h7+o0XVJZ8YV++}lGWVVgz-#Tg zobyw*9m>XNGZ$z6VYUWF2MuHP8(Zumd{&-h`?K5MYtp@v?dF`3XS%<<%{rPHfUX|h zB5uWx<=p#PDdQ>rE`t@o#!K~mFfT`F&sN&A3&q}8DaIUKuNmyF8FIIiC;lk5B2J{5 zm1YO}<;QdGnmzeHD}6VrH>!1#(r#vutK5F7|IH9*ojBfHM||eM*1+!N+nwvswatDH z9Q9Ia^0+_v5PJkilV_1D@!29yp2{!Fq?02P561RY=vYU`W5J3ZRx|l0W-fK&$6z<-M^i_p*pBP$@u)#QeAi$tTuwR zfmKJaUa%@KU4^2b>TeLN0&GbDdM@0bf^|8&PSy6Rnrv@Pp{FK$w5AOcGsd#epe0?r z2;bB2E$-JYMut7U`}o392+i=S?!+JT^kA$CkC#%F44E245HQpZeOu|b5hwqQobx{0 zw{5wMTrfYHI7sL8?`tZU_z8H;Hs?a?@}8~oa{n$!wapxK%g{+z z>6hkj8EgscT>`L&-FMDe%(=yEA29l38=AJC%sH(#uM>Lf@oZB4q#|*@uS7di_lCY< zr!<>#$p25}{C6gW9RX_t`ysyd6IKArgN5f)17KOOuy1+?K?Pv_fVkyv~Bn)%!XAB0x+iE9&j`r{4?&VCYB>U#806n+#` ziCg|W{%hiRdMHiQBVdzYIq@-d&VCSf*AbmpXx;iKLbCu(xITu#=D~hI@%X9i<6ui* zUVm=_n+96}J1(NYenVf5R*ZOZNRZ+$L%SPEXB})Og6)89N3e=6?3oBw54IV>4ufrg zd3B(4+rZW%STEQbn6_!?C%Qqf)et|m&uK7~&ztv(R(fj+d>Q;s0ikh@VX|bL%W0hR zLFN>YuMqb#alfj#`|25cUv*pjtc7_o4~ND~tB$2^iPJ?&D#KsCoAK?Lfxay}4;G9O zayGIsP_7E{@R@?o1^9IAXD=9^D&uj9Q<7J6UKxRoxoZoX;@46-x7hd5EXC)K_Hc>*(S#sow5*eUQP@C^80JhGjU;atSyHRmf|c;Y?OLy4LZPB9(m-~~@T zxN!sfERWTUXp>B#X5?tikZYTtBfYtw#y_Hh1$xZb6_I^CI%Ad4v#{mbgspCmPm)kO z33c=TqauCCz8xyy7=W=Ks%}I-KR@TbGfBDe2pbCpJH?!*Rb3y3W(AtCoVI~oieSB9 z%OR{t+(EDNJ?sSsQ-#VDee<&b4+;IAbdNB-t?Qr@g)Cn;dhk{mN>_0lVJG z{sXg5vAprn-mV@!55MK^Kxe8g=E0UC*d?$~Cvn7>_@p&LEm4z?k$l9s>8gpbHmC!I0?zrGvndP`SL z`VMPTo(4YyuJY=4n7>J|2{5Ba@#{=zj%@cpzSJ9m&Fwa>zjzV)i_rhA7*H2wz6IpU ztl^qFoY+G{2K{+QR_{4Q{SA7+DBGny*qVK%VcYmZ^yv$1`xvK7scy(ZlZB=cnkU&h zi=W@wzBQzqX<~X1lRDMc6uhc#4CDsL=y0^* z>nzw5m}vA(_f!X%Kh*%hVF5P4I8HC|?D59KI#zyqs^xt~sh)gWzM}mxP-# zYp#?@seF0)WBFElPAdMbId|WBBo8``<0fu&jjMKuBl%k<-T?8Y_*UNST^GST+~{1p zkDyIwH)78!mbzuk>)lb9{8xK>qmy(5fvD_Ljc?x_;ZMgBSQQv&6T?p}f4 zr5ER%-&2~@on5as^=)*wZlCHPYS`&TRFLoz0UjB*uc!Nw$6}y!)Yn_Unj%;htTBT1 zgQX+b5LiP58w0D4VCTT%56KjS&c3^1HXxv=A1siZ<(^IZ`qND{zONVj=Ko$;>&W*Kg*zs zOFQs&W_U|uHyNwsj+$|14_*VW!KSn6j>_f1Nb?fYH(8P6!4;J^{bJ_7uf;~R{nMUH zCx5=J)s~=Jm)KgF6t2<|#4K7$mkh!0^y_l&TX#m6cxwQ1Dlj-vT-O2Vers@2*)I^U zVkGDMl^vVHepWLdGJaO8-wVlJneOG)e$td_7hdPyfUm;Z=VANKu8S3tU2u?o@vS-b zH!@_E=nYy?a+xV&-~B@ASlkBQ4?YS0UR!s@pRDzcW~~?18%qjBXq2e?4>QL50jjSV zcx>OB3!RAx#`B+d6|NZ`tnzhD7UHcDZ}?3)|IWggGRD1I3!9Qc%gt`|N7L=p|C>u~ z^dn%kU~x8jA7L$o{lBu&=i$E!|L=#5E}c+y2mXt<1h%-a2CyBl3cmFd)(o}{hUPQB z!~8b`wgq-IHoNSLVd$$y?LIQbboPibDsrZTFzL=I!n1^biEr1w2--Yo=i}y%m_=wW zL;Ghj{i1BIdt2h`bc;B*gqgQizl8b7*&J^ox#@=cSisB9TbZMOGQN`;ds21P55JlF z0-IRa5ZE->?R@JeYz%A<%&Svj=fHMDm~_!aupO|w)gVUyp*!}|y^9Bu5A3G^i7uUT z8Gf~I&pEH&6C;VAcCe>YPd5Bkb0DJuVz^eeSDR_;*Ye4x5&J3_F zJHdUi-aURVjbuwl;bg=3`E-wuz(O*&1w2lm0|_+7EZv z!WFlXu(d11%@AJyj`FzTTO_Q3FzGTk-JdsduCUXDwGk$ncf;;7VWK@pSTA9BD~z@Y zul;MQgFg$QYq(Py`X;0+#G4@AOBK(IFP<%-V@-BNNAC^Wu|{z!?!v!9oDY}BnfBux z4dN(&t;Fe_%K2|(S^a9pMe8GIPW(fuGOZ)t5b+w`8Q3t|=p=a`2X6%T{JlrOroqx+ zOMFM=leYHGj%bG{)~w&|a)+65X`T49#5ea1Fu>eGS}N}@_&oTh#n;pkmFTX+GZFLa z$ba~~6|$zgDetf5{CChx+JhZ))(MnNVGGy>F=M{UF+kktM}b7i`(WeKvWR!Q1!T z%Hic|(POlY+PVJa$lnEgL8WaIX=l^tv~lUP>T*m!B5lxVY?J|bUHJVR`^aG&%v&Pz zXZL6nlc#iZRJoCIB2^^%EW9rNQOO^; z=VY!Z6P}Fkv-Hwo-kmP565EePNaOqm;`djZG2X24>IuLS-JVI^SPH1Emf*Doub>}Y z;k@He@(J-(a>AS^s=bH(>mLmJMHwos4=^!{m@r--bw^W2eUUjTy(&GQC+-e$-^jN+ zW*fVeF>$n_r>2dmWg5U`!1W}d-Ab;^O%T8NLpkRkZ2Sjw*1+C(7__Zl?-(#KQ#?i; z)yW#XR^j!OQd`#a>&RM9hSeo4oyUmD2_wIauRtC?YWFG|`8D-xV?XPh{9+B&x5G3{mTXVbTsJ(qMX_k3aP5aT;>NBs6N zZEfVn+?i~~B&)01;1Sc-(xI|%`{A_%uTM!=b+CS!{AfZ7Tjd%d?EI4TsU}7UY`?#% z{l073Co0>|9&Ber)&>8$NFK&CI{qSY(hmhTqV)bESOb_hcam+o3RVwh?7NaR5r!l6 zKk5v(!yDam5+}^TTgg-PtFU*7=aoIY7uk$Q@?1oh%re^B9(J9&<53^HHsK}RpkEVz zr@%JAz979?Vs|EArP0XES%*0@a1Pq3$-pjXmj_M4b z#&`a!r!=T;cm4xi-KVWT(!5bdUEo;LtXp+f_4C-@gq!(MFyFudL{l7dGl$;G*sJW! zz^gH~jd*$DeTZ*Y=LPzbHF>M;csB>1bGhD#O->Vcg}ComTGjE69Hd z*5j`>nI>-S=X37c7SXu);{3{Grb5%RMCZ!hD)G({&+8MCoh`7b2zD84GJ@3(Gk=U= zjbP&;Og^7hurV<2?dkAdQ62rUd1Ewk^E3$kz)FxO$?j>eBAD9R?GHw`v3B_jCO+NN z*vaRWK6IZfwMQbhm_IJAp_Iz)j_m&Ttg3#hUW32oFG_t~4PZMFtQl-Of@Q$Az`XrH z;@4;Sh4(m~0&~7lx|iw?wTJq_oYi1& zn!nGfIpmAr?6T*JV9z?`aQCdsS1?WdBJp1$o~MHIn{(jv;BOTk=qh7p*-?>h)M5Pm z8_+g<(b{C(mpRH6kP5>Y;;v}2Q`P9d*WoXQ?mxtn8t~Wnn#IxGhSee6WUEI?Kyuqg zyd!^=bADOz$V+(rj@<_K&52w@MRjTmP03`R?bN0oC*gM*em^FD{8Y#1!Onr1``64~ zke8u_+yX7bnq-7<921Nup3vtXNG&(j7->LEN|FPj$^^@>_dA3dHjZ7~I} zwZFB#FVhys?fC}QJZ<)mr?6OU_N8Q$hfBn({W7*v>D(xo%c2$hd%1@a3;X3keWd>N zjE5W8Cwzxw%+=c*c_dqA3{?AMpzVX!^nU`VEHmz^eu{+k6ZS=`yTW=h*=a|8BdzW} z_DSfg|K6VIHoQ=W{fA7K%u&uXJm;VpyaG)dG;7c-L33W|ns<@RxWm=vqZQ2HwIH7M zCnFd`%ca-&nE6b@8<>x5+H?A*9qd|FoBD(+M(ru4X!cuMBD`!FSdDIl~_Wm)j>+K#O)4vt@72&)SpN*`f3p?sjx@eX77rq+! z0)%aWosVFbl|L|V-<9ZU--tg1%+trsV2xnnm*KlqUs8)&tx_+E*x-G{St5>iuWalc zF7o(MD%yb_L*_GX|C&;I{}hZ9(lZyp=D?0vISSh@I309{OQCE_*3p=Qgz%Eg?!oW; zRxk!&yWHd14|S)hf5NVVPCwO46Ik8XtS{AKN5IlxVLPJ$))2u4!0N%gJ7>jj#Paj} zy}~BIYN5M&Vltsuw}4bu;DBC0`taY1*tA~!AHQ&``Jck<2x?j zGcp-b(d?I_BbI1w_YLXHFVJ599ITT|_WQujgMCGbBATNK8XMD*-V*;`DqrQDBwqj5 z1Njqn9;`2d&4cxVh3ozjSOKg`Y4S7vKd?NQ^5o{@4sy_?J<}#V=U)7S5k7TbgJ9lX z_Bx_AfenD&EIx?5RGj#?fnO})ulYyb=U*iJQiT67*fLm+c=1yi$HA7synZKa8f-BV zcNS~`Ol}SRM7IPsABnpLHW$IR!Db_v^Cma{Ub>QxIh?-wPWKKQvL?uw+Fv)TaAG2hA`Ff1lSH(PU+e`ZNkrkH*J^V;=2G|1P}9F z0qc)on_ztrY!9qAgejfsGsq8EH2p^K@d&PZIs(2Z9{cO0=7il;@EDYUXbbnhZ4W6HO^Uwy~NUx#Sqgdyn->)(^j#@)GA0ibEg3)8)@sH8$i4ANp7J zvLyKjauYk&GvUsaEE36)NG7cfpuA1NXZ+v8J|9z7Mhe}g;*K|3gtec>_E{#*9C0FV zrNqyX+BE#f_|>rQlz!b?=>Oje)(isskr}#8v@?{xWHC#eDdKqjNcGhZHVNk4QLSBs z#)ii7P7{8b@Y-+ZoZsQw=oEYe%${+^i>#(fG&9glKLX7XG);Rs=j$FH%tSU6n8~;n z(Zpv9nq6qTwljgpQm3F~)OBg9N3jRK6Zj)6jhi1!!^61F;qR8w+zcNx*Hyphg=QX_ zr|sV_+H{ywUJJipFuK?seLj>YW2)Qp@H_XPrTu&!Y$}AMp}Pb&3HE-zO?}E6!I>@o zOUt_bXa|~x%dGP$Z^kCGvem5QtxZNrly1Wq?ekxy={AE^hp;qs8L%p_OMJUy11aW$ zy3GO)LNf==RH(!e!^zJ@?cLhh>OjE4T6Pj zr)98#2(}JZ1oO_P7U8i2b_%T8%4T?;7Wne*T5=n!Z>{C76utI9K)KH$=gLj*S@iG0 zyu0pBn$iV>V2xm%e7klCDhMaTOHuIB@8g8`68XvG_N-&4kkHF_9ZWe z-^%!SP2Q=K0HNQ@>MnRsFvH26WIfZn7%eL?HhZg2!!P}VdFPw_JIXIIXV$uOrlzmA zrkHon)H7KjK}Nra7>_P# z=rwzA8pqMEKa_VqC?F_z+**>}#QD*Ku~LIV-fyAWrZYi0XC9w-9xK5Q(lI*1$flXU z+qH*{1IY;`R4#?Qn4Z@XYPsN2^J>Rk(rJBa-ubu;r${=%+L%p;Rf~Z{EH@1;?*7ML zLLQp~c?UK!-4DJ9{?`IzGrRgYq1~md+3;Vzeqi^%Ks)@Cc_(bkO@j@Eu*0yK1v>>+ zX=Rsr8>br3@49@FnFVseKVEOzPW@^F`utBlavlbtt^Q?Fe`enKL%!X5F>62Az4uiF zYd=;u6c{dIDxYBt*IG>7%VEYZ%il2RWJ%`_{IMWdV+!QnrZbc{6PplYlz1(|>*BNW z&h;`pOj~k?*8Muue_usJpPk^O;l1!;q=_ukW2u|+{|e*hbMwykto;`5-$A~tpLr;G zN!`0#{Med56u^?OD(wG_BAKLsMV6z83Q>4sqz8vLGWtf{A3S|fDM4%Er2>^_dBMl zZfJDF&k;UHxY>_JI3;JiagVcVt~hVQ!)b|?5VLDL*d)t=@*vb;#Khg z^J8#hCmFlV_1`KiO;|r+#wIgi?)g!L9U<&YOqgW4kFfKEeSmLQrhR(`9b{#?BgK=Z zu1u@FCy3MBXYEm=%Zy%($duKK#pHGI{oj4Nc1`Kul6RhbHT|P4aYt-_U}mJYYMf*~ zdaG?K_T$QO%h|S4y=DksBRqW0Pk29g`bFjVF!*2$J^?-(gP#ZA1D9-gKgrbs_zt+z z(ofh5*k!OM8pPcPwh30%pLZ^+E?E11KyR;v_e~?{qm{|eAJ7}M^m98MH@YDGHvCt& z<=yjy*iCjWocN%*8Qx7Z1I_r)1>?VDqYrEf%

sa-&=6?D$96Il{*Lwuu}6to>0; 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 z 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®SgjAX#in8EEryE$-%HL8J(~~Uto}- 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

6eQi0GvpR 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 _FBfAhyS^V8 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

@PUmQ@`m`-;e^ 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 zΠ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

wV83T+@U8$qkoN6hJ0r5?3PjgV50Em5Uz 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

A=Yj(u9MwFRuV8S^?N;ew3vP0ZNdr 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


r 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%0DvOPSpJfH*1C_=Ykr~T3kZ?%LL(M`01uE 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Ỵ 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

qFq)1QYf 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≀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

pQuLmlP4nFBbnaK3 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 F#jWP(i)Zv{W_r(Q&@DE67O(N^?Q9xCaiBV~3pd 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%0D;q*PRNPjWd!5ZY8do#BwDg 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

vm zpJBOGD;mD|@$u;B?-ad^$Fyy!2i?~~|Ch+&8tgYIQ94$pf@;n1`+fw&L*^Xgj!urm zv=B(`BGS{eF3X?l ziq$IV-Zly>wwazc%2x??{R<|D-x^CwVZtz3v91aE)6(OPx1v zQXRSgdH7%SgTie59&(hqdEi!+iHw_OA*5F1e8*S}a6~ zqCd!m`Ro{an`<_r8{1~pUBL(2wxQ$^qa+S<$>BecQJfqQs?QDg$iDp*$b8s3;ddab z02y{?M}N3f8&obA;8%fdO19=7cc_P)DM)>9m@j)Y$Orcw8xz>1h$E6O2T_iDjNxvo zQsMOFwXaA;m)E|a;c6#O*6F^(k$L5{SLeDbK4vqH8vrr-lO;pp2g4Sx5NjEC(X0(k z7hW8nSF`N)Q(=5gMl9=;vf7cw?kJWS`@C|jYn};}05O%FyqUaz&^z;znmD+L!QNukUb1aDGPD{Yr z0@4%2w7*?_GK%+~Je1e|3T52T-j92ZI*K6+KB2c3L=w8R%=^>k@g`UvkZNIyw|8FF zV3Pjt2R--JN%`yKVdaIAuNB@tbKi3dBHF(ML3)QGoYdj|*IL#yLs9Zxa#Y;pBP$ki98={*mGbYy(YluUjt= zBPQJoa=E*Ve$`~&g@$E0&axov^7&MaF5a}DmA8-FOP5(6PrMF}t3xE|iK4$^gSuH2 zbh8dA$Ts%d)K_T~&Y`_=lEidJWuY@3A|iNvm>sQa3P4`|F8*U*hh;6NXK*`Ge#a%s?q*QfjNNsXmBmW(8r|Ek~4 zm7Hn6^%K!}KkS16lXVESCrMboOm^Uj{-U2gV(}J8erIVlHWBJwtG{?_lN;m!c-ho| z0(}Gg!IjtAa9H6+e`GC^)8s53)r3jX>ZwE%Zh#8lmNV^E?~dGR#N2%s_$o(ez<#@L z+pNE~sYKDSKH0BrmcK+KtNIK0b}6OYs!NO+-LHlje$*Cygw^I6U=kd>lG;*w>md|Q z@@~kMUFa(n+UGn6jJL_%g>$mU`SnK(ctIYHl}KzaHONOFNTs!{E^Jd-;{I^4vGK{Pp#Co3I-Rra$*o{vG?|-xvi@3s{WcT+u^1$ca_=7uJU|8z*m9E5Ag4K z!kEw)uY$b(SS-ap)+lMdRt>1$L^UCuf5N*%bu7(7I>+W6+ecsHj!oB7b*+1Mq_t(~ z7}}EXZJA$8TRx_Zfrd+oL=74-c|0=ge(!;v5YTTt&}0EU?tvzT7%)gc%43fldA%2j z9RdzDuv04 zH_ShKpFpaa_fE>nnv(v0V)*;Uo_s%))xPq<;yT+37=GHZ626lu2z$ZuHl@a9jm>hK z52NeR+?J1c=kguW<^TAz%6~#|jmLk=*l8aBgFl7;@9FY;v{1geefx#lzikw!4d44k zxcz&Fzc17GL-%g6{RdFc)Auh*`Su_B6Yc+M-&FaU7RsmT+q-XwzV1`G>#b)7eH%{> zeeb02ckI<7eOoE$>05n&z`qNAioQ3}9oz-G2u`H}m(fvP}M-#wDi#e;;hW3xB`8`RDOB-ToK2 z`X(*^qd!D{_T*S~vwYnj{(ic?-+|(3LB4)XLErv7N_qJ|@F(Q!%yjv$KiIW=bNf%| zpV|KB8`Apg!JJ*%|7F3qKTE#S?SH&p3=8a&BL=Gfj}N!MI{dw@zTeTY#rBVVePhu6 z6@+v8_8;&Q?LRqP{{McY{7(9B_NCLp`tRX4HzNQ02Q{=XUM6RjADdA=-To!0g>?J7 z-=p>)7jFLx;qRUF{f-VTw*P1bN+AEWsrIL+Yf1jMruET_EtGHW|HDq-ssEGB{oiia z{y*~P^}qO)X8J2_NS7~a zp*+sQEoJfdSYmCYDO^k4CLfEQ#WnXAFl{PaF4yh~v$t9=rJO0}k|F);V~8N=c63&j z_BA3gIbMC<6-)pO?6+p=8i-PYDZV>HcStEM3s~7_Sb-r?>Sh;jWWj#y3@e6LW|?w! zE8*_?nQwr~HQr8W8G9vgc>T;b_2x6ZNjZwNkKwj?993x?+~2Zh5DOjBTq=%6Idga-)3p5Gd!481{e2DL6;Fr-|J+oO-{Kgs^?u`CrFPZK}Hy zEc9!m%X!GQ6J@j=`}p_ryd27Q8q~P;PwR`eoxZInfW~Rz>gmzfmApeJ=q@$E7EkL6 zH8>Yhr+)S>y;~#Hz0{djbIc{a!0#7@{J|Q3uY3B=Yo@Pr27O~5Ow*V6a|V59r|BEg z0)551(l;=Zz9;T7`u6)N`qKP+{**NTs*Vlw@AU`L^sRd=gT6^=`lc`3m4A=&ZYTcz z9d0$faxX{9{h$c|euB3m*ggMUaWtIXL6Hd`r$pRrvG5XLZgvpQrwteRr{1UM{Y=YWN{WhlYQv^ zl>ERfy*kofp?CfMxw=O4A6veQ$Q!PkPv3TPsRJMiA>P>eC)e%ktu3pzpYbwiL zP|}UFQe_pEfd2`=+4GL_jlm$^H6X6n9V3YswtCuk`vqqQT22QzT!;`zspML)+^N^R60@A?2 zPaS$~ztPY3**EyvzPvpCee+Z=|Ho0k;ftLoV@DB zKXZ#~it8J-E-yWwF24zCF+>tYueYMpOUkU%!}I?7a_0RRyYBbdwihuBcdQ+QYKYeE z#fjCf+$oBbPl!y;K^sKL+E1&eVBsPcUxW3zI z9|+_%68fik8Ld6B=VBlH;*L37Ln2bsnu^?UXBaaYb@ASM5sLh0Oa5V!?ZR2qftP;` zi8(SCwN>SMkuxCG40m&^$&Vko%E$5p!a6nlLjCRDJl(3eM^)7ODlSqLOQ;%7OoY7D z5S!?$xMRMUni3!j!v#;Z$5SdiOFW*vgs0Zy3C^_|sfFNac+u-yQ>+b7r}dp1&rSx+ zPEImSMvF(}cNHVMGIV66nLYf8Yt!1;6kAHMXoUin{Rf3&mL+<%afi3{1zxrS>i+EW zvyz|kDnem^ePWd!*)WU$#|X zgf61cOfd?a=aiv!vdKHO!krgjUhGS^=j*h+6=$--Jt(virWf@MA5o(B2l#&7gj97M zA~n8G#%HH6zjzx3cAV^~-Q;n02q-v1aEabS4uNoAc~uE-XhWhGcf`K9FH=|6-9z#t z?x2O+nj$aqgk%A%0DeN!OYYv+wFXC7_QHW;XlEFz6O>H@txqOtTd!o|DKz~AzGwo} z*U$M%7zFK@G>O*7U(?=TI}sn9k{bna9T=vxGfq|bL>Fm6xY$iO&8pu9`0em?VW6~O23PSM)+Z@iOLG4~gYOMFxs z8i(cBg^qqPUvNz$aeN&suYM(uH7Uo~$Q8J+$oXZ7@zxE5%zr_qqq;2B z%yol8P2F?n= zY}RbEsuM-!+ms=1tGnzTU47?+Z$Fm4AnoKN<>e#eFoyz5^UHNZCh-iH=NHDmD~Z3Q ze8)Y?O)ZxyK{7X%H0)L0t)V3Q{nG4p2CjvDwBl{=36W!i*nyH6SuC&0`@C5mIs47> z?C%G}U)NRcC&C@6xLQDPoRky$|AKrWRJh4KHMX%yFZ;Y(`AB&!6^T7PtUHo7(eusY zliqoy9lb;s-MAH}>##sBENG$8jRcXguqW^RhI*oR$H%R+T`?aOnozS3ACrrPcS@)> zp-twb@QI2j|8OclpEwU=1)TBudi|wKD-kT;l-ubHDLePsRgF!3VP&M|RP|vn3%FsP zfZ2Sl-qPL&$OB0@1a6B)_ZQylKAS!tM{0Hp_5I3|eBa+?aP<9GP*>YEzx9ax@}#b% z;gV)75#M+8QSxGdNi}#KpMp4j&4XqPn&XhwCdXdxSo@G!Re#8TSAQ+Lu+knYT(kgp z7=CWcUC(}<7n9g(Jup!ncdne`79V?%ey=}66GG*ldj5iE-F7}nTFmu2g~5<0`u7$V z_CqCYiO$()=|XV)Kc_OH7uVeY&R`I^)eI^H_a-jSFP8B&2&)l7a*cq*Gx<|gKQAQL zjKa-+AvS6}W|7KhbPwi-$k?{KTm+3~JGoQz5<`}rwBfdO#uL!ut|YvMdmO|Ci<9CQu|K7?I9Q4R(Y!)Kd#|v>H-i+GYdFPDQjOKK@;`%_za(a8C~SzA1Z-5^9(3mLDy$*#A3k zq-gSalVwcod#nvMrmb_KLcCIgvLdnR)X+55e#K+?D|uNv-Tp-5zIDV6PF5I(d$_MD z^&6uo^-80w$vxH2L^25Y(Wq?}MORjgx!WliPbQtTgHcuqWm8Nam~fOdy>HSn`3v5= zUeqG_{|@=TOiV`psnO)$R~Gi%u4YIib+hFZx9wyDd_aIBJmBkRc)%NGwL%$gWdF2&zSj3sqr3LP}Es9u?@NwvSLGnqEDL;eq2*)?mn&jm) z#O~RW{myk~L-KpkZPEBr;*4GOMz7D0fTuc*(@Wn8JZ+3-FLy(~^B<*6f$h~Ac=}&; zC9gq8HS2SC7UxNW9!r){JZMj;|EalP zP->&R=O)`D@446|Sw>dw;;n&&19BZg^)ap2cgZeyW|>DmE^X)ol!*3lC;VwHT~ z$AmS!j((~L5&f6i6SThv_4ZJ&4bnr&xdY>K@mojZ??n}@w1YV5RSKsR7edP^`9I)C z(~q1d;e5jvo)rs6c~+F!MnTAX;MU5iU>gMk<6j4R1yMqyU=5CiM+7H-Q&{Qx{esrM zz4w&T-fPiAwqG!11MO|UYkOa0Bs9F}`>!rY9f|3-z|z{wb<@RmMUB_|5sF-owQ8Fs z5ne!+Sehh_-b4E9U-p|R*w#zGl(LuBAFT&6?0&<|?R)S%+pF@0av-?uD^dxgBe6O@ zs=~Yy3z9xMxmeD=M=*{&r{jsY?_vp2ar0dZXFdFhoqEA##m!09mUP!rv1)epwzjv=j8@D`*_XE-k1s1x+7|BzVK8!d{LPn~CdGjv)+@id zA$MlJ5jX;lR4iMV4O+#04$!1^y=IgrvC>j!nfHPif=)S3&{5`hhhqw|W^kK3FdRl^ zVusJI^Q}9YW}a;?HyKl01sKF!vF#2%L?yk7albWx7%gJ*&sYDH$d}2umaUqblribY z3$E0DcZ_mquRdX8+g74B)s6%E~J zR;g?>u5^ynXhJf@Db@En<`_lp<~+OnaztJ0!d32jP>I~2dzQOwmKFXs&%e+fR92DP z*HwG)H1U<~Ja#CFOk2Q-8sKI01;*klXL~{pgAh7Yv?$zP4nQmfo*)V_p~kFa0Y3wM z@pv*VoCTz~4GU)%`<)Wb#p05%F49AyZTH5Og!BE%v~bo`pnV(859%4-3%$gmpl)Av zQlj(yx^r)IZ&OLvfHCE4+M7{}P1{K#@^95tyHCD}fe8SD;P(9WjwaCLK)`jPOwR1jV`ad(FPHrYm49vO&$&M zRFiKyX-iWn!@JV}_6PbIlzJ$o5ra@^vgPogk*(gan9RX9UK7rcSt809|S9~ z$fyA9MS;x%Y>GPulImUw^~||)+|&Bz;Uih*`<^k+{I4mRscP=#O`vLOreQtUojxi1&iHs!PvhgCis0i= zXivUC9iGL*{im2d)#}qjsZY_w)$JuJ7eWAPSN3WAHNxuMu%o<}?q$a8nTcDE$V%zn ztfTSaoy=EL6EzP>i=Yl_q->$1lYDO@c;2;xK9IJQEZGdCCp4mdYM4YhJu!wJzfoKq z&Z1Y}$LN?t5du6Ra@XsqU`mgY7LeiWR+A~#9{`BNZsLXX_G=6W1C;eQiUA@6Zxole zW9EG0rt=l>yPsESLyrwON<}b_)kD9sG*wZV&EZ)xL z@kx_baarvtq}Nlbfy>Zzl_S})B1@gX@2p*igl+gY;!!# z5f5v7Fw91|a*DE`ALSWk<#`W@g^IE74MmzEdv2Ps4MLdVT;D3+Gll%X0g4JNoKG z=?JfTBL(yhKpIhr&ie^&@$m+CIhCg1CIFY*Q$W*FKwtWr{&O-AkM0UFP!KCbTZJGJ zo$sMwo#yA{qry`(;zxVqFh1?n8|^jk)Uf!b2hpvbWt-f`!vmHXw=gYc?!>T{RG^t* z!~OwL7ekaJ6BRb666>0|BSVF~!0Iu%jx~*F7!-ZTHh(1*z(mXAh0Ht2c3KIDl9U2F zn~IW87*ObKq2B)4)>>I5Gi>6m;k<(B-JiCXAK?Z}jim=j$}dUW)n1-8S|t=%(`+w4 zq{+wZFqMYZR z;<4xAL^Qk4(%T9cj$G!2XR6!0Xm*42`fi7#qHr-*?LTpRQ(jfOXku_vx37lB-&J^x zw+nhM9zHVuu6{PJ?aV&MD)$E3)yPh!4YVXcdq)6oCrrKoiLLh% zz5BK`LfxeS;L%i@dzK&T_Mk zCaQUW-rzw4Zk`xaGm|pj#~h4jMYzaW`qug7@dbmUiGwnz16QrO>M9p*ZyrwTF0{{DuSZx5%^1*5VTMi?pMn zW9+5o6g|g3acktSq+p?T)IOQ<1Fzi@d#B|rABy!_OA0rO*X6Zs@GO7N1XMBuO&v&) zV<8vfs!X0FhnUM82%1w;F9w~jTddoHb{$}FhWNWj&4tYp* zaXH!+T9~Xp( zE{%7hG`vtQAXIdo;77v(48CqapSL3>C;|Do@`x z6_S@_pa1_MFOM^KWys4+FD=Vmd`QsrnGzH&SZ@yA6wj25=*<XnWAJ6X*T zlLuVS@BgcqtOu+`F`-lZ`e#>pInztSO+PL#TRt;+A@Mka#^l9|iWd(rFM8d@gvJYy zBtoJtdau9(-JaVbu#E>$cT}#+c z%56h!B{4F4Xw<$AD(v>M_?JG2*qkQ{CBqdkoo`J`;`8pxljQ!0DF}IN& zN;ifrh-k$|*~YhB>Nh*bX|wbCE41~oSq96K4=P#IvdU39rnNg+aDChP6X?%M8 zWBk!XmFy; z5`WVl+YU22hxCBXR*0baK{xk+(rhSYRTsAF{&^I=GBZq1`uxJhSkseV@CO@A4mK#{d78g8ux%0hF@y8xJ~Kfn2qCzVxDW`L7bYmQT}HF>O6d z&ft8>2}kPuD>Gm-ef`7V_tN)6Tc+1&9q<$`hSW&thOj2E1#NJ%d9IVr-o`ZqilW0E#IZfWGAd z=xE1SzT&AOHFG6Z>`2KWniYvDG$vZ>f4TwtVnN69NAU?q5X*Yy=mN8Q<}&T5`cSew zAWAOq;pnCD&119J^AfUX?bQ5<_7{7@eRY|Qu3;CEUMCW}$7+#H!tHy?y4jZ(?!$@7 z!6O;&x|@boldGJ{fWh?pRI&4S%vV!)a-zkcIz`>XJc+tJLe%N7w5qIWuPQME@YwiOf|`T4YBn6 zQSaF++RAI6@%*42y>8UbVpXxI9CK+i4?h*2hSH_8X&etm~{lU&0{ zL@xwoV>V%+JagQcCw$K^vq8Ghy*k=9xGz*Hqy3^4Q>_OBtF+`w`&OhrSCi@UcT_vQ z<$G3)6`eyI7L{0 z)%_97=c4z!qav0cVk$z8pVXSa7QL6;%Fy?A67_BOKYV|?rx|sy<6~Z$p4-N&*i-~ z@=~9)lgR$(L8kxya18oS?tQQSj*E;tR2Ueiw@@PKN`2`n?Nnd9Snz=>o{4{@kJBGTEzLJ2&6S#Y9 z7Z!UtbE^*Y^J2F!pI3B)&&jU4@cDr}_-y;3yU-gb{nSkThx(t82YP~w&r2>yQTu=2 zhXvw2ZX(--TG!Xp`Ak8W&XYuE$*y#+N2JM`)7%|)e^7?}?nLj}g+GH{lipSt#OAj| ztn8xN1B|af92KVbE>6z7duho|@_UNteK}082_x1uNzljp}ktf>j zKka`zFLTSx|K+^QSszfknV}NHjPo+%8#2DjFwZzIbKn;l-z{~mF5On@TGG|}j4HZ( z+w0FC`<^;cWO;K!<4y5cb^?z?nW>Ph4+fXU-^BhXM2~7i+Li-~YqFT9^!7)d-ng^S zDx2mJ#HRU}lQnX=lPTCTHGU0uD>mYaO6>mYbEn~M+5I!e3LsMYx>g!_;>G#y%y0an zA`+|AbT}N9B!4<~13!oPwolzM&eMpN6~5wC~u~M-m#R#m0dkVNs)X&rI9e#3gYQL8h9x3iKM? z^3{wbV%?*9eaC#y=JW{*pNHDF*nP&nIanuaom{=DHOX`FI;C5u70EF>Smm$~NEktI zMAeA9^4}(QzXrR)cZrsDiQN}Z=MbKIYF$&)#g~A%<{sMSHZx}sK;$czdyc;UPTIdWtQ?0r6lTYO$~MvCX(*oNiUm4gV4?g0TNj$NNGrf{41I%-M2$3Iit z)B7^MS>L@2y;uut^I6}$w4aUa@cQm@277Wk5b6C*7|L4g)5(Fwi`B{)_P)YMj2(=o z+Cn7*rIACFt1GD$Il-K(U3`LCF)w*FEnxpo^(T%U3~6 z8RUj?sUjw48%f>6l4F_9cS!u?xWiG#B2Cuvamy8TUUjn1Gn)0;4kB&8ti-ln<5qF+ zrT#7}GObUw;TV>*y(%n)D}<$Tru!4E>A48u7j;_W7w}h7?iP^0Js1UNiXH^0K*N(u z%@%3;E|+_Q(Kd_GtnfWK)?D5jo9Xd|viyk|*vsI0Z~GEYj?z_cc7tBuEa8nwGr7a5 zyW!j^&DB%uIn;`Lkx}!Kl0NmfUF6PQ^HVOKqE6X4SyjDU6xk@DUGMI%&RMRizI#kn zk=+why8u)BKS8_J!D!ycT^mGP1-s5>xYiOztA+YyZaY*ZA2l%x^k?GOGVYj9-e~WN znUDSf09z&%{gj8=vhj_lQoYLMYR^!6*wCNnZspjp4aCpTeOV$bdY=5+tMzO=yiC%UCz zQ_QerM@pyLgIhazn;3AoHj8~Q&?dl@t-Dpd4jUl@r0Pz7&Pyq$0@uI^jI?OD?R#mo zbRK;uv++=omnho*Q0C&c9M(jRZs%2Ms65Z;N&Zf>HP3e5{u^^A#hiiznRGL5a~n;3 zs}4ISKcJDB`&V{fVQ?uJLrbRz*<2pNf@tY1Nlm?b?__U!?0Q~ovs@WyNqiY}RAc(c zn7d9&O#9&T+~b0VoRD7z&CB@*-{M|*wD=H2+Tz@f$fM1B?rm)j`#b7TCYk5Og7)-I zwFk0u$7n(CUh^jOe{H|__xs4PWDfDL^wO^7?%zw*`H`BB^sSbB@<_t%D9t(i&Z?{=Eif~+W~4;t3lEt_^mg4Km1IkS z`H#KG`afy$t)KN#-5uxHrsbsd$tQJpC&xphN9i0A8R$*{{_|92Jd|JQ2qVwGDEG z!!)(oa2{EqHD)CC59r}q4dSq?4z!TEu8@u&qnvm=wthCX!JPswor5TonwfT3XwjZ? zfi28ONagG!g+Lgec39nH|DnH>`d)TaW3^sCAbo8?%|v!Y_Iy)U*jJ(WT8#(U;7lq7?i^_yThe8$|5zX5iR-y&j8rl` z8D(?ZRd2Df8N)~Z3Z}DUJ>4JJ3CedrhxDv;^Iyjz0NP##MVoaTuN68r(bmW(I?SCn zH0b38?q(b|Qj(|!+V2X->WhLDPr1|hojxtYeX#caNZyy3u>sg}xH>MY@*^8gxRn!~ z7wk2)c`&VVU5GNZ(stzVD?h51driR|isQr&vOK&ezl4_e+_T4ZOu^X6+`w**hy8K= zIO8QoF8cAg-u#8WUG?Kf-0|L`emq+`%In8!LARhE8#>weoqqu1_py|IY-S*;;~eGr8b3CoGz?$1{wre{+L}@k0GpFVEROHU&H?_J_{&P8YNtSEDzm|`n z&=e{s-y^Y2CeX~ewhLnxz}{NMog1w^i&T{8U0dRBvI!XE6Uw!d@Yliey4fk0Vd^#( zxN_mUT)e+nhssyV(a>Bsr5b6i6~e0jbNxG7KYgqA4X|p9I$uQV&Z6DS-1B+G%)L8* z+?GfxuQH7(OouPn(J-?RI7Nxlt2$g2q2{F4+9G&A&Dl)r_3qJI%?HoCvRY4oYhLmDa;`OPt(ySu zSFII^&iC!dDk)L)?gccsof^Ej?k{{!3DFCTnub@V_?UOvfPqqZ7-@LE8EGwmsgK3F zXO)adeu5p?Y$2vva+05#dbnetRY4P8-uH!0Su)>^7paZPo_{j51~STij~kv7&`AxA z`x=SIio^=&&m_^O4tE_yXkE%*B3%=#+_)E0ygx`i=K24pYWOeXm79tVhiv($j(KVu z$I2=rvHN&m^bne(@@mdOR-W$O+s&loB8aGaijOes@u5(%6bel`#4ax#cS`wCjY)`d z=QhuO%-iGsXyy|{=w_$-k;ye=r%tVd`5t{_r|+9%8&mV2=KVsd_2LofIU4N$t2EGY>a;?%noc z0>WmTfJ;d?M%Nej;B}TO6uP)i?{m%D8R*al;r7&_O?PQew;yc}XYNyi41S`Q8Hd|u zXH+0#%3YZ*SD!p)*Y>zG^gST(&9^e(GwC~}CHmq|0MU}ZaFKMqGUq*g!&{(l4cgle z$ogb$raxHxcJ=xm(09yF(N{;oUHF%L{Kx2zi$}(muJ&N_|8vSEPJ+Uz6+K%I3$bc{N+3 zn@zs2+ftOOhf%e~N!x;3M50_;b{^=3$ALH?F_yGK?sF(To3PWu* zxGCUI-7DoD_i(og?lccKQ*cjvIAy0u%JvI+lL9-;){k;r4RZhVf?JRlx3|;@Ox`7= zHLtGEwJVM5{s=BNuU}gvWe&$#G!mQdyT+~L#lUX6Exx-lKce#rk-EWxT36`}qh@`d zi9b7+Q!0N@$%-GvAErk)7y0rh{;cw?(&bzKtnx;WmSt{9-ydtQ?*GnpFZ`G96-6z= z51vV%ASK(}Z+h5WNCzYS_~obbtLG+}E6F5~X?@vq&hW4b(EOa^8L**PLw8Q)LDhu^ z>xzNmoZ225*6N<*w6nCmg1w7j!EXh1$KWy$OiN*e8TK1@K}P#*u59a0=d64*es!*3 zF|Sg}h`(B$DO*Q8eE}+0HC`ZhE2Nx9B#3;2e;LH@#9&4<{uq`#t=)H*A_tThfRh^fCwr{PUu)E)_(gswtN;B@;K>IM ziISSF&G3D_tp@r27&x13jx=kYd-NPNsec!D*T1EG`{#)Rqg{J(ATc=4ShLW5GRjyJ ziQOwO7@msmo5*k4|AW=vOZESP7v@i$G)C=*vYp${;P-hqUA2e%p(Aj8fm8bhRl6%x zp;VmRe|hmaXpM21?e}=53s14flPf&^J)X-&(j2d!zD6Zwu!sKh%lK{4+6i4;kC9vi zxutVef39N7lm9evb%FGyn=F12GBn@4YL5%tf40D*+VOdKd=LmW%ofUWn~W3%cD#F> zpl9UbhYrwnwx?^{mC*Gx^um=}LUav-F6D&dG{Y2kwTGK8IH@T3QY^R|JzQAd=BYgk zL*1?RTrhxqNK-d@X5*~yuxK2ABa6dPxY;||Ovdc#&V)KTJ`%eWwNiJ0s`S3m zOYbH;jrZA7H>K32VK9pzCrz89fY)_*KfJEKS>UFskF?A6f)JPzZ7i|)4%QAOL$vEr zY9Zq8?|n9~H;KQ);>o&$9Ah(2`a#;k9kU#93tp6SWil^w-6o7Ccf)43=X}024O*^^ z*VP`^=inF`RWiifyiDpO_QHeW@`l?3%(Rcm zI3V->8f|wjNb0Unjq@c$C)1yu+S4y~?vyj1MNeHp&n(?)JvxpmQj&HjL)c5~Gs?9Prpqr1I5WEIjvmJYjut3V4#`8S;LXhmQ$!WRZuXE1UhfiVuc+;dq{uy%&Uyq?g(nPb)9f zcsd|bBNvDDDf;DX^>lgd_#Wl8Hx`uFj>&bu!~ILx3&W^6mk3=;N`sV8s1moKtwN}187bUssH}I3=p9wNu*8Vcrt^XE=V2oBT$g>P6U%6+7du^FEL$vyDuEN$! zWXrVGSM9hiZWeYHIww-|IF*%Q4>G4s$jgev-sE>QaR~~D`%G&kL@PAT`q|?=S7|jT zbv$4hRm=U1X*@2^bsQ;OF5gCvn2j{EwW#v^|7KK~MHlBm`?#)fc@A+-vcRe)PeD}y@aQwZ^yJ)uCQD*YR6lb~n zJ={Hldn*MuT5!MfaAD1My5R2Av>y16x#?At`UmXC$LC`ckX1<8w&nkF`#-a#iM2vk zkRoOBeFgVU7Gs`AYX_o2Zqt6mI1<6>Pg#79RsiE%?op|>s9s4Gone&ALM62uMX8trNGr z*A?-pEba3}WO4%^O1iyYx2Fdvt9YwC`y02wgT=Mk>|XxXMB=$NbS>&GD=hD*r-dKE zp6TD2aJ0_ulva|LGD5k(J~=R~-?BeQ{u!Lj_RVmQe29=ML6phcjY4^;N7*Wbat}}@ zw};`?#$iFPzQqUVSMw~56W^lQN%#G~{JG&mS}J~bO+HS+)I7dyqc!gR*3^HPc#tUi zj(Zib(>{kXG)!<%*6h1UD5KsIPTyGb1dy z8s*LW)6wH`TD5%xB<;OfF9VC}5g}Im?aXV9 zgx;UvB2+W0@*w+BkLq_qr6}5Dqey73D_HO4$Y*EkJw|7N?miNrX-vOa zyOgtaEP5G+rHr;)?vnO?eZPIY& zw6^qk;@G!vfV!i(1`GsMv)z<$U@f6hPYXw)^QGB@M*RUE?KDl5p(z{gP08PI0g(cj z!gUWBU{RIOR(Qa$^ydOdnAa8I@g!5se4;PV%@Dlv{;KRnyU!Q>S|oOnKrgZipwab%Xsf|IFCz z!R|0EGqfG59(k3*q^{5jd0Zkhbz~LAde0K^@8A%p(-VJc{bS{$72mme$Kn>5OZ?Z@ zUVfYY*c9AfP&7bjPK1vzI&597lxnec#RIRcZ)$kh=Bb88gMUKs7G9qyYAg7S;01m( zl%=HqQQ%YeYr7W=?%1V^$)ly{Kl*GGI5Zeu_Ml-C>k3tEO?_Ww*_7^o5Qn%FzmhRP z%#^nN8=UID&|=rGcNGKS-JDE|ZX4-ljHTi~X#?t7TLtsnVKwXI(u)0--`HNc5ca1M z%GQq8Sq^OFF7B7V5r}@Tb~o~@;u6;C?VY)QPWS$foU5@hz;~KlY#~FM=>Jcl~g zHkZ#9xo1$|nMfHoKAXKljzJN}(EF{I@1p}=b65UhN7Kg0FG=lK@wUR%P>p%M#4#sN zZL+vVKFh*{%27VVvAP-NO}VU_VWNlLj~a^XNe++{R4kvhWR_lyb7!Uj(;!3g`$5fJ zTkoeXp9ZiQ@L2)!_EG-2ci^-q6#5MuE2|LLKb`^Xo-qqzYKHyTDvg;h*xi?g4eAxv zm6T{MuOsk!`?czN?a`nv;(dHw9LG^tMQf+$kJDc!mSRKCGn*tamSL%D_j}9V^aUq_A!IpObJr4 zN(19Zd*_EU^1L5rjE3TzhNpu0KefIrgvLVXD;(rxRzg)F_XGTFeq7f3|2scM%jVdb zAN^??T0Bp!c=CY%l^>5MjUR7)4?j*ZOhq5Q1*WIr(@y*tCKdBD`4L)=hxo;wwddDc z(fF1qIX($?h^cRa{h0L3-gBK6PK6f39F2D(CXVj35N1LQBC+K_;Mj4S$W+$HMJ7GR z@67qgrf;{J@8;zVTt)4Uk-k!{-^$%__eN@Tm|4CV^Ucc!|MF;+kgigRHj$cdRh8Vq zd^`j2A{jT4ST6u0lh^ZH^nks%pBF_B@>sP`S@mryA{S8FUbg$U*SG1*wV_nIeoErgX{w#!Nu+nhI%S0rIqU({%=qtDZ5X1J1icmI(J zY8qfFs%9v~lC6LyeutwU3d0m%XYvNxXq6?S`QF{WUg&&Mz9qm|z~T{#2=uWEclT!f#V5g=u=xBV!1kz(Djkw+m?nG!gwOYH6)(fAXT7V=2)L-lnRv5b zN$tL6wKrMce*Bg7Z7d(+{6SL%Zz(eJ>;-JHDz|rB(Jv{^Jv7vFt|Pc?4M)2Jc|B1b z2tDURr^&10H8Z*PYjl&iW@%)0SdzrW zV0lY^vOZ$C%~^s{eZ6;vImBQVxF7Zu3_2)bSGw85kWqGDTt= zs@OQ3EpQ-qzQC9@yBH#2E^q&}*Jo}%(uo~Yq(u?p-nLvDT(Y&U5`pFd+Y9 zY##1aoV8WB)fta|WKv~U4k(#hYhC59aN+h~JyS|5O#aG7tnFVQ_al3?Zr+8+^1Mjm zV%8fWmV5u>i+oIpwYE_{%ui`WQjx-FNq6iy9$>-P6{@k(oi@xkay8RBbh{Jz>E3ys zNwwl#cyjk=V&>eh_B%q^WTMqu+f|-aq>b9j9QD1bBuU!ELcRCX z%Z>a}&Y7 z-$PxIXZ&yOiXIK^7@vW2_gE@WuV|;;cf~Qh33tU=EQNvz%1_<5fIB@a${6ToB~p_+ zO6>wGaXog!8g~yLgPHO3Hd$G>-(`xSY9il|PFKX|_=x;cjDv<8=}FIx&2FkVBW`eE z8jBxJLv40bco!z^*~29E7S!lTQ+}mr?Jz%KtM*Bhz@4iB>aB`dl;FB|kyaij{pLCf zl-IV&4@ew?kVhtI12NEFn*M}nnw8UmGKOUjoTQq2(~AEkf7iO#DHlqor2SP(yY>oZOvXWwdA3xuDt16MM^x#mXjZ_*M zyW#%&l1i+<`U4sC*Gk@n`zwpuGWzQ{Q5){B<#{{xm&gwHR}m;e{dEeAicE^`+F$;B`^;bdATc*J4t=C)|HkBB>8)Uvrr zA0u>GDoy2O;Lt?6cW5G=yh;;kg!$mI&XaZk@28;&D<$N&hc}o;*Etm ze;M}F9wEBYU0>5aC{hAxn%F%(niWnxNa4^ruuBpmwN3*f`8sn(vwe%sE+CK+O_4l9 zlZmxgV<8fgk)yhEn%j)i35=;a^zlG?Al2sUK%m~h+bRPhx6I7pdiUTL-hi;#AK$2N zy8@SXf7Q3WF5LEEXZyB4D{7ml5&gump3>wYyh!y+bNhACgeWOFWS91L-KqT-Tl)!$ zNVi}2YakSu383(^nC3IfkN%B2_)AToI^;(<17O&Sc08}%eR&|cekdu;q1>L+&ykIJ z3N!hy5z*x36t?l>`5B4*L9BtuSqG=(CoXowOT>{=$olwiT9B%a14FwQV&{DB`N%bd zW{9081J-0{!7JVn8+W{r@oJ4bZKXGphNaA;?oHgo&6Z$$1jh3FJpr1uz^%f9kce!g z-sErj;@3MeBXU+y4*7VFPdZ;^N-vK(n)$`4F!v3K)>718Zt)J16U`#zA3s1nF~Zmt zC4Q#nZ2W@NePP#|$aIqQ%o&oW#FuKrq=VAj5?z_xdJI;k>cc(SqU0R}6V!P#q*;)> z{fuLhy!&USOxIRuu{0Y#J&#D9K)kf_J#UBDksgG3{-=5?*VEP zF7^EdMU+;Qm3zmQRYuGX@XGp+e3fpvRMC4jm9;%Y!BbT)8jqBNwK74tpHC^Oci5DM zqOUvfBf2&CfYsAUKczHWtFEmvQ&=vJ3GG(PFiL)i>bMZSW@Cs$iyXW^V)*em-IPT} zzT4ffOLebK7YV@qI$hOyshLDKB{7NmXJO?`3h_Hd36=9Qs&j|pW!Jz2~ydj_QHkuU4=C(!nU%7 z?QZqRK64;D!y_9aWHFEIh7huzAZs?aPG?Sb%Rhi@P#$@vx=P-Kss_)!d^*pZ0Lhss z99JV)Q!#{&)b?&wJYybfxP4ifQb!OzJCq9_O}nM*MijJhx5%1{e;5E@ec3@`ydq_ z{#L<{Ou>V_Typ%Y3vU#PfWF)0=1<) z{*mixEt%za55DZhf~($r)}Pzk>fN13`N_`HiZXOlmEIb5auE|F{`ag)IK&HHe(a%@ zxSU6A?d@#<5eBCnfJf*NH}${0#Uvk#`~9Wcd}@$MpIQHs0(D?Tle-*xluP2R-8V3u_=Y8IKg7pDadI~3 z3U?KnNjpfdC6zcOm`~jci%s6AuR>m57mE@_s}%+N$`X_IaJ=If)S2ynNHTZY|42+Z z8eLSqH9KyiR%6)Ffw#|ARn9}B+JcOaLGMcoMM6oL|HsZ9GI?|O7vcu zM?64nabC%J)DVqUC|g#K^O~;DA@d*Di-oz+{p)l)qUC${HVBA9zFan1AI)vZtAZ2^ zuXt-+h{wxDi(Pd)_V9Mnhuz7wung`_shwr5cHbVZX+97*pKbz~qZ6c_qwYye@p7t{ z0LAmbnezXeAaJz)4{5uns6=7-PsFyH2CR9KT2hEPEj}Q=rQ|%}95i+m94ogrHA~OP zOST1<(|$-LR%6{LwLtPVI`Yd{eh=Q4%+BMT_6bKpq`fXyanN?r+v{CtH57WgB-sr_ zhO*p^@0$EnevbV7$1oN>rMTDe@0tAE#H*B5QGjYpzQWiPTwf2@PjGL0xa$Ph-@{!I zg8TY?;5aKPxUlB<(6u{;(WtTaHq@jgtMdF7rD{-0s5E}Z{G?N%`5?7R zEq!;i!n6K&k#%cX!3d*la{#>ID>U?b%+D&8H~wE zfTh<>K|8&lzsLcHnAw-W}q>as;M{#Tqh5fJgW?gu~GmOMzMa!-D$f?Jq_0WIXbfy5r`kP0RV7rNg{s!0suU_I2BT>f5vv~6 zJsy=Z;iORY@u*%a;J{6jJ4Tqa$O_hT5~7}qEJTD5ZL>qd0jHsO19`grxnks8l7e+m zTS)Lo`~%I-*gx*}7r>wnO3lx?Zus+le$I7!9v95dm1A`?THe^K_*Yi7r9IFIwDN?G zV%@^p?V<~Hib(5^*jMT=%mDUsbU#I*+utf7%*U-*#hPs%*Il&n_uV7dV>0)~rkK%Q zUOOtsowu9WURvK=`GHM=He&lqv8XPc2=i0%W+Tr%_ME4@i@ShrH3r=vZt>Ecg(7t< z=Kov>CzFv8J)h--GGPnu00Z#oQbhHR1ZLo#BFKTZF$kaI^cQ_}FLczr3 zzk1U|rSenymkSd$9+xZ1^@X=gMieKP>ex{X(4@9SO#gYfeu5j~;hqxQ_+8-&fD@JP zwu8#o1O0iThyP(2@KUzaYtglRJ^VGHdL6=_oD#roBgqrvaK7LMdbs0MZ)*=17Q|-- zHw?Itk5KaE@5faTlEM~bb(UlK5w_soS&o{k9Zy6;y*qCqvQ^(7?>a`z5&99gj5X|> z@Wy7}$;)}NV4qtJ>tgCa_1i0%?~l{^ME2o^wA*R|!6L2AaWwOg|`$S4AH;-0l|z9u;aJC z6hUPbME7vaVZtTmJ*)X19=pqYb=#Iw-Q!f(FxmXEuWGlUWS0I7E?t<&quP}#vdN>z zUU6}OCYh|&W-d*(oi};9C1)Ky+9!nePoPa=b%*@f+8862KN^3dG(J~>fTi(wY#o92 zPzLoW)Mde!niy&}y-cHN(~3yx*X|oMwR+-jd{Le=6a@E);SeDhQx~fx0}YW+JS*dI z!LW;1wwqZbHiVFVYtuK#Bjj_qUPPTtfvZ!w+(=St>}NyRg#)>(Z?LWcaV{h0RL zMtf|XbSa1fT2+N}JQJkteAe2B5=_(9Y{0j3t=WT<0#OopjQ zB=)Ebeyt;RXWXj{Yi}Dn8#}wR$ zfF&|{kr zaY|Kh%Z^MwjR&gQM|9pUlocOG?yfN$s1QutE?&nljp62YOE&8I>eIb)A*e(A&Y^#i z%;KCl0Mak;Qg?E33Inp;l$p{mAWMwMA0=npa!K@=&onj7{xdCiJ-T=$GpgqzQ!`RB zpZ;yV4Vw9&f#rp+V^An3ROqFkP!nXZ(#vw~cwtMv?SIuA2S*3SuRtMTwoADUBTNDc z^?#vz<7w535R@mFMLT7&zSFghOoH`p(%XR_M7u(%PIg_E(&M^ym3$m?=_wArc`PY+WI>^nCj?;B-4ElWo9&9s_(I*nf8~FyPBa^^{j!L+2#gUO$}s z1vgL%_L_icS=)AlrV2#RB7 z>$AI2c?xl316X5K?4#$D+%GRVz^0+%EP8bx&0(+DM0N|7vA1X4f&1xPyjNsO)lJuA z*~MeQ_p^+i&hr<;r%p64sW_o#dZiU@9%M|W=D{tP6$syCnexf|DXxBO4W7F72!c_` zEoN;`(!EHD&SSxBWCso0NDch$xFHfp$uhzEG0sGunHI2WS65Y6S65e8gGZ>QADy4Yml@5Lx?Icl^jS9R<-$)^LJB8w7JjZy z<;MJaw_J3A{}+q@eLvaD)$!T=q;>=l`M*80`u9=|&@gddKp^FLH|lZ|wv z>*jmunq{nfir{6WETu!FtPpc8JwfW=x6e#`gejm*ja+W9!`79q@=H)q>@!OzG~7{b?WLHNJ>d)j*-vqizRj)Qxb4DYorGxV6M(&>PC%!a)uDkz7 zU44q}=jI`@w%C4d5G;mRSlML~=?V8dTw;BL=&6Zi!7bUVPC{DAy*tRn8vE8wZ!r4h zErNbM1PnjmlhE&Z=xTAVvXedihDde%+De{4EAlOPIIq74oGu>D{em+*grl1;9!{ab z@$bzs7p8FBiid)gF_o48hW3=($-})wa1Q`32`ieV$MPB3eHjK{45i6IVfm%wTza+h z4WHG|Rs&;LE{+$Z8XKfjCIAh6%I}w(f1jRM??xZNM&e8Ei8rS1qohTDW?Pf3^@Y8$ z#%A}NIq??@1tFNbUp|=?riA6wGUfNuOLeB4RN>j%efF!zr-o5C9Ix8DRm_eDu5~_a zY~!)=M#{S@{x-h3%_v^Cp=&IMR1t7qp{NN&TEM&ex9TfrQYk93Wj+XlulmU;7xUlv zFKw>xL_#?opIR@a`JS$7r}kE#W0xiimC=15J3w&L_Ttn1!hSC|=vAHi>HfFy9G#!K zd*95x``_$;)pi>HLya@=$_4L15AU(+R`3S=KD_X3Yl9&=S%5{jYMj3w(KI_rk7&7j z`V|2tS2CBI4P9aDE(dy;OWP~s(&|Hhw8rn+phn=`?q0#^0MGAdsQQct>@QkVp0=dXMM8rv#sA{ zuZrYWE__lKsa*Lv!mDOwTs0#*n|tM@Kz-@GlyW&)=D56_`IcxK-0R7oY2j$O@ zgVIgwsFK#{Jxa-c^Q!5yX$#R8^QehFtJ|#nH}xp5_ZzO|Q)m`Uk!rC#t?lW@pW{hA z_Xd48)x)V2oL4-Yeu8tahch$_P82xgsWAC;gMGVBY9|xU_KzLZ@*>|d+wZUoEmI{( zPexu{7vKKFw`gxy!jgR#Zld-6ziz$g4O$;dYwRQ8J)sV~m%if1ul5b}ukEk&{2c#% ztiHdc@Bg*$wn^2>=5Bsb6t(ER{boq-xE?#{%Ww`0+iKnQS0}^sqmdq7N5T7o+R%A_ z;W-BUTdf<#;MoiyOIpCYx&JoV{Y$C3XLkHQ`d|ODjq}O{^uNaU|L{io-vVB(`hV>8 z-|ruPSc12on%{%TBt~HU_s)W!X)d4Bbs`5#_z)ROtmE_D)3w@o&2a;mMi!tUto+zD z`peX?Pm-XY<&plI*<$G$J06IpvcLA{iR6hif-KtN#Cn*0KF{esfIkwA9U%2`)h+aZ zfk3X~EVBo?VTdZRu+96R`{3Tip8sU*$+@ic(Cq_n&^jv}lk%Y^HhKSqo+LC&2fs25 z5Z~TZo$p?seh{A4Mm zFCgL~K0B}EnoWA$=kg|2CdcV>TnVPCHf)zcfR+^Vmwqw0HuEHu!~zgn7pg?o!F=oOqN$k}V2 zUS^tD0^1BIVwP=PG2>vGH2E_g*W}N$L-)&3x3-1Hx+Xn;`tX3i5P;(7M1p4uaz=q@mq+^jT$Ve(f60b%rx0^mQN>k|@5&F}7|mVg9-bl1f5QwNR}@H$ z@cPQJ@2+42qagYC+}@Rm%~?}r`iWPp(%)!&$pLNZM|+Y#cPUSpRBdLTx~Kd6E4h8_ zp3;X14j88^G>_5lLZ~eFV-|8M7MD#b@7i|O+WhD&w_|)|+jcoia&jilm-Re!*3A5} zNwKbND)7YQ4J}LLb=3gMNbryu0|Q_fZW)oHk(cFYn=KDb@z7a~`Fb;*hYD|}E86zy zDkyCLFn&hQnL~LLhI-~WZqNDgo(X)W<+#(d+M!LD%-*TC64GR^FD{wdWCN5_Bgf@2 zE*T-Jxf!LvMD3{p#@fOH*8Emh1^JE3ZL^mjUjfrDWd67tuKO61@3FaD({s(xDHT=o zDsz`dUM?6qYj%F*W#T;kmQxG@6Mt?ZW@pr&x%ORt@OSZF+D0Z<&?@rsscoXOzS=RU zJ@LC7NXG2rJBr+@o0HKFZ$&507Oi_W`GxyNo9M`-9VsnKo(X@JPtS|b(Rd=|pOxh{mY1ye`~ius`K@4Q%>x(fmNFjY3@R#2N)kq z@02TgbyYKmr*6k}^9DUage}Z*@aEhEeX0M9|CrIV&MhHq7&8J0Ewh7&&qc&0+Bc~E zOZ+D`NiUcC(*xFhG`yL`bxijTYJ;9ExuRSW97AAj2zoFId zgIoRn^vIU=6WPCm`PVJx{CHEx*x>Qq<4rrpO5;sCRToF=M&hEzpM5<$afI@LnVhDI zp$MZ3Z0nkO;fV11JfUw1S0MttE)k29J|qv*!{%H#E@bQFvu<} zqU=?#?GL?;pf~M24%{F`&22NuN4Np@dXE)e&Zew=akjzLQa>%*`fGZOxu(TIEK}E{Cnjrbo;D_We{0x9-cre9aY=Kn(pOE#~8z-{`-~Wi9$QJvty$uQ7G`vPxbW37UeDR+vT_Ld;H|z!*3buMQeT;GqeSK!|&xxy~fmZ(z9pri{W>-$^))A z`TxRi92I{>zYf2P-zk}XoB4U^r7ik5{0_?0YfSC*8~E*zh2QiME!wxF-&(t;pfhWe zAD@r=@{?%soAWz^>aFQ_>~G+AccxxrYB-thThnip$^-iC^nc-Z!EfQWpyK!NJ3P~G zGyU$pq(%Qmza2958dD2zY*B9WyNby05WnHqwJ2{%zjvwltNA_nckz2ba7_<2t8mURCxA@%Bz+&<#vqHN;FF2;ZYF;wk zmPgF*_2J9-y+)~JvTy$z@q4xX@1~|1`x*|s8&<@jjeF*CjS_#RVY$qe@)m$PYW9DS zzjnHZS0i}CJiHAz0Pp$#n7>qj@Rtg(8v?wHpL3*dSE+U*e7iT)Zj5gi9E)1JV%lZ$ zHXZBRbyBtKwV5rmb?t{rt0R&**bqfA;7K3$h3& zxQzXLL1h(oU@7-WaKh?+#BtGzs%p69xK}?mP@QLwzN6_4?F-zYA7TpGreC(A?f(nQ zlm+A8xgdlyE{tXJ`B23tlPDp39XXjegsuO=BX=zj-It5DHKpG&e+Ko2Q11{h?zuKTxf;%6@~ldn)}k+H9i z0_{-`8}9@$jK%|9(Fx6lVQG##(zpJXTE7vt9;4PJzI71mYpqwp-07KSuLki|mNXnS zp~GfNF3PUpcng-Q*TB9lBo*pXd|9clBqq*Y1%qbI(O{<6BM*S`MaIpI8Qf_oTkELbi})9aM+w zm8$mK=r;U8*Bq<=;nS%%y9DOA;hlky>~)lxK#lH+4nhz&ravR8-A}gk%=Cf~9)qc_ z7f^TFZVY*5`splj(*D%;OCV;sy^~+`&FhdQ0hRMD=@&>Om5_I)21^)9z;bN)~E*xS_6q4dqB7BX3`U zyA-R;cNIGykuFjmu8uwk0(s`;R3uHcE30zc<4>*OVop(YSEc4CsNM_TBq~5sk8ap{ zs*eyl7hJ@D`c??=kpB2DQpZ_Y4fvR*QBbpc;RPb8vPKow5B#xm#!g)QrZu2ff7J`U zHppNoPHlZV0o&ph_Auv#nDl$rCHmJ0i(K0yDOcWeb8q56Qhj`k$ibLG*Et85VL#K7 zHtBhhTaQtNWaoRIxep&(-p$e6MNP|@zfhUl1{K$lKF}p~`J=b~=($wUp@Y%~Y5YDZ z#a%Ed46ndmjzR9h%FsMb$YW}JlHyCLK$7CcN^xt{=1Mfxx4SEYx@S;V=nk@=-|PA1 zazB-dXmpABnmh0H+oipbu2gtl!$cuP`QxAHJam(ri1o$4P@~e-KI;FT=Y~IR;f}HI z^(YRRM7erjyJj3&#)+>Y(5T8{=dX3sP!r%hSsq}wI~qq2uXzRqXbx`G*3jM=ZdcUY zrL0<@rVwWIIIzf8ZRwjOSFORnJe!BdYW>wL8(j%(N{E9_6+-4d?+|x6y)7u-VqtLe= z6=cJ92V;>{G!Dq8=N#U1HKYq%6?UQ!>mtMu@efw$_d2(bf6?3%=Xz#blBJ7m-I2dK zI0xVs6@C}IA;fZb(iS(vDw4SjzoZu8NC^4e{N3U$t8EO4+yN&Fqm~v}0w3Zx5jZ{& zo7+SDP@4dP+x1^WeC^B7dM?!{lASj{1?wjJo^{vUQ@-|l zstp3eSb)C2=8mTNNBH1wcoT~zfd3>P6jj6o0wq9-AK>Hm1&Sdhxq1k4@pTwWQ(+m$ zW$xjp*O-jmyLn4f{d0y^uiNN@D>9pTZ%MXXk6o4fXnAC^9v>oJO5eKiP0l{WNMmWc zC%$2!mah;T$fv<@Q+qH~S?eo01^U4tskRh|e#Kc`?%}(vrOVWE@i0oi5dV2p(@WN0 z;``5&`e%J`J+55al=_-V?v{TTBaC_iMz9wUcz5q=7-8~1jS;S8I3Wujdp}i~VPkdU zT%j^L(^VxYfKZ0%NXzANdMu&YG~ zK&lkT@QGV-kD>VCblSD|yxLvu(!hgPZ_H|UkJ{za&gM_CfbBOah*Q@of|>d8Dw+Lk(%%6 zUaZZ{`li@UWpz8iBe&P`(QT>ZO;EUaD(|9cRw0Iu204bgwb#70(7P8FGZ#wP$SnAd>IQx4}l1EcPSstl#ABcYErCdVqm!wI5xF& z<}fW@E=f_EN&QW}&;i@%PEsXyQ^C+uz>*Xd<8S{-g)7(!a zoY~Y>F{h!Xt~R-w_@*PS4cz(H^uFMo$V5{%WJFR@E}ks89k9$#7Qt>`A28I43U|D$eK zaZ%LMtk~_6jgOoZ?_8Wsv-YiM_MVZ-MY(6D!7h4ThMSk($yjtKK25&TH{GYXMzfYG z_81(xbxogbcm_cL{dbZ`c1g&HQTfICg0r!NZDcQ+D1KwmKIj;02s1OrkLkYr+w_Q0}Ng#)sHfe1Zg zbA>=LPoNaPjPJb`iHY#Ii_C1^`0D@Y!7Z92*8&8!k859@u8*(jBX(R{I8#I4@~L=) z_-FoQZC$N@)?nUw-9f^OOiK1lk6Am$DGX`jWoBP_qtDgd0U45=%l~s9W@67Z<_FLq zr!xLl>q@CKb8CvFv?4U`5B=c7)nb;^BrH+{i|DddVAHH~{ju>qA%Z31U-%U%i|x$C!Z)1^ue*F6jzfd4#{`TUcg&r^XZ zaF2O#*)FX!v%q;ovTe*%?`F8Ktv0w10oNU`J`$@Miah-9ABOWz{xzv||^L@{`*;`wfLf2~zA`cOQzx=d{bMkqa_PxHwXCIW27i@C_Bb9t*`HkeW5Ax2faimHXxM{8yUw>t!%W z#$b>>ny^5H=W&Zgmn1-QPtcS}W{mUP?M9&C|As)iccYnZfM~54XE?$f4!3!TXPecf zbpd+ywv8s6LF7{I!t>-_7$%)lVas80+V;nju3-AymUGo@^Nv=lFk>7dV!3zU6x(AF z_FO>Vx&&Z|_L%Rx|FC0JFT-Tu*`BPS@2FMM-q7vJepp2vO^~!dyygM+!gKU( zGteum95g7r1iSu98!ra-I(P5u(jVHt@gEe;hCZRiZt^Gm*u{lUJvXU_3WPot@8t8v%u+0}}4*N73$)NP|r-J6&Dddqfa1Cd<7IOb6+&HPT&l+S3)lC&u3DRVtMC$rMqWA=EB4e;4EFqVd7$?bUy5jVvD4E)h|Bcb zk`4DUKbm#6?}{L=f)`f0!+9p5cIN2)T>Eo2HQ^ai)01h6XXNEdodwWIerheuX~^o6 zv8R3a6iyy+*~Q2f!$_^L!?0~Bd-0IL6~Nl(4NR=XG6_ehIfU^vk!o=vqRNyX00fg% zHqEc!#`?ET6KEMHwf;R|_ov+Kr18ECjc^IPM!M%$3VkqmxEOE%{nkaO%WYz~WaoVW zBq6%t*MJTLP--i#AD%lQCt#E7<(t>3`Io-=*Z2G8fA-CFZ6d^D6=IosLuXmyI$$i} zZ@)Tje098M7zp8>7B7dCX+;##V-%FmTI^(BX4%(v)KsjuAS&ywDOGdK9Y&TRF2=|k ztG6pN!B8#>OT)aoGy5jwtL7|?0lRP8r?TI?i}O08@;cs5>+R0mM-=A-X}&vzKVei^ zi4Pz$cDbF))&pT2`DzohEw}cnD{|cT?iZiDWwK5g+$^OMk&Uer3F^5CC;COQ01PZ> z)%@RI3uk1ZYLO=6%G573!e)>buq1NH?wyCA0%l@A%CCpXzSdp;3K3>F2MGRHl^L(Q zcJ(i&P#zRSaXVG(=gRIJO8r1-SoYM>zNtITT8tZ^p7+xkTiuvqya4Q{LD@a{z#1Qa zlkSGl`SrnYHNDxx)ddGd(}ZsGx4+X? zrm*^$&<%bV2~vr3S?c4nZ^}WM-MBscMtD5Gu(5+%Y8>H}qB4EFad@70I;qW?vo*g< zL^KNGPpCRLBj>fe@Xgj!y8h~TRR;J;2&O~vSjDm{|T|O{KK|N4=b|mmTe!xRbn4ZdbEn& zLfah~)Jj{J9vSFKd;3coTw$&zA}1Kb>DoIdYh9TSxlNNx%=1+9EdXE?%w1Fa^Z#Jp z*_sAph-L09mRpJ0{fJZx0mkBQG`0HEcEXQKHb*9JVHo8~tFX>}&o^?T^#-*$v5#%@ zqy2EoHP09)770K`$u|T>#f_@ex2Og#*CU^H?Q2tP^X9M z^_Cq;))e}^xP+*O=ym?T)`TfYYJe(m?DLe=O}qV4#_kT`@k76sTM;`xd1CSywdDtP+~sU)Ic&j~-ml0{)>?zTTSi!y@_}br-zu^iFy)<0M;bLBO3hNZJ$XWfZ-L#@x^)llS-?!lX@5JvK*pn;$z-kWeQ8XkKT>L^w7b!xTMQ-`yV*O4aq4ekc7LwUw?9hle-7J!a1QPN?%QX3 z7LQZ=vKH-6_U%8si}s)S@!m;m#`{;_K6v5D#=CtI?SIxPoxy)6-~I))S3U*9|8(`g zt8br8uLiZ(rqX>FH=^96W^%k(vpv=9GXOH|?A`gTYId`_ozjqt3~BjVZ<`12x$${{ zIGmszu$bRQx4(IImNlUAbv*1wKFLN~30(tcp=+ZG=o1qnV!yw358i|Db~eV!sq1Hn zEk-bb60&?Z=geQTBi_A$9Zw4P8!h%zhx@=;Qn*l@dX4`~G52-qR{nI0Z#EL#cqb${ zP;!^-+hveZ{u7A9P_k#P=2l2BUd{gLoApz(qiB|Hbh`^T6J}|B_0j*%dx9J*E z1efxxgB#RAj1JuGzFBWI+m&YNM(#`2UD%@@X@(%n+%C^&uivHo3f^3=p7}fLw|}p_ zfx}z4Xp4L2IRkJoD+S=1_$2}7{D$^uk63}8wb=<0bIATX_JHM^@hP2@a>ko49P>G<`A{VI&q z$`w&jcVdAyOK^B^_b?^0GL}{kPmgQ%_s|{JX`%3F;js2qF}XHXTCTX|MZC;i*uda) z`ZVouDdO=9OCIEQ4N88dL?5NrM5Pqx!``P#BJ;#DADL2~`B;6M`((VBpnLZ-u>p@l zo?(a&|L$$T&nMV?XNRUFPzbnt(GYasTQ=^^T1PK_Qby=Hckbr^quU=p~(Q(D#Cx{Cz@_8yXOM3;ZRSj$ABUL&v*bM7)H1gJ0Ae4_fYVm zeg>QQr?ZoO&Ayf9xWOLYvwUng(8C)Pz#HP>Wh?BR1#f&7JlDmyzfSE-eEY(neUWdU z9T@Zu;+s02_Ts}*St{KinfBgClG01s>Km}v?nV~md`!x-m_oS?JhCS#0C=^&ifS~Oj1t5oH^ z)}Z?je}(?%&;YoI&z-ba)BAkWZ1FisO=qd;5a0AAn$qNAbvfL3Sr&hr`_H~@wyNDl zZEI9|UCMBJ_ zwvbcz0@rv|I|LEgk+aRRAopdC&{1tkr1gR&F* zT@U3RgOYwl_N|-l+kJQ&?N<8UH=IJd=fifg5_~(8-SxE(c48~t=^ozLW_XL&gz$R&KD=nWu`qQEDj?*Ccw>*$LHrJ`j44pr$27XdlP%d- z>^#(pJL)_Zz2rzZnE0>RuJ$NWl(Z#==y10CY^E@#KCRaYRl}qFN0K@fvS2`5IM_lF z8JxEtw32NGaC(*Zcev@Jjh;W%LC-zaHraRl>B|2CosDlKufhCFfM<&Hvf>$m_ZaYq zJX|Syo$e9f7P{MYos1;A8P$V`z3QW6#@PSMlynyl)`)Nz6B@+Ar*s@u1@4JLzqrTe z;^JB|cn+&__^0u?0)w|HsP!kyZlv2$2?3I-UnKPFP^oiVuLIU_XMdHu>Mc3y+7ba) z?tVB;py@_+E$-_*ZiUyk&m{-fn`&(SVt*etu?}2&HG4e134f;juMAy9(d#QMFqNyr5Nx z$0XXNM#3+$2k!Ol=BnL5+NI}v{Sx=zpV0T$!ur)kC^(G5MXm8BuOo6+LEUNgl((md%-4A*s zGjQ2)`?P!g?o`S6pXzAbJXb?JX`A_%gV-jwDJsoA%X`Uq`1@`yb25hyPGYQ5zR1Mz zQyJ?6(4dtrtwf!{`;UeAN%Za+YVt76^an}ZKMHRjV4I6!-{b;@C~z|A5->MmIkD^? z65_hpVUlF6H?~I*Z!BOdLP2Mto)uTzEYFh!FCeD9``Uc{^w;l;Hpfh(7^8^~+;<;s zC!}F(F7cc6%3p0_e|C4qi~>V7+X2^=98M0*`=AWmb2T+X?m0<{VrdxLZJ{ClP8R`JJLwOXYRF z(?H3)a|C$f(W{>)DwQnF7ao_F?wd+QuKrU=T%ypIG1p)HC4fgfAsa}zX-_#xWRUUr!p1VNs zXJNZ#xilp&8E$5V+{quBVG|n~-@HflzJAk;PsIL=mgeZbG~e{>ou=<# z1XKa}UEgZ1$DUL53Vpp=+3TTl4Doei%1xLAy4B@j8;?6#gbMm4YeVk_faJJ&J9riU zg@&9(KINBfu78K`{~oIxs}ZA_{^1Cq!2MyaEiJm1P!7b07i8LT90V(5`Z_1*tIR$& zgD7_I%`p(E8!2)7U1t+F2l_QUFSL?<=O4<%9m0gRnz&CSg)>t>!aCVF<-2hKz=Hwk z>CUd5_)g7bWB($cLO^?AC)8J7HNkW4jyW2DE!Vr4QgQ{r$FGZYE_2f;&14&4ytWdq zas%0gpqsvEwlcDKS^5ut`AGbXLqgp0#cYjucoFMxVK&{f`KuNGv(rK;#7KT>v0+DC$XQjiM*}F!ghrh=Z>7%y!;)KEHW04i4+PxU~ zYknnv3m5tR+vyajr-?^$kepuHMn-C05KJD@id`?wzkRwrBXvTCOUeJ}5&l;kgu8l# z*BuPPr(X-g?>7kDP~YT$aC&YyZ^we7duMr;rBVGq_3_U`+UKP259iONuQr185qyQN z!8W%G{(AZaezv{wASxhr5v-wgzU>OLhd;;vCYz`H6MiPzrh15TnoZy-(fIhHoLGse zB&nm<+5BrdcDIepQ}(Mc*1=y35nrG`Nb*)%5ZvQvosWjCadLpX_t8lCZ0?U}zgLkU ze~FoOXW9bERrW%6deBh^ZIo)~K_(ttDZ*ibmRAUvIiKo^(g%8a`S85$-{i`9s~4lD zF5$5!CTI>VgG&jEDCOw=LwELHWo>!i?o!I#$6P7Rn1&1ZqIGxiyB@;N{DwmN&4hL9 zPKR^aHn!nd#{6>;^*$~h91Jp?`AyG*cEX15lRbKnden<#SI+eU&bFQ?0E7h;v zI864vO#LndANtMSm5-+1)So?{h!BOu{HC}bB)*9zMipo05NIVYa_q(SsQJbwJZlj57an|fhfp=Ab9vccL?=;YkmsK z2ZZ?_T9?~>B(G7#RomVc`|c4dbE_MkFLUl8(hNU zy#v7GNa4}azF&?=2Rb;TFE##tNDWeomL-pvM!CC<@{h4yOh`M}uR`>AAe%*5_qDv> zmZF2aJ(=ZyF_Rj9B?hz{C9+Z(rn|zpklP6JoE&!+GwYYpF$&{*Up&pxn9f9QUnpwh z=dfFneB`{?-Dn>PCtj?YuxAOJ#lJn5DkS1Ij9q$5?-$r3+Hh^si>mv7CDHa*{Ttm! zmuVZn$!$YFBGR97?6|MZi|Q-X#;vC$s4u(l5o^%o-u4Za&|n{}Pkuu;pRby~4)XUQ z;nC6{TIaq1KHQk?a%onOo)1R%NF5Q#hw%kDE_G3l`IWtd@?7r&3Pu%cz^_12oV!F8 zEpxB)Hyx8SW0{)(InAC7cwhM83giv7x&iEJ+xv&EiBE|p+Q_G@??v2i%9ckdQrmCn z;_L=9b^>z}`}lbG&i2(>(s#85Du}9J;^_zdGG#hGXkLGiJ9F-F$}t)Hgz>RwCkXKQ zW2GXA7qd!A3fM?K@zs3mZ>;~MG}LyU)}z%!m5JdTYp-(S#@c19RlMm3U=lrN@~BTE z35V3Pww<_TR$+n*zAo5rERP2*|f9Ct`f__DJnl{NbLw%(TGDF-YGezk2?4Kcw%)VWtHX zFcYrM7bB1l`6`cyQ0?T3{!#(YoQi-Ki8>_>ubVj{PU$JisN`tSSmGMwrJs zH@qDoLxdl6OniwSr#sd?^KtYmkfF)m=hAgB51i0%4sQxUA;)cyx0iGC!kp@hj73s? z=nUU;NB6f^MO=!YuApf4jwVG%>QB^gZx6Krw$lJ>`~a`rmjQmrz)^Ekvj#X3O_W+e zM>Dkfc1L8vwdD@DU*e`n&8A95|7i7YzODTsOh@WxC7gyoPj^?pCVc!t`)`1zO}{Vyz~|CPvpS907zdY@N^&lg>+(Xd>nE*o;S!7L%5Zu^-)b&Rt^t+AgU}~#fy7m=bHWYH< z-mkLhuNwtnr+UI(zc+;a>=K)PC3Os^|5l_urMqO@hm}VX7Le%WQzggkD5>@#D@iXI zh2&W?$A6YSn~M)dh}}c&)W!54tY7YAk4B!*P|#{BCgghguWPU8>pdR1Pxk`3z8<+d zvyl4^Nwt`YR{%}6SfSax{$%-!zj`$kwpa-sYSCYzr3i`gO=y-*v7fOn`gcIZ6AO-_D3xNi<+B)1Xnvgk(bj9vXy;y zzCqhPHd@j{b}*a~9Ou;yDRxswDo8jsvVw42{8r1{bL(X(5Ib&jZm7zhT6&R9d#oDD zDwQJk)c8H2?23zjB~MdK#9GNirpp3XWL;J#-K`#GkzmgEFb@^X+e4UBm@mKS>|-#~ z{|oK?miFKc^2ddUcC2wzMDH zDP0)b^NbnolkKk>f~v~wY(swWU7=^Jt4Qs6V(*nlWV%C9(p|@p9>M4$weptXIDIzD zSE)bA@2C7P6$vdR8Bc(l$GcBruyR{K%xSueU^&el`Cne1kewg!x7yeKX8T!#E0gW! z{AFsB2XpGgA9$+3C&xY0o-mNv2D2$rdmR6A80lWqHZZYFFYEO9K^(|Klw1E;eVOqp zvo%!rd}Xs-41e-3#;iNWSaqIjdb){R!+M-@LD`uTHjt=xIg)v*e~A+NjEgWX4HAr0 zPKs=cGo&9DbowFq9MXNE^~mS13-a5b^goc1cy6@r5M6F+k-zR{I+qn%kyt{sF4UdH z>plKml3TB*&*15Tn6#hU0LFo4C_BIGf-HZz?oOni%dG!B-?w*cGqV_Nm4B!8Z_TgS zr$6=F2o64Vhg;b|chciYINZGA^U?R9Az>j z=jwdZj%xZB)mua3{%ZVpHP#BP2sSVM$I-YYezK1&e>#U&oa{vtAD_v{$`kW6SMIpS z{ql93)<-=`#6hy%%J`4+9R-HdITVVaX06q?ex;=w=d~d}1h|wX)|O3NnL|1G`CH^1 zv@oJ&Zrn-{AX1}q>`n7@ghd1-)%kk@jTQ=Qz31X|f3s^XN9j8E;s?35o|nv#vlegr zG(NDB?5X7%VP~JoyTgS2bZhiZk`=%%i_f=M++ox$E15s`dH0v|jP7?|2l3?khjcr( zhjeE_R0#J^K818|i@t%cE!?2j$H!Z;0_?EWi{+QHmB<(|pwCDCN=@xAtcK}@()%k0 zTUV9p%9NHK!Mv{>ouxp6&ZIoU&hA3#>CC2-^Kaj-liEQOA1HXK+CA&rjm>Iz0qrce zYBPVl_8tf(d*!jTYQ^0;#}iIeKO36Nm=3w!(NBB(^xe;~PbsQBldm)M3YY5=6vasm zU`6Mu=o1y?1VzKbq6<}|7k1sZPkDd?!=fiu)Llhy1w|K!eLbt9NBNI?Iw%?t7Ja88 zJyz$Y1x4M$B0aS;LkAu1hM;KIut<+J%{V|s7X(EcxkYWr6WbIN^$UuYhDE=qXdksI z4vJ=mMcXs2b`FaE6BdbQ&6ru{zFO{?`E!vv+3UH!#v|@6CLnbv-;9543)CQ@YybJ~ zSH8*P6sK;3SEl%KQC{Uw1?35q-`ZR*@~eDuP=2|}Z>GFL8!dOt5-`vVci}tNj&&Uw z^;1A7xxV)1>%?z!N|CnOOSRXhY}z|kR(mGDQ@c~aBcjPvM+XEQ<*1{h=*SmqLRH*8 zDE>;Vdr<6t8f5H#{?)LptPhy|nKm%()^m*6BU#7Hk0WfUM`-C`iW3C$xqzHW!Q7O= zNZg>}sX_78Dqf!{7DuQ!78IYN;&m#XK#9H3qT(}y;vOnatGG0~_~@W`R~4_KIQ~{s zgLp%2_XrAq!WESIR&9&3+y1i5Q&}OPsjn!`vWDePu1K)#@&uph-To}k&Blc7(RdBb zwVxhL;5I@2BmY5mwcPAQAi1XSMSa&%v*vC|phX|lZ`Ra3xx1$4i?}Vrm9u58@RtDE zN-C5m*2KSU6YE#Dab9Vp_9niRp>0n^+m>r($kJu@eoCv0#g0)% zY)bZ|q{NM1LSCmpKhvwlfIr6_C>FQ>Wep!a2o$vD%EwWQQ>5S6qA;O34f>C8-3lkA zuE6%n*nejkBlWowM%tT7Y=Fr)kt6+gwy{!neD(z>OUo_hV!l~BH9J_alYM)r(OBO| zv6`W>Jy?w{u`Ww7Uso4k{&2hLG}u^1PPX^Tp9ffuXL%8D=ny7Kumz|S>@^;z;s#dD z-Y?shBT_4UsJ+bDt0ZJ*YOVfGe=?Wz+*fbge%s=-T&K58P2=oVu{#rjgpnVjO?(Bn z5a!$P)4PjqqW!6j2B0R*_uvR23Cxl?Hx2RosoVL*66(FZEQzP)yKWD#m&RMl%)IVS zIn!u6>p*XXzxJ*waE3shyC1|ZzT=Jt=P{YBQkdZzxq zlcD>aYTX$&hDR=E3^$10$-V=&Glr>*ja4=K{Hhe)lRDleI>(*v+g+)4ao;YYc7uJp z$&4jrkbJcx)uPsa*TuIxO6_94-6z{I?#eq-OAGq$);g63fy@AR-?55 z1IcV{c&{4$6W;Ukl(F~4Z-Xk$k8-+c5~WO*n0--oEqJp@U096 zSs||JG#h3Q4YRGM&1+j3<_>-zE8cgYGpCWA$aga2jN? z?!Z}S zRN1vyin`F#4NKVlm8(l-@s;_Jnq_1N3nC(uk@S&g_5CenrvOYfga{#~XkYJM=O{P@Hi{){aJ!vSAy{(t#@vvRdtLC*w(MH1-+vU5D%L(|>UBCK^ z_%3A{yJ?#eyy^HS)=}G{-FTczzxdmlJj^?y0#M*?m@Ot5ONyCm_)B$CO5Nq<#0@^h z=y%&C&~J;U-|jy{zbfd8d|l6{P(}+7+mz2FkmL6E?aolUwZ7f5&9pnzx3euxAlpk2 z)zm(8sR0+YTK|*n9;2D@gnXDqfMP*eqwDq?Q)@GNPou{{Cy&EFZqODxQu8*v$TUT2 z&ZU+ZRO_HsF#@z1>72l^C?v%#yA3kK(Nc{BqZ1}bXU&>sbUaN{obY_WHfy|)$gNsWD!X?6ZHdf ziZrtj$T$tJVQ=0w(#`rF=|45|hs=k~y(QA)M*96L`99|AF6sA?$vUI?C&*$Jjv`wPMN2B`ow3zTO#L$V9BFM)%OKnOhu!+zQ-%laGk5QF2^Tr1~nGm z7dQ^>ILCDvDE?`;V&l}NhGH>ovUBOh3aK(BSnRrpSXEox^Got_?2%K`_tcuwUnl9a zdd_sOAgZY*qcOvG3n~rb-v!a$u}pSedr=m|1@s7^6(1fF`g%clLlDrK5D>P9fao?+ z?9JPbLW^w3)1V^3-l}e+GoS1y8D*B6=uJHDXY^ zKX|l1+W^`}LkKyvAVRWpPeWchFXA!J&hzrR#AQLMKe(e7^WEDghrA`f2d&o+fww*e zHbb76#anS2nk?^VgtNs0>VZ%1)G^=#(AU{<@K4GMyXSxHGlrd#F4ZA>{dzoDL>yb0Aw2enfP50y(j&lzPsTb{w0F{jE8@a;E(k1vv&_g zz)wFJ=*c5}yKZXtvTwI;J?)P5?QY1z%fa%gDsLb9ydkMtgv-|L7WSLpFC2^>Q#`QW zFW^aLCy2w%xm7y=shiTi3YY$jwa+Lfjpc~?oBfy!jx ze&AZKcb{!BR_d;K2>Z){!J;2su|vN4{w^_eICNQ zRPde!QzRtY;<-ThC`=>MUQjn*7tUGjF5qewn`o-CtrH+y?aWvBnG^YoOX-0>lba)b zzk8}*E{^zdtofdPOMJhhXc6k;l?X*DpQhg59Rhy<;Eca^W@+Ssc`0&*6Mww)L@)1y zfvfe)=yRvwenpRrC!0Q#fh#QMGmELmX#ci$-7~6lCKR~Sb^#EI=2!0>o%6||Iqs2; zzy01()5@0i2=Two%!Hly|0}n1Yh|(hpu41a+2Hyf(1GRk*-q1%Qtyu3mhJ8Vp57JW z3EkR02l;#%`tev|D>M%56GAY$oKE|PnHjW#>vB73*nyk+H#Y-@{^~s(^ExDZ{YSNv zeV^hV_cP!)Cpt}^(!orQqNLvih21h;Ulq(C?Vy+j{etD(Z~AH zuN)K6hspOoGj^2O5h#MLUEP_J4J(zNOR2m}ojEE~CXu4xXfqfOAA&nL8U1IMe{qdi zw;ZSj9rt$c{l|Aa!rvh!RY&a90EcP+HXPXTUdfKgXOvlIv0R@)Z#NIGU~rz_mo)dT zKYl+usVAkq2L>GXG(fqugH3g3fuXFAhvPp0SxA{>B z(Eb5a7c`w-1lj7f?H&wEzWdqKrqq^ew`ug8L+@Dr%U zd(3s?*x{}R{6q%ry4?n;2)!Dd`okpgdq%#Ve-X<~@}>Mq)Gib7s{!6pUNLA0_50}6 zk<>zrI{%4k4TW?hL2>ujF?q1^!3q(R|MW?YqkXV}h0`J~ocQOzkc)6;_*Y?_8U1PJ z{~v`|yC>m0ixk7t!c}$B+E2GHJW;^Cpk%LY>N(l@V-%aaC>R<{ArVPUq^8N=R*(iH zk#na&4e$~*c0+r*3;qXsgq88P*0kF(Jk%SX&QNia_=?}|77{4b|6+ahD*ja4sb%i8 zYcy7q%bRjAvR3wdU%lQ-JS6iM2x+T$pzGOUq03esa}Z{4by2i#HyzIsJw6cU(t=pT z4)3ESZ;fadEm^$BOsN+Ur&(i8`4xY*(x>uwZwlXY{SeVNANRO?w}P>;sktOMj5}s) zDwDm2EScJrZWmvZJK|)e3CCfwNW)Le@ew#BZL3%K?@FTS$~jOHDPY<9 zmYcwJXB8wC&04;5&qayZv%cH4=WP9I$FCC$9?Pxn5RIRhpR2elIHtX_TrrEEGFjd- ziE%$>bV1XpMcGm|>=PLK&Z{J4<;Yz&b+vvVpTYZS$GZy;g1aOW2|xAS)%0ANHcv>) z=A}9HG7lJ68>-I0{?1AL2|j7AFL{Fzi4vSqN~ybYlj&VWur%z#hry{s9z-rP_Lkyt zTg*T6X%$@?>S326Z97C>IyGNU>@E`Fv#S%ol&Syz1CWKsp6l; zEO$*8+aJztjBRhKELmUu31BvVgk`)F0%{wUFWsN6l!Cvhz%8bd*A0W~`%xZjQc^z; zhhQN;WsgSqmYvP=@gITgK@;9ORkUHS#?8p+q5m>Rj5P=}P87cS2kZYAj2$fWJpQAV z^?R|MS?H0PXZc3#MrHDu30BJeI}L`I8c246yL6;}*j-W9X$Gy)eO{TkG~bx0KyV`UB`B@!d?d9mfx2Z@p(mV+$G!fX}Ozsk^SM^>p&Y=vZ4CpfMnZc zFR!|S;WYNoH$bd+15i#q*QD7TC(0 z?7RL0Da~#5a>g@dGm2^BdT3Y)MT5qMRMsiqqWRZKS?);^5=AYRcf~&GS5(%tSd%ID zM`hy3{zN(OWN*c+mF+6qR_Didu0W}~ej_Y$_VAWU%Fg2+Qdq}w%oeS?vA}&@6BO95xfAqD&%Ug5`4+4Z zTr5^Jt9qzAs;i3#e2{ug@ftFUcJy{g8A+sRpYM)+PKx%X{IZg{H|OQuv4@o zzxuOi-O2gUl4ySQBC8vrhjxThP z40wj-vNh{z@^pNgE8S~k?uOX4QJGQ%f4g({qh@12V?OKW=WFTb&Y&OR0XMs&p5OqD zdC)wKkfHGbwQM;{#i^Q+=lU4|2S`@kc4j6dNN=^e=_wo=$pfx83c>{7^KUImSiVe) zjHKzJf3j~U4({AFkx5` z6-9&?!bE$6v__YvC_b>{)Rz!8zy`AsuI4-C-cD->x%M37TRJxa+O8 zol@?ZE*AfY9OUA5uWQ&Pg9>5?$_y|f{ew?sCG*C9?vecNYS`gYCePcqGU8!6ExH;* z{c`4+?tgrb9p^qnaft+g*FPeL(I_7WfajEbfs!>=)AzjB7N{cPDlUW#mYRK`hlfJ1 zmg?Do@EBeu`G$=T*3D0vqr-s=+aN3~SA?14uews$VT!PAayhtUjl~b5Ud6<%6F@b# zceL*O{L0)FRviH>Im(N!=@bg zNAah!_OHi4#h>ZvagKEw`$+TS-Ui2>P5Ak$ys7$x4;_@9^Xvo0px-gE+OyxGpXg=; zC1OX^etu^|`~k%F>YbU$1zKf9^Ir6!^OAQk>#z!M(!mSo@ZT_d9kT|c_ni84l@@xz z>^}~RSvT+D@3O>v+8s&wVODkY3)}_YY*!jaH$nBJS`y#k>@H~fpnu7x>Mv319i{D1 zT-6Wx133Bs%Y7M0MSOmdd-Hx9*hLm|$a^M4?^I0OuGxvyypE`9T_BOmko_(ix`UY=lgu?Zrsu}iz2+Y6YPjLXU)3N z>G+DOjc(id5?h~j#*$_#qq2I9)W|ibRfBBLjVMcfl*c=dB+`xkeFaVQYN(>{JLa#* z7Tl$L(4!g$XnK{wgMX!BLAA(D<~MtmNKGdJPjs{F%SG;(uJCK3QSgv$y%@14l8A{R z?D?hDIw!q@z{IRN`S8=wcw^_!D`PkEeW*b&6xCqpgO*#iCl_kZIntJ{Shcl0QsI&!Pc4P6i*Jtn=_;cZ@ z4I4X2gR5ZaOdVt>m0|%k#khmY(IHeQC~G>4$NeB=tWoz-E0`@MDZn5T3U#2;){i#* zXfPhGv!}%8qJ1f&u}b0a5Z9*EA@()IV(jH;+ecYlLQK;)Gvtn$^)(#OPmlZg=|k&a zetzInh8Car87e8RekVYV!xjauxpVihT#|`{XqkQ{qkSQQ*S?uN9ona%zzD=Kz020% z_H`)T`eCMYKSk-XK`=%8=paZJJU?c7^(E?r3U{w;g?p1VuAYg`a6M^lZx50X%id-@vpX#Eqm#L<_zp9-<%S@rtP-xy zkAKTBzh#)$UMnnp@^$I{ZpNo?NpH8Fmn2w>dx=dc_Y zr_1R!kGvf8Cdif-`8m2 z)bKZ*CDBGiJq@rHCyWJ3q~;R@kncUd&uw?_n>}+gIdr)QbR##;+>KO}`NEU$WgbR>pXB@*mpoW({5jPs7Cf z<6+iS65?_6@80=m~lzYgf8xgjT=p5eswMYI&YB0#mZesfK=VY!2{D1#w?eF@PKedj`1MTZ_O4?X7E2-AFD;}ww9r{n}4>K<8 zb>4`(noynD+& z@Lpqk2=09y-PT#g+1&Qsg~mwV4a8Gxvt8DBVxE|%B5?s66=t!r z6H3pQIwzN}!7leqT)mk{t!Ulek(b)i^&q?o6^Wk{ANO34-ZPXhj=3l0@pR`6xxUho z(ZtWCz30cqMJN8eiq_R%q&sC9V5PmkkJL1QLFA==c)jN2*37I-uY=-Z5IOslmN%nd^-FSNEXZ#JR#UoPi*(1?J`aypGb)v9azl5B|+E zz<=Vwe|`<%uMrhBvzj1-U$ZToi`@W?T^MN?lWX%JT-~_0JUyd{^U?XP3?(P$RZf6I z{QF#QRW|$2S*yx*#{8c9aUU6V=;^fb3Y6J#a%vE~okg7%@wF3Io7Uw3ErB)7^tZ2J z0a!?PeY*L)8C+P=ZSur>dNU)vv*zExVzrA2j%(1>6SrW&)+YU8`8?XQ)XSMK)m=*` zGamG236%*jo^G{vqM_*N4sK`Wi032#x_{r>zbb&@MWJ} zMjeH0-*?=7O-hy#ChVUgeuCtqBQ!{Y?713VFoZ+Oun@@PEVaCOEqGPHPgLQQwUq zs3y&kN9*tB^1Wiy`331mG;b)8LYm+eZQf5=JWfVG%q=X}wJE|=Rt^Tmz)4VcVQN>b z<|)zS@HX0M&ybeqf8*^rmuMoLKF;)Da=@bKJ-qZBJ$QC>)-QRLB|k=PliyKv#N(LP zAfLXFtjL}3mdzJCL}~_jYPxm&hK6v+D^|}n1QiHr5^>#je#hO}>3?PT5qxrg6Q5u_ zv`P+mvrlg1mP3Hyk^w-)ZABW|f@s|Jh~DF|ZEM(;Sf-1@x{A(4ijaU7JFn$9kX}kqvi>Z$3rMUlvfARIbE- z{hPk(o&=aabvZ!GS_3^h19T-y1xz0TXqGmYd<6RmJM>>~YC_7KLtFRty~NV;{&{5b zOgc%Has`Ju#11Z_Ggv5@R~k+BnO8)iyX&+tb}3Tx3?Hyd2TEAsRJjXp{TGg%372%u z+dhT3KN+oe0=%N?`e^n45rc+^A$=d2i%H*Ihd0*Zv?7?~)B40D?uBI7=^Vj5$ z@TWP|m&&m|&*WLB|CN56=TO!Ci(0O6f8&isB~W~z7543;!uPuJU9Kc~KKEn8V7xj* zOFz1G-heQm^0H#I^Sv7PO^n-9BJ~&MQtK(MgTU!a9!j+R#MSrEzOwE>EvxJ$(U|ge z6bANBq@hn&uXO)D+iQ~xgi(CU#gSX?rXefo*si&vzV!Hov7QY(@YP*nm8)Utr?UxTsY^_Lq;hhp?6OKD9QmdO774dBskGkZ9`Q_b; z4y#C>&y>t<;|+*PMrnK|yem;&16cxc-2s4O6)*DG8ls(jN3W3znnmH^KyC*ne&Vs) z)Sd>WT${Xf;1Rjk+o9~dau1882PU>>@G?Bi)3S5w4zN@IE!X9$C$znsXm1u1>Jo4>Cyz%jjHfPO$k#(`q z)m|hg&efD=@uaKv09PIZz(F41cb5Y2rVPMK>}%(#EdlNYKtdp!^{3A7KN0^_=-N6* za27LE+{`=fkUz4aOw6rl+n;~>D{wJVa~CXAk@)hYWVii$F3o}lKgujKjyYncDQ~9e zrS^Reixu{{5UslO)q}t*QvPY5gI?jBhrgDHvK5Hmyc%2@J4EiB9l3K>`kALUNJ1beb%|`ENF7eU50Tw;jRU~#0 zkPQH-Ectfi;T7@Y$uueri==0J#zQ@y%$CU0aPtGA{SB^4y+X;TsJpThyh4$)xvt!u+2v1q&vbr08}ddm^O-;6IOQrn;QsXqK? z3Qnc!9Sv}5D+{MEuf8okso5R~XqI}8QfQhYbTeDWXX*F&YS=Qr?XSMVMY-MEtjaiLcIVdwAe+)C+T|LW`26igV>al5}=fNvO+v9VMp zRE~QeyH3W_Nz~y1=(0xtLcKnn%_q^faKfMa@|X#=Da}(W{#Djbdb5M^hTH8nRE<4w zjn!G`t%Y;hxYV9TJ#0X_L?gRzbZnTI6C8_j5X60b7J8Pyf8w;%A^T~!czW?urz>%h zj|pvgLDTH%=Ka~1r0ddtz^Ie@NmG`*A~(HDvpjRmudUHdIo{xU(l8R;PqEbfNST?!C_U0QItcF7Z+vSw63+;>#&_L`Lcnq)5 zGi#0LrQb(q|8@)aG_Yp~?1%vDC+Z5Y85hBO!>4)Mj1AF^0B+863AU7 zxdWiwv;f4_A!h$u8(;>AV+3NN7}=l4geQ8i533Jag;oS^lb z@iuergB3cB_SvZvkP@;g8nb^+pYD;GbsF6JeU6XRoKG6P#RjmZe?e&*;^TwDg< z@znh&v0r9?&;_sxG~w1$IT54>Jiz%!jKmP zDTyphf_oaZC(ItDX=6Vg-oymI*ZPESvi(@?J(%yjz3`mmYE6nq8i=Q^+r#81cKCfP z7QBDlXe8d`rv6C|h*XdY8?ksi9g1deq&7ulf#kEiZioJ$U=Og$^Y~+B@)XSKrKd}r zFUTjC%Hg)oa8aleX8^Ugfn)r}f;O?!{Q9%;gpRD2x?+T%w!CEKO+%wqi)3m03OhWd=>|F@La9aCO%#P}m)d(_>S7jJ4){ZRbp-0By~*v6(` z@o>xQM&y;3jLM7sg`(Km>Lzw1U0@8*%h-RG+i6he_21=|WlO7R|<^NiQ{ z(L_}m;Dd$xM-)_+j4G&ptad^5SPnMJ;a0n;r6razVRho$}NaHZAlRGB=L zhXy7L$=gnN{>&`?ELj-2T|T?=k~bsCzeuR6-YicJ$}LY0z+us`0~-2# zBu-*~vum63$V+E+jJ$MGURj5?;$LkqPkbC-ncLxqNhjs?FIjj~bw$;i1FM!Eyjktb zSOt@@xAIR;b|jpoa$$bffV2J#>BEyI>M6HOZu$6pk|fVUNXKoCymZ4)kzG%8k(W-$ z>(GeqiLc7-u(@BdPn$_6Fp=+#onKB;$oO0YbWz|V^!7{D57FdN*AGnQ{lo}w+B;hH zy?%qz2FP+)|B~YIm8Fqs^GhQyZOr{XJ`0QK+Jg0E?CTJaiumlp{*jl4b!5yZV9JdE zjS-A_Lwx0SG9Z-2m*hmn3`jn(Hsu?Yt9$usJuDALK!u zLaVUO504UZvU*1u>haBOc+Gw7b_lJz#P$kcZ`gq2a;z&oT%`EY z4bsvJ;4W;)Xcei8JDpaU9Q$#;B>Fi%S0uK|snwfXNm!ERMy=aD@v$Bnlztv4iN29P z(B5ZvWEUK%)vl^6K0Y6L(u-ncsxJNfNC&BNZ$*u!!0g;g9Rz8~Ddrw5OOAV&qvNu= z0h~=#cI^$J%Akceu4Tp+RWN@ToRON>#K#y^#j-aeHFxuKew%%DKIQFM9lh`7`9tlj z4b~y|*c~UW>VpER79ffd1P?Ad(%5i$T~r|)(fgmH7(1NT zPWqz+H2zD6amPd(!ef@KdCB@y=PQZDT6?0KR@yO3{O79iDV?|%4a_BbBpuod;e+WB z&%Hcz@pXkIb0W9N;HXHJ<)+M87RH+!U6K`^E-N+0jplRC8Y8L68DoOm^0RYcX2!_S zY|6wMy=};vyqbRWAi6*>W?1GN1Uudc_6r2#KoNp@VtogtXXZ|dGF`_AfG3q87o|Fy ztay1Kot#aJy3E|y6@3LJBya6Qv?!|^#i8OVcL*FFa*F9I?T9>o1jKNUlF|v<7?+4L zM5vJ}1sFr81TI<*|8vOnB^EIZ=0>x2x6;Fv#yyFj^{6@dNIQR3tf#ElR97ZGsRY>q z_x>eoiCkT)iaeN1NhB^4i+%^@{up4Qn$5!!Dxc%RN^$%euKUqVpOxtnd`{|fko5bz zfRDKz-(a-;IrkMV1N}eN-UL4CBI*B6AV5%b;(?-~MvWRg6BQ*Ql1Pw=1~)4rD4@7_ zpm?B86lDc82{JndqN0zky584%--0WIGotG%;DL)8Jn1n4o&iP4|NW`{erG12yT9jo z{e8V8-|6nEuCA`GuCA``MxbM2@Y~Uvq*P<}v{UIRlHjm0%Eoc>DJo!`ThEoX4(gU& z%_z}$tGk&TxZ0uRV(9_y(Fi%5oMNx_1jK*6@yiFVfFJslA!B?{=vP)uoPF-vD_Jm*UCZnQj@v@o;dD-|I z81c+ofuAo(CBlQW_}i=jHoz4frt$UtxAQmuEr0g0@C{qx&crP|JlAgv`y)6{#?|Gp zrCdVSKRJ9zyYpm`+K0grx@s6i{ax!o1r%^8GTVWBg8f#Qp!w{{;o7-h=Z@ssr!c0L za`d(@cX3c|YEW)lU(OcH1kU8UZLY&k=Mim}qmQNm>h-L1BkLM5@gv)DJ0@J3J<2y} z$zN`1kx zzge*%z8Gk`3d`AK6R(`-MyTQbJ&Jp-(C2`BHz=KUgVK0^f%L-IVfOZQ>_8e2+s{9Y zg=1%8UKU4g%rwdjBk|)@OQk|8WS{Uuvp==}mb#XF%t#?CIK8O`J z_ZY<{Q_LR%Y`>$1T)Htr#fD}xE$b0(P6tPUmA|~3ty}KghjstoR84)qN2cAv`%l}j zmB5ua+1(iPb0g%qY1`qH4Auqi4i9&>;2!jF;{|uWhs(==s}kH4&qrn^MCEvuyegZh zp=$wENDPPh6hhkjXj$cAMWj}1+Gu^WTSfg)W;h%+Z@JEZC{T*~3lal*Fmduc2r=NX z7&i{*N8|s}yf^U=y3$osbFS-t0|z&?Ul)mdu_9)1++78M+KQbmXUcAnY<5gLg7ct#TS}P#Lky1 z6gikqF=X!mjotd{Hg~ZNUp8$Qx#!68Lg+HjzqtdqwtjkNFZlhr=XciiaPe^ZVY7Z( z6(MO&8p(f_*Jb0jkXP&nFRx#gZXvJBQF2?z>n6&HH}3`IGvxJtmETNW-=f&>$?J1M z^&jMQpRJ4!U+1UgbweF|5Pvq~!wOC&r{uMztHC`lxIkVT1@}GpfXkHEdj!``UT;;& zC7br!LSE~R`j7H@Dj+F&?P>DbGnCiDKwh`UyFyw+oi_&)I%|j9~o|`JA7gUh37_s%@;CPc9H+I8E`;d%Wx0c-$ z5zg(aM)(kT51 zWUBpezFI#EZqe@du*w(o=73@n2Yjl3(zhMDKW;|cJnl1uxZMMKW(@+(D3DA72?ts4 z6PkwbiH&A4J9!dhY{rkF#chMNRpmqLf7DI$vKwX|?KyE?z=Jmzu9Gx92v-Hm)`7HPQ!Y*t@rZ{JD2J06tK^} z#JG>tm226x$(??9s48Mtixu3u7XSC)_$OsUwK2()Cyg?Te_*CP{WKbWd_|lm{#I<+ zxwYW6cJ~+@PY#Z+vIl87bJ;qgo$yRF;mMkPIfXUBGD*FBn9jSkJZ^S>eEqCzgvaFL z{=<=v+k0oo2gdZQnpA#`@~yGa8Lh?)<5GTy=n`lP(Xhh_*)LEx@mD6S!k$U1y#%kJ+TQ|UC`sc1-I?QA0Crl@LOmabn zm^PrgQgOVj#>NeOnRP;2uvE~1UaGe%XHP^A-s)jYe>=uBQs?(eS`v>NR6P0gVEY~$>LjPvH4+nY!=S_mzl{YTZs zc&SWWjFs^vuCQ)%Un9+Jjd0=}=w_zh*Ev$tJdHip-`aouKtdKzVi+-hU`i1h&eDjE z{!!%;gBS@H7n0bcj0L#Tw?g$hj%HD-hBt}Sx27ZsQHP1Ih_}&dx{lzOkxW;X@}n*yCL$CeR-vZ5Oh2v9|5rWJVh=25bipm3boBl@Xbhl;*2ar-jpfd%ewPIS{fRl<)``g1Q zHH6z=a3={)^iA{S4{KjusbTr`@^|eKmY?FwcW7T;sbTphmDgJtCNC2p=3Rb-jZl0< z{P+oB;lHZzJpS>1o|;MHDgWRW<;RER+3Rad-k4E-Eaj^+%j0N|A3ruM&q|3``ZCIo zro8Sm3+bWo=&B=m|7NozNuB_={#5z zs@WSm==tT=%szS5$w(g!)qpZupC=$|D0X1dk&KakJ|ZpT@ly{XOADLJz7 z_a{AB4Pm^G*{W6P94&_-X_!&pCLUd_uJ-3dQUpsE&Rn3vOWBOkh&cq`a;8+>O-E{X!+bTc*X_|HYA%e;aseF#SP|T(!FEIxW5+RL{ z@-B&fHvJ(>Hk>B6Ob-_RNFA!K@ASn6E#jBrbF8YY-)7iPZEbTp0gJn71mEt+&&}NH zu>gN(`}s@1{}1!&O)qB5r_o-1KF!Mdt@-o-a?|=PlX{IGHg7VMw(zgwZ*6xYw&*=j z%mWZlqh7mShB?`L!+K3cskfw3^Zyq2Kk#&7M)y$P_0q_`f4pOL3ctPo8hLI`(% zS8oJS981EMTt9$|EC`f)l>`m{eHj*-x_{+&MS?T6>?N>^Hla|*Y%A?8J`&u=zo=)G zr6`@IvbOi|E8^8{jO{N&`zaJB-?Zbv%J>+LFerecollYXp9zOR#&fnq#xFNI*SYh4 zwt<#8;&(&|V5{Y}4aITPJR2{jTIAO3r#t>uJKb-HOY$zACq_k`3i*K@xp9Nxe;^P1 zrvt^lBiC&_$0*A7wfvhVhAQq!23B&Y2ln%Wd&R?_E%@^S`kJ75Ger;sQiSQG!S^pIuPi_L;Aah20t4Q`Td-u{RpvvDx?)eF&cTX{}m9gF2znBW>clX}U zk|~z-D@JJRfq6!IDYm|!Gj!n94)C?7D7e~a;0GuP~$0De>e ze@+Ix@_!fJ76Isikc|DE=0nlEw`hj3MnuOEJcB4g(sX$G)9{{*{oTKr)dc_PPn*N5 z#OCmU{yoRe>WBLGeSU@dcO~Dh^V*dDMGzR~{^5GYH&cSw+giFdCExs>vpSjbR5olg ze@j{Xhj6QfBQQ)ZUZi%>t{NJV8OF;M7r}6>-_j8Je;1)H@i&EC!YS@u%s|0{p{h5W5dR1LS0 zHrQWf7IGUJ;dYt2<2U)zo~qFP^!)ZQ^Tu+|4qOA1bBov5lbyA35@0ilM&yCV!!=xvDQ zaP{voztIvO3?6hc`YCjYXtXGJM2ES#^u-pGYn6r`)HL>~z7t*K{0#bhTN7WsN?NP< zXltU{7mcqDsi&cnU;EDub@566hXxieL+EnYyAPT;8X=6-PJ)aIJ#9k3s#no(!))Cz zddqKh>a`2i=s;Nq^V5j$Y<_yDEAvZ(_j3$=g4t=z_cq6L$S^kZ)>G8uC|(m_k-*27 z;sPb8`~sYMje)?QO#o6=~3(Vu3a`%xAf#N~wL z=BPfz_Mr5VGK!7~xZSJ_BS(52>ZTYwi+@-z3` ztgP+bO)nu`;M?MS`YyGcPyhUd-#(w7Nv)snS+@+^7ehbZsn27@kklAE%ze3!dAIj& z>eP)inH}aGi+B31*?%Je%i0!f=n&o@x-f$#vd{o7&t< zggcw>nd{6Kx%gEW4$>Yr+!I6a=yrPj{TqoHI9o3~C~&{B{vQRhMi6+Yd7BN62kw%! zf%`WcYTjk6Lk2cXM|%fwG@etS5jmc1{61MRLPC2t%>p&X`1ZcqSQ*XiQ&B&tP`eCF z5WOFwfO+R#e=70#bwJHyn}0d4yL02%%QnxI*A)0|zOe#B58R z+-^N~pvQBk@SF!888`iQ6{cd3X+{R7kJquD+ddOM2;N~~%u0rP4Zn9=?d84HcGx4q zDwTVA2u*T_k*?jiZP@=yJrK~KYk?+0+^oE9^=1ql)H3u6TNC(j4V^>g5UAF_dvY#A z16`q_74fgN3;mCLUz^Scdv`!zX^cAk7VaFLf=7Li$#c(AGPCNyUburMnXXd>dz=`= zEGUGtGBGwvkPTaiXrNO_aF?wTx0nP{SW959-CX^IIK?no?_RVsKI^5OwuWZI z5C;Qxg-MpAN;+gZ8c+@DBkZ4i+o~TkcF~WR?=xcy95K$gGdaQ}v!7CRU$a6N-nv#k z((=SMWN^eFTyO8Gh^f`E%RLP#s`U~6Ca(a@9g;NRdn%jGw`U9L`|YXc=a2kq3*q~0 zO{-nx3;bB%>5=3K2!d;IhN4y|KJaq*p&oeu9@MPq-|^C8&b@m;A;uM|ew}{PKI(33 zji(In&!_qr>)SaaBEDV5QEo0UhIkGKD_LI7nt2xusb{O;!SfCfBWr`hbSv=rlbt zljT|nAJg*<{-ig|{e{q&OfQ<$e&{l~~S^Q+J+YOU26@RCj=KJiyJs-jRF8D`)m+4dA z@4fp#klZ~)FqfYo*xEZArqCS5RKIyDbc!$Z1;3x+cQn5HeM%lhvY8&#X?}`Cr%8q+ zcB(8PtdEH*4Sm>|_Cj;4L)+V&kBh12{;-=a2<yv##3z5oo#b;wE_*hLC?nD|0Ux|KI(Je zj-L;z7^T8sK22NmmuBAI;a~if@$DlCmNJY~E9<*O?(>~NU9u>4C^w@(*5t_bx>9hI)=)m|lobkhQ9$`^7>F|^D zaTKNOP0VTy=@(evc+D0Yg~{fJ(mkEkD^T+&C}%wYNnD|)XeGUk=1ykfRZ)^j;;3gi z4%CkTQABvI!0O4tNl;yxu>vrker8xp(2pWwJi9vnBx!5~UMxRQcLQSu=!NhP>Ok)i zVV53d9Cg3t>@w(mEIIpOTZ0Bqyf#zwelqlQZe0-<6XKnBI_+ z2Te|%K<*_ci@JF^ISI&?X_AwUtC5q5>h1U^CMRG0L~q~bd;98L>Fpao-9k=|MM-!$ z86e0%c*v&(d6$PQ7344vnJFhZKq4m%o5{zT&EvBI8s_!=kO59bpVM6RUomS)~?nGz#YtMI}+nN5@ z&`6)xW2)F?XG$an|8!8QBi@Hg>z{ASZ)R>pAG{+o~65#a+MJk-Vt zo1d$?_1?m*U(jK!=Uu)3$ft(cYryP2+(zrfi7af_&R(a4H+({o624KwY@fh3%G%lA zD&bL|P^yIQlyI9*h$`WGC0y?laN(|<{eu#&P=YnyB4b)f!v+{l>gMxZXn%;|<4e8q zflr6p7r2J*Ho0-vXZs6%?`VJyMAww1$O_yPL9U zdwwsdklpzcTGZ_M#{u&8{FglKiCr&yzAvTBoaRcnf>} zZK)TK3|IYuJ#YGv-|l|w7(ZJr`Oq+)AdG>1KD#>@mr@h6Stl~sf(l+Tx0R&%v#b~h8?89Lam zSLt{YXKL5D5!+*9(+KR~5g2GIW5hkLcIRwI350W+re3+Qzl%l=X#6g6TBpxu$i21t zyU2Fs*i(Zk$1hu-@EKqGT_lfmkhd-OZu#=AYgCVrHspa+lRnDDF3)9sr{BeEM#P;@ zX|SX7%mj~re`lNR;<&%i37bJ-hcGj9%*&kXOTUVCoiF<4e-|0vm>fn=r^iRxspnQi zCM^!S&8?A3k=Ev8V$<5j*v@2Myg2BzvNL35Poc>~@wsd{87QKDqt7^P4!ypwMieg@ z6?9hF8ObPR)jiQNO;tQFK5i;k2DvJl)6+n!39As24cZ760C z?P#)>>I^S$2EWz~G|gkT3J+$7!<)KAz>3-v12bE;znM?&ON>?(lcht?NDp34Vb+Rw zXuuEqSPYs%rZjr8)|81~51EzIgxd*l*WA%y?rHe|VtoE@wDX`=)ptlacH98ip7j?mWu#;pix*GwJ39 zv4L*ddp3bQvlg}fy~lc01hwAqzD*$KL2bC)>9>-kMsX*-Xf->Bt%HHUC=6&%zDxAY zDXnRsll}e09*BY;HjOg(X5SEGe2Bou?BT{A9KMZJCW`C zdvmTHkGhbgi>tZD)j{sCca5uk*TB`B9P6unwuP%l!)>PJOs=l}fTHj;Jm#0-Um`GS z%{D2%9{h>EQ*B=B>UU4^bq?)~29&RLwG0jT8r_VqYuyMS0>0h=M3sysb%r|`2>4nh zLcGh#PbnGNQ}G4fkj)gS3Ur4WA48hd|9_5_jP1cz4kPA_N1%eUa#mrRqO2FY*?Viy za^#tGg?-VY+2av02yy?)X2o%-|DhT0$)0M3U&?f+Id;r5lemUigdYFd_DT%d&Whfd;X8#hGs2cL7ZK1mk~Eg8>E}g8~ZJv z&7ywfDST>HI1*jW7M(0nI($*_O4{-=yJ9fBdRTHOxuzcnb5{a;KfNGDuQ=?(_wIb- zUsv1*c~`Mei06kR`*I450?{eU&xVrv9zT^?*T`qZ=_=XcyN*wm`BUG zq_xSNzL)WLDz`N7>R0b?`m>>Tyros298hz|)@f>H@SRCb-wbNFjmxMBhf%9LIH0B| zd67**WLquU|Lw*$w<_P%@hL$qy)$a*wOK9i?wW3Mub`H2*tNQ*wqLBxeUe?QmU59{ z_1rm+P=g8W>v?mlbZh4Eo!Od;GU}N}Pl>moDrt3R2KAIC?>6f`T~F&9a4pa3iPSv; zV9=(sg1XRgy@zgAS9Yqd*lDUK9E`1Q{U+a@X!0oPaXprrW;iQ}X1LGm+pgep`O8c* zTuF84TkOp&_a;gtV*sApAdw>@i~X_99!T;2rGQTdy$AhOds=@~2DCre)$>WT-_Ca? zp9by{_DAe4(OwlOl2+F@puH+N5nl!O#almQJx@P;gX(0;KF%I}so?sG;$_;LdGtL7 zEhGGD#tXkSntHf}a@avMk1Ke80j~wPx9%gNxd61T)7z}7cx4Z==Dcq#>3*EM6B5_E zt>6}CMREsoQvtKzvdg%^djcFV2KfikAAYA*+vUcXf z50z_K7p%XqpHqj&_`u$jIkm4j8k#T#VDPr?R4kR0@vSPUu2yJly;s~eBv|pSm065v z?8U1dyQ|!mOSirs$oA8^#-_te<2_tIrtuv@ECXvLPsVPdEIMYn_5i)-82^tOu|n=Z zq0_Qa{Qfl!f5v~O=P}^_13kn-oPTa{R*FZ&cN23wj1}CxXs@Nwl5a2b@hKty!^;>F ze_%4)sQZJ=ADg%&UqM;y#RIf-9dMEBu7!OK(UJ|3>vS$IF=R)=&g%ZjCo@gmeK)qv z)|oOuG;!AbV(S&n_SaM<*k;k=Z>tq*keIh$R${=8(ZuD28hVcdGwVYk(TUCV?uP9( z>+fT|FFPzW>q2_nxPO|cfBglbuF5evbE({2{${4A7m(xf8L9r>CT-7y0eu?HZcMQv za@`>^u;Po7hw|6U7j2n!FKyAufS@hAaIrVDt`%yS^>Lckr)i{r>NhRlcE9Rui7anN z*7v>$T$m_&8K#w}_A#sHmXbxMzv<22Q5!X>FVJlb12wRXA1`aYZ;yLc#wTKgw&Hl7 zy)ST_A9NKZZ55FL0FBF!maHQMvw{0MVmM=4`V$|GG>`KLKmu1&2=@Qa2$ugWN;(RD zH_R^HSRQ8!to8xt4YP|4t6ll`^afrCUCH7u-Ta0jdP8}!{^8!fjps%#d5!E}i+T*Cj$85_7%xlIR?AbO7?4!aR}qkDc%5Ft=O$i^h@F#^0BcV2Lv@g?MPxhJzT z9*p-&b0gbDdqlRY;?AjsMG7{aLIzHbw%7>ye!su_#+2_jF)R=F{%$6AS27&>gMPnR zV^lo;mD@#rY{ny_6QgSOSdwwZE?=)pSh8lxX|7TWAgg7?mhX3O7LU$&E6UUn{BCyu zL-BM?gVZ%#uhK<-GD`>VC2Nvm%v_%9w&4h1c+^2{#GV{f6zw~x&t=4;4eJpt8J2rl zj?%;Z98w8z`2)OtJKF0tUioEhPgRitda(rgFFQp6xE8*_$rkq}fggdh;=tHqr%s1VI``> zPAQ<@!e>R2qHyBg~Y z)05|#VF%vCp#128^jRQJ!TRD|8?L5ERiM|~(Gqvr5OpT&`EcCIVQrg`gwyH4(!RqA zFYgx3rtD(>zJ-@CH`Z>Zt*(^NrIuJheJ;^t{P-k&5!cr!F7XG zW8zTbnC%@Vh7~GKzt=oXH8pj#GI1ioe5;v!G$}12!RzmmpD&vj%smYxqOZF`w#_{@ z!>n~(TcycZcheiMs)(O|Oq;j5tzY?%^G`Q6<~VMHdrwhBg(ITb-jkW0KM39k>Adb@ zqz0cxa$M&8an&8c{1MRe!B#@K$O<*M#}*m?4r+#f!Tc2kz&!_F)25p7ee~-jHD=6! z<;;~C^G^{CP4&wVkN3`x!F#Un&u{(;-Vws9ID)ju=NjIYhcu2tz2SE7@FN934tW38 zK?Ax3rGO0~W%A8sSf8zgc4U7W%^rr1`9pZlBE!DJ8f#i;ZT#jVaQ(&8a<*uBi#j%= z<o|-Pq%}oxu*h9F?XM7k(NnS9IrhF8B)qRlg8Iv|U%PClnhkA{IyJH^zO<$#yZV{r zh5Q&;KQ`OTSlW7^ek^ltl8C=v^8MvI_WIbZVR@3B8E;eE*pw}L!&LyXnHL>1mU*4Q=;FOp`+ z4e_nLuYKkgd6goyM}e5%d#%*IV>58>SdX}g>h|;1)hz0u8uzt~$G;H+dXtV2g;nz?anSKru10E&)`v(ypnXa(bj0QaSlO@;oB^jdL zSY$-G$Ai)ZBv;UZI$Rm4eKuXg%5lDijvj7KnSbCe7QiX&Qmgk|_ua!VM8GelfXxU| zyKe=F-JmMM<^bYpRq%YS>jkO|Lbrm@u&mwMN>8zBYaV3;W2!veov-@dUw*(JuAk&t zh(X(}O@T}=qIw-C#mLg>GtIXe5n@f`;56##@&sm*ao_jt$f((nE?cVw7*$?;wKd+8 zeHrCLI;X!v0aeN#8}%($aq7GKpo}87k(g0mneA1&gF-C!s3`UUmldwDmWprNlFMUQ zk-HZs8!YlfRLrcfeE02l)}u2B)nSvEZ1Prfd|NfY>+_}$Cb@V?vYsO3zOl2ZR`H0K?WE!-*clxKA@UFg_EF-i~g0sX=Bk# zUjwd}*kB0hwU^MWbB9 z&5vODeN*}<<9!*Od3bhKTEsm{Y_q=@T-)sAbT}b|8_esz>rP1il*mkWH)9Dp8*A!*N#uYcxFd(k) za|_1%&t;K{#Wb2_^$Cfa+v4+z8@XN89RLhvTISs(&^;?y!4xkk)1k6vH>Y9)iPa~h z`C+!kZ^Q!@H&Ov;E?(h9=$&nhlDDbsz3ZT_zW3?4j9cynJqJHPZ`nrc-802^J`NPBf`}gkuz;8X-6Wu=mm)E)?acXYM9H0MMwa?!($luxL=Y{ys z2=G5O$TLm%wde(Lizly4oL%6}*a`)bUVF5}GQ5#x=maMG9`b}Wn?6fVg~`h{;4^9% zqG0Zg?A)NuMjtG-xaAC9I4(7V+j+F$fE**w_3M+B^+HUZNbryf}>&<`uek$s3`7jhTxq6a%ESbR?z%x20=H zo8J`Y%Y`80^_m_!S<_Aa$(E-&$#EO8fr9%jh$D-R@%b1ky>wTc-#h)cE-%0-(BvLC zz^+-Vn3MI5a@V=T*-(@blPId$)HxQtc9Ymzy~CWWyv_5qY-Ox=xZr0Ud1Th&$Yb?& z!qI#h>yXQl;EeGrJ?L6Fap#e@%*(L)Q}z+otZjp%p>5?pypr7jmxs@t89Y{Dy8sJZ zBfcH|-baqd1;LM?lc{67~I zxtVWp;+>y`QnuIggFON>R42ae%b!Sj9#7NO!G<8I4@vY_H>Kq)^~GctsSlNw7o_Ep z#&0{-Fc5C{;iXTJpqW%HUr{87sJ5uFQ{(ed3)s+YyxX}bxsoCZ30clx?{9kbI%9^G z74{-gyY9kX_xImo%KpN7$K3K9yT3Tk%Mptc;P8}TxY-BPe06qKRzFD*(fc*mOvvIK zAX)CY?IaS{OpqXBIWKm{?TQf~vY#Wn>{S4GV!om5)lPh{*Nge|r8{H?HZ2LTm^56{ zRGHw(F_@8?=;I#9RV<|r!zYWEAx4V_$A20e|3-_24|=UZP=}kOo`$V{p;Sa#G5GOY z_9{$lYq{0E!og3By4YK+2k%nV_5BX-!e-cl7hBurY9&Y9QRAF#xSYq0pG{G-H5b3} zwOQe7mpfd{f(;2Mk~L9^h8Lj0dGHF@?==yWLYLF9&6m21dvH$f9S~Lx8$< zT*Jgd_J1<^BMRw3Hjb|Tv{yMBTx4TdgNB} zgC5x?^s}|=msUE{ziSg;sQF^58frasL;;=C1+OTlg9pYxp-XI6?3@9vH<<4G(H5#E&l)F%ReXg4a0oA9TJ>Rt=gjRroTF5%F-0^)QfXkk;a=$=H1U{d26rP z<^4&m#wjZ;E$12P0Uf>0t>0Y(GB!Zpui!-D`zU*rA~?K$9_*7|qoe2OB$k4O?mt^g z-0EF2`dai$ZYG>Ujq<_t@lvdHrtA2e$?ezwL~hSOl_9qibKuw3&j&)5BO%jZaxZi8 z88I@yY}u)(NjXi+e5lV5*^SIMY18rbW|@wQLqp>+w%g5=;4%jh)6yctF?Nm}EvWC_ z{RgJ@`rh?BnT3$D8LzyJ-`-|CAjd4VBV@rT4EFJVd1GLEpWYMe2!n87d=+`OW-DB! z**({ooiF{-1aFp1u+p(*IKaK3;BAEJ1r)|{j!oX)I7kj7{wEq;&PP*%w9V(EN7!By3E_Mc)n#G- zf6Yh!J)lr}H3hiWcmyW!8I8uM-7&Vi>r{g(KJ&APDTov6_zz6 zebPH9un@E~0qN z&%eY**B{qWfecd@8cx9*;-PY8cpLm+_cFE-&6d5k*qykYBoiJia7A57uUVKM?#7wE z=eIw@Xl_o$@37;+`O+56<%eAOGnU`#UXC{_&6>Pspyz7MsxFXG-KFNMY;J|lXhiSw_D1C-c$Pa&vorUi)=EX20A{ zedJv(a*N@cm{YFtMN3{}PSnDxZrP-6*cj!V?Pe7Gwbw2bq{Jy)r_@Z$&*J`7QY-5p zD7eSi-h0*2H@D52qHhHueCoa&VF&-iJ9^@)H8lKre>&4{_2FI!*F3s?v1ZX!rAYY+ z{t|;|ytY8S9oeq7U>bkjMcus!+@iX;Ie5H9^UL!bg>G(?=qVIYd&J3n&u14z-HPdE z7sbY?41SMvnW9vg=Yul+U)HiBDQ~6mbFK!OYyB17k6`rS1m&aS$H*%$%B0tO^4@n&bgXNfFi`Gy?el!- z&ilKK;qeO@!z0ZJ-LH8gWB8KCf-$ToGrR%Z(}s1+BjNhj9q3_?5bP-)_I|-09>U5v z^{}f@6^wt$c!s>Pvl|%4%V|haG;y@OGO0BR^AfD*BVQjbYwOS_SmB<3lw(T!Q=hF1 zSP(29aosqYv!Rl3ZnphG={YgfjM$^4?W*f{^-~D zmh>ssD20wSS4GP)!F=UbK4N{5Lv!XF;`uq`XWH}`4QAI-1A+bO9Py*!iPkpXd>U~x zNf`|c`^TymxrMHHH;tu9Zz?I@-T#lkz(D8!58ow=BDE#-j62;EP%|F_`g#JE{R9Dj z@&x=XgMbJG_<10upLDT&=*TUJ#@}!!a!}tKxz$9SusQUJd0w5I(5x-Mk9T5HiD?|^ zQ@8SAYyb1}sD5(LepUY&WYK>0XsZ2>2d6b#8dLr0x5>5owZj45BU1UgSOX!_I5ufA zv2xF{BK02T9i-IL+j*Glr40CHmqkIzMK{R>ZXKI#On*)#T#)g#x31Mdeq_$KN0FjO z%%FD+BWBLg0M60K!ty8RPI({l~Y|Z4n?CQ1d zrVE%@Wqa5OrtILnxJyK7ab06H-f@J#5X^F5{IRa+c(R%_KV{iKL65WF)9I?xjP~)n z*Ji&z=%0~qo0@(p&2^U_!z@{#wns_ao`zlWr}8yveH<1QpWIUv7jMGy^NHhn<4j6t z%qJ>$eX3kja$|M7eECM*dCKRK24>OZ9T#qyKgjYwCci1U^?qNs!(ZQN$+7>t_VoH+ zwP)iQ?dY*~XSV0S|I(f}# zW%}`&@|Q^ujg{KAC6_+>XdvjLH2h%b_!JgGwk$iy666tnL>dTpo1Ulc{lhCnjmF=V zuDFwLtBk*p!fMy1_=kiKJzpy0z4$nVzej3sB+&N-?LbGG-6yWrC~N|pGP?6v|p$W)6XF-m5ADs|iMXX<|CUQ@ThJ&n+n<;Fg!tB15{ zt0y0jG2AE7&=rh+EpMp8eF&FtIBl5+WEfX;Q_>xPmQ*)gpjjB{Fj>$?N;*nmf)2e$YUOxyOLl;w`h71m zA#T#S$}SKVo{sD;H1(ahx(x!0*q)COcg#z1>=F#ek0%)#j(`4VOdm^%RbqzE{# zvVH+x;jnKde2-lI|!UBu(4RKzYqiYx+(Egl?t`ef3h-K;b{A06vCR40_4 zLv8Mni09B>MPv;~nW2R`F@A$M^*H4Yq91VCu6O_3(U-l3vf}PP{O`Kb;P>Of?`4c9 zmA^CiJ>Pz}xf_Dt&+^;bdF$LXL`FR{iZqRaB6sA!LWw_!FV#~H&xG$7``hM@;I9er z9apaPMnv)`+C+SAdW$7bqLDnFz0Aj-FPQUq)Q;dkA2oi zk(X?Z)SY+iQ30;uuFX{i!(s%%wC+fhNb(bGy9V)NxZCf|wSfjnzCkMEAf-h zp36*6KB$?@KwJK4pvjxbcbo1oU3UG8C@lwND6MHfqO>|g3G}`6B*%juBR#P2A?Kez z0roJuE_t#Lyy_7gF9gjV!5|@6L|e6$@PtY5de$`e+t)gr9k=sU7EhMNSJ=TF4sP&% z!Y#K1yG4zzLfC(x8NAq(zRhC52__VEnhTTfk?Wh&ljbCwtZ|JPhw__i z@gviphcqSZp@a<`CK2X_8JiWU9cp5@&UNw`k=kXxWHd3V(0#L=wB|YuE0=?B5a+|- z__u@OF21;m^s-^7z8>y?ef>*Rk=h!A&7gTkg{S#@b^lF&HJ{O;d{XL1lWV_Beej^!!&i_4L1@+d@S{*4Zqha>g}H#BF}c>prIVhvq(yFcI_WNwFgi4HhsqUG zg*TgbgW<#R>HTZ5ixHjg?k<-Yu^+;QKD+1Z+`kY?1!Cnh;h>EWtwApekm2Ap4iSHT{- z(DxonpUP#SLFqeu>FY@?J&XxLSC$!?Pc4IaS;jq2-4!cS?{uk-MM36ZpLsl)G}LEG zH9ctgWn5dGJeVSB{fT{Ow+aOBn%S*8?_!i{pC?qDrudS2vC@F|CEW1E{Yit^l%KCNhs+CwSg_k7CK zn}U>uZhNIrnuku-#I`M~xh6L!Qrn$sk^izp7sfN^VL(>d^_yxA(^Igr>nhH@NFZp; zXLyigKJff#R#_AFBjUO;`49dCb{OpqiVRN|;l9|`SXptKd*?~OY9{68NUNDLi!X@O zZA;0D1dq5uS0TN*tvJTpMRI?C`l&s9TDKKtj4)}f8&cq0jF1zl>tvbZ0nkx23;<4j zeC2lLCQ|PZkJRp?5{$s*5a5;jqXC(yD6I6)V<)F@mN9Dz>;B{fA-b7p8`pAjIJm^+ z2O2K4e@>yjEI_LfBT7(S^sAOsQm%#WQ_jred!rGP7&wK{BE>V(prm+O&NKT^Wu>w9 z8DL^t_a(BJF@XT{+j=#GYw_#ELuKt2CX*(~sCBPc8DrW!;ynnOB0W8rzb#K9*TpNr zOm@pBfqyR1WpLsmp1q6Im1szOg2^HJC_=%OS3-HVwu#=$eZ9@T-q(QfRkk!_P|yB> zyZUf(Zh<@bYW|ii2)s~u%cj7p)c$D3GZUOgL3N3emo!K?C1C}8 z@gdfMf^a%^cLt1(56nx{+(|-YyMd$Qb^1yS7#|-v9u($X+}EvL!(0&`I6=LdV6%}c z59D_2ra+in%%_u7Vg1e10FBnapg(ijlgo0)bLpA}!yd}7k;5wUaEA3Gw^0by_U`m1 z8Q%l_#_bxDSFWeCto}ZWU*K9sR$0yG*%2QC&t)UN(5}7^zbN#ChwtOF+7=sXevx&P z&nopW)K~Nj|;Q5Xi2=%=Ob6{G`XC!s_Nlfm2ASsf}&o=NgUMJL6 z<#T+@?q>bbdtvgXHvgE@M|0EnD&44}KA>6pW4^2*Pa6(ww*ypgJ)W&Gb;NwZqP5g) zYogfcb_)RT#s!sreB%Gf3wr?{$~eEXp3JiPww`wBx=*y}`(l{LIm+ z^)mOp6`?DMsUxq!jk|IU^h}dpc##QP=A5ue#~-*2Ky$9wNe<=Tvd4%A#}~Ep<;i|wH`Q(Zh#0&w zcCK#cKI00Z#pg|hJV!e*-&K+lvS|P*OxLSL!XMmx9C0M{D2sPi!pZI#63POHxp}5D z%%PP3QI=9VYKN0dDY{p?Z$wH8WA&3-VEsaxkhVM;=p}z&jNaSD+4cdg7@m4p*7gW; zT>NqxfdbBR{n!2Kw_GB1IwZxMmZQT=wL9~pWI5r4gvU&aLa~ z=e(noB!AAw5-~CslM$9D$i>pWvox=e!uU|!6{kqmH>YiR8QQjanwoW-EEA2YMiXEQ z5xo$nz@_(wJ7x>qIecS~%VsA32qc#}fr9=9Dsq=^q~%={s+Md*udO{S-XRisp73e*Ff1$qH>jy)El9 z_Xo${NRoVO!vkR1`*3$Gw{x*U^rhr+NJV@@F-~E2$?8XHlPev&{IQ#xWwAA5f?(y3 z(Gxwn$t>iw^slBDxp>_7j2T*RC>8j)yX%ftE@lg(+znjAtLPg#$4dQ<+g=crZ^K0N zL=Tqjl1d8f7trLP+D8aYSlawuM1~)qnP}zR{R!jC*AK!Mx!Ua9^MPN$nMdIm3tq+7 zg*%6*HZaXjm)p9XzqwDeW^$h_^s{_`HIqxRGLg>n{Vx=Q`47n`#AUddPab(0=3;xG^fK%U94vx$cFt1t6bKq_!AL z=6^1<55#^?KB6_p6j1k$_UUIwdly8a*hc%oRT!KZnl{T$3Dh;@_+)q9Y)h!^?-MY4 zS(LAJPj^{ow|vyC1}X5RLHccB`V7+P(L&WR5m+5`$jAI(7_@W^^g(=4MPja({I%{7 zTLBBXgrLgfi5>1*EQuAvv6Oz#oXHAl<6o?|{!VJxTaQd{RvH{%tKAE0Ep~tFmp@{BM+F7#QtcS*ffyi)yDLf=_`$&22j9Q*trJE_q! z%*Ib;;s{j_3afs-8)MbS&dNw!?dJSlsAH8@qb=C=+=^R0&XRj`g;mMOg2G7cd=tQs z|AlRc6N2CHq}X#L`cB-r?05lHPqhwhnHTnJ;^qvt zH!!dzIp){MJXMLFb2ocDmBMqR$MZ+wxx?e>n4t}#;3=DfO>2hERtye5U1bGdJzOgz z$h^I4?1SOvKGPSP`;N142wtVNyw_V^>RiDlW&;~4Izlx$-|h4K+BVeW$iZ|9E|>a0 zW|z#5+@Qz%oSSM*zV}{8E%c;5G!jz3p$RB1riRo|xCNl8DH z&K3OA9{$=4_)&uAe#kWZI1gVS_~TRX`w4!$ho6!Gzq{b&7*5rHpojnb9^gw;@L!Gq zzR1I$lL7w`gC`l!gm0;{#y=qVV^Z)B3H}>U)Ba5Oy9D0{cxeC)H4{(9Kj-Hp<-M7@ z!;!of;-<87RCM)4{4vN~NM^GOsMxIcju{v8(0L5Me)s0>DM314^H;w7P!ONW7IHEDaFu7$DT`Tfd3$@Bm>) zkFZJz*FEkLK9A<0$$h9uYF4c~`$5EA1T2R!q_efP+RY8hS5jW9P&S}0XS&}ZfGG-L z?goY|HgHzEL;!;w$?|GZb~(W)XpHL&ZEE89@n^b-Md>IMPBd|N(5U{$c_#3B7Bp~S z0)3Mga><=_p3g1zwXuIBn{zi`U#%i4-J}{5wxj-r6ub)oez26Bh9K0`ny@`eYB=u{ zp(>KS1b4iLYr6|L*_<9OTW}|OI5j7P+ko6B^98r9hiektT`9OF!-3nu!%fV9YZTl( z`oq0a!!GT^2O?$1cR`A-GvIm%E&-kB*<{}b364(tBa?kmR~f&tEOVI|Z{hwJANzu~ z=I#hZzf1vTR&pXa8Ra=xjD?wwDB!$pesVPAVdc z6(5r*>(Mqk7iC;4^jM(!tyvi1z#Zg2^?n<@$UdYm~wl+Lt=~YsZ_tr`nEv<6J|?n_ccmO(o6l=~2NLYhpa{ zs49rIRhA%%SHAzMU{z*`apiEYMR-ojd#7KS^a1E9N7r3qijAimND9YqABLvi-%PGxbQm5tz{B?x{9h<+ zPW^tvg(B)vWSSH|GHUD{DYvzU`}}s`Cir^y5zr1>0yP5EGS&0ZEuUocJ}x*d>Y%sf z5#UyWkMW;b*)&ylh*64QC{z)>NRNIXmi2JAf9Cb#x0FzJASc`+G2?FQ2iay1(4`W$ zx*HkS$$wEaBdF(p@u|^sBF+FVDB^tx6HK0kc+r34xhMQb5BGroh`Nc-%MIQ>R@ccd2;oHnB zxDg^QTp6ABX%2svRc8M?Ls9+qX9Ixq@OI`CAc0bO=k;?V8;PX9Y}KCUM#eY(cy46n z!pL@|-$YC1SHD}DP8E zi{hErm>7+t&4@i~gDh8eE#miFsAm%=u&k#ts{9!D!5PW><`Jp0EIEYnp3!z)-W2~1 ztv$xepZsO{?%e0psXl=00>0DpgL=*lvg`r*BNlM~gKqVMBHMnQTLmF=Ob``dh@o|PF z`WN8`%6cE{6MCrF5x5A1Xr0^NY=-I`ng-~p2haqkhPbp!{xB=&zQf>Z;r$x)H$x_M z2E;C-NVnH*%>>7mRPC*FK`-!2weC!}Foq+34Pwk+C^f-tc?VdLR`8630Zg<4sJeRXr} zC+?}O)=6y-=lV(#4=1ug)~()+`LO}CtOiH3VC_bc-Zg8os~5O`U1iK`nhEoE6KVDR z%4Q8(Lu+I)F8iOgF_Z>rbKtr^b$yzMfeRI&Ss6Cq*)JZM1M!aZMa`$#X6sb%4<}&}Tzd)x=8E`PTw1zNlvG!9t;oc} z{ObJ}P2{U@a_#Y)Df&esxP&bBvY|_5`~GroTxlXua5Ew>nv#gXu{R+C%O`B1z@{*) zT5k07r*+MVY%jQI+^88~)c6qVjQiyz1?fU_{^6&}3Uz=wH#75IpJ}f)+p<9Y=3k_Y z#=rH?T{OFdRv&b6~^BOZiK(GYvAw44eQIx8k2yTDx zPBQk2O!iWm70eGhVTB!`md`yqE31QhwjbDN$QJKA&)SLiopX-)?f0F3JAU~0?(M^{E#|8&U$TTq)%?=~s(C1Sg4+);GVvTAe@nV@ z?VFmd?(#vcMc?#48a+4|u|D&)M)6U?Ss(b(wgHH+8UJ`x-156Nt1P|Et@uiO)uV-2 z_!B?Gv1UY#uyxJZY|keO3cRbklH-vqJub9Oma>|~+3rqY%eXJJUU3t4f5$p^o8j|R znD^z%6`qhwtisjqs0<=T1VjwUB%*btm{$GQ_6j>sPBI~MYbyE6svzGDx!->kx(dEzF_rN*!3Jzt2M~BnRuIx6ofGb8Mu=CyCMe4-Zldc!(;#R4aL5SOYWXt9F+y(`Bk{peizZ)4? z@2+~!_rA_sR>mRhRm3%Gu)=;I(41#!xk_qUVjlI+Zk_~tHKHD(%5d<>@e_=dCimvO z=#bm~$3_)(iO#DJ_0>H}Dz43~d)H^_hC%gO^J-H*d;)jf2Y#RNLNf82 zsmJD$&$+!DE@{V7&z%9{jz)M$*G=%z9UP<CJRv|vQhI%TgM3T|(Ec$bUTYIbUdu^lh0K$R0N?0y8EM95CEH{SVrtR1uK|3yXCu6dPyp_Wy zn)pY7EGXAYC4(RMDnTRdYwVf<8`H^aF<8>$%cosLnlzhS z=k3R8YM_StEIm+<#x37Y&m{{tPht4)sI(4wRDAI3gX2wMylMPhf8+O%=(S%z0%vt=c;e22RrYz@cshpT=GQJ% z7B}j6Q%rg^eD5xrIBu*8^)Ft@+0G8JorA)gEmpFE8Ng%#c|NBz&&`obmEQ$<*cuA6>2d<``5gaTB&*GO1xjn zmAD*}Bg#3|j~~fBd6;I;MUffTQrb1pN+N;?sTMcg(sxuzaEif>&0pdRV3}KirzxOE zVHKkzKIanO2zH_F;Rg7!l|k9spsZHnDw`Ln-IlThA7ZG{&F=nzY5`wCfgTJ13dnZD zWV0fT>?lfYj*Z%1joMM+CtdEU_>qaCG*T-QqddOq_OkWA%0cuV%`7W-hqOtnN(;z%#YMw+S&NQ zQj2x>s`h2iQQ5?#(x7=1xTk%AatcUhtU#o8yhv#}oXXN6jHP zf|wu0Tr0A}y(-LNZK|N%%T`!gnJbxOte|e$T6R;gYuKxwOdJ*m!9+!$D3(AG4KJ*W zzvnC58lR>o5NWR4#M~nr!Rbm?ul%nLzVh67zTCL;ZT<5@E$g2@QQG20&gze+_0kJ$ zg_-GT-Q!}yG@(d_n7dRG$=}-tumj8+S$@6a$9|cN+@A>k;bqtLqZq7Yn*sLbg@FAe^{h^6{x`K?@B2SX`J>sT2j#Pp zLoi>g&}=2s2f6NPU+z)wKud|VQp8k-$7Hm9Er ze=}1%TIxwYwPtZnP+YO~(|P)jvaRvj=Y=xai=UFceD~J$w1TOKACDKMDN=U;*~tkQ z7q|+v=FHbWRmnl5xWBarC^dlN1)yw?^y>Cy*zpEww#w|2DzjhvGWn6Zhv|xB2MUxk zRz?;#3fBZ#w-9wT?Ei#F8k^^|6D?YTB}6dzBHIXZqaPh{d*BH!%K-JHd)qFKwv@ zM`=samE3^3M<7ar&7oZiyiu4#UG5hKu|{lZ&?MgH!_ea9F_s^Mx?y+=w9wT~B~=vq zA-73kzX&h1MrtqS!#3PJ#8)2TxM2VPE;x-c8*jB}$x&Ee^^5evx1Htf#Kl7a(--wJ z7&+owx;{!QTx9C2;6;hylAv2``=Px4PuZ^PKRx~Bz!CR6vXhT3?Pc9B-+VzVWe&XXTFjTQ>vn-E%(Y}Af`_?oH-jN^vQlr zB9R=yf&?Cys+Wz=o@EiXEu@v-1aa~lFx0S)+Z=XJgM}lu%Y}yiDPZ&w03ZYIc5In& zQIM}RREPNc3>dM31{L#VpoKODOG)PNaL7!^<4x69IPnWB`nHzk4xrcl?`k#@fa&?*I#!)Viic^%$ z69_UWG<3@R%d%fC&Omz%#U<>8jQ+v#_ghX9)_iG)P3}#WcbI^#Hx1+llJL zoouUV2+0a-l}luu`wO_0_XVf?%D5DHV96Jgp9mK0>%<3a32B7wZ?d)$_cIBd$)E>? z8+Dy0tkAmnK2Xr}#M4#AmtyW)uK)BmqoTazK^@&O@i&|1L)!9E^^^f5c|QL}HttwSHM>KDlwqWZK9;&l?m@DBF=s{HRw@LCd1is%Rqkg- z2OqrNHUMuudG(Hea!7Xs_X#^$uN@}5Q8s0!H_`hL=EC2<7JZ~lRrp{tYxpRCG zt?^%{lx^IcqNRYZ*y$ZK`ZF&mexxt1s_qa{J?XtK#aY*um8^-(&~pZ&<`wbC-6oq; z^}6Z7SDsVURk93_3~#*or@1@Jo|ALie+LfsjWTzf6;Cp#2XA59nyq8oss?tSacR%S zP|Qj;QtReh9+n`;)^_td@lgXlhKL!{ZgH&DL`62N#?*MN7_V?Dwci}v)93E@cotp-o<9b7p7MAi8L_S- z!2=PqPqrdox?}!g`^?XV{tkRCvp}EPPeh)ow@yCflgiN(jhWJVZbLGmthE2g*q0Ky z#%%fj?`~AUSGX^?+52trE8D;4c5RbZ*2#h{dM>UMKZyTbp6h>K+ooH&BDD%poP6}l zQz%PPH{OZZ_ht1NKl1p|#Wj`3nz`&}f1lx&-W9RM z;q1kv9Iz}wyvW)QBeA)&en_F{3%AV;{XOU4Sz~Zf*+1GwT&H#vzx{xL0tf(pv2gK} zfyB2wrusjcy`1VR;-_VozlG!HB4BWh!yS{d%S*Q1)GPWHhM~F9!uGmE*Sc-HYL`WS zygX~{{Vw8%=ll;*FF{b@N(Z$K+&+c`V7pyohk%A^!GWM1fbZt z-&m_`1z#V5p>y6H+`8vCJX8H1+&0qL3j#?yDNja=E7;^7l*Q_Vup?LC%}}eze;je< z@Si(UDp9PuC|F3Wbzj9mu1$D>lApGe{6rp}gnpP$Q%+$}*yN>_>=QCxElVmNbIhhJevxr|! zvreW(wN(M$!Q?@-&vgHT@9o03xaGnWzTeWBYGU_-j%wR3FrxBo6Ww8zjQ^yq8`Fx{U9oXd9BEO@}0nIMg0}E zneJt>AYiK`T!(?C{HqUUOwxi-Lz6!GYn#^BUjHp!mGY~R`46Ro&3FUyl9kvGx35vJ6*(4;iW8?W3Qjej| zsx0CFtLoZ)RJ<(Je)p7>ukNV+XDZK(Vc4IA*nx#3(AxxXQR`-Sy)i&4RU-U+Ss2{0L zAmo-R8NsveZB`uWVdVaLHE3zV(y zj_+zJLH1^yRX1!fj~pHc?ojuA8p^4S9QR%Skn0L~IJ0rsan_CHu^*nlP3W;hbFeaf2^sx!h9!mh0c<^5z3vxaZMwD)4*Hsi|_W9;{}WLqWsk>W%o2 z7q(~})#h%1yLl}qhv@O>5>3DK>7b7NfO%R-j3GnIqSXAVkO8r0CaWbB8hD7-i8ZH* zTBh(F4#d$^ppPM6!GNHGQYtX2*4h1U*}>(oCL~&Z8PFTseKa;h;1l9EI@`RZ^P)O- zy4;>&gD!GQkry>fr|e9PA(bCZHl4qe{3-cto-x50n$>FXaultKEwrl{N9ZnKK;snZHypnPi+MMZahx#EyK#D4&-d<=EG1Yn;Z3rzwsg zzCFW9)Ew8Ve~cXtvH_-903r~cxu%01pY`4`f~ zL-+Vzy2pETkN2uQ-pB0mJ|XkH^Ll8~1oza_;@$BRGN#-r{!aCa}M+Ia-8BN3(YD19G#nn=SINM1fVbHrohA^7HjRDER{Mis*=z3w~z_xIh^|J|xp&0@q;2Xcv{~iP0KNV=`Gd!N+hLC+K7}Mq{wtNN3XHD zwwSsJ(nJ~uW17PnZ1GE@v3@uVVRn!S8X;x-DA-#tLZ|8P*X?J_1&&MsTBVf)FUXw* zZm?ch{@+g}>zNMlv#XyQs{gJq*Sw>1<+BgPt_D_{-@iKuOK`>{g(dJ_TV2S({nsXx zp{z_m(bzTEu5aLesL7e~JJ-Jj^1BOfvN0gT-PR^%my$0-cU98JXk$OFxuG30wR7nW z%`K1JXGD~W@VkC$OR)7yKMkpjc?$b+X`y8nwlcv@VfhKx7abt{7P)`TN5uNbH|V)E zuI3oNK15FD2J4P0U*N)^z!VCoD-}DKzPq2F`T|o=!%UC4COHxxqe7cr2SzPFA2&nu z#{_0W6~x&y0vj}JBaimelkTT{30RQxBMaSSyo-N$S>Vp)1wV`qMyz6*)-qa~9hS@K z5R!j-&YlGGuRN)?#64sq{ubzR2Vme8Ngr#=T05L8U_u0{`9i+q?vq_xz$N6xxP;4x z5HymfeTQ9n9}w zcZMXif22FOS#_8@*Jb~fvM}HE=tw$?B-OncmJD(YC5dTaPd1(z~c{+9^moo2_Dr) zHT?nq7LUh~=49h0n2br`kviO44?*B&Yn$M;0`Keq-WeWVXu9Nq!EAL$dYFfMPqBgk z<|1obhyJcTpsd6I;cD_A_7#SN+1a^_JJLATN3$~dgX`wFJ-A2u`z3pQ7XZ6)%?^jz z#UY7zXKxSS2P19Dmq==e%{cS&a0`=A>K>CFTYIw6w2lmRO98jyy8d)B9iZ`X?Y|W7 z-j^d1er%m2ZU4LZyw*+Iwlm4;7kolcH<5y0urkTBds2#07d)^Nq01pkYs#bvaZjV- z7srP@UZU`qYh6Vr2+{c83MPYxu80`Z0bzpPuW89)y{NmK#)u{AcBiUlV}Q}PNcmC*CG`45k@4r0<$!{2%=_BD+VZK9NFD9d_l~bcfA)#&;O87_w3w zc3MB*VIQ@nsP?}*ETRtkVXDJgD?Ee;J3;vG9o7?jdaA$P+L@~PfArUqe}7|tJv+ks z>!m9@`m5_(^w*2gz544KoQ%G$ztZ%b3iWI}{7VmXqQ=9Y?0LslqwjAs>Dz5hn!XqQ z!_!ytm9AgAzalAqVnG|%x;wa2zGFnd;bQ*127_C$qjR=ZisMBbm~UWvccCYU5Kc32 zmV5jRqsLv<(BmD?znhmqkEfv19{fAztQ0+72>ADzJ^6RTFEjXe!r#AHbdB zKh6KFLeunTn&mk~|7Y|TAF`Y{h+Nt1t}wi6?j&i1@jQ}^03>VORN~M6A9#LQd*^Kb zjpwiOMC!!zlR-!LyjbYzwCpKQCtU&T>GV4HQ#GD$^r;!!F)luM1Nz(x-S!w4rHBqs zpZn7EDYprEmKZo=KK;eB>G@Qrd8T}6q|t2pH{ldWk4Nm;&!+eB?C<H;(r-zr7z&$z&{h&nUQGLyYU{ zxRUxExAQN4kblhw#%ZYGab=J9|KK-wC(&gaL|1Zu@5k^!_3Prki@y3l5pLujg89D@ zZiGqri3iVqu$$HPyVoS**Sh3ScLo=TOqt(_HKiYZYH>zgA2yxAcmAc&x88~mznJ3O zAFvmFn;5d5z8(66r|-jQ`ucU!{K+;|u~O0S^wU#n-ii+?{yBk4=RN5O*GL|iDn6vo zH5w*1Ku7=I253PN*B3!A=jqjF3G|u= z9rqZoH!y_%*LWR;BclYEDv`6i_KSl1lwmB<;IF>DA5Ma)e)=cTfb)j&Avgcsw;w|; z?f>}y*8V+Y+W%@l;&IRRQ}e4wd3pcI6V@RO+Dq|lwH$f>ppF&B``>pV^x5U<)3O-) zJPzIVpidP;(9`F)X?~Jvc_R|2_?3~fyNPQ6p)m$44{)vc@n8=)(pEKc{CaT)U+Lez z?(OSk-G5x~i~!=jbA>S?c1DK%l5Ltvd090>Q2tp3%o21zrD##X=yF9UGzPl18?_v5 zFjD=FZO}b(lUCvKi>`f<_KcXt)&($>R@a8Cb5s@RB-t-hJK#= zg|>bD7k4s=cpBh<{_-Ot+_nDOlX||)Ka(TNou_B5?peij$n(zo12Ns@I6=FpP~=U? z;~cl{0Lmd9?&)v8(HMsKU!2t0t5CUIyML_D$9k2t(%{t8bp;hvg1-3F zU44k}i)Xd&r^V%sm1xl`v9?z>R_5u{by3h0zwS?gM~a@JPLiF&8^w&GyQ=~q>b?(6_AKi z9=$kWNDC+pJ-{m}k5=ibKrtM~oHi1VUag2Cg$i*g_IE$oV#R zW`M_e_67G_w-}*`4lqX0$yb>dkY>BKrY;`fTckMqs`bVHbM9f z?h`h6>a51vQY#`SheN-ih{?;xe36>-IW78MUaP^9(Mf9!4UdyM+rzA#rJD2MT=oYvLjM#3y}J1m_6Qpv5eeH|J8ZSp1SdP9 zOF40np&}SXgBHha0-#2>244?NmX9VLCA;5mv!U+#f-B+js0Bqzt}(qDDKjxoTMLn~s834?lNjYqieH&ywapi07% z(;Ay0Onj=O#Fy(a(U*RM4Avqq>q>+6Tz#+q{0}Q%nb%S6r8oL7_wyz3AKPYR1n$Kz zGwY0BLJcv{OI)K`ymT;O6--`vE_sAOYxn>_3GR2`JpUM9u>K_8wEvu|IKru^NxDLM zoNRTE{2525#*=6gPCV?HXrO$_l?&=fk+R$iKs(yOsM+8yzu#l8tq)-`Nb=i8n!9qW zrl!7t@0ZdEHn>$xba4IE+K)Mk3_KrDuHA9J2ex#*>Z*yV9`qYkp`6<|cSACFoIq7} zlO>>@@L3Pj6s4mr99TZlc$xX*$7sWen;ADYn_R57@9U^n{%A=iuY?>;0!MP#2MVT= z=WGCE2w<-aJVMJ%3xU&38(1r1{8He$2(S)JWei8f(`ic#_@~BnJw+MItQWt=h`+g?6Fl+vY(dsO9zDAS6W?5DjFZRx;6-+D7aAap^56ige$16O1F{Rc2zub zgnGE`S20W=yWBym9ug~BWv`ZIRx6sgd8HT&Rp zko`gq&sxdO`n~~bIL#C6Xnw}gRcllI6I$N zI}#tOIG>SF`D((ee4V4^A@szjA>6#PYk4(}Gn&HgzF1BkTBL;P52YCDRt_PK+X}kStnh= zKfLe6k8FIH2F=sx+{ea(P#k_pZO_WHTGB~w(YcjT~Ie7%LtjniV zLS7h}zvP9@MGYE*b+-?tE&Sk8oX1Zz3+>N5Soh~gj;I@vVu9b0YaAI6_-&*7%^Y=S zW9^rE3C5;+a;Q5%5kH|J#2}_rUPWW$Bi0l<7Y;X>wys7D6;*pv-i@V~5}ITfH}3J{ zQf++5xAC-c+W0hWppCa=wDF>%t;rW@hcV&P#-Oe4o$kIv(EoIHHP$xkZ$NQ}U&4~z zTj0KB{i)FcOH|}hgGR#*p~Cut$-KCG&J$1j*dmH6fMbYndjiO5jcFk2RH~O--3GW( zeLRysmS~)4d~$mZUi|SP`_IM9@fvZgQ&5KrD%XPEU`TU{srVHO0;YgBBI*ElIN`d1 zH*KLBb1vaHY(@|PuQQAgxLBj;2;4zt=Lze9N9vOJ(6k!9bK;Ew^jc`zt)N4c1Y3|7 zE{3xt0k}W=h+}9KPZ~2~wug_l!qesO>-Im^3^h-Uz)1x@zI@_H3bP-tW6hfs<}J4RS{quW zv1YkjK9-MxWZsyI62B*3+Wz1UElly@t2COi_SZAu!=oX_9t~d3@KZZ~2L}`<-HFhS zp9*5nCN2euVEpLn4OF0D?sdZpeD}O#bg*-$1mVIAU6EqKKs4sK6N0Rt3Jno<840Q| zLAcly%O&0IXu;uFlelh;sAU?LA{~BJ7W;&Q+%_Ug$?qO5%*wj(ve>-D-%vkN^PkBY zNTWCI$9g#T0taw^6PTB+ag#wl%N>`7)9m5QjsRzfhZExDRm)8CFX@`j_l3`Q zkMix)kuPyNGHHhU;27V2zRp=k&IDTv7___Z%GRZ=TfO9>v-4EDO@fINLB~Dr%MDYx zoho+^5;My=z$R2-~Ye*-R1>v|xhB!7evnfQV+;x0-Tsq#g(olcP}eGw@=sUrKS z2>Y^}KTj7q))!f*BE`PQbs0rIW*Ato9ryLoYG(KE(5g9yQHZ7?hO9lliShnZ1msIV zt79{cu>DqoB}IY6_l3X(sKJTv=;>a ztec8m?~92`Q;i;|$~BrkL)%O|M|oG6E<4_rU3?m4Puv^v)i8ZxN@s^H+5hbF(F}E~ z?#rPuTiM@+zI8qpzCZ8QHBf#kxn7bBHR`RT(puf^*@!?N-TMYTqB)6*IqqKyEEpfM zAbEEXCtbQ*F23}$t6&eJrB_t1WPMN}#qtuQ`FZCt*V#R5DAA$%PsF8~DOU1;meDm- zKiNMKkKi)@lpSigiznuZJmzOD_d#s?VRkZo+9Ud+RF+_C-TBhL1ujN1Qz3I~(oGuW z)hr!Z?ohLCBM0mt1?vwyKABg0rnHZm2-?NatWfZ&FGwd>Y=1hC%t3-dGN^80JvOai1+VZ3O7D+lntXyJ?K1!rwz5l`3Rnr z{Qc&6_P|=}y_z?DFZteSuob$WUAJ&}I;DFLrd}f$p5P)zuct(_ntOw3b+FJ)p9gc} z%LB%^M(Cz=q(k+Rp&#bC&0=TkytA;Np%6myKuN?ZAkxZ&rsO=a+3)Ef_e1(G@tscf ziS_L0yY$T1`|itCe*L?NT!Mo!C;>)*m@b_nkK6dr}}-TLiOY63xrpu(EJc#;e&QOhGLw!U4>m!iFw}h8f{y>Q$uF$_`_W!5n19-kO_{wgV9%nrFQl&6-rJ z{I#rwbTjFMb6LX<)Di0Xx2dD99U*)#ncd98;TZ07AI41Hks3Z=L?vz@ zvus`_v}6YOG#uOL&O8VOdI1{_u~B??uMYI>??V@<{c6VwaHRd2EBAVaZla*09g0eV zp{CXCxb!RJCO4Rb2s+f4;Dq{MJRHML#QD_sy#V|SjKqW%F`?C63Cn}%)Yp`p#t|jo zaQxF4;g}fRL2qkAwE7*fZ+q@BS&0WIsC9WC@CWZJOW+QL&44>jXoEWr@NM{U26r?e zloB6N&b@o66*^vp6cHxzq(OtOnexY^51JANE-!&FDxxk)0&zcgG%bO2FJ4*#^+DiB z0u8c6MAEM;qe&pmm?nX~zokP0{mx4u7~yAI!TaK^UJ!L<+#rx7i1OUlHg4rqtRLQT z+)yCS{uK?A^tr~as3~{NyHL1xN$4b^ofIweuK}a_M=8O0&$VH$_y%?D7f%+A5cy>FWcc%ekdU!ATm)JU&yg+h3@pQ&__rU;e)%2fNh-`(jw-o4kHV>`a@89#*kLLQds+ALfTJI`AO{ZMA?RTn2N8^+xs z9mIwI633C0_U769!OI7f6P$46*EUs6L`Im=8V!XYP8R*$9WV|$1WHD$GD)GtUf5>R z;UlFWgQv+AprU%C%xuG?<`X#94wdIIJ+yp}^F>^2VOnF|)xcvzL?iiOO6ve_Wp-P% zBoT_w)qCu9^?i=)XhfY|6R1!a^I=Li((7I-XyQ8qt6iFSsI1=Jqr_=cx+^%Y%~YV!wBM0E5+BH}>HB4c z@0XQ)8k%+vN#T+YLh-+A5IzuISjLa~kI`xtPEIn)7TR7Juj$SuK+hFudn>}^pharL z!g@pfL2h>~rZ56wmJ(ZW^Xg^_)e*%jGs?z3JpmO>4o$o>FK zGqAwDUTo{hY2T$<;gH8#BR6tSa`r?JPMPw`;(d=TuYOO_e2qJvJgq}(eO(lQ?a+}~ zX>;dJMPoogd@X64im(r^UD2si$yy)};M`)+wo*l1}K9)LltOL?5V|+anDQbSL(uPf6(3 zFI?2^Ky5b|*=(1Ri18`zWD)hd9SEt$Vd9pl-|s?n-!f0flX(_h%CcjQCgC{L?7Bgo zvTPLin&I|oKl@Os)cx51ulbIp=38o&QuCSoYxc9$VoM#X)Z&<{TfH$hmq^TZz#x_= zTo%7ES+~2#Ek~8dYx-wH#)^JLu_gTWsQa`%duiP#Jt;1-_TYd<_0rHxzC+A|;#JuM zC%%@(W+ZhXX>{kLt|Ub|Cv_vKxN}l>k`5=SuBk^FT*-ooeWDM9W}e@T3j6gmKa!Sf zQvBm~>}T^&zaplRYsBR7;YWmVukLz)&2gGtQmch$4kT&)H~VEUPF(3dguy}%2Vp*} z!@MXv<{U&B!wLJ@$FjBD3siS5!=~k*DLudIxNTT07A0!%rb4FIo1wduate|@=#%3bQqouhIm`f?n_O}A%tf65)_%MoV$T%P2ynpnX| zC3Ja{>F+c~<29k}Zty~w=1TJjpGzcyumzu!nQeD5=f$yiLYz+F8MI%KtMIG({|#j- zQ)N>9A^BYvTWH6CVy|n)bZP|Rc@RRvoiBh1y#NaND% zCQc8Brhh?FB(`z(XGY8+U4D2^GE(Rw@d-&XMaru&5}Tbk$2~F}J(G)`t8FtF&vl{# zYWH~dT3>Y0srL}PcDI{AKE>41)ZEN>N%v^=mT2}yBrK)Nvsu@0Q=9xlV+qD{lrURu z%kmdDw;vH(#9R_BTv)gu5?kE1qHt;Hk*AhUJ((*-CcPbwpOGvL&7_Vk(g}0Q3!8PX z6y(Y)N4i$VPbwCvinJ;i6d#dnBij0={C4&4SduOgcy#$_45<N>DdsA(F8! z_C)=#P1Ak_4puwLwpMeNwVyu?nqK83l74td1Z zB!47!PP9}m!ECKBm<+8_dzKPD2idnG`!*x^reDBI1~o@YoDY-)28DCuH<=W|XnIn? z=F>tG`}T@vzY$wza_E!x?8SATrsYtm{?~ws-;k~S!|FGXwRiMNmKNR7Z!AHTC*B#q zp$ieymAao&`&#PN@f*7Co%h`M4c+$6drJIJw$4pm7q})8+wSMq zt&2iFOff6@NZ~v2%4FU8E_J(l+;U_ze$oNzg>EO7A08R#y-dHM1bo>WYbnk;GJx0v9e>G_kjlxnGZ> zeqD8j@)1}-T2Rz>tEHLcL0QeO5;rKTZv1spa%*qZD%W4i-L{loz_L56zzg^CatU`- z=~?GmnN=)jK0Fzbd&4l~0UxkPO7o5PUr6dK`Axb}d=fVeulI`HC0z+8rtWxOw*x1iIOjzl5MP z@1nK#Yg<;gCR?%xVKjykWw9Nxufi5WwydxvT-SoQct2A7URmtJ!cF1A`Nf-hEr}Gr z-@N^Z*s`|ek^J}aNeI{N;PR%I)MeGUG>f!(0FTJ&Pa`85DP%3A6;=GY6T3uWUol@r zVoPDvNdD?b;c{dr%~_GZ2#1nztUcVew1jxScaD}8I3Nt z7cYS_k$iNf<*|3#mWT6OLNf>No4<|>Q~PluqnQ{@uW(=C;k`_`X*&jGK$l{p+?+NP z&OD~KDG|3p%9L{R);>hxhJD=Z9JB(3q%M9X!O8B_{&e-FIwtDJMdJQ;>73H?my{P~ zp76;)-t#YH(eT@^sCTq(E(3hQr1gozQE?)%ZN*KrkB@#ZYFaBPRjU$%2{q+cWXz5=!lDn znC1LqIkNUD?Xz}t&~XBzEr=sIV`R9ly?1EZB1(jjfF+CBKjt|e_MwhsxSV`)b~Jty z7S_eQ)Fra%f`W`L$arM@XxJczwbBgDJaF>%WX<~e<)MZ`4R%YfIlitP#ZB5Pd1J2@ z&Wn`18k+tG-f8Yf+pK#!_@nlHm*{3KJ5N0%^BXPuSs^X^iWb9PH8|i4mZ(IH zFR^J5C4THnh)+@_4x~ilX7F%>eWCkRNIgI`FRRc=zR=Yfg+77f?V>o>*40`(R)wzc zg{G=d5@57=Tt=b4sZcM~G}jk8PKD0)g+{8-5?@I6_7r-zs?giaQrx#WI)2*Ly#h=r z%+<7B^cgm*GDOMPE=frD{@6ysw`)dj;NW8w9{;B>sC99@1NUYryUf2Wm2i2uKJ~6wz`fc}m z8>6^x{(#_6Sn8IT4*r;YN>=StMEn6)#tTD-`wxx~3X_PfGk-wpF3;eqzS1oBU>85$ z^C8nORVY4u@OL27{qfoH zft+7@F9Vftd7QzV6~>*8St5ZZmcLYMw7DAeg$q%WBC(Zi%L^ADdEnIjG_@{g>6%0* z2-~Sc;^i04i@n9#5t{__Y@fEb%HyXe^WUobs!M3*E11Rrxjg@^{8yWI9FdwIG#_Wh zmiYxIcL;nc9dUX7l5nrrVk@MHEy~|iPW`=B#CDfa<(rZCq^+@Sh?=t4ms%2h)jM4H zb=df)Zd32Fcuxv^CdIsz;9m9leI6b#HDr1+oM z)I@WQegnv*LQD8!b5MNAop4+O(bIh;#Tt!h>631_%eloc%vXXYGUXLUTO&5 z4@5sz&#qgX{HA!5GUZ$YS{zpULlZKN&WmD(kwLyFXQ_-7rPAGpsuL^l8^h9|tRU8|j8SspxRtWwnwxUQk@q6^ zF_-$KHd$_DC9H)R_qs5E%kF2-DLDpNWxAUNu0pMwH*&MNBD0$jZG5gkZe;EkTkNR+ zwE4i$^_JRw!3n8l@C@@YvgmQAag7vUrageR~v5I+Y*&=q2hh4f5TWa^a#7rR|seI=F30Z9>R z#tvTu=|`qY2D^H%2kC9hOlDBF5R0o)z35Wk&!-$rXACzE%TG?I;YaFIx{ocmRkQ*t zAac`Hw5d}$4&r`v0eesJ%B`jkRKy?RN++@6;YIfp_vDSW}cpnFq{fQ)@|&XjKof?Rh`PCHq%c&x=P>?vMOKbRhY`Qwzd!32wjnLP3rhpBWS)$fk7tj=E= z+tdpbcGNCIIg70T4H2QDZe#Ddw%+kGvwO7{zEM0kwz6;|GZLcvn``ezdSsyEMXy!)QQS(rWwcL-gR;o}tITGqVAB4;v zX7Y0Q%ZEu`Za0~odBWk7U1Vh>l_PpE-~WjFZdhNov~Nky(G|L;*CYOC0}9E1tDtNE zI+8yV%7cOe1Le)`MInUI*~(aZ4B~mpdn7svaOPirJ z^FUyRf!XrB`_v~rt|Uc|Z~3Ep( zABcQ2ed?`BtybPz<$c7bUZGT7*hqVe52d|Ng2x{19kpXiGBH`zj`xKkD)hK7wDS-O z-Mn|9JPH|?Chk_rVqdbiO8(WCoU4*0zGPO0v;7)IL(6>Whbg_^lzzK*?1u8X_LD=u z&>17PH;xbe;wO5FZN_0@b`cU<3$Qhnv4v#{i1CHHnE6f<;zV*WOv_~M8ifIV;+u0A z4_Sz=_?gKzEOkw3!Ih8g)--g4Q6}?tEKE9{H2%A1#g=f>|6s}08M^D2 zJaKJZUPOAA`^ym)FM~O}Zgo%To9_7=q%yK&8Jby}H57Nzb z#BVRZ1-B-(3yLRq9i_ZSM*l24GWLV6;gaPOuSZd#Pf}_4w=AD{apyF4;+9Vw(K(H+ zxaAX1?3~74+;Uu8Qf?U@WVmE*;Fr;zYWrnFr5uDsHg|R$1hH~vNuxJjD17g&?fFZ? zQ?u*Y_&i44UDwiuz)T0*_+t5dF!s`s_EqOpG05PHHEE5QHpdOzXv3p>F2h5<9CDT( z^p%N`U-Ft-zrMp*k(!p%GQiuPV2zWe-jhVJri-*jATBiF7v7xx0B`Qshwa3PllamV zY?_cIa8JuvJg3$-?!i_d6NUGe94xz+#9;Kjl%)f-T^#PXJvlP&@C(|%{r^-K{S-#Pi zuTlAGYHFEd?WvpBWf)fD5oBd!F(IU1`rc9Ce5&-{EipDpx3^=8|};9fE6ugHGG z_0G)JMh`^em&m&j5S9m_?+!dQqo%i%Csh-o0Vhat;$xp4jjezuc9VZ1zJga-!(qaR z4N5#r8Q-tt*BgD$-v|20=}v^b6`F-vZO69FQRl!yyZ5^{X(GM2$sN2Y;N8T9K#{~x zY~7JeS|l<%q~#9sFtY?xz7v9Z6fo(mYj{m{_EN@$VVKbUm3|yBVVPa6Z$-R{1a8XxX_VYTnb7r!E$JI)qPPQp5FcT1S!tt5B=E zHu!uJRmt!OgVR1$+8Rc6tE_(I%=k&<#I`2F7r7r3u~1blp@5(ism~RWOi{uSy>!i{ zir5=cbWWvL$=aZ1Xv&jyc6;GEM0qq`alr1$#o6YB@)($f=4|r_`mMD>wgzyC z5)-L;LpLpBKx0$1c}=&9LR7zX7_l+-fNV6I`y0Ml6PE8u7yk6qjD_<_w=QUpICpNW z{Xz~1@|G84mMwvL5%w`88e)h523r{&r%j^^S&{*dvEhH z^VfyUUy=9({E{?|pXaTtWH(OIdWf}ER&$kF_ctfwTx|aCiV@SM%XtvaHSo8Mrpg1o znKwsk9q4eN&Om4`-sFz#ZoE1DR{~eOo@MI^hy{;^7ZHh-h`~UQatAoc)$W&5-<;2& zo%zknfpLSD0V^atpLultDPgJ5?=Wx-k0kG4R08b_@xHiWW@mY3@GdY|5D(yMV}HY!3m0p_P;!^e+byhOniH%@g19jr%A!%yN}_! zFb&Y;6Zq=9MMd@2#M6k*ifRUC@z%t02q*u{O!+?C`(t*J@0ijO@0fH4FT(NG96By^ zefJaZpOIWNLLuI;6v$gi8*SHD}rJ6As754~sV*%TbH!8yLJEU(O#hwpy{jX;P@o zWotNB_q44I92Oe8YRGr&|L#e@x@2k1iBpn~GeOk+U`p~4{f4I`AJy+^Q<9JA_w*^r zGrIAM>32$Ugq{wZ$56}$7u^r1=k7eW5S_1kg&q<-bk)OZ9(wCx1rPh_;SC-J=wX4E zKU0z-sA6C<+`2D)hG{rdHn)QSY5NWfdj-W}{Y}{8C>4*W3e_)EG0la4)SxM@r6F&*Z$;{kh40anE)Sr5+c$yLt4XZ)(cZk(93Mw;>t4w7JVWURvFGyg+Sc z^)|PapokJ@`zc_6sbio2M(5#bt8~9m(*3%REs@C%iXXb^GV8lyw5yFnU4xGA8MIz#wYUjF>c==cMnS( ziMnue)S?e^i&xS$eaDcgcM@K_SMBHDSMp9=0OckM-)c-J`g%{0dv=if$RPJ$E%#Ye zII`4os~5(&>L9!Bi>5fi$G*xH_|nk*3lbAZ^z;jlUbdTnkN!{*Hli@m)!6knLk@

7p9;}$(@SEgR(CcG=1nR!<5;*{4m6p6 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

uC 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 W? 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�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%0DQcxUVG6t9ruUCM_Oyk^Aj+aGLc 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

xw^4_|FxeFvIodImP=3e^v3_ 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

i3L$741j5 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%0Dnv9xrS4JSY8eEyX;~tt`c6zs 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 None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class ANSIX923PaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class PKCS7UnpaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class ANSIX923UnpaddingContext(padding.PaddingContext): + def __init__(self, block_size: int) -> None: ... + def update(self, data: Buffer) -> bytes: ... + def finalize(self) -> bytes: ... + +class Encoding: + PEM: typing.ClassVar[Encoding] + DER: typing.ClassVar[Encoding] + OpenSSH: typing.ClassVar[Encoding] + Raw: typing.ClassVar[Encoding] + X962: typing.ClassVar[Encoding] + SMIME: typing.ClassVar[Encoding] + +class PrivateFormat: + PKCS8: typing.ClassVar[PrivateFormat] + TraditionalOpenSSL: typing.ClassVar[PrivateFormat] + Raw: typing.ClassVar[PrivateFormat] + OpenSSH: typing.ClassVar[PrivateFormat] + PKCS12: typing.ClassVar[PrivateFormat] + def encryption_builder(self) -> KeySerializationEncryptionBuilder: ... + +class PublicFormat: + SubjectPublicKeyInfo: typing.ClassVar[PublicFormat] + PKCS1: typing.ClassVar[PublicFormat] + OpenSSH: typing.ClassVar[PublicFormat] + Raw: typing.ClassVar[PublicFormat] + CompressedPoint: typing.ClassVar[PublicFormat] + UncompressedPoint: typing.ClassVar[PublicFormat] + +class ParameterFormat: + PKCS3: typing.ClassVar[ParameterFormat] + +class ObjectIdentifier: + def __init__(self, value: str) -> None: ... + @property + def dotted_string(self) -> str: ... + @property + def _name(self) -> str: ... + +T = typing.TypeVar("T") diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi new file mode 100644 index 0000000..3d4ea4e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi @@ -0,0 +1,8 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi new file mode 100644 index 0000000..3b5f208 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi @@ -0,0 +1,7 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi new file mode 100644 index 0000000..a4c0cb6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi @@ -0,0 +1,127 @@ +# 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 + +def decode_der(cls: type, value: bytes) -> typing.Any: ... +def encode_der(value: typing.Any) -> bytes: ... +def non_root_python_to_rust(cls: type) -> Type: ... + +# Type is a Rust enum with tuple variants. For now, we express the type +# annotations like this: +class Type: + Sequence: typing.ClassVar[type] + SequenceOf: typing.ClassVar[type] + Set: typing.ClassVar[type] + SetOf: typing.ClassVar[type] + Option: typing.ClassVar[type] + Choice: typing.ClassVar[type] + PyBool: typing.ClassVar[type] + PyInt: typing.ClassVar[type] + PyBytes: typing.ClassVar[type] + PyStr: typing.ClassVar[type] + +class Annotation: + default: typing.Any | None + encoding: Encoding | None + size: Size | None + def __new__( + cls, + default: typing.Any | None = None, + encoding: Encoding | None = None, + size: Size | None = None, + ) -> Annotation: ... + def is_empty(self) -> bool: ... + +# Encoding is a Rust enum with tuple variants. For now, we express the type +# annotations like this: +class Encoding: + Implicit: typing.ClassVar[type] + Explicit: typing.ClassVar[type] + +class Size: + min: int + max: int | None + + def __new__(cls, min: int, max: int | None) -> Size: ... + @staticmethod + def exact(n: int) -> Size: ... + +class AnnotatedType: + inner: Type + annotation: Annotation + + def __new__(cls, inner: Type, annotation: Annotation) -> AnnotatedType: ... + +class AnnotatedTypeObject: + annotated_type: AnnotatedType + value: typing.Any + + def __new__( + cls, annotated_type: AnnotatedType, value: typing.Any + ) -> AnnotatedTypeObject: ... + +class Variant: + python_class: type + ann_type: AnnotatedType + tag_name: str | None + + def __new__( + cls, + python_class: type, + ann_type: AnnotatedType, + tag_name: str | None, + ) -> Variant: ... + +class PrintableString: + def __new__(cls, inner: str) -> PrintableString: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def as_str(self) -> str: ... + +class IA5String: + def __new__(cls, inner: str) -> IA5String: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def as_str(self) -> str: ... + +class UTCTime: + def __new__(cls, inner: datetime.datetime) -> UTCTime: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def as_datetime(self) -> datetime.datetime: ... + +class GeneralizedTime: + def __new__(cls, inner: datetime.datetime) -> GeneralizedTime: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def as_datetime(self) -> datetime.datetime: ... + +class BitString: + def __new__(cls, data: bytes, padding_bits: int) -> BitString: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def as_bytes(self) -> bytes: ... + def padding_bits(self) -> int: ... + +class Tlv: + @property + def tag_bytes(self) -> bytes: ... + @property + def data(self) -> memoryview: ... + def parse(self, cls: type): ... + +T = typing.TypeVar("T") + +class SetOf(typing.Generic[T]): + def __new__(cls, inner: list[T]) -> SetOf[T]: ... + def as_list(self) -> list[T]: ... + def __eq__(self, other: object) -> bool: ... + def __repr__(self) -> str: ... + +class Null: + def __new__(cls) -> Null: ... + def __repr__(self) -> str: ... + def __eq__(self, other: object) -> bool: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi new file mode 100644 index 0000000..09f46b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi @@ -0,0 +1,17 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi new file mode 100644 index 0000000..103e96c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi @@ -0,0 +1,117 @@ +# 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 +from collections.abc import Iterator + +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) -> 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi new file mode 100644 index 0000000..404a300 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi @@ -0,0 +1,80 @@ +# 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, + hpke, + kdf, + keys, + mldsa, + mlkem, + poly1305, + rsa, + x448, + x25519, +) + +__all__ = [ + "aead", + "ciphers", + "cmac", + "dh", + "dsa", + "ec", + "ed448", + "ed25519", + "hashes", + "hmac", + "hpke", + "kdf", + "keys", + "mldsa", + "mlkem", + "openssl_version", + "openssl_version_text", + "poly1305", + "raise_openssl_error", + "rsa", + "x448", + "x25519", +] + +CRYPTOGRAPHY_IS_LIBRESSL: bool +CRYPTOGRAPHY_IS_BORINGSSL: bool +CRYPTOGRAPHY_IS_AWSLC: bool +CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_330_OR_GREATER: bool +CRYPTOGRAPHY_OPENSSL_350_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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi new file mode 100644 index 0000000..eb44608 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi @@ -0,0 +1,189 @@ +# 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 collections.abc import Sequence + +from cryptography.utils import Buffer + +class AESGCM: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class ChaCha20Poly1305: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key() -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESCCM: + def __init__(self, key: Buffer, tag_length: int = 16) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESSIV: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + ) -> bytes: ... + def encrypt_into( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + ) -> bytes: ... + def decrypt_into( + self, + data: Buffer, + associated_data: Sequence[Buffer] | None, + buf: Buffer, + ) -> int: ... + +class AESOCB3: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + +class AESGCMSIV: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_key(bit_length: int) -> bytes: ... + def encrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def encrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... + def decrypt( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + ) -> bytes: ... + def decrypt_into( + self, + nonce: Buffer, + data: Buffer, + associated_data: Buffer | None, + buf: Buffer, + ) -> int: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi new file mode 100644 index 0000000..a48fb01 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi @@ -0,0 +1,38 @@ +# 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 | None +) -> 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 | None +) -> 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi new file mode 100644 index 0000000..9c03508 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi @@ -0,0 +1,18 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi new file mode 100644 index 0000000..08733d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi @@ -0,0 +1,51 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi new file mode 100644 index 0000000..0922a4c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi @@ -0,0 +1,41 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi new file mode 100644 index 0000000..5c3b7bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi @@ -0,0 +1,52 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi new file mode 100644 index 0000000..f85b3d1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi @@ -0,0 +1,13 @@ +# 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 +from cryptography.utils import Buffer + +class Ed25519PrivateKey: ... +class Ed25519PublicKey: ... + +def generate_key() -> ed25519.Ed25519PrivateKey: ... +def from_private_bytes(data: Buffer) -> ed25519.Ed25519PrivateKey: ... +def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi new file mode 100644 index 0000000..c8ca0ec --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi @@ -0,0 +1,13 @@ +# 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 +from cryptography.utils import Buffer + +class Ed448PrivateKey: ... +class Ed448PublicKey: ... + +def generate_key() -> ed448.Ed448PrivateKey: ... +def from_private_bytes(data: Buffer) -> ed448.Ed448PrivateKey: ... +def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi new file mode 100644 index 0000000..106b531 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi @@ -0,0 +1,30 @@ +# 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 +from cryptography.utils import Buffer + +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: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def copy(self) -> Hash: ... + @staticmethod + def hash(algorithm: hashes.HashAlgorithm, data: Buffer) -> bytes: ... + +def hash_supported(algorithm: hashes.HashAlgorithm) -> bool: ... + +class XOFHash: + def __init__(self, algorithm: hashes.ExtendableOutputFunction) -> None: ... + @property + def algorithm(self) -> hashes.ExtendableOutputFunction: ... + def update(self, data: Buffer) -> None: ... + def squeeze(self, length: int) -> bytes: ... + def copy(self) -> XOFHash: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi new file mode 100644 index 0000000..3883d1b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi @@ -0,0 +1,22 @@ +# 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 +from cryptography.utils import Buffer + +class HMAC(hashes.HashContext): + def __init__( + self, + key: Buffer, + algorithm: hashes.HashAlgorithm, + backend: typing.Any = None, + ) -> None: ... + @property + def algorithm(self) -> hashes.HashAlgorithm: ... + def update(self, data: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, signature: bytes) -> None: ... + def copy(self) -> HMAC: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi new file mode 100644 index 0000000..a9d8a11 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi @@ -0,0 +1,108 @@ +# 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 ec, mlkem, x25519 +from cryptography.utils import Buffer + +class KEM: + X25519: KEM + P256: KEM + P384: KEM + P521: KEM + MLKEM768: KEM + MLKEM1024: KEM + MLKEM768_X25519: KEM + MLKEM1024_P384: KEM + +class KDF: + HKDF_SHA256: KDF + HKDF_SHA384: KDF + HKDF_SHA512: KDF + SHAKE128: KDF + SHAKE256: KDF + +class AEAD: + AES_128_GCM: AEAD + AES_256_GCM: AEAD + CHACHA20_POLY1305: AEAD + +class MLKEM768X25519PrivateKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM768PrivateKey, + x25519_key: x25519.X25519PrivateKey, + ) -> None: ... + def public_key(self) -> MLKEM768X25519PublicKey: ... + +class MLKEM768X25519PublicKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM768PublicKey, + x25519_key: x25519.X25519PublicKey, + ) -> None: ... + +class MLKEM1024P384PrivateKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM1024PrivateKey, + p384_key: ec.EllipticCurvePrivateKey, + ) -> None: ... + def public_key(self) -> MLKEM1024P384PublicKey: ... + +class MLKEM1024P384PublicKey: + def __init__( + self, + mlkem_key: mlkem.MLKEM1024PublicKey, + p384_key: ec.EllipticCurvePublicKey, + ) -> None: ... + +class Suite: + def __init__(self, kem: KEM, kdf: KDF, aead: AEAD) -> None: ... + def encrypt( + self, + plaintext: Buffer, + public_key: x25519.X25519PublicKey + | ec.EllipticCurvePublicKey + | mlkem.MLKEM768PublicKey + | mlkem.MLKEM1024PublicKey + | MLKEM768X25519PublicKey + | MLKEM1024P384PublicKey, + info: Buffer | None = None, + ) -> bytes: ... + def decrypt( + self, + ciphertext: Buffer, + private_key: x25519.X25519PrivateKey + | ec.EllipticCurvePrivateKey + | mlkem.MLKEM768PrivateKey + | mlkem.MLKEM1024PrivateKey + | MLKEM768X25519PrivateKey + | MLKEM1024P384PrivateKey, + info: Buffer | None = None, + ) -> bytes: ... + +def _encrypt_with_aad( + suite: Suite, + plaintext: Buffer, + public_key: x25519.X25519PublicKey + | ec.EllipticCurvePublicKey + | mlkem.MLKEM768PublicKey + | mlkem.MLKEM1024PublicKey + | MLKEM768X25519PublicKey + | MLKEM1024P384PublicKey, + info: Buffer | None = None, + aad: Buffer | None = None, +) -> bytes: ... +def _decrypt_with_aad( + suite: Suite, + ciphertext: Buffer, + private_key: x25519.X25519PrivateKey + | ec.EllipticCurvePrivateKey + | mlkem.MLKEM768PrivateKey + | mlkem.MLKEM1024PrivateKey + | MLKEM768X25519PrivateKey + | MLKEM1024P384PrivateKey, + info: Buffer | None = None, + aad: Buffer | None = None, +) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi new file mode 100644 index 0000000..bb37b96 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi @@ -0,0 +1,205 @@ +# 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 +from cryptography.hazmat.primitives.kdf.kbkdf import CounterLocation, Mode +from cryptography.utils import Buffer + +class PBKDF2HMAC: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes, + iterations: int, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +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: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class Argon2d: + 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 derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> None: ... + +class Argon2i: + 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 derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> 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 derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + def derive_phc_encoded(self, key_material: bytes) -> str: ... + @classmethod + def verify_phc_encoded( + cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None + ) -> None: ... + +class HKDF: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes | None, + info: bytes | None, + backend: typing.Any = None, + ): ... + @staticmethod + def extract( + algorithm: HashAlgorithm, salt: bytes | None, key_material: Buffer + ) -> bytes: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class HKDFExpand: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + info: bytes | None, + backend: typing.Any = None, + ): ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class X963KDF: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + sharedinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class ConcatKDFHash: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + otherinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class ConcatKDFHMAC: + def __init__( + self, + algorithm: HashAlgorithm, + length: int, + salt: bytes | None, + otherinfo: bytes | None, + backend: typing.Any = None, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class KBKDFHMAC: + def __init__( + self, + algorithm: 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, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... + +class KBKDFCMAC: + def __init__( + self, + algorithm: typing.Any, + 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, + ) -> None: ... + def derive(self, key_material: Buffer) -> bytes: ... + def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... + def verify(self, key_material: bytes, expected_key: bytes) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi new file mode 100644 index 0000000..404057e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi @@ -0,0 +1,34 @@ +# 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, +) +from cryptography.utils import Buffer + +def load_der_private_key( + data: Buffer, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PrivateKeyTypes: ... +def load_pem_private_key( + data: Buffer, + 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi new file mode 100644 index 0000000..232469a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi @@ -0,0 +1,23 @@ +# 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 mldsa +from cryptography.utils import Buffer + +class MLDSA44PrivateKey: ... +class MLDSA44PublicKey: ... +class MLDSA65PrivateKey: ... +class MLDSA65PublicKey: ... +class MLDSA87PrivateKey: ... +class MLDSA87PublicKey: ... + +def generate_mldsa44_key() -> mldsa.MLDSA44PrivateKey: ... +def from_mldsa44_public_bytes(data: bytes) -> mldsa.MLDSA44PublicKey: ... +def from_mldsa44_seed_bytes(data: Buffer) -> mldsa.MLDSA44PrivateKey: ... +def generate_mldsa65_key() -> mldsa.MLDSA65PrivateKey: ... +def from_mldsa65_public_bytes(data: bytes) -> mldsa.MLDSA65PublicKey: ... +def from_mldsa65_seed_bytes(data: Buffer) -> mldsa.MLDSA65PrivateKey: ... +def generate_mldsa87_key() -> mldsa.MLDSA87PrivateKey: ... +def from_mldsa87_public_bytes(data: bytes) -> mldsa.MLDSA87PublicKey: ... +def from_mldsa87_seed_bytes(data: Buffer) -> mldsa.MLDSA87PrivateKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi new file mode 100644 index 0000000..768a340 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi @@ -0,0 +1,18 @@ +# 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 mlkem +from cryptography.utils import Buffer + +class MLKEM768PrivateKey: ... +class MLKEM768PublicKey: ... +class MLKEM1024PrivateKey: ... +class MLKEM1024PublicKey: ... + +def generate_mlkem768_key() -> mlkem.MLKEM768PrivateKey: ... +def from_mlkem768_seed_bytes(data: Buffer) -> mlkem.MLKEM768PrivateKey: ... +def from_mlkem768_public_bytes(data: Buffer) -> mlkem.MLKEM768PublicKey: ... +def generate_mlkem1024_key() -> mlkem.MLKEM1024PrivateKey: ... +def from_mlkem1024_seed_bytes(data: Buffer) -> mlkem.MLKEM1024PrivateKey: ... +def from_mlkem1024_public_bytes(data: Buffer) -> mlkem.MLKEM1024PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi new file mode 100644 index 0000000..45a2a39 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi @@ -0,0 +1,15 @@ +# 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.utils import Buffer + +class Poly1305: + def __init__(self, key: Buffer) -> None: ... + @staticmethod + def generate_tag(key: Buffer, data: Buffer) -> bytes: ... + @staticmethod + def verify_tag(key: Buffer, data: Buffer, tag: bytes) -> None: ... + def update(self, data: Buffer) -> None: ... + def finalize(self) -> bytes: ... + def verify(self, tag: bytes) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi new file mode 100644 index 0000000..ef7752d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi @@ -0,0 +1,55 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi new file mode 100644 index 0000000..38d2add --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi @@ -0,0 +1,13 @@ +# 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 +from cryptography.utils import Buffer + +class X25519PrivateKey: ... +class X25519PublicKey: ... + +def generate_key() -> x25519.X25519PrivateKey: ... +def from_private_bytes(data: Buffer) -> x25519.X25519PrivateKey: ... +def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi new file mode 100644 index 0000000..3ac0980 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi @@ -0,0 +1,13 @@ +# 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 +from cryptography.utils import Buffer + +class X448PrivateKey: ... +class X448PublicKey: ... + +def generate_key() -> x448.X448PrivateKey: ... +def from_private_bytes(data: Buffer) -> x448.X448PrivateKey: ... +def from_public_bytes(data: bytes) -> x448.X448PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi new file mode 100644 index 0000000..b25becb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi @@ -0,0 +1,52 @@ +# 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 collections.abc import Iterable + +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, +) +from cryptography.utils import Buffer + +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: Buffer, + password: Buffer | 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_java_truststore( + certs: Iterable[PKCS12Certificate], + encryption_algorithm: KeySerializationEncryption, +) -> bytes: ... +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: Iterable[x509.Certificate | PKCS12Certificate] | None, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi new file mode 100644 index 0000000..358b135 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi @@ -0,0 +1,50 @@ +# 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 collections.abc import Iterable + +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, + content_encryption_algorithm: pkcs7.ContentEncryptionAlgorithm, + encoding: serialization.Encoding, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def sign_and_serialize( + builder: pkcs7.PKCS7SignatureBuilder, + encoding: serialization.Encoding, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_der( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_pem( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: Iterable[pkcs7.PKCS7Options], +) -> bytes: ... +def decrypt_smime( + data: bytes, + certificate: x509.Certificate, + private_key: rsa.RSAPrivateKey, + options: 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/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi new file mode 100644 index 0000000..c6c6d0b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi @@ -0,0 +1,23 @@ +# 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 +from cryptography.utils import Buffer + +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: Buffer | None, + certs: list[x509.Certificate], + options: list[pkcs7.PKCS7Options], +) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi new file mode 100644 index 0000000..196a3c6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi @@ -0,0 +1,312 @@ +# 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 collections.abc import Iterator + +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, + ecdsa_deterministic: bool | None, +) -> x509.Certificate: ... +def create_x509_csr( + builder: x509.CertificateSigningRequestBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, + ecdsa_deterministic: bool | None, +) -> x509.CertificateSigningRequest: ... +def create_revoked_certificate( + builder: x509.RevokedCertificateBuilder, +) -> x509.RevokedCertificate: ... +def create_x509_crl( + builder: x509.CertificateRevocationListBuilder, + private_key: PrivateKeyTypes, + hash_algorithm: hashes.HashAlgorithm | None, + rsa_padding: PKCS1v15 | PSS | None, + ecdsa_deterministic: bool | 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, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @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: + @property + def serial_number(self) -> int: ... + @property + def revocation_date(self) -> datetime.datetime: ... + @property + def revocation_date_utc(self) -> datetime.datetime: ... + @property + def extensions(self) -> x509.Extensions: ... + +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 + ) -> x509.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, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @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) -> 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, + ) -> PSS | PKCS1v15 | ECDSA | None: ... + @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: ... + +class PolicyBuilder: + def time(self, time: datetime.datetime) -> PolicyBuilder: ... + def store(self, store: Store) -> PolicyBuilder: ... + def max_chain_depth(self, max_chain_depth: int) -> PolicyBuilder: ... + def extension_policies( + self, *, ca_policy: ExtensionPolicy, ee_policy: ExtensionPolicy + ) -> PolicyBuilder: ... + def build_client_verifier(self) -> ClientVerifier: ... + def build_server_verifier( + self, subject: x509.verification.Subject + ) -> ServerVerifier: ... + +class Policy: + @property + def max_chain_depth(self) -> int: ... + @property + def subject(self) -> x509.verification.Subject | None: ... + @property + def validation_time(self) -> datetime.datetime: ... + @property + def extended_key_usage(self) -> x509.ObjectIdentifier: ... + @property + def minimum_rsa_modulus(self) -> int: ... + +class Criticality: + CRITICAL: Criticality + AGNOSTIC: Criticality + NON_CRITICAL: Criticality + +T = typing.TypeVar("T", contravariant=True, bound=x509.ExtensionType) + +MaybeExtensionValidatorCallback = typing.Callable[ + [ + Policy, + x509.Certificate, + T | None, + ], + None, +] + +PresentExtensionValidatorCallback = typing.Callable[ + [Policy, x509.Certificate, T], + None, +] + +class ExtensionPolicy: + @staticmethod + def permit_all() -> ExtensionPolicy: ... + @staticmethod + def webpki_defaults_ca() -> ExtensionPolicy: ... + @staticmethod + def webpki_defaults_ee() -> ExtensionPolicy: ... + def require_not_present( + self, extension_type: type[x509.ExtensionType] + ) -> ExtensionPolicy: ... + def may_be_present( + self, + extension_type: type[T], + criticality: Criticality, + validator: MaybeExtensionValidatorCallback[T] | None, + ) -> ExtensionPolicy: ... + def require_present( + self, + extension_type: type[T], + criticality: Criticality, + validator: PresentExtensionValidatorCallback[T] | None, + ) -> ExtensionPolicy: ... + +class VerifiedClient: + @property + def subjects(self) -> list[x509.GeneralName] | None: ... + @property + def chain(self) -> list[x509.Certificate]: ... + +class ClientVerifier: + @property + def policy(self) -> Policy: ... + @property + def store(self) -> Store: ... + def verify( + self, + leaf: x509.Certificate, + intermediates: list[x509.Certificate], + ) -> VerifiedClient: ... + +class ServerVerifier: + @property + def policy(self) -> Policy: ... + @property + def store(self) -> Store: ... + 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): ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py @@ -0,0 +1,3 @@ +# 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/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py new file mode 100644 index 0000000..1e447a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py @@ -0,0 +1,199 @@ +# 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_CTX_set_ciphersuites", + ] + + +def cryptography_has_tlsv13_hs_functions() -> list[str]: + return [ + "SSL_VERIFY_POST_HANDSHAKE", + "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_ssl_verify_client_post_handshake() -> list[str]: + return [ + "SSL_verify_client_post_handshake", + ] + + +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_dtls_get_data_mtu() -> list[str]: + return [ + "DTLS_get_data_mtu", + ] + + +def cryptography_has_ssl_cookie() -> list[str]: + return [ + "SSL_OP_COOKIE_EXCHANGE", + "DTLS1_COOKIE_LENGTH", + "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"] + + +def cryptography_has_ssl_get0_group_name() -> list[str]: + return ["SSL_get0_group_name"] + + +# 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_TLSv1_3_HS_FUNCTIONS": ( + cryptography_has_tlsv13_hs_functions + ), + "Cryptography_HAS_SSL_VERIFY_CLIENT_POST_HANDSHAKE": ( + cryptography_has_ssl_verify_client_post_handshake + ), + "Cryptography_HAS_ENGINE": cryptography_has_engine, + "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, + "Cryptography_HAS_SRTP": cryptography_has_srtp, + "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, + "Cryptography_HAS_SSL_GET0_GROUP_NAME": ( + cryptography_has_ssl_get0_group_name + ), +} diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py new file mode 100644 index 0000000..6a0cafd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py @@ -0,0 +1,122 @@ +# 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 +from collections.abc import Callable + +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, 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[typing.Any] = None + ffi: typing.Any = _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/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py @@ -0,0 +1,5 @@ +# 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/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py new file mode 100644 index 0000000..41d7318 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py @@ -0,0 +1,5 @@ +# 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/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py new file mode 100644 index 0000000..703c8e4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py @@ -0,0 +1,142 @@ +# 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 warnings + +from cryptography import utils +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: + warnings.warn( + "Single-key TripleDES (8-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key + key).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key + key + elif len(key) == 16: + warnings.warn( + "Two-key TripleDES (16-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key[:8]).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key[:8] + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +# Not actually supported, marker for tests +class _DES: + key_size = 64 + + +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 + + +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 + + +# 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/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py new file mode 100644 index 0000000..1786bb0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py @@ -0,0 +1,53 @@ +# 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.primitives._modes import ( + ModeWithInitializationVector, + _check_iv_and_key_length, +) + + +class OFB(ModeWithInitializationVector): + name = "OFB" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB(ModeWithInitializationVector): + name = "CFB" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB8(ModeWithInitializationVector): + name = "CFB8" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py @@ -0,0 +1,3 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py new file mode 100644 index 0000000..ea55ffd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py @@ -0,0 +1,19 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py new file mode 100644 index 0000000..305a9fd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py @@ -0,0 +1,60 @@ +# 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: utils.Buffer + + @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: utils.Buffer +) -> utils.Buffer: + # 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/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py new file mode 100644 index 0000000..deae8bc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py @@ -0,0 +1,105 @@ +# 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, +) + + +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) -> utils.Buffer: + """ + The value of the initialization vector for this mode as bytes. + """ + + +class ModeWithTweak(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tweak(self) -> utils.Buffer: + """ + The value of the tweak for this mode as bytes. + """ + + +class ModeWithNonce(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def nonce(self) -> utils.Buffer: + """ + 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: utils.Buffer, 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) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py new file mode 100644 index 0000000..9c3c474 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py @@ -0,0 +1,136 @@ +# 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.bindings._rust import Encoding as Encoding +from cryptography.hazmat.bindings._rust import ( + ParameterFormat as ParameterFormat, +) +from cryptography.hazmat.bindings._rust import PrivateFormat as PrivateFormat +from cryptography.hazmat.bindings._rust import PublicFormat as PublicFormat +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 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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 0000000..b509336 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py @@ -0,0 +1,3 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py new file mode 100644 index 0000000..2f6b834 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py @@ -0,0 +1,159 @@ +# 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. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPublicKey: + """ + Returns a deep copy. + """ + + +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. + """ + + @abc.abstractmethod + def __copy__(self) -> DHPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DHPrivateKey: + """ + Returns a deep copy. + """ + + +DHPrivateKeyWithSerialization = DHPrivateKey +DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 0000000..f245557 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,179 @@ +# 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 +from cryptography.utils import Buffer + + +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: Buffer, + 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. + """ + + @abc.abstractmethod + def __copy__(self) -> DSAPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DSAPrivateKey: + """ + Returns a deep copy. + """ + + +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: Buffer, + data: Buffer, + algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> DSAPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> DSAPublicKey: + """ + Returns a deep copy. + """ + + +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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 0000000..39e6751 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,369 @@ +# 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") + + +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. + """ + + @property + @abc.abstractmethod + def group_order(self) -> int: + """ + The order of the curve's group. + """ + + +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: utils.Buffer, + 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. + """ + + @abc.abstractmethod + def __copy__(self) -> EllipticCurvePrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> EllipticCurvePrivateKey: + """ + Returns a deep copy. + """ + + +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: utils.Buffer, + data: utils.Buffer, + 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. + """ + + @abc.abstractmethod + def __copy__(self) -> EllipticCurvePublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> EllipticCurvePublicKey: + """ + Returns a deep copy. + """ + + +EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey +EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey) + +EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers +EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers + + +class SECP521R1(EllipticCurve): + name = "secp521r1" + key_size = 521 + group_order = 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409 # noqa: E501 + + +class SECP384R1(EllipticCurve): + name = "secp384r1" + key_size = 384 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973 # noqa: E501 + + +class SECP256R1(EllipticCurve): + name = "secp256r1" + key_size = 256 + group_order = ( + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 + ) + + +class SECP256K1(EllipticCurve): + name = "secp256k1" + key_size = 256 + group_order = ( + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + ) + + +class SECP224R1(EllipticCurve): + name = "secp224r1" + key_size = 224 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D + + +class SECP192R1(EllipticCurve): + name = "secp192r1" + key_size = 192 + group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831 + + +class BrainpoolP256R1(EllipticCurve): + name = "brainpoolP256r1" + key_size = 256 + group_order = ( + 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 + ) + + +class BrainpoolP384R1(EllipticCurve): + name = "brainpoolP384r1" + key_size = 384 + group_order = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565 # noqa: E501 + + +class BrainpoolP512R1(EllipticCurve): + name = "brainpoolP512r1" + key_size = 512 + group_order = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069 # noqa: E501 + + +_CURVE_TYPES: dict[str, EllipticCurve] = { + "prime192v1": SECP192R1(), + "prime256v1": SECP256R1(), + "secp192r1": SECP192R1(), + "secp224r1": SECP224R1(), + "secp256r1": SECP256R1(), + "secp384r1": SECP384R1(), + "secp521r1": SECP521R1(), + "secp256k1": SECP256K1(), + "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, +} + + +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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py new file mode 100644 index 0000000..70aec5b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py @@ -0,0 +1,116 @@ +# 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 +from cryptography.utils import Buffer + + +class Ed25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey: + 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: Buffer, data: Buffer) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed25519PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed25519PublicKey: + """ + Returns a deep copy. + """ + + +Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey) + + +class Ed25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> Ed25519PrivateKey: + return rust_openssl.ed25519.generate_key() + + @classmethod + def from_private_bytes(cls, data: Buffer) -> Ed25519PrivateKey: + 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: Buffer) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed25519PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed25519PrivateKey: + """ + Returns a deep copy. + """ + + +Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py new file mode 100644 index 0000000..9ecb478 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py @@ -0,0 +1,143 @@ +# 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 +from cryptography.utils import Buffer + + +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: Buffer, data: Buffer) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> Ed448PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed448PublicKey: + """ + Returns a deep copy. + """ + + +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: Buffer) -> 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: Buffer) -> 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()). + """ + + @abc.abstractmethod + def __copy__(self) -> Ed448PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> Ed448PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "x448"): + Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py new file mode 100644 index 0000000..0bd9684 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py @@ -0,0 +1,441 @@ +# 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 +from cryptography.utils import Buffer + + +class MLDSA44PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA44PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa44_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). + + The public key is 1,312 bytes for MLDSA-44. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA44PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA44PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA44PublicKey.register(rust_openssl.mldsa.MLDSA44PublicKey) + + +class MLDSA44PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA44PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa44_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA44PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-44 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa44_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA44PublicKey: + """ + The MLDSA44PublicKey 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. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA44PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA44PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA44PrivateKey.register(rust_openssl.mldsa.MLDSA44PrivateKey) + + +class MLDSA65PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA65PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa65_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). + + The public key is 1,952 bytes for MLDSA-65. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA65PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA65PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA65PublicKey.register(rust_openssl.mldsa.MLDSA65PublicKey) + + +class MLDSA65PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA65PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa65_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA65PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-65 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa65_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA65PublicKey: + """ + The MLDSA65PublicKey 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. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA65PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA65PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA65PrivateKey.register(rust_openssl.mldsa.MLDSA65PrivateKey) + + +class MLDSA87PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> MLDSA87PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa87_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). + + The public key is 2,592 bytes for MLDSA-87. + """ + + @abc.abstractmethod + def verify( + self, + signature: Buffer, + data: Buffer, + context: Buffer | None = None, + ) -> None: + """ + Verify the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA87PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA87PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA87PublicKey.register(rust_openssl.mldsa.MLDSA87PublicKey) + + +class MLDSA87PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLDSA87PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.generate_mldsa87_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLDSA87PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mldsa_supported(): + raise UnsupportedAlgorithm( + "ML-DSA-87 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mldsa.from_mldsa87_seed_bytes(data) + + @abc.abstractmethod + def public_key(self) -> MLDSA87PublicKey: + """ + The MLDSA87PublicKey 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. + + This method only returns the serialization of the seed form of the + private key, never the expanded one. + """ + + @abc.abstractmethod + def private_bytes_raw(self) -> bytes: + """ + The raw bytes of the private key. + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + + This method only returns the seed form of the private key (32 bytes). + """ + + @abc.abstractmethod + def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def __copy__(self) -> MLDSA87PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLDSA87PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mldsa"): + MLDSA87PrivateKey.register(rust_openssl.mldsa.MLDSA87PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py new file mode 100644 index 0000000..64bca11 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py @@ -0,0 +1,278 @@ +# 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 +from cryptography.utils import Buffer + + +class MLKEM768PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: Buffer) -> MLKEM768PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem768_public_bytes(data) + + @abc.abstractmethod + def encapsulate(self) -> tuple[bytes, bytes]: + """ + Encapsulate: returns (shared_secret, ciphertext). + """ + + @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). + + The public key is 1,184 bytes for ML-KEM-768. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM768PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM768PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM768PublicKey.register(rust_openssl.mlkem.MLKEM768PublicKey) + + +class MLKEM768PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLKEM768PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.generate_mlkem768_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLKEM768PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-768 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem768_seed_bytes(data) + + @abc.abstractmethod + def decapsulate(self, ciphertext: Buffer) -> bytes: + """ + Decapsulate: returns shared_secret. + """ + + @abc.abstractmethod + def public_key(self) -> MLKEM768PublicKey: + """ + The MLKEM768PublicKey derived from 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 (64-byte seed). + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM768PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM768PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM768PrivateKey.register(rust_openssl.mlkem.MLKEM768PrivateKey) + + +class MLKEM1024PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: Buffer) -> MLKEM1024PublicKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem1024_public_bytes(data) + + @abc.abstractmethod + def encapsulate(self) -> tuple[bytes, bytes]: + """ + Encapsulate: returns (shared_secret, ciphertext). + """ + + @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). + + The public key is 1,568 bytes for ML-KEM-1024. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM1024PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM1024PublicKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM1024PublicKey.register(rust_openssl.mlkem.MLKEM1024PublicKey) + + +class MLKEM1024PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> MLKEM1024PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.generate_mlkem1024_key() + + @classmethod + def from_seed_bytes(cls, data: Buffer) -> MLKEM1024PrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.mlkem_supported(): + raise UnsupportedAlgorithm( + "ML-KEM-1024 is not supported by this backend.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return rust_openssl.mlkem.from_mlkem1024_seed_bytes(data) + + @abc.abstractmethod + def decapsulate(self, ciphertext: Buffer) -> bytes: + """ + Decapsulate: returns shared_secret. + """ + + @abc.abstractmethod + def public_key(self) -> MLKEM1024PublicKey: + """ + The MLKEM1024PublicKey derived from 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 (64-byte seed). + Equivalent to private_bytes(Raw, Raw, NoEncryption()). + """ + + @abc.abstractmethod + def __copy__(self) -> MLKEM1024PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> MLKEM1024PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "mlkem"): + MLKEM1024PrivateKey.register(rust_openssl.mlkem.MLKEM1024PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 0000000..5121a28 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,111 @@ +# 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): + 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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 0000000..d730ceb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,295 @@ +# 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, lcm + +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 + | asym_utils.NoDigestInfo, + ) -> 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. + """ + + @abc.abstractmethod + def __copy__(self) -> RSAPrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> RSAPrivateKey: + """ + Returns a deep copy. + """ + + +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 | asym_utils.NoDigestInfo | None, + ) -> bytes: + """ + Recovers the original data from the signature. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __copy__(self) -> RSAPublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> RSAPublicKey: + """ + Returns a deep copy. + """ + + +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. + """ + if p <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + 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. + """ + if private_exponent <= 1 or p <= 1: + raise ValueError("Values can't be <= 1") + 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. + """ + if private_exponent <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + 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) + if e <= 1 or p <= 1 or q <= 1: + raise ValueError("Values can't be <= 1") + return _modinv(e, lcm(p - 1, q - 1)) + + +# 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 d <= 1 or e <= 1: + raise ValueError("d, e can't be <= 1") + 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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py new file mode 100644 index 0000000..dfd12ce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py @@ -0,0 +1,123 @@ +# 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, + mldsa, + mlkem, + rsa, + x448, + x25519, +) + +# Every asymmetric key type +PublicKeyTypes = typing.Union[ + dh.DHPublicKey, + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + mldsa.MLDSA44PublicKey, + mldsa.MLDSA65PublicKey, + mldsa.MLDSA87PublicKey, + mlkem.MLKEM768PublicKey, + mlkem.MLKEM1024PublicKey, + 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, + mldsa.MLDSA44PrivateKey, + mldsa.MLDSA65PrivateKey, + mldsa.MLDSA87PrivateKey, + mlkem.MLKEM768PrivateKey, + mlkem.MLKEM1024PrivateKey, + 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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py new file mode 100644 index 0000000..c01c342 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py @@ -0,0 +1,28 @@ +# 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 NoDigestInfo: + pass + + +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/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py new file mode 100644 index 0000000..7498998 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py @@ -0,0 +1,134 @@ +# 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 +from cryptography.utils import Buffer + + +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. + """ + + @abc.abstractmethod + def __copy__(self) -> X25519PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X25519PublicKey: + """ + Returns a deep copy. + """ + + +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: Buffer) -> 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. + """ + + @abc.abstractmethod + def __copy__(self) -> X25519PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X25519PrivateKey: + """ + Returns a deep copy. + """ + + +X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py new file mode 100644 index 0000000..b9dc826 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py @@ -0,0 +1,137 @@ +# 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 +from cryptography.utils import Buffer + + +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. + """ + + @abc.abstractmethod + def __copy__(self) -> X448PublicKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X448PublicKey: + """ + Returns a deep copy. + """ + + +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: Buffer) -> 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. + """ + + @abc.abstractmethod + def __copy__(self) -> X448PrivateKey: + """ + Returns a copy. + """ + + @abc.abstractmethod + def __deepcopy__(self, memo: dict) -> X448PrivateKey: + """ + Returns a deep copy. + """ + + +if hasattr(rust_openssl, "x448"): + X448PrivateKey.register(rust_openssl.x448.X448PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 0000000..10c15d0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,27 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py new file mode 100644 index 0000000..c8a582d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py @@ -0,0 +1,23 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 0000000..6b20dd5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,138 @@ +# 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 ( + Camellia as Camellia, +) +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: utils.Buffer): + 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: utils.Buffer): + 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: utils.Buffer): + self.key = _verify_key_size(self, key) + + +utils.deprecated( + Camellia, + __name__, + "Camellia has been moved to " + "cryptography.hazmat.decrepit.ciphers.algorithms.Camellia and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.algorithms in 49.0.0.", + utils.DeprecatedIn43, + name="Camellia", +) + + +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", +) + + +class ChaCha20(CipherAlgorithm): + name = "ChaCha20" + key_sizes = frozenset([256]) + + def __init__(self, key: utils.Buffer, nonce: utils.Buffer): + 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) -> utils.Buffer: + 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/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py new file mode 100644 index 0000000..24fceea --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py @@ -0,0 +1,146 @@ +# 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 +from cryptography.utils import Buffer + + +class CipherContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: Buffer) -> bytes: + """ + Processes the provided bytes through the cipher and returns the results + as bytes. + """ + + @abc.abstractmethod + def update_into(self, data: Buffer, buf: Buffer) -> 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: Buffer) -> 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, + modes.ECB, + modes.ModeWithInitializationVector, + None, + ] +] + +CipherContext.register(rust_openssl.ciphers.CipherContext) +AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext) +AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py new file mode 100644 index 0000000..0f1c217 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py @@ -0,0 +1,192 @@ +# 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.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.decrepit.ciphers.modes import CFB as CFB +from cryptography.hazmat.decrepit.ciphers.modes import CFB8 as CFB8 +from cryptography.hazmat.decrepit.ciphers.modes import OFB as OFB +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives._modes import ( + Mode as Mode, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithAuthenticationTag as ModeWithAuthenticationTag, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithInitializationVector as ModeWithInitializationVector, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithNonce as ModeWithNonce, +) +from cryptography.hazmat.primitives._modes import ( + ModeWithTweak as ModeWithTweak, +) +from cryptography.hazmat.primitives._modes import ( + _check_aes_key_length, + _check_iv_and_key_length, + _check_nonce_length, +) +from cryptography.hazmat.primitives.ciphers import algorithms + + +class CBC(ModeWithInitializationVector): + name = "CBC" + + def __init__(self, initialization_vector: utils.Buffer): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> utils.Buffer: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class XTS(ModeWithTweak): + name = "XTS" + + def __init__(self, tweak: utils.Buffer): + 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) -> utils.Buffer: + 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 CTR(ModeWithNonce): + name = "CTR" + + def __init__(self, nonce: utils.Buffer): + utils._check_byteslike("nonce", nonce) + self._nonce = nonce + + @property + def nonce(self) -> utils.Buffer: + 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: utils.Buffer, + 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) -> utils.Buffer: + 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." + ) + + +utils.deprecated( + OFB, + __name__, + "OFB has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.OFB and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="OFB", +) + + +utils.deprecated( + CFB, + __name__, + "CFB has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.CFB and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="CFB", +) + + +utils.deprecated( + CFB8, + __name__, + "CFB8 has been moved to " + "cryptography.hazmat.decrepit.ciphers.modes.CFB8 and " + "will be removed from " + "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", + utils.DeprecatedIn47, + name="CFB8", +) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 0000000..2c67ce2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,10 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py new file mode 100644 index 0000000..3975c71 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py @@ -0,0 +1,14 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py new file mode 100644 index 0000000..4b55ec3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py @@ -0,0 +1,246 @@ +# 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.utils import Buffer + +__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", + "XOFHash", +] + + +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: Buffer) -> 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) + +XOFHash = rust_openssl.hashes.XOFHash + + +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/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py new file mode 100644 index 0000000..a9442d5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py @@ -0,0 +1,13 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py new file mode 100644 index 0000000..c802808 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py @@ -0,0 +1,27 @@ +# 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 + +AEAD = rust_openssl.hpke.AEAD +KDF = rust_openssl.hpke.KDF +KEM = rust_openssl.hpke.KEM +MLKEM768X25519PrivateKey = rust_openssl.hpke.MLKEM768X25519PrivateKey +MLKEM768X25519PublicKey = rust_openssl.hpke.MLKEM768X25519PublicKey +MLKEM1024P384PrivateKey = rust_openssl.hpke.MLKEM1024P384PrivateKey +MLKEM1024P384PublicKey = rust_openssl.hpke.MLKEM1024P384PublicKey +Suite = rust_openssl.hpke.Suite + +__all__ = [ + "AEAD", + "KDF", + "KEM", + "MLKEM768X25519PrivateKey", + "MLKEM768X25519PublicKey", + "MLKEM1024P384PrivateKey", + "MLKEM1024P384PublicKey", + "Suite", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 0000000..26c45bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py @@ -0,0 +1,32 @@ +# 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.utils import Buffer + + +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 derive_into(self, key_material: bytes, buffer: Buffer) -> None: + """ + Deterministically generates a new key based on the existing key + material and stores it in the provided buffer. + """ + + @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/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py new file mode 100644 index 0000000..03e84d4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py @@ -0,0 +1,17 @@ +# 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 + +Argon2d = rust_openssl.kdf.Argon2d +Argon2i = rust_openssl.kdf.Argon2i +Argon2id = rust_openssl.kdf.Argon2id +KeyDerivationFunction.register(Argon2d) +KeyDerivationFunction.register(Argon2i) +KeyDerivationFunction.register(Argon2id) + +__all__ = ["Argon2d", "Argon2i", "Argon2id"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py new file mode 100644 index 0000000..398dc5d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py @@ -0,0 +1,16 @@ +# 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 + +ConcatKDFHash = rust_openssl.kdf.ConcatKDFHash +ConcatKDFHMAC = rust_openssl.kdf.ConcatKDFHMAC + +KeyDerivationFunction.register(ConcatKDFHash) +KeyDerivationFunction.register(ConcatKDFHMAC) + +__all__ = ["ConcatKDFHMAC", "ConcatKDFHash"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py new file mode 100644 index 0000000..1e162d9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py @@ -0,0 +1,16 @@ +# 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 + +HKDF = rust_openssl.kdf.HKDF +HKDFExpand = rust_openssl.kdf.HKDFExpand + +KeyDerivationFunction.register(HKDF) +KeyDerivationFunction.register(HKDFExpand) + +__all__ = ["HKDF", "HKDFExpand"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py new file mode 100644 index 0000000..e559df8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py @@ -0,0 +1,26 @@ +# 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 openssl as rust_openssl +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" + + +KBKDFHMAC = rust_openssl.kdf.KBKDFHMAC +KeyDerivationFunction.register(KBKDFHMAC) + +KBKDFCMAC = rust_openssl.kdf.KBKDFCMAC +KeyDerivationFunction.register(KBKDFCMAC) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 0000000..771d80d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -0,0 +1,13 @@ +# 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 + +PBKDF2HMAC = rust_openssl.kdf.PBKDF2HMAC +KeyDerivationFunction.register(PBKDF2HMAC) + +__all__ = ["PBKDF2HMAC"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py new file mode 100644 index 0000000..f791cee --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py @@ -0,0 +1,19 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py new file mode 100644 index 0000000..8c4e2d3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py @@ -0,0 +1,13 @@ +# 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 + +X963KDF = rust_openssl.kdf.X963KDF +KeyDerivationFunction.register(X963KDF) + +__all__ = ["X963KDF"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py new file mode 100644 index 0000000..a3e56b9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py @@ -0,0 +1,180 @@ +# 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") + + if not key_to_wrap or len(key_to_wrap) > 2**32: + raise ValueError("key_to_wrap must be between 1 and 2^32 bytes") + + 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/python3.12/site-packages/cryptography/hazmat/primitives/padding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py new file mode 100644 index 0000000..f9cd1f1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py @@ -0,0 +1,69 @@ +# 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.bindings._rust import ( + ANSIX923PaddingContext, + ANSIX923UnpaddingContext, + PKCS7PaddingContext, + PKCS7UnpaddingContext, +) + + +class PaddingContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: utils.Buffer) -> 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.") + + +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) + + +PaddingContext.register(ANSIX923PaddingContext) +PaddingContext.register(ANSIX923UnpaddingContext) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py new file mode 100644 index 0000000..7f5a77a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py @@ -0,0 +1,11 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py new file mode 100644 index 0000000..62283cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py @@ -0,0 +1,65 @@ +# 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, + ssh_key_fingerprint, +) + +__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", + "ssh_key_fingerprint", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py new file mode 100644 index 0000000..e7c998b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py @@ -0,0 +1,14 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py new file mode 100644 index 0000000..58884ff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -0,0 +1,176 @@ +# 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 collections.abc import Iterable + +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_java_truststore", + "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_java_truststore( + certs: Iterable[PKCS12Certificate], + encryption_algorithm: serialization.KeySerializationEncryption, +) -> bytes: + if not certs: + raise ValueError("You must supply at least one cert") + + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Key encryption algorithm must be a " + "KeySerializationEncryption instance" + ) + + return rust_pkcs12.serialize_java_truststore(certs, encryption_algorithm) + + +def serialize_key_and_certificates( + name: bytes | None, + key: PKCS12PrivateKeyTypes | None, + cert: x509.Certificate | None, + cas: 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/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py new file mode 100644 index 0000000..76b667a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -0,0 +1,412 @@ +# 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 collections.abc import Iterable + +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.hazmat.primitives.ciphers import ( + algorithms, +) +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 +] + +ContentEncryptionAlgorithm = typing.Union[ + type[algorithms.AES128], type[algorithms.AES256] +] + + +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: utils.Buffer | 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: utils.Buffer) -> 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: 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, + _content_encryption_algorithm: ContentEncryptionAlgorithm + | 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 [] + self._content_encryption_algorithm = _content_encryption_algorithm + + 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, + _content_encryption_algorithm=self._content_encryption_algorithm, + ) + + 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, + ], + _content_encryption_algorithm=self._content_encryption_algorithm, + ) + + def set_content_encryption_algorithm( + self, content_encryption_algorithm: ContentEncryptionAlgorithm + ) -> PKCS7EnvelopeBuilder: + if self._content_encryption_algorithm is not None: + raise ValueError("Content encryption algo may only be set once") + if content_encryption_algorithm not in { + algorithms.AES128, + algorithms.AES256, + }: + raise TypeError("Only AES128 and AES256 are supported") + + return PKCS7EnvelopeBuilder( + _data=self._data, + _recipients=self._recipients, + _content_encryption_algorithm=content_encryption_algorithm, + ) + + def encrypt( + self, + encoding: serialization.Encoding, + options: 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") + + # The default content encryption algorithm is AES-128-CBC, which the + # S/MIME v3.2 RFC specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.7) + # however rest of S/MIME v3.2 is not currently supported + content_encryption_algorithm = ( + self._content_encryption_algorithm or algorithms.AES128 + ) + + 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, content_encryption_algorithm, 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/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py new file mode 100644 index 0000000..411113b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py @@ -0,0 +1,1621 @@ +# 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: utils.Buffer, + 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: utils.Buffer, 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: utils.Buffer) -> 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 TypeError( + "Key is password-protected, but password was not provided." + ) + + 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[utils.Buffer] + + def __init__(self, init: list[utils.Buffer] | None = None) -> None: + self.flist = [] + if init: + self.flist.extend(init) + + def put_raw(self, val: utils.Buffer) -> 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, unsafe_skip_rsa_key_validation: bool + ) -> 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( + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation + ) + 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, unsafe_skip_rsa_key_validation: bool + ) -> 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 len(point) == 0: + raise ValueError("Invalid EC point: empty data") + 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, unsafe_skip_rsa_key_validation: bool + ) -> 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, unsafe_skip_rsa_key_validation: bool + ) -> 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 + + def get_public(self, data: memoryview) -> typing.NoReturn: + # Confusingly `get_public` is an entry point used by private key + # loading. + raise UnsupportedAlgorithm( + "sk-ssh-ed25519 private keys cannot be loaded" + ) + + +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 + + def get_public(self, data: memoryview) -> typing.NoReturn: + # Confusingly `get_public` is an entry point used by private key + # loading. + raise UnsupportedAlgorithm( + "sk-ecdsa-sha2-nistp256 private keys cannot be loaded" + ) + + +_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: utils.Buffer): + """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: utils.Buffer, + password: bytes | None, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> 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 != _NONE or kdfname != _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: + if password: + raise TypeError( + "Password was given but private key is not encrypted." + ) + # 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, + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, + ) + # 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: utils.Buffer, + _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: utils.Buffer, +) -> 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 ssh_key_fingerprint( + key: SSHPublicKeyTypes, + hash_algorithm: hashes.MD5 | hashes.SHA256, +) -> bytes: + if not isinstance(hash_algorithm, (hashes.MD5, hashes.SHA256)): + raise TypeError("hash_algorithm must be either MD5 or SHA256") + + key_type = _get_ssh_key_type(key) + kformat = _lookup_kformat(key_type) + + f_pub = _FragList() + f_pub.put_sshstr(key_type) + kformat.encode_public(key, f_pub) + + ssh_binary_data = f_pub.tobytes() + + # Hash the binary data + hash_obj = hashes.Hash(hash_algorithm) + hash_obj.update(ssh_binary_data) + return hash_obj.finalize() + + +def load_ssh_public_key( + data: utils.Buffer, 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/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 0000000..c1af423 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py @@ -0,0 +1,9 @@ +# 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/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 0000000..21fb000 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,101 @@ +# 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 +from cryptography.utils import Buffer + +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: Buffer, + 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/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 0000000..10c725c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,56 @@ +# 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, +) +from cryptography.utils import Buffer + + +class TOTP: + def __init__( + self, + key: Buffer, + 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/python3.12/site-packages/cryptography/py.typed b/.venv/lib/python3.12/site-packages/cryptography/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/cryptography/utils.py b/.venv/lib/python3.12/site-packages/cryptography/utils.py new file mode 100644 index 0000000..9cfc992 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/utils.py @@ -0,0 +1,135 @@ +# 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 +from collections.abc import Callable, Sequence + + +# 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 +DeprecatedIn40 = CryptographyDeprecationWarning +DeprecatedIn41 = CryptographyDeprecationWarning +DeprecatedIn42 = CryptographyDeprecationWarning +DeprecatedIn43 = CryptographyDeprecationWarning +DeprecatedIn47 = CryptographyDeprecationWarning + + +# If you're wondering why we don't use `Buffer`, it's because `Buffer` would +# be more accurately named: Bufferable. It means something which has an +# `__buffer__`. Which means you can't actually treat the result as a buffer +# (and do things like take a `len()`). +Buffer = typing.Union[bytes, bytearray, memoryview] + + +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: Buffer) -> 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, name: str) -> typing.Any: + obj = getattr(self._module, name) + 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) -> 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: 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/python3.12/site-packages/cryptography/x509/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py new file mode 100644 index 0000000..cb34833 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py @@ -0,0 +1,271 @@ +# 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, oid, 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, + PrivateKeyUsagePeriod, + 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_PRIVATE_KEY_USAGE_PERIOD = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD +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", + "PrivateKeyUsagePeriod", + "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", + "oid", + "random_serial_number", + "verification", + "verification", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/base.py b/.venv/lib/python3.12/site-packages/cryptography/x509/base.py new file mode 100644 index 0000000..a11b8fe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/base.py @@ -0,0 +1,773 @@ +# 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 os +import typing +from collections.abc import Iterable + +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, + 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: 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 +RevokedCertificate = rust_x509.RevokedCertificate + + +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, + ecdsa_deterministic: bool | 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") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_csr( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + +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, + ecdsa_deterministic: bool | 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") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_certificate( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + +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, + ecdsa_deterministic: bool | 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") + + if ecdsa_deterministic is not None: + if not isinstance(private_key, ec.EllipticCurvePrivateKey): + raise TypeError( + "Deterministic ECDSA is only supported for EC keys" + ) + + return rust_x509.create_x509_crl( + self, + private_key, + algorithm, + rsa_padding, + ecdsa_deterministic, + ) + + +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 rust_x509.create_revoked_certificate(self) + + +def random_serial_number() -> int: + return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py b/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py new file mode 100644 index 0000000..fb66cc6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py @@ -0,0 +1,35 @@ +# 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/python3.12/site-packages/cryptography/x509/extensions.py b/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py new file mode 100644 index 0000000..7b78e9e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py @@ -0,0 +1,2533 @@ +# 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 collections.abc import Iterable, Iterator + +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: 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: 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: 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: 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: 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: 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: Iterable[GeneralName] | None, + relative_name: RelativeDistinguishedName | None, + reasons: frozenset[ReasonFlags] | None, + crl_issuer: 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: 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: 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: 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: 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: 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 PrivateKeyUsagePeriod(ExtensionType): + oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD + + def __init__( + self, + not_before: datetime.datetime | None, + not_after: datetime.datetime | None, + ) -> None: + if ( + not isinstance(not_before, datetime.datetime) + and not_before is not None + ): + raise TypeError("not_before must be a datetime.datetime or None") + + if ( + not isinstance(not_after, datetime.datetime) + and not_after is not None + ): + raise TypeError("not_after must be a datetime.datetime or None") + + if not_before is None and not_after is None: + raise ValueError( + "At least one of not_before and not_after must not be None" + ) + + if ( + not_before is not None + and not_after is not None + and not_before > not_after + ): + raise ValueError("not_before must be before not_after") + + self._not_before = not_before + self._not_after = not_after + + @property + def not_before(self) -> datetime.datetime | None: + return self._not_before + + @property + def not_after(self) -> datetime.datetime | None: + return self._not_after + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrivateKeyUsagePeriod): + return NotImplemented + + return ( + self.not_before == other.not_before + and self.not_after == other.not_after + ) + + def __hash__(self) -> int: + return hash((self.not_before, self.not_after)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class NameConstraints(ExtensionType): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__( + self, + permitted_subtrees: Iterable[GeneralName] | None, + excluded_subtrees: 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: Iterable[GeneralName]) -> None: + self._validate_ip_name(tree) + self._validate_dns_name(tree) + + def _validate_ip_name(self, tree: 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: 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: 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] # type: ignore[return-value,unused-ignore] + return list(objs) # type: ignore[return-value,unused-ignore] + + 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: 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: 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: 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: 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: 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: 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) -> 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: 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: + if self.full_name is not None: + full_name: tuple[GeneralName, ...] | None = tuple(self.full_name) + else: + full_name = None + + return hash( + ( + 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: Iterable[str], + profession_oids: 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: 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: 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/python3.12/site-packages/cryptography/x509/general_name.py b/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py new file mode 100644 index 0000000..672f287 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py @@ -0,0 +1,281 @@ +# 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/python3.12/site-packages/cryptography/x509/name.py b/.venv/lib/python3.12/site-packages/cryptography/x509/name.py new file mode 100644 index 0000000..57f3d56 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/name.py @@ -0,0 +1,482 @@ +# 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 collections.abc import Iterable, Iterator + +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] == "#" or (val[0] == " " and len(val) > 1): + 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(0) + # Special character escape + if len(val) == 2: + return val[1:] + + # Unicode string of hex + return binascii.unhexlify(val.replace("\\", "")).decode() + + return _RFC4514NameParser._PAIR_MULTI_RE.sub(sub, val) + + +NameAttributeValueType = typing.TypeVar( + "NameAttributeValueType", + typing.Union[str, bytes], + str, + bytes, + covariant=True, +) + + +class NameAttribute(typing.Generic[NameAttributeValueType]): + def __init__( + self, + oid: ObjectIdentifier, + value: NameAttributeValueType, + _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") + elif 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: NameAttributeValueType = value + self._type: _ASN1Type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> NameAttributeValueType: + 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: Iterable[NameAttribute[str | bytes]]): + 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[str | bytes]]: + 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) -> Iterator[NameAttribute[str | bytes]]: + 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: Iterable[NameAttribute[str | bytes]] + ) -> None: ... + + @typing.overload + def __init__( + self, attributes: Iterable[RelativeDistinguishedName] + ) -> None: ... + + def __init__( + self, + attributes: Iterable[ + NameAttribute[str | bytes] | 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( + 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[str | bytes]]: + 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) -> Iterator[NameAttribute[str | bytes]]: + 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: + 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-]*") + + _ESCAPE_SPECIAL = r"[\\ #=\"\+,;<>]" + _ESCAPE_HEX = r"[\da-zA-Z]{2}" + _PAIR = rf"\\({_ESCAPE_SPECIAL}|{_ESCAPE_HEX})" + _PAIR_MULTI_RE = re.compile(rf"(\\{_ESCAPE_SPECIAL})|((\\{_ESCAPE_HEX})+)") + _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[str]: + 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/python3.12/site-packages/cryptography/x509/ocsp.py b/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py new file mode 100644 index 0000000..f61ed80 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py @@ -0,0 +1,379 @@ +# 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 +from collections.abc import Iterable + +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 _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, + resp: tuple[x509.Certificate, x509.Certificate] | None, + resp_hash: tuple[bytes, bytes, int] | None, + 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, + ): + _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._resp = resp + self._resp_hash = resp_hash + 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") + + 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.") + + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + singleresp = _SingleResponse( + (cert, issuer), + None, + algorithm, + cert_status, + this_update, + next_update, + revocation_time, + revocation_reason, + ) + return OCSPResponseBuilder( + singleresp, + self._responder_id, + self._certs, + self._extensions, + ) + + def add_response_by_hash( + self, + issuer_name_hash: bytes, + issuer_key_hash: bytes, + serial_number: int, + 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.") + + if not isinstance(serial_number, int): + raise TypeError("serial_number must be an integer") + + utils._check_bytes("issuer_name_hash", issuer_name_hash) + utils._check_bytes("issuer_key_hash", issuer_key_hash) + _verify_algorithm(algorithm) + 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" + ) + + singleresp = _SingleResponse( + None, + (issuer_name_hash, issuer_key_hash, serial_number), + 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: 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/python3.12/site-packages/cryptography/x509/oid.py b/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py new file mode 100644 index 0000000..520fc7a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py @@ -0,0 +1,37 @@ +# 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, + OtherNameFormOID, + PublicKeyAlgorithmOID, + SignatureAlgorithmOID, + SubjectInformationAccessOID, +) + +__all__ = [ + "AttributeOID", + "AuthorityInformationAccessOID", + "CRLEntryExtensionOID", + "CertificatePoliciesOID", + "ExtendedKeyUsageOID", + "ExtensionOID", + "NameOID", + "OCSPExtensionOID", + "ObjectIdentifier", + "OtherNameFormOID", + "PublicKeyAlgorithmOID", + "SignatureAlgorithmOID", + "SubjectInformationAccessOID", +] diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py b/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py new file mode 100644 index 0000000..2db4324 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py @@ -0,0 +1,34 @@ +# 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", + "Criticality", + "ExtensionPolicy", + "Policy", + "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 +Policy = rust_x509.Policy +ExtensionPolicy = rust_x509.ExtensionPolicy +Criticality = rust_x509.Criticality +VerificationError = rust_x509.VerificationError diff --git a/.venv/lib/python3.12/site-packages/distutils-precedence.pth b/.venv/lib/python3.12/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..7f009fe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +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/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA new file mode 100644 index 0000000..8a2f639 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/METADATA @@ -0,0 +1,202 @@ +Metadata-Version: 2.4 +Name: h11 +Version: 0.16.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.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 :: WWW/HTTP +Classifier: Topic :: System :: Networking +Requires-Python: >=3.8 +License-File: LICENSE.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-python +Dynamic: summary + +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.8-3.12) 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/python3.12/site-packages/h11-0.16.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD new file mode 100644 index 0000000..a8f8e63 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/RECORD @@ -0,0 +1,29 @@ +h11-0.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +h11-0.16.0.dist-info/METADATA,sha256=KPMmCYrAn8unm48YD5YIfIQf4kViFct7hyqcfVzRnWQ,8348 +h11-0.16.0.dist-info/RECORD,, +h11-0.16.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91 +h11-0.16.0.dist-info/licenses/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 +h11-0.16.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 +h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 +h11/__pycache__/__init__.cpython-312.pyc,, +h11/__pycache__/_abnf.cpython-312.pyc,, +h11/__pycache__/_connection.cpython-312.pyc,, +h11/__pycache__/_events.cpython-312.pyc,, +h11/__pycache__/_headers.cpython-312.pyc,, +h11/__pycache__/_readers.cpython-312.pyc,, +h11/__pycache__/_receivebuffer.cpython-312.pyc,, +h11/__pycache__/_state.cpython-312.pyc,, +h11/__pycache__/_util.cpython-312.pyc,, +h11/__pycache__/_version.cpython-312.pyc,, +h11/__pycache__/_writers.cpython-312.pyc,, +h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 +h11/_connection.py,sha256=k9YRVf6koZqbttBW36xSWaJpWdZwa-xQVU9AHEo9DuI,26863 +h11/_events.py,sha256=I97aXoal1Wu7dkL548BANBUCkOIbe-x5CioYA9IBY14,11792 +h11/_headers.py,sha256=P7D-lBNxHwdLZPLimmYwrPG-9ZkjElvvJZJdZAgSP-4,10412 +h11/_readers.py,sha256=a4RypORUCC3d0q_kxPuBIM7jTD8iLt5X91TH0FsduN4,8590 +h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 +h11/_state.py,sha256=_5LG_BGR8FCcFQeBPH-TMHgm_-B-EUcWCnQof_9XjFE,13231 +h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 +h11/_version.py,sha256=GVSsbPSPDcOuF6ptfIiXnVJoaEm3ygXbMnqlr_Giahw,686 +h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 +h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 diff --git a/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL new file mode 100644 index 0000000..1eb3c49 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..8f080ea --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/licenses/LICENSE.txt @@ -0,0 +1,22 @@ +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/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt new file mode 100644 index 0000000..0d24def --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11-0.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +h11 diff --git a/.venv/lib/python3.12/site-packages/h11/__init__.py b/.venv/lib/python3.12/site-packages/h11/__init__.py new file mode 100644 index 0000000..989e92c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/__init__.py @@ -0,0 +1,62 @@ +# 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/python3.12/site-packages/h11/_abnf.py b/.venv/lib/python3.12/site-packages/h11/_abnf.py new file mode 100644 index 0000000..933587f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_abnf.py @@ -0,0 +1,132 @@ +# 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/python3.12/site-packages/h11/_connection.py b/.venv/lib/python3.12/site-packages/h11/_connection.py new file mode 100644 index 0000000..e37d82a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_connection.py @@ -0,0 +1,659 @@ +# This contains the main Connection class. Everything in h11 revolves around +# this. +from typing import ( + Any, + Callable, + cast, + Dict, + List, + Optional, + overload, + 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(f"expected CLIENT or SERVER, not {our_role!r}") + 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() + 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 + + @overload + def send(self, event: ConnectionClosed) -> None: + ... + + @overload + def send( + self, event: Union[Request, InformationalResponse, Response, Data, EndOfMessage] + ) -> bytes: + ... + + @overload + def send(self, event: Event) -> Optional[bytes]: + ... + + 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/python3.12/site-packages/h11/_events.py b/.venv/lib/python3.12/site-packages/h11/_events.py new file mode 100644 index 0000000..ca1c3ad --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_events.py @@ -0,0 +1,369 @@ +# 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 +from typing import 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/python3.12/site-packages/h11/_headers.py b/.venv/lib/python3.12/site-packages/h11/_headers.py new file mode 100644 index 0000000..31da3e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_headers.py @@ -0,0 +1,282 @@ +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 + +CONTENT_LENGTH_MAX_DIGITS = 20 # allow up to 1 billion TB - 1 + + +# 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 len(value) > CONTENT_LENGTH_MAX_DIGITS: + raise LocalProtocolError("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/python3.12/site-packages/h11/_readers.py b/.venv/lib/python3.12/site-packages/h11/_readers.py new file mode 100644 index 0000000..576804c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_readers.py @@ -0,0 +1,250 @@ +# 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. + # This tracks the bytes that we need to match and throw away. + self._bytes_to_discard = b"" + 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: + data = buf.maybe_extract_at_most(len(self._bytes_to_discard)) + if data is None: + return None + if data != self._bytes_to_discard[: len(data)]: + raise LocalProtocolError( + f"malformed chunk footer: {data!r} (expected {self._bytes_to_discard!r})" + ) + self._bytes_to_discard = self._bytes_to_discard[len(data) :] + if self._bytes_to_discard: + return None + # else, fall through and read some more + assert self._bytes_to_discard == b"" + 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 = b"\r\n" + 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/python3.12/site-packages/h11/_receivebuffer.py b/.venv/lib/python3.12/site-packages/h11/_receivebuffer.py new file mode 100644 index 0000000..e5c4e08 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_receivebuffer.py @@ -0,0 +1,153 @@ +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/python3.12/site-packages/h11/_state.py b/.venv/lib/python3.12/site-packages/h11/_state.py new file mode 100644 index 0000000..3ad444b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_state.py @@ -0,0 +1,365 @@ +################################################################ +# 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 _SWITCH_UPGRADE event without a pending proposal" + ) + _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( + f"not in a reusable state. self.states={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/python3.12/site-packages/h11/_util.py b/.venv/lib/python3.12/site-packages/h11/_util.py new file mode 100644 index 0000000..6718445 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_util.py @@ -0,0 +1,135 @@ +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/python3.12/site-packages/h11/_version.py b/.venv/lib/python3.12/site-packages/h11/_version.py new file mode 100644 index 0000000..76e7327 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_version.py @@ -0,0 +1,16 @@ +# 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.16.0" diff --git a/.venv/lib/python3.12/site-packages/h11/_writers.py b/.venv/lib/python3.12/site-packages/h11/_writers.py new file mode 100644 index 0000000..939cdb9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/_writers.py @@ -0,0 +1,145 @@ +# 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/python3.12/site-packages/h11/py.typed b/.venv/lib/python3.12/site-packages/h11/py.typed new file mode 100644 index 0000000..f5642f7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/h11/py.typed @@ -0,0 +1 @@ +Marker diff --git a/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/LICENSE new file mode 100644 index 0000000..b3dbff0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/LICENSE @@ -0,0 +1,22 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/METADATA new file mode 100644 index 0000000..0fa6efb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/html5tagger-1.3.0.dist-info/METADATA @@ -0,0 +1,193 @@ +Metadata-Version: 2.1 +Name: html5tagger +Version: 1.3.0 +Summary: Pythonic HTML generation/templating (no template files) +Home-page: https://github.com/sanic-org/html5tagger +Author: Sanic Community +Author-email: tronic@noreply.users.github.com +Keywords: HTML,HTML5,templating,Jinja2 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +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: Topic :: Text Processing :: Markup :: HTML +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: Public Domain +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE + +# HTML5 Generation with html5tagger: Fast, Pure Python, No Dependencies + +If you're looking for a more efficient and streamlined way to generate HTML5, look no further than html5tagger! This module provides a simplified HTML5 syntax, so you can create your entire document template using only Python. Say goodbye to the clunky and error-prone process of manually writing HTML tags. + +With html5tagger, you can safely and quickly generate HTML5 without any dependencies, making it the perfect solution for developers who value speed and simplicity. And with its pure Python implementation, you'll never have to worry about compatibility issues or adding extra libraries to your project. + +Ready to streamline your page rendering process? It is super fast to get started. Trust us, once you try html5tagger, you'll never go back to Jinja2 or manual HTML writing again! + +```sh +pip install html5tagger +``` + +## Intro + +html5tagger provides two starting points for HTML generation: `E` as an empty builder for creating HTML snippets, or `Document` for generating full HTML documents with a DOCTYPE declaration. Both produce a `Builder` object, in case you need that for type annotations. + +Create a snippet and add tags by dot notation: +```python +E.p("Powered by:").br.a(href="...")("html5tagger") +``` +```html +

Powered by:
html5tagger +``` + +A complete example with template variables and other features: + +```python +from html5tagger import Document, E + +# Create a document +doc = Document( + E.TitleText_, # The first argument is for , adding variable TitleText + lang="en", # Keyword arguments for <html> attributes + + # Just list the resources you need, no need to remember link/script tags + _urls=[ "style.css", "favicon.png", "manifest.json" ] +) + +# Upper case names are template variables. You can modify them later. +doc.Head_ +doc.h1.TitleText_("Demo") # Goes inside <h1> and updates <title> as well + +# This has been a hard problem for DOM other such generators: +doc.p("A paragraph with ").a("a link", href="/files")(" and ").em("formatting") + +# Use with for complex nesting (not often needed) +with doc.table(id="data"): + doc.tr.th("First").th("Second").th("Third") + doc.TableRows_ + +# Let's add something to the template variables +doc.Head._script("console.log('</script> escaping is weird')") + +table = doc.TableRows +for row in range(10): + table.tr + for col in range(3): + table.td(row * col) + +# Or remove the table data we just added +doc.TableRows = None +``` + +You can `str(doc)` to get the HTML code, and using `doc` directly usually has the desired effect as well (e.g. giving HTML responses). Jupyter Notebooks render it as HTML. For debugging, use `repr(doc)` where the templating variables are visible: + +```html +>>> doc +《Document Builder》 +<!DOCTYPE html><html lang=en><meta charset="utf-8"> +<title>《TitleText:Demo》 + + + +《Head:》 +

《TitleText:Demo》

+

A paragraph with a link and formatting + +
FirstSecondThird + 《TableRows》 +
+``` + +The actual HTML output is similar. No whitespace is added to the document, it is all on one line unless the content contains newlines. You may notice that `body` and other familiar tags are missing and that the escaping is very minimal. This is HTML5: the document is standards-compliant with a lot less cruft. + +## Templating + +Use template variables to build a document once and only update the dynamic parts at render time for faster performance. Access template variables via doc.TitleText and add content in parenthesis after the tag name. The underscore at the end of a tag name indicates the tag is added to the document and can have content in parenthesis, but any further tags on the same line go to the original document, not the template. + +## Nesting + +In HTML5 elements such as `

` do not need any closing tag, so we can keep adding content without worrying of when it should close. This module does not use closing tags for any elements where those are optional or forbidden. + +A tag is automatically closed when you add content to it or when another tag is added. Setting attributes alone does not close an element. Use `(None)` to close an empty element if any subsequent content is not meant to go inside it, e.g. `doc.script(None, src="...")`. + +For elements like `` and `
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�nK5uK^E~W_H_oHWJ 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

Z!|ZIKfU!Xic{jh0q5!v=#-N%d-Mw0TDM` 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νu1X~|3Ms>m?EW#MpzTGyhi+xN*v#u^09YX2}x8I*Hh1J96Lyhb{ z#P@o7!R+nSiA5?yCn{ArozQ62$PnUsOI5#=pxZ;G$$%ydBPG?E*TyPN1E+B? zS@cwdDpBS4^BbF6_ONL~HV(1-O@nHvi9~~E^2^cU=cv?^MG!4WqZ?alo08X%?AHO} zAF?PQo(CwKd24N;_~-;<;hGr5xMaj~JSqnsvzs%wYzqBNzb^DS+*Qhm)ayc@8`EkL z&znicRh$2Mnqa)g>uK;T+}aeCiuFbjcnXd=x|CdFuy{&(&+h!#5aAmHCdRJW61e7@ zn#PoB%&K)SUQZKY*Cxz+vf$%n;tv#32M@L#JOJS;Ze2nB=MMh%yO|Ci<#({p|IooE zo;T|v*6Vk05FONDZ`2^+H$~=gJNU@NTz>(@XO#M{y5Vv3ZKq+Cj4_c#hFLE!vllAv zyG;2z;bh5Q-yg_xO-fF|aP^o{TIu-lrxW{f?J@TOc1#7jgrgr>K+enukY|`iEwY88 zHQZxN`A4v>{P2WbQ_7!ed~H{2`piO496&L%riFBL*p3o^y;4HwS?_nSVT@XQ!co?X z=W}d0kC*x2-;mOW%l=I6Ie#d-ho7oB_m}-eh2LcpBKR3nkb&RV(1HvU6OC*eQZG_D zjY1LVr(UIU8gC#1{VZm`N9~E(MAOfIHSWu>$Z6lR=|Y5@yl3-_a!SE#-*>W|=dwuX z-R>FM*Eo&*X&M2c67A6S%{BZAII#OY-+StT;#%n2l_pqU{_9qIx4zaNnL*#nw%he} zl{7Q|<@)kpw@Rb$#hpNl5v(h9a4q{ZI#~K`*4NOi4n6}vS3PN z2r(4mc#B@>Z(hn;3FEUbWgUN-x$&!)vL1^AF8`%0(QVZgJq-;$`co6tOIcm++RjqJ zUsdwWbtSF~#JMvl4=3Jnl_&iUTS&fkIqhTFwo|idLsluCNl)UFZd>ex33j z)0#H}6I0}ID;B*#;3ZG`JZU5*wpV-dCLB({bMGai)=$Tb-a5BUQv-n0qWlN|P3D)E zt@zdV`TR=Q;%x3Px+bNy`oJ||aay%7MafKJJ-?!}tjx??Som5)OJU!_lGnO+Z($wb zU)`xpk-8!BNI0b9*Gk>!{*!pB)LgFslw63s2mn}4hY|*X$)ig~{#mb69l+H_f;Xi< zPnI>2($PC&rT#l&!#3DQ!-j20y&|?U=M}NOc+Sp!Ma*gJNu8|1hF@x6@#7Y}&Wkk~ zu%L1!@l#b2ZQm7J@0IZY-eb?l<(+M0Zd=2Mtnlf49oN z4;e`h@8c1?9kzuxs{ln66*HNssX3OYwn|#gf|O(=tcY-B`A3D~$NlCDtx7w~*>187 z*TuljNCpeO;to5WZ6*raCYv4#rPX78YYm3FC?If`)lgPX#lEQJvug5HR!blZsn-w} zBh(rHY_lafrFiiXgp(x)z5_2lgdpA3FbohF6nLIl53LW<_m5w-6y9V*0)o8|r!_ON!qb$OB$}BjOZVR`y-6i_^$I zPj&q$9et+)H^5D7ug=xeZ0VWW=v~8|((YOX2cd zkM_?k))F{McJSMJa?j3VFVJ--QZkulC`9HUPn z_$r`4DYcnEc`9W}sjq-DyOoJB*24}hrDv?hPvA$Vz+VwFIF?@T;kC*L@Dr)kJm9K@ zS?b2n(7C)6FaqNoBT8%ci*0JLs8qdA~hgMXB(g0WPV+N~E(#Z^+F@fB8d! zdH-j6?sGdo+cGeZeIKqF)XoTEwzS5_uD^Ozq>X2+e?^RIqqSvY^&CqmumGxk6KNu& zO^g@g!U1`*9;qrro{(&hI4~LKl?N&{(gqv4gzSJ|A0@=fW7T;{Xsj&o)IXG86w2^_ zH$INLBHBzvKN`9=S+atDfl|2DFi;q2idD-uWb^@r8((!Ao-hpNJ5u_`L zHp za2kW%1t`(&=q6#nIPdu5LJTISPNZqqPH>B>6e_JBf#ge+|EuZqHsH0Ad{KLZ1Jg@6Tee)8p zkub;jcl#DiCa%`_q~um-)CmJkvjg12NqT4i@=-$0V z*c~TJPFDABCkFA@$NyZ%Q2?LH_ykWM`J#9_E4ZC70^u{l$T;uN&xOJg_O+Z00?_4t z>T>_eJzecb4kj(XNlQ_DizB2l~z#f4`ynl z+sYG2DYPI}2#R}!EukbzJZ?(NGuu`FNFr)iYeFT@Nn`Isms#wL(>dJ=QPD4+# zRISd3RZlB&UTl^&k$QG8KlL|jNW55&d;LG#doZMW?A^8u>j{6%-W(zcqaGCVFd*vTJkp9wEEFB&x~-{~Wz$t%F7yU{nr~kE zg_8b+9IU^eLWKsggAGMil-r%KP_zS0Q@>nQ*x~(41t3&mTfOf+QDhk2Dg!~QLv%SI z&`L2YNT&vNab>}ikD6lGNX_~0vUa-AKq2HEJe`C!8uZ@#`}IYPnbiO+ME$=jSXfRn z22Ar6Y^lv_@fdvjpVlv{iAef+Yl|(KwMn1wR;sjy9e}6%Cu#7UWEqXXb0QcFc!qo% z@Dv`H0Z*UDEqGdlgb2k3z~2HqGtT}R@WkS|;E^%nw*wDTgnBQvm;|0vJ_bC83L*By zp4M$?@Eih?_}Fu*m}z9iux|sN#QquZe7@9z=OwY){{lR>uZLcBgguwe%>~akLph8b zvX|InwV&DGDc=To@`Vt4g6C;YLV$@l&^j&4JvmVE__C8E{JJiW}lzcWH3qNZIf zjr#~4_8`-&8>|o7(G2k(*XJ8ou?0-CwaXNVew0{_vdo|F7%PMMuKYIhy|Zs-z6&0+ z^WBRz_}|R;BQ0Mu-&fDcneWE^&GS&Fzd zPuY7L@9UgmDDt0ZPy{bwPUD^QJMmXz3X@`O%J2Ryo&Pnse@5|)BIw$`L%3p27GKoh<_L1aPXZK3w&ky82qxY+$d3uI@iTc24 zlHA4bcWb}(UDk|T3$cV4n8F{jx*bN=K8)3%=IY6;>XQ>sUrdG$L4T9 zErb23bsI;0yaKDYH9(~~qxD8Kn~2fH84MgfAcKHB5F52(dtxgO?tVKyVb#>xgzymB zmsqkH>}CP&akwI>yM=Eow6nF^PU9(s;x37UnKA7;Idg8<4{(OGmbmz?t92D$pzUi| z<21-f-u4fPfG~o_HMx@5&kWyEQ6q`n^d40Ca-ipvs8?0SkhAvlT#1QDNDOqiILpJrT8!4vc zYI@ipjLjmyPVrcz)luwvWWGYbbTr`)_u`Ksz8lQ0Ad5{~YHEddG0HuJynS-P*x>EG zT%Nelv}X4p-Vr}wbNs&0B0ln3*KDh&O(>OV(V7Qxw$%fg*;e1G?#D;|z-BznX8cob z#ZFSn6ABcj_KM_xw)<*mwBf- z4b?u|fa;TUmdBgZiM-#L*ZP~*a?%nqI81Ekk5$$A*|w43ZTyI`+7&C&7Z64Xby5`U zI7P&*-hGyJb2zcq?7aV`XgaS!A%uxP`}HyHrjf`aAok5_ZY37G>d|$6j*c{j7t)Zw zxLv<$Txh8a=kulY*vud(Em=FS_Cz;k6`aYE!p$%b98doJ%$$X$YQ_Odhbvi+EtgfzC{}v+y=Oqp$7>a)BJj(T~tX|wQ!}Q{- zny>GL8~?Z3cH@tjfyZyrldRtGEvU>R=1X4fnQqLqkC4*xsq;(I*GbNd>ZaCl>7LW! z+i4}Y!$`fpdeDuVQq@DZ8%VVN#*P1`sa>0YeU~<4B**I3#LmLgr76PktoM~{Zgq8U zYim_@i-S6B5qrsaL2;Oo=Ri-u^1Bg^{-!A$ty`l3xzU=9ZoIC^+xQiYq#n%pvEH)( z$?}wk?Hv2JVVa-lQ&zvvyR**1Ad+xh7i3`+Q4UxBklC;}-0iI_CMU}jX&6b&2uI2@ z5w$GJp&wF@wEh%1byQ?0lJbqDfb3UitHbXgP3rI`=sxQ3eG;PnzB+uIHg6N$SV2#v z8g;l{L}vJ%l@Ke3OiCSoo)YPTk5be$5+7w-U%`!*%^3M;q=m zK1EVS5&oKX@@Yl*2ioPP72&&437FAnx5UOt)PjlAv}7s5xu9KE&zDg2Y{T+@svXzm z68{G9XH0PjGb&L~c4URY*<*@Ym%`3gY!!5+8}I%XxQ$+=*Y*6#{L0|ByNoQ16Xd4Z zqemsqAh{~qmiP(3(g6RCUo-b!c(~C3k7Gv^(``^TU=!5-Wa%WQw?9~yWIUg)O1G%r z^x+)oMT|=EMM@`LN?gLO1SCn(nxn)yYBq-~E_rxzKG?H|n1d>HO+WCsduOcg@ydzCGr zBuX4%O3X7m5?}56kyH$r8+zo(Rr{_^t9|R(VHoU_`jLEqtDTf?)V>TFNEp3jk+;v6 zs&{X|pmC~y-k@W7*7CO1$9Qnhy1uMc!l6?G~aRAP{vc69_~YXxXTH zRYBTTprf4t-ru@tV(e7;pub=jtMxIVqgaG0^tIs!LhzuT0*|lm6?jTS1=H%@y#vqw z1*K{59Cn`t&nSaf51WPWehcv2Sot;J`NOna@D!LC|2E)Z4etq_>Yae+4+hk^;Q8{E zGOU3dXkY znT#KdHW^RK&3GFbjVK<;o=m2$T z=1&hP0zJ8hU%$Qi1ie$KI&q6a@nzB*;=4;3*@>&umnS@3v3*s7jNSuk1Zv03jTa( z$`DcNoKWnfFwe;bV_l_o`4K7M*kQBC60#3a^LS-mO~qFH+XIsZ%mP<;qR|*8n=$jw z{ElYQi=0NI&mUe;y=Y)wZO@roNYshD;5fXXoJ6PbNNU1UwrQg9$q$M$K6p1uIn=Y-ya#s(`Z=7xMtx;Im)o;ly_(L$)%_S^c|rz|?S^v)kk@dW#hIRQxA_6;J6a)CQdxopwR+ahx8G=SeHJRui?g8Yg>gS5u*} zjtl;qeP!Mg`UAc_?5|X$9w4-0b!F#^e(}>wSLan-c>YDAy%jw^PcBkkJt=SG8^YnJ zH3$ty?=fiJ5k1lOJrSRU8-yg#3%HxXMAWpMeu!>jC$k^VsMtjDC|U*2&i`p^l{=JBst z24&yV{WNw;IgXI0)X%^(!ZRg<5_bpD&SfUhkN)@H`=3iBn07>CiCzt%;u{}5_d`#a=GnsxI>=T=LU7%tHDz? zbfon*>W!{Umef52t?^nPri;<#$&yKmZft${YdG>LeAd8cy_!C9%i%AR0TqV}digO{ zFzTQbf4M{(|5!dQ8>AIBNGl8~#cuLlZx>jG zHohT1FE|$I{PY&%f>`QtqHkjwAZ@wiC88_+sFn`Vd;RD(21In0AJvd+Vkh`-lKyIe zKh6*7SoWKw1-B_bMx=&J6%|bPqfNH9ANWy2rHTqp^rQ3b*8~0NjYLximRiKvO07lL z;&SC;N~vhOshqb)6>d`5V*3VZy~6AeRDldz=9}L?SfW@wLFPWLQ|w|@S7}~Xl$gl8 zV7MycRBmDfibzE$!?q}|HmI8+lR12?J5Ql0%y-p;h1Uk2?%Ron*(*c8q z2!l$6K}E(Hjb%DQD22-PVWI|sRLK>H#Arjp5I7L?ylrN?yggB`-4n~pYM{ub2Dh1p zQVtSQSlT)S7%plo&GQZ&2L02yx7l#l{DTem7b3ob+{udRp|2Xf!pe0UU3MI&hvp17 zfw|~m0D3h=y{gpw8Tnr@&>Hu;6mZqA-bgP6pHP__8)7J4Ct1?6PP|+4wf(KFfi*1q zr&xoPEwZfXLoxUUY+NTQtKWh(c=RXh{o0-5MDXaY*X;;KvM-Hg^|IV>KVxMtdwWC7J26$Pf`2U2lUJ zN|v0?RKP~Ae}^=7d)(zA@&OTen^50ryo-pn2#9{fIdM4pCcYb!C68+kSsF)4L&(7< zxu`(493q!^A#xg;loVMpIft5GT{gsf1#>o*^AHxp-^nAi9wx~T@ejKvtpU)Vy_GX4 zzsxDu|B5hyIw$Mk0I&5r*PMk>SG|!6-{pX~g~_77*dpq*_z>Z5*zo_7*6FZFfe6IE ze9GI&)_vidA1blGO`J=j_rfgu8Zw7~qyCS=$EYwn#F58>Ad`rE4aY1YvvBaN=TrOB zv~7wcrI4Pp7PBnb&oaxp(J;y)MJ%Iip$}d^a2JZ9aG;iV{jBCrf^CLTsy2a^!+(P* z5P%)tN87Yucenmu7(BF!q2o@x%<|8AYP0+^w4dR_N?;DI!mj*(I`)%A=YUT7|8y{_t9L0HVF(TO zK*v?A=#bE;5*I3?edA@#SjDL?iNjQVtl+r6??JqZyp4EMrq03CS@6Jrtn&csZ2h}v z6Id-;4{CF9ykqe^@sNn7B?d*_6G<+O9_>B9AX@!sJ}g%-@e-4N(k(vVmB z3&D6oq1V?0AcFS(i`Uh}D@}=Q>fZKCK^db^ja9Pav)hnZfz$a=(X+-sn*J2Db<(;j zj1`=auXK4{*i31s@h$tkAIafZDT6!vK8tgOOE}WY^A6uvh&Q;*@A3W&!3-}91!Y+G zJh(ZzSQ$Ai(7xwJm3fOYetzRN$by5tI=$0M)~|#bTHAnVwFF@}&wG5IbRoS8;x{rw zjr@yDi+hhP|~G*XkwZ_0;7RXBwN7$!|y0Lu zdLnPS_fTf+@k~xz{UX2mN?ZNE?%kZUGZW8Le@CW}8N--=Wyav0m8pDTCZDZ*fnT|6 zD?j&1`p&6*o;M^@`HEDburNAaGKx(8JM`PVwoJZFseGwfkQ^t#jI)&o(v@#0N(1JD zR3T~LsC?VnbmhCfJ2I8a>9W79Y|rQWl~1sh-$Oaub53zV#(NCS_o3fARX7~`xf;1O z)5yEt@id|rtNKxqpMO9qzbho?+eF*Q$>~OlGb1=5mDyhrRhh~^^j^;7`@Zs})_NWl z?y=FDN)Dg4+uauozu{7oh)XN61c|EX*kDZ$=RUU#t=fZIh>u!ZG@3( za=Wps(KQ*RofT53Ww=SV!g;$-NAB6cp(e>lg_)gVf0JnR5t#wl8_@Y>+S`X>2fGpO zEjOAOK}pu8S;XdRoB8!=AYI;U;+ApiDS;N^Ve+przt+h^7{e_%^H@;^%YaibAI#*O||3uZU+^7|xOz(a#eJ5wlVP62tf+FKj=LT9XnpqL}ZruCM{$1I_JBe(BPHFA8P zbR)~ZG>v>#@`W04GL2vjNF(~IMlQ%S@^@I5ZR80~cmFdm&OYD3^V)I)&qzTEp2fcf z7?)7lf39L*TgAz?iV5i|p1qMO%J-^@mY4RngT}@jY9`{pl)}-9Q!n_o|8&KQ*w}_8kL@ zndvHKsEU`tuKx^+nYM}tY!zpxs~Djwu(R2VDo(OhTx_d2GF`>a>#5?*y{h7iiDn>& z+bZ(XRV-8$MSE4nB3s4VhZ$JB&^a|DQ&q(?$Ta^M7In6YyKEJIPggNSRm|C|Dvq&L zoNKF?k*?x{I8~goS5;YoQlEV~SVuA-K_++X? zPsN@yFu8z}Xr*+98>nGFo_ zF0_GR-j8jd(mPXu$Z~eBo4iv9BX%UNx4p578}E!+ebJD)(fqR3i_PB&=5NUSJ<V0Gb<=(qCFvNS^28Mah+d!rFlmdP~o*;}SHf2pL31eLk>Fe#oJ?@B4I-7`c2jxkeldp_s?uxt-+i|;gyip>UltS+aQd-~gZkR76g3_)otMo4X zh-~ZVoi{Bfy+-LTEB(Bj^y8HNtkO@(Nk2&GuPOcLob-Zp{e5%NKlqURuPcA@Pucx> zLFwz2zBMQP-%5W&>Cfe)|3&E=l>TT=dOV%}r=0XUrLR%`Svl!HQTp>r|7A{kwbIur z{hXZiqm*v?b8=34iPBAf4$n#d>I2d})#K!(zoT>mhaG>+hQl*T4@11YO*!e0C_Sk3 z)j8>RD*bGwKj^3XJ1$w%$g54AW7OEcxD_oC?z}YrOU+i#+vhVaR0OtK`%F&vtX1)en=nw~7V+AlCn1r9d_8p`TfF1CzD ztOaUPC}y0kDT4mjFF8adKlnubvD(tyl7=*-N-Bch-7i_LlK=8cTB0EXr|Ev1CWVsF zqNQe+n=E;D9`G>aIkDScyNO=XKmz?laVju46&R8Vj86q7qyks_f!2tbKWzQ&{FSOt zhxmQEo$lt2#-yZ1qe!ykMEc~$MwEHK`ZvnNaeuI8N7nv|g9vo(oLs+{Ecw0iaW)W? z+A_L2S+Y)GAYsH%&apWz&~+vq6&M4rXy-cAdYkM?!MVx9p1+yQ=GXuetoX=-E-B6P zW*@{MHe_mu_s1^&*+Ctr1>bOP;DX1}I+v#moQ=1TQk6kBj28=YrjbCc2uQbjKkwf+ zua#?Z9h#+$qDNA9Ub|E(ecd&c4Hy?-aVpQCIsJY%S@IWk8`WdOT!B*N0#wXd^Fcl^ z%Jw)Y;aGnSyYkipe5umT|G6{ZiZz_e31 zfhaz`R1KAeqNm^vDRHJjI>gAtjfjEf4(n}j3r3Xq9-r()grmb+Pa(LM)qbr_Wg~59 z{#Gtd&7Z+uK+P!>rY8texr+ZEGYIKKP#wt;@|~m8j*#D+jQ#<9`8A_;V?SaFI_t&o z83$;gv4&-5gz~OfS{E)#85>F}XPh^HXtB<%FUdra)ichG>c|qzoW&RKM7kD^E+|#Q zlCFQvQi>HU-Dc=OA#@{ORq=mfVlfs@luuz@ZqNS=@Vt?xmgP0EE77|lcygqf#4m0X z*E4jgP&IkK`=su_caEf zcKy$MzsWQtAhw6#0h-_Var=(nj6%gKB9|1BThs6?HzMqj$-PQJiDz&$UefzvtK1ov zRx;nreF!ZC|7u&qLN&-LLASTtyYjsh23>G;67eM4)A;l3hvJrY@XTab%^#D^AimG=i)O`%VA6 zL=*6RoT&cQx85v%Sx->50jx0$7(~*HF`7Mz*VW=oC5ht!pb;b1#91J+c>{FGk|l~A zCEyyYx|t|gW!>w@8cSk`J!0>wqcEm%Hd)LOyM;@xHOM6@&tB>O7N(}&SkR{CR~_yc z&rUSf0>`~MPUot)yflX_s_A32Va89;bs8*}fL)Y#%S<+=&76aY!Yz@1pWL(Zhbdsx z@U_2CISA?K!tM3{M{2#(@E3k!on@u_lBrkMRihgSLWMx`lPw*5P2Zx2n7OMboSM5T zovY_%TLETGoF)UTNL@zlKo|P^)6GVD75YMSp-;SVh_4Gh>o-`vYpS%?Se37gQTe{ayr~JH z{K<*FL_`=_c+q~XAww>8U~A?-%L{LelC7OBH3c(D{~zk!Jie;x>ig z4wX1^j<+I$3Zim<@6X!j-rPVypW*w*FE8buVV}L%UVE*z*IsMwwO!Q>WmfHldf7)W z;i$^socBH#jRn7Z2+iiQ|Hu!hw%iiY@m0&gYv(kvuWeo$+Uq9q{Csm+n7>Lh-kW%w zvooI8EM|i&E|}mPj#D3#Xb@TaL2GsReL93N6*s9ypMo29U}K{uN24F{{<#CcqTu=H zHcj7BP64%w6dLR}ASVy{@O(2e6OWG7sYwz#9cEFs<{|=qVAP{t=IrH`D0!Jcfq9uF zT`qe~rSo{$K9wFRCO28;%5^s1I%qoXqy8{q6KX8+Ht=w0=m_RivxdCd#w_C^X>nY% zL=Pym6=s6HXw%&6lBpmdd+f!A~6Xr?An zo<2jD-v@~FFiLhNf{JJ%j|w|>K4@{1^S}q%MUc}j;jGT47TMe7_Qs#HKDdMdAiRNO z4ka89hA=LZFlJiLP0bBM(ZnQ|msX0(3YJv%MiNw46p7_a5%qnDzuSY^yCUK5Zxf`i z36f-Jq+MBzm@Uba?hlL%T>9Z?f5GuUwxF`R76gTQ#VmQ+W0h3y^>5@~VuGuSTeBA; zC417Ay;$Qex*0qqmagM;>PBj7Sc)z@?<^I!Fc(7p-F4Y1i(HVG+6?;cm(QD~Ph|D+ z&M}sPtCB^daWoNr`(qTE{pE9FOL}tXjUqje$%MK-IQydw8Oc)%ppHw4uhrSgQeLipkTX2D)jSA!PPEnn6E`9vp#$T^%!7 zy{lI)S>5z$R#xA0xg;zFmQO~J8BwCe)TrLqqk1j)TYKE(HALiUYH8`O1|vBv$!h|0 z6ve|LcNfg*LJ-sjF%g1^O&mj;ufh?nP6VG^qGblNu@IC-@v`8g%An|9@E3FBC|V5q z{j7Zy^W|$RH8}aoYJwHTOhO>1v!a-kT~Pq{#5i#Gpq84%oN~$=bmJMyWKrTHi)L%y z7m|-94`pF75q6O53{F&fDrQ!d69GOgFIQP*{snXVNyXdL5rmVEJ+v)UKVRK|6 zg35ogg_n8*&0C_8Zv{77V{%h=KPob8wui(0-dfCCxcrU>*@a6h737Ir1Np)A8x2|UAsLN#|xRjKI;CzdGI4H-PL89O4Q1+fWpd1;zd zuzhz%a=Auwo8t3sJ^=T(oC5uP-A7hrvkMJuhA1}L*U{Do6aE9E<_9_xZTloK7N75( zAUmfaBui7mz51(}t?l5Q;WlnuX9uyGpbM223@ZRxI4}u0#CDXVziqN-uYhuDFse5QyOrDQBrz;KO``w%t^U$S*fe1_w#~w} z-o9=d6U72vntb?6`$Q4R<9vO!a}jvsS~+Qpj(Jt$?y8C&(zc78-kvNP%Tr6o)BLa- z<3sg8QZaa7%4#}4SR%60<{PNctzER|OA87#%c56b5omcezQO}n;|s3Fr}V&ToN6^9 zFl-g`Wl)Eqdb1_VpZLaU!pGZ^r6zf0yN>&xYBzEC7CLUx4KxwfNjjzem-_C#)h0t< z?`bkLvpy?B&%VUT(4uc%rm4ee3Y26@_npO^0!xEe-*M{csU{<}9AQaifx_x;WzcN} zR`>E?mxQdll8@-59Ty~THi9Qar)2H0rA(!Ij0b7t2~=Y^N;J zI1%HqKKSW6CJ{DqHH;DWY&EntXCia^wX`zqtIb-d5Yn;CK$iZs7W}cE7>JgCZ+hF6 zAXEtB@14@y!<*CF-8p(Y>eP<()&)#%NpIDvHlN;KQ_?TDj{-lp|1dOK}nmfpU&D5AIX)l`n&9=Rqu+{vq(2^L25o_~mutF?($%SMZ#PZs?_Pf6%ie4Rq9iulFZqt#DXW=CU~rL@RowmH*`UE?&b zdZyQHFip<1XYuIT!soW~S!6cUCCK2IotakCuNYK@&p2ycZSbP^;Gyg34Bho zBEo0I$(`YIdkLaEeC~!dh;`0{9W##hu9JaKMQda8nOf>i^k|F@HZUEU>&I&<`9Xl9j@k@ao}_dm6Xkh zdfe0?Iy?jX0j!#`RY+>ohNsnl=AwNktYA=Sv#ANFN7 zQkm3;849=NpXdo&F|CA*SCJVDgI3Or6;21O-Xjay71}ULTYa=in!U9>{Nd}p+r|_w z=}Mr1FlpATtm=^E}}BNwecmVOe6 z?3yk5VrG9i0O&=fdci-sC1`wVKK;!tOy8pcoe})1D=JEFs&^AllH2r>+<3O{ABoJc z%oO!Qs+No^1g@%kKN+OVI}_3XHuf*Pgl{$i<{sKQhS}gqDpV2kRRseFprec7B#dJ3p0qJgWP=Uc6gHe?gw-(FPmL+ZO~bZM3DJf1-K*q2WmDI>pSt1Oll7_1u6P@- zHEt96if>6ZfD@M53hMhnpDd&=Ef3`1sY$GHQz~P+ct0b#tB=c)+*?12c0L7tzW~xE zNB@^GVc0I=&8q8T)nPyR9qam$*X$TgbBT5GyBtFOcQ>$JKZ&>5{h8Nbs+IS5gtIYF zD6!I@$?UU&vbO&3D!MFEhnEAt9rvAB9~JsFg(B#q55uyxJMkU)f>qS*^=zONkO%+x zZN72?1GURPoVcbYZ0W(7^r17e)rrZI2(;SdzBP$9fBgz?mIg@k5dN|CL4Q+UWUpDH z$1GhLE1g(|?Nr915?Hnu&l*;LNyFKyAk+(6|7_XLo0qElA2e*2CP-mXJ0kAo zx%7iBztK?U@;yg3`8%XFB)*Q#r>FXBL@`Ov)}HFPHuL-QCe+3zo_4~IYWzf8Yx*Ve zN&JhCk_}tE{dsjKS63Z7F*YW>$BLCFFVc4f^;h_r)&}Idt0u`2Wz_a~L81FY zv-Afo@p~@u2R-ZeeAXYZ)?fd!Kd&+RV|5mP%DZjh!gss*seXg}Sx*@-9DmaEn}>8y^&os0vR&-FKJktKEN;3L3?El#O1iO!!#SL?*;!P$F}M)R*qaqSeeZ7 zSku*xTbSO)UlNJNBe_U(Xm{;0k3Dr{s=ypplh~VSKzPn4KW%_*ub_Ljf^RQ&gLT5M zBfi_(c;)cSlzkNTVg3}w;U|8Y~}Fx|9rf5-`05T@5XDlhQ0I0 zYy5WDCzEG0UgHX5!xuN4YQyE}HX5zem&lr5_cWW`~$o_s+JuD;MI&OMbZ}KQ{fFpevdnIe5!eJ;j-?_=zWG!1P}8 z@2HjUGNsR4Lt4-_yJ|!)abUzi)j!%}b}(dDrfb>HH+(H5RXl zHGSh<{scm%zFw91g#3Fu!R@NqQf{;*giqCu0AlQI$y(*|5YfB+SK?Pi%}O zUaKj5FAI>>sE*SE1(^tECKYwcBdqvef z*UsA5NLjkR^zWj>DuiS5gn9m;wSM9=fA}lj4M(8hlU%!DR&`=+<9nkUpKTnQSW~qy z=w3BzQvu7sOV6xoT-I9M(yh2LvAdXk)e(;DwO0b7%bb>rp5U7OzPvGBbesus3U=A+SLdoHQ!`Fi@Isz%B; zE^Dk#tY7$PZ(2so7hGOZ+4y2xWy{Fo>Xz!_LYnUqzAg4h#kwqfuY1ppbS8ahW#g*R zlxwV7xO#`G#uwHIi%#YXSM^*zR{Mt|jJ?SmQB(LVgVIQ& ze?Vtw*te!I%^o(8<&T&=$7>p|tK`__jU&riz0_##!ZSb_#f4b!I+juyZdUIJlfR`k zzPzuT9&pq$X8McS)>A$Fp%bO&~ zMP{gY>gWTr?^QGJcXsdV|2c`5z1++C7YFmEd-e@Ed5-RyeS@RrxWeokZjROs4IcU; zaSJq2oveScTXo{IS)UcGC@7eG!c-JL)68;Abd2#Ir5;AL2`-x%qZ( zVbv^#y)WfH(f5xu)eOI2xz}98r(?Y*ug;8(XJ&2}%lsof#qH9h@J)2bfOVbgn~dv0VhZhCrC)4ToC9v9y>z2`waP4ABG`O?C7 zp+ac4=Zo2voA01yok4XGbk4&@ZZF-pDmDI%rVYcr8%`EOJ~m7JDroHDdw*DX$>6Zs zSfY{+Xa3A{?|Q|GRVR>*p9f`^?})Pd5i?GU63y(QS{JB~tmvc8Hp`P2HRUy_L0~$q zs%hzhs;11YHOUKRd)K9Sn(n4+lSYHqhJT2osQ$OjzStF{Ltv*r??TCVKc^lp!Z@9qHAIBNN4KEa$vQ(&W;h~``{L+B#7fwFpjRh4*= z6WIs4u)?NAT^GLFyXUi2iGQl-Xo`yZ^ZJt|w;ymEKvSzDxH93oBf%XMcIBbhzn$LJ zske^a=G1qfw>Ko5HmA2&^e`gre)+WKHT@P=P4CT1jlXyO&D>HkL4m=9%Ve#7$Ht$h zFlTr%zo=GBjtzGx-SGa~QAD$3vX%*~;SM{p;Rl3Ng?I7K;|wCwOU2g(g;tbeiFd*y zH>>8uo*k>nQpI&28#J9yQrqWGQt6t`=aU_`m`~DsefRX?7p`Mgr$?v(gH~{oh#2oO zT=HXEs}ma+e%!r^*{AW@)|P(EQ_MRWe)a26#Asfp-wAqK$7+|Q7hC@=nVBr_|11`2 zC(mWlpTlb5>H+24j=hCbOqPR2sS|T)wI(_Cmf)AuWC4gyAHRB@mM5Hbk|`Z9%_)9Y z+?=WQZwW+nCT~rhUh64DIYh^KoU&$(2$MV z&B~J0DHwiKWa>V|lGwUf zUDnbxVe83bOOSt8#xmndrjhtgTX_l!Ye;rc##QjDj*Dj3{2ZPtFP^DmR>gg8!#3|) z?f&j4Q^TEZBUGyBxKCN#;g)aXHAD07dlhHva=z4zxPTm+n`Ag3)NqF3gQTuX*{aIW zsu$}Ww)7d?+Ql%tNV)Cda7<0&UCzBc%Y{ZvkdVvV}i+OHMZN~+we@z0->mXexK2*wtKus_>+7OYYHy`xrxzZ5srh&zt zL%;1@55f>4qS4VXX5?{6BcWy%8l~s`wHNhK9*gr0EmRsy-0XgCxP?2R&NC7t{s^(qtW#+;mRz73Ym<+vMll$k_<~r< znj-*PKBxFuV2Mm=Ui@mIi&4eqrUV6WeSz0}2?%yOgS>>!qVr+(Hj< zmE?1Hr7B5{I`MBnjrD6-aNCh&AXbvC_Ah19$)WvQRhsRTVmg8yuoZ3r$1)Y0wOE29 zf4w7q4eo!{B4FUpxPmF8epYTEn6;RcmP9gl;^>N zVmj*pu#G5M)#}BA*9YzmM`{>b)E2dhD<_u2K`HsviW7 zh-Uokfarz{Y0Yk{tJqVMSh>+gcr2AyCD;}nqG#vSpmy2RT^ z3)KpxLMnJIAL*XXAmGaF6ph!c07l_VX|+Ch+U~PNyP1SAp~=Zp(!nfH%560C;&agQ zjj0M$$FHkODWdr7*XNHe)J1~6=y2EQ! zm4~_|+}g}25t_s*AKXcR77hkKdCWi)R1Eq+&UaNMW&9G`qRQAAB9U~^Da9O9VxSnG zMRemp7!SWrfOZ@JHHm$L0*ZG|H{ssd-t|-O_Tw&7+EbSsggORyM$qM>oeILqQDZZL zbsutjIvEzzRiWbl(+8(dh)#hd7AG2+&qd;HWT0RK;rmAOnC(OD0Kn-HzT|%0Y}hfy zV9Tl42MK{+DxjGcH3itFI|h1iZgb?|OzCM9Y$u4BXXOz?!%0F_d3vm>F%~$wC!znv z!GyEy)XC17VS6f!n8C%l!6rDc82!&r$-$JRC(Rq%8#DE2=*~P(2?679kONr#ce^>u zGNpf|jNCh`oAXpr^KoaxEN(Hhd}(o06A3p+5FcHG7K)xCo@Z~6Vk~Abr*)yO;9)AN zNm>Tc4oTyGLq`;DR$w}B7Dcf%25&zLMWBb*E6ry%WYZ-opP3ENnyvsXvsI=a zR&+)qmH3G@7XEiM9K6a;K)5Acyb`6fvAlPJk}RtTcg-3nvLBCaII%>Dcx1YtI1*6_ zM8|Spdd4@ByGw(o{w+1!Hpsd5;UgV_jvw96%g=2-EM(xU#f{W`3L6HXPVr)SP@=wk zgdJIqrh$>8X>jn>T%sz?^Oc{}{{3epA65ptHQ`BIdwMoPxWoEF53-tH zV9A%Cb*iMd@PSj(h3McQ)Cei7NP@0j$uS@Yf>3-a%mI-*Hc9nqeP0-mwwLN7--NC! zeKaC3ejs{KP0FeHas$@Yq+Et7b#WN__!pp9`u`lY%%61gI=i4WQLf~9|BQ2ACpmq* zTBS2!)la%QP5buZAL%)BK%F-J5P8y78tNUYj^u2!`9-eF;*;2@6rUx*WzvsOQYT4< zLpTUi;HhI}qXnQ-Qr%}fWsH~n!6~Vewyva7OP`DGf)yhr74DTjCybH>v?k@kilv{v zaj?@*qrD-sZl%dcMU5qYmsL@xZmFVPM_m?E|7%^)T~toFhMf{g4pD80cLK&syk%u_ zqomPjzJVz(bRXM`4jSm~`Z$8vh%&fEO6;t~qN03d(4}MN6bq(OI0$ple?QAWvvkZ(&Z?Ncq9R1EjH_ zK!iI;EpW<&7dK;!B;~UwQnUF>5ho-v*W~n&(bd-UnyC}r{KU{9?Xr4Jo%EW0B~0cS z3UjZb@j@R}1HmO-rFSSatHVTrl#AppyFm^Erp_h)?)D}-3SP@?***~&)xveAbmIy1 z=W4tBRF_j#LMVJ!cct8=_l%A+8IeAX<)Q=Ytu8j*v`u!0m3#+)w^LUHpe7#E&)DP% zIWOkf4`(5LY53{2k_?I&;K$c5%OqtkdzVL|;a zHMV>8vXa>F!|MCX_OPVe;HzbK@HK+^Yh-B~1wo{`9mZ7F^Y6tx_PsGHV5pfVaIk?- zmh8PE0l6|bfpj(U{f=guiPY`Ew-qT(O7z1 zAkx!=cPto^Nf+hpNjH4CgYAdyAfHV_CGXYZ)KC7(I6wYY!{{MzDQ|jQn=bsNx$eQd{G5leLJ0FZTHR@vOs|A2fk?W$~9cXrElU z7f)H6MGsHR*AF=fW9BZzhLL!tp|77D^`E@WlwM2?ig2q9{P_nONTmZv18^CUcpg;tAeLCucsjtvZ z3LUhP48B@nLGKbKBM$iFN9xT-5WnRU_?AckVNbD^$*bFYr*F`H{|Qv>+VAGQdyKc_ zf2Br!>yH>!#{=uucrL74@9V0=i$UTvc%er)O#KnosM7SBcY;IP3PGgX_wU93tRV+Am6B8_9#w2@ z(Nqi?2YY>EXx+F(6DNbMLc^9abdvx)2k%EK3sKilG??WCwC$=?eWWx;Mlj}F?QnCWMevPgrlxmJcD9zBFN>^*CT<>BBVe zC$*{zj4oYa+VWbe@+unYD%ygYGyANqvoIg8`4lPvJHaoh6xfU}RCyiGhqTg@l2~Fg zCmW3gab_rZm(>D*+7U9Zam~k>(mzmLxRg7_53iP;$(;vUcwhS~QG2$bjtzv@Q_9%_ z>oNUxWABvNK-_wQ+`3 zWn@I+6a}e1FyYI3$Kt_@KT2O?aE-<2KZ?NLS3FN4Gfp(z!V@3 zswK(-HSo(&>uwsmbsLSvbB&$p8oMgnSj4&C5FF|_w|QJ{q~kl$7DOP@Cl;7KLH~}L z-~s)Gx2x?(sw?_R9Gs0XvK~Ffs|6+eL%hea^hz(a!5BsOHy2B}fg>x62T{OC4BmxP zv-8JsQaQ>sFD9h>R1I8OH5elmmwm&1+ncMex27%ys;H@XwNX=U&vTzswrENnDYn41 zSfW-ep=QDs?W`O-uxja8Yll|_VSkWdG$uf498L=5a1z}|w`R3WjoR+lw2DU6$m8JY zL6|cS2Ww1zee+-3rx!YxX~|ud6Dv6OkTlXj=!=%{`Zk)*yGv1F%YLge@ADNPMnq-2*LaoQf^Ke4s zG#PJBlXAY-dTc)X8r62uWR9_N%ygOoBRfUY%ozpsURC4#iVIcTYhJ^v;8M2HLxykP z27=(B!LihM=q<{g`vm3m*J#rE3MmIbJ4tP%y8TOUY6=76Wgf*+lZ*MfPWRTK6uhmX z+|RI2My);?o4{T5ka4Q9oUiq2x?X{k6}nH_Bm0WQ-ZCEPuQst7ABSeCBni7IpDEp) z0W~)I0#+a=U$R$bp%*X03#Fyp9jcSM2V3386n{DEFI0mFF*#B#_t>)(jnY39X}-A6 zIYnr-j*s+*z!4qmHdET|n5exY(5vv0D08yjsI_7C+iXd*RFTiUtGbD*V^BgPPh!gd z&99szYgB!z=!V<1Ty)9Uh1grHqR5Wkbgj=A9cc>L{D@*mi~e!&((7x`YL@s5gWLw#R^{41`!T^WgXGOgDaX=o6 zNVpbk*myG?))97RJTSw72W8`FTFi}knzUTkXQCc)iav`$Hg->7Glw`zk4gMZ1IYX7 zD%UiOBYPgQCmq)Q8;vA$ZY#~sUUPv^YRXpPXM7^!UQVa5sEq)2s^dtkSnAZLiRYLh zjRqMadC2XqVu$R&4nNTBJzo4rdKzIQxR{Ny0)M8!0=zap6!>-z_-u>mU|^UpU}h~| zM)ADWiU>L@58P%J(NyrkW9GT3L|N_1MC3=?I3oij?`iB!v(oOuJ;HS&Zag<&@> zN1T>9M6;mOVOH+@aGfcw9EXazE0>Wcr_7ZwPfl;H&B?Hv3T z0D19~7>a4$Wv#$}Q&XF@QG4&XNlbI^=I>Wxe?B z6n0VqvN4#F(Hy(>s@}PgvOx%zAGIyKhu5R$CwhgS<2X_J8#E;Bd{A^Hoe3}GR^zUr z4aSdg#|^voPQ`O^_V2YbT?~^o=nu@ zPt!ASjooO|7(O`B?u(A*vCQ%;X=KduDQr) z9WJ$B(Y(AiP?&RFLe%p4;gP)zZJV>IL1m**IkIY7V`99ZEo=r+aL^+;rX8vI?Z3se z$vdsZ8iY;@E!QAfi~&=QfxVK-VsDUxmW^Ph%eX$Z58LkI%JyON;=PPb4W)lM3Y2~= zP89O8=0WvQ=0Ui7yd;2-(A`d-#ke1gMkN*kZNkb*0oX*{&Jo+2mF>h9eEU1m8Hz|p zqhJSO8vrEj#O8twB0{SlYor$bQPj*}83H<$Kn~%nSFg>4m#C9kgDGTM zpZ=@4k^W1s*79mwq#S;rZ$%rRV_Y%+;L9sH5nIe{6@LOzbo-_XB{Sfnx#D^*yg?_H zBTgFd725bg~0vE+&6gaFa;m5Q9FFVX{R=u6oab3i!$hdr4Hm4fa>D9>oy zY1zRA$M)i*vnu2+&|I*0me%~pzS~HxqGS!AWb}k`fIGH~f5t(`m~l_V$$9ya+Ze~> z-)E{)AZ)X`nsghEFjQPsF*?|F*UvKH&p9fN{702waX4>c$wx(_{)mTc?((jG37>2& zRW!E4cbijWqV+#+{x(|OWiM|!4aApv%NvJ1f^jRTNN!eYzmN&pmuEFQIp}|b)qen7c z+BqY&8jvGX{qcD+qG4RbEJ@0yho^Vdvh(2dj_K`mq5Z9Ca*-}c?~uNXAHiTV%!ecM zn7;VV?`Hb_sXh;c{qsTSAAwLT5O%O;AcmlCdTl11(k-AfAJ21i>JDWhv$fr#XO%iV zYkO6hFqiOxQEpY7nTBuh^|4vsVEca9JA^Jvkzrf@e}Ji-MOVY_;?)aD3X3#)D1>sn@HBoH)B9vd? z(Wlv&d4JB|U8TKnwsG<_vL;KimBJ|+kQ|lW)PAX6$L@kk>dTa-jsR?}C5KvpCFDS?NwC|O zMrAfeNwG#JsdAo8o7P}L5okIMl&8w#S3m*L;8c-ho!h-Lzl<+ryRVaV`mKZc z9M91(0l%Ej%;u!u5qDaffQ*sYA z?_ib^bc^5xMt!ArX7kJ1O~b(YVLGLyKIy4K@Kjn~&6RG?04Z+(H19P(zr70HU7@3K zlmSfQ&4%)N;TMI>2EUzd%E~c!@;`_@ba;f=W4V`%N^n4^YbuM>I|Llh1cs0rYswVX zAA`Z6Th$Y~f1*SWlOy*hFVKjnw$VGOLyNRLHK?2W@!*)m?yK1uCKHV6@|&K<)%R%w z8TTSgfC0la_tq%)D}-_G?E~)HqI*~FrszGvaC**rSwv+d8D_t?RaEL1#<6>*W4q6y zXVrdEx+C6zL=eu+6|MBf=Z1yQ-8S{NkgRlxNI^(BH`3W43zx7k1(jF_mGt}j`LB^EHH>3ct^JjF&n zgN1ugRHxjZw+2Cq>UQrT6PA7}$>Wy{(nn@1%^2({En6 z3&mBt{u)Ksw{W4#X5>{oGq2*t=(*&!ctO*LctMg_QbpAgYew8{ zfP2m7Mck|=j!leQQ=6KG!{P1a|hrt7In zCW}5f1RM?@SM0_AAXxNi&~w^4WoA@#)dr54R8gY+IQW^CwWYe}Xz3_uyk-~sk3*xe zyN0rn$}NcX84ODR*TUeWGuJuWnuR{euGV}y70Y3t_O=Q(oCa!-8BN1O+tSIm7j zZZtcakak!}tZ%ZQysf1#Y5 zvGxD58nShIjR*6g*R((XW=fwQ%#v<^s;0>mG$}<#w69rL{NNAz+FzLNCbg>RYb*wb zOQQgIv06dBol_$2%amS6O;Sm`+gjP@4si~kq~Vp?np+#XSIy^wHTWuhzCqVgiHvG# zOOY-Owb~nBu3RSz+q%UH7gN#7`VxN-Z7*7b{*6D2R`6m|!yN*k!5lamA^iy%8nxh0 z`w`Im6WcC9wYFV?u6X3#lK9uEh@_=ituuYU3hFx*GyzG$*>-=Y`GdScz)AhGaWA)D zXHJpmQQPDxD3SB6fg=k>9efbJyALP93QhCMd5L>r&r-^?Z-H^Xgj*bu^kL2|S&Iz^FwnH=>CQq(yirPY3;&JT=`!FP7_A>R#) zzVq$(&WcI=3XB4p6EU`#ypGgNjuzYk7nKfotWt7wOzVIn{HE-c!%)=w1Y> z`#I759nt%_(S2KVukAf*NA=s{OYafm0l(&7wBLs+rW?FMBbCnJW8s(&=iUr08lzKczZf@?ETV*(Klk+}M)u6#K$Snc&Hu(bhw{ zsur~Pj8xL5lXa}QTSBzUj^UDb=qd3L04)>N!2IYHb+Haq2|!T|0Pda-pwT?T(ZV|> zo014XGks$}H|JDQ7d<75CBv{eOqta(fI3N-jmf^+fhzXRuB$WA zJc)2z%`5d%Q;B-1?`4Zwp6-ZL7xs(ImtV1Oc_$%yn?by{ASP_XvL)38AGA39 z7_ydrk%z2jc{Aj#@CC{AY!6Rlpd#nlP|$`M#++w^xHcu>o&A^vcC$M|RhR4TbU9yI z#ShB`Bone`hpYnC#&y^Yhh^oB_f_0a?`&-HfU!XIOQwF%tuoP@Nd4j9kk_{1Ww#%C%)~t-yY+eGWp}E%QL>c8p|xL9lojIJ!2`V z(5VNSU7s8%Fty#JqrK!!nVM%Umc1=!mz%suQND2hbjBr|Nq9^Xxf+^B0b$byRKDR@ zoeA`rM1`0UXe8!UW7crjZhW-Z)gXJ-SX!fNo=Murysn9`|dfHKwu*i{N%M}rDJyATa2#WEoLEhc>sMwhiuZC7&i=W0s4 z6a2DC^cP7rJLBR%ON8yajfjNxR_d4BbM&Lw%fv4!QmL~o-qbcz>mG%evX_y733Xa7 zoThJy@Vt?NRo!XTH(mrac0-8PA$X2t%&jFt5ngpJWygF%nML{UB$TPg3cAh+<@$f- z2&MLYM<{h7lqpwzKSELAd_tM}Jqcxx|2Kqk_}-3Cnm1}>JDWfDP>Idv53QA4w#gp6 zCE1qLzi^H@TDPFJA1{H{ZqV!zMN)*&WTy1Py_o9XG4dolfZt%mzdV~+?%*bT8Oe|p zjlo^S?B+Dj_!)|qWN|;*mDq2|gOac9`~c-SA~z{O6{?KjbVViGXmya3JiKQ)F5+ z0Vc56>z6gU-AfzgKDc5LqkE5I=5sgTq8&+{-y|3+2PUsC@s^O8gX)Z*avq9D2Rd=HgNR;6#I9n1K5lQvaXU8{Qht?3051 zFBiw>=>p`PV~b-@EhCxJ4hri%X8=iQ?jQRJk&53zRLxLW*DaB2yT>yGWS zDS`ISUYA`L6x3fRS`ecIS6{|SUr7vUma^JqO+~ky%R6}%RL0C?461xKKELdJ;A>q4 z2feGtph&$=gg)s#YZ4=g%cw6djvP8NCFJ#X{J)t*Y|~WOla8w-;~e4>E5h zmGOq`J}i{D(6h*QJ_T*$=_W0iw!k*umQ^eT>xwM9{xM#DE`LD4rx& z_f#c5m?~E{1|Nb!BX{|JsbKZ3Ol2x17vEtb!0NQ+JX1>tcDSxF6TJK9Z+8u7=g z_$PQ_(dS0|dZP7dtNl8=4i!zrd|)SeS*7juE#&wjw-&U~VSqI|AQ%jZvxPh)l6Bqe z!!51v0YEg-Jj69|5W6$c+{2~Qe=7MlSoEayA83O0kzSw6>#dXFR^}B*OF#u(Jd_Q2ZvG$xeCBf8G`A)EzrH;@Goc=q`+UHI4CYl(@8(N6(VmSZncLFwwlovfOpOTwOoz?{s|@(mqp~E~BA=G^?_bUw8C^SU_{IhsZTKe3{MhJgoBn3}ibUdkFu3CfgL5B{N~B-(fyN~G zow==UGT#FB^!k19Tg$WH7cNBFuT-R$vr_qk$lktm4d+Gd%zjvgR6RC@)1*YyX*%C# z_gUU&%vD$j(>^qIjJA)vv8YqY$1tZ@{M_Z#9uCD8=WKgk^AKHwHY}23^lpIeVTBVf zghDpJYr>p$rFu5Z@u+x0fl=;kyWR!hi86k~fhMc1#%nY&XmiSR61XSq{t>x2;0=X3{Zlv#wC4sVKOg z)6;+zGaFCp=uYsrOKIa1`#igFLG`fv{}UVSwWV6ep|zvjlEunYm-|#HyS4Wuf}LM4r=R2mAhOF9h@X{{Wc9KKkt%f zK#BkC{~Z-SwnK;U`{|6l@te#!njOdQ6kdlTfc`tpIk%1!%2BNi_4%HFN1x7_b1wUp z(^;pZ{RTIms~G}iKwltz*F>E*T`tcPpu+j)oS9OcDPIh8+44a*bNN`m(&!^kz@DO^ z@8=1~QCkUheuq0?dS4+ByPt{oE$x0UJfzLKs7IN@D3~?+J@v^a8Qbx%$cC&8`{XT$ ze9?HYMzg|lW+0Vd)}A33)gVo!NA2R0VX>4DZVI$lY`#r%6`6 zQP@JcFargK+EpmfnxY}guKVgahsiAgS@S(&w7N3rN@Q_(PjqwQG%#={H^KCscjVw( z*QfAy_gnQyVR)#!)X69EztD^*V#@AN#Li$S7|eHi*cnBv*uHa$XytjH;GT7U-YA{* zMjj!a!<$f;vKBfxRXo8BDnuVmeceZ!Ie909q7Hc0(7sA4L*Nf z9?*Z|&`S-fanA$1VcNB^Y?HA~X(a#Ahq>@&?zZg71H8(^#-;fcpO|0q$-GI=Q3YA9 zJv{&Q5qiBx{_C>**SqnW`5-)t2~dTSUAaSev}}TEvWnYUT}yErDtJGL=*c#swln~J zw%kv2t#W--?v$usfTf^?U@Gl~NAq#8L#ab_=DGU51gNoMSsy(0)%L`&e3bq;Vv07% zs6&9yUA;L44buCwYFOUSYS8^Z95{0e5w42x3-_Qgxe^eLWm}zP88Q^X^9tN-P=y1my6Ba-I32&zHZ7`oV zq4IUFN-sPGqn zk}Vu^ZH0GM;kUR8U*R7^1)|;ga<_K~Q-;Ge+c{(yTRive_Ra)F^#l+d%3umnnk9o5 zPY2SyG~Dm*z(H_t^AD9~O~aaEZlRc5|F#G$Ut37RGS_mtj*{U=QP*=)2-{h9UiuMG zq>Cw)K3I@;oG^w8#CA>k%X9L$`VCIq6o>}?+q?<4&o_bewel zLlvnTg1a^P-6g7+)@&Y`lHc^{d?C(Nx)=2TGiE?}Ej0JrY@xZUx~-Wxx6qsja0UQ1 zs51o+VbFgQY@nQcR2My$=^$9e8JXe!*=9OhW$!_C;#+DmhI)-rg*SC?Ywh^!pJ$j7 z>yMTmv#ka9tHq8sY{8wmv5;d^zHIoRfMI~xD&MRlC&SqcJI{1XOY?19V-XbR>rV}A zYk_`g)PSvOHwh(qc>C@J`tE#A%Y@umgI;_-s%Lt~ppwW>4FZ$k$)z3>F^8K);X+!8 z4|I`=!)Py4x}t~NWzPK7@eJhGc{3I??YJ2^{)y+prAe@!&na9eQWrElEx7IcWG-qV zkJGN_J8fcFG8jA9Z#;PD4*tpRWzw(mh1&ip@@Pt&_{oz>{3MAf$74mjxWpg+y6+wJ zx<7om*Yv)~7i z2vEU39xT7Ywiiz#aLFwvu$MbiIU!4pe4s$dN_pl6Tc%3+70S(|kvj;K7riU=5}>sk zykj<*8@ekbbTrW=vDCH2LF44ydFZu?i;8O#=al3Qw8i2)UirKxaAa&4v&A4=F@xHh z*=cqIk+snY=iB&BbJ&!Fl#+)Heu8DSNxZHN9~nNrE`s6SdVaI>tiRR3f~9SoXMOL? zXn)=R)p^!e{ER+j&$Djd&xX_D@?zz&XnqpZ`-zbi3~rh2E4oq-lHGVvK8yG2Qf{ zpX^;x&&p}IlVxpwQ~t@UnSFlR4U~3xmcN6qy}uCr3hU_Cl`ZuZJu1CNmZWBU)wHS0 zrI-21Bg6o?bo@5UAY&RSuRmKe@tY~VU5%kFHUC|YktB`!eVl*v*XSpXFMhm?E6enR zMxW?5!cX+(DCR@p2{oVNiOGu+tysI3f3Py+pV{qtzi>^a%}*W-?QBsp{4*J-d=4{A z7Jb{bLo$KY+}5O`Q+2CuT9;9F70hZS$Lavs6S0c96Pwvi-M+a}`^|G7jl9fJn-{;C zT??|nmWU(KQZwu5h;uSp)PeRr&quvdN=l%Kpw9k^zEv62US&Er{fxcMWYjJ(=D3RG-oKc0>q z!=Clm+dK6eNJzG6L5LyVXYvANz(yOAKD6;=@HgQx)#sKm+BX2!l)JK0eEdj-LdL!L zuM}iJFY9o@u?L;Lv6bGTj`}#5T!c!6C$0jYPbzgr?~5;(oT}0 za2!v)W-ZoglB0%m!#06T=`b#|d|$=$c0Vnd(sx!RN75D9mq>*ox3h&ZF3DimcA<>- zz24S7nyXLhLk0l_;U`|y{4pD#$*mYX^6TwLT6GwEI3u`;m#cShKS&$dt-&;TH@7)_ zeVTxxgauNs5vzsUr7KKIU+jDSfAa4s_vB<&F2;K|k2ysWAuR z-TtvDqdxx(D1x0yv+pPCiqo}Ql)GwyD|fxhefqS@m8AFGqS(VPxnkF<*fJ|tSCZ^v zacy1lX>50O2`1?Qe@qaQoKzmZPNEJWOw<_hQc)@TOd}_qHs1g->w|H{Vk@Q$B~-}q z1$h&ok?=N4^^qgyVB8N_#3Npfj^lG}1W}_zgKuTl&<8%!#OAXb#$QkRYt&|M)*R?v z@;7ld7ibhXsi2Rb_wTVuULk*0M|SD^Cne?-qu4T@I++xeTRCwI+=8)E`Lk{KY_M<; z<_0XhkZ4;7gI-ghXu&ZW^y-T4#y^ZK3VWNI(i?J7Jf$hy*2V!H>!lPN_-WZMwB84aC`Bjarzkc zIh|QjqoaEbE#9oDS&Zz_0V5k{gOPK9i7p8K{_XHLTm5u_D04NGEZQO0iw)S7=ta-G zUO3?sJkqFMMB~-G5hW`pcN_vm{~~*3rx?TXm3-z)l_;QNT7@1mY;ECW>&%;0K$fv75mI}#fgWd=~!PPB*EID;D#8-<$&nAq3^7#+k$B_+Zh zVnlX8imq>+Bnsq^%I0sgZlZHe#n6N7BKWmcZ*!F;+_SJZN!YA^oxcC=r@glM{>jfs2a`AB z@3~hbC!0pog%+e}r*n92LRvjQo$T}Ch|MSPFLrkm_<@X%#XFgF6{hMsTX#*_C?2{J z35L8$oQD_Hjp#|1bTN|DvuI3pX>C?n*l?mm!VKtOu!)2^i3D_@oJhDbCla(fp4>T% zDVmexem76}37;G{#^pvpL6j(^l{-oL1z%}yz#FW4TtUnD&CU>ayHJ5yi>O>S#l%%y zNY$B1A$X7zfJk{9to$_`Rzq((3NZ5PTsx+muHrO45Ar*{EUbij$*Ta_xb6H;QXf!c zf?a-OxwF0Is}$PF?pv8)lzTr@+WjlxZ9^>aZ?T5M9@5%jFR~Qvs7^&@r5;?v>RS*} zW}mk=NTlhc%&6VwxD&sEsy(rD9KzsSDT?9e`fe?+_UAU>e<}0ad zvz|=I*0^|!o{Y2_x80MN+}}}8MsZC~Sm*L#c#ySNON;Y_HDl+fAFF~l>Y{!q18x?7 z1PQ*EDP6K&{b-9NK5_jxkA9q)?T1UH+GH(~i|%r%>4^;cn)xXF33rZ^vU4aT)uk%= zsMIRNO>k*X@)MzWH3Coap^BUM2^Q>#dA@0#5(6%{K!6ef#=kZJM2b$$q+XFka^kXk z*o!f%AF>%Hi?V$>1YsK9PsggX0hdgK>}v==Wv3*%Hm_O;&0f>W% z72TgaobQ6Ut4O$Wy!>28NuQAr{nyJlxnj2AVpPvDdP>N<$}ZN6FWrmz7h0wFlM0`_ z^<6qvdJ2_#y{t1*->$u52hdHjzmAwbVFxgM7Zr!I_$STTON%1m>$35`_Qe3qZ~8E+ zD%Z2>bZT-Mud*E(Qdv+R6C?YX(m$&F$St3z?0LGs>bEU3N{2^+>;?!4Qn_BQi_(Iy zGqHCLuSF|=-ZPicwXvajL#tf@SAG>hP2MaVP-`o}$t(Ve|MxJwsFtMIh;-7YsNtf z2D{0mo)p-@s@91<7atW%Rt}3Lz98L>X`@Y709CB1wb&NoI?2ysY*O7S#fK##nyDJ+ zR%`5Q63y4@bmFIi)@XG%Rg>p*>S7gigg$3Kck>xs*l8qoB%)4Oq!O0DD7l3AYN@Z~ zyAq)l(3{|M@-##TFSMUBcPn*Z2x@SgXwI_(v}0@4=MsuKe@sXB z;Q4%w<8E!&Q=jK~mF$~&3L_ku{rkpWR-%9Mbo8}E|9|CernF@p!`psJB&T*rtxgRs zSSKGhD|U;AMXV|$7A%$PsBaS2PS>)S$*o){v9&nT{$u4CnBDKDV~m?DZ^o#7_rlVg_2+IFcR z6w9mC5{fzy6?sw8B6kiADcQU8AlbuKb%bSfNU<}<8XDSft@GzsQcE`ccR>r)WlF2o zf*#p!_NHjEwg8J}S5jyT6T}q{ee<)nO!kYck|-6B!^xjv*7TXqr-LyCZVi-hnfe$8 z6N@f}vdg(Fc#Jmbypgkv(p(KgpMj&Z3p2t&RRki3=c7bmS&9#$H+7t5G}swD`;IRc z;n_Ed38;J7++h>2zGD5i<|Z(m_Xz)J>uzH^XPGGX;tBMr%?YWf2`pG#OHU5Gxr`e{ zwiI0;+*nxL?g$j#oT>)|#wihg9oL!C{XQFDEZ%UQ#`p6J-S~bjRU?xA1rFTB? zzRn>1)zJ#!BBJjxjd9guHk`f&I- zC%t8zX@1h$LS*C(o}>!uf{B;G`~vjA@DeLHs*DPP=XOC?Ox{s$Wg7LPzf9?abSJyL zQsg&~!Sho!6^K^$rQ6L-j$FAfiSFmdDF(AITUBN48CCS4%}+yS+WfTF zRUA_CLrMm%ZDfy>{EH?25eW*;4N2~rJwz#nNkx+^G7KJlCzyMF7tQt2(sZKr!2uEz z$yuYsxd{qc#ue&4z9ce)$FTT^m9iQnD~oHBl_lXdXab@_btDFDtdJJfQB@qAC zRA9{M+s#s7wNgG9!$yhlX4S04&jqX;B(B5B*(ay)B%Eq3ME!>vF?Hdwf=%vza zRat4I4ha%<8F_^F<2qB?OZD~FV~EKHbwp9-lK}8P&jco5zU4t^lN_v1V7{7LG&>{xw!r00?`Z@R&y17N&(gRYaKehy%n((?f& zJX24^F$_I=D2Foq(4-Tiz`%rOvm(fp9;K2GP}0TmN_GY}-NLHkWYIXR|JAw1`p#|8 zUfT~l&K%*+TE1&Zza&_Hs^$;_Cwqo5TOY1A0&F0Ol)bm%(gR1(2{X81x9JlN{9zNBu<- z7=a>+laL)|9d33op%|!T{_D`sbD`J{{XAI@TtBb)n10H&=bB@=J~NxAGRuoq(>ymd-kG^mHqg?$zarOT= zSO11LwZxFJpA`s8DiH|kY_Q@px8BIUxleCC0t>;D?oE2Pttk6H4Lg#={X$(nNz67} z8c7^W+0bUBJRm+BN6f-C!HKk!J|`c#fxwVHI~cVYXjRKrXVS+8`$mtEw&=n-QO75j zJ&J_n@P~nbvNxNZSN0rl+zN@VwgOr(YdbkPRt8`7&8=3Rf4;+dMGBCPj!KYnpu1GN z_^+)`b|ffw(!FuOlutas<+OtZl=Lmiv?7kcAxdF&_?=BW@#lY zh#yYnwW3lOXV-~RA`~OM2mwpVl+Tn*5z)c%YFU*aXUzUk@2v12P2(tfTMv@MSPTLd zT{g9rBaLcVs_e6rbFiwCy(>Tik9spXKIEplME9D+h1hSVmH3ZSkNz5mJi~br8Xi2g zXu0a~OLVUDGk*72g)uSN*}2hOd1DKzO?+H4V$>L5@S1nG4o}PB4j=NH9}U2e^$Ev^ zYK65a=&~0{q?Hj^YQ4)u(W3#6a)#WkO^eH8O;5MgvXtYPw`Y`m0_$C<^AIKCW|+&P zeV;ByIUN~_eFzwAc)~MnQH9Tg70;637X#GR;?CB{V*rbU&ek5L8kySSIuE1b_D$m_kD&m&Eob_u<5_ab?`%2FP*-S+0A;65}>xjYp=p zZVZNyoG|QVEdWZe!v#zShZvYwRd~Vsqc^hY%HEDTXqY%UvOFE#Dw9<1v$xLYvwQWK zY&N-*ww1FhI-~vm_j9~x z^W%Y9>9r-{{TeBBe19i}dT2b0O^-)d)#|CqpD%=>R8h4aXhVaY&s7}Mcwg9V!;;7? zaTa7HXHEPARnC!NITv&z<|SVGGO8ZN5Szr27ehRXnpVjNwyg6hWfyh!MCom&Wy{x6 zehBI=>?1Bghw1ac-kUoIRv?*KKgMsOu;`)=nzS`mrER6nvtC;~5do3HJYXIE9$?K{ z+)BGSiOwWFYCL`mkrmN|gm^3oE`Rb91k#t`x@h!~tX~FCVKO$_t4U60(eo|K-UR?) z$yl|#gS{)|rNEuq9R^M4%nD+`8cuX;|3S__qllXIE4;>qRIR;VbAd&&4ZE@Y4Z?`q z%R;}5^`<&8V*X0x3zSYfwTaJNtz=>2iL1s`F=J23%Ps2`+QV;Yw{@-aXs@4QfwlPF`I}}KBvl7`MEKpL_BvUyLn{F!S4Nrg+jE+5=J-cnW z>4iWvLlfl1BScVWu|DqQrYklSHXNAEXi;(;(hOr|d#p~@scakMyJ{#g8YhpVzntHs z&qNk}qR#E0H#&-B@M$(2GAeuV<23{_EFZ9v0U4_|NxxjmxuH7}s`*&AdJPQm$7SWlye+uU5W8Z6$i6z6C4JK?K%D$e$5Bquc+|ZPv&=I1+9H4VJt3I=zR$!yAy*+ZmFN%jep7U*Ih?SH5DUc~GZlfg5+5Pe*3+H>{(?h`E zv`{ropp>w)8PRFxA{(F!{#_?J9bMRP%oa4-VSGfLl`7&(w3;-Uq$_Bp2D2EriwNbX zTF_8?WpMvA;`o;42Xk`WK_AvMmOU^0Yd>{Oanq57^)uBi-Cq(!w367g!Os&RdM@$__C_Bg& z{y`|qrweP;iKwP>ET4~no9-v;A=AEUC^hsimgS)Ir?)F80BSi(bR}*1$5iUvmYLFr zX`IBx%+lyDTw9OjN5Bnn#kDNy$xPbIO=FO zGV0p?O|R)<kES}G}%{D44N&m!va4=_hhdRhx99o1Yq<|d-WT(sSF zE$cEkcj+Ii5Ho5$BA(N74bGQF!n{o7;qSPtKkW&6=X|w(a%4sD`5}>$bBgdwxDHDO zh}Jh>$^I3%6^MI&6CIg8`kk*7-r%*L>{k)qf+UDsf1mi|l%S!4^m9U4T`>Plguz7u zEH$d&0iv4EJ8LI*@?<;YGtY2P*B_algz_NjsLN8vsqAw}9~IpC-VV|$()f{3+QSU;2q; zgOsx1V39}mMBwJx^ie%zsNM;dzTslR2z5ap$a?(QTyXIi1<|zJOJT41A^zRWEqmd? z(I_NUT?beFDR0;U1y?<)E3wbh$k}mCS!^kmcZwRnj5oA(Z5Rz5Bg!VVvvOvik$f(q zWLw3!!ID$_!_gfW8jqF9aU9O6DJ1OZE~@DG?V|Q@pC4VRjfeT3;Tcei(Ax>WKD)wU zbGt6>*d$*4L`i1^;@74qUZA1acKr0Y$NV41PY*x6)A(uPiRaPzS}m7fh$UXd&uE5- zM`jBpl5oLF6iO{>qEn%ov65l{-9sG%(WSU0ckX);;@e%UVJ{}f8 zUlLqIef~Vmulj3@4Il4&ac$`4$j91{D*Z#eZ+wk^@#3`&g|J?^M&SXKk=chxh5X(CtlGZHu0D1_rWeidGi12L&*C;+ghTq8$1D- z(wljl?(QNgVu?LrWL6$iN<<%1s!p4{Xn`BzcY=2>cc;|1D+zh>=)39lAiDZJ>Gjoy zPrsX91=n}eYsdh8H@&XI_r&V%)zZ!Bbt5rb-$}1=qjp?j|3i8;%32#7`|nTL{k1v0 zuBiH-(5oX>olCkow}<2V8NF827`^Uts?qDY_jjPzMT@sWuSdg;(ChEs+6uiM{mK^f zdbl1zuQz|Q1-+sgh#qP zR*4FpkdWZ7u{L>8oh+x#B@mlH>Msn3Q+g7|_i_qv`|-W4)vO2xJf%3Sno=Ej_7dmxF6U&=jdBnE zkT%v;d-Gq66}}W3urNlpG(3H^gO5b=O)J8Y3&{=aW1j^KB*lY~Zr$%crq5 zUDI715jKLs-7n zCil(HM4p9{sJq~?Z$4LQJb?{~$y2Y`%GummJNzh~ zS(ZUg;Awga$LPY6@FtXF8~og`1ak`3WrtoCr5q#R17VtH1vw5{QUHoIa_^rDO_PH& zP~fDbm*~p(6TK^R!6JdZ9gbsO5N4Yeh}9q_IiO;x1#`L4N=xg)vLJm5x+=97>zwOY z@@AnmYY^D{xa-zxf)tx`4!jms%b#?FVt?=K1ka5)s=5e{qO@9tF5G7jN2^*ZM+Tk5 z;n!Lvb|JIR zH{6pusyAvmI;z)ezQBs3y7(`(NeE(89$Aqo?fC-Il4TQ!#fyqI@OVaP8zgwx z#yk>i-klz12{ah7V0$C$hQ8^OH>&|r=|iIRRk7qn9Q6HRH$U0HhmeZM`uD%FJ@ST-vTF)3cg#O;qc0qU5juQ*y6= zuvgacm4d{3y*D+2$u1*{Q~D;p&VWZTNKu zJ$9EG&zP&q+N8VPL9w)SH0~hrlTQjTt31CkGrf#Fjr|fr3@PG2C(-F4 zY#gfR-C&fBmrgUvM#+Kc!|!tz;be53si9N}Utc!%xD zXE|)&mvQ`^`ZrRQ!QUli`U>*p9h7HL@KTS0(^f*k-}qX4rPFv2li_IR&ewo1&!k?e z)cu_XQZs2M_4+oIJky!%rTIrNNB9gIEi=q|q>%5!`x$Hs+GhT|$MZ2nlGd?(W9y5a zR#o+FmR2R*Upw0I9JH9zgCBhF#QRiQLh4ay-F9@%eY4>lQ?L=|SJur=zRs(#dX3fF z=Q>JMXio2t#=qZ^J{WtlU<3i7&GLUG1{krFE~*@ zM)6~YWCV;1wRD_1)Z0sX#}4fJ$9u-lEJva9FY!l6c9Dt)h>D0>S-J6mJjZ&~Y)#U8 zAB-cB_;K;VfjEe8B0m{3)j{$Vi6dIDvR1W3_qJn~bHsuELG`|Mg58dJ1%OLUv2RgN zQYG6~h{zveU;)d^@xo7WZh?wT^@2(izHIzrG8Y^pvEywV*^>WcvyMA)1RGf}+!`u2 zbtDR(Ze5FYf+z-s_e6BDTPCw(e<>7+l4);l)&3*$f>frl+;>}G{8#B-Rlo1$-xs~a z2o+QW_Mq-D2&4As$7Il-(1!l~&kg!LJ@j8L-2wf3=JGq+85lA<^z`Mlm+{m$fU=oHK^U5NpB z^+>IeUgOvb7l}Nw*4PV<)lc>jyq~U{r<=L&ewJ)%e*?4+VWBZKBd}v-TS0eD{^~C2pe0SlgH7=aLuj)jja50~mnx zD^{!MZH`V)6_}Qo1irC}mD}%%RlK{2BW%YfxnWDLJqQr9bBbuaLLR za$J3}2U(VZyhf1nA~I8gs1;3HDGAkNwg1mjW3^u|*m&V}%d_UC=9YAlHO^xv$yn;& zzrt@1T4QYN&_^HvzK&O+Y2;|T{U*H;(m|(lD5H0tG&+AGQmw~ZO)o2-Gwo4Xt`Zn} z&rpPBU3-R@zPZ;uQZoQlYeRb>BwuiU-%2v3-f@-ZuCrRq_oV?l{v^-yg*o zKh2DhR_d(43aF9Phd(B7;-2U(2PReWguu@hI8{!fO60yfjc;f_-cRmZYB5jiqO=f# z=egID-fp5CQs+;UrrDGb2Nh{Gdus$!m`;@=_e(Jk<&4|6;MF2 z@cJhEx0T_lirGZ#mJ*a(+YAzJDG5De`tIJus7Fxt`W)&wyiw9-z4YVNU@J9;DL7kT zR_YrsocW2-8t}<1yqKTxbJ)nys?BxPsS*eLXamQ@3(qj+@cf`tls(au&C&)AF%mNDnRO4Gx|A?0XRB5nR)c zUSJk*_Y}CIJ?6I-r!oKc#0=(;hxxsg2J=G;{WuS4%$uF&fH0rndpi}o2Z34hT6)1% z6kn(&I43ky9U|(uU)wOk|@l0?z`^ZmJK3us8*&UVLUH{~3qkf49+#w2zpfRdk~E z5o5^fs-qs?M>S+}se>k_(Q31>ruu}1zkTx32y5aP zyYMknhYV%2TLPMAEpCCm@RqXh@AW^HBt>A;6V)5A+;s{qnnHQaa{H9To`;4TmXsP3 zYi<0uqawOa{T5g7pzshU96xCbbakN5JJ5JTBsOu2xmadyKfj`zdW90nVO&jgb-y(F zDE_gy#1NJ_xp#;SY;HRsV^afObDyS!*_cU7eDPdwTef4j-BmoW>dW~I zr4M};N}%el*}z`Br*e@hdh?XBwNj+|Aa=RhPssZhn^aiLxy zx*8o&oG@M!rBU%m39nYQEM(oboU0+C{Gbb{qv0rFqUjT5JGzDsLQq9I<9Sgt=i5mB zC*g}I9?wM{15aX?j-sjL9so&oOOl3D!$v8l-Yw9vXLHL$o4x@SS&oy z^8=mRH=%4B#n^PCLM;xc?bvBFOZgRqWiNXMmhJONHp?ccR;J5Q7<5Y04&>I;C*h?h zvo7j1Gn%7T-3*DH!4w@{Iw6Ll+df{y(u^eVu2btY7}Um@G9Z3&&!dHgOLXpf-#M5? z5+xVise}0Ss)h?Sw|NGb>O-(%IJ+-QJ6}N63TM{c90HU&16wF#n26NU2w~Qo{~uj>i>C~&XuZjiTZ%SffFNqui`fQvOyHHZsTr%)tDq(d|qrd z91K=%z_nKJw)cy5$0IjKcFs2#H?MeYQTnkz_7RGYGPAaLf^CkuE@?^j z3&*3J@t_&64lWVm?jESACIkRM5(iK z3=Fy_tbN1n8P+a#HvV2hF^X7A!P-4{!B)c!m@e9aGqN1uNUmSLhTV7U@_@gHj(~B^vO1#Fz2iVlNs;os zr@)!W!C8~W$r8hJmkb}M-D>{kv}T?lPs@QMyy{L{<9@ia*6;|pi?=3o3sIEr>kwi8 z?myXmefXek?DS0J?Bu?5fCri(Q&)j5%mJO61DZ>wQ9Ex~!_v;~Bp-dIWe+?F`g3y7 z?~#MvZ&`tRbbHWA0uAJVz8&bGQ-$v4U$mpg(E|PKgKT=--4SRPcTs!L%})UOU=HZ7 zI|A+M&TbF7NT8E*K>KzCTI3FI4?0_*19L!=400Q@`ef?ncK1Ou12|luTi?&_>ti{f zxdV9P2Rr4ghNZi?4-+&fhjG{AV&VKT2mPtLKwq&d^xfTBAAN>D<_P_u9Q0VmckFl% zcaQchs}*Qo4(QW4pt&8t{rz2Zyr=sl!7Fk){?%ik|MPpQS0G^POyJ9^2N;Ub}4Oe*a6&?CrYfvpIuare?nV zc6KvwfTw(FE~ZyIAM^{}{okSHp6-zL&Aj>%npu(4%=M7aZ|23jY~~~H{O>fg#Jy{8 zkD8f@`xYy!6MHM0n!$84jb7o4>b>0YK4?Z%-zCtIIiQ6(pt%z3Q(nITSqCbKnSHWC zk0@sX?d@*b%WGssS|J#qnm&CqyODQ6z&7ckngUXOI0#-)k=B4vXgL`r z42Gt$>>>yTRVhWH)NSyq%+O`=LsU66r^@o2Dt)@_7dbfIfT^+xlZ)d(H_5G#oOP_PaJ>6^cJFU$ zha+1Lp8Uoxh#PRP_t9sDz4afU|6LCH0XgV>;vVFl+P-Di3-rVsP%(0L@3LF==Igs? z*}?8cEW7G>re#M6{o2>EJN}0p^nS|@ai8`SsGbsuaG`q|$By(0(ol#W2l-O24i9~9`hIiP!X1lrFX*dBDUKo8CVeHRAW zp|6LzTYI(Z>p+37U!C38KXn9pxVyML=+?gh`fv{D`5l4wcN^M+E*9w29MJNPK#y?k z_Mi;{JuV0IOU8bOz8>lJY!7M+^oM_E_w|X6K#y`?D{e=oo&s&o0lmB<&7yPsAJ&S}NB3~|=T;}C$E*mDRtN2PbqDgCM`T~OxvpA?iy z0_af(syo(QvwOQC-&Y7;emT3-jXSUIi~A^jZ%*kcJFo6IcZy$KhQ<#G!Ff3dN^+{p z(svAvH@@7S#%oFW|3c&A-Oqb^oz4jJK5E4~7(UwEC~kzK+Gw2B3P$x(uf`6vE@)cm z#(ngeAzc0!&|i^*z9I*`FO;j?QSCwF0zDxI^gB4Dtz|ywFWr*%of;+3&N-lRd1?dA zTA{9=x{DQRkgKDW9F5_>e+Kv8S7j6Iyq)2$*cI-@W>9t7~;N%?^Vw5mI!pki`m4O zl>?g9tW$T@tfB7A_PC$97u=WR;65Y=ckW#4v~m|JU5$HSd-P`uea{^9FTf^kz0PX- z?U(L?qQl&!JRXpvA#MK?xF27UO_qr{xPA5-?#^isx>TUkb3l*i2y}#7+a7d|KnLZ3 zZe*<6dgRw?yL+?;trciq4(QVzfrj0;IV{NO>mCAK)|%bdt9Af2i>SXhImx}HJ&srI z0mtkd9F;rZKxCfmj`2Y=6XOR^t9r7;EF&g-Cn zYDu^AoV#e}?m6vT25+@>F?WFOU$zSh{>p8^hc<_Gw*3+G|9l~vf)jGk`xHFQ)ype6 zpe+Ke%K<&SBhb2?JB3i z#R6TL1Nys;K+kml*dFvv&cdBJIiM$Y1UkkY*Bwb=lKu-7j2**1u+1JT4vt^t;P89*8}m4FPW6unbbJoz-W`FS?;hX2 z*2@IiCkONcSR~5?|XjN zeLrPKoX4~7?vkD-{shh+uH2#TbHOR1a}#*9JigXDT^7qN8=~tBG0314Gk(2rTq))6 zp8Va9D>j|oe|^#(@a5-z7T_VDwgG74=}X@;K4r+a1w*a;p`V!0G;P= z!J9u(y;i@U1RBl483NZuKWZ)<4$D^$ja#BXVfdB zG%KTrpK+lwQq1u*s&DzN%10_=otGis*NVKlVmXynrb=$PSswWSD81BA4-o$;oo)#7 zppU3fh;(BaIKP#guE8_@1V~#Qc zSs8o!8IzUKH7nx>76bMB3}t-9N4mKm`x({Bcrz>GML(lV8H=(q9`iH0E8{*dqa6uH z-Q<(dkZP6>@ZMtpnBWK=ZL?^8U=D!1{M6Wg2q5IuU@#r)XWXQW@~n*g{fx_%VP$1> z_A|~?#wJ#xbiY66x9S+CjQ?b1yy|E4SH=rj84LW3-pY6=E8|{2Sji6$*aR%esEdHL@OSn zw{D$q!wGrB$y3c^L+7gYRG*h9pZVmWTCXpP9ih&Yrb-^709}~qV4(L15avgQ06e;! zu($K(#I*dS+;PG74IRF-3G|WbGX>C5NYI1V>k?^6uYI}1b_KxB(`Tm1Yu~1NNy1;dDB-^UCGnMXTL?eLtzlj&+x$+%vI$MMc>(8B z5cb%Iu3Dc8&)(u7imhuj!@|#;eAcy*<2wCg#Nu>xF{z@+Sr?p;RIDVq&@EA>^@P2;URkJ0HqER~@%QIZ`V%?RIxJe9G zHP<<{aO@X#xKORcT?(hQY}6v5+q7q z^^%d{*d%qaGBcHkKAEZ1YSI(O$=k!RW*k+`?eLAE)M|LT!N6g{DR<6~OT!?_Pbn_W z1RQyL#pYM-SP%aOjURZSc!Ig;dxY_iO_!Vfjh?NxXQMyYZ>QJkvVvyNUMEZm_cw{@ zm?#L!twi3nEgRx?Y14~bJ&DiA>hqCwMc1ztAjgjPC?hV(LCiItOG&*HZE(K_y`g~H zg96UdoA=0T8^2NMYu$V4vXM343O$Dv29NnjqR+3!u1ST@?{R%3|D)7L(5WbkPP{2v z$qjX$Zc4odWe?>$XhtFSq4OuIA+2|9e7X!~8ck-~=h5Ah3zG{%&jt8AB|eb2PRn_6 zWd81c6<)=ukMvq;8Z%I_mI2M7=QtkXVtCyOH^BH3K0iN_Y4rEHyrVesr zFEhr^x!vi7E)H=0w?0Lld5e*I=#3lspli73f7CgW7)%^_9^bsGFTrm-M2d8&;V1$* z7jEp@s`b$i-A`yZT?_2cwQ}0)>M~F^Ni!V}Dz_84q33XgAwbZ~qC6tKaT5kawx~~# z%veiPv!OB}s;E4+!X3#UZnoa$H`OQ@zvRONyH_*(#xj16-raL2`3=ZQf17mm!v+qJ zAQiGMnU;l~vuR8+MzY2#mp4)$>)ixhU1@9ug-8`9wu>Rt;J#@pUEyQ@7(`I12r7AR z$S2=b7{vJS0B^@d_t1MRrs6l$l;;4VP7ai()0~g>`tQwzn^V?bOjV{{pA;wUyz*^a zaD_uJ@++71ya z^yGf+PlOOJU8y^4L(Y1Eu-mcr;mr}_j*q7TX5>WHf3?Ha*u`A7lo2!=6JIvKEg zQ&2UVZxqh1elNy7a)2R%T3z>?X~H64lTSDYK}ECRw#jRhk{KrCBauh8r^DQuhnRKW z5v-?PtLAXQe{#{Lrhm|7Zpp>@V=h153Q?IXH{3!352#eqGwX)C_~070iO_d}ZI*vh z>H)wE%X=QHO@t1F%@RHC(aqp*RfuB9a+Rlfc|j*S6XF6$GiRRU$AD%6#^S5j1zOF> zT03;LiuUMPawhSx0nMqB`@|#84q&qAGhl+%mQ$)6 zZ%z(h{8+UM)NHq(O6jKI_-nY5U#u@QfgrDsVtKf8C(JyMveGx}9`YDg(R*fpu|hpU z@p&wN(`@az0(IRpcf`b&g|`f4TG6(klqs7-e+W?6@V=*EjWV~JI12zr;o)D4x3+YO zo@~c|(U*_3DlRUGeMo%K*$3q{Y(XS11wgOI+zJ;f5_iIj#2sy-Q;NGCqhRbEcN!fN zY@)i91ZUNQSVcQ}|BjC7{ksR$jF|r-02fwy?;-s-oi1_laffNW2^4pI**ty3dq%%f ztM&5TUrg??7brn-aqNT_i=Re0!rjW0)L z5d|hPFQeRAR43ED=a>KOJ1;s~26X2ubZh6luCsi^Xe451UvDYm$A~@WB5l zBlZr=s8nP%%s95J*xZF%tR$!9U`;@^lL2~k>?YHY+aI7IlhlyY>^L#cdAjQ?u90in zn0mUhn8Z4t1G4HvE`h18<))~&pV(P@m($$ters3E+(~PnR>m${+dI3pd46kozZTo! zwWbNXs=upWzrC~ikN?T~Z#gK7TJ`66)S9UJPxR`~mG8AN#H)fq+bn7EN9!S=c?;>JX!T@m->^)ws$K`pdfRCcKi>v_{{$^C|sr&by|}+x*w?^$g%8 z{~K46k_ixF9?Je7vdp$#-uYd#U$TdXgb>xAomYfk<>qNT-Kq_0auJS&b8qJ*+T#h^ zwmbg4PGer2%#RWm8> zCG=Sf%+M_*dC8mF0NkTVFvI=0ig{99YD$}0Qg1Ly_rQL&v{IC)e%F&M%kGCFcQ<73 z7rhWFTqWJ!W-DVyt?ShnL-R=N$8dZs&YpBmS4!Xi9i6)YG-=vcH%zCPRwt`zrWsuJ z@wMEIE%n0A>Om$?>t=V%(DyG3$2om6+RJi~8IIjZmdCMnj3?1~QvOM0#ru~j8Whfy zp7ZB+D{sUE#8}+&h(fiE=GZ-Un%y^D0$(@}6l_>L{{j zGVIKrZQer&zq($=%-%F1RWjfT7Acgz<;gv?JirHLnw2^Z%at8xA@V|2cGrmn?3+({ zaqd}vq23A*YQp*R&AC-Hi4!)C%^D9@4=vY^ebW5D!A=!LT@>bklRT1;j31f=EEP-3 zvu40*vecaY)Ep02on=Z&PY*bck?*6f+ry-59teYT8(C`GbAP67x7MdutL*p`qogEZ z`i#VhYo8*@)*55~Yxpt0ZqG>K@AK$DIQ6n~F@K4MkX&c1xqD}t=Ua5oP4(G&d3s`} z(}+>n3T$0TIPq|q5P`rsSO{ZZ_=viJC{?l;P$r18mzwz{?}B*tSym71%u^Wg(<$H- zkt-7Tk$aDkm_3ZKX&}tn?TMUxNiS&;@W_IbLW31L7Nm;&a>Zkts@#h!yNKN zsk(z326d|INh8dD&b^Iv^9ze%%6a-*WYItD#Iq{ygpXi^*h-{QC4Fg5@+RjG%72`F zkv{*SAsWDcgc0Ul)1tml+xU@8D(-TQdtH2&d&v<{(N#g7niG7=VLxj3vBZT-< zn9$Y|J;k+5=kn)_lxVp69o}yXC$3)W{Nwd+Whp=-TxQ-qPF?nqTW6#`v9K0J5$<{w z!;TXHJbsRr4Cn0_K?cmyk=2e=pPMLahT&cTNwBEaqDa+`b^C@JuHKRt#o+R_QI0$JFJVf3}I`F_Apy{B!{tkt*2-C^O}q$$x^@ z9$*TJ)G>p{RXFdm>gtcDfrc&Sz=1rGht8LdIp*}@gb1ld2&TN>+} z{LEjNzwoH5=zaIo-k#8p)RGGm*TVP^(s1JJ_tw1eSHt@8Q(Dprq+v z`pl$y{!}E;MWh?5EC|q;yqPt*;`v0aq&afA($f;t=W1O%!OMvI?Q90Cq)WwDNS78f z;~ya@IX_**-HRMm31%a4_ximaAl&S{@<-N@zjC)HJv~f&Lpr}i{X@2}E9n}EPhruU z!ShoSOC#~|0lQVr7FVsGLU;ZMj!4WL^BAG=9z=*2?*E$XPjzQe3&tPL;b@Q%1>&OP zjf$Qsd0x{+%8gEo57vuc^pxW{=57>|WW{6oFqC<=#GHm|JK5hRV9OuAFUfN*lPsO* zoP2k0P241hkKH4&0ffPFw%pmq!XE#=-ZsDe<*hS>NHc5J)QQrgoecTeWQk^f%);*N zzZ=r;4cW1`QYCxPQ|vE11?KHssAeJqMPM+ZzYD?EkGB$z|AhQ#P}(FqWCR@&Z4xsT z?mseN5IaCNDc>$-l`DD2O;z%*aur&oeZcE&<7c8pPh{kVDb^SJDNgKG%AEI}<|oAG znc$&Fa*U?!+V$&nyJ~jXE<289hb08P>16IhOCR&ys&s+m2G({+(%? zet7f2yq||Q1#dAP@iD}TPV>|)m%>xS?+{O!`2i27O0H!}#S2&b+wc^z#H*h?b#2Pn z37x~KPOsmv138woFTmqS_s8h87AQNuPrQ4o1#6jhg*w6DgfsT8^9Fjbu_y7Ku!+KC zWdB97kGJvQq)4pN@Zc10$~oJ=rbG3y?#aj1@hk(BtU|%u*TMS-C3SiZ9bxQ1gKH5! z*XvtKG16i`{RF)pR@UVN3T+z5mZ%n3wPUizJ3` zunw$|DDJ@^jqZw`|9Wk|Rkht#g=4SQ_ItIq8*i4Zsy%2`xN23jo6a;NRn5~m5#4C_ zTT)lb5p&l8FIorcd0mdN4zg1%cGb%0H*z=l-HApVcM?UkOLy$!(J%5_Oqp+@AK0-i zO9fj`US9oRE3b}a81NO*jqWIv7W*B=sy||R>^VDRDrv7cmkA06q z<~^4^4U)no`i^bojIjV&SX3l}VeT(d@2$ok`L7+TeuP4PC398DFko3FCxDevMu7>X z-p=~5p{;7<4tCkBpydm@DR!oUmfykkazAMKJulLTM9J=JO{~k{G&zJYUqTDGgpuc5 z_tMrB??ewm-j;PA{Cze|%;+C)KAvW=+`5*>>4}8tumB z=&I!-3i6Z7(un=Pr_uiAiy+aoThN6&u&bYQF^J+lKX^mv*>VIloT4_lc@6 z>Po@nZHZD&-wdLcqmG(Wx4(fnK@h#1X;ZWzlBIo(le~%j5RP2VH8(?yv&hNefKl)d z6d3mT=9DAXhO?GB{jn(n3lI32fGV9Cj26zD&;vB{zKE!Jj924i>BXr z8{6ANMb(?4m1u@J;ZhJo?ZEXB&{AJvS&l=id9u9wk-;wwUJeb52 zr;Ue5@Fgx#AY8qh;M^U;%!kv>9z0A-mWY8`MYp6;jkgm|`42s3o^KDwK9zN)(cHWo zKEBF{ePs>VV2yA{jeYLCLE0&^t{K3#=0U!ETZ}U9N z4srSk!WV01PJZzCn(P>SIQ)72xS?BaKi&vt`f(K1{Oo=#GIuG@K(SmOd&}9K)|>cx zR4-d0RMOe2Gu>dnIu9x>LsFV7g4PVn<3F_=d={S`=zvrjz3Xq@m*~5~ueTeVo`2hy z!7zyR46R(#?Ws}xdnSsFsBRs{3eNh!?wds)Q@&;{R$^kVMrX2&5qWj~Ta~M0ZyCz3 zRz0H$_OnyE)5kow!CP-S4~NN)3o!v1oUom^vfIKDWT#60c!8$%;lVihBthQylB4m{Cw*;(re5MS0}CyqGm zY<`}9Ra4%`ME}nyoVS>ZE7bh8Q~cbf6w{8^B^X>gvB z5v?_`$T!<+Vg=vuWOIJFYFqSmJD+`dv*y1s>}e=C;iN58U2NStqSX1cH%!Is4{G+0 zvif-^vsOP3A~g5X0eV5RnU-t{2J*4=`OS9@xAs+l_N}^<) z9j@WV7wchR+&+ohw)tInov>@^ZY#XCKIVbPntg_RJa_0AvQ&GrirV-Fp(tK3UVW{ zqjClCSI*zJdjeRjA+h(4{w8{aw9QPEe50E3zwbZ_SLR6JIgzQUnmGkiJt_QSwiH&q zhS=?-@Q16ZSTe--Oo=3X`D^vFS%&**y%{;cms_)$)v}HSBLs=x{EjvIIk4_PE2P(2 zr6bWdq-)KH-wP>D2^?YJ+258Q_jo(EBb z;OuvKX;*m;U+w|;I;wugKld?!jJ>Pht>!I9tKZ$;3y=)V93ZtQ2atI}tksRjl!a9K zVyfhkv258hhBE2BMpmcY4={6yVA>#Qr7G56F&vDiaW`mVLv433Ep3}~$iL$ybd0`q zm|0NJcz&L~GQQ*69pidcbU*;TdL>hB%rEeZeMPCv+UkgJD%aJIC!W87--40%ESiP<%J(ZH9KxBu6d zlFaTQ9nIGqech)o9lGzUuiNkDdHJGiE=Os@vB|L$~%+h6K zO#+RsL6E#6xqC92+$}jZ*(EtC*@^D>DC>UXqq_!l$#sZ9S_jq$CDiA67^SDwVc?DYmGN3SPcsakvG zao6M;BiRpX|I6*0h7-C+_vY!sBCA-tEqwNRVW!lvsyx!z2kV)Xm^AU^Sn-@KGt#8P z%)O=jxdBp^JNbwBo(m0wx_ieQCt_Zd8nu2lTl2-^wE@l9e;U92*xL%cUpe4Q)icJ9 zO>{!f6w$*-%-+ENi}2^*v5hTyi=vbrd4Mdcn}{i-6UM6^e_q4QiGun_ezP&SznxmE z8!qsA>~n3@OH)g%##zV^-pGm1Uj%H`M(es(64;LiVsByt$9TGxje3D~-8g0*U3BKH zY1=c}aW)T$F%<*8oV`G7Q^fu_Xx+-u@Q*y=R*h7TH%JyH=H%slor`1I5*xO1;{AjWAT_GHfY)j`Sqmr4o~V6HQ$%0%c_O&- z@Q?}E4JUTBu35mBE?l&doqAbI6o=(pyB#e_#mRtua-v{Ime~5I3`>~G z#a{G(1hq{pHZSu`GK3nB2f@o>n2x6NpJz2-OVpvK>X??L7A%6xOIyQ8TK>&q4Nl@TulHJcN}VyLt#ruS zPg%1MRXcPtc{1F_$=B=!nsJ(mA?w!Vp+y?Gu#C6Y%y4vUyu7Zj=LqEeX~;nmSa0sSi1uW~%05a=o7rNf zmu@3K(8}PMHO}JSwO^n;f1Eado582Dl*`{Hs{F^ukKZ0svXQ3J;7BfN9!LEh0TRTc5@ zS!X*juzsPYMkMx)bMLd;Qpwky%K!GtGaig`1Kyn&q3On??ail+ioF>!$Ah|#9T{|D zlzF_ysZg)uLyLyT25uP=yJSm2IK@+2SHj|JoTtcJKD3lR821*=@H{7?QFi?Jh0N)^ zXIT95LS~ja$0IClb2dbpE*?cYjp5*^mBV*u=Ej`%NZSd}cOGZPxra&Ii~=oXCvB+1 z0MWMFVxxj4)R{&EbQ0B0t$OSfPpvAtqHWYf|Fr)`kmDB?o^i$D#PA**dh29q+U_6= zW>yJ24}Xj!%HzRpHC0QZ#qlXyYT`vTRj;sTm{y+bK27ggdx@c{)gf!d>QL1;A#2Dt zDbA^yC2^`YUjBEx;fGFE!+%5&Pj7B|-)fXsFCr*hwPyO^L*q5uw+xN%xvpwStkoLv z{m^)1`?jI+x^)FruY_VNFl;UlrJAvFPCur$YMa&YHLx3pvguqWPl(znc#u(Kk-=Ygn z4XZ$HJX(}o5Q%a4{mx}qhFgberD2~`%L1_HyPEJQ5aaki(=mn?dnPk#ak(n{7+$UoM&mQtYA@yfpM?30&#B+b&ZznAo53%o#W z;9ot0WNF%~0v}ayep^42*M^T^qsMZFpy{IbK*h5<1C$#ILSnOXG*)Pwl(%RHpFBLb zuy{MC@Gurl6*dM}FnIF^dnOtV48AoZS{NlUFzl9*iqpa)f0X=Obq03j3%)B(QNO8DjeUF&EpWW z5s>$Jk#*fObVM%paA(iYZd=o~CD2J>yr9fGrRYL)?bb$P+W^4)E``o!2uYWX!=%R- z`39n=Oj+kLt&-Z%m@$nHrh3`i&%v0PJWf?8k5e*4WI9iw?2`MY^UN)^Ox__fg0e^= za9}S+(d&zK-G@Sv=y`m05B!2bx;Ca_2Sv!Z**5-GJCj=sConUi*1$4nyVM;j3D_cz z{z=+eO{b%;dy8_lwm;T&^#Gew#D*>Gb`-a#VGFyR1^m!F&y1-Zd(XLuC`vmbHCn&b zG=kBLK)MI0OV=Hn%6)n(LI3PHZjh$i+LLCXb`BCn+Ox|Z42SgKn*_?EW{u4Thi@KD z6#U-P^b&(x#8hrI^Uk1`qZE7#u{PO*o+_&cDjLwH7~Mi24lZYuOqK;ec@h~uF{SNQOf)+%b`%tv;kI5T!@z! zbYb%U=<4FJ_)i_uKPfje%wSC|<15{~r!a7FN-7DES$q_3!mHP?o@en9I64vsaxhUF z^%|Nx*;QOSRU8g6f7}klgJoz00lV7@10Lguw#5ldh_TkSb|IE9;+&4`Se+P!;wFwH8%x|(C3pM-&>gE{sMLl=o3BZW!&?eBS}bhzhXM>Z_> zBaT4vp;DKu#z9~-{vtT-CvKQ$YGrrCicMzX;{%Z=fF zQSRLtmK|OIq+^F4St%MBnAmXJeOcs_E0_C#_EPa_kb$k3=5ghDSruQfkUrvqk~PiF*%Asi<$bWXNA-w0D|eyn)%cltu>4Dykc-d5~tPb`ZVTn&Z<`#Gau5Q;0S*TrJf0 z!4JY@C$21V!gR>^>jdfo+(!(1R#P@B>z&;>LI)sO?A&y+TuZ@#zw$4{8Y$<`mtaMm zL3*}|H!8CqDgte2K?VPH4!s;SJJ}Ci`dQ9?C`Uikip_mkJN6@PP=_Vfpz`#%3D3~f zM{)qN=JPC?HUG@A`tLhgx3(0$s*gVUXsVYtk2U{{3S%F_C-7ziyyxw9{>IeD4`3)Q zK^nOyV44!9xg*L38x$>@TF6^kOmgA^d1tTJS%^)CeV?M!5wqV~labkC{h*wRMsbJQ z>gV|rO|WKl(KtV*UJq~?!nuDEGn?~ftLYG+8{a2fkB7yd0K6>QOc|lUwdh-Gmhmkj zg+}-;;!U*T%!c|?YrpJ&!a)GEcFMSXA zGThol+(n?3QX@aVWP)K)(Zxi9j-WvoV3b*l^Jy8^xJT!gOJxqQPUYh_fF7Y=xD((c z*UAB~l-+Hdj{j3gv`V1O28eXj7DM5bVxE1S)m2I?1m%vN+s!QFHV$2?zp0kC3Y48m zV_7{I^%2y^96t1B>W-K_?V9rp~Cc6?FU z7*!L)KU*IH*9Yy@`p!CJYeiyjBV#SgkaY37-`a@+V{EhY9nl^3^6Jr(!)m6U92Q;z z6?E2g?p#be3p)ENjYitsN^2}br!b-VL(kD#4Jm4N*<8&U|IJsirs{MyolbuIZo@L} zRWO4VlnUoS9$0gq0u7TQdjgEm!)u;X-Rme)I(@7#Z}~FG(*9!j(Q#l6r|@4^Un1Vj zDqsuY%>{Wm4dL*gowp{`X3m8CQ0C-k&QfeJsc7S1UrFuez=`c^ZO!P?X(m*6Oq1>D&nv({E zV$&J;7(YLXBB_$m)jk2nXp`%^6t@P8$mBCvEUNLh)c+P&>83L$W93A&TROp-(yUs8 zm9a7Qgw|jrKW}+(BoK@Rk~a^vkm;_e?1{LP^R4zPZTda8``I370m#}Ou)lFuRZ_9( zUuzd~)thoVJ}9{up5?+Ao1!yVuEBT4zIo1{Iw+jHS;7!e#17!UI+X7=#`uHD8jv>d z_hhTKX(@ysgQS+Dmv`^8A6M*JxZ>;y&`hkW+n@?j$ykL8Q0vwfFu(tr4zBdzM-$dfav9JyA z8=5=w;LzOZWudue^dU6Zq43fDj8w3GJA57IS7Wmk`@P04K9w!_sn~hwv>p41euoxl zw49sIH2r1@dmeBbNYsaM56h#7KFY0ro-M)1;Y!12_2hAwgfJu03%`A;)lXm>AwNbh zt=5)`r?OVBVE`i4RD<+AA-BqJ_5CkSFHfGG9uh`aL(=yw(=-pU-@t-0zj0cF>Weia zP)28Os_zsXiXhf|3DrW9#%`?Beb9-I5GU;78fc#hxU+C+H=j@vPHWSqryvTd~=&mu=Z*d2cL&m>g##Uc5I5 z&a>0gRI5WdnY>JaCP+wG@sfumL1YZ;~QaXA%k#()6H0vP*PY$OSWILMyu#tJAXBs zFJ>;1!c$KaUCJ08i>%@xnt7X+Qm!Hn4X9*U!VurK#PLuR>3cJWs9a6_v5Wz@XhnU0|L^qo(EmcBF3VDT^u`T>)Ef7xUFO>ebC zK#dJSDWn>yZ7RA#pg9yf6ZAgCMxZ?Il44q&ds$5tlvB$-fU)FvtoCS8Hua;xH0wLF zC`*y7kOrjjjFxEZ^~*W0Rm!rMzshgqav{Yy_y8!LRS6uCwW54RuozBseuYY-mo%tM$v~Xi;X}~HTy4ETV zujSZ<|A+WJbOYZT_~!qi4K%T{Rou<$)twp7-PEOAu;BPHwIaIA6Sv5N0F5&15lbbz zC#gS0+5q|syj#TXQT?w}|Nk@q1?))Andx^E0-Qe%!cpmERD%D18mmE2V{iXLC!C%o z(RfGDQ~8J?Vn>;a6bf`Q#p-o#(CT%11tK;e5sOM99a2g>_&R1CX|l=!$;a$q_4?k8^VmFMc{#2ivCmcr%y_U&F! zexvge?o7L0a=O4Uxl7JO=#W`*Fa<)ZJhz!Bv)+v;a|D+vx#k#Oqnk|4;l#|MImT>~ z2e71ooJC~7DAqT-$t_^fHbSSPg-)LhX2X~Y$BAVj@)h4A>e={}*T4an4wXvC{ z;Zzs*K-fCObz0=Jfqo3+Kc%+&+IZ{He$)Rzi*^p2t%{E=vt{_J)V4DyO|yb{qUBEY zTXIUwEC3j*=BXL|JbU#ssC{YWd3U3++@#!cuT+M_?!Ug%1n$ALXB;ym?WDJ=FDtT; z$t*UH@8qM6%^~Z~=1}7X!g4}{;HK96Xj!mvlhsryip_s5|24Z|Wg(sjiDOHz)@<<& z6$4&VsauJ@NY3`FvT`I|D(%If$9t#P%-P?JgmYR)Y*Cu~3fmkz#=)bx_Jgu(_Fc13 z(0n)6D^U$QR6TaEW;|@H8S)~%>D+zM3FqIini>p7T{e&#>3TA-Mc_tSVbx!*V-6|k zIUbwdJ`gRnuW{O3WJs1O`LP0@#@L2``tXADJ7>qWZi% zI`1FOY>mAj!&^N+ zJ+T-ACz@(>1X})_!LkVH-~$Z|V_C~r0lV8P5hy0+6`kyee2|F;@yR~k>@i#oyUwG4 zdjtP@p2dkx-?*dvd3KQ*Lv0vZyK2Ln?J&Ps3C`$ev_i;egAyLJ$_tl%_+bh&;|hBr zJ7&X_SUpx?s$k(@8Sq-e_5t_Cmdvg*r1x{vKpGX zsjv3C32ESukxoTjtbQX=$j%5rI`zi5>;e^qNinTAOF zbu)aiW$q*+?hcTaw1O$F!oVy>^K)_DA+N;;+6dDu_3bMKe4+u2%R=XL9z`iVB)axZQXQ=galBUN&H zf1f5VYi{^7>6nopdTGbHdJrwsF4(=}D%+ShK$SbJ5<4)j2G2b~dr$ntdmvY0MaY${ z641ts5nHR1kwoFa6&cZT@M}i2j5;^KdzYJ#6sss5ieGw(F2FWQIo5uvaX1VUd$_D# z6%6Kgu2ygpL!Mc(&Qqwdf`AbxS*g==Fin_ew?^@02a;x;os z(4k}|Z+eX?pM}18?g}*|T`r(<0#BEFQ00(~dG0*@GW58eUwFlYTfPp+yb{_pJOIYO zQHa@8gO2>^sT}WoK`BJ+W5aQlxsN<3*nbbSDrss@D)AmKaiS8Xsm;$y{Ie3JsXe1a zU4#PsJtaz0Ta<-!_ueE*Q+r;C4|yd|BGFllCz(5v&R{>Bf)J=D&wVwCA|sa?tLfh= z+mbxgY8r+ipeDAZVePnv=I0x>>|h_Fs0WrfTvj>M-(g zu~uN`*DLW{)RbDR+2;I8pETFIQtS-Pd}=Z~12cs+K2AA^y~3J&&OUs)^Z4c7#eX<| zboT_4S?&G9NU85me+s)V3gFSB=(>(6SwWo7kc#f-D)0wCnK8^egtBic@fs4jU~)JY zOb+GV$02Fw(e#xptMO5aN%^`%Eiltm-in|`G5fvAW1%xwa8xI7Vq5a2`*2!ilM|D_ zR4!=d@R=(4cVD!{`O4AbKwe8PK9~yrLYng>geF;gjS69*%X3xs;!N2SDQj*f6qC%^STqbbC~bn%+fSpet5nyqPCh9JH;*{= zq@_xp=)--UBa-FGzM1~w4~l=5^TrYW$ofijrIOjKnV;vRJuX!}T`B03WtE!CELBI)yAs~;;lN9t$L z7Wt#b4&khQhE6bWhlEbmZ+2nQk1;NvCEtAVA^Kj=jU)+4(SQ_h@0_>U7Y|ES^1M7w z-BTb@@l99zff6B2Xep&5zUyvRt*g3*lFgL&co?TMb{>=5kbgN;%QgtfrNqjgKg{H&5Ulmm^?v znE{Fy@WcEns+rq0eoDbGR=K7}NW(g4OPh~hTHY`VpTWk3ByPlKf3S&TP{Xq~`l>Ou z%bK~V)=}!wes}jxS)P-|UT}_L`#^bjVk=KyT|0f0_v58P`Oa&{b z3BeMWVC|{BbFbjwnmJPWcG(s$>*+v`TeBE0rzJt&U4OZ8f_8gA&wQLx9Pec`YR?C8_V@K9TMaAtFONoT z3q74*?WO#Ol;FSeeSV#OpwF*OB*O~a1%peu_Jzkaj9JS0iq?hni7t#;*&4J-$P3Py z*v6lVf2x}hOuJH$n}#qALe_1qb%*f2MKON#A-iG8b}_^rcA_8@oI4Tw*PFrSFT2t< z-*HlI;#u4`ZV5LU|CO)c*R|w$uBEfT&x8A|Yx@~8C# zt7jsyOd93P7R7FOKaVVTYkLmX{fi(^oaINm#=8s}UQpLN`w&`pP89|#_CtF18FEe= zH;2h+u6>9p%{0%qTSGaxdoBUMB{o#G8Sv8|LDZ}7wcn7T0ZaM^=yy&mCbYTU7r&v>t-%QDVwM+cExx-6SBUa}HpjDr=1wo~4j%IhSPK4) z4{y7Cf_%vF=XwVTVB(*=AmY#voq9mG3Q=< zYxgB}Ru*7snldlLh}7W!ZE!kS*Yu=?k@!=dfyua~7m{L5N*mYe;9kD12bg_&4UR(# zL8yQAawf9vs?}E0fhN8fM^4(_PVM&5TTT8t@t+L@Ofb0l~R5YSkd1!(+74H{Jns&D>C=hQ%^q zcRs5k+}Z-yr^42dcO#vrmV=gUTZdq4Q6CY39+Sls(HC#dS`s0F88p;2BKq^x8dCRJfZ>^5)BV?#&;n~5uF=Gjf^I%R=7{@= z!x%tW`86$~Tk4VBx?C-?tsx((M-gktreTdQMyL9{nM`j&2fbDybWx?rr;^y38eL%zMGcv3(-#99Y5 z(=*B<hPuUbud!ck826FGoMb8>n7tikb%YRrI4up6%6^5r;lANH(ti80;J8ri+Dn|&{k*~5FRhVgsb^tlg0&k>uoJ(kh!RT_?+k`i zv&Im^8!&HbMe?~Mctx-#K9#D9^SNwVR!&T^vhg(tr4PUyJ=bf)8K#x^Mw%HJ$zM^^ z9kA{mSQIW-R|c4d9-b+5M^2&MUZEa#^-UnQ6T#KdH+`k%&wKew&8s@Szy_oS`+7L) zHrH{eR|hACi0j*z^F-Vv2S513Ydz#15+&mF#MLzJp8I6`o}dc77DBB zOqF9rnDZlwjMlsHYYAdlS^Z)+V zJ~Nph?c2Ve_n!~UIcMMZ+H0@9*4k^YU1?=y+s$+p6kOw7yR{-(W0T=7uWV=bxQ4mw zl$75JO;U$fLccNQr4rfP05nAf73{+q%@hkI3(&_ktZ@#GaQ+3-8l1J71Z)j85iSN! zHc<1G22`Vk*P4BdYU;E=h>7jJI&T$~LD^ASA?p^hQ5ine9_mJ{^GYu;>O){>kCs z^#mIZf_r4ZOQoeV700#C?2OUW)HIedi-R{FnH|X8I83lmMG6bM>c`hh^^8gJOh8o&9|gB*&k)wuHc+I9N4=F71+hEZ!-8GHu1Ec>M8y` z3+yA0_}9QzK?1fi%SdM%&wNN5>mAPf5Zsqxj}d&uC_4FQ1ULDqKdc57STZsYHGBaM zAP1nQ*V25z_?=O%ZK#~Lr@FBHKtrDzYp7$IdX*l01IaLz(c7+4 z)9SnVPu5h5P_ks(o?6d$8h;(RkdS(GEFmKGvw^9Fro!9cQ&f)rT%z!phT$k$nEZmX#P0MvQKUw(F+omSW;l~E}sW7xv zp;i27Ed7G3Z1ri(l>W)$C4+woAV?2WGzlH~bNdWDork_Qyw5OkbukAA%eeM|L(+r& zHNXCq`}4iu?FeRB1ssEJW-*wI4or%^%;Aly#C_l=1cx6=fiwNdnbJu_(a<0{ae(&6 z>4(9=_xubBqYn#PhC25xvE-6Uj-2X3tY1kF65pBLMv>_L32h!7OI}?W++Y!z^jj); zA_WH{KGB^#{Svzv+#r&F9~&0Zh0SAP$qTrC>yco(&9m3jsegG9+k(Ba{oDN_w!NcC z$gTFVKVp=13!5utIFQbiR*{dE2iKoK8!y;<0LXP6YFlcM@%Gs*8g|Us?`rECvY3l9 zdW%z45($%|lF_XL_Rs+C)9j%YXAXa6IMe&kAsY4GlGJ6%eG9lofYU14-GIy?tzFH9 zS2U(KQ~GPt(yFslr;^OZ8x1Gyiqp-{jED+FvU^11S<_|q&c2!GRwzi#QxVcWC4>jP zn%_KXKrDNfgx_W1cSPUe*`GX3ao(w)#B3TcgN2!@(dtKX9y(cfvbz2`B-t9gr7Czn z#o8C%CRJg$_ZN1{^Ok_gw~HRzviW*&n-Y_$39gRo$zCl9Z$Z)ZQn!##`1AJ(?vf>v zT}JZs_FxUkxc{W>?Lp=SkHL8irQqO2MH9L~)t-;cJI+v};wXT3gnNqN{0Zv9hK{p! zBk$U)qp48svdgUw3Tt27UwazXkJ@Xj_TBWOqKuzF7(4+k+8g;%k$LhXI%&AtkHDLT z&kn#F-F>z>G@QL|SU8f0Fub%XNYeLkpT#RVOJ2iHi_@RQ@%x*f263oGDQg7BE$`0Oz~HiO1z zkCONaFcIBoI3Ck`qF<@&D2NpXnQa=v#{spTvN;j4tjIj2t9rQN7B?IVnM0{(X}ulD z2!2LKIICj^cEE97trA_20M0jQPpmPD;h*{&Wk%q0Sln@i;wqGC%xUa5o3kakOY>7# zXpVxv5JY4!U8N))SaK|))k}!t>%h>5{5M7RH|7Z^OeUw$nAd4cuTS~gtTEcv zK9e~=)Q_V_cWszil3p$-6$e)X$<$MZ!83}}?QVWmk}-JxHsn<;keD`pZU~Fj;aa>z z|A7%$4a4tXW2kZg8hb}>VJnF7F zTl=XW6A-%CsOBB5eu(T%cQ3`M=&xXrwME;!#gWI=gZurB5rORicgxB4;73H{*#b~~ zc>mtPYADKTH8{7tT}o=BD3J88@-AGv|JdEvGq-*Qb%>EF<4nIB@T!(xM139cUN-h z$;k!Ha3rTgoZ!PA!H?etq# zoLeRLRw=BK|8oqhWbKG>m301I=gATyF|*h|8btkY4;;x?KchLXJ7Ks{EbA7*4xuOJ zfZj63&|2mvWRx$ZSoP!0i~Zyj=#gf`F28LfTRuINzT94Z0Cu1O%k0&BS}a++xq*!z zT;-KFQg97^4%z#$lf$;r&%EgqZKxD&s9{%H#8T9!kr=#YbW?JrpS-Lxii#mxy}6}} zSz_M8W|RcS8_L?HPfQ-orhxron$Te8^GG)DEio%OzFHqOpro6bY4}WYjV~%B(97=e4D4~ z@T-c&sAf-vofj6SgLSq%sIUD7w#GG&PCv>`G~vUtH*Yg2VS~d7gPp-92Q%5C zyao9a89H1+%Qh$-?16x*P|+I|HjKi87csMS{HODOr`w0_>Lf)hvG#f%YWZOe$@4jr zeeB`_dIm?`SI|5x$`QcY%u~^}4IEo}OxOL`86)^B+L9@qP12P!=1(bV*{dJ=QwBA^ z3u-p%HcWDGTP(=RU0>l8N}Zn&FQ&f%`=Qcy`tc{x`l zd~Pd&v_C?Yx!3c4b3s8@fm;SfjaMY&i!Zatg?Sn z-^N+v_R4O+6=M1{_rT6shdU33#YESx7F##r{oqNpjnfF7vQyJOCDkznZ!hoS$?YBd z<|JIV7bnOcIL|!pH-AN^)I}7yU+=k0>F5lt?pFjwltk32Yg$e{^Fv?d^b}EQnTbhPonU5PwWT74D2M6Md+ooPFD2a`qZ=1y(d%@GK zoH>8xoaQM-+JmcMcVC?8@a`?rooy%YW?NN>?ubiF@G({*t!;z&%%+tZ9>xn0cFtVDV?^i7OV96}xv;R1kNrAl zHfz(Nm6p7Rfp-gGqdI3^#-pTj=H(k#D59`;#WEh6;556#UN^2-!E-Np^{tSKD-yH{ zP;^5RitJwJa&?eqOpF^u2^-zHJV{u6!7Fv0KUX=c2K|1}QL*wdo!6-Xd*8eBx@)rU z`*dEnJo`Sj^SU< z-#X@71AKiGjY-`qBI6UnWb!&xvLIID$IF~$(tPw{^UGGYn~Ho&k=OALC0VvXlOmry zBq!Z`j~P}@cpgR+!89m$pi0GRzEB zYQ0hdz9!lfOPs%hsbQ{rg%x+B>XqwW@-;vWh3)F7C5>Ob1-GHwV(}$GEPer#`s-wK z$dt{dv^I+gucEl%fsab*ZhLu`wLr2|yq1kbXV}6~WA+kB20Q!_oRzv!WPGP{nWv)G4AK>;`5qCJX+*|GY7NnlB7_>`%qPe^x_*{grIpoI-J-*)5QWz2JRk(!PFdZn0 zTh8$lD5jLYD)=R3q-T@VI*ZPksIaKf6qOs1<|A1KG5hLo6eqiPZ_qguriJ3XL&g6T z?wS8F`YeS--T~I&RQd(oT_f5r#^w*7bxY0hv3LXvOICtj$q5i%mK>PvOzF*i5f+ji zeCCr~j( z1bjRf#ksy#l!|ELGL{L{i`dZsXxQy&f-6&O`Xo+)s)4bsS1nMrZpiAqy06HO-=iy| zm$DRj)Ntk!e$Lj<;k`e3k;6*OFP-l_|JU=qtvu5&bExCi@>T%k-8H9$M30~Eed!_Z zuG!i5bsXX-J?Iw8NZj*$Z{?ZZOhyFuDTCO{e~8P`0;*0KgXVQSMR8mrBD9WZ0oDuD zT2i)c;i3QJWga2mGdw;okWM4hGw7-j?irBuD{AxsL}uXAgk^v`V;U6#aAq`d!Q6b_ zOVA7K6Q6NmeD=k4onH}I_c~`>TGx5eLR&!~ zc$2_dmY8!PuJ$jkgCy<4cS-6^m>N-l!$8_B&*^oP+Lz-ygPpC+a8U@*YEVmY&7TIP z?j#rP3$lgJ7OPr#6?X5lT{WriC;>NS11*M^iz&R*1c zQFHvEo;ON5PiyH(m37WuY$thyB`(2|&Wo0Y2`;k)my0qKAJi|j=j16T?_x5Kzu$g& zcmHVHRp<-GLy~f%kU9Kj@Q0DDex-XcNNk5cJl_&F}V*Z`d2IpF;x6^O9 zQk~p;fC@wIU3efAFUsvPP7nxxbM1pJ;GUcIU0ntb+432*>)gZ2@uMpu!I2csOHFW` zMmc#!Q}8oDfM6otr66An4CR@hzZcnqJmZ@@lm3~>F>NGC|JYC5t92;XtIXjSDJ!P< zBc-oUo%d+gC zi0a}V{Bwv z4#}^1?KU6~j`J5TbK{IkEgvDyw032692#%s368hayN&lzq}6y|^C{r*ISz&oXkc!< z_uRe^A9qv0JoAq5F{gl8-QpVgNPjPOSHOJ7l9&QU`0X(9dKE*Q$R`KXvzM5bC)y1#7Pmj*!Jv=&ZTE}lq=bH8Q(ao;t~ymeK})Gh7*2?-zA#=9dKrtqBuVR_Vq#!e6lDJJ z^t4M8V@SF(*7278rbuCqS(T?9v8d8`E(mH&2WXc^T#We`VI{#aNFiLwP>um1cfrhu zZv$fFD5zbzywx7YD%W~<-6VEfjG;CT&ySy3UYodS8A0-mdRDh2j^3Fiw2!TqnPof* zI%gi)IfrwIR2??|cRyij-m5tNFqx81mW~tDUNpVfYvpatw>!fbw&tZ()mD9zJz3VI z>|$Lwoka)es5QZvCOETAE&^U7)fc*P5-6XbsV)9idqS(#qKe?BL)3A0$#%=a?!p<` zV#wN30}pY$z~hqnwQxeUE9Wf2iCMGbfcB&Do$ci>k)re3@|X3vvHTSt+Jo)3=dI3R z5q1%q`KE}=YdUW)-=d`L)QhN37nz`V7!ng6^bg>%9AT!fg zRnw`8IdM#EpH|B-1srCY2gCIK>R*yDZDP2;Kz?C z56)w=R9XouR2!*i61E2?|KcOtC|DqUKUPrd2MMoLoOv(XO7nI^Gwi`!r+tOBB}Aa_ ze=Fj){|X2*<{C6+SsioB-%)u#u$oN3NCVFg^UJkQQ$ur310@t$TVaKb%oetRY_be= zlhJ1jk`iQ?(n>mS3??MfgT%B+g0vIAkRjaP?m!FmZcn~Rs|?eIzoZ38lkHsmw@M30 zJ9DHE1(Ad*1Mrn)caNfUU9l2+Cc=?#NpFX$)Sp2sxC{v(ee1yXU?^jFUUEx$7YT*; zH54hlH>xn+(Tv=Q<}vj?-jF!V6^2)Y=`d7t`ToWH8*>Iuj~g(ntf$XkD9CSGI29kp z9yG_xaj7*l2iv)zXGPqtBG7y(m^|Ndd4lN#r#f5)SxYP^Xzr5ex-?U| z;thD8ZRRJ|yJn`>;yyPs{-Rp%EF)*Vcfx z0(SV->y~h}j4jaN-7{C5!IU?ZFW-6KitU@syly*YW(4dJS8o6TLbyutTmrriAsA zgW-Dn`mYG;Yl!cPYD)qkw2#9@RYj0j$cb5^uccRm|FrM9^N z7ggzQL5u79%k;~67?I;Ao$1RGE|hy;=KFXO|0H|;&DSG)Vy}@4LiQRw$!B}dC4F99 zdL6(oYAG=N0EbXzM~KA$>_Rc^)|YSQjpHDjoGI2GN6%|T_bYP3^9_met>MOJ`ZXIe zHbg_R4J6s<>-%nuo&^a?! zyK!cN)V+iGuwx2fA?uS3TBlpygq~wsTj^4WV7=1$Re)t~*C}~RxgwG9cksW@u3G~~ zK=@2)HLZ=s|0uS4?J9mUr3btAbCIYR|9cc@v}b*$bflt8;~}KUTdl(r%8jcE;U)(2 zI~Z#udPQCi^SB#n-1}Q*s^He0MuLLRbh03ME9q z0hSh|tk@*Rz0o`KA@B5c-o{P0cpD#FY8-$4h%fuKI5}2VOT3N?Vfn31wK`AN{AF5{ zL-%?d&?!}_jzn0^P;)`*FMyODHy4GYRAQkA04IiTuzl#T?jH(M=kwqKjz1J)())Sd zCp?jAqL)X@b#n2O23=Q!U6URuQyK~Eei}P3ON1KmFr#SDPF3MuL%#{9RJM$(n zqehknaURPysM5UogGRdYCTXNg@BaHi1#65Uo3#epcL^dy-b-k3)9VCmvDSVZOHM%x z(py}+a)oN?tScWTw>y4VCoI*jOz9V>widxylY6SJbn!Y0$*zR+YTedc7%ZPVa%h`BTqoV00PItgE7*J+d88yAPEmE+yi=a&DHeP|4*Dm8=b8~*| zQv_LB=!hEjl$on*G0iLG{mfHg2|vu1@B#%Q4)tdRlyM>@oR&zQjc8hcbht0&r@>Z} zl7{$K&BF!C+hMhx^)cS@o3V*g-ItEviq+D{PIoCq(uqQ; z=k}2Dg8r23No2MIv#L}t2_>5OsbA;~=r*N(z*{siH1%selQYXwKjFm@Ie{stY3rwY z7H~WPvV}zWb6^Sf8=|3?IsDXZTv?czQIoC=9t8*T6TQnEO^*uha>40giX){k{~4mh z!>=t4ai@O<48Z^Sq|4ebu+hIWIPOr}vK`sh_XV$G9;2uQM`HTj?EH}w{^CW}mKcEA zgrpAxFV?Ay*J$EkwHUUD8}MEVzQYVh1FC6toQmn45zeU@np0l;Q3{mPm>KifyYG(n zyfP$K^p(a~@^-849xZZc3=teMr8m47!B5}U_zwJ?TS8YLT7~r28m-1N#tz<8eTlV` zTr@J;w!yfzKH~1#!jBbJM_~eu_y9lAre#MGQ3b_u^F^Om($+-~C^aikGTB>dG0e_r zT$>D{1iChlu02FjKj}{Dg;ii-vi$`Ts4>CUVTm**%zA-(z^tFN@GmcF)<5|VZ9sja zd}MF|=a^E%1(@X2pAi~#z09mv^Yd&KL`K|@MDHg?f34ivN|s-)dr9R@Cg++4f`c9` z$5J|~-Jg6vHD}73^_LbZkF)w%xsHM+TI?a^^%P?Tj8fY8Y+49{kpz zt)bc=0FW7vD$S#GC!VcGza>-?YViU5BDYWFMC&E0iWmcweM%yx_e|#RO!L>X4NW`?m*ybt2kqS)dX)t#=ze&9- zZl*P&U+Krf>DcF{V{fZsh7ohN-5hE5Qcgb&hdaPN@K71_IW4QCz4{ z|HkuWnlEWC%Z7lPiJ;WyN)a?!oT<+-I&?bfH=m;fCQjX85cpFda6T}2M;>F9hE`lr z>F#UIGvHiBUI+M0>8)1I&fue>{=Fa-W%=VD2Dj3H@PNRi+YJcz{dDK3PgQv_ToBJHpw*Pb6vjaZmK!)b^Up>XO%kV4*kSPs!w>4}<#VV+pdnC5m86&&iD8Jz8tm~~&|!HiUM61t zLI^{sMRt=AR3)K<#I|X#oo8ZweJ)TbO)6!+B%emHQ1f*8?}f9IgMsEYkY^;wb4;PX z?15WU^|JL{)`Bgl;_A4+xOUktoAy+qq=y@M|JV9 zX&hEt{=x_R!Yx$PvRX(BG;MG&1`h6crmP@!x{(PD@eqBsiiy!|(;Aif8DF8I&dL%h zfQZ3fIy`6+4ymNDYdKUMgCRcxp}W7rgR< z60A>;lHQSs#$c$f`pY8G2lyI5>A1$FawvJm(?+U-M|K@p5Zp>kmhAS^pT^vZ@bwaL z46+yx&sXvNNU)IRgI@pUd{xO)5y+A#>K@NmNuL`WLn*tTuiEk#w`r4DUn0inC_&}O)vO5CTb2dp+O1Fe`>8*M@P}vcRb_S>E?P0zBuhR7d zFFvnP{CDeRE45_T%Vyq{`OhC6m>c`tddYfAV|G6;QJa^2QkXrtTXw5o<6~lC9xx#D zqw4k_2#nl-z5awXCOb~^SZioo_H#UwH1G2bj$Y@D+Vj9B9JQt=45`z<$V+|S;|lx4 zxFpL4HYK=GAkL1|ROjk6gt&l)rGW)mt|z!jr8BOj93hZJKT!Qq$JqVbQy9lKvVPP4Qs zi`FrsENPn7l_;Ad{726`EO-(^trJhAAPM@RX#615h_*p3B2EY4t)0PGG&z%18C{o1 zafxj9Q5v9;8Owhz{}c6%D89l^9{Buzg51b&`-RWyT1Hxzm{r+UHO#waI^$%n$HLHg zX#`3ta=vXZYc?WfVBNVnQjj{3MmS=08*iz>8s$V&<(~Q?hkmAA7|IZ{ z@UY*HA2TdyKG2V^%RA$jckF&b_u1jmpi0UM-d{MTAWI+iFu%Vt08I+sc{&71 z|9ZtSlBME<9js+`vA^ zyawDu`0lznKbWtq_tG-`wVslk@|Ip=xV?byj26Kl2)kQJWqK3tfuL-b_y$@vFPfnd z(WFifGMB6-DMxVcx@?2so41&ANy%^4m{Q`V?M^8%Hh5wecHje9%ig(R3${&9i6pkXE*k3F4unYAW_(k8F zwaLz`wEvN)+WXNI6~wAj9A<%8*zS*i)()<`iU982u#WU>ownn5P-h+eaKv;vqu7_i(dlvK_V{JC zddR_w-R~KgHTNnz<$__cY_Hy(#3t+m6P`~$S$n@~Ncmv;s>0NKBZFJgzcZjfpLBlR z?9n)>1F1cFU%%&#!2|Zd7atXlQHk0NDZ5jQcs@_G2OiKf;58x3HvT4+?(99TaS#jMsK6`LEj_BDdX63bv#!6 zPp|#YVZ^B_{(-i>FM92A$`bBrwA*?CGHK_&-RXf7CcU-WPOHU8Exm<0;ZW*9!glj+ zf(z}4-nHJCJ8j-YEqG@$B`hjlUxraZygtBB_72Sw-uu+d4K%y0?^v%rO&0mj-%Mmd z%NWJoW^rHe+9eeE$$M4xgzmPFzo`2XAS&0IMNrsg846aoeVQi6?y-rpfJnC6v2waz zbtW1tR~PRtf}Y-%7h$X`OGr0i*%iV}zg6G@iSpEOZFwuv9I9uwVN)JIja{N56!`e7 zoDuqcMGsNos#NkWCC7kDBZqzh1FPBdIF~=)=_#bH^lofZw^=fT2{~kz3D~5=R@+K+ z-(THIR37+!LhCcb&8TGd((T%CpK1)}Z43_LaRsGXZsTaT!$!5e1){0Cwo=3;z5@|L zUDjW#A8!+UQ%fLQhChr_dw28RdSAOgo5Iyo%GuY28Fd)*e_t_EBYln5y$Tu9{E;0M znU|pEwM{oUg=Gkg;RtUd$A+|bW+fV;f{imLVr+XeZ-u6cbG9hK?+$f1e-=io**1^J zwOJkm44#hh8D8T%WI;nN}Uv%=JLtG~p4(;#c5oOtdc>xwXH!xu*{~bpB>+UZ68{OSQ zCv%vR8VQPor&GfoUl#ykQ0mOLpx?*-8TvhdzRd%Y*kjuk+ut1nD|WUl*hpX6`ldA> zB$dhC_Hh)RZuq#}K92S}CNTGM;jh6ZrqwJv|e$cvz6U5)LIZz?%o$R3>k+kc{B9hjntf0MX*D!B9H(QKn zr-t3>nmlXk3K?{dnZ+*7T&Uk!P``8M)0mN+bC+U1RvEmlS_=Q+_tvvVtxnY=u}T1j zf<;Uwh4dnrUS6D~%K3C_5Oh-*Hh^wU{pZhY8}OJd?C&*FXz1+@n1+7+< z(M<-Z7Dw3()c8U-MxvT@0+R44l0|6^3IS_`>uoSQ%`}1kHi=sOH-Z57pT6Y(O$Bef z;pSnkY^zP;1*Ef5`r@+Sd|fPsx_Ng1q>eS2;Ps#$yo3oJN&5;?vqij<^}A46y->Ur zZO7PUej(!jmJHi>Gs;qviOCJZ)bjy_|DiW4g!I!N^u6fyKh(Cqys)t-(~UjR!pA~JYeob-GxJFF#PC04ia(&Y?~xiCYE>2(NONG zT0Bj-^4nf0NR>%UNlv}vX{6G-zK1R=IrVzI|C)DamX$h@Bzf?RC8lAdS<;aBMVC5R z{a3Gjgc?c30QWYkX>fg$`?Me0bN{g8SC#uyqbh*0AF-ftnj61{JUL* zHpE>qaVtjR7T!s#pIcPXI%U6ZoETL<*}T7Z=g9q56}CUP^umE%UXOe3x0?TozyELj z1&%5HB1bucwf7?~Q-5rEJmJ|8z}i5t`jKRy-#Oy22O74VLi~+O7lT2oOOfIrV_W%-dw?acV+} zYu9T!AC334Z9kX;qk~VZZQC|&%GSlxygN@nv9Pvh+t9X`rqvB!H)Y4dYVXdv69=_z zAJ(&d@1B>2l5Y6s;hWOm_S!F_V*u-cCk3oWgYVoorl4p0(7NG0+{L1n^lTqF{Gm8r zW_Zu7dUf>Yqui1b{7uG|PIGSuU-*b4%;q--dR-KYe+7tN6k%3W?1lri_vLVY;c^?v+WU<2ZTo9MN}~%T z*VE%*<97|=f28+&cy|b&UfEBdw$LX0e%u=|0ss zvWtLx9gnUOahuE#Gpsra5rmi;1jo57r%s=?u=@ppyQsHlUKhFWh8ld39Hx zB}=21Nx~Y{^n;gljn(AGBxv1XBT&1#nsgKJh={rsoO$MP@MQ0`ytl1K3vu|cyCb|_ zfwvvpYE0&gC*gKD^$kA(cr)Fp@0b)Yb-6M=i!~#Kd|B_0=$&27OnpsiIRd$RkEAZ= z-8;CB064cUDwpOr66@EkaW{R<_*5L39j zz*egbT$F-?(Rh!|GM-@p;+60W3?%!=L=pI<@j4Df+Oq`Gw#1(%I5msmY3sYfYyXrn z*Wd8keO}bm+t}c(m8#-w@0&doQrq@1B4`t_YJ=J*0dgT7()lHQ>l~KMMC2gY*K<6^ zku$ymo>z11YqL9OY4GZuffpZY}c zNDuz1D)a-np_K!d+f@BzM|-^XQVk4sb}7`I8S;Av7b{aZ_b_7#Glc{z71!1l%~k29 zDw5c?}spfbKw^^UuK6+7+Jyi49}?cI#^_;mA{L@~uYGy^cy|s9Jn8RR&K!gmUB{3khiq z!>AZXH{#ETROCnuBZ>d1gJ@d1io)NUS=pckLRnZWNzj%@c9iSgxn-1>e*AOL-^*ax z4?)<{dObke{7-tK+f?YxX&cx`TIgp9?7!`Nm}PS>S*HD)ELl3-TS$l4;LCy{6Bh7w zE}92`LIh`Zx;t53uDKJAkQh&wijnA_McFlm+hbBULPP(f<_H0usTC?n&DHFj&&qjJ z;a(F{5OcJ7mb#K;j-3*U2K%3Z*#3%T{V$;W8>|&l!&zDWO*20Urz0OFm+Wux=M1er z&g=NCRqW^$r}(tLItVBJ{{^{T&qm(AA=j(vqyAdiNntYx2PCMqMH>G7D$~!`z|SO0 z7xxWZYG`U^yX(X`UWYqDkwuDKK@-05{M^|-?RLN@XnJV&_gUS8xFm%ghEr|bYzmh* zN7FdNLnE{2ik;qHx-O$%ti!(O6yF7aPH>WHM&F`me)T&XbG(}>^w+8zI;uC(Sa11i zsCFK=X1(g#Ij~om?Ycu|zUW^fv|=!{S%R*KrFQ_SLe0bu_1p*yCA!{!MP28jiaKjP zX?)vSnuWl1*3#@m2!gGUs!?XU5E8F_xsfXMtYQBh&QOpY7W7hi=d+R8KM$$6yd?Fc z5jnTU<&YcIY3L8&iUaRadRbfMpW#vOq#VvzPmTk{_xEPZtdQwQzWIt9Blkq6`Re~HMy z`NzN^H$)b59MZI)`8iZeXIY5M)xWAw2CqNCX$l!1-1a9lQU~WpoTP}7;G2pVnjW0J z>N%u(!G|w^fasIH{WMbA|BxtO+I99#X2ZU_|M$DLslUzz{EAo1zAF6!(w`VyT)<-_ zzE&xMi!3gaenT)mPL&MFuSENudoDks(GSaid5`5QMR1YDh2=l`6y^U6gvxbdfV?TT z2NSK`+CJE~rDiH_o!Im0PJg=X zgGlp1Z68#&95c0`(uE!5-Pw&aR)67ZnU#Z!)1LeMjQiXi+*a7q-Vpyt)nl`Q;Bc7N zG1ch&udNLUHXTuHbC_29eyESNmcx9@6rjXVlP}Ibz8)M!>z#_C)@Whv{qzB_sMF+a z*EuoHx2mRtsYyz@Oz;o1jcp%u-Vn+(h6s67q5D48L-vfo6 z_q$Lt!=1u%VeopUwEmY7CRb7VFw@sCL)qFhFUoT{hE6Z`Lxx*jD+~6<)iX*Qo0U&@GhbSGQQdo>mX~OJz3)U)8lW zUYqUXleyS$TdK2RJ;p&{4kmp;{JG$;XInaVA_7AD!!S39E>r9p_4VVf{Oo23>BA!N$xA(L^aka4kkQ>pwdL?q76)}^LX{+ zX~bG_o_5qQp)IGxG3BXKO|^u7OtQOvCSV5`sZ8kwKaVhd=w~#({LfU!Yej8*`CoXg zjsI{H4=BEnc7PD$#wa*+i}sypuJJ51j3KD)U7=iplvZL}aK48im%fX+wf%19R_u1z z^z&$nm<^l#Ffd)@5d$N1M|M05bBr-_oA{a47)_!YNB9vA+Hg|~dcC}gQuDx9Kj|3B z{sy6ZC8=v$*kLC)+-s-j#)X-TOU(5mFO#1zq461)srQ;}zf$IhFgfXuH^G47Ze^mjR&(0X6_|I%>07s}P8$lA?%sy-C;EI#d)TQ)r7IW+D#)kxr^Csw?a zv0Vz)38?4`j6$7#V2$Qeb&ln3I}_J0Z=OHXnwdVKjynbd{Cw)`}ELppP& z5jIU0^^yd>a`K;H$@czrmL<#bd+eHFR$k(cqMLqxIlYYcEKBJC(;7y+z?*>s{O8(s z4ffiu(-0&Uq8?w2vI1A3_>h~bgw}9x#^bU__~+n7To$ZLpy$-+5W7-UHcUQQu90>I zvaQUX)ruBf<&IynPnId2@RNv62+1uzo37aCCp!xY-p-_d*tU){MMFNFGS>WhI}^19 zS&WV0^mp4f5?4CZ;wJvV?(%#t^O~PLttxez0jDTsj!Cq?tozaZ#6GE$Y>u8*mYTqe z7GA(J?kC3D24f!vOzB@{!Ia});T;Cpqju4a;JZ&zV7_@|OTQKCi!%n4F<`x}g7o3Y zMZ3bb8%SBL@_}Qk-@5HmmSA!BHAY)T2+aL%Qe+)Y#i|3Z;|WU8JiR`gnz;mU70em2 ziYw8HHzZDUyH=QIRxj_O6t8`)vJ(H$%UVy5^*a8*AS<96MWYU!xBoZ{sIUNR_wAh3+>)U(@NJ1KUvQa zVJ2Xo0C?HSpm> zZ?4brvLEMEjxwE9T5vZcQ{SbW*YayH}PRa9U%W!RV1wK@A`m2J}(3rAHg4Vr>c zvKuvVy4^&{Bt{ve_T8o@#Wm3txmjCaPgEy*l3ox~`WkyWb*|EjxGTM>ec4us=sU|v zlfEk~UtLxD$}oM6Jt>E0ZS~XM3Oj6{T`eGyFc9*#q_V6{lx1y_idt{kbCfNMe2oAth3 zG+s)h{I*?Oj_n-Taa$5q5;EB9I8AYhOA#OzVM$!*m2e|($!8;M#=ykSE~7E9zx{eB zJKQ3ueJzP!m^w-KP^j6dxkBC2aV4WKK z-!-9|Qa*v$d4uD}N5oI7qW1J*GLi zhd%J{P%^5yaLo@wPOqEXsf*QRoDZ_R=vyfk9hM$1T~_|+F80#g#<4z&(@R+0Uk@gI zkIGn1A6dVL^G&*u#!6hRf#R<Cfyu1=+9&lp2h#UOvG87 z)ayp~Jq#+)O*Y3Jz`4v#P9drn4glkn_NC{YGI#G2j3wHd|`oCH*+IW<9~) zY=HE$^ROfkC%m-}Uif1go?k<1{{M;z+h~!Lzco0E0P!$yuiLG6`TpGC6o$2~Ryzwg zA<&j@B$e7o$76}EF5+~EfU`tnFl_#N%NRU*4M@p3f=p2HS58rDK%FY5wyD)Ah3T z@rP>h&Bm=nvG@!1-knF+x4*UY*oOGa4IFJA|8}&nw?0|Ven#JrMd#z-E?WIC4%;>~ z6#kXBmIqTmpw9aE+qG@4A57+P<+W`uPpfVHc$s8M2@lEzBi`%U zKh&fv?Dg(ExczxA{uW=IkHbrEJJ`E(qSyA?wAK%|EI>pkPwiE z^xXrIKE0}WEy()$-w4UsI)Fla5S)I~n1aq8gB;~e936u0Xg*FKJpgvFUU!}|`V+uQ zw`L*N3~+k3xjySKRZbSr%K(190y3j(sLg~*3=(QJM4yf>UQw%6hK5d(o{x+udjxcqW)0l%kKFYjOexXa3GzljoJ7)q+c zPmv#`#NTGUCJ}w@&$AxnhAc3ZFLInOrmgXw>q(d?ok@uRBWH+zZTV`#3AwS{7zdVk z7L{Pi{aV$yqll0EVOOQWTzYLO$r&tMOUKEkZU!Ce z1%>(u@*hjzKm(9NUhP-CqyT&zp zoBD8E0s48Go$JT_>)6d42VoLNLPUBNFX;pHWe{k{FKR)4Q7>nxgd7dkZ?^26D{tz9 zu;kZJRW9UgYDh0qsmBZ~`rYgET3H$vZNRM{i#6tm^a;6^oInb^hIF#1Dh#wORLNqc zB!vIHhmVoLg<-V%2;*_pJ-ag;PJa_#Ts%%*t(uPtE>MHx>sMpM$Md-ypTq?`Wx)ZI z%n2A4?DW1_AL%*^quG)`oj=diGtH!8B~N!-dv*rrS+1?YM|YzadGh9*P9tkS?^9K# z^pfi%W}mummVE8WeqKp~hfWChK-8^aR@u(rM=E@Af8o2+SMiH>e#b6fX74`$cfFD! zD}G0{P)%G+NlxcLINK9LmMOHYZ;%`$d!Xf4qC11ewJL~N#X)Hfr}PU>;O zLOUsmJJ?B;h>oZ%^lVUM8lyQWF<6Idx_K&W6Hn3rwQ#!()vy{WlGCLpL^;n=(QQoL zTSkn}0#Hm<+Wde*+fKkGs9f`oz*M(eisqAKbEVq7_W9_JRF`u4iT3g`Yi!+tCA6{* zryrRO0)<+I&1zZt7e1Qc5}1jO?xiD*ED!VAKM2dP634sKM=Hy50oZNi){bQ(>TuV& zj>pNOB%4)mxG!R6sQONheS`-cnRx0c$+1jHcD2+zo)z|VS`8VOn7RP4)ex>ffv_q2 z-tnN<{(S|aRMY@l=0s7RT?vPjuatR@vg@D-%89^FoVLYJ%-)7CF659KT^vt8tXc{b z`G1-J{Uv6^7cfF ztiy?M<-xC61yf5@SI_n$KdDVTlwmbaLTLyVA^TpRl>)8B#3foJD~twO~=es zEVkn88wpd@T8*lvh+5r$=UclaK*hXGQ*fKoB<2HOzfa$N%A0_nz#*=#I%Fo+SQ2`e zH8b)Vw`|?TGRGE5KC^w@~X-|+*PcUMZ z4Im%EnrVA(g*--ovIY03q5Pqf+1zqXdUmW3m-M?D;;&-JAP=_LI>qB?R<@EhHkkqO z+D~TiZH(r`%efW+tCHsONJIS1)T6t!nxrU7eFStn$i#MQk!)DQkqc&lCQ>@V=-F00 zBkK?m1oRU{xw(#h?qPwPGi3{OSE~DJ`w#-WKU!EEnniVuE`mW%Fxl&f5vDsm<}*Vw zrSHZgjx<(V(hXt~=@gEVXD6ooO;_0k$B%v&J^!q}XdL170b;S?Ot);q%%ft27&eD! zwWi&GztzU>M{fdQ2uVpe7LHar;xF}QE4`(uYDQzYmw%ZWk{t3x?}0XY1ymijwvp>%%Ue<`hj-;SSvIUQFp75pmqDZv_%s*%;(I_w82R-+2&eq zTDwZwNF)r>a=2fA7wXE7)Q6>Ho*l0bA)E%}M144{t1F)-dZT*zXLL_3*E_~lYV>h7 zqBJMs-4!b@N3JSA!UqgTdUt-K{P;Q|D(e(8kuyQ3a~&*$dLoim`7{q>;xQCJ`wro8 zJ~Qxeo}D+A+r3buspEaGeFU7Q(T2BdoZQq6Dy9sal_}ec#Dm9psA*87bts^Y*mz_Uh%3roO&Sho z`Ci>aW*BjY-M<6rah}h;di<6=YTDl5`;$NvfD3&Q$xOJUsdGckooKO%+Mx%JGF8NN zAI;x|>h`YiTeCG~&VBMWZaz0ux-q z#F2Z<%bAR%!r(gM%&RYD+v+kFAX#3K9xJE>Zd$y~osuhPIomaU96iqK`o{7zQ~JXW znz+Tp4((P4sUvCqo5~&Enkj8n?2%>@IjQEFtvNz-&xLB#NyofrmXShCfm%HiH>MtG zpwY!Qq5$O*^)WSBho3@)rl9qt_cEz3TO*9D#>(gW$G&`TQ%3R0h<7$kxrHUz8+!4Y%;5eJJOHOP&O0kMRUN$-<-0P9m| zt2u?<74#h;c7C-RcJLB|wNwQoDn?Ma-aNJ;d9e)7Znup$V!vzFtFT6t9=u=WW{f`c zm+B!_JF;SVvSG5CA&iR_M|d5&(aKGTlLQ5MV>5IiK}xepnUO3SZWvXN-a9yyb5ja2 zutnAX>_qq4Z&pcQQYX1GrT4WlRvjwsxv&(VSqypvdy_5o7J??nrFZ1^Mj#Pi?qE2QE1alrKzenPF|0Da@l0NgIS|A38?3M~j&2|yXN ztue1(E{i1_2SpQSVS(pi1%Q-aTN16_9QCGeh7t7Iw38S^jn-&4La-%;@I0<3F(T80 zv^#?J2$l(+`5b(gC1#aH6LTYef+gT%RG7i26C6v@j&cm*`^2uKbS$$f38yY3tDFB3XaBDHLf|| zjzPI2-nDY?7mbf^h<}OWn9&keXn`X)E?)>w>x(1>=Ntw-{ye4O^~)`uZB@3$8^o3t z2d_S@exRDnQvLXa5UVdiJU@pU8(0}z9Uz*FeQl++pdY@`cwxFVmN*Z_j3SoiMCdlJ!YKSC=>_;EVkP(Le zbSTWZe7aJ34D%DL)+~C#c_%Pgi-Yl;O()LPzL%?gxYaJHV~kO>A{0bgrk}mi5uE*h z!Mxki;ig=U-k6H)Tqe%||Lxpqg#Y$~a#$zok@dq^`~15`J=k#UCl{H+KTkCSNkvJp zuO%JmkMYyi9PD>~PIJB#Hpli`+C@L{z)eGjP?%7if3@sG@NYU6w8eisse^agLW=AB6~_P2 z#W!EdhIw?^M&f9Fg7gmq&7Yiptqr5b2vRWHI8QsRukpwS}W zHKSx2-#p8z^wj0x@zW~!uKYCLxK98hX4gonZ*!aE+U|14pgmY zn0CH*efmDPVB>N!dT+KZ(VZ_dy8XZL$NdRCetc7Zh>OKWVFHNEbw&fyN8_8L6S~d& zpri>u=aJfh1E)5Tm9iZ{_u^_;oZ^UJ<;LoeEvypkQv7; zM3ZN5gsXSLgPGEaAhj_D4`_z@oE8nWLl|Xy?b@2eCNdW^)N7whP(#Nn zUVEF0j$cH(kA@de({fZ<4(y!XJ^1SzeHnPwE~1g(enDm}*SGO0*=w~u-R7I!;Hwjd zjdq$u3{H)3!Ph5L4k<@(5iP;E z!gdh?(Fo4JbXQ)hRubwbv#p|Ga+=wQk&oRNoNW;M9=$zau_L3ZC*1ewQ7HQ!UWfTP zO%7R2KrB&Nn;6Ol2E#K{*j^L7^N7|b)O+diu$!!ZixEzK$ZBjX^27T2sjbbfC5}U( zHVAg;aZ7Z4$SF7PLa~M3n5YFhltb7}N0l-b91uxPnty==Ue(w0sCcYBi4*5im(nZ< zzCfD<&_>9F!MFwwE2(4D3@c-5W^6$UVDUNi*>2^c{lSV;-^@6}Ik(SO6O>X;`kdgX ztBg`OEYfNZ7PTM((A#tih-RoX&BBMaoM`=il5pGF%WLXB#wQGG8IdWyL(y2^fl+Gj zZ~jbsAwRGFkVWIZEAiZ-^K;@AlIOzCt>^;5U_OeQ=PK3P3~Veh_4+03ohF85x?5&N z6JIP&6xqHKil8>hy2AU3wyYRKjOJR(lA`95tQVJ^oL#8bsO(_essG5Ne_9(Kmc9d! z{It%TjqN;@edoieEJ%JM!;q!7-mYpLmX`np87#3wB`6G@hgcHQrP)-;}lGPSF^t` zhJ&|WQX*?Cac_j!SmLKr|JlY+#w52EzwKVBdC)5VFTZV*Q3wB!7lIpIaIgEC=e{0t zUrp{ynsrjncVCjd`C8zBvXaLd&$&54w(WNg9xtX>lL zYP2$?D;F_QrV(Oj^$mS}&f;;(DGvV(ec;6izqT@2Z~I`Y*IsK0kXpz0qL|*n`7dRY zS1jC%s#C$>$KTDQDqMj26}-2ZfWuvYx)!{ofJ0q?dKWydfP-CtIv8{-V4MP$tydoz z-dz7uKP8d@PQ6C2_1qLbe-%Ejbm3mPWYx?zz_&4jvUVWgmSazXP^ zY;LLN&4&!^b@LdL)3ZH3jpW%8MD`84J&INlY9nc(*7u!{S*VSug&F|1yHFch3pFS_ z>_TmXE!05qCl@Lx6yW752an(uB80Hz-)91Ucp1X>`}~B$HV?z!(`B(JdTzqD{GOxa z%aqnIXy8N3mEgU4gO0G{UQ$hX9@_l55Z=79|RxAc=TUdPSm`b@!2YaT>e4 zsTI})325gxyqQVgXbr3a1bG_o^zTviX4T*6Tm9rY5)ZZpcf8Cx$xQ2*irUCD{bGS4 zVu9Em9i&)n{T z5k;@_99M=ImX1c=9bHfv)7@8aq~&edpfv<&zZjgqX)g%hdF3V6^%joJaO%A2nNT&V(}wfZ3tuwg3tVBFNOw`boDW1 z@p-G^5xjS=FGLUxluo1Outh#lr52Y-xz&ylSI7TY_2?86mr(sVin4{feJ2a|6Vg@N zXNx|k8b~*~a+dBFmeJyLlC#NgI!U+Ge9HB&WgqL?af(FkV>(RKRi6~*N0LJGI`-Xs z?V+2`l35qXPLCx?+Q8d@6mz5b(<;!Ji^tam>*#OP7=+8L?x8K0QXnk;f^gCN<=3bJ z{-a9uv~TOf7WCvoW+@@|v z+VUck19RZFqSb$C{yJ87?5|>1Ij)>_e7bk%F-4qoenq}^>9fueC}V7cw6 zCp4P)40oKz#(yARhbxL{`zgtBC&dak#R^%9xoY{*iy|U|Xfd}&ljjyTRG;j%-)Q4K zvzYEx9jeHF1)m!+EPp;BR|p=bBu9QtfQ(i@#(|T^SmB4?Uo60SGg5xXTVCfF`WB7< zoq^*Zx7VQ+H@wmK?O@6VSwr+1*fZ*+9Q|_@4X*v;$E9vyhRjY5p5|At=T-s~8-2r_ zIt@FFZIXIA|3n@;uJ_AqsWM`EZM|s>z4jjo8cf-S_z~`4Y5d7p;fLNeZK}$yJ9wCa zA6A=l1#h>4zeEZf9z1OMIAr2hgD+dFRsM37Z>^Rp6t8Y4s=Rm*i@%|!tG4SjxxpCQ zjKtvpMgqayr#{T2Mne!# z`YVx5%GG@1B_>=>LPGkgk@VH5%V~+g?Y2^nzp=cVr^%r@g#l$ncp55(zasel1w#u` z`-o6iKe~9t^wufG-Cpw75H8T%s*+sB7CpVap8In9&WKK^oZB@6tD1I^6zHfmc_L63qT){iMj)_<$5Z`hJEuxfbE z*HOvvCN4X}DOm1L%{G^*9b}(&&h*ZUrxjuiF-TQ4U@@z{s9o0bD%nV~YPj=H3N9s_N?hPC^0+f+wg^v?#GgC0-y= zu|z>KkiZO1G+Gs`*wRO(P;V3xz-e+IeT6^90+E4eU4{G7jf^IFWqJ_qa ziZRWTN9U`h0$S?bewX*=e%t~qi=#FcnW!J>J0Qni!i1;z4KKk~G2vsjib@v5n6mEj zL^QcXOj#&Cngh^3=cz9FJwj*i%yn+kn~rv8tv`??Sk%qmV@{J#EF_b6Z{a~^XuMv2UKv*MTD8Z_~SV22uLj zdhgrYcBlQz__VV*CFE&x~oOwhyLecF|z`yuoQNqyYDuVHrKn z%6&vn8#YB#S66sH-IZbPy^pv@#d4Xq-{`wN=Li3@9zER8didnWyVLD5ZH2woTIqAU zrtrI}rTY}t2MJ7}CV51&=<)*G33j2Hyk67j- z$+OrcTAijC!;<1Eys8gR+B+v{n#3a;-@Y@7in=$;m7DpQ%o!r{7S{&*`pS+pat_KPH6m1D-LRP3`C^XigcV@^ z6WeA!x{SJX0drcS!4%dfSIOBH%rTxl&xLA!BZ8&PA4|DCyLuT{mand&|#3L!-`H1mZ4vrw6| zEYSFsp%A#kMI~&?pT~Yr9i`$UN}Z;E^w&HTJkDS>6sM`(5(?Rk-FNkf?yVRkmt(|< z;IVAQdS?o^S;<7kZ;4v!i<5oL?nwA*41W0X4U{NZ*y(#qSHjWcwFA#eowAE6pdeC1 zw|b+TrqA}LjmNrl3oU(gsOF|kBx5Fa63cl2y3!@9FDVW9oTn{iyX2E~)FrLm2K{1W zZ{t^g>5}&@r8N9Xwe8VARU@!YBDeNXQJ8rcp_Hg@#9_ShFp*+!*Kw56lsSv5PaK)o znC~ned-BLUj@cBpHs4Yfr?IecLJXZk(4nz+k zBppM7jqWBfP_#5d9^CK@i|yLZ+zskFcY6PRA>TiyS?6$1G&Sm=HGK&1T5Tf(fPWdd zo5Fq7(~8_dV^4^-^74Hf$~)T5xJLDtnho)YFQbV!MJ;ot#bwUcse1mz#D)2?jpz93 z(B@Z_m?Tz=w36{kK~)o??2lK~d_A~fJ(`+TogHyQ|8VMZT{`JBeS?y<3C>8JL}OVn zVp_FDhP~WlnB&x_39I{1`CSG>H~?oLYXLOiF}`Ae>lOBTqTGDcK^-M+(=XgW1#>2+ zp;FBqkTo#seirZGOz`!5O-SL=aPoXO^^-B zHP1axbsWL2eOWq;7dhP(&25Xi?GgM$d2P~tg1+2fHKCpJ33U2Tl9QcZ%~0YF>ozY zG6-%0m*Z~W(Za_swCTQ7T#|YfxQ8hgqJN*kfE3tGEvEbcoRgEdkm?!drkkO>>5_Y? z+Ayf2Und5|tTT`{h@F6Y2C>j|%77yP50d5Fw2`v0A67sz3l7vct}7d1T>nzu0KrsA z84BnuhJ;g(*n)e`37-Nij~HrXh%UO*Kk|)*%uSbkJKL9+RjWILp|C}U-EgTMN-^sd z8WL5$fx}xyPCpC|;z$QN|J5v3mtx4Y3R zybD#z8&1=A>4cVmx!W7UewfK*iuTT&rcBN)u`HdO(Z$Q~PD_nCetDl9e-f;#TNpS= zV%J_z`hldI%EdndaN~aHi{ZPfc+AeC_vdwHDiSI|Xor4{b-+|KkXOkaL+tl1b3v6v zu?RpCO<8Eafz!uV#Z6EX!c>knBYbdI$$&mt`blbzH}R2e_|}O`U%8ClKcs*!@oNk5 zz}z>0DW=nZ9KYn)&;IwuDX-L3zOY z6`WV(<8_TK{v6eq8Z~ipDK}p%5Tp%%>5|PhRU%P#nA9xa=7Eso+{$I^G!PI*yoj)$ zj-?lYv?kxl0mf_HmZ>A4R_Ad=#|Mw^$9LR-6Fz-%*$`a81R~x~&eBO8yuRCr9XoPE z7_kWPC!lhVi(aNnc3xD9uN~w}-*zIU)_-Ycot@@^a>11UrkqRUz}d$FjBzzcy5t{x z?|-3TYU4S@x}VwEm?v!*VGzmFue*&K)#%7U+w9ho;1s@ihz&vkC{&yMaym)BsYI}~ zlD^g6HJ}R)-rs<)!p17F5~&qf>DxWQNQCS`Mk!R*rfh2pZ_{vj=cMFsh~%P9e5h<8 zn~6<{j-6;;VLsV0f$wjX=3v6tY-zWgq`tTx$ma&kJJ8bN@4y=>4-*YNW+FJ zpwasX{qnB)eT@I%C|>d9AS}$$uB_c!yR?xbXFvo;SY4C_TQnV5w;dr=>=zYNA~kBn zJ%|n0o{Fp2{s6G5(7vfE7TGUiKfn46tlQhCwi9Zc=HEM%>kcXRDj{i*@|O#Nl$-UQ z8a0k*Z~J@@BA$i(VOBEO#k(PZcJu%VGOn^yguS_$otloYBH~W3aV#=vaROpGaX_k0j*$n*}qMvbK1}|)fQ>Rs-@In>+Z}B3y zdDEJ?xw^8srtEtrBeeCIkjn1>PYl?zGjGW8^u$i&h$hF*jV6;ZCfHdVE&HCz**PF+ zC!Cm;1|QZw86*Q^phL<}mt3Pt(yMx)&0YFjizT!XV?V+>?r(-RcRmGWe@tHO84&P7 zYrF3^uO6g<|KHhfgkpvY>`#UY(tt4#u(zMOak#o6`$2D~X%NpQC9S;xIN#siwuF-T zWohW*BFSq=KlO|VTz%HRg0Q}9temT%f;BLQPE&t9CrABWvmuYbm(nFSQWb}3qVAXe zN62U52(*m`JmmgDG|ck26;Gs`37FwzOa#w)yzMY&{#hm@_P>`3lLOLA*-o$|4rqFD z?vdd%insxdhtk}2WsTyI?cx5+I^&ZfO13-=boyG;dPkVHJJTdn&nGLAp!_fdEm@1A1*3InHpS(r4Z! z3F?L8?ks|+t{C>j2Q3Z9lzQ=C0_89d#r`3zhk4mouJLQB!rY`J zXwLbfILALkY*1>{v=i{AdubDDv)&{iB_@u8eMCU>v~2wOYi$)wSmbV1SOlbOWV$yf zragZk=Ub5)RaCRERlneCOuuA6a#Ri}6@HXhro=OvTvj}bWl(<^gxF$5KgG9+m-aja zfH(&oTNqsr$PzMbD)FlDgWHvjj_jObDoJm38l=|YmED`9J=9~Xk5qlwaCwy5vtfOpULHYIGQ0aP zGrK#gCwBK;=Ylz1ySuFzqrIIbq*`6Mv~!CGu8D6(I(iUuTmtF+J4RO17Z9q-np z;h{7lOS`-|kFYPX@YBf2@$;m^u~n{m&51A5{*XF{9r--gJ-xc|pW##}H(a&BiEp#5 zLolKIWX-yPIDF*!ciI=DmX-shC;kH{odQ6a>sTM-PF=Kt5{1}CY#ab?hZ^QjQ=FrP z(Nyak8WI(tDUFE;U z3WcSZQmqJm|IK%DYs^TH{0gKrb?S;X0)x-gEtZb`{~T%coW#XN;s_){)9J-WLmYumVBbs?vxlRiYy0 zoJXc<@mkn*P!8OxF4GLicT3qc>Jf^}P-C<+SHIobQNGu^Twerz)%h?rAbbky=Kce# zTZ)I_$g~D0R_*8B2|wIFC$j!+MF*5;zYJc963avJ_MLjRls?vAAe3?eHEV(JMA0n4 zGy)(?-ZiS)ZRK2YpJ?io*1B9wMZsp;IhcivOd$K9MyUCJnTISqCvTdIVAsRjNEbsp ziA5BuOZ`c?kyK-05GI2u&?qg~N-*$FzbvwX)ea;M9SeQ?GIZPBW=;9tqDLjX2ysw> zbP6x8ha|ih1|z(9CcI=gkf1*F-e;ErH7n#6IN~t@GH4#jT~Sx{852w|Eh2sp`rqc7 z65d;j#48F|qv> z&FjXMCUovx-JB#57oK$wdCe;N<_Wjiy5)GCm|xrl}mu&G|dre zcl!z~ecekf+K=|+1Avr7km?I5L~yNuT#wwkuf;yMPE^-UP$Q{kCgAN;n1FMPux0{9 zfNcwH^tS}i&Au5(ool1H+cY%86A)OqBZ`MubeCOrgcb&6H{WJ9gh+D1S0KXuDwO)8 z<^CTc!ax4&1B=kk1ayP>cg}#v&F)%z!o1cbU2G@;ZcdmXUhoi&h?fYa$gy5tGc{cG)U zksed(S8CKo7qJ2N1MyKyG`8YU)%? zwW|51RdY*byqLnn?u31ecLJX)(0pFhooQ0^R_pKmJJJFJ_Qt!_jsKNCa!sT(=V3rc zika(g6lW-7PY3v9_Xl1okpPv-E7mcQ_)<(^oXrfx zm~EQ4s*STa=3_!kgU61#0?~!5Yc-TwV5 zQNFg!2GG{E0kF-5(yy3XGv9P^)7NUhfmBv)bmG6T)ti=!SlZZbDFJmrumrMv*yol! z|3(UR8s~H^9rEh0>o%u-`A!JpjF@#QEm1=%og*c!??`F+ij9J4*XKC(D``|Q<;W=IM zGKK7(VeJZwY(lMj7g(48Z4Y?p(LKUL7(aa$eZ+^y9XHUwRF#RD`cD!5%pfwr9cOMf z{1&Xzul)Q(nDVz|q-_ey#G>P zAFW|EGWvRdvCq`ecg?TY_ZRGnhrJEFKuibvU!w7Fd-?M}R}sPI%~bB+tyd;no1p10 zGy+K_(z2-cdLNN_Z{znd{-@+~?S0LlW!7HlU##qqK+KmgE@GuRqrZJyPKI_ zv+ERGhdYRfoK{*kq#VX6NPhYpC4e_`1OBw*HY`#{ObWggk7fF z3Z$0of%gvV>}zBv2LR(=D`F!aRf(<&k2tI73=GTZx`pZ zS96H~sjd;$Ja-|#j>bfSolAI|md zikaZK6MCcfZ_{Xe2P*Avu?|>E83MLz2+8%-=db9@qXj{sH-Jz5he`H-o6YOa{aTqn zz*@8q_0G-qLhV}j{{AyQK9fehQ?q&9KQ_!-^b1Y_oZoX;lf|nXsPA@%nfQ7?kLOI0 zjOLFf0yWq8QtwKt@c#*JcrQ@gf0?JCa56K%K@70{EMK>#`j2LF+ecX;iSJS_b#{95 zd5q>DFQJx$db$@e*xC8>e=p{iR+j?m@&O-ZYuKxCIF4HBl0wqEEu-oS)bNNL-UC$Z z%j?)Hp!v)oI(LVMb9-_ZPmPA>V_8RtxNFG+kAP-rOKg)vb3U18)12Q-0B%15FTT)TpKQO6r9DocT-5y&TlSx-y8V?mnmj)($lAd_ z!FaM2e?t{NrM!o`=XFhp>=Jh@6kR3X`*d8lp2+~?%~sxZ%G*GmmFjH4`f6sS_lB_9vLV4S4Nfc;lF18|3aC{H!449A7<6~ zHQ5Ru&J~307J#dMdop~s*M~b9qu;57tSw@Ue#(n6Mi)`7e_Qrr!Ha3)cHYZD#saHB z9fK8;wr8-01eJx^&cy{8T-KHO7h#ay0A-^rAyvcKY@>?fAS5k7F_0@+#ib& zF(=nV-2I)zV@gl_vvHhCC8Q|9Qx|XDtBWJ8Zx`Sgi-N52oZx^v7{ZFqTdwvy-A|Tf zO-~974)7QkiK9KB;k@{5X=kGIHdH6n;IMB?>h5=KNDoYrBeR zU6vb3owYT5q(8p-gi~spujp5-Mo^$GCIZ7%tEW0E`=7LNV>)lD^JObJH;xBt-pNNU zjFxk#f0HptXS&f-wuasMj&MA1RK8fQ${bY(!iNyQ0T8j10>-NHKDn&o6X(fibcXcE zOi=5#aQW)+>Gd6rhli6hwhBqm1~X{jTNAEFOl0?`#-^tpxq&0gpdvM2P0gAiF?^*) zMM$Ie#)W=6%SlN8+^uF9L8sq>K(NsyM#OnZ?};ySUoo@4Uy~Cc5W}!uAux zt#1>i_2|eQW%^IzH&G(izbAXL^VHfe)`wTE0B6$Smp(uV!2MeaJ6V@18wTRZ+dEy- z^o>maXHYH37EYay!)6`0fTG_cl-B!^_&+-$<;!WjDD3tti#lVrm<8jDr_(?l=g+Aj zdh!);5|5vF(aMj)t~wH-XF=WBK3L3&rQw&*pr0I0qECXH5z_-z6ma<__FTVeJINJ3mg4en8eauO)&(akFVZ=HRcNR9^RE*W zX1X4&Iu&Vb<<$7xA=ur6(;fgE``srs?sHz=LHRVdnX@y~Mrohau>+am%BbQiJNDD< z*^Yhked}1lE=R+c>JM*AoI3F-6Q^!C0B}#8SLQtZ4SMChdNjUWpYf!b;M9_WtEzV# z6+m3{3Wv~sd`E7>P+XMg1=p9eJfi>KM$(8tc0l9vG*6qoeiLZGP5ZY{A~s@J`#QM? zQxB&=Dy?uMWx*|qR*tt}jqKYw<4!_HNOcE^Z5I27SB=ZeItRKIcX*rY4$sjP+^ObM zqb_~O#&qG6Hm2VV=rN{VjH$hhQfCCEP7X>XdY0N)CQ&kbXEVA(;tZL!4gPE1$y7)` zUz^-*lj{bL^WNV8ldHf!ItvKPCUa~IIB*tUcwFquyvD*19@~#Ft>LHmM}IMf>aCHz zys5>mMRl6wG^Nx}4zpB_y^uhN)lU?v(WEOo!CK>P!%+PEv>oV@G1LB>LeiHkqEgYw zxzdj;)Jy>GPS7!)rKUUvB64f zMq)%Hw`m=XhTD?GFF`-L@OtOc7oq2 zpxKkb)TSP>o_Mt=jv}dX)5C<259U0B8cpxf)XkwWyH}6QbrxKpInKg)=3BeJ7Q9QB zbPOxaiB_GAbCngFTsB3zOm$jlxl~pRbU+qA4B4Jf?Qy%chC- z`BXSb$L?8xWcw~Jf-Ut{DnLQK0QE8b0_9T#g{$>7>v6j!X1R7)cv&%~;8ix#6ofo` z%LgMhrG__+*$m|is_o{;7D;l0Q!w>`FPr!2rMo$fIy8Hq2$wbMmX>kAr1q!`uT{~` z2{rbcm%BOx46EDOZkBe#fB^>!q_{*8dM{AgH?SK6YNo;gq0*>Z9>#VVcF%$0Pao({ z*WB_c2mF>g3tFTocVFsURH!`@!HkUSepWl)a91y#(bU{r|4&rr#2?i#AtV$WNQh#@VbYX;vk=!K}V!MF-d*)lGM$cSfdmLkw}u zS)21d(Wpz4t~x{ky(^>OUN0tLWoS%}|8tx9U|;h$+Dqoh0_+NP%Mgu0M%|8SE(T<* z(5v#Oiw=z`^tE_)BNC2A3w}y!Y~=o2s1{?1{kyEtC()`|#X(-T$u&qedzVm2e=*qA z?vUIKVZCwRmD}n5`^45MS!{(WY|n2XtOw9ZuQQ+tDKWV8r!#6Np%~Wa%P&5*l>UR- zD=T001|F}VQIRCv?@ADe&4KQSQhoFhM1{zn$J*1?aJ9`i zG$LrJI8>e8LCGbE8c4x3@k|H6mf(4Yo~Q8KZLdVRQ0?Ok4M8$iaj$+%+@NqNmlx}F z@eLNKeIY$em!!)PA90#maE=agbU|*{G{=@Gu|OPEmvGWU}`PZ1w-(`LGa zZm{65_jMk49;2^QC9{5Jr-*N@*v!nKZ3{hyV{VT3zRPK6UUTFmEs`oG=8N{*13@P> z{M(d&+)B1$P{ zuScq{9mBf%Diucd{Q1213k9U^mysu#7X8H{DQ+bUSM^qK;@L0}V3yLwLr-Cj;75d1 zkp1BzT_jz?#wLB=nM4^DJ0OljwTk1=DRuT+iOvLp8C%@`5BVyH7KqtD=3a%r7!4cI zL5gT_i~R=Ymc=Ku>AhU;80C;jyHITi`ehW2#R`5=>Tu~ov&z5|3Z2C}KFF_ZhB1C^ zf>Oil&erk=4QGweU5Zq7NtH+xWIQtqH3bZc;Ei?;Lf!Qy{0P4Y`h3Ur3gvf@DS)9B z>PV*&QBOuM>X8%STiPIxVu7pG;d(Tq_&@1RodZod>YiEbucw3*jU)er$?!-?#lbyU zC#V$w?$WbaywivFTs+pVoBS1j|Nl4ofMedvirv>;^#7GU;6*Q2L>qm;q!a(w`hb+5 z?buu2>Y)!9@>7$Y5C4P7&R69D*#JRCAF!(AU+M!o4*4p5z)MQjDxmLJK zA8_P1{!@Lx3n)gq=>yKY&&Cva$i{SUZjUj2a!^JeFf=H2R8Xp+XQ}5X^;Pc!7ijY@`5rT-1YjCt`=!4oOzI-4W;Z?*1p|NqA@@XC$08GIYczKlnL%0-}~aLr#dj7pbG<5SEYCnpR3Xyu%yThwr> z;3rnL^H5CGJ<9McqmLFz>So|*e|OLAWOXdNolGU@R5pIb4YbdB->25vFMgMDi4{w3Yw`*oua^WK-I7$*?B>t_b$K`Zyv&qTWEXTI>J7crUA z&p`J%J4+qflu+*GM;B%87G)VMoP(v(lkC}~U}gjXJ61u{Yv zw0;QhqTfcZ;cDzRNsur*xDaS+f=VZs&GjaH#hJ?%)!ME0b$Yk+0fp>El37$j>{v|D zK}jp&2I6Ko`A|Um(a1>r8K8iQ}#2!OO6|;SOb0*j1)Xn+-v?nIci>8MomWz0GlM+fRc4@n_^T;&6DK{BAg8 z>uoj_MGgESpUhB4ZBJynrr3*)hoPKWsG+l=R!D)k$i|%2>}41ODTruD`5X8(#g2Z3 z7E$UJ1ZgtV#q8-b2W`_Ly#r}&%BPa8_8VM31*1?*TL3XvFa?Jt>bS?=n2Dw^$(;%z z9KrXc5W3>uD1;0o0Plsg;MKtXM^d$>5V8zl$Z)Y~2~Bk>gpSulvNWZdlFYZ6YftUK zUKKw1ihFK&$KX`P%0ibzeR$6*ggU5Ia|&_pt`HJM=%x_D`W{ZrU`3h7n4Q#sCHvey zB_*7iG%Xp6(S50R-c&NpJl=qaX^t{nMrQ(500!QVp6Q$8|58#IpsP+vuB@=%jNXrh z*JKD(>oq`DPKwTK05ieDBIN^EDhDHFLTd&qyq5l^1DabDg87rV&Cza&go}p~WbJ>G6usMJUNAJ2WoCg6vSlyJ}sH zkhJiU!RcVi+ldYN{8tZOIYwBh&H7s*Sfe&pJcIN%U#0Yz(ozcP(;%N!Co_s3_1+XcuST8PmaL-Z3nr>l(R1TiDSDiy z|N06o&s=)x?WhT~Jcbsly0kpX%xZZ|C4-YIlZuvSsFrt1M!D8PgWh7|oNAf2YMC-v zWT2y5f%Q83MNHv0(DJO#V53TOgGzl()u!bsB}-bKNtxOW2fEj;0(&7%Pc6?vX34wh zXi#uyR?G9O=9tcEOY#dzm0GY~-RaWu1bc((uC{5|ThX&wwYZ!ILbfz>@zf>nxO}$I-z$_d|cBG!hV} zs@1dm1sW$^R@P2@{Eq?!&vhlG%w zJ>mA3s!2Um^B`GVfB(n)^OuMG3;y{6B00k$i+>)=;~xC;5ZVv;=b=`v zlYc(M%Kjho&v(zr^3St5ERo@#XU(MjRLMbQ#^vrjqLY7yIGbGkt-1f6e?FtL=0ROG z9~#vB_)%8#FRW(cpM#pe@N%r?ee=(=DE_z#dN=p#&OZm*9Q08KQah6*eN>Jb>e5FA z-mG78UDTdz{!gyXPC(;Lof9zg2Q~o*2HpJf@Xc9$l(jy2PEUOlTr}|4rH^`s@4svii7JLQc8PTbo`X^u`y~FZ` z&!H1&J7mvn;C8mT#Th4UYSJiV(?EWS`7G||e zW_Xsw=uImOy=V*8e*9+W9qUCL8rUo6u zA%17h5tT32RlVDoU)j=rRBYsdr!_>v>7}|*7NzIXc7MrfR>LMCmCrZ zm4A>f6U+U3UDeK6z3p2jXgLPaS^wkTO9@$7a@x^W6GN}+d1t}pyvIg5sL!V(Bl`cv ztL#bTOQX44@uk7*g;Sf+^3Q8^DatDT4OhKNh@{rw;;U%tpb@prtLt*tVRP`d06dQi|RnaqG+q62p0H z!M!NnC)E1p0U;}~Wpr)PYqdq|+kYQPw~*XBRD|5KzWqLm=JDRIw&e%khvrpn}Om3)k3*Bs^6RSzxI<{R7b@zgaaXn{bRhb!7XKx72`8bG{TKu49XwoL z^adO_AZJmo{}@@gMI5+{^G~UgzvIF8bTK{iPC85&(?foKkiD`73FtbBL17CsUDI4s zHrOK7EQc>7maq(nseEHSH9%Z~Oe>1FP%ip$6r&{OJe6~dvNn5%AH**z)I*aT*z7;2 z$a!FeGyfuLig%Pc3(n;sn*3#1j8C!xDjT~*67A`?^E=9Fr8@)zxQ4pJRnIwz?-al{ zEUp!A)VxdVYI?;Jg1u2O{&C`0tJ>VR+}3$pHm3*Y#9Nodw;$%*(VD7X7vIs>xnpVJ zS|?$_nmMe>q3L3(%lI8k3&PoT#j|r3*X1PEIL*hY?eq#e!5_|j$z40|O{;t9lK2kl zUYhy=tKPTTxnpH@;Vax*pqd+(`hV3>x1Z)T{SOaFU>aI5qKr6>>0X@^c)RFt9&Q!^8lCP5HWkV2BrhIa_1=3B2B}LQCI%9lX#v|nefgZdB2&S2_Lb9@znC7>C;vlyan0Y^8BSfGdJ67o z4cZG?J6D$+3TAb-Cx6z-&K~V;%CxuH+M~z~S2(oSnh%*&ds`ch2;H22$e#1jkC6IH zBULXqzNvdxH_#)kd$m@Rp002bAJaL=$@L1d8H6Ws@9!@Z?ufSyb{3zX8-Ekhm~M08 z!$`(;G7q{^v<`KM|1A>Y9j7^o??E2o9YdX)`hxs!OU8@`9)z6-TAlf?!Ls`D<{Xs+ zdUL6Q-Z$G~tii2)twpq7Ho8&VT?=idW4ZBnM~gHc?<{VFYKf*5?G{#!lPtKCo>0d| z1^t>wi_BJT&J#hm-pKKq71_=24itnSkSNp(^OYF14eN(|DYpf0q8 ze{%d>rqiB?!S0>&r()8z61LoaGez9i_!j26F_+05+2+jukgr(#Jk9Bwql>nU-0IAK z&(dmx8Q;>Ub;|+lcx*pK>xx$Utr%P5+lS8S=dNMRS3C1Z(_48QKADNtvZpuXuh(N_ z_!?DuH;f6aANTSik*t{hbj}s><|oe8!I3sDrNe(SJ9|%1Tz|1}YV1WBXOA!(r%ntG ziKK2Q^o|}Qw&@7J1IQAO!6YRK9W~fOuyVN?_Nqs(1Bc5@5CR` zW+QqW8#!=^bL;Q3uTl=htoHlNwP8sG-xAlbZ)pIb3R#LUEi+|3RNZRl`*-3m;HBtGo9IbruJuwso-@HhYqomtO=+UWN(Ajt=)kfTn z``GbB%G3d-MZFy%p}n->FlstdH4&0%fzEQrMjpoOe8^L{{IziI>){jvKS-82Fepo= z%~t9zL(Hgq8s4tAwQ};rod9Yov;arhQ^Vg{u*nF5*3nCUSC<97t%cOPx(&=d609nU zae*(>Rs+Yw3K6Gzh>$>tZ(<4o(0mWy3gcOV)Hb;eqKdkkGE%*(T2oDB-c_UC#TqNW zlwUQ!TEDD#h0t;s}ogpA{~gdfvX5-D6%^IE_M0V%(tF?FK-EAlAWrG;w=xxuBm1Kkx7ky zK+aUl-p<_@KAW|f{%KS|&Nga2SO+Vglm~c*{0AhhPfuzl&7=NeG%0EZmG>^gu~|c1 zN4mg#2+Ge9MhzE|!b31h9*E>;DL0-oglZ#NLKN6tb;{l2$+9-ecr6^;kX}bXX15@E1ZOw`3v-v z3YJGBRx{mn@me99ZMXS$O4_ot@j!=TF@6J24{Bnp99}v;({wSlPbLJqn#kPVJ#g<{ z6)1@)bkj1Ik$qOc`@H26NV=)-@ty;}SBJ|t;5jZ^OKQ~LFE|`l;Ol}*5fssq62+b! z;l#z^Lq_16ds+4xs310N7gZb$+zNZ)P*)m(wN2rk%v zCeWfLtdtEIvCw_Zd$525jbF0S1gD-W_|*H&Z;0pjE@`2sXu{~CF)cS!i4n=%oX}H% z8LrkFbNmKUox}lRRYXdy`f$>27F&(c9HWqJ!3Ck?8xU1xVDYPNx3fAW`XQ|M!r{Unb5F5N@%# zJ5M8>FhW#l5edXVfox_dk2|5XljZ>2+DZuY+6^P~{Ml5t7neQrGML&tF;=j$>*2e% z&YZ4IIN-Y4`n0wkU{e3PC?{SB6(NTcKof7P%@_A^Y zod1vtcVpv&D$ukAJWpG{qSg@CS6IdUNp%thu=i3UA0jOP(nE!Rf1nRIp$w`u89orc3h46|}Tz_^;055)uw!Sw(wMyVr6l3bhM0P^#4H%}p~D zMQB!3d;HSDH}3hQP^JpeLnm<=GyqK-@!QGqC-TNe{(tei=R$6F7I1lEdauPi;$JOh zdq3kkJ68bC0+wkQH>MLLqiRLN*Rt%`+}mNr+TSvMs%iCStj@-OqxYZS<@R{TWLrKb zakKzc+Kxwwm66qUEMj{2-p=kta-U0uH^#RQnzJ@?N&aoo+>UgcecQgejw`63m4pt8 zl>fs?3=oLV|Fy_V(;M(w0eyVbFrkwZy~(oIDBFnS16|pLuwv85mb#8OY4uLxN6PFq zSxSi2#LB^;dJy~GrM_9r&3wsKm=)xYE&t@~R{-0H{OE75v^S_dC#x2iMv-^ez)a+4Bd^foz z*oDLYmO1Dt;COYz1CrK*f_IaWG8EkSf>Oo^{pmr$-9!Bogx5{4u@(4rjLB(wUXM5w zgRJcI-f8FP_%;5jtd^at1BZ{BV>mn*Ej<#{jDPe%_B9`Y!z1Ag|E8TPBgEcMeI~^I zO`8-UUvun;R}Y-Ds{tetasTP=!JJ)h52U6Z{KoO*bmKRgmVsJwBbs%rc~yl|;|C_s z9yo8i#Fxgeg_A@lKYdPd!x7=sS)4mMn~PWWNA-KKTUhy`gneWjr)KH0zv-#{J<~Hp zQ{#vtHog!HJ1bXuR%skYk=iShIu3X z4#R>|)8B3ZII9lpyyJL8TQYsk4mx}-RYki1^k&+@+A2f12jYehtoT0TZHQ|;# zSkV#rqEQu`E>kryGco`cyEuKDf8{(-R!VTOh<7iKD_{lS3Y()@U* z{tPP5QNF-~Mg`f?<=qOKTa1?uxKZqF`YX3|fg1=X!IYhu%I989qK?_51ya!2?iI&u zRVRmV#QT_zOwKCI72=Gn&b0NUP_2|kaJOSS@;?>^k~>X)4jMn)1zL1nB00(FZq&wY z$ouRHE&@Buh@&nQY&c3pYzT3HcYsF+WAxH?HV{1F{vsvyH{+~^@C09~nGsT}55PJj zlO4RZ_vSf7ny?EOs=e72L}6o#+`ml1`Lg!sy|%o-KGp+%2QqPTiSDFp(ImbBXB9@Y z?R-Y2l>K%*J0CaTamaSW@^ZOBHq7my(NzDzf8hc)X0H&Ua`HOjB&CMWnzPC1&@YS8!RK1>vaIMTO8S^Gaq0y$jAoP-mSuSAMBRGjT65jNz7h=q5;zek$vlo$C}6=CGI`PJ(8+?8L~I3y-8AK^HDcDLr=bk7w2guncC!$Pi|!td0RL z)bD_cs+|Gftmq0L0inXTeBN@cM>lv6Cr6ffZ*1;I)%j&$^S(MYpc;4cFg`c+L%N39 zK2qRY$}$R4IN+fZ|uHYc$xsjQTJ>9)d6V!?;36&U;vw4f~pRd z0O0%4kx+QEXPzIj8<^V|p8S)z(TN}ey8R#s=CZBJCttWN)XrH6*6t_gc295o=~;`Z zzi>*34e|}F)&8z9GNKK^F;O&0eIf$;jIsdan2|+yPm-Thr!(rRFq$08hZ0O7SHZH& zVZ@dSkNg-7ImP#xhn7=5Sw2!-v}K9_V5>?15CPyIddhdd!n{DFMB+e4$9_)J4`@Wo zFa_US1B_BR$#cl;Jh{tL@1F1wb&rp_*mLJC*MxP#!?KVmtuvbI<)3GE1Cic;skv?t z#T-{kUqKhndUM*2-+>X-$o9uMi1DU`OlRk*!h<(f-DbpF9JqXk#G`Gz=nPxem zNvZLiEI@O@XS~Rvvv{IK;)bdNJo;uSxP+Ld$OT9F%jj-vTQBs1k8+_% zIN1+!Q8MUTn<1O|sB(JD$5iG+5&v1vYzSjbTvVRGWQ6_R z6*#;O_$CJ)&IAjs2NpD2C)P6B8r4GA6w=NOwBJ!Tz@(Ld%@gl|gN`6vrftCp2R&rOz(?`zaOx1Id^3RB>je7$ho1qnA#i6>@+J!6Atko#xs;j5)n3 zzQfGa>zw%)qOP2`LyV@AxSN8(bHmE`o15b@`yT4tG>>HN)`G-L>ZTwFxP$QH))t6U z-|c=hYxbC+*4zcaoI%kyW@Q?VnH#Ewi8I|8}G#=ru0I-o+? z?zRODXr@loue1C>JDJ@TwV^H|-2$X~rB2gT%xX4v02j9wU>_vqv1ttrGfwUebDbam zc-F(YMXgTLk4TpVtm>C#3srOC8hX1jzxQdvI~e^yqZ|7YXOk-_TDit%ug={CfJZq7 zF^>JekEng~Ohoo@nuFc=6ZEZd9$4zkf03lnybeuS!{KBMYvh`ch`XzO#qHVTCrF;$ zz}2|-1$-+ZgnqmX%=Z9pN1~&E&=NM2;tRyVeG~ z{HYL;F63iNnGs+h`c-aKdn1URFZ!j8N&6an)o4Y`^A27<2x~81Di>2d{z3#Jm&Xu} zI_afO(?*If>SH+gV;*B84;kh(C3!NJB7Gv4qM4fpnKSCQ^`6R(2sty$ARBiq;v($c z!Qdk92JfL&gK~U@Ffm)MiSWUgSsu}kACM7WmA)tmS-y-q4sz0N|CGai-Bs(ik}?P?1y zP54-u$J08Tp8Qd0P!V8JY9EebNtaifYl6EXSajKXLr9b4pCH98`I6z~vxSj2ha^7f z)hcxxF=3za&i_$y_U`FT-to5==Tv^~OGQ%WJ`-{~XjeT)Bd1A7GYJ<3T@2%hrEbX^ zY&|(2ISU@ZEb1(ti!+v7#@gt68-3r>8&3v2uxg4{g)9JVo%@>qU2EV4YCd~6&SpT@ zp+U71cZFg*c>O+gg;NuEQsO&1nKdfwoGSl(G8JRLoZWB3tOM{a&-m{=6H1>mB;vk? zY&!IB+C%DG{Ldm)ErI{psn+URT+pJqOIb+sK|7L&Q1RWigM94FzXEzVZ?|Tz&RyTQ z%Y)EkdU!+5{$`Ei(bF=81N~o}Oq7U7l#qU2h9uSH4n zygyek_e%Vo!MNRx2Tsr_M{P!Bz6mA1PLddc7;M&=+| z{iysuzMZ!F*uD3ch$?cp+THv#H4^kprUhsPz$;#=ppjKY=G;!ZR1C*OE#P!0}5Y^n{1N@?A2V8+JZa5lW1c=6o! zDmm|kc{B*lw&mTfs_92HIg*Z5qNaHyI%1f(-~;K%8}77vQz|;biau$dD5x;&s;}}I zi>A&SJ!}{lutJl{U1Kld!y*1Dae+a}=BEKMT4A*5`XPRZ#Q3hEa}EZ<&=vAGja>6+ zdIoiRs&+}d1ODxg`c~YVb==#0NcSD6I`&+t@lz@gkfon1Y&C=jf`EhQ+FBe;8e}B_ zccJ|({<%Z^|3=%4pR6Fgwg{wHB~nqL#MH#yqM`m&M)bJRw%?L$wC^4CjVs-4{tYAm zs=b`1ODW_$+I#q%S)xa0+oSJ?x|nc9%enPKTaCEa$*>;hy#%|J`L^ImW~ z^7mOdE@PSDt`Y}p0kV6TJaVd$o&t#7Cxo_!9s}fX{$d)Orsw!tnydZB#6}*2d9{(e zNY%4W(?a@&yH2{K-+O~`Gk2P9(@g%Ss1R@tes6FA@+@M)Dzw(PFZg$BA5B4b<8M0M zNV*8Oh!8tYox9!NjFQ{-jd%JLec|r#i$As5#?fFKo6x*7#J6(4F5{VZ#?r zPI~1VK5kABmHP}9#c;QZ`IXWK6u5~occM#{;ri9%Vz^+V){`k06Yb}c**Y?X1?U0L zeZM65JOKUiaAju~ugotT7EX;p=M@}^Ti$^P3!Ab4 zhU!5wOMM<^uu8X1PP||s$wMJ2=rp5n_h<>nB3~eUdIMI;``pQ!2 z1}A2sFxYwY-sEW!6`PQTZ)u|tb!wl`PFa($gB-IYDkoj?{kPG-_R8cgWb`=*o_wbb zru~4-cj7~X@IX`2B_sGk3b5PvKRl<0zO|4FYfyDgu3_+HM6*|nkK_&LV>+T$XN28T z$}&8B^31}O`MJX)sX2gvyy;I4<6YK9i}>L1Z#Y#`c7kRQ-QHM=RuI{86-JQ2Duwn4 zt1;~9@KftH;ea3>BI5qN@(pJ&*!aGaxP*SN?dF$Bo_d%1xYokNsgU_+4E4BI4KHG`b(D$+67O7Z4BsZIGN=_ECj*^gh2&OQu4| z(Z>oriN3%rk&L~e%={%c&mPIkd1XDkB?s{tsWoM=CCl^SUwimVwwF9+wA*?UU!L_m z3`0*IqYZ+sl;ITQ*?XS zH16+>mhW~Fg={Bvsfk=5gqp}ZbCck`^2N$EpncE#>*2Ewt>yCV=6ZeMt`FJlgi;rM z5&!bMIYr@12HhXd{WAR)oLgw#moXN`iQh=kXzqGKX-2Ai4qPm)%U#P;sWk9aC=)S9wn2N>nBAw74AuKQ|Uqi2Y6g)Ejwx)yy|C zLbsEsp`gHY3Tm3hQ-n z^%>B!zgVOSr`Q#B&iMDksd^}XW*V5MyISf9czYxub|PP><`eie#aEpoj;-W)4X_7* zV_0>XrFY;uv-oxs9`OFnWibLvtCRRP1uH+$0fxkFz;Te=@Pu>=W>;hb64Vh8`AJhK zMUH&gY06Va>Bx_lMZWllGcP~$dRL34v_4E6LUE0p^I^h1;`>Ca@WW}^g}$vWc|BOt z5p1v}G|2`Vqf9Q~b}1y2y-=^rTaEh;Dh6*S4!aTe#YnQQTFci$rYR%&rw+?y@%g3v zb1_Q!tRnxevW1k9=1XuR%~p(GjwNEmy07dDJp)F~`ejfazcucgbo|mN>}iZwMfRN( zI=4PF@%%I~5<}nAzutMJs$_XJ8VFJOZYrFpy9^ItjWF>HQ{mitazP*nu=CYZA3_0M zWUqv)-g6SADzNxqsUx|#gr1Cp{sRtDO73q<=sbXC(qpDV#y!m25u*kt{&zz(36tC_ z?!@2c6&CDNaO+(jkqx(=R#VjCB#xkOk>vFi5N#=RnQK$M96^OO?z&U#j@SCaR>3{? z4lH{X&G}@$5}}$KO-;>p7WYoH$W~YFG*<#2ZfmPw08lTTzcHQcceC->hmt-a)$de| zY@Nu=vyhpVhA{x$$g*lFcu!{%YMPgFnv8=~646_|0!&obQ_1*b{ya9Kj#UrXb2#t9?gAJ9!zi!Ua%b@$O`D-R?5ywn1zR_~aM@)D*5)8%AjpBboTmTXP#qJ1f7aXXVvw z&2?^9mlDsw{61XdOGB@)tUx`+(Qq=q?B;N_2`M^E_jy!kem2~9R-vBH{DeXCw4wP9 zXa0QXaNw-%B&3fHo*P!yq|RQ)A-le_DpNIo$O)xR2rbIRHEkXWrktGFzd>aFBTYL^ zO@g>in(a@31B0A2y#c5o(>YD^gFPg%n*=6fGDbf-3ENP}hEfu);vPt6!PhAiYyn5` zBrW58OvuzYR_&sxLWpNb4h}-t1cj4>V4F63r&3CsPdIs|t$>I zv|{?|=bp{UY4s0G9aNM$=nnV8@>i;3&sS78H*7u5n+J|InFQ2!o_*fP1#|*W4`O{m*^L49tV805^+zkmWVa1>TJhqpJ2u$&x zL|P3kVXZ9rcqL+%G5+ajO`lY{y{=`y!odOkV7lZtuMf_Nl9Js?y5in&3`Ki~IK}MV zG4(OUo1#kFZv?<$zf77F1?;dEk88grwmlcZUDyiDI!%X*rP9Vr{1v&{cz^l>f&fcV z{BT~tKQ_&mDuNZUjUw1KRI$2PZHL}_X8MbO?$8oGvIfijfnX%6ygPxuZixAC5m%Yh zt6KLR{|N-iF6HE;PUWPtpp{zKHtLZRJGE|sf^rBmPEIun%9gKEP|BYX{;bA*BmS1O zS$2Xm6#Eiw*7IbMGBb0E^Bu&?=gC2zwJpf`76;)t%K28G*0<0dzJmz*7G*x+Oe!3j z|1%1OOw55J%K;5%4$uReV#p3MML5W${lcFl8QJ9Atme~_?@%)IrhKI{Uj!weO{1g7=vTV>lUiT;W6rU%kXZ~iZk z9wz|Qk@WN7RDLz=Q2Ecdq{Cd+&_&A`M<~$b#5?kwn@-kPpmH<184hs!g9uCRtT+)c z*+Xb(B&a640`N7%|4e#_d`{(d&o!iPD+cp?^lWN9wDL$u=DP6niJD*C&t}R;XkKo&oiTqd|0TXBbVGsaWA%PTAHb&x`a{K}koy4L1i_ly5LU*C}W3T_K zjN5|ObaJKaZixFXQE-fB#tY5E60r|<`R0IIZm89^^}phj4gQ{!CSv`Yk{fWz!GWp# z70eL^Q{trCfQ9A@d-zjxtn_+@muLYtSv`*VfBrJ6i9ZWeQ==x{V@C3$u#Pjp=sI!G z=apS#w}W~8SK+((86>d_4q!HLN{u+;H**iW@O`&ho73 zl4i9wS(K*5e^$K@_SKiw4z%s0sQkZEqXyiKvJA?R<9+elL5#>StMMV2A(6bW`<_uX zd}FDi{l^?-g+#_E|K~h^K0i+Ub22dKv74q#4x&x=X>NeAul?7_L(%lQk<=&4Vl*T3 z&lK;CSG719wl~9OXGymIcR5q03SM|lm8DV99R4!v{Wh&dh*a*bH~&bOt{XB3F`WfE ztVqJ_?x!)G`R6f%q@IPqyK=81nSQ?NOV)B!t9poGBNjfvL}A4BqV7vx;jahLo*h&$ z%09#RD*yMQ9!yPrP2oDI2?AwezmHK-4nS2{3sL_b>x&Z#CZ3pdsPUDCC4tT~n3S`W zP7Q$TN(r7#ZCahws5ImC&RxV%K?XK0!ttG*C!Vzsr7L_c*Fi%+>#!6p(+7b(@#kL6 zx0t+vmSiiRVbuN9z7Fn$*B3#n*U1&u`ju~K`TY;ylO znF_Iy_vIks9n`D7WFP#D5goP?mV8d$q-EW97w?T@L`;|`{n3Hd4VyE2$lqrb=McYU zcR}OV0uP5iPr(5K3(ib_)cjhbTRN!&aP?-5u zSPIlwtN1X!3zgn}6w$2rizac&id|QP-DXsT6T?+bLv1uUv2g6l{Jz7YsR(MDvy0=q zhBY2m?JWKrGbU2;z%FsvE?uO?hAlyMOZz{b60Wu1CQ-CFq^mUmB6J(3$U-FOwm4i@ z0`c@l9KFX91#}h)SjWh*siGJ#0Zcjq(PsZ5(SCQAsUTkfPU5@HaBh3u#)ebGK*mOo zoy2RTbP8fG!gF?$@=(T(M-ZwlwkzS7g^D_eA#gAEx>IXKE0Fb?mSnSS;Uc>N9pv6FcWtn~Bg zA(gR~GSrKCeG{W{5Yz-)eNEBEnxdEeBdIvPYjneyj2ltYv5X(4$(RY0p@9d2PsHqu z4ZaUgR7;J<0o4|5t8riUUuOoSvUFeXxrTV9s3^q3!lcx=8~r~~G=nC8m;st}{`G** zaMXRyKoq50$o(whZa1PGT&{tJESkI#oz3DEK*Au%UCjv8WG=p|Oe&!g+MUES?euK4 z<*&1G;yw~_{}FZH;*&s?eF53g)FlM2_#(dj)Hz2*F6q}C&23F@3F#!LlNiQAw;wEp z!q9Ad2-8<9Xh|e_jkF?8)3>OkR))pft+7wkOx631Ke0;>Y3iD490l?kUI>?Ogx6ce znsbpv)P1Ig9dVHNki>U|Sijr=vFdmyF`HH+$?1hFYYK9}kb*0}u_;}bx<0qgy{1?p zF>jJ6BM;_&V`A40&%tbVKMB=~bIp%%8_%Sln1e3oCY~8@UHNAgNTW%itjEmbo1;&< zY$#$pf_dh(me)hFuS3a3rQGec)=6xK6DN9&`3BeE5lisFdlBhHU=YO1pO#c3D>jB zE@_UWpHsw-*+=;^0nplHsM^}vBt3VgsmV0OkQE@Re=!p`;gbB{hI3b^+ib3eqDSXT z;G>i;)Z&6q;FJLlKNpF3PfG!{b7+&b@;)owi-e5Pa${^@_3U5`agyBK0GM42ZDs?O zd#thcBM|~ev#QSGYXi>SJQ@^eVNd{p`IPLHF&yB}K%j*Vd|pInBJ85SgAVq1@Ec*WJ)WTFdPs8J0KdR&j`IWv}aK$TjETW(A5?}M>2rXiDO|pLOn-QO8X1+v>cN5 zmVYi^gkp+NGfYo62bk(4j#N$Ev6Wd20C_hMALRf<@y zOHilB-W(lU8w<6*-#Zk0wx#*J!rYqXvcfY{`GoPsgsok+a~nRBI#B%pVW?ME=I5VQ zm-_|ovTBy$m*zAa+gY@9kD|k*aH^JNRHfD95wQ_$11V+@In$QBp9Gtyp@liDx)oUo z+oy^%VC!dra&aXjuBj27f`6w-MQYUlxTfOxbIahIrz@zz+x$Q=^ZqFFKK$sDYzd;n zaoo7f+%qQPO0bX2T4grPMA6|7r^I8>>Z8Xbqr-D5TPc0s@cen(PP!~72WD}?n3JiQ z#3+d$pHxd?Yr}9psQro$4&BEGPE#M6V)LzYpM_q|E`Z3je@(m30A6Qx5^5Qm>HxE1 zWepdxSbJdlcPp6G#E}40U6S1p9OUMXP$BJ&K#82_1BJ;f?+acCSq^*|$|uxh z5j!L7x#lck5CL=Lzbuh4H7cjsmS)_N?9$YreF~orXX(tp6ub+kMjj_Fv3xyou12<8 zH(epyaR;7RouP{(@L$V|cU;#{K$N7$J?(w#@JukVUkhy{W67?$s#m3*LmbNH z)0QKIo!fY?e729jLK7{mf|Hm??VTvvKU-O}du7rka5NG#&9m7G2~y@S8_diEUXkLQ zhj^nUbVEx)R5UWl34xys{i#sI7kN>XZ;h6BRB{FIv+xjAJF#sy%sMl5$}!FB!?_=u zWo>R>ZeWe?7}oGc`(*P}WLMAX>t>$_duJYzoj!^)iJx)~&Y-Qm;(NF!wm*uS0{po- zZngi8A~+G4Us3x{>)f481A*DB--_InK=TpITyh#b)4L5{tIPc~lYlxdmu-Y;WYnLq z9Yk}%63wzp*(SbYZ*&qlf*ze<-#`(68{9y!T>hit-a*s;!eHUf@SG(#Opw7#sfN-* zrhE9Jb0I$6s=eFKQfE0Fv1!{k-67F$5M!SjwaDTpm27$$LOIw~F8zU0Ou zcsy?uip?cA4&}Y`{9{|9tBt1qV+1)mire=r<*Eiq9dByLOGAr{UGD~tBD?ZY3Ivs z??_(o+z~*di)8A4+NwQ!v-jD1QU>tXLq6danpYy_&pU}3K&K8yax5h-_B~@uZQv0L z0+e7LPtnxTx6a(CnRgPS`5>CQD%XsoPO}(uvx|n3{cbUf=BMW1g!tGoOX1a+zeUV257SbEGHI%m3d%5FBtC=|^F zZm@>l%|>N+*v^8JCLLJ!PQQqJF5!W4Gbqck3^4u+Gbl$mEbK(%zX5u@^-WK9I$xw(^p`hSRf z6Zojg>;FFk2@FWML5N0+5@gWCH7F=izzhVq0~5q*5qDZ?#GOI{s0e{cB*QRjTiW{5 zF4)@ER$H|ys6|QG0U`~CLy=cSo@@3WuhJm)#* zJm;L}+8^*J0egW9VKFr!k z&b&^02J*%duOmi>Mh#*_^S9`y%!+sJVEnLpS|@ak^D}QDejZG{8TR27bwgfq zsg#v+F;GirCekf^|N7>ZhIlRgwbZm!byllZ>i?ZqK=@V{>v|G&27b=ju1&cdD~Dg2 zU)N_RXspQ4jD*pi6$&k}Q;?)lj!lU1LRnI4!UR=9;Ky0KaD0%Zmy2~QxQD7J;3-5G z@ccUVYgC9!8dd;V>1!`srhXT$~$B#LM{hZm zp505cyq55&Ve{CZM|G6Pfy1NI2Vrs{z+`NQXpnkJom8eB(RN*3ba9hdR+N7Ha)H|) zDZ{!xhw!w&<&k~)v8RPE|CIR;yNmu9RgJUeIW)~jBPx7Ly~7nSOY)dqs=e>dyLhY_ zbOufU^La|V2Xj3i2g6`J2A=Az{?&xIXqdpjnAvF^3jNv+CDk{%v==P?Rm#B5p4V1%`AgDf*75MG!t*OM-2q9M8Q}E%| zlI$}ExE%@8kIoBWHl_Uf#YVGB2b^j5GDTG?UgtNkq@Q-NgVfu8 zD&lZv@x(clZrjQkhiJueTiHS@baTioPJb?-%g$b$Hxqu^680coZ)L;sEOR$Hzf5Q* zr}{_9@lZCIJ_8-%v!BdF%4VT*XcP9DCI&x#3k{rGsIM3<1UUf_5+sVCHCEj}_^U&- zqr|oXy^{fV&7f|}W_L{Vc#lO5dF+pFSP=_X3X)XyZsCbofq6DzqTA0TQ5crW z|M*$K5btTby;w${HC-3jL#3 zC>sYL1F3=hgu>ALsM+9LLt3jt%?!~!X-Cc0O~_!B=DkB;*$k99Cut+ZetjT@WO;)q_*b?l zg}p$pX^1JP4tT9v9khnxH~AQ;8%@0ny)}YMtjpa-ZrM+7QOupuZ!ICN%Qci2`*G;E zO1_9yXA;LNOU9~;$yHYWneBVo_PrB*t8r&etp5`0cW+Hym$;v#jPnEoWtk|VbnO~0 z-fWk?Xw4JTVjP%tXu@5x$w@ zvuJ`tHPE^s@foXN)-u#YssMAcaVMzEOT59T3DLtD!XcVQq2P2q zsDeJ~gE@lyCh>)UhIdBs#wp`Q#ZM*b$eu){zPUQ=k1i$(a_UvtYK7yc-EsTo6wwz# zu@1y#|2z1mBZc->gu8uGi6QBIYjs(6r=~1sG0Szhh!1{$lA##o(X^0v+e`&lcKuN+ zqANhX@jmS1QHsx1sjt!iRn3(YLt&AwJ*_B&m;tUr*)#{co;A-Pfi)aaAlgLajos5JF z{@L84l2z58>)b(CiPFFOKoG5j>3>fodKOxTPwZHKI>`b)p3T8qz^KjG1=M!dEVEg{ z8h0c)_|+=b@wp(9IbShLu}PvTy>4EPDwUWl06`(<40QeVItcmV1I9EkzjDg)T(xSH zxw(l4A7x$v%7G1@;>VM(TjCSNjt{f8Dx#}H#UEI;x6yJqJ_~yDjubY{=fxo`9PDUU z@UWW*pSks8U!ST#eCTeZ;3-#Osb6(zC~`i`oLow?yPl>9vpUP%ea z*qkgkwutn!9adm&XMZGbC*z7ehdd;);dW-BT`I>XnWqLjeSulxlxmW)v2P-kAztVi zCCY%7#aQ0yUCa`54Y+gK*U0GW0JtBDq95M`xD$uH4cR^{fI8PIOE1wmy>P>#*x9ujlur+re&&;=LSKuOiahZ_FAhLEG0#C0Jeu zkAkDb3kl(&8IRdtftyj-U6Ti`=6;^(r?sra1+l{YE0R%1k;v(0F)K{BFH^0(-)ikh+Y$`wYn*ksKeqk^ax&4GGT zVuER9x4nBEF-tkjCk_u4a?)r;R;cDd9a#m6NhiYlCw`-EM?UGrj36s_34pa@QP^{Y zsK^51AxXjX%%s@He#HyuVC-zSxf!stHIq5E`Gy_h8VIFd=(NwUxgy&TFK}&$a1E3| zJN4D`Xnk}tm^_xfA@2@^38)}M;VU*4VP7tvz(y0t1D8vMB+aFG6<1~Dbb3In=Qo#$RqZzurwa@)s*&yYE&oXRZ$X- zLQ0T6nF!SW*eN(=eYLmY%*NN~oAjKc&BsxiNFhj#3i2s>f+=M2nlbS~C7di1!v=mc zRQk3x?-m--GOL$bj$?ZMdZ#TZ=O{*Dv|(;f5L7sk-orUJ?V-fMD+xJf??b|u8v|ve zp~;4eaDM&LKJ>a|A69?kV3;wijb_T{7xKcET%^N(6V5Q?If@kq=bw(8g{@fMs<1f$ ztfu1x4@B!JRns#M|JnU#@7`>$--erjX-9`BEBf>>B0}q?!sqo5R6md`SGGc@SD*)S z#kPlhPxl4xR=Fd{6bjZ*+U!6wZZu4&;iH$pW1a*x7~edzPy_P8Vi3p2f@Lp;uK?8y zziuE77^;>la3aCnFer(xOfHr8tKmk1dk6{&KmixU39#u;yk}NH?i;g?%fi(eYcMhX z&P|cLEXN+5aRhYyDEz@GhjTX&^_;ZfIez*_mmx^5YsC;`;Mbh(lUV(%bsNjUWzC2d zyUHJi&%usda`$9;pgv7vRi~QsF2_w0;yx4;-7N7%p|+iF-Z%Zl7vboGfko!mD@@G8#~8L5InPGISEccsnK!v14h>BvX;((JJ<9K9 zd-Fy&n^r9Ul&znpJEluXjjZKGHD^_JT?w&VNC>(#%xOpPwo=VVc>{qvN zFq)PxIS@|8r~d{ajNH???|Luehd4iwqMs_jRO#QTyRDhy;F)sJ3LqZHzgx99BrS}F5( zss->_!LQC#D=e+?k)^c)!s&M0PTAIQy0alMh{THMKRFZble*HXlgx~<8O9D)t=5yu zO|F@NO8&0TRVDtAlkNCa95Pm7Q@_;hIXxcnJE@mZr#JZNo?7u$Upjw5;5we_{mK3= z8YjXay5vh~ffrF9>WdEYu+>6fJ}2^%l{kq^tB3ouLvaE`K>;~}HhX{Ve1@A?FPDE{ zrkX`2i0vL9*)EBidtpE~lL&6ou}CvML39J4q`skEUA<<TTf!++IUxSXFA zW&Y2riBGejRq5nOC^4cNl7(#3+wMhvj<@4o;$$$&MaTfMy|F_lTny(&QlNm$pNYhT6ju%+-#x&P%)m_SS z;zoTiU`O{Rb%E2lMv&KVSsw$<0lAx!${)FiJ+wV)MOkT=nRrdoz+-Ei7LPL0Qdaru00oC6KhnDTAA93!~(SJ$vYTQL3E#Mj~M-?ac2kIp#9|Nil1-; zN`GJd#XethTv?6G@?WVbr?~<`IyHXUB{&CNcUIQ)?qxNvZYrxE>docRSx+m}$TyL^jYdR|5hSTf>@U)jk8kODu!o zaVOKns-P#P81NJbC~GG+F*p}zCB=F;`=N0SZZq}Z>_##Rv4peRcuwGg>XxoOTGXw% zT0OZ)Q`V<)SNr*9O zuH0=NzxjXSV8KbKeSEIyed3(GbAPwjInCOuY_TJSR@jhl~yuI*Vo= z0$5N^hkUjXq!W&9eGa zIj92s!Ui)<1{CzGEg7=v#v=144BOIb$bY&0TZZhgGmE_ZmxkQWwEf>5vZ0CYac99} zJ`Ou)LLIpp9Age5NPz*JrgXJqu0vufKXo4ip0m_2e1?TEX<~O$`5%(zMZ=e0E(CdC zaIr5riba^N%8GQ-0?equigeZ&_>g;84&l=ML2Kb^If2#X=sW0G!g|qjIEo3G%c)5b z^Rvca?JHK@ezO}aHJq%|4K}obh*aUyF9`8OigBeaRYe*6w`3TX*?Gh;KYFk6^3?OI zfBJS0&!Oi|=S~0XR=|7tf{d|1$W4Wqu2Uz`b5mVmI_A}@>>*+-cqu9*Q9Mdc-qdoN9|Brcwbh@|IwIfYSW_&o>I~=_Z(ccvn?4~SI$Y^tzUM5qnW?&?Wqu(>plyjSeIV{+=a z9ahiW86i(OXV-rrw{oQiWAUwu`ltdNjB%z zGnAIpyQWUAyZAGt^1rXoJ54sX_0>VYFWr1=T>4q#30T4@SVrBGh&!g9hxNd=GC7rh zv}(GacQoVuqzsJ@&b}MdjM;dYCguwH(nx;?C}>I#GS$3h)g8e@;&lEnUlWa~2jG-6 zuJE3dff?@uAJ6WgixKL+&cmnbXA>6 ztTr#|4601#k6Vi6*Bh8K$by3d*rDucJCocfZ7}T_O%j+kg>EDtk(_zLvw~v3ugYWD z=S0`nY%U1CzG=8`Wn}ptMTOw{=IX7f{9$AdS}zF#qBG%W3FpRs7cKE6)ajg%s3+MH zbIqFxY&-iLru=RSxL4Vf@#NgVx8y94;^-@n%W|&Ax;cqdp7R+O-zMQlau(^KBM*=A zAb8eL3gxp_A^HU^4t;SZ;E9<$8?r?f!KD>$c+)^D=U!qI$tNt}i1IrsSMW>ObU-l@ z3wTX-NM1wBy%{xuzrZsBe;Ww?*m1cGscL8Gz@*VTZ)o*3Z!YUh}bksNSND33@ z&uVkBX|2`D-||z{<;}2rv7BZ%mA{sRw#Ab2?~}{3MkqDfS!lL-`pzPtqsFpnq1CMppVS z4_>vxj?a6DS0X5~vpHW);Ra}qY~ne;xPr+mT~Rg9j?P=8{Hf7U;AtW%krlgaPCU2u zGjv7f1-)WIn!-~7OmRL zRdsZp>9A>QR7P8~ytdBPxA?rtRO0oxoF1d-a4uJOUxWuTZQf96wi^(mwF9m(Fyv0jsNrT6)n^eM+!SAgUQ;w)VCeO29Y#V?srRivNsRlRG@ zFR8j-%O4~Edl{>>xQA7(lzRpveOVJX4)83jpfR4|;plR^m~+BL-6a;;*Jk<__CYuM-Ij4yI0C4$zYjlNw75uA3}zPGAoC0~Hi>K0kOjx1BJ zZIfL6Ve(4Di1d5y(iP2(aZ*NiKCi+pq({H_ZObB`Y>YH@E(dCneY7@oKb;9$<@HnlZwE8I=A9p9xZ`ZZ7jXOYHD zemh67Np3`==gdQ+M^O;wGv^3`!SijYzLCLv+kH3A1LkU5LbW9x0C$3hs{z`HpQbU% zUFR9+0g>%RHM4`PSzQ|hTGz}jW2cc}3U*lBky2S0ij6}W-eS$YRXJi~*z9BtN{H@x z*Xm2W|EZi_-aHWRZ$e{yonu7~P+Edyp~CBNTDygloDx7aXrK&LbI@XB9Up`B{pLM(F)>Eg5iEy!nsQ7qx-M9}CK%ad@eyZg zYwqhbfEkBwttxH|$Ft82rPj3AgGf2t4SjlWAhTMQs{WLvm%PJ%rpEghdN#}F{=?6# zsUm)vuflqCLrZxMk;A2Hru7TO%XW>5UD07zxF12n@Iv|^)ae6#*keAtjgeNU)5cKm zS1Njc6DobBswq^uZu-fJ&(ctp&jEO9=)jpBpyF$)cbGM(Bd-@cLK?i9!?$?ug<{*6pA$ZgW4xKQd?8~zD|xW z44-!n_x(lpJL{g@ProUkx;&zO# z;+nxb`wV+cWMv6^aPaCC&y%p{CVpCiMDFTM>=*&oV(a2dlS&;um(~haBMC z%&>0+M!xrHkF;>2&G*wSzi;SJ%d zP!y`S!2|{!Y3OA~HevaJw6ph90v;4M5&x{7Ici2O@^k~^TiNlbegK|a>Fg(d9Ud!ohF{#5>qP+@$Nxb9SEdKB%*;()tv0l_YYj$U@Az?YKt^S0q zquaAu%**tLBPt}cJ$c|2mPQzh?@Z-C$XNRkQnCeGg6CEZ&a&qGg#sW>2=r%GDtw7h zbYE!qyP?J{*`s6S`7Ky7`mFN&!=MQ^Z=!#_8x5-DW|=9&V%p%~Gc@qUaBwi>doR>` z#ju*0#|dFxR7as4#l&9x0O!+CzYjvaSF;>&`B|wnPzd&Om)&~}C^VR*a1*zDU7SM< z5UHr+v)O4~_c0nb_q0j*8XpTS(W)kJp&=x|!7n)2r?8Uj==buB$x?Jp1kQ<5UFlog zn1W$z*sjE}NXae;*;h9f!!12;02+NU%mV7Y!i?`YcYMV75-wJ4O&kx}dXuhIX$G4p zT6 z05Qn6b5mC0dV@`Qz=4sB=|^moqoHG=1J{L#M}JJtcD zyLF)5&jC8w)n|W>wY~gSv_oZmjulGY>X8NQ?52+5G0+F9c!oakX((D&l0Z)wi0rph zYgEa!ld$3%Td(lmn9Ld$o*mX)g0K-INjb(|_NyJKR|;wyQ#f8m8b-|6|tLf|F1+9DJOF0*8D}Fy!XyX!+=s&a)%@LI1XcfCT`6zKk%R^6%g~~YjjA0PZ;jF^exX#)R+Verr?f%(SApSk6oSdWtLOO$Y}5P!p&08ghFoiW*@fvT{s*V=DimIVkuBnzv7!F`av4C7+Pzlg2j#K|#5zQ9F^dn4FTJ zuKRO_QYnL8MYbaZipz|R1S1K52*0@1a8W=K4mEZ)M+LegrVY=?1u=GHzu$@|7*z|? z$PJ(N`cG}e$T~tSS}T50rY0p;BuMqG_k`y}X4n&)XQTtg%G2Jx*{Zq9bfXCaNGlZi z1Y_wd*;C#R`@Xj34kO9%f#>pI)N9Aocr0L(SH1=mv-K(`RylTTGLts8P>SRNXWMnVz>LOl@4eccvb(?#trI7EohE^inP6RVQO={@ z*KFSl;okcQ*n*{HbQ)RWqf@=#3Bj5-I`9Egc7=O?6Rgkf5v(uo&+tXYcCS;*_HJDB zdwe$(t6G&8idMamXZGk-VtZ$2zY%?rXobpNgf#(FBGK1i==#Jzq#GCOC5wSWv0fFi z6UBK|oi5I6%m0Y;g8F}q|1%SSEZ47L*tyw*tT`r35RqBk-dDs=U8#E?P+G-L6%@C( zf#QRcc0t$2zyd6{d%s{H*#k)K;z}3A-?*CI1HP~1FWmbTz}Fw}N&Tpuq4;eX^cSxS z!RYs1gC-5uO8OXqG!(xC)@uE*`s_iB6sUH|3zcp}^}((rYk2uvxtg3V*!CTstLhOz zb&{93PvF$?mNU3|(0?Lvt)eX?t{qrKCw{Cmrug7FGdGG;ULH(y%4K-eHgjd6LirKF zh%-Au8GEq;&D+}>(lSCr9&Firi3LF*m#~Z=y>l}(!fRF7IpoU04to91#ey9lprpQ0MGvJ}{7bhlO zrgxmf?F#i7;eR|etiH>jV0}27KvKg^7WH&E*fG1E35;pf9L;|+=T=h>K&H|En4~WpT3B4s$6OK5-?JrC6A3WEfD_Y<_0XTS@uI9=sFoB(F4d zlV#aAgB!EiSGusT5KDryN+m`gF}paPdd*24@Sq{dwq6Wbgjm|d1OQE_K`8ousBs6x zZ8Ne0K;0N>{1irV3;a2{!1qFpNw~Lyr(WkvnjI8p(S@@ow82Qj5Hs^Uf_QjFr{l+AL5zT8L7};v^$<5b?r;RO% zCj=v(C^7qd3ijsVy_U2>oRv(PDpg$m4}v5rvpyV8{8W=VgqPz;eK#|;C#r6fdM>Ho zA$5JQahoZt569v)Vp2Z)S=C!2wmXbVB@FRw8-POAfTgbWsHWwFa!#G7Hr(9y9w0j zHquSsqg*TKWK#^e~WzJUTgO;IaVNn#vzF!^7l<+0Kao6*#e;`1RpH z-+J4p-$1?HPQQKXZEjo&2TrKBne}j>cfFmX-@N_ z$JN`N_1m-FK0?3WskghVF3UA}j;*)5>f15(Hb{lBLcN`*-=pj8ZmY|VG$hvYebnl*qfL5Iy?cVy$tG7=mub+Xb>;kjI?53b!KtV3e+DsnM}&nr5TdL%2c*qHOZ&l zj-{_1$LT$Z`!Oa+Ie(wlD=X$tHCDBY8Yh^6b}avoe3Jy3!v92MZ=f-Rk^uv}I~B>s zd=s^5{1Zg6cbOOOm-jq%yU5L?%o-}T=QsooFSMgqLjP`p&i#-{bfI8qr&WKBb}y0E z4p!YMJTo!a1ysY}>*|wCC=GXzD_kfsWshov|L&B*cxyO}2?0;Tu``D=K0O>m>8<(` znI4bY*Bxu73W0266IRL_!SVRQaCA};H(!)!pY2u#R@It2-BpuT1Zr5f zN*)#%eBohXppZt;FfI(#kcS7N!SEL>lL?#7I0!R#AsrOFCOGCQyjadyeTdm*As90L zAZB82CZ<-3iU|W1zk<^^Bo~Q5ucJ5G+s%sX)c|Ap*_$XE+1t^&bH6^E)zLodVhg@_ zi!{-5J~w)Cw9&i!zP^!+e2WZjc&ZoZR{JseLog48*qO1X9nTlKb6s%2B{RI6u1=v1Frtcnt-~!t8-tXQ0J@^x?~d6 zSk7B2!C8@e&KkvDM0it<%l*p-=rSTHza%6@31dJna z!52f}rwdJL89;+f3&;*}imE~Yf9a~QZ}0yTa$q$iv1eHW?-u z_ce2JcDm$NC5C zv%+T{3Yx_#0;Wb4=my+%;n`jdvOrknxt|ilK+lH;s7JJ;HF_?8*SWPg1hV(V?NK$ zQ## zOrw)5vM25&5u(Hio_j#FL9`>$#8*|~Pvi(sW7q5v*@R>QZ46-z5k)H&gns8Fh#K)N zWL8|%n1T4+QUUVuKuF|Mrgqj5_MbPgE&iVO5KPy(5K8{l%00n* zTUSZo+)C@Y&mv!0b6${nGPLkD-{+6lR7Un$(>vLzoU`cF<1|WLL+iVoP#>I$0nFjF+na@$5sWs(5}A-lnBY|%fLWe2Ox zzPfuItelClNS?#OEbVjX?s-g}+awRJw-yfz4TA~B9DdJBbX5>moqc_h_-8WTortD&)rha8BVZE$&gVlG^XXi58NVWpZ? z7Q6VUmJE+FKPGa)c;yrUv=$#NU*GjRky_5dPG;u$G`6ns6TjVQx7}}V;ta1!CVPrE z*>Lbjhhz9agSRc^I<_u1JdGKuot3!iRd*$B)ktZWzG-bOcOLN)jx`0CwvahH0l^x~ zS}RQWO<$q#S`lM8+({>uq?I&d69XyD8BH^4Rv(!>jd!8!{R8AG{$AB_X1uBVXoJvjYEI)ki!K4y+F)Zl{ zE>D!wLUV}>{RjGHf(hv?iz;-1B|%0 zgPK}5k!q@QzWxbu1i+Pois%s6UboCzxXKWi5XJ|;XsmgM6QfntCyk0{?*LA&BBnd# zAt4>ODm0wE$Bvinsrtmti?x5PtW2!MjSK|8mVjl|xfXw%lZK<`ddCl_Fn2nWpX<&j zcUZM`Q038)ncMep3N({v`&n3K{p%I(y{%Qtmn)Tn*whQ5$jt0LjDohehD#G+YiJ@- zrc?mIuFIHZ0tReu^kQ?#9m}+~-1HsLd=z z8~NZpbUIzr8?H&YI{R z9d=A~G}hX!;@vW!;PdK%Y~m3}bpPORKuT-nz@)qWsGp__oP7nCk~gb5lAM2N2r^AK zd1wPKbG{dffqNnnPSL9F>FF~kq*|ym1kcDxf_Ul6*1XZ0AQ8G%d@;a#LUn?G zMRw!I?Fj;5knCCY+njRlbxwJ0pQ}*+u9^B(Ks{mN=o)xgwWjW-?0|Z2u$&6B+*QEH z3nW*X8N)O_ue!5~^&K%EFXLVuGohoS**m2rR+LbH)28!NFESl}MBP-!tJ57<6tr*c z_>}ct$LA|&bQQXKj0+PV(mk1(0Z)g=aC9pP!l$Cd8ui>DRn=s^Clf~NDK~+NC?_^r z!c^rAE-|0}rq>2G96A9!^Pr`HQIoqgCXw46eCNBh&24@i!K-zI%=}g)$MHuU#7}0T!I9*}Enn~%=NKwEz-0Au z8E+aqFz=eE5c}R*uI`mQu{l!_0WHi}ZXk;RjwjY%&b!3=C-81!7op?DxAP^QbAoV^%E3=pr73F4J2mK{M8sr(h>6^JhoK4Ku!QJ;;qVv`$q)lfWVdupeL zrx3XU!u+3Rx~X-!`~Ads0fws=(kPtIv%SD6&#N-$LtZ^R$_SitnHmNG{uUDgn0P`4 zTrdyD5Jmu#4d=4%{K4PD?I%|`^J)O5?*{{{@3ZLxQd0`-cUa zIpCG$=CU59H*vBGYGcz-)8~Vsw)mUFLM^oWgG9I)Z0fV~_x5S9sW#^kUJnLaAGHkF zZl9m24s-oSIGscoi(kNz%N8Q9xtGx&_x%Ts0F{pWrf@5o;Q?QqjrvW4TY)w6@0;x^j z8X#JS_3Wl&rgY=)u~y`!yYsSQ=O;$2G(YFBXw6(w#>;Qbjr+I#kU8rhczX z+bJur{+ac9(O9!o-Rm&sd{e^!ryTEHo%?kboAZ=B-GrBUZLYbCz(kUB)*jGgyV0!Y ze67M$3vg}rdsii3qZ2VcI9hzsZCTZq|;MYY)$+hWN0nty165w8&-W(*r zPmln=k9_Tt52S+xH10UEtmfriYLV~&euW3{6CS|d-jJ$9tg+P`q%E)Q7!faY9FEYi zIx+57=}gw*j*XkzgCTul7$C)ZphM5KvoW}fWrGCZ0%c+$*^{I;3w~I_gKls?vI3H? zJ4t6k%|Gh)1zKO-dpqFwDTSN;#A#gonM+Er+krG0VY5Rf%U5<4C?1jJ=v6Cp?wP?2 z_9aPc`;K|7u>Yl(455|Gfifmqq7D>t>PrXCI`uPl2+dP0rFgTMyu0*VlPgMt#3meFYiy-S{8Z_rfe3!zxrI`A%D_|S#X6)KIQQuwQePO1We0f84kGht z_6wc7%R8gaMx60De!lZY7`Csot0%jb=*lM}$m(=f!>P7pyESMGnnKY}#r9Ad0XKkZ zfWxP2SWLRKaVj#{?S;lH5X-+Jkg8P>v!?c6tw|2=&RD*({rV$1e!M#2Y$l5V~%2r};AC>X_!$s!% zP`*EZA$@U*`ZE76)(eb&XCAdBkK>cqp(E2Ba#^~^E_1h{U6GzyX(tb62f3fS2FCf_ zaE!KbN8qHkU2VgRt6^;jeM7J(eqA!_2~@kCbD#Lq6?leex}~S63Uo1a367?(nlf7y zg=?^V1HQZ747(K?d<3f&YFjs3z-IOoHmAUxIPTA7T?zDqQxn< z(LGG~>E6(T?pYkHFuf>mTbkvrQkqWQoxW@^J(|Ha^vas6+fj8<-uX3aYof~=w{;k- zJ!5JwB3;y99OzOX4&=Iq%1gH0%yo`i@Y6b!?sST4o7 zx;Nz{)*-m0w*{iXblD{^Dq}7gyOPmzZLbr5B%|TS6#8)Dek-e)znZgs1AP^Zj*UZ8 zNni+V>bEKuJ!ai++okKQh>jf%agX(Ej2&T)P>)auPWfDso3&wc)a-=zcF96m;IzI@ zhMO{=g&le_P^02aBMSLxb=0P!x#77WU33hJnGpqNDnn!qG_->>jjY`~Ri5YEa@W@g zKq=>vM?Zs>O8tYz64enJ4rw=QD5sZ!deNa9uDDGd%yOy-F5@(TRH@92t>iJ_ER$A^ z4fsZ@xC`p{y_axwRMZ7$HtZF;jq&S1w?>7E+(T_Xj*x-om?F-K1TBwc)?TDuHU9Op zRMPLfW!TF@T4er$7PL#fO(*i%gF6uKFBepW;Pri11j|4-qcodAW1{q3Zv* z>%Xc$@bC6Nwz5_K>62~7kbk{p+L#;mtpi5zt*kso3|q6(PV}8aso01jzLg}m@l?i> zom4*bzT*ZeVBLs9o{Ey75Cy8{nP%+7DYf3i+?e;!sm6O)^&qGcn@>$@*YBWdJkoH>tQu?;mC@ZCdE!1NBkG_NiXYWdFguTGe6_y4}V8*sFF%K zroYPY@L0NsOVSnVYv+OO+~lC?n^$P!d!h*>C)4AbGS_PW!~t$49?~$ma9+zRqHCOW zw{*;Mod8wvgnN{qhwEH0FpSCL&4-854IQ?ox2AT?N|u|<_eZ>HrWlCPJxb9iBnsv> zjC3WYGCGC#@&ImvL+RTnc!r@x&bkJ-C@X+&d^Zi~wKF@Wsez#flQ{0c-TKyIZfk(E z$zCO)pIxgfq~NJz-%pLXrb{&BTN_MmHj4-IY%JOneYOC$_y~W^^Yvu+$wLvUUWsYdVR7)Y;P&V_m=Gj|=yS^a%%ho--3JANZ=6HQpp!LvIYY9R04qV;nS z+&<*;@w~&>hq`Pn%r$a7YX``e9}61aAn2}J@|y_ciTMgH}N?=ahp!2)$~h* z%foGMwx4eg{bntZbEcc|N#)UL%m*kyAR@+{D(t#bfwG3jT%GtHY^MO1hS2;kTTlP7 z-)+4JMwy+bC4cAjt;@gAMLUz`r)AD7-O)6+@z!ssGd8WT4Fn$jp<7j(&r%U)_Hg{o zZV%gJ`!Q-p)38UdnBO4dHzIjE2vlpTqm%rNrN?n%}q08fidLB8Qa)7EAcm!;ceFQq%(Y3X{JK*`{W9R;$I`%4upt!etA!s z0C$b-^;ee!Yi=FN?GyDJhVXc5Ry9{fNM(VW^o5UahowaC;`*|&W+q?00!RC6y3EHT zIknj<`Y;TlTgKV=2*JQ#a`Aa1WP~YaA29_J7ro>`k7Lf(DWwG-avzkkY=kL;AkntW zs@o2iGWu~dd50*oem%IYnxN~VM3XO1%qkL6xB7I%DeWh@I~J={GgrN_&M1JALY4p*jdiCM$oU0d*c zb>HE75UxC2JAdnXxRe=ly!CKUJEzxpWyF4?HwfbZ>H)|oiaLj*+M`-+<{O=XOIR6y z5#Fjo%|}eUaKq`vY2l$KZtLeFC}Re~`jwep)0}t%aA*{vH4j~A!g=lF*d|(a zP$zoiWnfd?#W$yX0o)4A?ma)>_dIya*YcW{Gmp_=q8 zxu5=gNSuFTFH6IluIl!&F=MU8gyit1*Dcs>d}l43p9DqMKo8IR_U*Ly?OW~)I;GD+ z);PZ7EYX7;m!x>WKJD6!S#9}z_B9!wTcN>%G`Dil49v|w+`JV5)#kQUo7?~Ib41%_ zNJGx>Scrpa?}2Yvt2&wq;bjDSICRRc`wo{f&L%}|w8iqTeP-Z=$)5ezLXXAra@Avh zVoanHwg?kx#Z@lHW}_*X=1+A;P8Azx;#zT2P$6b1Ebb4S zbwl^22+i>k--__}P#SN}lT zDF6Hs7`iVWGnBolqySxVdWQ*lrB^+y6P+rOq)70E-45V8Beci%|4qk7e7<8kC-C zJd=ZJi{<7#K@v9Q!TA>$=j={Ft5|kyBah)>gzKd(g-$Pd)mNRSN$4eYY>Q)Vx6#M& z0ax1g^`rf0eAELM%GMDr7~;Y=-9N8$8;5^O=bTHQ{x9~dMIU5ypEH|F2)A7B9L5w- zav^9s{^Xkd?W#M(er2pip5deZ)tFEivT(L)F91`6G4w}49}4pJ!Dz<{9Und!xR>3_ z_!ouIp=14tSgRup0*^8A`$Rw5s8voRh zdJ!CPpwPMS6mhQPl$~WBpq5S_M;Yw=wW+hdp*q15t6LB_xsg|+m!g=ywE%3=)MVJ2 zBXe^c=p!#4dsA`CUs)lH~OSfS{=r%e~jfFAkphTTq zq~8`8mM*U@m6B80%qW%Duw_z)CMM~K5q&* z8joC;MsBo>kGRO4T;*ghtmKcDSQ4bTyv)gAkydq{O9qDw&#NAu`Z4+mC zhOeq?jp+;;WxcqF=DKBgM44eJ2iL1S#BVrCh)I4mboEGzWA(-=T4*jtQO)a=j*0Ci z^Sdyxi(f*jW&t($67Jx7#9XEx;x0h*uO?jjR2q|O_+r(WptbReA}+ZstuC^y@-^1t zpFKsA9j`9qQyHIjm0L^V3Rzwrt5c8cDAuK#C^KPCnxQzp@1XaMkGRe*^-!R0s*+i) zq7?IkL%&%w$TvNk{g$f1@Qkuvm>vZl373|*?5!r*UG6U8BX=pdM^>V%IOX_&V-4Tr z*5k2GrhIf?*h3+RH|#Wy5zg69h(uZyMA@i_UBJ25$b#VfOW0)dM)uZxZ=x@u*z^)z zj`OMdrEtD=Kj*0;`bpwKcrQjF13uZP)xq~Lf;-q8J|<|H6P=Wsg%e5L5`)E0EmF4F z&oqGok-|$k9j^bHE;kdPLee-pV0@ZQA~a~MMLa{43W!g0RXB=;l?UGFyUz5#!6}lZ zK@B(40m_?|hL7DM669m|7{AF&1YOiHG|$ZNB?CID)c_v7Fclx~Rb1A#V&#$ZUZMG2 z=u(1i7s4Tg;1KYXap|PG_SBCtW+iE#=vo@w-avJA~xR-@oP z3{4DkS$DTl)XUYuO$yrr42Jp>CyU+H%^KF7zvdeb@?E%weI5`_EL0x00y9s|K$>2n zRkBIl?KZNv9mQ{zkZ)0uf{_LFmlQgE&fs`GKH#GZ#cdyqH3AhC({m$%ZE@N^u6Qmd zDp*Y@c9n?>1Gqcy1SA(pB9`<0#JK(u4cJH<&vLxBBt zHdZ#D`=mLDR{Qls2BokHlf_jtoz{*!{-BK;02G?+9Yj$9+c-l`3v`I41+~dQC*@q8 zuRu9xRK%tOmkWT)wP5Vmtd==s&<5;9zcG97O6yLPrcCLJLJd{%FScL2(p(GyR|=f- zA9D}T&9_N9p#y)aaLokuYp6_fQK*HXsWbOM=*7ivm|SJc5=JN+ixt)dvo-h1E*AY` zBsJ6B2J;dIPR(0vu}ugkwpU>BBW5aBf6?z-3sodLW-MY2W8lM&x)-3=@ zq0r5(X$VQts(@`rr#J7mx*u+fvqeZ6hc=mUUbshPn3AB`XbUawN@5PysTbyga{$G& zhy1zpJ-UK;pe(59hBh;1_IUh?O@CcBRDQzlu4eiOQITExBq0mkBtU`gp_`$}p`!}| zHFQbj!foAcSySVvN;cVDUe$}pr~HqZ--X71MVoB{?`*Q`_|Ya?&y8dJ*nj7b{&Cgh zuhbi>&fNsGx7wvww%uF#^lf{q=0MWm1m_`Q4cqZyW@9y98OM05PB$(bw5x97qbZfK zrP`%1#D-@Btz0y85xsd!Js$c6sxDAr?Qpl5wzb3U)DHK@@4GvkiTxgXYLT+F>~Mb8 zg%h~O-QhMVt!YFdKW2whVtR+uZl@iNNY7W<(c`sv+~J7cm<2!P5qG1zNA2^)U5zXb z<xsh!OI zPVTag4P&$W!fbXPvGj6zJ+Gvdu3Wz z6QGejubP^Y9dHidmn|mHUn}`jc9$(gUAKsOPDC_}da|lc;BzYf#&g&%jy7g3>hjk^ z*;&`_g=XL&YyV9DpP*|gWGLJ>h=rKD?^ZKIZ)Y-OTq9%F~-wm&zEy*Py67AT- z@)Cce>E_J|WKh?Ib%pwcD%Ma|#Y%XWq@dzf@xs6&^XpoVD71VH?_9yJG+WA+=5zKrw^t|t-RZ==GeP;_cd$u0h|fuK=$h`2;oEAx&KgE7TX)Rt+mH%Wg7bLi^NhSfMRDHtzS0a(8U>2JNxJxQsnE zJ?Y5fTCPRIqmFai@3gPFNGCin^N&$Jp_m|H0;Nc~ut2-60V&f_q&@)JdrZ13(yDKZ zc>mF$X5kV}p-dyyXT#u0-Jv;?d$lLH!T=$-wr3$GI%}IH`&ec7W+1(vmhmmUpQ=@! z+X${G^X%w3+{yxmHw&&VZDVgmV7*0Zzy*P{2n&$*IkDqYDa^5~j2k|of(^_mzNKuk zhmdBaOTeHSK89K2+1q5(x0#ooF(O6+sHcK@z-wN<~SKWC7p8z*8mx@kF1^jyv31vLGOFb~tmV#brlZ%d<6n zbNUgXA`~ClEYBKARuftGPm&MXd9yS|Z+T^GzTwh=R^0%l(Uyy=%}0od&hEbZh`OMC z_j2-uV9LgGxwSUl(=T&3UUT~ZIKPODA+X!b0Qk0t{LY^)ceh^CfsCzJiRrCZ+psHK zHX&RN5w5x`K_qQPNF>b@FO6dOFp}lP+Hh@E#W+bQiIbrd*GPNZT2xyJ_y6)wBmcL6xtKgif>M8DW=x_iQl9p z%4xX@eaz0P$obk?WBjtSzHlnb0JR*8K*C$3q>b=qVgu%uWC(9H)a~4Vo)O+k1|B55 z<)!jJI3q)N^VZk(@a7WUPPkP2ZiYS`J3G9K2ypp|^M|`|s+ELj^!-CxLR50FtdNv{ zpc`@v!3no@NXj-L8>i8}-K@@tDTy;9 zhV}(dch5?$lLh@DqDFpky4Z}}&ROFPn~^czR~fG}=yfg&fYE>$_(~&hu5DV#i@e zq;$+9@JrrU=zXimox#-|e2fgQAZ}D4%m!N}`iT#m4sQ~%-2i*D2ZMK97$|3|@9fhQ z59E;3F_9!q$AjbCJ|5IRtQWGJkJtV0`d9hJxAt!%OYJcITS=1omwmYYNd@SP_+R#K z_3Pi-zw-~@zkt`jiQhX6{DdpcA71_6!SBRx@854uIV}8YNfP{4UVP~O!JT*dUv@6~ z+P4C)-Qhd;$;oc#PVzdJJ`Y7l!4(_EklY!)liH&z(g$zLo%@(;7uWqRu2*$%kIle% z^N{L$H}xI{pQ^kjpS-9U9`K~E$J!as4fJa7I?p_EfK5d4Zqp4kpZ2u%^d_kdGHxF>3`kGs^Y_rWSO-WW$r)R1k_?S!jEX^5NKk_fBHev1)L6SGaf+S6)p&NntX2a7`We*?eLc zymv8!CuVqF$oCI5&YfP>FD2`En=(Bd?|*hh>6aDO$S-x(go&L%42E4Coz1w6OHZm# zZgyz7t_Ft|)Bx;};ZXDre?_!fk;n-xHDDZ_Nd8z&njqI)U*VkhlYOb=I^1=heeWZr zM*LlI%PA7kTHhWPCPsi?d^zr2Yh^txpxV*wKr+#S(1)0x@n(LedGn*#BkuelbGMnA z-A?XGO^pdrd#I_|?QCDsk|8}cL}iawa%giERu=E+a`f^*o;x*HsIiS=*^rh#cV1ni z06zA4?SF(Z=7ji#p;*MrB@Zc?Ng?O?B@)E=;C#-*C5VsD~SPQ$Fs zi9+6v4l!}?V}t+NmwCEdje7I6*ZJu^%+q@2>Bx7@JpCOtjXO`p?KDrQ^yOv$X;bq= zF{tDxhQBh)rj7x!WU{J`31h_)icKR>@Yz+p1pU?7C0B-0BQnpwqa8h>8qdc3a!;qf z3qV{*u>p|J_?|96Vkjq=p#X8`pLZB1s;V;RwBgI5$E$_p@0-U!RBz|iyBPx!k_V4@ z$Be;h9>WiPd0EcbHCC1(`eJELWFQ{Br2AFpqi!<2@2%c9TKqeBau~jKArYfA4A_XQQb{B!hpA*1v&XxkdilKX6rg;JZ@>wpzt$ z{=3YzKMErffJZ4On+A;q(#Z(ddGp<1s^hRg8nESye_(;sgN)R{fZv$zQjS8uN6x z{#Ie6eCYlTJw$(Jo$#OZcRacOr~PfgFAR-mR4AIlm&OYYs zj$ID3u?J1*n?gipqfCq8=jmRFC)n3?mZe!A+bxF%KR75mN6C! zMY|GrQua*#P;?A#{v!%;ah`OAE(jh`6l@w%A~lT5&kXY<5bko56gHz}YfSb1s*twW zGm(GB7%aiL4h@Z*-X!XbK&{px6h+!=RSr?-;(#sB*|CZfM<4)$qzUKlCPCoFKpq-z zKL?HYe((+w(yK#P@5*k3-iH7VCoG&*?_GjG3|SO8=c2zeYV!&@nmCoWmIlQdUX`l= zt~AJG5K^Mz-)$_hS&E71sR6gu83n*dEmpJ4QPOYh1v+Apt#pOvQAGG!jYSv|-2y3) zmCojCwizutv@o9k+oPn^R`L}Sy~V_0ccROQH*-X?pZIL*dt)Zf zi6=RwC+T2ewKvS8)59d(D#P4!Ji}~%7{>fP62*u5NcV|w1oAp!fuAd(<^ycM$(E7QE)Kvay^p*e+3J|jIIB)%YuHy0B zXL|g*>!%2f&%x{G9B=)c!TL$%ucY>5;Qw&VT)y&uzh*9yn%l)-_vR2Yy~og1b8|@R zHFGj|i2RS%%#LqeGY|J{yJo5;wq7rfPsms=Q@XcZFJ~_JPu9zqw|A#*|H^u}|CoQZ9v-+dV?F#JukCs`A^M-JhmWTH>-A8%wAFfeoFc4;qk4bK zdU&(oTh_zVBx*hE3I6Bnq5m^DYi06Rt=7Yx&m3YseBV0sdbkisAAUWYMQSQPm#%v2 z;oYNKuZREF_1|3&e$+?*YCSwi?f<9i;k@VnTkB!(QHNX)owNTd>mf#Kt$C`p`@@ef z%UBQJ&276Lj=b|fSr2ba{nzVZ+%v7#!vhpyJ#;w!Th_y>{BKzg50R+#;0!tBdYH4_ zfg`Dia>L)7MmskDs=)C1GftLKt2zE&d~;u&{~LXc&Jl1hf*I$WkrDi<|j2{Nv(_0}=!GZ*3L>W%i&7Q(KI=G-hYk5^e&L30bp#o7|}D~JrU6(Sd-%3jn`@5Rjd33NhEdesbnLJ{cAkvM6zDLI<@9k1{3PK z%ycH1--Ur0`o%CJ&-^YhHX%CYm(UbMkabOtI|Ntr2(Pv@u!satnOB6wi8;aOB|h{( z?to;+p&aHe!eT_6pm8lANU74dYKqWYx-{l5zBiOxs|@+IA@ zv-zd{x^qDdkmXXcU}~>AG*TS_mt(JBoGAEJTWX_0zgQ)d==6CVFUAaykjp4Q%L6@a zdTtsq_v&;ttYpXP4ypW^-6eT(ON_B%u$GhuPN<~7csCeMqLd+3M)uQ>zNU*ZMexAm z+}gi$7B^@tqA}!8#h-5;ETDHgw$PZeTiq50<|^qpu3b7tyAc%KIz!8|{f}g4!n>Fto;Zn`) zBY|hRAW!}tTmx^_oz5Hik1{X4&C9h%TXTEy#AS6oP-ezmqcogJb$g3o$wESpfMFn3 zOL>J^SrhCip&W9aMTgE2ztQH5J8ox6Ch5HajG(SL5I!z4#W7+A@LD4h!KtN)M{G># zr2f^Y7UJaq9UNk0A!-btK9Asr*p58JAg*gqv~y`}!hxZQ-8xwZd0bS!1Y`DFD5|*m zg<88tOQCSAvl*2Ae7X^Ci3-Q3mBGT763&#DXoCC4I*y9>SZvgfR* zr%&P19o5Oi2?C2K*Fx8NA%6Qn6e44V;*+hsVP#q<6T;D9WdumG1uCA$I)C~!iI7jk zO64Y}9UomL7|JARf{fXlhK<*_Y3(szsI>Vi8JQ8?LoI3Q5ESZXS#=6RnYf>ER4RZb z;SHXrTY%mjst5pz7Y`oJfl7rIdc;6MRFWfvgBK+lel$S8SM7&e5s;ZhY9)gxiW-Rc zHJD$|N|p3QytB6P5`MWIIWTq6%rF?y7Bw)LK?&Py__bpkZ&=+fV8Q->#GMIzl-1S$ z6Ce=Q32HEIIM!%`bpeGIL^Oc_Gm-#`DDF`yVqJ)sXxxKIG?OvbDs8Q`O4X`uQE62~ z>XHb8TkF1WXceAu6i^fpm-&Bx=RUKAfY`V1`_JbC^DOtd%eiO2=blT_><8~J))A~T zA{T6@5UW#FAaVgINP5hOOv;ZOvqS7GCSF4*m5ItUvmXgC63tZ>6qA`YBT~kv<;6@& z8M~c_%!nMDUmiJQhfw4UhVHNqA%k?diq09=-$_*huZ?uiJF9$c!cGVf`aZ=Q$e`a3 zA%puSrO9B@&W;Se@R`qlLIxZzoka%mdZ`I6-hJfWD|peYs?>&HUn9siVB*{^Hy1_x55TN9=0e0++`3 z`GuVnv86z)Waq&3087!1u##Qv<`BC%eYe0B>|qw`83^lPJFT&vVBoiu*BR@S|B>BX zU^l1l8MvyJD_|b{AuvB|VjAXe6*-u*LEPZY>~Qh@BJ0s5uKN zYhOWnJo3xH{fCX%Ln^Q14_Sd*V(~6ay2R^40~JvT7l?SK0gsDn0aF67MxLFl{TC=E zJ~2?HbgWKs-U0N?QcMEPxq9TI`Ic?Xu_4Se_uIaHM%UgPN`{y`{Ep6y+OW(1MNHBYI7krjTNomF@G7@ho zuvtda@^vn!fW>lc5Waw4?!xag1bS=eTi0~oy@tvfb$Rgy&=7K-C7kYJO~l|U8nD+T zpjgv-QVfz>uwX{nh*#Xv1G=jH5jhDsYcW_h{8jgq3#47US5h{7ssE&9e179oH9K-# zA!!mf<<{hptMId2X$>paX}XC&ZymO8&bx^?N!o_JWXVzq;$&ckl}dl$&%Pk1=dNSt zUhFupcePGmLSL$9@DkrqrRKU)5m)NXkFx4INTt{mIqDo&>NHpC{_IkHDW#zO zWe`ejUi@Tpj%TXymA(o;Hc|Lc4{X6vl=xoFC568Gk6N3E_wsJTE{n?vDcTe|iEom= zn`8xNsH{oaE-HIm>UPRWSf>-bnS9EJ@gK#z#k;sVAU@TRQ$rF*_idsukG>(-YFOS& z%Cx!+>*Krb&Qd*eaN1IJ3ws&<%%r{ggl#ThX~VZy10C&Dq7t)>)eF}2Oc|>uiltS7 zB+gi!DqHpB*HX6XW;W`+#b%`bo-fQu4NI87I1tPoHpT45n@)^W*`{Wsx?`38r$*{n zXQL9ZFg9(XhTy60K33r<_mq z%NJOD*XC0a%E~fM%OA}&PNNj*V4V84sdQP#bGKrfq8FAIlPx|Iu`{q^MK@=gZl_|C z#bkGOwy6aJ#KtkAE!n2;?3HPo{t~?|HP#NopmW=l?DU)4ru}{Ouo(~~vEaX9n_9%C z8K^_IXrLa#8Bbc?cLwS`HknC7U%5Q|%^wV|jS227u-VzBHL^`Na7HV(>6e_(D%XPr(4YsMZdg%Q;&}Qui2XX8Ev$Ks= zLQgQ*zt>pZ(-iomU=K8JpP{|?;F|Tn#K4@`H&U_E3Grf|aF6kNzi+%c9l5+^zB69W zbiitSl3(!}tXIdIocVeb#3DZ9?ALL2cUCb5>=-UpUKZ?MnG54h&V-%It-f(K?3H$R zRxw6w1D8z54>DpeIxIaQ1A4g$A=TV}J|S5~tUn=Vp9^pLMaq)3&8xz#6XFIIWm2lwm@tZ<)cU@ ztpbg`wZ8K!u)R_iZ(7~(#l9TllC`e(jHZuY14M!&^^PZ zog>M<6`QtGrDY1Uong}!m`%H#WAU+&nS;Jfd*P8=v1!euUHL`Yq&;($GihH|r_7{{ zot!dh&$$Jp_&$^Np}(h0+UFn0FlpQW_=8MZ>X#;ezVq%{QJyW zzk5^Zo+#^i4c0eAO#9R5H?wUkgnF%`y3ID*_NzH&+aCU!vqyV4+xE7PecSfkbFI`% zu2gY$sY_K#w(TNU>TXx+yH~U7I!2{r+g|5NUFAx>o?U7PrEEhwM=@jDo~}*kSth_# z-?ptx6uz@@U`~9jl}M(O8uLBM4tA@#Xb#`sY~05P=9PU)8(X$<2dZq^#+^0EY}`gZ z<-@d%%NF`hY}`HQ<5q0koq4Uu+2i)ev~jcS+h(cA+WF=CsoeJYuF-L!yK);2#uPgg zJF6mkDjVw4XNPbyT}YeXN!1`mhi#y1nd}wO%h1ZmpTNB=@|JO7b^>FSkHdx2Xs#jl zNt6=ks5EPY8-}7MVv5Z#uBWiqW$p%+LEI*Kod)jlPzD(5pjuO$si^pB`oqHt<;rmT zd&^tu%0z{-utSmk&5IN{iCRx0ER)^uNGb1z8$rhg*8$?0pCG>3-OWjx=uCF>!JI|d^2aR&XH(qw-~8fM2t7-$48D}cq#uh_X@BhtOWe%Z9E zU)F0>QDD&3Zu9dXH#Sn}z*g~N+oaV(;MZ(=7M`ldc>%twRy|~N zXF*dIx$^M3Ogt^19f)D{6ch-61F^JH=m99WV|Zm+F^HubW-@3G@Ao&*vo`9r|IvX+ zZ_F?{hKn+jY1cA|Twx15-9vae-{Ju*ufgJG`T|`L#+-OX3^*iyHaV))e_dF_bnAJc zRzPoN(i#QFUwLe=9RI^L+8Gnw@1`#X<|JMTCzgiiJYhACSXX;{>`jIoj=UXit*tkV zryFjoH3J}Pat^5R5HPg%!8LNqGPc6)4t_V`HPA$McVwPdS)ce?AXZPlAt`Ic$mzv1 zNqFCSN%2fCx>A94CuoZ?#Sd>i4F5XYhK>Q2Sa&RMHvHWareNV>lPz#@ny4mT&6Q6^ z-zDd+JNvO_N4=QI3#-Q)P=bE^6mKgc=2*MmB1Z&tmC@L$^&9!z0egqfb^15mxt34# zdxQP@ORzxvF5Me?d^2C+Q!M=H;3m@|^^M4Wo_-;7O{d6JWF#M+O)Jhdo9+?o#BHkr zO@Frb{ihMC(g5^{_lqf7zMigJr2p2+OwfiTXxb{XX386fU<*Y#csoRaQtxL4>XeSp z;dZiD!mToBKoP?spx9Ta528?BmMAOY#yiN|<#>5+?V?Ijy}&78@qTUupmZ&tiID{5 zArL>)n)p2Xz2EUQIjXqVK-=h%e@yacBEZbbl=Q*)mS0YkMa~z;)x5-4y&>;44zj22 zy_PO&TV{XL8@krIyfwG3MeYJ@OZaAOAxb!QoP8;DkoYtGE>b>56`tvXb@Op9rcNRwVoEonDW3*2%)$dR_(;3L?o7;~ zUDin9bW$f{bx+zwsP!gae!u4@x7+(vF++BroXMsEPNriv#4EWD8mii~O+Z+HzPh#@ zD&Nv&HXc+)zKb2dK@$IA(@-$W3k|h7Bx|Hw9|$zO4=#X6WsmBdPX8#<_SFCzW4AG& zDjzC8BVXqN9wbmKoNiuR%@86V~{N^T!&#{@9 zbK(;MM>sOckk3F?LiQ4#vCm?Xc3g~SOG}S1DP3Z>>kG+i^)%4&S14;6~@tX zI0-tVeetOj?TeWSdSYT?W~KZJRw4an(KPJG=SF|-oyCoA0oLUntP2xbFWco+B?p;%h?!c+#m82a*59xL3?k6 z5jDH&acXv(_LfVhRr`A}R$c*K0%( ztPqt>vdEay!O}<;Ba2DHh+nS9t!R^RP84!i0*vHcC^A0iwO7PB0fNuHJ=XvTv@OS(m_D(0Y#DDD8Co@@YX3cGUZ^8_=)p7!McFfEySL1X0JLf?Yf=g41b+;rG6VIrvYA}@ zE+*S>gLmlmis<%X74hXM?F9w#$jQX&A?dy53G@+dYB+2{KKmrPd9%iY9*6S8d*QP{ zj`uA8W!aY-X}coA`Y9>e*kx!*SYekgwD)OJJ=`2oRPVq_+dqm>jKVD%zV22tn_azu z#-|xIO5b4Amm$8g!{tyeucuDHV}6W$%h;>p5PQ>R_5< zg-5h;q8^*ly@TG}9#9h7{HnH;ePaW7K;2weiGhl)W1DI}_b%?aF##+?I|iCo=ryC5 ztjMe?v8M%kf=<@PZ_C(0Arb^gan6tSPdtt#(7$DBNoh;%{>#P=LYqHJI*{$C8%roF zVx#$};=#@3#Y3WF_o1H$>Oe&HN-rT8lgzu+=&T-Ah`fc(>^`7V1>Q}&ISkW#@7~;v zi5Le_(DuG9>Nc4vXK0IJ^SCa}6T2>B<q!^|+q^)`E z=-AF;k97YB_4Ko8-K{tDl&lNNP&8PrYD?oWLDg0YF!ZR0ojhEjgV|RQK-z6>D0icj z2?vrxLlO4!qembYiz)y)m_mM;Tl)Io}d;;qw_tbha%gx`xs4}QKUJ=wRok`b{> zmG&#qZY`;9O`U9=#;+Wm4u?2LK%4?Umxpk98<(1}QXzLeH<|>eJFPPE1g9%0U+m*~M!3^>$#gUGziz~Cwj?BmLaS4r)c z9^w;@(d9S_9}ggFdr6^rj6q;spx^#spv6Xw2q(3Hikgdj z??J?K>@}aNzvCCG9y2=j1y`cOZR>G3^16Eb%+Zdj=`oA-qH6q9>YNgb9}!f2AzP*F zpBuXn(O2PYEy#L>Pu45wCui(tlC`$gPhdh z&?hHz&u@&Z>pl)p89UBXgMIot)BviwqPOUJGkI&otaL>c(I2T<*vfl|9kGXV_jN)O@qY`VDxtnVKZI`M#qt&O(o`j5bbKm7)zN z-S7R{k;duyDUsJXeZ1mljPmINS?68Q3lW#q?6 zQV4SfNaw0(h8}w2f~}Bbr+VmLNzH~HN=`4+LsJCSQ5RMEh&A6?s>S~+T{J1pgnVf- z+vl<*nH6mB_@CZ;dv1WzwoMn6NW8x;MP~mUT@-QP9WXhY%*1tILjNUQG#6i6Owvj; z6-?=(1AeG3s-8AY+Gd)%*l6l?>7t?5Zm(}y7nSsYi@oR7{Wc}EAd7<7j-vV`RUY3z zW75v~(g<_OzdI1-fQ^nY|J5x;n0_7|mp`Y&c~Z09;i%{>9d_mQocOX>!o|nr# zqVu=jetRoj()n8l{crXvopfk3erERdmr8&|EW0#HHdHKvTa9h`d038BTM8i5fUK?6 z@Ah;mzR3N-sT7GXQxntGOEb6i0X< z#S#9>m5K8J+h?RGMd!Pp^qxAkjzeHF^RI)-j*=^Jn(eXEDGX%|m$HK0q!Vel40fs6 zDcpKuP#Vo-Wpnaw?4tvMnzcg7`@!x#>)QVNC*AvN8udO}TiWnl*FfWbbgC>mwLA%# zNUR)1prQiHFW+1Eg2fLBO5}ggV0*r8*ZsUWw1dr zRttor)!+`x1YVcJwMKwKd!DJ%EtpXSV|MZ}U`&b4e(XrGDvbjNgO}rvLy26pY05ST zZnkZ;8sBurw$-jh$vCq$-eF9>rltpgklF(XSzfYUjtgprFcGlhmv_ElHMLW(TI;;| zURl`1)Xbv-2NshrzCVL~BG>W)l+ig*&aKeHVdX>y6^?c*X04)mv<9fuH)IEH8pe&C z;WLoPm)MV0td$6h<=G*YQt`F{2{#hUkkSv zmKPt=zM?DpV0L3E9CIrllm6BVEtZS!`!HVaZyQ<+^z~OCYQ2bULziI;%6T#o*Wu$> zT?0}`Vo-i-{FcxJzl}qa-Ho3$?v5K=lgPP@blTtWJGUNH?1yn}_GYK;U3kVG}F)B{f@7dDoq;*L0%zQsbf?U zNOFX%Wmd{svi91@Y|~>aI2OMdJ?>h2Ofq|b$U?8>;`{A!{w57hS`9SbVl(`MHfh8! zlskDitGJVipQ*i6GH;qV`#kFM>MGKEZ)(p)<{8UF=9w&-TdYvb$P^QKOS$Ca43l`0 zNnG#XXSr7(c4R`I*lH0&Iw~8{D==pG_EH1)H;dA>t;}}FxedVnzlZm7AdrAB+8C2FX5f@FtIMZ)fVJH}9 z3J#Ks+?Or6D}{ z@HB+?qt*Wa#gZZ}4dHkx5WZonQmzBmpOVp~KZMg=Y6BhrgDxc{UAjxt)TKNp>;{iD zF}4@!^$~XG|HX-(-Y%y3xkr3q*UObkkVl?Ule0_xS*0ZGJ}kGo-f*S%$u8BPQWAEL zx>EPKQr)skok*#Gj&O_zov@Q#F6NgOcAK6Vm?JhI#%|kT9Vupp8Lgp+^t%JT+`7C| z6h);@EPd9F+~VLynZ^(Ol!I^ToIdqP6)t zg|W6wJkiq>`8@GSuEi5y*ROvvc+o)qIAWAT!fA9rx9(EIPIT0vW`9y19=x zJ|AdOVjHc~2er%XTI%CXqeZMNI)yv_6l!vdM5xx42su%#2MbSHh?~K2lJ}ckBGKCC zw(rojIetnRT1inIJsLIh+io&%IR{n(umT|Q+2x7VLjQ&O6BFzhl_9G zHIi!{KcV)Ig5@o@+ZLo2w*saJ4+*#M3m++*gym!w1Ii+=Bnl6EdSDJGmW6wA>I>FJ zy%yjPft4}xXE`t_zdhE?!ZfdDyyeKFaQm9zjBr=$|J?J@rl}) zt~M?y?q1F^6+3@uq+z3LrFKY&>=tEhYr4_R*FqznXHsytHoh2WT&TX!S=mO@Wplo( z=O@r~t5|d70~vL|L6hYTpZHZG)<=SZQD1KO0UN7^W$j@nauM9-1!qLY^eK;=mS5g{ zali8B^I#|79Ft4ZceXCknegDb$Innj!&KPh$sAertFVJI^Zo zX(j=jdB*lANSt%fL=+}S{%s}k|E)>43t)5y!vy;4fFAN}O_udUf+H4x1U*xQ-L9X< zx>?VTTMn~Pn4L~v##6mKXX?~2>eL3Wdf;b?MJgo1G?*pLEa!wOQ<_$j4Mho{2x1Rk+F=h9{k39$f=Go9-FG`O> zI5CV%Ao0$)>ARGHAz4bPk5VRq`5ILXX&RUnE** zpp|RY5-1|%%Myt}vD!?LIC@1=BtjO0yQ_mhP;xPtl#4=7MKyJhiv;+1IUa6m zg9}kjL98p%Egb!sC_p`vho|*aNw>q0Zl(yfIwy6;foGCy>;=QLms~56Tnhpk$Owhe zWoYu{+7)U?a_#Uux{;P^3Gap-Gvu1VWVx>(q~scLkBh3PmOrVct9`kKM5*(3ItZ~g z39$xee4@>{##Kv(U%aAqx=s)$Xlt3ynOSupvV?N{dmV_Vll;VXU-;NSMF0kooN-ef z0Fmu|uqC4y2UMCY^v9eb3#({_KNO?3SV5GtmNrLKs=h$W2E*yfw}%e2Mip!O0lBzb)>~wBBy> z>mP5j3TUz#w&5flK4S26i_2GMZH7`>*5QI`iQ8d-4LD7u2&$=~<*w49z$rjX3kw#;@yPmGm zGyNomPsWI@f1pNb-<9-uA$5GjP$fzzGk zIGu>}2XVTLkw5slme-vKiZ+gpY|ebt)nOUx>TM?ywr~D__t`x6*=ILoeRepXnNl08r(O9&fH@{C z8ODkX6-6@+K)0*~6`x4muI*1ayACtDxg~pQQ(`p*Oo7;zOZKILEQrnPV6@4vsvfl z8MNrUn%QS0AD(l%9@g$WV|kC9E=%Sd`it{som1OKcezXSf6kIA92*o25%U;yTvl^r!DH0Kke zWVNzxrapiq?1gF2RYhn>q~ixt%Rd0FC^qHycPpyY6n1JZEJ;JYDeq|JNi z?JuzM^I9I-6-UXS@oTw0$G=`d?LSJ^{*tCpwO6V3?US`jp`)g<1eUW}NLG&YsY3dw zg=1U`C9c+SYN3yRy#h88;n>D8!43zumYe`9?XLyL38)fN&?%0eRu(83w}SsBuI0Z0 z{5y#SKdWlcJpv1D+ev+`@7U$yJw7^B;l)t}Aj<-u&>fEj5v?%_iY^s})d1^WG z5^a>OUxtdLE`*I9xu}nJO|;bx{CjR-;kiU$kLul`5Rjn{hVbgIY<#hH_aQIFiy~e1 zzKr+7;t{<+NkNBM`bqTiA(+1(jk3LP?fDIC4Jw>AInD_8+$gM^CYTlU)TTv6F5b5qw7n zV5bq__lXQ9>o!#1RBo;n-zO+N(pPuTtHFMczq=X z4dABY6^xz-ke;;A=(rMH5CF(y{YjD16CgUBeFqIo+3E7*l-^tgc`i9yRgm`zUBGZhMvcd^{CspY6=DnzpSn~$& z(OnzyYn*T8iU-8~+`3oFEY;$i{+E|JYzyI%~ZVA&GbK+gTD1M4gncJyRC|%wR<{KqkF{(1@qQk*7JH^u-CSL;F_@ zdLb0~S%`$;p(dQtw+TSeNkwB@*t{^bXIbmy$LcqAKW}7NecMyzk;lUgo6uvgq0rct zvfQz)p~uSVzv_P8PxN|hq`mEfZsqw;6B3tbr^cdVmkeo(Z=#M{RL1}c{W-pvk8VkQ z^ym0fyneoZQ?J>#FQ-;{5lXu)EA2rLd1;>LpgNyA?&YKB`KZ_IKZF|ol^e%eC|rBU zt~o;zoWl4zTQIrb$h}jKK-?Vfah_)L8?)iyH{vN~57lu2b8+$D6*^Ybd4_d-Z3Wl$ z)Pr?IdEXN-;z`cfZ0Fq3K*NpVQ`B>KbYCx>!bK_YA##a^ipWgxa;$&;A9ajcL;Oad zNy&$!JuJNjJF#R$_5&e97?GR@)WQ**SIHmiiLlgBo$_d^T`HZ;nv-TogEAnKXKr)|CaO5@g@?L~Y}`wYdF>Uk1q z>;Vsny~`!*RH2AAzI>S|d|iPh%J#wbkIcqu!|`9s7zL-<3!`4p+a${0oD4YFX69U0mN1%NmCZX(rO zPA-a*BLLbLfMlwxr}zuZ=i#;pDDcoRX@AjVxzp;^Imv>%`1StQo!OIA7fi}eKNo@Whi zjGqb7pSdjzd2=t!V872fzZOH@yOPr+Ga2$YG31E=SNxoOMQErAB*^B>_t;ENqLdNkYtS24cq=!J0S*Fu5Oua!Q~Rf~lUzo8HgVK8FX z_GoVd9t#Q+OO)(kn%+s%(U%z~jhcTnnl~Omq));s9L}vFpR~v&B*YZ8ArtV12^fPV z;n)Z@J%cu@)r(G&di-<#z?{;0ogTDQ6Bxb8b){isj?ks-eJQM zQFFS4L~G%mdLYWu;1*u69+eYDuMw6Q7HNdT%{NzjVrrnJCgWq<$sT|=*3%DRg!o7` zNHQ*{BcDMZeJu4BUNeJULV?^2FKa%h6yBm)%-}6r_i*fgzVjaYY?c9yCrMH#V=3ja z8_R=mX~yz1J!o~_Fb(Im=cIZv64(F1=>c^ZiP$M>tm}ZcA|LyGr2LSr_nQM-AOZS^ zs)XPvjSkEM;NH+FUn-7UPqA#)0Eh|-$NJT+DC<=n;TUAlyE4~#X7pH|w~h8Y*P;=ahm43{ z`CXjtoa`U&F>2sezesqT(d4r{7CF+F4?!3^6h|b zZPtUlsh=|wOrUiP>|4uYuy3Ls8}{vifMJMZ^&rY%eFS`E`g&W}fx4JB^JQt$hRf}u z`s8n{Wm_+8PDctRHO=a^p+tV5=?mTpO6r7n+?DIFZF$0B)&B9qz8F_Ul0q7DvwjCT zDI}W*i|=87Mx47;nHD4kLBn~U3BAZ1)yc@_>Ya^D*~}POR1ZKAZQTtt{+${$&gc*u zs`rN**C$X;f~X9ysMC-Jj0yTk4_Oe%7@@}XB(->>5rAvMi9W@(y;Rps@7$(!h8y$r zwwSk-k#Axjs=?M#-`+Ve$6N5iF7$0tkViRzYu=zeVS`3qX8II10Sc9)I^~g(a${*y zN*}<6$~!~JgSIg26ZOu9ZO*>dyrc9WV?u}i;v1V#H#(!kK+_x=`2K+&o7Z8W&5aoY z9m!h;DkxRSRSui^G{lX=+f3E)SYSYwIiU{VK8>VM=ByVC=J7})zRNn4)J@=7LJdLN zCkMBjqiL_VlF4QQ%{x+Ek}`U8ohbbH4k#ms%hHy!iuZ{9!}!Rk`^j=7b|OaM9ne6o zwaDYKMt$5msxI^0_WnCNNT+qwuKqiZcD)T(e4L2=)2-0&nfc;fy|_k-P3x%RhV07u z9(Qi975Uat53hFZ+V>Bt*hzFq&YWgyYZN_(2p&A>kuD z$twTJ6&dAkQF$GROj@TKT{EdK8#;dZ_slPUt}mammY@#rj&I0oneYAMZp%qf`arJb zT8YnAfo&0zHm*4PWZmGXz#UoDCTNaO!w?=ABa1+1SBqd#JN@h@gdXhnC**ZwW(u+; zU1VEqp2lk3?M~r4=wCR*dePyeC(QkJu+On2Wm00MN6$rZc@@RwRkRl0U=J5nM2EJ2 zkHP-tf=nH88w~a%=;8m!U@x#CCcG6b|33}(76qeVus{85rjD4UADWDg5LX?=or6U> zg5(mjAUx+DGgbuPoqG%+<7`@7z_+^vGT%MMQYvVozk1&*h8Cv9m4+u^HV345_GGyI z-N!kv7{TtoLRiJq2kV?y3||?B8)E#^-JMr#ve)Npsga5h!)hs&LJ=}t#484ClywqS z9#JRDq|3y6$tkuTHBrZBpM9&?tc#wK7Aanp;fBH~c4Qb_E|q>UPw()GnU_+2F)rkU z#4yt{{Fm~MT}!FtR(QwtaE%qzo+RKkb%U|RQJ)CUlTq_tv+~ys+^im{esYf~#<>5{ zK(p{Y@%&w#`laRL389EMTiOSFHdg~ zKSFc@&satDa{9VBbLmwnJSN%>kMOGyW+vg!D6aXU)r%JlaOaXqduIhAE zsu>oWmS!-y^DQQqP>IxTbZ@5H+;1$l!d>`6p9KYq1Fq719q|1gwhzMzyw7ZeV_{~q zI9a87kO*601K3+4QUoHsQ|PC;*VL=iQkXEVN@Rsp$(j0x9q^tL^H9VKPGHAHm zY$?oF1-IrEFr1%cF4;d(+YPIHrgT=;G7D@UsQu`EOEmb7=6#|~7?q!8 z)BgiR3q=dhqkOcBc@J4-L}?ULUB8#(!d04vnIf8MArO;9s(c}Gy*}ni(YnB8juz<( z)E0h(?&+_-GhJZSP4rEhr>%s%o=qQN?`WW`8uek7$U2z`3I^8G4eVon0$1z-(#E=B z)~*Z8%KPmG>T4}8{;@PrtuCw+JO+!4MfCC54i==vU_&g>hDL0t>6a+%0|*ra z^%p=O_o`kKI1d**8X3zPlXv42-{37he?Ql>M%h10Gwq*z zz+-k#pefHPH+F;gNm-Lsz}$&e;H)(*y7#=g$DjovnTXf&t3~8y9q$=vyq?j;bmpsn zIQk>BYrxvnPv1Skm>1s8!ljaxFCUvvyE%)17O%jziq>JTu)|UTo}A!Fs^ax&^qQfr z$hLA55o<-s7fMVww~dbyZPa8aAwCRB9Ahu@rkSrtDDky=k%1ETO#9Nb4~G(gCMSgq zEi_(rDvcIt8PpLiLeYo)dBc?#XkI1D#P<@s#dYj``i-tJAW#rq8|dF>02!Co()scM z*jGJSMH^5+uUXG;U%{5!Ao_3Z3$e3wOj1(s@h9G!I6;XD?>9LkyG^r!*G1z-9=}R6 zv3mYfpACh{CKl<#Y{b4`b{^t_Djmt=B`Uk&orHJE1mP8%rHm*7O@E<~PZ&Q40`yb_ z>FrxbQpita>)E{0Vm)+UdzqB8yVSL`i|>Us3?^y4NY-8qumy2ke8gM^b;E_*bTv4z z{TQPUId3wOU7Io`KAXYlNfRxxx!!yH;6P67?!ExO1U4ZAEg`bTe&-fX=2sZ43OOZg z8Yfs2L{K`sB2rSpno&ihj_yc>h=l0HOuAz>QK-bJ8QYiA9XmlTG&81b--*JFzeSO8 zCx;|OzmGsG1bN!>^b1xw1X|0TLuV$4edx)!(HO}(rD{eq&}B=B4V|$*{=ly=?hw-XLRu6*w1JO2Z$Y;CAmD)Si2v% zx4~Pt1}jmVXr3n5C?m4mLhU|J6u$c#lSMxxawv8=^+fwV`;alXNB9wg`*UvrVE7i7 ztsbEQpJlXgX$`%sZ;WNU{TAke&oYKEhAfuxI9{C$mXTr~hSXJbrvWC|Snuge z9Y3`?*3L~$<|AC%WEPPa+D&HZo9W3Msu=)hX~{U}TH3IT`C6QiQf)j7&8OYbiQ}qT z=e$}Kqez!f2v_s4hoAGYAdMSNgv7{-l1M*{t&pz?5ii)Nm1=-ua_$BmnDEdnCZV64 zP0j9bd+x&fQWMFnmDkGJ*5`Wb|}i&D8U`s&D2u z_6Y1ZQ*}58x9O)frzz`H`mv#e_*>wwaYX;-5e2iZcataqj=lrzk`}TlJN{PohK*g# zk_|LCuh7mBhld&E2m(Is_J+%r!zjtibYM@Cb*w)7u zisr_4mr}?07cg$)AZ>hR*r3VFRD7P)J*RiLoX-`3F`wGzzLj~Qh{xuEk%3>gW%1+< z;SvAhkfHqcK%*kzQnG39q41;rgaG>3;*7{ll#b!pF~(-#a@d^aZjAygJ$`<(9>3n9 z$DZAb8#Qc9cbjyQ>`A2CTkrEPnb1_9XCFkLPejF&|EK5?A;_cpPacDpV!nLX6;NV4 z8BGbC5@?m0PaMf$)2(2CnjK{NuK#eYoAqfLasl5MW`_*3 zbGVfCAEKEDEBkeEhUi68}eeTigThQ39&*s zro>8jN~DNVt1`*3GJE*-FA)im4R#z!hLZKcH7Hp-j#kf-N|uOSeXTt{!vaYeA9GD} z>tJ_ehz2ax5eOzzOt2RNjdRp_)UP_a=8bA2W_uP-3;=%e5~PPokytb_I@IfgTQ&Wub8huftIw@Mt;BT$ z#oP>L9EK65c`@g<(`IFBtu62s0Q3~wX}!9*NcWMlf=uR=TQL$+s@+qaE2tQ>R*80V zh%aIhkQEBqhW*BCKOdVeZT?iJpc3R4$7IC;1?$QJmv=irb2cqG!Va)_0w>a!;StLM z4X4SH`GN_uSquHdR3jRGgxWa&BdE3oPbS+d=mbQk-rhNg9>4krfaqP@wo_@GZ!MFX z2GLx1vQcDo7KplpMph(2R1_NIZ4RRN*h~v2yF#sBWtmP3!&67@9Pbb*2l~BodDqt1JLS-jD;}p>iEmFsp4m2`?|x z3%LUxYf(}>!shbcAsKC0$%0#l?Q@T6EL`#%R)kyo&b?BebF6`~Mcea{H~$k`K&?Wp zm3X22Y)UNFyAV8a9u0ZJalgmzu(zYi?W}XRo!7hfbo%$nT9M_b2~V+JGw|x|$@||) z&-P07Y)ram*D6Y-wQ!K@*`}uKo^6-vSX9{okK+`o`n>wDu7iM>Fq16?U?{D>Cm9bVY zQmYlN)ij4xqxjDnE7gG{x1Xekq*sh~3E z@5y6T<4lQ633Q4Y>-5cdy`cihM{oA&@kD%I?-=U!j&zlqc|{aZP47fuSITNFmySSv z-q7v81*J1YU}lSJu2PGeuZ_OObNRS;{2R2k`j=W8&G4Vk`J_njjcC1YU`6zN*6Y>< z8rdqx@DmeA1!9Ni$|vZ}OR_wAvbt=IJ+*ien7 z*6T)f6K#`0!8_qSd5Q_~l8d0U!+IT{Y@;K11*Cijy~n=HLlgT@UVK6U+piQh;T+IVnz>ZR3Wp0|XG@!wI0~k+;IYLux?=^fCT)L=|HLdC>7^sj)*|ix zwkyuB;=i{%R}uLI%X2J3Y`ujzXEiTL17cAVfMA8B;ND$wve(1Y@sQnWI*W%~{j1Ei z?rn;PJdMVPoBW@|Lk6vi4c^)_|9{0p-rVRQ_U_%8Yb~3tKY`ZluEF~JP77{b!mZtQ z4=;ZLe8B47#Ictn9xo02dRd_HUTCVc`Lj^&3xS3E5Y|w)r@wpry2s~yo0HQ&r!+8k zS!w>O3dW8+UD~+5_VM`7BOf<>uzO(Ph=x^%5xMx@`5cvSZf^Uk?hPvs>-il|*3LdK zu<(p74IlJq|6sfJmE9XYI;`iHJzsGZ;`bMAH?((+&n2|I{e$j3H<9GMnI~P^Kj_o* z)yS9TMAXKHk>L^VoY!|u{UHam1+H%6vpB*=N*Cy?w3`nFS7Y_FWu~hRboh?U?uo(^ z8?Zid&nZAn={5&75-sKmZR;Z787j+}u_%$KDQ<0o_vW;Fz53>070>x7fw?v3pE+z~ zoxyiq1hZ~4BCJ)jY{-LPs8#FlW~;a6t@{}ZYFrclw?V7a1>U(26q**h;re&>vmM`3oqQTVEJ7R&P=YIrlJBKKpq5srB2{Jv%N zhwR9JSMovww&3wWFNJeoiw%`kpwRGly+?MpvEvnat7ZoPfSdL^b|obWPq;#hc^rO~ z`(9XQU^Of)$?ynkB;ArDdFp+l{j)}L=Re#?8q1}qz~@j1SR5O5iAy_Gg33eok3AmS zCFjwf&^_;G=dqLW#~B5u`1z@NS-};LCJXkXV4yeOKENZlooUF6O080O1t%Z1=B-RU zCY;>M;USDd0w&*^LTo{NJMYOz#6y;l;HO(=ry&6tYa9L-!%5&e1?v zfmPanH=ToG74nram*<7sKkAA_vk}LVXcm-bN)M0LjN+4JwrWaMb}mt9Neud1N?XCYhJU(5_sEdxv zfXDG_1bF=W4&E$o`tQJ_`@m2Bi}+E}?%=WD&Hp+)-g$^o=O3E^j|pl7c)au{-u$I; zEAVhzpeuET*mL+Wq!jpBqUf8GU@z1YZ#=ATHrzOf_wao)HS1)8;)fMo~MdoyHa*ZZDZENkSUWm)i6F_2vCSqiaCHMk z!x6G?BmIXxKYPn_2}AzM+ws$Fo=%e%qtvkM(Ll5E-!hx*sSh;mZT%sN@~{h;%p&gG zWNs|Wp3LviznDzvrbSjwhZA~p_*_$2dh0IP=M{R4s{XRloSK6f)v5ldf`*}ClFP72 z5c*S?h&SbXUgo`U&o|nau8%TQw+dz9dxixiJ1Y5UgFyj^%Bdu70fY?)pmUu34i)1!u$L z{_6YWa0Jtv9`PRmmFkC>#MpvlKf;m9{8%NJ!4jSy`V=IQDG4tFjXlxs{ZX#;F4_ZU zf&hWPM?<~RI%qBE=z@$EIvz+d3AXIA@_&R$lj1?Ib7MTH`Fzm!X}x0N=XIGS zS~Z=OqvrzCtl0t2pXdRhL{Yc-SJ7LV zSMT_ZU*jM-E4kO|7d|kzI#dOu%0pG{FN4dWY(z?8l&=7BqVNy340Sb^X~%kHq}Gr^ zmXxhuBEyms0YZT#pg|KLN~5#wn5@Y0l#}@>1a;g+**}C7*g$fg$<=bIi<@hfwL;vjVYCat?WxI7r!U2YBF}55< zF(X+3z9K!V3$gNC&v57imHJ=%)0Ww6y1jSCsXZfHjs?hUwAEi(Va-HHMj$cy0=JXp z3eKk=uWdJxit#1Z;~ZaVG*C0!4tqZ>R7zoSEl_x{;Oh1~2AZs(V+;423VM2plK{DT+1z1icZnH^ys{tSPu)y zAl0cmsHPK+pd+>~$dA3w5)^bg%{=rSS^ANB3WX|~2`9yLMQ*{<0!;@RH_AI$?>i8z z;|OK<=axN?%bsZO<}bcZutiF20vRLqfOkp6_se{|bHBU?J85u%bKB4eh|!d{ZfY4S_u0Te~XH&;HhQCQWbC%ny0Xc{-V{qOpx1O53uq zoDQgV;<@U8-dOOp9ngSiOYYJ>l-G@%5L{-sYQFxTCNUac%sz(DghmgbTx?g}v8+B1 zAjuCE8aZki&!rpe_K4bvyqhRIiVH=HJ5{q?@XH?5l<9Epz#rq+?NA?*QFmw zdI#6Bi!>>nEG%3$D;dh(-?&gRAMXsFYxhP4>_Pd3BxH)%JB)He&HR?7uAY~X&tp< z<-hsq`^tL!ZLN8GSLplH;VGA&?B{@A>`LA1O66piI+jv_-bVPNzLl$9GQ%hSOV!ju zx76Ei?_96_*C*-q_RN%#_o(->OL&Lr^-{6w$nyBf#-q53y)Q!I$(hL~j)nuxcxK&t zo21Hf+?d$-)!7hXyZfdyWH^`i4)5s|5qjAuF@?9VI?X!DsQfrtOQA)nrBHVL{a7`z z>yI?|DW;OaHS+ECdRGq;Y>P{(@0}$mN-DHIt&NPyajJcE9)0&C;W=CoeJr;YbVM0S z6yC{q*`?99RC<8SuR$*{d!+BcF4PEX(^b^Z!uJ&+^L(eF~w zyNJ`+;{&~uYqXx&Ny%Q6luzx{U&~O<@ITG@M5YR7wQ6}zyQt0$+Jfn+aI%&%jw?O_ zH}#7gHhy4Y8PsxMY0J5Y)eDQtTgvtiEiVi5F}yMviI~jVQ)^zu3wfrBy-r)&jK@g0Y5Rhxru zOg)yD8{ z0&HAboW^@hK~M*ylhEQVZT3X^F&@ov5+>tBAW1x}Qcr*GJ%ehS)nPo~=u396afTZ_ zHl0d01Jp?a#J`Q`;3*D9b?R*76UCoZSr@&T{hSgOloCcv8k(37t&qe|CbWj*T@Cs{ z^adpIWRNSvbg(LK_vHXfs)S*Q1!>td1sZF42)-cGw&y4>LhVIavVNe{D8E?(2vT9mbH1 z3Go3!kcHR#32>dHuoPcG}d$Un_ZA!@zf&<4s8&I2@r zIyler&jgSM|GsO_iLa2ja@~%mkDzEOS#P57l^Mb$UicD4uT7$m=#Ofxsyt`Er)}{(L-6(NEAMDI*FDy zk`H5?L9iB5G7@ zeyub`{oHIQ0>78u0EQ}W(c^1rVL)nEA$GsI3+9qM8pEa{S_YhG+)X= z<)heY@BE$$!V^pE$nhqDaa-hg+M=uM?BaNSb(6h)tT)bYbcs`--D!@qtmRd)4`sh( z_Q3LMJt(7odvI0wjk|H zst0I+5TDn5jjv~Zwx$=P4*IkRo=Bvr3k$!mEDRTSAqaywbhKoqi|47!r z1$ncKUHc8d(CW0rc9(ai%;RRe15(|Z^Ox30_8irA?r0Y32DB8&<)uUYZ4Zi z7m32f6p4MnUAfNWSRycjXPGwDw3KZ&Em0VyIMoro6lhXznV4l`NN(5X>^gXsD8O&1_GG`2k59rJJ5nP_3dd_7 z#`v3LPb59(==K%1-P*7O##f zo@pnkd|er>P8)$`Ev19P(U3A@d=%jA-9w3>v;b^C{xmj)RqBelJ@P6DKI zh++a>%gg4u<`==M;`DlF|FNTj201_}W&c6$3|@

-mT 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≧KWy4gEnYLU*rLc2pChmFZ;lx; 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

? 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%0AM8~wdA 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

C-+P;dW-as7xm{s_F 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pydantic_core/_pydantic_core.pyi b/.venv/lib/python3.12/site-packages/pydantic_core/_pydantic_core.pyi new file mode 100644 index 0000000..122c86f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core/_pydantic_core.pyi @@ -0,0 +1,1056 @@ +import datetime +from collections.abc import Mapping +from typing import Any, Callable, Generic, Literal, TypeVar, final + +from _typeshed import SupportsAllComparisons +from typing_extensions import LiteralString, Self, TypeAlias + +from pydantic_core import ErrorDetails, ErrorTypeInfo, InitErrorDetails, MultiHostHost +from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorType, ExtraBehavior + +__all__ = [ + '__version__', + 'build_profile', + 'build_info', + '_recursion_limit', + 'ArgsKwargs', + 'SchemaValidator', + 'SchemaSerializer', + 'Url', + 'MultiHostUrl', + 'SchemaError', + 'ValidationError', + 'PydanticCustomError', + 'PydanticKnownError', + 'PydanticOmit', + 'PydanticUseDefault', + 'PydanticSerializationError', + 'PydanticSerializationUnexpectedValue', + 'PydanticUndefined', + 'PydanticUndefinedType', + 'Some', + 'to_json', + 'from_json', + 'to_jsonable_python', + 'list_all_errors', + 'TzInfo', +] +__version__: str +build_profile: str +build_info: str +_recursion_limit: int + +_T = TypeVar('_T', default=Any, covariant=True) + +_StringInput: TypeAlias = 'dict[str, _StringInput]' + +@final +class Some(Generic[_T]): + """ + Similar to Rust's [`Option::Some`](https://doc.rust-lang.org/std/option/enum.Option.html) type, this + identifies a value as being present, and provides a way to access it. + + Generally used in a union with `None` to different between "some value which could be None" and no value. + """ + + __match_args__ = ('value',) + + @property + def value(self) -> _T: + """ + Returns the value wrapped by `Some`. + """ + @classmethod + def __class_getitem__(cls, item: Any, /) -> type[Self]: ... + +@final +class SchemaValidator: + """ + `SchemaValidator` is the Python wrapper for `pydantic-core`'s Rust validation logic, internally it owns one + `CombinedValidator` which may in turn own more `CombinedValidator`s which make up the full schema validator. + """ + + # note: pyo3 currently supports __new__, but not __init__, though we include __init__ stubs + # and docstrings here (and in the following classes) for documentation purposes + + def __init__(self, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> None: + """Initializes the `SchemaValidator`. + + Arguments: + schema: The `CoreSchema` to use for validation. + config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to configure validation. + _use_prebuilt: Whether to use pre-built validators (False during rebuilds to avoid stale references). + """ + + def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> Self: ... + @property + def title(self) -> str: + """ + The title of the schema, as used in the heading of [`ValidationError.__str__()`][pydantic_core.ValidationError]. + """ + def validate_python( + self, + input: Any, + *, + strict: bool | None = None, + extra: ExtraBehavior | None = None, + from_attributes: bool | None = None, + context: Any | None = None, + self_instance: Any | None = None, + allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Any: + """ + Validate a Python object against the schema and return the validated object. + + Arguments: + input: The Python object to validate. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + extra: Whether to ignore, allow, or forbid extra data during model validation. + If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used. + from_attributes: Whether to validate objects as inputs to models by extracting attributes. + If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + self_instance: An instance of a model set attributes on from validation, this is used when running + validation from the `__init__` method of a model. + allow_partial: Whether to allow partial validation; if `True` errors in the last element of sequences + and mappings are ignored. + `'trailing-strings'` means any final unfinished JSON string is included in the result. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated object. + """ + def isinstance_python( + self, + input: Any, + *, + strict: bool | None = None, + extra: ExtraBehavior | None = None, + from_attributes: bool | None = None, + context: Any | None = None, + self_instance: Any | None = None, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> bool: + """ + Similar to [`validate_python()`][pydantic_core.SchemaValidator.validate_python] but returns a boolean. + + Arguments match `validate_python()`. This method will not raise `ValidationError`s but will raise internal + errors. + + Returns: + `True` if validation succeeds, `False` if validation fails. + """ + def validate_json( + self, + input: str | bytes | bytearray, + *, + strict: bool | None = None, + extra: ExtraBehavior | None = None, + context: Any | None = None, + self_instance: Any | None = None, + allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Any: + """ + Validate JSON data directly against the schema and return the validated Python object. + + This method should be significantly faster than `validate_python(json.loads(json_data))` as it avoids the + need to create intermediate Python objects + + It also handles constructing the correct Python type even in strict mode, where + `validate_python(json.loads(json_data))` would fail validation. + + Arguments: + input: The JSON data to validate. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + extra: Whether to ignore, allow, or forbid extra data during model validation. + If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + self_instance: An instance of a model set attributes on from validation. + allow_partial: Whether to allow partial validation; if `True` incomplete JSON will be parsed successfully + and errors in the last element of sequences and mappings are ignored. + `'trailing-strings'` means any final unfinished JSON string is included in the result. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Raises: + ValidationError: If validation fails or if the JSON data is invalid. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated Python object. + """ + def validate_strings( + self, + input: _StringInput, + *, + strict: bool | None = None, + extra: ExtraBehavior | None = None, + context: Any | None = None, + allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Any: + """ + Validate a string against the schema and return the validated Python object. + + This is similar to `validate_json` but applies to scenarios where the input will be a string but not + JSON data, e.g. URL fragments, query parameters, etc. + + Arguments: + input: The input as a string, or bytes/bytearray if `strict=False`. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + extra: Whether to ignore, allow, or forbid extra data during model validation. + If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + allow_partial: Whether to allow partial validation; if `True` errors in the last element of sequences + and mappings are ignored. + `'trailing-strings'` means any final unfinished JSON string is included in the result. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Raises: + ValidationError: If validation fails or if the JSON data is invalid. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + The validated Python object. + """ + def validate_assignment( + self, + obj: Any, + field_name: str, + field_value: Any, + *, + strict: bool | None = None, + extra: ExtraBehavior | None = None, + from_attributes: bool | None = None, + context: Any | None = None, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any] | None, set[str]]: + """ + Validate an assignment to a field on a model. + + Arguments: + obj: The model instance being assigned to. + field_name: The name of the field to validate assignment for. + field_value: The value to assign to the field. + strict: Whether to validate the object in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + extra: Whether to ignore, allow, or forbid extra data during model validation. + If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used. + from_attributes: Whether to validate objects as inputs to models by extracting attributes. + If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + Either the model dict or a tuple of `(model_data, model_extra, fields_set)` + """ + def get_default_value(self, *, strict: bool | None = None, context: Any = None) -> Some | None: + """ + Get the default value for the schema, including running default value validation. + + Arguments: + strict: Whether to validate the default value in strict mode. + If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used. + context: The context to use for validation, this is passed to functional validators as + [`info.context`][pydantic_core.core_schema.ValidationInfo.context]. + + Raises: + ValidationError: If validation fails. + Exception: Other error types maybe raised if internal errors occur. + + Returns: + `None` if the schema has no default value, otherwise a [`Some`][pydantic_core.Some] containing the default. + """ + +# In reality, `bool` should be replaced by `Literal[True]` but mypy fails to correctly apply bidirectional type inference +# (e.g. when using `{'a': {'b': True}}`). +_IncEx: TypeAlias = set[int] | set[str] | Mapping[int, _IncEx | bool] | Mapping[str, _IncEx | bool] + +@final +class SchemaSerializer: + """ + `SchemaSerializer` is the Python wrapper for `pydantic-core`'s Rust serialization logic, internally it owns one + `CombinedSerializer` which may in turn own more `CombinedSerializer`s which make up the full schema serializer. + """ + + def __init__(self, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> None: + """Initializes the `SchemaSerializer`. + + Arguments: + schema: The `CoreSchema` to use for serialization. + config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to to configure serialization. + _use_prebuilt: Whether to use pre-built validators (False during rebuilds to avoid stale references). + """ + + def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> Self: ... + def to_python( + self, + value: Any, + *, + mode: str | None = None, + include: _IncEx | None = None, + exclude: _IncEx | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + polymorphic_serialization: bool | None = None, + context: Any | None = None, + ) -> Any: + """ + Serialize/marshal a Python object to a Python object including transforming and filtering data. + + Arguments: + value: The Python object to serialize. + mode: The serialization mode to use, either `'python'` or `'json'`, defaults to `'python'`. In JSON mode, + all values are converted to JSON compatible types, e.g. `None`, `int`, `float`, `str`, `list`, `dict`. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_unset: Whether to exclude fields that are not set, + e.g. are not included in `__pydantic_fields_set__`. + exclude_defaults: Whether to exclude fields that are equal to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + round_trip: Whether to enable serialization and validation round-trip support. + warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + The serialized Python object. + """ + def to_json( + self, + value: Any, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: _IncEx | None = None, + exclude: _IncEx | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal['none', 'warn', 'error'] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + polymorphic_serialization: bool | None = None, + context: Any | None = None, + ) -> bytes: + """ + Serialize a Python object to JSON including transforming and filtering data. + + Arguments: + value: The Python object to serialize. + indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided. + ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped. + If `False` (the default), these characters will be output as-is. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_unset: Whether to exclude fields that are not set, + e.g. are not included in `__pydantic_fields_set__`. + exclude_defaults: Whether to exclude fields that are equal to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + round_trip: Whether to enable serialization and validation round-trip support. + warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + JSON bytes. + """ + +def to_json( + value: Any, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: _IncEx | None = None, + exclude: _IncEx | None = None, + # Note: In Pydantic 2.11, the default value of `by_alias` on `SchemaSerializer` was changed from `True` to `None`, + # to be consistent with the Pydantic "dump" methods. However, the default of `True` was kept here for + # backwards compatibility. In Pydantic V3, `by_alias` is expected to default to `True` everywhere: + by_alias: bool = True, + exclude_none: bool = False, + round_trip: bool = False, + timedelta_mode: Literal['iso8601', 'float'] = 'iso8601', + temporal_mode: Literal['iso8601', 'seconds', 'milliseconds'] = 'iso8601', + bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8', + inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants', + serialize_unknown: bool = False, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + polymorphic_serialization: bool | None = None, + context: Any | None = None, +) -> bytes: + """ + Serialize a Python object to JSON including transforming and filtering data. + + This is effectively a standalone version of [`SchemaSerializer.to_json`][pydantic_core.SchemaSerializer.to_json]. + + Arguments: + value: The Python object to serialize. + indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided. + ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped. + If `False` (the default), these characters will be output as-is. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`. + temporal_mode: How to serialize datetime-like objects (`datetime`, `date`, `time`), either `'iso8601'`, `'seconds'`, or `'milliseconds'`. + `iso8601` returns an ISO 8601 string; `seconds` returns the Unix timestamp in seconds as a float; `milliseconds` returns the Unix timestamp in milliseconds as a float. + + bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`. + inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`. + serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails + `""` will be used. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + JSON bytes. + """ + +def from_json( + data: str | bytes | bytearray, + *, + allow_inf_nan: bool = True, + cache_strings: bool | Literal['all', 'keys', 'none'] = True, + allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, +) -> Any: + """ + Deserialize JSON data to a Python object. + + This is effectively a faster version of `json.loads()`, with some extra functionality. + + Arguments: + data: The JSON data to deserialize. + allow_inf_nan: Whether to allow `Infinity`, `-Infinity` and `NaN` values as `json.loads()` does by default. + cache_strings: Whether to cache strings to avoid constructing new Python objects, + this should have a significant impact on performance while increasing memory usage slightly, + `all/True` means cache all strings, `keys` means cache only dict keys, `none/False` means no caching. + allow_partial: Whether to allow partial deserialization, if `True` JSON data is returned if the end of the + input is reached before the full object is deserialized, e.g. `["aa", "bb", "c` would return `['aa', 'bb']`. + `'trailing-strings'` means any final unfinished JSON string is included in the result. + + Raises: + ValueError: If deserialization fails. + + Returns: + The deserialized Python object. + """ + +def to_jsonable_python( + value: Any, + *, + include: _IncEx | None = None, + exclude: _IncEx | None = None, + # Note: In Pydantic 2.11, the default value of `by_alias` on `SchemaSerializer` was changed from `True` to `None`, + # to be consistent with the Pydantic "dump" methods. However, the default of `True` was kept here for + # backwards compatibility. In Pydantic V3, `by_alias` is expected to default to `True` everywhere: + by_alias: bool = True, + exclude_none: bool = False, + round_trip: bool = False, + timedelta_mode: Literal['iso8601', 'float'] = 'iso8601', + temporal_mode: Literal['iso8601', 'seconds', 'milliseconds'] = 'iso8601', + bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8', + inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants', + serialize_unknown: bool = False, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + polymorphic_serialization: bool | None = None, + context: Any | None = None, +) -> Any: + """ + Serialize/marshal a Python object to a JSON-serializable Python object including transforming and filtering data. + + This is effectively a standalone version of + [`SchemaSerializer.to_python(mode='json')`][pydantic_core.SchemaSerializer.to_python]. + + Args: + value: The Python object to serialize. + include: A set of fields to include, if `None` all fields are included. + exclude: A set of fields to exclude, if `None` no fields are excluded. + by_alias: Whether to use the alias names of fields. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to enable serialization and validation round-trip support. + timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`. + temporal_mode: How to serialize datetime-like objects (`datetime`, `date`, `time`), either `'iso8601'`, `'seconds'`, or `'milliseconds'`. + `iso8601` returns an ISO 8601 string; `seconds` returns the Unix timestamp in seconds as a float; `milliseconds` returns the Unix timestamp in milliseconds as a float. + + bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`. + inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`. + serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails + `""` will be used. + fallback: A function to call when an unknown value is encountered, + if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call. + context: The context to use for serialization, this is passed to functional serializers as + [`info.context`][pydantic_core.core_schema.SerializationInfo.context]. + + Raises: + PydanticSerializationError: If serialization fails and no `fallback` function is provided. + + Returns: + The serialized Python object. + """ + +class Url(SupportsAllComparisons): + """ + A URL type, internal logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed + by Mozilla. + """ + + def __init__(self, url: str) -> None: ... + def __new__(cls, url: str) -> Self: ... + @property + def scheme(self) -> str: ... + @property + def username(self) -> str | None: ... + @property + def password(self) -> str | None: ... + @property + def host(self) -> str | None: ... + def unicode_host(self) -> str | None: ... + @property + def port(self) -> int | None: ... + @property + def path(self) -> str | None: ... + @property + def query(self) -> str | None: ... + def query_params(self) -> list[tuple[str, str]]: ... + @property + def fragment(self) -> str | None: ... + def unicode_string(self) -> str: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __deepcopy__(self, memo: dict) -> str: ... + @classmethod + def build( + cls, + *, + scheme: str, + username: str | None = None, + password: str | None = None, + host: str, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: ... + +class MultiHostUrl(SupportsAllComparisons): + """ + A URL type with support for multiple hosts, as used by some databases for DSNs, e.g. `https://foo.com,bar.com/path`. + + Internal URL logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed + by Mozilla. + """ + + def __init__(self, url: str) -> None: ... + def __new__(cls, url: str) -> Self: ... + @property + def scheme(self) -> str: ... + @property + def path(self) -> str | None: ... + @property + def query(self) -> str | None: ... + def query_params(self) -> list[tuple[str, str]]: ... + @property + def fragment(self) -> str | None: ... + def hosts(self) -> list[MultiHostHost]: ... + def unicode_string(self) -> str: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + def __deepcopy__(self, memo: dict) -> Self: ... + @classmethod + def build( + cls, + *, + scheme: str, + hosts: list[MultiHostHost] | None = None, + username: str | None = None, + password: str | None = None, + host: str | None = None, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: ... + +@final +class SchemaError(Exception): + """ + Information about errors that occur while building a [`SchemaValidator`][pydantic_core.SchemaValidator] + or [`SchemaSerializer`][pydantic_core.SchemaSerializer]. + """ + + def error_count(self) -> int: + """ + Returns: + The number of errors in the schema. + """ + def errors(self) -> list[ErrorDetails]: + """ + Returns: + A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the schema. + """ + +class ValidationError(ValueError): + """ + `ValidationError` is the exception raised by `pydantic-core` when validation fails, it contains a list of errors + which detail why validation failed. + """ + @classmethod + def from_exception_data( + cls, + title: str, + line_errors: list[InitErrorDetails], + input_type: Literal['python', 'json'] = 'python', + hide_input: bool = False, + ) -> Self: + """ + Python constructor for a Validation Error. + + Arguments: + title: The title of the error, as used in the heading of `str(validation_error)` + line_errors: A list of [`InitErrorDetails`][pydantic_core.InitErrorDetails] which contain information + about errors that occurred during validation. + input_type: Whether the error is for a Python object or JSON. + hide_input: Whether to hide the input value in the error message. + """ + @property + def title(self) -> str: + """ + The title of the error, as used in the heading of `str(validation_error)`. + """ + def error_count(self) -> int: + """ + Returns: + The number of errors in the validation error. + """ + def errors( + self, *, include_url: bool = True, include_context: bool = True, include_input: bool = True + ) -> list[ErrorDetails]: + """ + Details about each error in the validation error. + + Args: + include_url: Whether to include a URL to documentation on the error each error. + include_context: Whether to include the context of each error. + include_input: Whether to include the input value of each error. + + Returns: + A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the validation error. + """ + def json( + self, + *, + indent: int | None = None, + include_url: bool = True, + include_context: bool = True, + include_input: bool = True, + ) -> str: + """ + Same as [`errors()`][pydantic_core.ValidationError.errors] but returns a JSON string. + + Args: + indent: The number of spaces to indent the JSON by, or `None` for no indentation - compact JSON. + include_url: Whether to include a URL to documentation on the error each error. + include_context: Whether to include the context of each error. + include_input: Whether to include the input value of each error. + + Returns: + a JSON string. + """ + + def __repr__(self) -> str: + """ + A string representation of the validation error. + + Whether or not documentation URLs are included in the repr is controlled by the + environment variable `PYDANTIC_ERRORS_INCLUDE_URL` being set to `1` or + `true`; by default, URLs are shown. + + Due to implementation details, this environment variable can only be set once, + before the first validation error is created. + """ + +class PydanticCustomError(ValueError): + """A custom exception providing flexible error handling for Pydantic validators. + + You can raise this error in custom validators when you'd like flexibility in regards to the error type, message, and context. + + Example: + ```py + from pydantic_core import PydanticCustomError + + def custom_validator(v) -> None: + if v <= 10: + raise PydanticCustomError('custom_value_error', 'Value must be greater than {value}', {'value': 10, 'extra_context': 'extra_data'}) + return v + ``` + + Arguments: + error_type: The error type. + message_template: The message template. + context: The data to inject into the message template. + """ + + def __init__( + self, error_type: LiteralString, message_template: LiteralString, context: dict[str, Any] | None = None, / + ) -> None: ... + @property + def context(self) -> dict[str, Any] | None: + """Values which are required to render the error message, and could hence be useful in passing error data forward.""" + + @property + def type(self) -> str: + """The error type associated with the error. For consistency with Pydantic, this is typically a snake_case string.""" + + @property + def message_template(self) -> str: + """The message template associated with the error. This is a string that can be formatted with context variables in `{curly_braces}`.""" + + def message(self) -> str: + """The formatted message associated with the error. This presents as the message template with context variables appropriately injected.""" + +@final +class PydanticKnownError(ValueError): + """A helper class for raising exceptions that mimic Pydantic's built-in exceptions, with more flexibility in regards to context. + + Unlike [`PydanticCustomError`][pydantic_core.PydanticCustomError], the `error_type` argument must be a known `ErrorType`. + + Example: + ```py + from pydantic_core import PydanticKnownError + + def custom_validator(v) -> None: + if v <= 10: + raise PydanticKnownError('greater_than', {'gt': 10}) + return v + ``` + + Arguments: + error_type: The error type. + context: The data to inject into the message template. + """ + + def __init__(self, error_type: ErrorType, context: dict[str, Any] | None = None, /) -> None: ... + @property + def context(self) -> dict[str, Any] | None: + """Values which are required to render the error message, and could hence be useful in passing error data forward.""" + + @property + def type(self) -> ErrorType: + """The type of the error.""" + + @property + def message_template(self) -> str: + """The message template associated with the provided error type. This is a string that can be formatted with context variables in `{curly_braces}`.""" + + def message(self) -> str: + """The formatted message associated with the error. This presents as the message template with context variables appropriately injected.""" + +@final +class PydanticOmit(Exception): + """An exception to signal that a field should be omitted from a generated result. + + This could span from omitting a field from a JSON Schema to omitting a field from a serialized result. + Upcoming: more robust support for using PydanticOmit in custom serializers is still in development. + Right now, this is primarily used in the JSON Schema generation process. + + Example: + ```py + from typing import Callable + + from pydantic_core import PydanticOmit + + from pydantic import BaseModel + from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue + + + class MyGenerateJsonSchema(GenerateJsonSchema): + def handle_invalid_for_json_schema(self, schema, error_info) -> JsonSchemaValue: + raise PydanticOmit + + + class Predicate(BaseModel): + name: str = 'no-op' + func: Callable = lambda x: x + + + instance_example = Predicate() + + validation_schema = instance_example.model_json_schema(schema_generator=MyGenerateJsonSchema, mode='validation') + print(validation_schema) + ''' + {'properties': {'name': {'default': 'no-op', 'title': 'Name', 'type': 'string'}}, 'title': 'Predicate', 'type': 'object'} + ''' + ``` + + For a more in depth example / explanation, see the [customizing JSON schema](../concepts/json_schema.md#customizing-the-json-schema-generation-process) docs. + """ + + def __new__(cls) -> Self: ... + +@final +class PydanticUseDefault(Exception): + """An exception to signal that standard validation either failed or should be skipped, and the default value should be used instead. + + This warning can be raised in custom validation functions to redirect the flow of validation. + + Example: + ```py + from pydantic_core import PydanticUseDefault + from datetime import datetime + from pydantic import BaseModel, field_validator + + + class Event(BaseModel): + name: str = 'meeting' + time: datetime + + @field_validator('name', mode='plain') + def name_must_be_present(cls, v) -> str: + if not v or not isinstance(v, str): + raise PydanticUseDefault() + return v + + + event1 = Event(name='party', time=datetime(2024, 1, 1, 12, 0, 0)) + print(repr(event1)) + # > Event(name='party', time=datetime.datetime(2024, 1, 1, 12, 0)) + event2 = Event(time=datetime(2024, 1, 1, 12, 0, 0)) + print(repr(event2)) + # > Event(name='meeting', time=datetime.datetime(2024, 1, 1, 12, 0)) + ``` + + For an additional example, see the [validating partial json data](../concepts/json.md#partial-json-parsing) section of the Pydantic documentation. + """ + + def __new__(cls) -> Self: ... + +@final +class PydanticSerializationError(ValueError): + """An error raised when an issue occurs during serialization. + + In custom serializers, this error can be used to indicate that serialization has failed. + + Arguments: + message: The message associated with the error. + """ + + def __init__(self, message: str, /) -> None: ... + +@final +class PydanticSerializationUnexpectedValue(ValueError): + """An error raised when an unexpected value is encountered during serialization. + + This error is often caught and coerced into a warning, as `pydantic-core` generally makes a best attempt + at serializing values, in contrast with validation where errors are eagerly raised. + + Example: + ```py + from pydantic import BaseModel, field_serializer + from pydantic_core import PydanticSerializationUnexpectedValue + + class BasicPoint(BaseModel): + x: int + y: int + + @field_serializer('*') + def serialize(self, v): + if not isinstance(v, int): + raise PydanticSerializationUnexpectedValue(f'Expected type `int`, got {type(v)} with value {v}') + return v + + point = BasicPoint(x=1, y=2) + # some sort of mutation + point.x = 'a' + + print(point.model_dump()) + ''' + UserWarning: Pydantic serializer warnings: + PydanticSerializationUnexpectedValue(Expected type `int`, got with value a) + return self.__pydantic_serializer__.to_python( + {'x': 'a', 'y': 2} + ''' + ``` + + This is often used internally in `pydantic-core` when unexpected types are encountered during serialization, + but it can also be used by users in custom serializers, as seen above. + + Arguments: + message: The message associated with the unexpected value. + """ + + def __init__(self, message: str, /) -> None: ... + +@final +class ArgsKwargs: + """A construct used to store arguments and keyword arguments for a function call. + + This data structure is generally used to store information for core schemas associated with functions (like in an arguments schema). + This data structure is also currently used for some validation against dataclasses. + + Example: + ```py + from pydantic.dataclasses import dataclass + from pydantic import model_validator + + + @dataclass + class Model: + a: int + b: int + + @model_validator(mode="before") + @classmethod + def no_op_validator(cls, values): + print(values) + return values + + Model(1, b=2) + #> ArgsKwargs((1,), {"b": 2}) + + Model(1, 2) + #> ArgsKwargs((1, 2), {}) + + Model(a=1, b=2) + #> ArgsKwargs((), {"a": 1, "b": 2}) + ``` + """ + + def __init__(self, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> None: + """Initializes the `ArgsKwargs`. + + Arguments: + args: The arguments (inherently ordered) for a function call. + kwargs: The keyword arguments for a function call + """ + + def __new__(cls, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> Self: ... + @property + def args(self) -> tuple[Any, ...]: + """The arguments (inherently ordered) for a function call.""" + + @property + def kwargs(self) -> dict[str, Any] | None: + """The keyword arguments for a function call.""" + +@final +class PydanticUndefinedType: + """A type used as a sentinel for undefined values.""" + + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: Any) -> Self: ... + +PydanticUndefined: PydanticUndefinedType + +def list_all_errors() -> list[ErrorTypeInfo]: + """ + Get information about all built-in errors. + + Returns: + A list of `ErrorTypeInfo` typed dicts. + """ +@final +class TzInfo(datetime.tzinfo): + """An `pydantic-core` implementation of the abstract [`datetime.tzinfo`][] class.""" + + def __init__(self, seconds: float = 0.0) -> None: + """Initializes the `TzInfo`. + + Arguments: + seconds: The offset from UTC in seconds. Defaults to 0.0 (UTC). + """ + + def __new__(cls, seconds: float = 0.0) -> Self: ... + + # Docstrings for attributes sourced from the abstract base class, [`datetime.tzinfo`](https://docs.python.org/3/library/datetime.html#datetime.tzinfo). + + def tzname(self, dt: datetime.datetime | None) -> str | None: + """Return the time zone name corresponding to the [`datetime`][datetime.datetime] object _dt_, as a string. + + For more info, see [`tzinfo.tzname`][datetime.tzinfo.tzname]. + """ + + def utcoffset(self, dt: datetime.datetime | None) -> datetime.timedelta | None: + """Return offset of local time from UTC, as a [`timedelta`][datetime.timedelta] object that is positive east of UTC. If local time is west of UTC, this should be negative. + + More info can be found at [`tzinfo.utcoffset`][datetime.tzinfo.utcoffset]. + """ + + def dst(self, dt: datetime.datetime | None) -> datetime.timedelta | None: + """Return the daylight saving time (DST) adjustment, as a [`timedelta`][datetime.timedelta] object or `None` if DST information isn’t known. + + More info can be found at[`tzinfo.dst`][datetime.tzinfo.dst].""" + + def fromutc(self, dt: datetime.datetime) -> datetime.datetime: + """Adjust the date and time data associated datetime object _dt_, returning an equivalent datetime in self’s local time. + + More info can be found at [`tzinfo.fromutc`][datetime.tzinfo.fromutc].""" + + def __deepcopy__(self, _memo: dict[Any, Any]) -> TzInfo: ... diff --git a/.venv/lib/python3.12/site-packages/pydantic_core/core_schema.py b/.venv/lib/python3.12/site-packages/pydantic_core/core_schema.py new file mode 100644 index 0000000..6ff202c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core/core_schema.py @@ -0,0 +1,4461 @@ +""" +This module contains definitions to build schemas which `pydantic_core` can +validate and serialize. +""" + +from __future__ import annotations as _annotations + +import sys +import warnings +from collections.abc import Generator, Hashable, Mapping +from datetime import date, datetime, time, timedelta +from decimal import Decimal +from re import Pattern +from typing import TYPE_CHECKING, Any, Callable, Literal, Union + +from typing_extensions import TypeVar, deprecated + +if sys.version_info < (3, 12): + from typing_extensions import TypedDict +else: + from typing import TypedDict + +if sys.version_info < (3, 11): + from typing_extensions import Protocol, Required, TypeAlias +else: + from typing import Protocol, Required, TypeAlias + +if TYPE_CHECKING: + from pydantic_core import PydanticUndefined +else: + # The initial build of pydantic_core requires PydanticUndefined to generate + # the core schema; so we need to conditionally skip it. mypy doesn't like + # this at all, hence the TYPE_CHECKING branch above. + try: + from pydantic_core import PydanticUndefined + except ImportError: + PydanticUndefined = object() + + +ExtraBehavior = Literal['allow', 'forbid', 'ignore'] + + +class CoreConfig(TypedDict, total=False): + """ + Base class for schema configuration options. + + Attributes: + title: The name of the configuration. + strict: Whether the configuration should strictly adhere to specified rules. + extra_fields_behavior: The behavior for handling extra fields. + typed_dict_total: Whether the TypedDict should be considered total. Default is `True`. + from_attributes: Whether to use attributes for models, dataclasses, and tagged union keys. + loc_by_alias: Whether to use the used alias (or first alias for "field required" errors) instead of + `field_names` to construct error `loc`s. Default is `True`. + revalidate_instances: Whether instances of models and dataclasses should re-validate. Default is 'never'. + validate_default: Whether to validate default values during validation. Default is `False`. + str_max_length: The maximum length for string fields. + str_min_length: The minimum length for string fields. + str_strip_whitespace: Whether to strip whitespace from string fields. + str_to_lower: Whether to convert string fields to lowercase. + str_to_upper: Whether to convert string fields to uppercase. + allow_inf_nan: Whether to allow infinity and NaN values for float fields. Default is `True`. + ser_json_timedelta: The serialization option for `timedelta` values. Default is 'iso8601'. + Note that if ser_json_temporal is set, then this param will be ignored. + ser_json_temporal: The serialization option for datetime like values. Default is 'iso8601'. + The types this covers are datetime, date, time and timedelta. + If this is set, it will take precedence over ser_json_timedelta + ser_json_bytes: The serialization option for `bytes` values. Default is 'utf8'. + ser_json_inf_nan: The serialization option for infinity and NaN values + in float fields. Default is 'null'. + val_json_bytes: The validation option for `bytes` values, complementing ser_json_bytes. Default is 'utf8'. + hide_input_in_errors: Whether to hide input data from `ValidationError` representation. + validation_error_cause: Whether to add user-python excs to the __cause__ of a ValidationError. + Requires exceptiongroup backport pre Python 3.11. + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + regex_engine: The regex engine to use for regex pattern validation. Default is 'rust-regex'. See `StringSchema`. + cache_strings: Whether to cache strings. Default is `True`, `True` or `'all'` is required to cache strings + during general validation since validators don't know if they're in a key or a value. + validate_by_alias: Whether to use the field's alias when validating against the provided input data. Default is `True`. + validate_by_name: Whether to use the field's name when validating against the provided input data. Default is `False`. Replacement for `populate_by_name`. + serialize_by_alias: Whether to serialize by alias. Default is `False`, expected to change to `True` in V3. + polymorphic_serialization: Whether to enable polymorphic serialization for models and dataclasses. Default is `False`. + url_preserve_empty_path: Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`. + """ + + title: str + strict: bool + # settings related to typed dicts, model fields, dataclass fields + extra_fields_behavior: ExtraBehavior + typed_dict_total: bool # default: True + # used for models, dataclasses, and tagged union keys + from_attributes: bool + # whether to use the used alias (or first alias for "field required" errors) instead of field_names + # to construct error `loc`s, default True + loc_by_alias: bool + # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' + revalidate_instances: Literal['always', 'never', 'subclass-instances'] + # whether to validate default values during validation, default False + validate_default: bool + # used on typed-dicts and arguments + # fields related to string fields only + str_max_length: int + str_min_length: int + str_strip_whitespace: bool + str_to_lower: bool + str_to_upper: bool + # fields related to float fields only + allow_inf_nan: bool # default: True + # the config options are used to customise serialization to JSON + ser_json_timedelta: Literal['iso8601', 'float'] # default: 'iso8601' + ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds'] # default: 'iso8601' + ser_json_bytes: Literal['utf8', 'base64', 'hex'] # default: 'utf8' + ser_json_inf_nan: Literal['null', 'constants', 'strings'] # default: 'null' + val_json_bytes: Literal['utf8', 'base64', 'hex'] # default: 'utf8' + # used to hide input data from ValidationError repr + hide_input_in_errors: bool + validation_error_cause: bool # default: False + coerce_numbers_to_str: bool # default: False + regex_engine: Literal['rust-regex', 'python-re'] # default: 'rust-regex' + cache_strings: Union[bool, Literal['all', 'keys', 'none']] # default: 'True' + validate_by_alias: bool # default: True + validate_by_name: bool # default: False + serialize_by_alias: bool # default: False + polymorphic_serialization: bool # default: False + url_preserve_empty_path: bool # default: False + + +IncExCall: TypeAlias = 'set[int | str] | dict[int | str, IncExCall] | None' + +ContextT = TypeVar('ContextT', covariant=True, default='Any | None') + + +class SerializationInfo(Protocol[ContextT]): + """Extra data used during serialization.""" + + @property + def include(self) -> IncExCall: + """The `include` argument set during serialization.""" + ... + + @property + def exclude(self) -> IncExCall: + """The `exclude` argument set during serialization.""" + ... + + @property + def context(self) -> ContextT: + """The current serialization context.""" + ... + + @property + def mode(self) -> Literal['python', 'json'] | str: + """The serialization mode set during serialization.""" + ... + + @property + def by_alias(self) -> bool: + """The `by_alias` argument set during serialization.""" + ... + + @property + def exclude_unset(self) -> bool: + """The `exclude_unset` argument set during serialization.""" + ... + + @property + def exclude_defaults(self) -> bool: + """The `exclude_defaults` argument set during serialization.""" + ... + + @property + def exclude_none(self) -> bool: + """The `exclude_none` argument set during serialization.""" + ... + + @property + def exclude_computed_fields(self) -> bool: + """The `exclude_computed_fields` argument set during serialization.""" + ... + + @property + def serialize_as_any(self) -> bool: + """The `serialize_as_any` argument set during serialization.""" + ... + + @property + def polymorphic_serialization(self) -> bool | None: + """The `polymorphic_serialization` argument set during serialization, if any.""" + ... + + @property + def round_trip(self) -> bool: + """The `round_trip` argument set during serialization.""" + ... + + def mode_is_json(self) -> bool: ... + + def __str__(self) -> str: ... + + def __repr__(self) -> str: ... + + +class FieldSerializationInfo(SerializationInfo[ContextT], Protocol): + """Extra data used during field serialization.""" + + @property + def field_name(self) -> str: + """The name of the current field being serialized.""" + ... + + +class ValidationInfo(Protocol[ContextT]): + """Extra data used during validation.""" + + @property + def context(self) -> ContextT: + """The current validation context.""" + ... + + @property + def config(self) -> CoreConfig | None: + """The CoreConfig that applies to this validation.""" + ... + + @property + def mode(self) -> Literal['python', 'json']: + """The type of input data we are currently validating.""" + ... + + @property + def data(self) -> dict[str, Any]: + """The data being validated for this model.""" + ... + + @property + def field_name(self) -> str | None: + """ + The name of the current field being validated if this validator is + attached to a model field. + """ + ... + + +ExpectedSerializationTypes = Literal[ + 'none', + 'int', + 'bool', + 'float', + 'str', + 'bytes', + 'bytearray', + 'list', + 'tuple', + 'set', + 'frozenset', + 'generator', + 'dict', + 'datetime', + 'date', + 'time', + 'timedelta', + 'url', + 'multi-host-url', + 'json', + 'uuid', + 'any', +] + + +class SimpleSerSchema(TypedDict, total=False): + type: Required[ExpectedSerializationTypes] + + +def simple_ser_schema(type: ExpectedSerializationTypes) -> SimpleSerSchema: + """ + Returns a schema for serialization with a custom type. + + Args: + type: The type to use for serialization + """ + return SimpleSerSchema(type=type) + + +# (input_value: Any, /) -> Any +GeneralPlainNoInfoSerializerFunction = Callable[[Any], Any] +# (input_value: Any, info: FieldSerializationInfo, /) -> Any +GeneralPlainInfoSerializerFunction = Callable[[Any, SerializationInfo[Any]], Any] +# (model: Any, input_value: Any, /) -> Any +FieldPlainNoInfoSerializerFunction = Callable[[Any, Any], Any] +# (model: Any, input_value: Any, info: FieldSerializationInfo, /) -> Any +FieldPlainInfoSerializerFunction = Callable[[Any, Any, FieldSerializationInfo[Any]], Any] +SerializerFunction = Union[ + GeneralPlainNoInfoSerializerFunction, + GeneralPlainInfoSerializerFunction, + FieldPlainNoInfoSerializerFunction, + FieldPlainInfoSerializerFunction, +] + +WhenUsed = Literal['always', 'unless-none', 'json', 'json-unless-none'] +""" +Values have the following meanings: + +* `'always'` means always use +* `'unless-none'` means use unless the value is `None` +* `'json'` means use when serializing to JSON +* `'json-unless-none'` means use when serializing to JSON and the value is not `None` +""" + + +class PlainSerializerFunctionSerSchema(TypedDict, total=False): + type: Required[Literal['function-plain']] + function: Required[SerializerFunction] + is_field_serializer: bool # default False + info_arg: bool # default False + return_schema: CoreSchema # if omitted, AnySchema is used + when_used: WhenUsed # default: 'always' + + +def plain_serializer_function_ser_schema( + function: SerializerFunction, + *, + is_field_serializer: bool | None = None, + info_arg: bool | None = None, + return_schema: CoreSchema | None = None, + when_used: WhenUsed = 'always', +) -> PlainSerializerFunctionSerSchema: + """ + Returns a schema for serialization with a function, can be either a "general" or "field" function. + + Args: + function: The function to use for serialization + is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument, + and `info` includes `field_name` + info_arg: Whether the function takes an `info` argument + return_schema: Schema to use for serializing return value + when_used: When the function should be called + """ + if when_used == 'always': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none( + type='function-plain', + function=function, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + return_schema=return_schema, + when_used=when_used, + ) + + +class SerializerFunctionWrapHandler(Protocol): # pragma: no cover + def __call__(self, input_value: Any, index_key: int | str | None = None, /) -> Any: ... + + +# (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any +GeneralWrapNoInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler], Any] +# (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any +GeneralWrapInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo[Any]], Any] +# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any +FieldWrapNoInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler], Any] +# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, info: FieldSerializationInfo, /) -> Any +FieldWrapInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler, FieldSerializationInfo[Any]], Any] +WrapSerializerFunction = Union[ + GeneralWrapNoInfoSerializerFunction, + GeneralWrapInfoSerializerFunction, + FieldWrapNoInfoSerializerFunction, + FieldWrapInfoSerializerFunction, +] + + +class WrapSerializerFunctionSerSchema(TypedDict, total=False): + type: Required[Literal['function-wrap']] + function: Required[WrapSerializerFunction] + is_field_serializer: bool # default False + info_arg: bool # default False + schema: CoreSchema # if omitted, the schema on which this serializer is defined is used + return_schema: CoreSchema # if omitted, AnySchema is used + when_used: WhenUsed # default: 'always' + + +def wrap_serializer_function_ser_schema( + function: WrapSerializerFunction, + *, + is_field_serializer: bool | None = None, + info_arg: bool | None = None, + schema: CoreSchema | None = None, + return_schema: CoreSchema | None = None, + when_used: WhenUsed = 'always', +) -> WrapSerializerFunctionSerSchema: + """ + Returns a schema for serialization with a wrap function, can be either a "general" or "field" function. + + Args: + function: The function to use for serialization + is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument, + and `info` includes `field_name` + info_arg: Whether the function takes an `info` argument + schema: The schema to use for the inner serialization + return_schema: Schema to use for serializing return value + when_used: When the function should be called + """ + if when_used == 'always': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none( + type='function-wrap', + function=function, + is_field_serializer=is_field_serializer, + info_arg=info_arg, + schema=schema, + return_schema=return_schema, + when_used=when_used, + ) + + +class FormatSerSchema(TypedDict, total=False): + type: Required[Literal['format']] + formatting_string: Required[str] + when_used: WhenUsed # default: 'json-unless-none' + + +def format_ser_schema(formatting_string: str, *, when_used: WhenUsed = 'json-unless-none') -> FormatSerSchema: + """ + Returns a schema for serialization using python's `format` method. + + Args: + formatting_string: String defining the format to use + when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default + """ + if when_used == 'json-unless-none': + # just to avoid extra elements in schema, and to use the actual default defined in rust + when_used = None # type: ignore + return _dict_not_none(type='format', formatting_string=formatting_string, when_used=when_used) + + +class ToStringSerSchema(TypedDict, total=False): + type: Required[Literal['to-string']] + when_used: WhenUsed # default: 'json-unless-none' + + +def to_string_ser_schema(*, when_used: WhenUsed = 'json-unless-none') -> ToStringSerSchema: + """ + Returns a schema for serialization using python's `str()` / `__str__` method. + + Args: + when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default + """ + s = dict(type='to-string') + if when_used != 'json-unless-none': + # just to avoid extra elements in schema, and to use the actual default defined in rust + s['when_used'] = when_used + return s # type: ignore + + +class ModelSerSchema(TypedDict, total=False): + type: Required[Literal['model']] + cls: Required[type[Any]] + schema: Required[CoreSchema] + + +def model_ser_schema(cls: type[Any], schema: CoreSchema) -> ModelSerSchema: + """ + Returns a schema for serialization using a model. + + Args: + cls: The expected class type, used to generate warnings if the wrong type is passed + schema: Internal schema to use to serialize the model dict + """ + return ModelSerSchema(type='model', cls=cls, schema=schema) + + +SerSchema = Union[ + SimpleSerSchema, + PlainSerializerFunctionSerSchema, + WrapSerializerFunctionSerSchema, + FormatSerSchema, + ToStringSerSchema, + ModelSerSchema, +] + + +class InvalidSchema(TypedDict, total=False): + type: Required[Literal['invalid']] + ref: str + metadata: dict[str, Any] + # note, we never plan to use this, but include it for type checking purposes to match + # all other CoreSchema union members + serialization: SerSchema + + +def invalid_schema(ref: str | None = None, metadata: dict[str, Any] | None = None) -> InvalidSchema: + """ + Returns an invalid schema, used to indicate that a schema is invalid. + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + + return _dict_not_none(type='invalid', ref=ref, metadata=metadata) + + +class ComputedField(TypedDict, total=False): + type: Required[Literal['computed-field']] + property_name: Required[str] + return_schema: Required[CoreSchema] + alias: str + serialization_exclude_if: Callable[[Any], bool] + metadata: dict[str, Any] + + +def computed_field( + property_name: str, + return_schema: CoreSchema, + *, + alias: str | None = None, + serialization_exclude_if: Callable[[Any], bool] | None = None, + metadata: dict[str, Any] | None = None, +) -> ComputedField: + """ + ComputedFields are properties of a model or dataclass that are included in serialization. + + Args: + property_name: The name of the property on the model or dataclass + return_schema: The schema used for the type returned by the computed field + alias: The name to use in the serialized output + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='computed-field', + property_name=property_name, + return_schema=return_schema, + alias=alias, + serialization_exclude_if=serialization_exclude_if, + metadata=metadata, + ) + + +class AnySchema(TypedDict, total=False): + type: Required[Literal['any']] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def any_schema( + *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None +) -> AnySchema: + """ + Returns a schema that matches any value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.any_schema() + v = SchemaValidator(schema) + assert v.validate_python(1) == 1 + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='any', ref=ref, metadata=metadata, serialization=serialization) + + +class NoneSchema(TypedDict, total=False): + type: Required[Literal['none']] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def none_schema( + *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None +) -> NoneSchema: + """ + Returns a schema that matches a None value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.none_schema() + v = SchemaValidator(schema) + assert v.validate_python(None) is None + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='none', ref=ref, metadata=metadata, serialization=serialization) + + +class BoolSchema(TypedDict, total=False): + type: Required[Literal['bool']] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def bool_schema( + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> BoolSchema: + """ + Returns a schema that matches a bool value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.bool_schema() + v = SchemaValidator(schema) + assert v.validate_python('True') is True + ``` + + Args: + strict: Whether the value should be a bool or a value that can be converted to a bool + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='bool', strict=strict, ref=ref, metadata=metadata, serialization=serialization) + + +class IntSchema(TypedDict, total=False): + type: Required[Literal['int']] + multiple_of: int + le: int + ge: int + lt: int + gt: int + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def int_schema( + *, + multiple_of: int | None = None, + le: int | None = None, + ge: int | None = None, + lt: int | None = None, + gt: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> IntSchema: + """ + Returns a schema that matches a int value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.int_schema(multiple_of=2, le=6, ge=2) + v = SchemaValidator(schema) + assert v.validate_python('4') == 4 + ``` + + Args: + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + strict: Whether the value should be a int or a value that can be converted to a int + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='int', + multiple_of=multiple_of, + le=le, + ge=ge, + lt=lt, + gt=gt, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class FloatSchema(TypedDict, total=False): + type: Required[Literal['float']] + allow_inf_nan: bool # whether 'NaN', '+inf', '-inf' should be forbidden. default: True + multiple_of: float + le: float + ge: float + lt: float + gt: float + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def float_schema( + *, + allow_inf_nan: bool | None = None, + multiple_of: float | None = None, + le: float | None = None, + ge: float | None = None, + lt: float | None = None, + gt: float | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> FloatSchema: + """ + Returns a schema that matches a float value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.float_schema(le=0.8, ge=0.2) + v = SchemaValidator(schema) + assert v.validate_python('0.5') == 0.5 + ``` + + Args: + allow_inf_nan: Whether to allow inf and nan values + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + strict: Whether the value should be a float or a value that can be converted to a float + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='float', + allow_inf_nan=allow_inf_nan, + multiple_of=multiple_of, + le=le, + ge=ge, + lt=lt, + gt=gt, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DecimalSchema(TypedDict, total=False): + type: Required[Literal['decimal']] + allow_inf_nan: bool # whether 'NaN', '+inf', '-inf' should be forbidden. default: False + multiple_of: Decimal + le: Decimal + ge: Decimal + lt: Decimal + gt: Decimal + max_digits: int + decimal_places: int + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def decimal_schema( + *, + allow_inf_nan: bool | None = None, + multiple_of: Decimal | None = None, + le: Decimal | None = None, + ge: Decimal | None = None, + lt: Decimal | None = None, + gt: Decimal | None = None, + max_digits: int | None = None, + decimal_places: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> DecimalSchema: + """ + Returns a schema that matches a decimal value, e.g.: + + ```py + from decimal import Decimal + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.decimal_schema(le=0.8, ge=0.2) + v = SchemaValidator(schema) + assert v.validate_python('0.5') == Decimal('0.5') + ``` + + Args: + allow_inf_nan: Whether to allow inf and nan values + multiple_of: The value must be a multiple of this number + le: The value must be less than or equal to this number + ge: The value must be greater than or equal to this number + lt: The value must be strictly less than this number + gt: The value must be strictly greater than this number + max_digits: The maximum number of decimal digits allowed + decimal_places: The maximum number of decimal places allowed + strict: Whether the value should be a float or a value that can be converted to a float + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='decimal', + gt=gt, + ge=ge, + lt=lt, + le=le, + max_digits=max_digits, + decimal_places=decimal_places, + multiple_of=multiple_of, + allow_inf_nan=allow_inf_nan, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ComplexSchema(TypedDict, total=False): + type: Required[Literal['complex']] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def complex_schema( + *, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ComplexSchema: + """ + Returns a schema that matches a complex value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.complex_schema() + v = SchemaValidator(schema) + assert v.validate_python('1+2j') == complex(1, 2) + assert v.validate_python(complex(1, 2)) == complex(1, 2) + ``` + + Args: + strict: Whether the value should be a complex object instance or a value that can be converted to a complex object + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='complex', + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class StringSchema(TypedDict, total=False): + type: Required[Literal['str']] + pattern: Union[str, Pattern[str]] + max_length: int + min_length: int + strip_whitespace: bool + to_lower: bool + to_upper: bool + regex_engine: Literal['rust-regex', 'python-re'] # default: 'rust-regex' + strict: bool + coerce_numbers_to_str: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def str_schema( + *, + pattern: str | Pattern[str] | None = None, + max_length: int | None = None, + min_length: int | None = None, + strip_whitespace: bool | None = None, + to_lower: bool | None = None, + to_upper: bool | None = None, + regex_engine: Literal['rust-regex', 'python-re'] | None = None, + strict: bool | None = None, + coerce_numbers_to_str: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> StringSchema: + """ + Returns a schema that matches a string value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.str_schema(max_length=10, min_length=2) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + ``` + + Args: + pattern: A regex pattern that the value must match + max_length: The value must be at most this length + min_length: The value must be at least this length + strip_whitespace: Whether to strip whitespace from the value + to_lower: Whether to convert the value to lowercase + to_upper: Whether to convert the value to uppercase + regex_engine: The regex engine to use for pattern validation. Default is 'rust-regex'. + - `rust-regex` uses the [`regex`](https://docs.rs/regex) Rust + crate, which is non-backtracking and therefore more DDoS + resistant, but does not support all regex features. + - `python-re` use the [`re`](https://docs.python.org/3/library/re.html) module, + which supports all regex features, but may be slower. + strict: Whether the value should be a string or a value that can be converted to a string + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='str', + pattern=pattern, + max_length=max_length, + min_length=min_length, + strip_whitespace=strip_whitespace, + to_lower=to_lower, + to_upper=to_upper, + regex_engine=regex_engine, + strict=strict, + coerce_numbers_to_str=coerce_numbers_to_str, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class BytesSchema(TypedDict, total=False): + type: Required[Literal['bytes']] + max_length: int + min_length: int + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def bytes_schema( + *, + max_length: int | None = None, + min_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> BytesSchema: + """ + Returns a schema that matches a bytes value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.bytes_schema(max_length=10, min_length=2) + v = SchemaValidator(schema) + assert v.validate_python(b'hello') == b'hello' + ``` + + Args: + max_length: The value must be at most this length + min_length: The value must be at least this length + strict: Whether the value should be a bytes or a value that can be converted to a bytes + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='bytes', + max_length=max_length, + min_length=min_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DateSchema(TypedDict, total=False): + type: Required[Literal['date']] + strict: bool + le: date + ge: date + lt: date + gt: date + now_op: Literal['past', 'future'] + # defaults to current local utc offset from `time.localtime().tm_gmtoff` + # value is restricted to -86_400 < offset < 86_400: + now_utc_offset: int + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def date_schema( + *, + strict: bool | None = None, + le: date | None = None, + ge: date | None = None, + lt: date | None = None, + gt: date | None = None, + now_op: Literal['past', 'future'] | None = None, + now_utc_offset: int | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> DateSchema: + """ + Returns a schema that matches a date value, e.g.: + + ```py + from datetime import date + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.date_schema(le=date(2020, 1, 1), ge=date(2019, 1, 1)) + v = SchemaValidator(schema) + assert v.validate_python(date(2019, 6, 1)) == date(2019, 6, 1) + ``` + + Args: + strict: Whether the value should be a date or a value that can be converted to a date + le: The value must be less than or equal to this date + ge: The value must be greater than or equal to this date + lt: The value must be strictly less than this date + gt: The value must be strictly greater than this date + now_op: The value must be in the past or future relative to the current date + now_utc_offset: The value must be in the past or future relative to the current date with this utc offset + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='date', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + now_op=now_op, + now_utc_offset=now_utc_offset, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TimeSchema(TypedDict, total=False): + type: Required[Literal['time']] + strict: bool + le: time + ge: time + lt: time + gt: time + tz_constraint: Union[Literal['aware', 'naive'], int] + microseconds_precision: Literal['truncate', 'error'] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def time_schema( + *, + strict: bool | None = None, + le: time | None = None, + ge: time | None = None, + lt: time | None = None, + gt: time | None = None, + tz_constraint: Literal['aware', 'naive'] | int | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> TimeSchema: + """ + Returns a schema that matches a time value, e.g.: + + ```py + from datetime import time + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.time_schema(le=time(12, 0, 0), ge=time(6, 0, 0)) + v = SchemaValidator(schema) + assert v.validate_python(time(9, 0, 0)) == time(9, 0, 0) + ``` + + Args: + strict: Whether the value should be a time or a value that can be converted to a time + le: The value must be less than or equal to this time + ge: The value must be greater than or equal to this time + lt: The value must be strictly less than this time + gt: The value must be strictly greater than this time + tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='time', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + tz_constraint=tz_constraint, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DatetimeSchema(TypedDict, total=False): + type: Required[Literal['datetime']] + strict: bool + le: datetime + ge: datetime + lt: datetime + gt: datetime + now_op: Literal['past', 'future'] + tz_constraint: Union[Literal['aware', 'naive'], int] + # defaults to current local utc offset from `time.localtime().tm_gmtoff` + # value is restricted to -86_400 < offset < 86_400 by bounds in generate_self_schema.py + now_utc_offset: int + microseconds_precision: Literal['truncate', 'error'] # default: 'truncate' + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def datetime_schema( + *, + strict: bool | None = None, + le: datetime | None = None, + ge: datetime | None = None, + lt: datetime | None = None, + gt: datetime | None = None, + now_op: Literal['past', 'future'] | None = None, + tz_constraint: Literal['aware', 'naive'] | int | None = None, + now_utc_offset: int | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> DatetimeSchema: + """ + Returns a schema that matches a datetime value, e.g.: + + ```py + from datetime import datetime + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.datetime_schema() + v = SchemaValidator(schema) + now = datetime.now() + assert v.validate_python(str(now)) == now + ``` + + Args: + strict: Whether the value should be a datetime or a value that can be converted to a datetime + le: The value must be less than or equal to this datetime + ge: The value must be greater than or equal to this datetime + lt: The value must be strictly less than this datetime + gt: The value must be strictly greater than this datetime + now_op: The value must be in the past or future relative to the current datetime + tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset + TODO: use of a tzinfo where offset changes based on the datetime is not yet supported + now_utc_offset: The value must be in the past or future relative to the current datetime with this utc offset + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='datetime', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + now_op=now_op, + tz_constraint=tz_constraint, + now_utc_offset=now_utc_offset, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TimedeltaSchema(TypedDict, total=False): + type: Required[Literal['timedelta']] + strict: bool + le: timedelta + ge: timedelta + lt: timedelta + gt: timedelta + microseconds_precision: Literal['truncate', 'error'] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def timedelta_schema( + *, + strict: bool | None = None, + le: timedelta | None = None, + ge: timedelta | None = None, + lt: timedelta | None = None, + gt: timedelta | None = None, + microseconds_precision: Literal['truncate', 'error'] = 'truncate', + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> TimedeltaSchema: + """ + Returns a schema that matches a timedelta value, e.g.: + + ```py + from datetime import timedelta + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.timedelta_schema(le=timedelta(days=1), ge=timedelta(days=0)) + v = SchemaValidator(schema) + assert v.validate_python(timedelta(hours=12)) == timedelta(hours=12) + ``` + + Args: + strict: Whether the value should be a timedelta or a value that can be converted to a timedelta + le: The value must be less than or equal to this timedelta + ge: The value must be greater than or equal to this timedelta + lt: The value must be strictly less than this timedelta + gt: The value must be strictly greater than this timedelta + microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='timedelta', + strict=strict, + le=le, + ge=ge, + lt=lt, + gt=gt, + microseconds_precision=microseconds_precision, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class LiteralSchema(TypedDict, total=False): + type: Required[Literal['literal']] + expected: Required[list[Any]] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def literal_schema( + expected: list[Any], + *, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> LiteralSchema: + """ + Returns a schema that matches a literal value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.literal_schema(['hello', 'world']) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + ``` + + Args: + expected: The value must be one of these values + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='literal', expected=expected, ref=ref, metadata=metadata, serialization=serialization) + + +class EnumSchema(TypedDict, total=False): + type: Required[Literal['enum']] + cls: Required[Any] + members: Required[list[Any]] + sub_type: Literal['str', 'int', 'float'] + missing: Callable[[Any], Any] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def enum_schema( + cls: Any, + members: list[Any], + *, + sub_type: Literal['str', 'int', 'float'] | None = None, + missing: Callable[[Any], Any] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> EnumSchema: + """ + Returns a schema that matches an enum value, e.g.: + + ```py + from enum import Enum + from pydantic_core import SchemaValidator, core_schema + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + schema = core_schema.enum_schema(Color, list(Color.__members__.values())) + v = SchemaValidator(schema) + assert v.validate_python(2) is Color.GREEN + ``` + + Args: + cls: The enum class + members: The members of the enum, generally `list(MyEnum.__members__.values())` + sub_type: The type of the enum, either 'str' or 'int' or None for plain enums + missing: A function to use when the value is not found in the enum, from `_missing_` + strict: Whether to use strict mode, defaults to False + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='enum', + cls=cls, + members=members, + sub_type=sub_type, + missing=missing, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class MissingSentinelSchema(TypedDict, total=False): + type: Required[Literal['missing-sentinel']] + metadata: dict[str, Any] + serialization: SerSchema + + +def missing_sentinel_schema( + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> MissingSentinelSchema: + """Returns a schema for the `MISSING` sentinel.""" + + return _dict_not_none( + type='missing-sentinel', + metadata=metadata, + serialization=serialization, + ) + + +# must match input/parse_json.rs::JsonType::try_from +JsonType = Literal['null', 'bool', 'int', 'float', 'str', 'list', 'dict'] + + +class IsInstanceSchema(TypedDict, total=False): + type: Required[Literal['is-instance']] + cls: Required[Any] + cls_repr: str + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def is_instance_schema( + cls: Any, + *, + cls_repr: str | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> IsInstanceSchema: + """ + Returns a schema that checks if a value is an instance of a class, equivalent to python's `isinstance` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + class A: + pass + + schema = core_schema.is_instance_schema(cls=A) + v = SchemaValidator(schema) + v.validate_python(A()) + ``` + + Args: + cls: The value must be an instance of this class + cls_repr: If provided this string is used in the validator name instead of `repr(cls)` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='is-instance', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization + ) + + +class IsSubclassSchema(TypedDict, total=False): + type: Required[Literal['is-subclass']] + cls: Required[type[Any]] + cls_repr: str + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def is_subclass_schema( + cls: type[Any], + *, + cls_repr: str | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> IsInstanceSchema: + """ + Returns a schema that checks if a value is a subtype of a class, equivalent to python's `issubclass` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + class A: + pass + + class B(A): + pass + + schema = core_schema.is_subclass_schema(cls=A) + v = SchemaValidator(schema) + v.validate_python(B) + ``` + + Args: + cls: The value must be a subclass of this class + cls_repr: If provided this string is used in the validator name instead of `repr(cls)` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='is-subclass', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization + ) + + +class CallableSchema(TypedDict, total=False): + type: Required[Literal['callable']] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def callable_schema( + *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None +) -> CallableSchema: + """ + Returns a schema that checks if a value is callable, equivalent to python's `callable` method, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.callable_schema() + v = SchemaValidator(schema) + v.validate_python(min) + ``` + + Args: + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='callable', ref=ref, metadata=metadata, serialization=serialization) + + +class UuidSchema(TypedDict, total=False): + type: Required[Literal['uuid']] + version: Literal[1, 3, 4, 5, 7] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def uuid_schema( + *, + version: Literal[1, 3, 4, 5, 6, 7, 8] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> UuidSchema: + return _dict_not_none( + type='uuid', version=version, strict=strict, ref=ref, metadata=metadata, serialization=serialization + ) + + +class IncExSeqSerSchema(TypedDict, total=False): + type: Required[Literal['include-exclude-sequence']] + include: set[int] + exclude: set[int] + + +def filter_seq_schema(*, include: set[int] | None = None, exclude: set[int] | None = None) -> IncExSeqSerSchema: + return _dict_not_none(type='include-exclude-sequence', include=include, exclude=exclude) + + +IncExSeqOrElseSerSchema = Union[IncExSeqSerSchema, SerSchema] + + +class ListSchema(TypedDict, total=False): + type: Required[Literal['list']] + items_schema: CoreSchema + min_length: int + max_length: int + fail_fast: bool + strict: bool + ref: str + metadata: dict[str, Any] + serialization: IncExSeqOrElseSerSchema + + +def list_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + fail_fast: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> ListSchema: + """ + Returns a schema that matches a list value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.list_schema(core_schema.int_schema(), min_length=0, max_length=10) + v = SchemaValidator(schema) + assert v.validate_python(['4']) == [4] + ``` + + Args: + items_schema: The value must be a list of items that match this schema + min_length: The value must be a list with at least this many items + max_length: The value must be a list with at most this many items + fail_fast: Stop validation on the first error + strict: The value must be a list with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='list', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + fail_fast=fail_fast, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# @deprecated('tuple_positional_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.') +def tuple_positional_schema( + items_schema: list[CoreSchema], + *, + extras_schema: CoreSchema | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of schemas, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_positional_schema( + [core_schema.int_schema(), core_schema.str_schema()] + ) + v = SchemaValidator(schema) + assert v.validate_python((1, 'hello')) == (1, 'hello') + ``` + + Args: + items_schema: The value must be a tuple with items that match these schemas + extras_schema: The value must be a tuple with items that match this schema + This was inspired by JSON schema's `prefixItems` and `items` fields. + In python's `typing.Tuple`, you can't specify a type for "extra" items -- they must all be the same type + if the length is variable. So this field won't be set from a `typing.Tuple` annotation on a pydantic model. + strict: The value must be a tuple with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if extras_schema is not None: + variadic_item_index = len(items_schema) + items_schema = items_schema + [extras_schema] + else: + variadic_item_index = None + return tuple_schema( + items_schema=items_schema, + variadic_item_index=variadic_item_index, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# @deprecated('tuple_variable_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.') +def tuple_variable_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_variable_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python(('1', 2, 3)) == (1, 2, 3) + ``` + + Args: + items_schema: The value must be a tuple with items that match this schema + min_length: The value must be a tuple with at least this many items + max_length: The value must be a tuple with at most this many items + strict: The value must be a tuple with exactly this many items + ref: Optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return tuple_schema( + items_schema=[items_schema or any_schema()], + variadic_item_index=0, + min_length=min_length, + max_length=max_length, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TupleSchema(TypedDict, total=False): + type: Required[Literal['tuple']] + items_schema: Required[list[CoreSchema]] + variadic_item_index: int + min_length: int + max_length: int + fail_fast: bool + strict: bool + ref: str + metadata: dict[str, Any] + serialization: IncExSeqOrElseSerSchema + + +def tuple_schema( + items_schema: list[CoreSchema], + *, + variadic_item_index: int | None = None, + min_length: int | None = None, + max_length: int | None = None, + fail_fast: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> TupleSchema: + """ + Returns a schema that matches a tuple of schemas, with an optional variadic item, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.tuple_schema( + [core_schema.int_schema(), core_schema.str_schema(), core_schema.float_schema()], + variadic_item_index=1, + ) + v = SchemaValidator(schema) + assert v.validate_python((1, 'hello', 'world', 1.5)) == (1, 'hello', 'world', 1.5) + ``` + + Args: + items_schema: The value must be a tuple with items that match these schemas + variadic_item_index: The index of the schema in `items_schema` to be treated as variadic (following PEP 646) + min_length: The value must be a tuple with at least this many items + max_length: The value must be a tuple with at most this many items + fail_fast: Stop validation on the first error + strict: The value must be a tuple with exactly this many items + ref: Optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='tuple', + items_schema=items_schema, + variadic_item_index=variadic_item_index, + min_length=min_length, + max_length=max_length, + fail_fast=fail_fast, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class SetSchema(TypedDict, total=False): + type: Required[Literal['set']] + items_schema: CoreSchema + min_length: int + max_length: int + fail_fast: bool + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def set_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + fail_fast: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> SetSchema: + """ + Returns a schema that matches a set of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.set_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python({1, '2', 3}) == {1, 2, 3} + ``` + + Args: + items_schema: The value must be a set with items that match this schema + min_length: The value must be a set with at least this many items + max_length: The value must be a set with at most this many items + fail_fast: Stop validation on the first error + strict: The value must be a set with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='set', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + fail_fast=fail_fast, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class FrozenSetSchema(TypedDict, total=False): + type: Required[Literal['frozenset']] + items_schema: CoreSchema + min_length: int + max_length: int + fail_fast: bool + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def frozenset_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + fail_fast: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> FrozenSetSchema: + """ + Returns a schema that matches a frozenset of a given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.frozenset_schema( + items_schema=core_schema.int_schema(), min_length=0, max_length=10 + ) + v = SchemaValidator(schema) + assert v.validate_python(frozenset(range(3))) == frozenset({0, 1, 2}) + ``` + + Args: + items_schema: The value must be a frozenset with items that match this schema + min_length: The value must be a frozenset with at least this many items + max_length: The value must be a frozenset with at most this many items + fail_fast: Stop validation on the first error + strict: The value must be a frozenset with exactly this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='frozenset', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + fail_fast=fail_fast, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class GeneratorSchema(TypedDict, total=False): + type: Required[Literal['generator']] + items_schema: CoreSchema + min_length: int + max_length: int + ref: str + metadata: dict[str, Any] + serialization: IncExSeqOrElseSerSchema + + +def generator_schema( + items_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: IncExSeqOrElseSerSchema | None = None, +) -> GeneratorSchema: + """ + Returns a schema that matches a generator value, e.g.: + + ```py + from typing import Iterator + from pydantic_core import SchemaValidator, core_schema + + def gen() -> Iterator[int]: + yield 1 + + schema = core_schema.generator_schema(items_schema=core_schema.int_schema()) + v = SchemaValidator(schema) + v.validate_python(gen()) + ``` + + Unlike other types, validated generators do not raise ValidationErrors eagerly, + but instead will raise a ValidationError when a violating value is actually read from the generator. + This is to ensure that "validated" generators retain the benefit of lazy evaluation. + + Args: + items_schema: The value must be a generator with items that match this schema + min_length: The value must be a generator that yields at least this many items + max_length: The value must be a generator that yields at most this many items + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='generator', + items_schema=items_schema, + min_length=min_length, + max_length=max_length, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +IncExDict = set[Union[int, str]] + + +class IncExDictSerSchema(TypedDict, total=False): + type: Required[Literal['include-exclude-dict']] + include: IncExDict + exclude: IncExDict + + +def filter_dict_schema(*, include: IncExDict | None = None, exclude: IncExDict | None = None) -> IncExDictSerSchema: + return _dict_not_none(type='include-exclude-dict', include=include, exclude=exclude) + + +IncExDictOrElseSerSchema = Union[IncExDictSerSchema, SerSchema] + + +class DictSchema(TypedDict, total=False): + type: Required[Literal['dict']] + keys_schema: CoreSchema # default: AnySchema + values_schema: CoreSchema # default: AnySchema + min_length: int + max_length: int + fail_fast: bool + strict: bool + ref: str + metadata: dict[str, Any] + serialization: IncExDictOrElseSerSchema + + +def dict_schema( + keys_schema: CoreSchema | None = None, + values_schema: CoreSchema | None = None, + *, + min_length: int | None = None, + max_length: int | None = None, + fail_fast: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> DictSchema: + """ + Returns a schema that matches a dict value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.dict_schema( + keys_schema=core_schema.str_schema(), values_schema=core_schema.int_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2} + ``` + + Args: + keys_schema: The value must be a dict with keys that match this schema + values_schema: The value must be a dict with values that match this schema + min_length: The value must be a dict with at least this many items + max_length: The value must be a dict with at most this many items + fail_fast: Stop validation on the first error + strict: Whether the keys and values should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='dict', + keys_schema=keys_schema, + values_schema=values_schema, + min_length=min_length, + max_length=max_length, + fail_fast=fail_fast, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +# (input_value: Any, /) -> Any +NoInfoValidatorFunction = Callable[[Any], Any] + + +class NoInfoValidatorFunctionSchema(TypedDict): + type: Literal['no-info'] + function: NoInfoValidatorFunction + + +# (input_value: Any, info: ValidationInfo, /) -> Any +WithInfoValidatorFunction = Callable[[Any, ValidationInfo[Any]], Any] + + +class WithInfoValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['with-info']] + function: Required[WithInfoValidatorFunction] + field_name: str # deprecated + + +ValidationFunction = Union[NoInfoValidatorFunctionSchema, WithInfoValidatorFunctionSchema] + + +class _ValidatorFunctionSchema(TypedDict, total=False): + function: Required[ValidationFunction] + schema: Required[CoreSchema] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +class BeforeValidatorFunctionSchema(_ValidatorFunctionSchema, total=False): + type: Required[Literal['function-before']] + json_schema_input_schema: CoreSchema + + +def no_info_before_validator_function( + function: NoInfoValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> BeforeValidatorFunctionSchema: + """ + Returns a schema that calls a validator function before validating, no `info` argument is provided, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: bytes) -> str: + return v.decode() + 'world' + + func_schema = core_schema.no_info_before_validator_function( + function=fn, schema=core_schema.str_schema() + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-before', + function={'type': 'no-info', 'function': function}, + schema=schema, + ref=ref, + json_schema_input_schema=json_schema_input_schema, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_before_validator_function( + function: WithInfoValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> BeforeValidatorFunctionSchema: + """ + Returns a schema that calls a validator function before validation, the function is called with + an `info` argument, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: bytes, info: core_schema.ValidationInfo) -> str: + assert info.data is not None + assert info.field_name is not None + return v.decode() + 'world' + + func_schema = core_schema.with_info_before_validator_function( + function=fn, schema=core_schema.str_schema() + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call + field_name: The name of the field this validator is applied to, if any (deprecated) + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if field_name is not None: + warnings.warn( + 'The `field_name` argument on `with_info_before_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.', + DeprecationWarning, + stacklevel=2, + ) + + return _dict_not_none( + type='function-before', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + ref=ref, + json_schema_input_schema=json_schema_input_schema, + metadata=metadata, + serialization=serialization, + ) + + +class AfterValidatorFunctionSchema(_ValidatorFunctionSchema, total=False): + type: Required[Literal['function-after']] + + +def no_info_after_validator_function( + function: NoInfoValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> AfterValidatorFunctionSchema: + """ + Returns a schema that calls a validator function after validating, no `info` argument is provided, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str) -> str: + return v + 'world' + + func_schema = core_schema.no_info_after_validator_function(fn, core_schema.str_schema()) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call after the schema is validated + schema: The schema to validate before the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-after', + function={'type': 'no-info', 'function': function}, + schema=schema, + ref=ref, + json_schema_input_schema=json_schema_input_schema, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_after_validator_function( + function: WithInfoValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> AfterValidatorFunctionSchema: + """ + Returns a schema that calls a validator function after validation, the function is called with + an `info` argument, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert info.data is not None + assert info.field_name is not None + return v + 'world' + + func_schema = core_schema.with_info_after_validator_function( + function=fn, schema=core_schema.str_schema() + ) + schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)}) + + v = SchemaValidator(schema) + assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'} + ``` + + Args: + function: The validator function to call after the schema is validated + schema: The schema to validate before the validator function + field_name: The name of the field this validator is applied to, if any (deprecated) + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if field_name is not None: + warnings.warn( + 'The `field_name` argument on `with_info_after_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.', + DeprecationWarning, + stacklevel=2, + ) + + return _dict_not_none( + type='function-after', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ValidatorFunctionWrapHandler(Protocol): + def __call__(self, input_value: Any, outer_location: str | int | None = None, /) -> Any: # pragma: no cover + ... + + +# (input_value: Any, validator: ValidatorFunctionWrapHandler, /) -> Any +NoInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler], Any] + + +class NoInfoWrapValidatorFunctionSchema(TypedDict): + type: Literal['no-info'] + function: NoInfoWrapValidatorFunction + + +# (input_value: Any, validator: ValidatorFunctionWrapHandler, info: ValidationInfo, /) -> Any +WithInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler, ValidationInfo[Any]], Any] + + +class WithInfoWrapValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['with-info']] + function: Required[WithInfoWrapValidatorFunction] + field_name: str # deprecated + + +WrapValidatorFunction = Union[NoInfoWrapValidatorFunctionSchema, WithInfoWrapValidatorFunctionSchema] + + +class WrapValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['function-wrap']] + function: Required[WrapValidatorFunction] + schema: Required[CoreSchema] + ref: str + json_schema_input_schema: CoreSchema + metadata: dict[str, Any] + serialization: SerSchema + + +def no_info_wrap_validator_function( + function: NoInfoWrapValidatorFunction, + schema: CoreSchema, + *, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> WrapValidatorFunctionSchema: + """ + Returns a schema which calls a function with a `validator` callable argument which can + optionally be used to call inner validation with the function logic, this is much like the + "onion" implementation of middleware in many popular web frameworks, no `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn( + v: str, + validator: core_schema.ValidatorFunctionWrapHandler, + ) -> str: + return validator(input_value=v) + 'world' + + schema = core_schema.no_info_wrap_validator_function( + function=fn, schema=core_schema.str_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-wrap', + function={'type': 'no-info', 'function': function}, + schema=schema, + json_schema_input_schema=json_schema_input_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_wrap_validator_function( + function: WithInfoWrapValidatorFunction, + schema: CoreSchema, + *, + field_name: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> WrapValidatorFunctionSchema: + """ + Returns a schema which calls a function with a `validator` callable argument which can + optionally be used to call inner validation with the function logic, this is much like the + "onion" implementation of middleware in many popular web frameworks, an `info` argument is also passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn( + v: str, + validator: core_schema.ValidatorFunctionWrapHandler, + info: core_schema.ValidationInfo, + ) -> str: + return validator(input_value=v) + 'world' + + schema = core_schema.with_info_wrap_validator_function( + function=fn, schema=core_schema.str_schema() + ) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + schema: The schema to validate the output of the validator function + field_name: The name of the field this validator is applied to, if any (deprecated) + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if field_name is not None: + warnings.warn( + 'The `field_name` argument on `with_info_wrap_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.', + DeprecationWarning, + stacklevel=2, + ) + + return _dict_not_none( + type='function-wrap', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + schema=schema, + json_schema_input_schema=json_schema_input_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class PlainValidatorFunctionSchema(TypedDict, total=False): + type: Required[Literal['function-plain']] + function: Required[ValidationFunction] + ref: str + json_schema_input_schema: CoreSchema + metadata: dict[str, Any] + serialization: SerSchema + + +def no_info_plain_validator_function( + function: NoInfoValidatorFunction, + *, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> PlainValidatorFunctionSchema: + """ + Returns a schema that uses the provided function for validation, no `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str) -> str: + assert 'hello' in v + return v + 'world' + + schema = core_schema.no_info_plain_validator_function(function=fn) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='function-plain', + function={'type': 'no-info', 'function': function}, + ref=ref, + json_schema_input_schema=json_schema_input_schema, + metadata=metadata, + serialization=serialization, + ) + + +def with_info_plain_validator_function( + function: WithInfoValidatorFunction, + *, + field_name: str | None = None, + ref: str | None = None, + json_schema_input_schema: CoreSchema | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> PlainValidatorFunctionSchema: + """ + Returns a schema that uses the provided function for validation, an `info` argument is passed, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + 'world' + + schema = core_schema.with_info_plain_validator_function(function=fn) + v = SchemaValidator(schema) + assert v.validate_python('hello ') == 'hello world' + ``` + + Args: + function: The validator function to call + field_name: The name of the field this validator is applied to, if any (deprecated) + ref: optional unique identifier of the schema, used to reference the schema in other places + json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + if field_name is not None: + warnings.warn( + 'The `field_name` argument on `with_info_plain_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.', + DeprecationWarning, + stacklevel=2, + ) + + return _dict_not_none( + type='function-plain', + function=_dict_not_none(type='with-info', function=function, field_name=field_name), + ref=ref, + json_schema_input_schema=json_schema_input_schema, + metadata=metadata, + serialization=serialization, + ) + + +class WithDefaultSchema(TypedDict, total=False): + type: Required[Literal['default']] + schema: Required[CoreSchema] + default: Any + default_factory: Union[Callable[[], Any], Callable[[dict[str, Any]], Any]] + default_factory_takes_data: bool + on_error: Literal['raise', 'omit', 'default'] # default: 'raise' + validate_default: bool # default: False + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def with_default_schema( + schema: CoreSchema, + *, + default: Any = PydanticUndefined, + default_factory: Union[Callable[[], Any], Callable[[dict[str, Any]], Any], None] = None, + default_factory_takes_data: bool | None = None, + on_error: Literal['raise', 'omit', 'default'] | None = None, + validate_default: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> WithDefaultSchema: + """ + Returns a schema that adds a default value to the given schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.with_default_schema(core_schema.str_schema(), default='hello') + wrapper_schema = core_schema.typed_dict_schema( + {'a': core_schema.typed_dict_field(schema)} + ) + v = SchemaValidator(wrapper_schema) + assert v.validate_python({}) == v.validate_python({'a': 'hello'}) + ``` + + Args: + schema: The schema to add a default value to + default: The default value to use + default_factory: A callable that returns the default value to use + default_factory_takes_data: Whether the default factory takes a validated data argument + on_error: What to do if the schema validation fails. One of 'raise', 'omit', 'default' + validate_default: Whether the default value should be validated + strict: Whether the underlying schema should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + s = _dict_not_none( + type='default', + schema=schema, + default_factory=default_factory, + default_factory_takes_data=default_factory_takes_data, + on_error=on_error, + validate_default=validate_default, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + if default is not PydanticUndefined: + s['default'] = default + return s + + +class NullableSchema(TypedDict, total=False): + type: Required[Literal['nullable']] + schema: Required[CoreSchema] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def nullable_schema( + schema: CoreSchema, + *, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> NullableSchema: + """ + Returns a schema that matches a nullable value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.nullable_schema(core_schema.str_schema()) + v = SchemaValidator(schema) + assert v.validate_python(None) is None + ``` + + Args: + schema: The schema to wrap + strict: Whether the underlying schema should be validated with strict mode + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='nullable', schema=schema, strict=strict, ref=ref, metadata=metadata, serialization=serialization + ) + + +class UnionSchema(TypedDict, total=False): + type: Required[Literal['union']] + choices: Required[list[Union[CoreSchema, tuple[CoreSchema, str]]]] + # default true, whether to automatically collapse unions with one element to the inner validator + auto_collapse: bool + custom_error_type: str + custom_error_message: str + custom_error_context: dict[str, Union[str, int, float]] + mode: Literal['smart', 'left_to_right'] # default: 'smart' + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def union_schema( + choices: list[CoreSchema | tuple[CoreSchema, str]], + *, + auto_collapse: bool | None = None, + custom_error_type: str | None = None, + custom_error_message: str | None = None, + custom_error_context: dict[str, str | int] | None = None, + mode: Literal['smart', 'left_to_right'] | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> UnionSchema: + """ + Returns a schema that matches a union value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.union_schema([core_schema.str_schema(), core_schema.int_schema()]) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello' + assert v.validate_python(1) == 1 + ``` + + Args: + choices: The schemas to match. If a tuple, the second item is used as the label for the case. + auto_collapse: whether to automatically collapse unions with one element to the inner validator, default true + custom_error_type: The custom error type to use if the validation fails + custom_error_message: The custom error message to use if the validation fails + custom_error_context: The custom error context to use if the validation fails + mode: How to select which choice to return + * `smart` (default) will try to return the choice which is the closest match to the input value + * `left_to_right` will return the first choice in `choices` which succeeds validation + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='union', + choices=choices, + auto_collapse=auto_collapse, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + mode=mode, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TaggedUnionSchema(TypedDict, total=False): + type: Required[Literal['tagged-union']] + choices: Required[dict[Hashable, CoreSchema]] + discriminator: Required[Union[str, list[Union[str, int]], list[list[Union[str, int]]], Callable[[Any], Hashable]]] + custom_error_type: str + custom_error_message: str + custom_error_context: dict[str, Union[str, int, float]] + strict: bool + from_attributes: bool # default: True + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def tagged_union_schema( + choices: dict[Any, CoreSchema], + discriminator: str | list[str | int] | list[list[str | int]] | Callable[[Any], Any], + *, + custom_error_type: str | None = None, + custom_error_message: str | None = None, + custom_error_context: dict[str, int | str | float] | None = None, + strict: bool | None = None, + from_attributes: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> TaggedUnionSchema: + """ + Returns a schema that matches a tagged union value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + apple_schema = core_schema.typed_dict_schema( + { + 'foo': core_schema.typed_dict_field(core_schema.str_schema()), + 'bar': core_schema.typed_dict_field(core_schema.int_schema()), + } + ) + banana_schema = core_schema.typed_dict_schema( + { + 'foo': core_schema.typed_dict_field(core_schema.str_schema()), + 'spam': core_schema.typed_dict_field( + core_schema.list_schema(items_schema=core_schema.int_schema()) + ), + } + ) + schema = core_schema.tagged_union_schema( + choices={ + 'apple': apple_schema, + 'banana': banana_schema, + }, + discriminator='foo', + ) + v = SchemaValidator(schema) + assert v.validate_python({'foo': 'apple', 'bar': '123'}) == {'foo': 'apple', 'bar': 123} + assert v.validate_python({'foo': 'banana', 'spam': [1, 2, 3]}) == { + 'foo': 'banana', + 'spam': [1, 2, 3], + } + ``` + + Args: + choices: The schemas to match + When retrieving a schema from `choices` using the discriminator value, if the value is a str, + it should be fed back into the `choices` map until a schema is obtained + (This approach is to prevent multiple ownership of a single schema in Rust) + discriminator: The discriminator to use to determine the schema to use + * If `discriminator` is a str, it is the name of the attribute to use as the discriminator + * If `discriminator` is a list of int/str, it should be used as a "path" to access the discriminator + * If `discriminator` is a list of lists, each inner list is a path, and the first path that exists is used + * If `discriminator` is a callable, it should return the discriminator when called on the value to validate; + the callable can return `None` to indicate that there is no matching discriminator present on the input + custom_error_type: The custom error type to use if the validation fails + custom_error_message: The custom error message to use if the validation fails + custom_error_context: The custom error context to use if the validation fails + strict: Whether the underlying schemas should be validated with strict mode + from_attributes: Whether to use the attributes of the object to retrieve the discriminator value + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='tagged-union', + choices=choices, + discriminator=discriminator, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + strict=strict, + from_attributes=from_attributes, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ChainSchema(TypedDict, total=False): + type: Required[Literal['chain']] + steps: Required[list[CoreSchema]] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def chain_schema( + steps: list[CoreSchema], + *, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ChainSchema: + """ + Returns a schema that chains the provided validation schemas, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + ' world' + + fn_schema = core_schema.with_info_plain_validator_function(function=fn) + schema = core_schema.chain_schema( + [fn_schema, fn_schema, fn_schema, core_schema.str_schema()] + ) + v = SchemaValidator(schema) + assert v.validate_python('hello') == 'hello world world world' + ``` + + Args: + steps: The schemas to chain + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='chain', steps=steps, ref=ref, metadata=metadata, serialization=serialization) + + +class LaxOrStrictSchema(TypedDict, total=False): + type: Required[Literal['lax-or-strict']] + lax_schema: Required[CoreSchema] + strict_schema: Required[CoreSchema] + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def lax_or_strict_schema( + lax_schema: CoreSchema, + strict_schema: CoreSchema, + *, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> LaxOrStrictSchema: + """ + Returns a schema that uses the lax or strict schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + def fn(v: str, info: core_schema.ValidationInfo) -> str: + assert 'hello' in v + return v + ' world' + + lax_schema = core_schema.int_schema(strict=False) + strict_schema = core_schema.int_schema(strict=True) + + schema = core_schema.lax_or_strict_schema( + lax_schema=lax_schema, strict_schema=strict_schema, strict=True + ) + v = SchemaValidator(schema) + assert v.validate_python(123) == 123 + + schema = core_schema.lax_or_strict_schema( + lax_schema=lax_schema, strict_schema=strict_schema, strict=False + ) + v = SchemaValidator(schema) + assert v.validate_python('123') == 123 + ``` + + Args: + lax_schema: The lax schema to use + strict_schema: The strict schema to use + strict: Whether the strict schema should be used + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='lax-or-strict', + lax_schema=lax_schema, + strict_schema=strict_schema, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class JsonOrPythonSchema(TypedDict, total=False): + type: Required[Literal['json-or-python']] + json_schema: Required[CoreSchema] + python_schema: Required[CoreSchema] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def json_or_python_schema( + json_schema: CoreSchema, + python_schema: CoreSchema, + *, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> JsonOrPythonSchema: + """ + Returns a schema that uses the Json or Python schema depending on the input: + + ```py + from pydantic_core import SchemaValidator, ValidationError, core_schema + + v = SchemaValidator( + core_schema.json_or_python_schema( + json_schema=core_schema.int_schema(), + python_schema=core_schema.int_schema(strict=True), + ) + ) + + assert v.validate_json('"123"') == 123 + + try: + v.validate_python('123') + except ValidationError: + pass + else: + raise AssertionError('Validation should have failed') + ``` + + Args: + json_schema: The schema to use for Json inputs + python_schema: The schema to use for Python inputs + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='json-or-python', + json_schema=json_schema, + python_schema=python_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class TypedDictField(TypedDict, total=False): + type: Required[Literal['typed-dict-field']] + schema: Required[CoreSchema] + required: bool + validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + metadata: dict[str, Any] + serialization_exclude_if: Callable[[Any], bool] # default None + + +def typed_dict_field( + schema: CoreSchema, + *, + required: bool | None = None, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + metadata: dict[str, Any] | None = None, + serialization_exclude_if: Callable[[Any], bool] | None = None, +) -> TypedDictField: + """ + Returns a schema that matches a typed dict field, e.g.: + + ```py + from pydantic_core import core_schema + + field = core_schema.typed_dict_field(schema=core_schema.int_schema(), required=True) + ``` + + Args: + schema: The schema to use for the field + required: Whether the field is required, otherwise uses the value from `total` on the typed dict + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + serialization_exclude_if: A callable that determines whether to exclude the field when serializing based on its value. + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='typed-dict-field', + schema=schema, + required=required, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + serialization_exclude_if=serialization_exclude_if, + metadata=metadata, + ) + + +class TypedDictSchema(TypedDict, total=False): + type: Required[Literal['typed-dict']] + fields: Required[dict[str, TypedDictField]] + cls: type[Any] + cls_name: str + computed_fields: list[ComputedField] + strict: bool + extras_schema: CoreSchema + # all these values can be set via config, equivalent fields have `typed_dict_` prefix + extra_behavior: ExtraBehavior + total: bool # default: True + ref: str + metadata: dict[str, Any] + serialization: SerSchema + config: CoreConfig + + +def typed_dict_schema( + fields: dict[str, TypedDictField], + *, + cls: type[Any] | None = None, + cls_name: str | None = None, + computed_fields: list[ComputedField] | None = None, + strict: bool | None = None, + extras_schema: CoreSchema | None = None, + extra_behavior: ExtraBehavior | None = None, + total: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, + config: CoreConfig | None = None, +) -> TypedDictSchema: + """ + Returns a schema that matches a typed dict, e.g.: + + ```py + from typing_extensions import TypedDict + + from pydantic_core import SchemaValidator, core_schema + + class MyTypedDict(TypedDict): + a: str + + wrapper_schema = core_schema.typed_dict_schema( + {'a': core_schema.typed_dict_field(core_schema.str_schema())}, cls=MyTypedDict + ) + v = SchemaValidator(wrapper_schema) + assert v.validate_python({'a': 'hello'}) == {'a': 'hello'} + ``` + + Args: + fields: The fields to use for the typed dict + cls: The class to use for the typed dict + cls_name: The name to use in error locations. Falls back to `cls.__name__`, or the validator name if no class + is provided. + computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model + strict: Whether the typed dict is strict + extras_schema: The extra validator to use for the typed dict + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + extra_behavior: The extra behavior to use for the typed dict + total: Whether the typed dict is total, otherwise uses `typed_dict_total` from config + serialization: Custom serialization schema + """ + return _dict_not_none( + type='typed-dict', + fields=fields, + cls=cls, + cls_name=cls_name, + computed_fields=computed_fields, + strict=strict, + extras_schema=extras_schema, + extra_behavior=extra_behavior, + total=total, + ref=ref, + metadata=metadata, + serialization=serialization, + config=config, + ) + + +class ModelField(TypedDict, total=False): + type: Required[Literal['model-field']] + schema: Required[CoreSchema] + validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + serialization_exclude_if: Callable[[Any], bool] # default: None + frozen: bool + metadata: dict[str, Any] + + +def model_field( + schema: CoreSchema, + *, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + serialization_exclude_if: Callable[[Any], bool] | None = None, + frozen: bool | None = None, + metadata: dict[str, Any] | None = None, +) -> ModelField: + """ + Returns a schema for a model field, e.g.: + + ```py + from pydantic_core import core_schema + + field = core_schema.model_field(schema=core_schema.int_schema()) + ``` + + Args: + schema: The schema to use for the field + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + serialization_exclude_if: A Callable that determines whether to exclude a field during serialization based on its value. + frozen: Whether the field is frozen + metadata: Any other information you want to include with the schema, not used by pydantic-core + """ + return _dict_not_none( + type='model-field', + schema=schema, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + serialization_exclude_if=serialization_exclude_if, + frozen=frozen, + metadata=metadata, + ) + + +class ModelFieldsSchema(TypedDict, total=False): + type: Required[Literal['model-fields']] + fields: Required[dict[str, ModelField]] + model_name: str + computed_fields: list[ComputedField] + strict: bool + extras_schema: CoreSchema + extras_keys_schema: CoreSchema + extra_behavior: ExtraBehavior + from_attributes: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def model_fields_schema( + fields: dict[str, ModelField], + *, + model_name: str | None = None, + computed_fields: list[ComputedField] | None = None, + strict: bool | None = None, + extras_schema: CoreSchema | None = None, + extras_keys_schema: CoreSchema | None = None, + extra_behavior: ExtraBehavior | None = None, + from_attributes: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ModelFieldsSchema: + """ + Returns a schema that matches the fields of a Pydantic model, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + wrapper_schema = core_schema.model_fields_schema( + {'a': core_schema.model_field(core_schema.str_schema())} + ) + v = SchemaValidator(wrapper_schema) + print(v.validate_python({'a': 'hello'})) + #> ({'a': 'hello'}, None, {'a'}) + ``` + + Args: + fields: The fields of the model + model_name: The name of the model, used for error messages, defaults to "Model" + computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model + strict: Whether the model is strict + extras_schema: The schema to use when validating extra input data + extras_keys_schema: The schema to use when validating the keys of extra input data + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + extra_behavior: The extra behavior to use for the model fields + from_attributes: Whether the model fields should be populated from attributes + serialization: Custom serialization schema + """ + return _dict_not_none( + type='model-fields', + fields=fields, + model_name=model_name, + computed_fields=computed_fields, + strict=strict, + extras_schema=extras_schema, + extras_keys_schema=extras_keys_schema, + extra_behavior=extra_behavior, + from_attributes=from_attributes, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ModelSchema(TypedDict, total=False): + type: Required[Literal['model']] + cls: Required[type[Any]] + generic_origin: type[Any] + schema: Required[CoreSchema] + custom_init: bool + root_model: bool + post_init: str + revalidate_instances: Literal['always', 'never', 'subclass-instances'] # default: 'never' + strict: bool + frozen: bool + extra_behavior: ExtraBehavior + config: CoreConfig + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def model_schema( + cls: type[Any], + schema: CoreSchema, + *, + generic_origin: type[Any] | None = None, + custom_init: bool | None = None, + root_model: bool | None = None, + post_init: str | None = None, + revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None, + strict: bool | None = None, + frozen: bool | None = None, + extra_behavior: ExtraBehavior | None = None, + config: CoreConfig | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ModelSchema: + """ + A model schema generally contains a typed-dict schema. + It will run the typed dict validator, then create a new class + and set the dict and fields set returned from the typed dict validator + to `__dict__` and `__pydantic_fields_set__` respectively. + + Example: + + ```py + from pydantic_core import CoreConfig, SchemaValidator, core_schema + + class MyModel: + __slots__ = ( + '__dict__', + '__pydantic_fields_set__', + '__pydantic_extra__', + '__pydantic_private__', + ) + + schema = core_schema.model_schema( + cls=MyModel, + config=CoreConfig(str_max_length=5), + schema=core_schema.model_fields_schema( + fields={'a': core_schema.model_field(core_schema.str_schema())}, + ), + ) + v = SchemaValidator(schema) + assert v.isinstance_python({'a': 'hello'}) is True + assert v.isinstance_python({'a': 'too long'}) is False + ``` + + Args: + cls: The class to use for the model + schema: The schema to use for the model + generic_origin: The origin type used for this model, if it's a parametrized generic. Ex, + if this model schema represents `SomeModel[int]`, generic_origin is `SomeModel` + custom_init: Whether the model has a custom init method + root_model: Whether the model is a `RootModel` + post_init: The call after init to use for the model + revalidate_instances: whether instances of models and dataclasses (including subclass instances) + should re-validate defaults to config.revalidate_instances, else 'never' + strict: Whether the model is strict + frozen: Whether the model is frozen + extra_behavior: The extra behavior to use for the model, used in serialization + config: The config to use for the model + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='model', + cls=cls, + generic_origin=generic_origin, + schema=schema, + custom_init=custom_init, + root_model=root_model, + post_init=post_init, + revalidate_instances=revalidate_instances, + strict=strict, + frozen=frozen, + extra_behavior=extra_behavior, + config=config, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DataclassField(TypedDict, total=False): + type: Required[Literal['dataclass-field']] + name: Required[str] + schema: Required[CoreSchema] + kw_only: bool # default: True + init: bool # default: True + init_only: bool # default: False + frozen: bool # default: False + validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]] + serialization_alias: str + serialization_exclude: bool # default: False + metadata: dict[str, Any] + serialization_exclude_if: Callable[[Any], bool] # default: None + + +def dataclass_field( + name: str, + schema: CoreSchema, + *, + kw_only: bool | None = None, + init: bool | None = None, + init_only: bool | None = None, + validation_alias: str | list[str | int] | list[list[str | int]] | None = None, + serialization_alias: str | None = None, + serialization_exclude: bool | None = None, + metadata: dict[str, Any] | None = None, + serialization_exclude_if: Callable[[Any], bool] | None = None, + frozen: bool | None = None, +) -> DataclassField: + """ + Returns a schema for a dataclass field, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + field = core_schema.dataclass_field( + name='a', schema=core_schema.str_schema(), kw_only=False + ) + schema = core_schema.dataclass_args_schema('Foobar', [field]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hello'}) == ({'a': 'hello'}, None) + ``` + + Args: + name: The name to use for the argument parameter + schema: The schema to use for the argument parameter + kw_only: Whether the field can be set with a positional argument as well as a keyword argument + init: Whether the field should be validated during initialization + init_only: Whether the field should be omitted from `__dict__` and passed to `__post_init__` + validation_alias: The alias(es) to use to find the field in the validation data + serialization_alias: The alias to use as a key when serializing + serialization_exclude: Whether to exclude the field when serializing + serialization_exclude_if: A callable that determines whether to exclude the field when serializing based on its value. + metadata: Any other information you want to include with the schema, not used by pydantic-core + frozen: Whether the field is frozen + """ + return _dict_not_none( + type='dataclass-field', + name=name, + schema=schema, + kw_only=kw_only, + init=init, + init_only=init_only, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + serialization_exclude=serialization_exclude, + serialization_exclude_if=serialization_exclude_if, + metadata=metadata, + frozen=frozen, + ) + + +class DataclassArgsSchema(TypedDict, total=False): + type: Required[Literal['dataclass-args']] + dataclass_name: Required[str] + fields: Required[list[DataclassField]] + computed_fields: list[ComputedField] + collect_init_only: bool # default: False + ref: str + metadata: dict[str, Any] + serialization: SerSchema + extra_behavior: ExtraBehavior + + +def dataclass_args_schema( + dataclass_name: str, + fields: list[DataclassField], + *, + computed_fields: list[ComputedField] | None = None, + collect_init_only: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, + extra_behavior: ExtraBehavior | None = None, +) -> DataclassArgsSchema: + """ + Returns a schema for validating dataclass arguments, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + field_a = core_schema.dataclass_field( + name='a', schema=core_schema.str_schema(), kw_only=False + ) + field_b = core_schema.dataclass_field( + name='b', schema=core_schema.bool_schema(), kw_only=False + ) + schema = core_schema.dataclass_args_schema('Foobar', [field_a, field_b]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hello', 'b': True}) == ({'a': 'hello', 'b': True}, None) + ``` + + Args: + dataclass_name: The name of the dataclass being validated + fields: The fields to use for the dataclass + computed_fields: Computed fields to use when serializing the dataclass + collect_init_only: Whether to collect init only fields into a dict to pass to `__post_init__` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + extra_behavior: How to handle extra fields + """ + return _dict_not_none( + type='dataclass-args', + dataclass_name=dataclass_name, + fields=fields, + computed_fields=computed_fields, + collect_init_only=collect_init_only, + ref=ref, + metadata=metadata, + serialization=serialization, + extra_behavior=extra_behavior, + ) + + +class DataclassSchema(TypedDict, total=False): + type: Required[Literal['dataclass']] + cls: Required[type[Any]] + generic_origin: type[Any] + schema: Required[CoreSchema] + fields: Required[list[str]] + cls_name: str + post_init: bool # default: False + revalidate_instances: Literal['always', 'never', 'subclass-instances'] # default: 'never' + strict: bool # default: False + frozen: bool # default False + ref: str + metadata: dict[str, Any] + serialization: SerSchema + slots: bool + config: CoreConfig + + +def dataclass_schema( + cls: type[Any], + schema: CoreSchema, + fields: list[str], + *, + generic_origin: type[Any] | None = None, + cls_name: str | None = None, + post_init: bool | None = None, + revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, + frozen: bool | None = None, + slots: bool | None = None, + config: CoreConfig | None = None, +) -> DataclassSchema: + """ + Returns a schema for a dataclass. As with `ModelSchema`, this schema can only be used as a field within + another schema, not as the root type. + + Args: + cls: The dataclass type, used to perform subclass checks + schema: The schema to use for the dataclass fields + fields: Fields of the dataclass, this is used in serialization and in validation during re-validation + and while validating assignment + generic_origin: The origin type used for this dataclass, if it's a parametrized generic. Ex, + if this model schema represents `SomeDataclass[int]`, generic_origin is `SomeDataclass` + cls_name: The name to use in error locs, etc; this is useful for generics (default: `cls.__name__`) + post_init: Whether to call `__post_init__` after validation + revalidate_instances: whether instances of models and dataclasses (including subclass instances) + should re-validate defaults to config.revalidate_instances, else 'never' + strict: Whether to require an exact instance of `cls` + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + frozen: Whether the dataclass is frozen + slots: Whether `slots=True` on the dataclass, means each field is assigned independently, rather than + simply setting `__dict__`, default false + """ + return _dict_not_none( + type='dataclass', + cls=cls, + generic_origin=generic_origin, + fields=fields, + cls_name=cls_name, + schema=schema, + post_init=post_init, + revalidate_instances=revalidate_instances, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + frozen=frozen, + slots=slots, + config=config, + ) + + +class ArgumentsParameter(TypedDict, total=False): + name: Required[str] + schema: Required[CoreSchema] + mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] # default positional_or_keyword + alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]] + + +def arguments_parameter( + name: str, + schema: CoreSchema, + *, + mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] | None = None, + alias: str | list[str | int] | list[list[str | int]] | None = None, +) -> ArgumentsParameter: + """ + Returns a schema that matches an argument parameter, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + schema = core_schema.arguments_schema([param]) + v = SchemaValidator(schema) + assert v.validate_python(('hello',)) == (('hello',), {}) + ``` + + Args: + name: The name to use for the argument parameter + schema: The schema to use for the argument parameter + mode: The mode to use for the argument parameter + alias: The alias to use for the argument parameter + """ + return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias) + + +VarKwargsMode: TypeAlias = Literal['uniform', 'unpacked-typed-dict'] + + +class ArgumentsSchema(TypedDict, total=False): + type: Required[Literal['arguments']] + arguments_schema: Required[list[ArgumentsParameter]] + validate_by_name: bool + validate_by_alias: bool + var_args_schema: CoreSchema + var_kwargs_mode: VarKwargsMode + var_kwargs_schema: CoreSchema + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def arguments_schema( + arguments: list[ArgumentsParameter], + *, + validate_by_name: bool | None = None, + validate_by_alias: bool | None = None, + var_args_schema: CoreSchema | None = None, + var_kwargs_mode: VarKwargsMode | None = None, + var_kwargs_schema: CoreSchema | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ArgumentsSchema: + """ + Returns a schema that matches an arguments schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param_a = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + param_b = core_schema.arguments_parameter( + name='b', schema=core_schema.bool_schema(), mode='positional_only' + ) + schema = core_schema.arguments_schema([param_a, param_b]) + v = SchemaValidator(schema) + assert v.validate_python(('hello', True)) == (('hello', True), {}) + ``` + + Args: + arguments: The arguments to use for the arguments schema + validate_by_name: Whether to populate by the parameter names, defaults to `False`. + validate_by_alias: Whether to populate by the parameter aliases, defaults to `True`. + var_args_schema: The variable args schema to use for the arguments schema + var_kwargs_mode: The validation mode to use for variadic keyword arguments. If `'uniform'`, every value of the + keyword arguments will be validated against the `var_kwargs_schema` schema. If `'unpacked-typed-dict'`, + the `var_kwargs_schema` argument must be a [`typed_dict_schema`][pydantic_core.core_schema.typed_dict_schema] + var_kwargs_schema: The variable kwargs schema to use for the arguments schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='arguments', + arguments_schema=arguments, + validate_by_name=validate_by_name, + validate_by_alias=validate_by_alias, + var_args_schema=var_args_schema, + var_kwargs_mode=var_kwargs_mode, + var_kwargs_schema=var_kwargs_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class ArgumentsV3Parameter(TypedDict, total=False): + name: Required[str] + schema: Required[CoreSchema] + mode: Literal[ + 'positional_only', + 'positional_or_keyword', + 'keyword_only', + 'var_args', + 'var_kwargs_uniform', + 'var_kwargs_unpacked_typed_dict', + ] # default positional_or_keyword + alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]] + + +def arguments_v3_parameter( + name: str, + schema: CoreSchema, + *, + mode: Literal[ + 'positional_only', + 'positional_or_keyword', + 'keyword_only', + 'var_args', + 'var_kwargs_uniform', + 'var_kwargs_unpacked_typed_dict', + ] + | None = None, + alias: str | list[str | int] | list[list[str | int]] | None = None, +) -> ArgumentsV3Parameter: + """ + Returns a schema that matches an argument parameter, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param = core_schema.arguments_v3_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + schema = core_schema.arguments_v3_schema([param]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hello'}) == (('hello',), {}) + ``` + + Args: + name: The name to use for the argument parameter + schema: The schema to use for the argument parameter + mode: The mode to use for the argument parameter + alias: The alias to use for the argument parameter + """ + return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias) + + +class ArgumentsV3Schema(TypedDict, total=False): + type: Required[Literal['arguments-v3']] + arguments_schema: Required[list[ArgumentsV3Parameter]] + validate_by_name: bool + validate_by_alias: bool + extra_behavior: Literal['forbid', 'ignore'] # 'allow' doesn't make sense here. + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def arguments_v3_schema( + arguments: list[ArgumentsV3Parameter], + *, + validate_by_name: bool | None = None, + validate_by_alias: bool | None = None, + extra_behavior: Literal['forbid', 'ignore'] | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> ArgumentsV3Schema: + """ + Returns a schema that matches an arguments schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param_a = core_schema.arguments_v3_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + param_b = core_schema.arguments_v3_parameter( + name='kwargs', schema=core_schema.bool_schema(), mode='var_kwargs_uniform' + ) + schema = core_schema.arguments_v3_schema([param_a, param_b]) + v = SchemaValidator(schema) + assert v.validate_python({'a': 'hi', 'kwargs': {'b': True}}) == (('hi',), {'b': True}) + ``` + + This schema is currently not used by other Pydantic components. In V3, it will most likely + become the default arguments schema for the `'call'` schema. + + Args: + arguments: The arguments to use for the arguments schema. + validate_by_name: Whether to populate by the parameter names, defaults to `False`. + validate_by_alias: Whether to populate by the parameter aliases, defaults to `True`. + extra_behavior: The extra behavior to use. + ref: optional unique identifier of the schema, used to reference the schema in other places. + metadata: Any other information you want to include with the schema, not used by pydantic-core. + serialization: Custom serialization schema. + """ + return _dict_not_none( + type='arguments-v3', + arguments_schema=arguments, + validate_by_name=validate_by_name, + validate_by_alias=validate_by_alias, + extra_behavior=extra_behavior, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class CallSchema(TypedDict, total=False): + type: Required[Literal['call']] + arguments_schema: Required[CoreSchema] + function: Required[Callable[..., Any]] + function_name: str # default function.__name__ + return_schema: CoreSchema + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def call_schema( + arguments: CoreSchema, + function: Callable[..., Any], + *, + function_name: str | None = None, + return_schema: CoreSchema | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> CallSchema: + """ + Returns a schema that matches an arguments schema, then calls a function, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + param_a = core_schema.arguments_parameter( + name='a', schema=core_schema.str_schema(), mode='positional_only' + ) + param_b = core_schema.arguments_parameter( + name='b', schema=core_schema.bool_schema(), mode='positional_only' + ) + args_schema = core_schema.arguments_schema([param_a, param_b]) + + schema = core_schema.call_schema( + arguments=args_schema, + function=lambda a, b: a + str(not b), + return_schema=core_schema.str_schema(), + ) + v = SchemaValidator(schema) + assert v.validate_python((('hello', True))) == 'helloFalse' + ``` + + Args: + arguments: The arguments to use for the arguments schema + function: The function to use for the call schema + function_name: The function name to use for the call schema, if not provided `function.__name__` is used + return_schema: The return schema to use for the call schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='call', + arguments_schema=arguments, + function=function, + function_name=function_name, + return_schema=return_schema, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class CustomErrorSchema(TypedDict, total=False): + type: Required[Literal['custom-error']] + schema: Required[CoreSchema] + custom_error_type: Required[str] + custom_error_message: str + custom_error_context: dict[str, Union[str, int, float]] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def custom_error_schema( + schema: CoreSchema, + custom_error_type: str, + *, + custom_error_message: str | None = None, + custom_error_context: dict[str, Any] | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> CustomErrorSchema: + """ + Returns a schema that matches a custom error value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.custom_error_schema( + schema=core_schema.int_schema(), + custom_error_type='MyError', + custom_error_message='Error msg', + ) + v = SchemaValidator(schema) + v.validate_python(1) + ``` + + Args: + schema: The schema to use for the custom error schema + custom_error_type: The custom error type to use for the custom error schema + custom_error_message: The custom error message to use for the custom error schema + custom_error_context: The custom error context to use for the custom error schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='custom-error', + schema=schema, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class JsonSchema(TypedDict, total=False): + type: Required[Literal['json']] + schema: CoreSchema + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def json_schema( + schema: CoreSchema | None = None, + *, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> JsonSchema: + """ + Returns a schema that matches a JSON value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + dict_schema = core_schema.model_fields_schema( + { + 'field_a': core_schema.model_field(core_schema.str_schema()), + 'field_b': core_schema.model_field(core_schema.bool_schema()), + }, + ) + + class MyModel: + __slots__ = ( + '__dict__', + '__pydantic_fields_set__', + '__pydantic_extra__', + '__pydantic_private__', + ) + field_a: str + field_b: bool + + json_schema = core_schema.json_schema(schema=dict_schema) + schema = core_schema.model_schema(cls=MyModel, schema=json_schema) + v = SchemaValidator(schema) + m = v.validate_python('{"field_a": "hello", "field_b": true}') + assert isinstance(m, MyModel) + ``` + + Args: + schema: The schema to use for the JSON schema + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none(type='json', schema=schema, ref=ref, metadata=metadata, serialization=serialization) + + +class UrlSchema(TypedDict, total=False): + type: Required[Literal['url']] + max_length: int + allowed_schemes: list[str] + host_required: bool # default False + default_host: str + default_port: int + default_path: str + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def url_schema( + *, + max_length: int | None = None, + allowed_schemes: list[str] | None = None, + host_required: bool | None = None, + default_host: str | None = None, + default_port: int | None = None, + default_path: str | None = None, + preserve_empty_path: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> UrlSchema: + """ + Returns a schema that matches a URL value, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.url_schema() + v = SchemaValidator(schema) + print(v.validate_python('https://example.com')) + #> https://example.com/ + ``` + + Args: + max_length: The maximum length of the URL + allowed_schemes: The allowed URL schemes + host_required: Whether the URL must have a host + default_host: The default host to use if the URL does not have a host + default_port: The default port to use if the URL does not have a port + default_path: The default path to use if the URL does not have a path + preserve_empty_path: Whether to preserve an empty path or convert it to '/', default False + strict: Whether to use strict URL parsing + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='url', + max_length=max_length, + allowed_schemes=allowed_schemes, + host_required=host_required, + default_host=default_host, + default_port=default_port, + default_path=default_path, + preserve_empty_path=preserve_empty_path, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class MultiHostUrlSchema(TypedDict, total=False): + type: Required[Literal['multi-host-url']] + max_length: int + allowed_schemes: list[str] + host_required: bool # default False + default_host: str + default_port: int + default_path: str + strict: bool + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def multi_host_url_schema( + *, + max_length: int | None = None, + allowed_schemes: list[str] | None = None, + host_required: bool | None = None, + default_host: str | None = None, + default_port: int | None = None, + default_path: str | None = None, + preserve_empty_path: bool | None = None, + strict: bool | None = None, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> MultiHostUrlSchema: + """ + Returns a schema that matches a URL value with possibly multiple hosts, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.multi_host_url_schema() + v = SchemaValidator(schema) + print(v.validate_python('redis://localhost,0.0.0.0,127.0.0.1')) + #> redis://localhost,0.0.0.0,127.0.0.1 + ``` + + Args: + max_length: The maximum length of the URL + allowed_schemes: The allowed URL schemes + host_required: Whether the URL must have a host + default_host: The default host to use if the URL does not have a host + default_port: The default port to use if the URL does not have a port + default_path: The default path to use if the URL does not have a path + preserve_empty_path: Whether to preserve an empty path or convert it to '/', default False + strict: Whether to use strict URL parsing + ref: optional unique identifier of the schema, used to reference the schema in other places + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='multi-host-url', + max_length=max_length, + allowed_schemes=allowed_schemes, + host_required=host_required, + default_host=default_host, + default_port=default_port, + default_path=default_path, + preserve_empty_path=preserve_empty_path, + strict=strict, + ref=ref, + metadata=metadata, + serialization=serialization, + ) + + +class DefinitionsSchema(TypedDict, total=False): + type: Required[Literal['definitions']] + schema: Required[CoreSchema] + definitions: Required[list[CoreSchema]] + metadata: dict[str, Any] + serialization: SerSchema + + +def definitions_schema(schema: CoreSchema, definitions: list[CoreSchema]) -> DefinitionsSchema: + """ + Build a schema that contains both an inner schema and a list of definitions which can be used + within the inner schema. + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema = core_schema.definitions_schema( + core_schema.list_schema(core_schema.definition_reference_schema('foobar')), + [core_schema.int_schema(ref='foobar')], + ) + v = SchemaValidator(schema) + assert v.validate_python([1, 2, '3']) == [1, 2, 3] + ``` + + Args: + schema: The inner schema + definitions: List of definitions which can be referenced within inner schema + """ + return DefinitionsSchema(type='definitions', schema=schema, definitions=definitions) + + +class DefinitionReferenceSchema(TypedDict, total=False): + type: Required[Literal['definition-ref']] + schema_ref: Required[str] + ref: str + metadata: dict[str, Any] + serialization: SerSchema + + +def definition_reference_schema( + schema_ref: str, + ref: str | None = None, + metadata: dict[str, Any] | None = None, + serialization: SerSchema | None = None, +) -> DefinitionReferenceSchema: + """ + Returns a schema that points to a schema stored in "definitions", this is useful for nested recursive + models and also when you want to define validators separately from the main schema, e.g.: + + ```py + from pydantic_core import SchemaValidator, core_schema + + schema_definition = core_schema.definition_reference_schema('list-schema') + schema = core_schema.definitions_schema( + schema=schema_definition, + definitions=[ + core_schema.list_schema(items_schema=schema_definition, ref='list-schema'), + ], + ) + v = SchemaValidator(schema) + assert v.validate_python([()]) == [[]] + ``` + + Args: + schema_ref: The schema ref to use for the definition reference schema + metadata: Any other information you want to include with the schema, not used by pydantic-core + serialization: Custom serialization schema + """ + return _dict_not_none( + type='definition-ref', schema_ref=schema_ref, ref=ref, metadata=metadata, serialization=serialization + ) + + +MYPY = False +# See https://github.com/python/mypy/issues/14034 for details, in summary mypy is extremely slow to process this +# union which kills performance not just for pydantic, but even for code using pydantic +if not MYPY: + CoreSchema = Union[ + InvalidSchema, + AnySchema, + NoneSchema, + BoolSchema, + IntSchema, + FloatSchema, + DecimalSchema, + StringSchema, + BytesSchema, + DateSchema, + TimeSchema, + DatetimeSchema, + TimedeltaSchema, + LiteralSchema, + MissingSentinelSchema, + EnumSchema, + IsInstanceSchema, + IsSubclassSchema, + CallableSchema, + ListSchema, + TupleSchema, + SetSchema, + FrozenSetSchema, + GeneratorSchema, + DictSchema, + AfterValidatorFunctionSchema, + BeforeValidatorFunctionSchema, + WrapValidatorFunctionSchema, + PlainValidatorFunctionSchema, + WithDefaultSchema, + NullableSchema, + UnionSchema, + TaggedUnionSchema, + ChainSchema, + LaxOrStrictSchema, + JsonOrPythonSchema, + TypedDictSchema, + ModelFieldsSchema, + ModelSchema, + DataclassArgsSchema, + DataclassSchema, + ArgumentsSchema, + ArgumentsV3Schema, + CallSchema, + CustomErrorSchema, + JsonSchema, + UrlSchema, + MultiHostUrlSchema, + DefinitionsSchema, + DefinitionReferenceSchema, + UuidSchema, + ComplexSchema, + ] +elif False: + CoreSchema: TypeAlias = Mapping[str, Any] + + +# to update this, call `pytest -k test_core_schema_type_literal` and copy the output +CoreSchemaType = Literal[ + 'invalid', + 'any', + 'none', + 'bool', + 'int', + 'float', + 'decimal', + 'str', + 'bytes', + 'date', + 'time', + 'datetime', + 'timedelta', + 'literal', + 'missing-sentinel', + 'enum', + 'is-instance', + 'is-subclass', + 'callable', + 'list', + 'tuple', + 'set', + 'frozenset', + 'generator', + 'dict', + 'function-after', + 'function-before', + 'function-wrap', + 'function-plain', + 'default', + 'nullable', + 'union', + 'tagged-union', + 'chain', + 'lax-or-strict', + 'json-or-python', + 'typed-dict', + 'model-fields', + 'model', + 'dataclass-args', + 'dataclass', + 'arguments', + 'arguments-v3', + 'call', + 'custom-error', + 'json', + 'url', + 'multi-host-url', + 'definitions', + 'definition-ref', + 'uuid', + 'complex', +] + +CoreSchemaFieldType = Literal['model-field', 'dataclass-field', 'typed-dict-field', 'computed-field'] + + +# used in _pydantic_core.pyi::PydanticKnownError +# to update this, call `pytest -k test_all_errors` and copy the output +ErrorType = Literal[ + 'no_such_attribute', + 'json_invalid', + 'json_type', + 'needs_python_object', + 'recursion_loop', + 'missing', + 'frozen_field', + 'frozen_instance', + 'extra_forbidden', + 'invalid_key', + 'get_attribute_error', + 'model_type', + 'model_attributes_type', + 'dataclass_type', + 'dataclass_exact_type', + 'default_factory_not_called', + 'none_required', + 'greater_than', + 'greater_than_equal', + 'less_than', + 'less_than_equal', + 'multiple_of', + 'finite_number', + 'too_short', + 'too_long', + 'iterable_type', + 'iteration_error', + 'string_type', + 'string_sub_type', + 'string_unicode', + 'string_too_short', + 'string_too_long', + 'string_pattern_mismatch', + 'string_not_ascii', + 'enum', + 'dict_type', + 'mapping_type', + 'list_type', + 'tuple_type', + 'set_type', + 'set_item_not_hashable', + 'bool_type', + 'bool_parsing', + 'int_type', + 'int_parsing', + 'int_parsing_size', + 'int_from_float', + 'float_type', + 'float_parsing', + 'bytes_type', + 'bytes_too_short', + 'bytes_too_long', + 'bytes_invalid_encoding', + 'value_error', + 'assertion_error', + 'literal_error', + 'missing_sentinel_error', + 'date_type', + 'date_parsing', + 'date_from_datetime_parsing', + 'date_from_datetime_inexact', + 'date_past', + 'date_future', + 'time_type', + 'time_parsing', + 'datetime_type', + 'datetime_parsing', + 'datetime_object_invalid', + 'datetime_from_date_parsing', + 'datetime_past', + 'datetime_future', + 'timezone_naive', + 'timezone_aware', + 'timezone_offset', + 'time_delta_type', + 'time_delta_parsing', + 'frozen_set_type', + 'is_instance_of', + 'is_subclass_of', + 'callable_type', + 'union_tag_invalid', + 'union_tag_not_found', + 'arguments_type', + 'missing_argument', + 'unexpected_keyword_argument', + 'missing_keyword_only_argument', + 'unexpected_positional_argument', + 'missing_positional_only_argument', + 'multiple_argument_values', + 'url_type', + 'url_parsing', + 'url_syntax_violation', + 'url_too_long', + 'url_scheme', + 'uuid_type', + 'uuid_parsing', + 'uuid_version', + 'decimal_type', + 'decimal_parsing', + 'decimal_max_digits', + 'decimal_max_places', + 'decimal_whole_digits', + 'complex_type', + 'complex_str_parsing', +] + + +def _dict_not_none(**kwargs: Any) -> Any: + return {k: v for k, v in kwargs.items() if v is not None} + + +def iter_union_choices(union_schema: UnionSchema) -> Generator[CoreSchema]: + """Iterate over the choices of a `'union'` schema.""" + for choice in union_schema['choices']: + if isinstance(choice, tuple): + yield choice[0] + else: + yield choice + + +############################################################################### +# All this stuff is deprecated by #980 and will be removed eventually +# They're kept because some code external code will be using them + + +@deprecated('`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.') +def field_before_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs): + warnings.warn( + '`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.', + DeprecationWarning, + ) + return with_info_before_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.') +def general_before_validator_function(*args, **kwargs): + warnings.warn( + '`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.', + DeprecationWarning, + ) + return with_info_before_validator_function(*args, **kwargs) + + +@deprecated('`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.') +def field_after_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs): + warnings.warn( + '`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.', + DeprecationWarning, + ) + return with_info_after_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.') +def general_after_validator_function(*args, **kwargs): + warnings.warn( + '`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.', + DeprecationWarning, + ) + return with_info_after_validator_function(*args, **kwargs) + + +@deprecated('`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.') +def field_wrap_validator_function( + function: WithInfoWrapValidatorFunction, field_name: str, schema: CoreSchema, **kwargs +): + warnings.warn( + '`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.', + DeprecationWarning, + ) + return with_info_wrap_validator_function(function, schema, field_name=field_name, **kwargs) + + +@deprecated('`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.') +def general_wrap_validator_function(*args, **kwargs): + warnings.warn( + '`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.', + DeprecationWarning, + ) + return with_info_wrap_validator_function(*args, **kwargs) + + +@deprecated('`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.') +def field_plain_validator_function(function: WithInfoValidatorFunction, field_name: str, **kwargs): + warnings.warn( + '`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.', + DeprecationWarning, + ) + return with_info_plain_validator_function(function, field_name=field_name, **kwargs) + + +@deprecated('`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.') +def general_plain_validator_function(*args, **kwargs): + warnings.warn( + '`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.', + DeprecationWarning, + ) + return with_info_plain_validator_function(*args, **kwargs) + + +_deprecated_import_lookup = { + 'FieldValidationInfo': ValidationInfo, + 'FieldValidatorFunction': WithInfoValidatorFunction, + 'GeneralValidatorFunction': WithInfoValidatorFunction, + 'FieldWrapValidatorFunction': WithInfoWrapValidatorFunction, +} + +if TYPE_CHECKING: + FieldValidationInfo = ValidationInfo + + +def __getattr__(attr_name: str) -> object: + new_attr = _deprecated_import_lookup.get(attr_name) + if new_attr is None: + raise AttributeError(f"module 'pydantic_core' has no attribute '{attr_name}'") + else: + import warnings + + msg = f'`{attr_name}` is deprecated, use `{new_attr.__name__}` instead.' + warnings.warn(msg, DeprecationWarning, stacklevel=1) + return new_attr diff --git a/.venv/lib/python3.12/site-packages/pydantic_core/py.typed b/.venv/lib/python3.12/site-packages/pydantic_core/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/METADATA new file mode 100644 index 0000000..3dea620 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/METADATA @@ -0,0 +1,57 @@ +Metadata-Version: 2.4 +Name: Pygments +Version: 2.20.0 +Summary: Pygments is a syntax highlighting package written in Python. +Project-URL: Homepage, https://pygments.org +Project-URL: Documentation, https://pygments.org/docs +Project-URL: Source, https://github.com/pygments/pygments +Project-URL: Bug Tracker, https://github.com/pygments/pygments/issues +Project-URL: Changelog, https://github.com/pygments/pygments/blob/master/CHANGES +Author-email: Georg Brandl +Maintainer: Matthäus G. Chajdas +Maintainer-email: Georg Brandl , Jean Abou Samra +License-Expression: BSD-2-Clause +License-File: AUTHORS +License-File: LICENSE +Keywords: syntax highlighting +Classifier: Development Status :: 6 - Mature +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: End Users/Desktop +Classifier: Intended Audience :: System Administrators +Classifier: Operating System :: OS Independent +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 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Filters +Classifier: Topic :: Utilities +Requires-Python: >=3.9 +Provides-Extra: plugins +Provides-Extra: windows-terminal +Requires-Dist: colorama>=0.4.6; extra == 'windows-terminal' +Description-Content-Type: text/x-rst + +Pygments +~~~~~~~~ + +Pygments is a syntax highlighting package written in Python. + +It is a generic syntax highlighter suitable for use in code hosting, forums, +wikis or other applications that need to prettify source code. Highlights +are: + +* a wide range of over 500 languages and other text 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 + +Copyright 2006-present by the Pygments team, see ``AUTHORS``. +Licensed under the BSD, see ``LICENSE`` for details. diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD new file mode 100644 index 0000000..9db8138 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/RECORD @@ -0,0 +1,686 @@ +../../../bin/pygmentize,sha256=qqqXuR-4dURYfPHXT-Bxin0D0OSFQi5_YCux1MtJkSw,200 +pygments-2.20.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pygments-2.20.0.dist-info/METADATA,sha256=4FKPUbMEJ_rpRyNmK6Yi-NjbKk2NPxNlaY1npSRQqEU,2476 +pygments-2.20.0.dist-info/RECORD,, +pygments-2.20.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 +pygments-2.20.0.dist-info/entry_points.txt,sha256=uUXw-XhMKBEX4pWcCtpuTTnPhL3h7OEE2jWi51VQsa8,53 +pygments-2.20.0.dist-info/licenses/AUTHORS,sha256=DbYDpfRJn2kMRCVHf_ZkwWbaMl06zDtajU3j2wckQ9A,10873 +pygments-2.20.0.dist-info/licenses/LICENSE,sha256=qdZvHVJt8C4p3Oc0NtNOVuhjL0bCdbvf_HBWnogvnxc,1331 +pygments/__init__.py,sha256=ZzpnXpvnv0c7r_OIS8h7UeaESqYNKMy__pV7KCiWXxw,2962 +pygments/__main__.py,sha256=QZWj0T6TTRsqr-w-0YvVILo1DyAvzzydGCRONwdEzqo,351 +pygments/__pycache__/__init__.cpython-312.pyc,, +pygments/__pycache__/__main__.cpython-312.pyc,, +pygments/__pycache__/cmdline.cpython-312.pyc,, +pygments/__pycache__/console.cpython-312.pyc,, +pygments/__pycache__/filter.cpython-312.pyc,, +pygments/__pycache__/formatter.cpython-312.pyc,, +pygments/__pycache__/lexer.cpython-312.pyc,, +pygments/__pycache__/modeline.cpython-312.pyc,, +pygments/__pycache__/plugin.cpython-312.pyc,, +pygments/__pycache__/regexopt.cpython-312.pyc,, +pygments/__pycache__/scanner.cpython-312.pyc,, +pygments/__pycache__/sphinxext.cpython-312.pyc,, +pygments/__pycache__/style.cpython-312.pyc,, +pygments/__pycache__/token.cpython-312.pyc,, +pygments/__pycache__/unistring.cpython-312.pyc,, +pygments/__pycache__/util.cpython-312.pyc,, +pygments/cmdline.py,sha256=_dOnrta_2GIe8Jg-1Y0pb5vWi8L6QTiJzyoTuHrYrbM,23542 +pygments/console.py,sha256=C189JAwhC1Qh0AKgshzb1wVDzFqBnv1VZ45kTEDIO30,1721 +pygments/filter.py,sha256=1dnbkq2AdC3AkHt3DaXwnOkTBLChl1kR6naSLwnr_tc,1913 +pygments/filters/__init__.py,sha256=03ZYdIYmxnWCh7gNXD7lEQ869Df4kzHnOGM44iJxan8,40349 +pygments/filters/__pycache__/__init__.cpython-312.pyc,, +pygments/formatter.py,sha256=PTBnTW0EHke2vzlAKuvHJkj3-_CMj8duIx-3yVUie-o,4369 +pygments/formatters/__init__.py,sha256=qPG4q5cuaZRGBglmEJVLP4SDv43QI3tAUskj30M-mOY,5352 +pygments/formatters/__pycache__/__init__.cpython-312.pyc,, +pygments/formatters/__pycache__/_mapping.cpython-312.pyc,, +pygments/formatters/__pycache__/bbcode.cpython-312.pyc,, +pygments/formatters/__pycache__/groff.cpython-312.pyc,, +pygments/formatters/__pycache__/html.cpython-312.pyc,, +pygments/formatters/__pycache__/img.cpython-312.pyc,, +pygments/formatters/__pycache__/irc.cpython-312.pyc,, +pygments/formatters/__pycache__/latex.cpython-312.pyc,, +pygments/formatters/__pycache__/other.cpython-312.pyc,, +pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc,, +pygments/formatters/__pycache__/rtf.cpython-312.pyc,, +pygments/formatters/__pycache__/svg.cpython-312.pyc,, +pygments/formatters/__pycache__/terminal.cpython-312.pyc,, +pygments/formatters/__pycache__/terminal256.cpython-312.pyc,, +pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176 +pygments/formatters/bbcode.py,sha256=lvG1REZJv0pM6VMe-QwLSuo0Yl1Ra_X51wh93fW8A2k,3299 +pygments/formatters/groff.py,sha256=anF3fNbDYwwOlppOUoKTZg4FzzPbZrE4jcJCMd49_1g,5085 +pygments/formatters/html.py,sha256=qe4P6qIV462HkZovS8s5xKKyYXPQQvUjz6Wj7HX1CxY,36053 +pygments/formatters/img.py,sha256=41uSY0pKg9VmKJYrHuavCVgg0z-mg81p28QEMktwf1o,23304 +pygments/formatters/irc.py,sha256=hdOqAvF02bI8CY8_tutnUptBZpWEBLzVAqS4_YA0tew,4907 +pygments/formatters/latex.py,sha256=cQmE1Nj4E9q5N0XQJBqpc6dLtu5UNOtYq3plPyCX1Hk,19261 +pygments/formatters/other.py,sha256=Hq6qY4POBZ_llAWRr1gzSO9UoeYPONlLMBK60RkW4bg,4989 +pygments/formatters/pangomarkup.py,sha256=L4jU6oO18UqEMqXeN5FkPtkisXrXuXq9wcecgy_6dzo,2209 +pygments/formatters/rtf.py,sha256=YQYW8NTrB4XfOjLbQzutyD4j35TimFNZ0T39dET9DOo,11924 +pygments/formatters/svg.py,sha256=oksXT-ZnTHDsn1WtWgBs9V7qmKFswAKx7jV37tb2tYY,7141 +pygments/formatters/terminal.py,sha256=q7jLLanle33eCZkDr4CoHAN-dHBFf1DBhi4FcBQ8_1E,4629 +pygments/formatters/terminal256.py,sha256=PpA_oATHCih3UTjrbfKqIRUcvsyp_TeBaGw3kpIxeBA,11717 +pygments/lexer.py,sha256=gNMYzmdSkTNyWfqiLJ37oUd1KrN_dMXtsPyaX2-n9EA,35154 +pygments/lexers/__init__.py,sha256=G4dtqE5QMEAqoaaD1rwSZZeuqKhMyS4utlMz1szkrTg,12070 +pygments/lexers/__pycache__/__init__.cpython-312.pyc,, +pygments/lexers/__pycache__/_ada_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_asy_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_cl_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_cocoa_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_csound_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_css_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_googlesql_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_julia_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_lasso_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_lilypond_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_lua_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_luau_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_mapping.cpython-312.pyc,, +pygments/lexers/__pycache__/_mql_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_mysql_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_openedge_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_php_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_postgres_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_qlik_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_scheme_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_scilab_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_sourcemod_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_sql_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_stan_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_stata_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_tsql_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_usd_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_vbscript_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/_vim_builtins.cpython-312.pyc,, +pygments/lexers/__pycache__/actionscript.cpython-312.pyc,, +pygments/lexers/__pycache__/ada.cpython-312.pyc,, +pygments/lexers/__pycache__/agile.cpython-312.pyc,, +pygments/lexers/__pycache__/algebra.cpython-312.pyc,, +pygments/lexers/__pycache__/ambient.cpython-312.pyc,, +pygments/lexers/__pycache__/amdgpu.cpython-312.pyc,, +pygments/lexers/__pycache__/ampl.cpython-312.pyc,, +pygments/lexers/__pycache__/apdlexer.cpython-312.pyc,, +pygments/lexers/__pycache__/apl.cpython-312.pyc,, +pygments/lexers/__pycache__/archetype.cpython-312.pyc,, +pygments/lexers/__pycache__/arrow.cpython-312.pyc,, +pygments/lexers/__pycache__/arturo.cpython-312.pyc,, +pygments/lexers/__pycache__/asc.cpython-312.pyc,, +pygments/lexers/__pycache__/asm.cpython-312.pyc,, +pygments/lexers/__pycache__/asn1.cpython-312.pyc,, +pygments/lexers/__pycache__/automation.cpython-312.pyc,, +pygments/lexers/__pycache__/bare.cpython-312.pyc,, +pygments/lexers/__pycache__/basic.cpython-312.pyc,, +pygments/lexers/__pycache__/bdd.cpython-312.pyc,, +pygments/lexers/__pycache__/berry.cpython-312.pyc,, +pygments/lexers/__pycache__/bibtex.cpython-312.pyc,, +pygments/lexers/__pycache__/blueprint.cpython-312.pyc,, +pygments/lexers/__pycache__/boa.cpython-312.pyc,, +pygments/lexers/__pycache__/bqn.cpython-312.pyc,, +pygments/lexers/__pycache__/business.cpython-312.pyc,, +pygments/lexers/__pycache__/c_cpp.cpython-312.pyc,, +pygments/lexers/__pycache__/c_like.cpython-312.pyc,, +pygments/lexers/__pycache__/capnproto.cpython-312.pyc,, +pygments/lexers/__pycache__/carbon.cpython-312.pyc,, +pygments/lexers/__pycache__/cddl.cpython-312.pyc,, +pygments/lexers/__pycache__/chapel.cpython-312.pyc,, +pygments/lexers/__pycache__/clean.cpython-312.pyc,, +pygments/lexers/__pycache__/codeql.cpython-312.pyc,, +pygments/lexers/__pycache__/comal.cpython-312.pyc,, +pygments/lexers/__pycache__/compiled.cpython-312.pyc,, +pygments/lexers/__pycache__/configs.cpython-312.pyc,, +pygments/lexers/__pycache__/console.cpython-312.pyc,, +pygments/lexers/__pycache__/cplint.cpython-312.pyc,, +pygments/lexers/__pycache__/crystal.cpython-312.pyc,, +pygments/lexers/__pycache__/csound.cpython-312.pyc,, +pygments/lexers/__pycache__/css.cpython-312.pyc,, +pygments/lexers/__pycache__/d.cpython-312.pyc,, +pygments/lexers/__pycache__/dalvik.cpython-312.pyc,, +pygments/lexers/__pycache__/data.cpython-312.pyc,, +pygments/lexers/__pycache__/dax.cpython-312.pyc,, +pygments/lexers/__pycache__/devicetree.cpython-312.pyc,, +pygments/lexers/__pycache__/diff.cpython-312.pyc,, +pygments/lexers/__pycache__/dns.cpython-312.pyc,, +pygments/lexers/__pycache__/dotnet.cpython-312.pyc,, +pygments/lexers/__pycache__/dsls.cpython-312.pyc,, +pygments/lexers/__pycache__/dylan.cpython-312.pyc,, +pygments/lexers/__pycache__/ecl.cpython-312.pyc,, +pygments/lexers/__pycache__/eiffel.cpython-312.pyc,, +pygments/lexers/__pycache__/elm.cpython-312.pyc,, +pygments/lexers/__pycache__/elpi.cpython-312.pyc,, +pygments/lexers/__pycache__/email.cpython-312.pyc,, +pygments/lexers/__pycache__/erlang.cpython-312.pyc,, +pygments/lexers/__pycache__/esoteric.cpython-312.pyc,, +pygments/lexers/__pycache__/ezhil.cpython-312.pyc,, +pygments/lexers/__pycache__/factor.cpython-312.pyc,, +pygments/lexers/__pycache__/fantom.cpython-312.pyc,, +pygments/lexers/__pycache__/felix.cpython-312.pyc,, +pygments/lexers/__pycache__/fift.cpython-312.pyc,, +pygments/lexers/__pycache__/floscript.cpython-312.pyc,, +pygments/lexers/__pycache__/forth.cpython-312.pyc,, +pygments/lexers/__pycache__/fortran.cpython-312.pyc,, +pygments/lexers/__pycache__/foxpro.cpython-312.pyc,, +pygments/lexers/__pycache__/freefem.cpython-312.pyc,, +pygments/lexers/__pycache__/func.cpython-312.pyc,, +pygments/lexers/__pycache__/functional.cpython-312.pyc,, +pygments/lexers/__pycache__/futhark.cpython-312.pyc,, +pygments/lexers/__pycache__/gcodelexer.cpython-312.pyc,, +pygments/lexers/__pycache__/gdscript.cpython-312.pyc,, +pygments/lexers/__pycache__/gleam.cpython-312.pyc,, +pygments/lexers/__pycache__/go.cpython-312.pyc,, +pygments/lexers/__pycache__/grammar_notation.cpython-312.pyc,, +pygments/lexers/__pycache__/graph.cpython-312.pyc,, +pygments/lexers/__pycache__/graphics.cpython-312.pyc,, +pygments/lexers/__pycache__/graphql.cpython-312.pyc,, +pygments/lexers/__pycache__/graphviz.cpython-312.pyc,, +pygments/lexers/__pycache__/gsql.cpython-312.pyc,, +pygments/lexers/__pycache__/hare.cpython-312.pyc,, +pygments/lexers/__pycache__/haskell.cpython-312.pyc,, +pygments/lexers/__pycache__/haxe.cpython-312.pyc,, +pygments/lexers/__pycache__/hdl.cpython-312.pyc,, +pygments/lexers/__pycache__/hexdump.cpython-312.pyc,, +pygments/lexers/__pycache__/html.cpython-312.pyc,, +pygments/lexers/__pycache__/idl.cpython-312.pyc,, +pygments/lexers/__pycache__/igor.cpython-312.pyc,, +pygments/lexers/__pycache__/inferno.cpython-312.pyc,, +pygments/lexers/__pycache__/installers.cpython-312.pyc,, +pygments/lexers/__pycache__/int_fiction.cpython-312.pyc,, +pygments/lexers/__pycache__/iolang.cpython-312.pyc,, +pygments/lexers/__pycache__/j.cpython-312.pyc,, +pygments/lexers/__pycache__/javascript.cpython-312.pyc,, +pygments/lexers/__pycache__/jmespath.cpython-312.pyc,, +pygments/lexers/__pycache__/jslt.cpython-312.pyc,, +pygments/lexers/__pycache__/json5.cpython-312.pyc,, +pygments/lexers/__pycache__/jsonnet.cpython-312.pyc,, +pygments/lexers/__pycache__/jsx.cpython-312.pyc,, +pygments/lexers/__pycache__/julia.cpython-312.pyc,, +pygments/lexers/__pycache__/jvm.cpython-312.pyc,, +pygments/lexers/__pycache__/kuin.cpython-312.pyc,, +pygments/lexers/__pycache__/kusto.cpython-312.pyc,, +pygments/lexers/__pycache__/ldap.cpython-312.pyc,, +pygments/lexers/__pycache__/lean.cpython-312.pyc,, +pygments/lexers/__pycache__/lilypond.cpython-312.pyc,, +pygments/lexers/__pycache__/lisp.cpython-312.pyc,, +pygments/lexers/__pycache__/macaulay2.cpython-312.pyc,, +pygments/lexers/__pycache__/make.cpython-312.pyc,, +pygments/lexers/__pycache__/maple.cpython-312.pyc,, +pygments/lexers/__pycache__/markup.cpython-312.pyc,, +pygments/lexers/__pycache__/math.cpython-312.pyc,, +pygments/lexers/__pycache__/matlab.cpython-312.pyc,, +pygments/lexers/__pycache__/maxima.cpython-312.pyc,, +pygments/lexers/__pycache__/meson.cpython-312.pyc,, +pygments/lexers/__pycache__/mime.cpython-312.pyc,, +pygments/lexers/__pycache__/minecraft.cpython-312.pyc,, +pygments/lexers/__pycache__/mips.cpython-312.pyc,, +pygments/lexers/__pycache__/ml.cpython-312.pyc,, +pygments/lexers/__pycache__/modeling.cpython-312.pyc,, +pygments/lexers/__pycache__/modula2.cpython-312.pyc,, +pygments/lexers/__pycache__/mojo.cpython-312.pyc,, +pygments/lexers/__pycache__/monte.cpython-312.pyc,, +pygments/lexers/__pycache__/mosel.cpython-312.pyc,, +pygments/lexers/__pycache__/ncl.cpython-312.pyc,, +pygments/lexers/__pycache__/nimrod.cpython-312.pyc,, +pygments/lexers/__pycache__/nit.cpython-312.pyc,, +pygments/lexers/__pycache__/nix.cpython-312.pyc,, +pygments/lexers/__pycache__/numbair.cpython-312.pyc,, +pygments/lexers/__pycache__/oberon.cpython-312.pyc,, +pygments/lexers/__pycache__/objective.cpython-312.pyc,, +pygments/lexers/__pycache__/ooc.cpython-312.pyc,, +pygments/lexers/__pycache__/openscad.cpython-312.pyc,, +pygments/lexers/__pycache__/other.cpython-312.pyc,, +pygments/lexers/__pycache__/parasail.cpython-312.pyc,, +pygments/lexers/__pycache__/parsers.cpython-312.pyc,, +pygments/lexers/__pycache__/pascal.cpython-312.pyc,, +pygments/lexers/__pycache__/pawn.cpython-312.pyc,, +pygments/lexers/__pycache__/pddl.cpython-312.pyc,, +pygments/lexers/__pycache__/perl.cpython-312.pyc,, +pygments/lexers/__pycache__/phix.cpython-312.pyc,, +pygments/lexers/__pycache__/php.cpython-312.pyc,, +pygments/lexers/__pycache__/pointless.cpython-312.pyc,, +pygments/lexers/__pycache__/pony.cpython-312.pyc,, +pygments/lexers/__pycache__/praat.cpython-312.pyc,, +pygments/lexers/__pycache__/procfile.cpython-312.pyc,, +pygments/lexers/__pycache__/prolog.cpython-312.pyc,, +pygments/lexers/__pycache__/promql.cpython-312.pyc,, +pygments/lexers/__pycache__/prql.cpython-312.pyc,, +pygments/lexers/__pycache__/ptx.cpython-312.pyc,, +pygments/lexers/__pycache__/python.cpython-312.pyc,, +pygments/lexers/__pycache__/q.cpython-312.pyc,, +pygments/lexers/__pycache__/qlik.cpython-312.pyc,, +pygments/lexers/__pycache__/qvt.cpython-312.pyc,, +pygments/lexers/__pycache__/r.cpython-312.pyc,, +pygments/lexers/__pycache__/rdf.cpython-312.pyc,, +pygments/lexers/__pycache__/rebol.cpython-312.pyc,, +pygments/lexers/__pycache__/rego.cpython-312.pyc,, +pygments/lexers/__pycache__/rell.cpython-312.pyc,, +pygments/lexers/__pycache__/resource.cpython-312.pyc,, +pygments/lexers/__pycache__/ride.cpython-312.pyc,, +pygments/lexers/__pycache__/rita.cpython-312.pyc,, +pygments/lexers/__pycache__/rnc.cpython-312.pyc,, +pygments/lexers/__pycache__/roboconf.cpython-312.pyc,, +pygments/lexers/__pycache__/robotframework.cpython-312.pyc,, +pygments/lexers/__pycache__/ruby.cpython-312.pyc,, +pygments/lexers/__pycache__/rust.cpython-312.pyc,, +pygments/lexers/__pycache__/sas.cpython-312.pyc,, +pygments/lexers/__pycache__/savi.cpython-312.pyc,, +pygments/lexers/__pycache__/scdoc.cpython-312.pyc,, +pygments/lexers/__pycache__/scripting.cpython-312.pyc,, +pygments/lexers/__pycache__/sgf.cpython-312.pyc,, +pygments/lexers/__pycache__/shell.cpython-312.pyc,, +pygments/lexers/__pycache__/sieve.cpython-312.pyc,, +pygments/lexers/__pycache__/slash.cpython-312.pyc,, +pygments/lexers/__pycache__/smalltalk.cpython-312.pyc,, +pygments/lexers/__pycache__/smithy.cpython-312.pyc,, +pygments/lexers/__pycache__/smv.cpython-312.pyc,, +pygments/lexers/__pycache__/snobol.cpython-312.pyc,, +pygments/lexers/__pycache__/solidity.cpython-312.pyc,, +pygments/lexers/__pycache__/soong.cpython-312.pyc,, +pygments/lexers/__pycache__/sophia.cpython-312.pyc,, +pygments/lexers/__pycache__/special.cpython-312.pyc,, +pygments/lexers/__pycache__/spice.cpython-312.pyc,, +pygments/lexers/__pycache__/sql.cpython-312.pyc,, +pygments/lexers/__pycache__/srcinfo.cpython-312.pyc,, +pygments/lexers/__pycache__/stata.cpython-312.pyc,, +pygments/lexers/__pycache__/supercollider.cpython-312.pyc,, +pygments/lexers/__pycache__/tablegen.cpython-312.pyc,, +pygments/lexers/__pycache__/tact.cpython-312.pyc,, +pygments/lexers/__pycache__/tal.cpython-312.pyc,, +pygments/lexers/__pycache__/tcl.cpython-312.pyc,, +pygments/lexers/__pycache__/teal.cpython-312.pyc,, +pygments/lexers/__pycache__/templates.cpython-312.pyc,, +pygments/lexers/__pycache__/teraterm.cpython-312.pyc,, +pygments/lexers/__pycache__/testing.cpython-312.pyc,, +pygments/lexers/__pycache__/text.cpython-312.pyc,, +pygments/lexers/__pycache__/textedit.cpython-312.pyc,, +pygments/lexers/__pycache__/textfmts.cpython-312.pyc,, +pygments/lexers/__pycache__/theorem.cpython-312.pyc,, +pygments/lexers/__pycache__/thingsdb.cpython-312.pyc,, +pygments/lexers/__pycache__/tlb.cpython-312.pyc,, +pygments/lexers/__pycache__/tls.cpython-312.pyc,, +pygments/lexers/__pycache__/tnt.cpython-312.pyc,, +pygments/lexers/__pycache__/trafficscript.cpython-312.pyc,, +pygments/lexers/__pycache__/typoscript.cpython-312.pyc,, +pygments/lexers/__pycache__/typst.cpython-312.pyc,, +pygments/lexers/__pycache__/ul4.cpython-312.pyc,, +pygments/lexers/__pycache__/unicon.cpython-312.pyc,, +pygments/lexers/__pycache__/urbi.cpython-312.pyc,, +pygments/lexers/__pycache__/usd.cpython-312.pyc,, +pygments/lexers/__pycache__/varnish.cpython-312.pyc,, +pygments/lexers/__pycache__/verification.cpython-312.pyc,, +pygments/lexers/__pycache__/verifpal.cpython-312.pyc,, +pygments/lexers/__pycache__/vip.cpython-312.pyc,, +pygments/lexers/__pycache__/vyper.cpython-312.pyc,, +pygments/lexers/__pycache__/web.cpython-312.pyc,, +pygments/lexers/__pycache__/webassembly.cpython-312.pyc,, +pygments/lexers/__pycache__/webidl.cpython-312.pyc,, +pygments/lexers/__pycache__/webmisc.cpython-312.pyc,, +pygments/lexers/__pycache__/wgsl.cpython-312.pyc,, +pygments/lexers/__pycache__/whiley.cpython-312.pyc,, +pygments/lexers/__pycache__/wowtoc.cpython-312.pyc,, +pygments/lexers/__pycache__/wren.cpython-312.pyc,, +pygments/lexers/__pycache__/x10.cpython-312.pyc,, +pygments/lexers/__pycache__/xorg.cpython-312.pyc,, +pygments/lexers/__pycache__/yang.cpython-312.pyc,, +pygments/lexers/__pycache__/yara.cpython-312.pyc,, +pygments/lexers/__pycache__/zig.cpython-312.pyc,, +pygments/lexers/_ada_builtins.py,sha256=dZb-lodsSM6L5emLlgLg-rClm2HumvnHK72CetnSRdA,1546 +pygments/lexers/_asy_builtins.py,sha256=zg54fGhgzWXQUk6-qZ6OW5GTL9d4OrnB8SJQkjrD0xs,27290 +pygments/lexers/_cl_builtins.py,sha256=oBF00ZkJyD14LkYuR603EDFIMyitkze12aT7UzDYLYs,13997 +pygments/lexers/_cocoa_builtins.py,sha256=ab6sq-iy5LapE1cNYyl8PJXLg6EINXx-lMRbwERdKYs,105176 +pygments/lexers/_csound_builtins.py,sha256=wuWiQjmEMhaVvsV8b_efsebgU0YKIoMC0ECpOavTCIM,18417 +pygments/lexers/_css_builtins.py,sha256=qhmC4tRGG53zvzMQE1qn794LvbVODRtVacxWCP3kIkA,12449 +pygments/lexers/_googlesql_builtins.py,sha256=mGfOGuKKjZoHHAAZq6Hpc68x0qKycx-SX_wGDScSOYI,16135 +pygments/lexers/_julia_builtins.py,sha256=u6v0yAzZjqUENjiIQ5qMfcso1r_6ERqGbj3aHwFnyqA,11886 +pygments/lexers/_lasso_builtins.py,sha256=YO_c_f05ZoxspHxCR3oWQCpZhbAJUzBJBhVuuwTjr5c,134513 +pygments/lexers/_lilypond_builtins.py,sha256=-_4i4gpgDgcFHvaZMJL2s1rI9dSNY2ZC6vO6ul7Hml0,115114 +pygments/lexers/_lua_builtins.py,sha256=MsDV9sEbJngKoXSZu0xXawGvd6XjFf125nuXBNoffdU,8111 +pygments/lexers/_luau_builtins.py,sha256=YLUj2bcZ0Cb0SBkIlNXWHFGDUFkpddOJx-ukrZx1cMc,958 +pygments/lexers/_mapping.py,sha256=YbQJB1eqeGk7ol5NgMMPLrD3VGdTwgQCl3TvLwtxwxA,70758 +pygments/lexers/_mql_builtins.py,sha256=CkcvMyHYh4U5rGgV6AgShxRnTOv0tmqEbOiVdpaFLqs,24716 +pygments/lexers/_mysql_builtins.py,sha256=SqiVYVVirtnN4lSgtfeRPxtVpcumjUvvUq9n8JObuQ4,26876 +pygments/lexers/_openedge_builtins.py,sha256=AzF1o6eAkMc6vnvmeVqNnUZUVijzbNYkjzrUdtCQe3M,49401 +pygments/lexers/_php_builtins.py,sha256=9g-WLT3qbSGolQJB_4PS6FW_r7HVXBWvI5OWxKtbwok,108054 +pygments/lexers/_postgres_builtins.py,sha256=QGg2mBTThgv4LdXjFm5wrKaKnCU9DubmJi6lXB7y2Mg,13346 +pygments/lexers/_qlik_builtins.py,sha256=3ZkCSDjtxSxXDI9erigWTLSUct_ap_W0LqfZjFeKQYI,12598 +pygments/lexers/_scheme_builtins.py,sha256=O3BRtt5suTESbZ_bSReQ6D0r0G_Sq_i4wCcUkdUrdS0,32567 +pygments/lexers/_scilab_builtins.py,sha256=E33R0mknNc-5eZ9Ugu7u7Av_vUxAaQoMTtdx3MceyxU,52414 +pygments/lexers/_sourcemod_builtins.py,sha256=8qspLxCLsfN9J7N0SWZRNs0A-qJOIGRBAJaNMeWaOBQ,26780 +pygments/lexers/_sql_builtins.py,sha256=fLphonB6wv6Oit8zanJiExgRoLvZoFW4l9lNH-PTYvw,6770 +pygments/lexers/_stan_builtins.py,sha256=sIoONg4TjLkGN7Ab0tZB1HM7tQ071_Z2c41n9lXZl04,16623 +pygments/lexers/_stata_builtins.py,sha256=ymh8GxEca6eSCgHnqsLqsi9OrfRqYnt5O8yBeinJ7DE,27230 +pygments/lexers/_tsql_builtins.py,sha256=Mw6UnMByju0U-OBgT8af3A4utfrS00Tn5lDnaxBtj9c,15463 +pygments/lexers/_usd_builtins.py,sha256=SGp_ePf2VuktrFV59q6df4mTyxZV7AZYs0TJFSWoBAI,1661 +pygments/lexers/_vbscript_builtins.py,sha256=TFtyc11yvpD_OlNrrFbcCOJoUYkWMq3EeHHCpw_zJNc,4228 +pygments/lexers/_vim_builtins.py,sha256=bB6kdLFM33uoY-sAtsRuUZOoccaa4gDAsdFg4WAEsoU,57069 +pygments/lexers/actionscript.py,sha256=kbHhoDl7JjVzREMv1UuxYBRJaPrxHDkmEAC5COmOIVw,11737 +pygments/lexers/ada.py,sha256=m2O7dYzJVc3iuSgaC-Ti-Gh3CG-JmpZit_DPZpu1IIM,5356 +pygments/lexers/agile.py,sha256=Du-vjnGZEPHuE40XxTMO-XLt56pYcIaKIAxIwcYHFH0,899 +pygments/lexers/algebra.py,sha256=2iQSGxfVuBXBHAJdzbbPvtpvROYTfxfMivDagk9yfh8,10032 +pygments/lexers/ambient.py,sha256=8Nk8WX4qrId9zj_zgjJBuFT40yQXG2OXxFhhF3R00g8,2608 +pygments/lexers/amdgpu.py,sha256=KMDLdb-1aK9RJaNsSkPyiU3DQE_HdjBF0paw_yiJMRA,1726 +pygments/lexers/ampl.py,sha256=IKuMVE6aXsa4S6qpuc_DvEsJmJpyKDNga09BPFLvYxA,4179 +pygments/lexers/apdlexer.py,sha256=puSpLBWevciSc8G0wg2Y_yQIoGgqPes0UBkpFImqvO4,30803 +pygments/lexers/apl.py,sha256=-jhPYsBVT4ckL7CZmukIZNi6tfaJJbBk2vutld372rs,3407 +pygments/lexers/archetype.py,sha256=buhH2WHtxBowy_KD5SPm5vgHpmLoKMdV9tgMs_bE4xU,11577 +pygments/lexers/arrow.py,sha256=e6mb3Ix4jL7TMGNUpk8iRbJy_VF7VY2J0VUknlo1EvA,3567 +pygments/lexers/arturo.py,sha256=GkklOaiEJTOz8E0zdxd_hd1zXwv7lYgp8IJl7uh13hg,11417 +pygments/lexers/asc.py,sha256=CvFSi7FYDW6eu-5oiKGc0MN3aF1XVZDoFI5-3mHpBDk,1696 +pygments/lexers/asm.py,sha256=jHJ3CDtvHgu4hX4kVvvJyciFLWM6FAYFDPG_zjBTyrc,42219 +pygments/lexers/asn1.py,sha256=MLakOeBFkdhKKr42eHFzonpGITEJxR_God8vqjWPva4,4267 +pygments/lexers/automation.py,sha256=gdSYslkW9sblDcvMeK1ay15rb9vRuK56pPOJzdxlIvg,19834 +pygments/lexers/bare.py,sha256=AdqL9Z20nd0bhNGWielLhAHej6V8_8m1OWUkCOrSBx8,3023 +pygments/lexers/basic.py,sha256=NqNxIeHhjBUtenwCdJ88WWEE3N7a16DA9g1_D1mdmF0,27992 +pygments/lexers/bdd.py,sha256=KeuOzXBLJRxxM2gV7aMuYsdV_aJFHyBuF8ptBpWxD5M,1644 +pygments/lexers/berry.py,sha256=vIfT4sBtm6ao64WdNkJTVJmS00BiFdEWvb3obBXBeX8,3212 +pygments/lexers/bibtex.py,sha256=sgfFqXyNyCxROHIe8C0gre69rzbADALnpDtTaqHfSas,4814 +pygments/lexers/blueprint.py,sha256=dW-L5bFIiWxRPDashee2EOYmXIRHdwuCZ1y3iN-a6_0,6191 +pygments/lexers/boa.py,sha256=VqYF1Yg_XdDA5tK_Eiuo1rr7ediBsKUTpB6xza_B5d4,3924 +pygments/lexers/bqn.py,sha256=se4W2XPsNiT36z0Am9X5F4yNLrQvMI5X8miY6R9SEM8,3674 +pygments/lexers/business.py,sha256=MXYomjQiYaTmnUgjQ7LUxjcU1i2iVDjZdXPTeqOlDjM,28348 +pygments/lexers/c_cpp.py,sha256=2ViQoLY22Y6xdzXDmwz30rMcij7EemNq4vpTEsfl7sg,18321 +pygments/lexers/c_like.py,sha256=rafLNQqcEbf-97VpDtSIQZSSmdiMD4oBYeEs7DZyByg,32024 +pygments/lexers/capnproto.py,sha256=kc3rT95GkeQmrW1LeP5HMVb7Nk8sqXkW6OJkkWIHYbA,2177 +pygments/lexers/carbon.py,sha256=_M-0YbofjZMrJIaiXKoT6wpkd3_6fFq-OdSZ7V961I4,3214 +pygments/lexers/cddl.py,sha256=7Wri2q2yKexWo0CD7s2c7AoCAMbrwyEE6f_gz5B6x1Y,5079 +pygments/lexers/chapel.py,sha256=SvZXNJijW0greFToDHZQ0yeo4_3niyTx31Iz_yLctck,5159 +pygments/lexers/clean.py,sha256=_oV3Tbmrpl3S1phPMS6gyMWyGZGj6Wy0q8adpf5vunY,6421 +pygments/lexers/codeql.py,sha256=hAX51uWeG5Zk9JhXSnOxPMaznFOpaX4ZnZHZ2krrPGk,2579 +pygments/lexers/comal.py,sha256=0fcTyV5h36zPlxoQPWWGet2yaYi5ESXiFpx6EbsqP00,3182 +pygments/lexers/compiled.py,sha256=PRimy7eb2weP7X6bEukz1JaSLuQAETO34K7maHLGbxA,1429 +pygments/lexers/configs.py,sha256=YDBIWV_R2SiRb0Cbxol-S8RRieDfAg_YcThc1BmZ90I,50925 +pygments/lexers/console.py,sha256=MrTIkXDYQ71z0yDepwbpO9WwXPQRPHAFAOvt_ngscxU,4183 +pygments/lexers/cplint.py,sha256=wPs_Zh0OJ2_UUeF9_mu_r-X_65DJSWMCTKOmOyjWLz8,1392 +pygments/lexers/crystal.py,sha256=k8Xy8TpfGKTPvsP1WikxILScILD8iXZ5qceLZopsLYE,15757 +pygments/lexers/csound.py,sha256=7KHoJv8wtuIe9dques4mFFkQLULcGQ23dgZ_KBd0f_k,17001 +pygments/lexers/css.py,sha256=OVjsd0Kn9pNH6iltS4ikjy9nnilPlCVx-VAZ26n3UJ8,26439 +pygments/lexers/d.py,sha256=ee5oImP1BcHYZ93UwXoHIzw6BztgR3WCCv3ksu2J-o0,9923 +pygments/lexers/dalvik.py,sha256=0f5mB5y8g0l1ID2liJ_5qM2NjyR_SBs7ckuIDeNbbj8,4609 +pygments/lexers/data.py,sha256=MhLXoZXYMZFmmKlqL8RV5zp-eUwVb0yFzsyzXYrpKiw,27049 +pygments/lexers/dax.py,sha256=N-5fxukBfFtFCT2LJoyg1VJsfldWHxXKLY3ZPLnPrig,8101 +pygments/lexers/devicetree.py,sha256=Tig-tTSrpmGzbGxN2ka4MMTiC344roav9A4hoc9o_kw,4826 +pygments/lexers/diff.py,sha256=-627lmuYpJ2uUVUM_orVGgXJGXIBGSVHbmlqflCEeaM,5385 +pygments/lexers/dns.py,sha256=ElzSN3IEoRs1g6iWMnzVIV8H0x9WSEF1wBjm2a0ByxE,3894 +pygments/lexers/dotnet.py,sha256=GmoaOxSoITkYdAUaVJ0Z1RQa6MvcRxh1gQ3XxgEs4X8,39444 +pygments/lexers/dsls.py,sha256=a6Jdv3w1aF3vpQJrnF32MNHAl6pEZkkeGB1cYv-x2Hw,36753 +pygments/lexers/dylan.py,sha256=fi8mSyni4dnGP5x1crE6BKNqLWJs4dDWdmkur-TbbNc,10412 +pygments/lexers/ecl.py,sha256=XJ66PU9EjkXJwA4f1OfC0RdfP49v8N62RZXnFVVAYNo,6374 +pygments/lexers/eiffel.py,sha256=m2eVPDG9J-VXieCeOy0S7zHPjiFHQB9F6bOIy-K3MDQ,2693 +pygments/lexers/elm.py,sha256=Xj3HlbUTEkEUFwhem4NCujx55tQ2TS61_Kcf6BCPHAI,3155 +pygments/lexers/elpi.py,sha256=RZqAVzdxfA_BqBmtMHj8ATw5f9e24SKL-QeO14QuKk4,7904 +pygments/lexers/email.py,sha256=NPlGc2L6F3KWWDop3ZOJGpUM6LDnOEqKZj1YJG3vP2M,4807 +pygments/lexers/erlang.py,sha256=2aUlVBVFtQdR2QISsDeKTOPjGrV7r1ckyXL17KJot9c,19150 +pygments/lexers/esoteric.py,sha256=tmJ9Pa1wYtYgR6yDqgp3zDYuXPFzXw7SLTHOH_eE3p0,10503 +pygments/lexers/ezhil.py,sha256=tbGYtGnqZAB8ffnyPdeMMJHtKYJ7OAciUFRrR_8QpXM,3275 +pygments/lexers/factor.py,sha256=HBUqNcwd4VplYw30ScuC8YgdipFAYiqjRgWsyT0s4xQ,19533 +pygments/lexers/fantom.py,sha256=w9IU1x61Codd7Oxv5DpvVlkVPn_bfvVLiP8Ae3660zM,10234 +pygments/lexers/felix.py,sha256=DvPOs_Em6fFtUBQCaZWWHWgnvAFeBrDe97Cy9XyQ8ps,9658 +pygments/lexers/fift.py,sha256=Q9OLJivp4ngqFso7APCvX85h-ghdbp9ZqpP8GKHws1M,1647 +pygments/lexers/floscript.py,sha256=TcyuCiv9bAb89x1_7_Zq6nUhsdsHAF2DPsKZ6m1W1NU,2670 +pygments/lexers/forth.py,sha256=4e0OCfJGcElkQrHInmQMucvsI9dljuPx4FYg38dyjno,7196 +pygments/lexers/fortran.py,sha256=ouvOHdMbj56j40Nrj21WCRf4rNKpuwphxmtlUVsnzhc,10385 +pygments/lexers/foxpro.py,sha256=nV8yyES39QoFmTNlM8xl3MteOsvmt6yV1l6x6xnXqIU,26298 +pygments/lexers/freefem.py,sha256=ZmZLIYLXtCK2kWqGzBmrkp7ychOy7vgfaorafUkUl1I,26916 +pygments/lexers/func.py,sha256=ZjOerj3njJcaGnHWc_npWjxb4D5O-udxBRK7RLHEibU,3703 +pygments/lexers/functional.py,sha256=4m9JlAnfpXxSkQQIrNOob2VTwin6hPLAqWuhv9aVkAg,697 +pygments/lexers/futhark.py,sha256=mCU2yvloJF5LN7uRE_8rCVqA8HAINny04HXVxjSiM4g,3746 +pygments/lexers/gcodelexer.py,sha256=t8JMWaaDjWZwdGiRGwrpsYtfjKTmibwlRyOLCiJZ8qw,877 +pygments/lexers/gdscript.py,sha256=VNOwd7KcDREvoBAfw66GjxxQygPsHH2akdzRbPNKqoQ,7569 +pygments/lexers/gleam.py,sha256=Jx6LnUpWV9pmjdSYLUMt16c3UamZ-v5X6CcuEhWxf-8,2395 +pygments/lexers/go.py,sha256=ynHVStw2XUJFlUiTS4UqY65fLkvcJBMNzoYtSNSl_BU,3786 +pygments/lexers/grammar_notation.py,sha256=ZGYcwhSvOolw1J7cA9Pc7hVpcIselaCgxwsvAf5yRgc,8046 +pygments/lexers/graph.py,sha256=X_IsdjxXZn5p9Dq4cLXyqwo4C8emcYA8NP6VZ9ZTBaU,4111 +pygments/lexers/graphics.py,sha256=IeruxvEQR1Wsu-YyLpAjkUnisLDzUrk2cd0xXrDCbjw,39148 +pygments/lexers/graphql.py,sha256=RWE_kfVUDEAnv_-SYyOi9IJUks61NvOakmUfWOlorzo,5604 +pygments/lexers/graphviz.py,sha256=DRNWwetEWasJDtiLkdqkKsQHDryJ5W80wUD2PE6oKCs,1937 +pygments/lexers/gsql.py,sha256=pzSj5Pd4vtNLES-pIdxaG0vOVTqUWE9UuOUw0RrY-oc,3993 +pygments/lexers/hare.py,sha256=eCrRPwcswXL-ZHTWNlhE1A05G9rxzS20_IqSddv2M9Y,2652 +pygments/lexers/haskell.py,sha256=sr5gq3U7xt0D2Bi41xdTsBaponX6otP1qPyRMCuxM7E,33323 +pygments/lexers/haxe.py,sha256=_MzQJTWx18kD9bP_sniBshKbtOZDRm4OZnifaOgw_aY,31169 +pygments/lexers/hdl.py,sha256=2jYX3fRWZ9mAZ_QER1WH58SvJaOdYQt0OmOxo3X2JK0,22741 +pygments/lexers/hexdump.py,sha256=VvYp_NTaE-6NUuSG4FMezwcjxbPnAz0fOWMzr3Y2Gm4,3656 +pygments/lexers/html.py,sha256=O6qkpyOylH2n1Zi1qzlmBsIEW31RfvzBn7jKTaAR2LQ,21999 +pygments/lexers/idl.py,sha256=W2QRH1j9LMaNvC5jb6MkP4dLYo_OQU6lQlCAlQveFH4,15452 +pygments/lexers/igor.py,sha256=Sv5EGBJeKy0UOyl6-GF3lBeeoWArVcMDJtQtl2RXRYo,31636 +pygments/lexers/inferno.py,sha256=cjVMyOg6jhJ6nYX333Z6KaCCeS0pNVHv4WTxyFGY8Tg,3138 +pygments/lexers/installers.py,sha256=ioK2JUPSJk9vcsoe3NASvMt-paXd1_Nn3e8WmaqJ7CE,14494 +pygments/lexers/int_fiction.py,sha256=BUjvXilUiuMF-euXEQDzQZs45jn_aLs17eTKcfEptrI,56547 +pygments/lexers/iolang.py,sha256=kMUBUqVvdHlB79ez0pFZPRf0NgiEC2SdtYUwdjnbE90,1908 +pygments/lexers/j.py,sha256=56qUbs1C7wKdZ6-WpNnxHntNqsxWroqiO7keI2yoR1o,4856 +pygments/lexers/javascript.py,sha256=muyCVZQAsXbhT7IHZkoqAkI5myhRaHze6JMAR4zILNs,63246 +pygments/lexers/jmespath.py,sha256=726PDpRr1g2Ky_x5pZORhbXofXxkfr5HXQ8eHMhX5xg,2085 +pygments/lexers/jslt.py,sha256=UOVw1J3uK-hVRWomoFK2jMS_yeuo5iqga-LhXUJI660,3703 +pygments/lexers/json5.py,sha256=GyeZ58AxKkJJiiH7JuPmakDZXropa6bDkFRguJHifqU,2505 +pygments/lexers/jsonnet.py,sha256=_T7Y16sW7nFuAoGFuyWUKCrUyJAC4FTWZVoeSt0mTXk,5639 +pygments/lexers/jsx.py,sha256=O0evHioyudtWLhVmAXRrIfkEd5KRiokgHu8l9_Kn8sM,2696 +pygments/lexers/julia.py,sha256=F0twMg0iLBNDdedsoJG4Rn5LoMciVu9cufyUVdxKQPY,11713 +pygments/lexers/jvm.py,sha256=cf5V7NOLIvB2s7RM9wghESYRCoCDyrGjRuPmY8bfGAw,72936 +pygments/lexers/kuin.py,sha256=n2lMina8SskpfA2KbPYQFwVJOSHgNYu10U5pz5YiajE,11408 +pygments/lexers/kusto.py,sha256=pyUfWLf9Q0Qwqu74DyPAbRh20lkPDLhxnExD1FVl8Z4,3480 +pygments/lexers/ldap.py,sha256=_ya4_InnSIRj-RYjGagWFZJDy1NdgQUocQzVWjiRp68,6554 +pygments/lexers/lean.py,sha256=qP_iwUQ7MJZ1C5s3KYBW8Wu6-uuVj4PAG3a2Ghjp99U,8588 +pygments/lexers/lilypond.py,sha256=o264ovGZFrcx8ZwpKRX-gjUECK1w5T8T0n6LoTjP92U,9755 +pygments/lexers/lisp.py,sha256=a5MGcmtrWTaeEj_5Uv8YMODr3dVV6s1v1_SMBh_Ch9Q,157903 +pygments/lexers/macaulay2.py,sha256=hLNDs1TdubudvqqovYN6a4Ne9JjnU4FvNE6EE2hPn8Q,34139 +pygments/lexers/make.py,sha256=wAV0KRRXTAFMliLfAKXDihQIE2VAVUvWjFZ-yuM7a08,7834 +pygments/lexers/maple.py,sha256=OBjODNLgqanyw522CE7n9thZctWi47Uua2hPBfj7KF4,7963 +pygments/lexers/markup.py,sha256=4TMRfujXvOE9xiWwTiT_fq1lau4dKSoXYac1hWMLYRs,65264 +pygments/lexers/math.py,sha256=Nspl6IZtCyh9egiYUahboWSmy4cJCVZUBxyVnCuF1ag,698 +pygments/lexers/matlab.py,sha256=QYVBdA-IRNcuWggBJNC9lpq3jgX_1w9GQwpv907VfZM,133030 +pygments/lexers/maxima.py,sha256=ha-f-JzGkjggAr5kxmVq7_uJTCcpHstQWnsLHYIhNwU,2718 +pygments/lexers/meson.py,sha256=ZpNVp7lSHwJHEj7pwXlGcy9Lftdc6MMgJiOoY2TnfM8,4345 +pygments/lexers/mime.py,sha256=l5BsFkad3agJ6KDg_IHI6nU0Ao0Y9EcwjYoYP8vQD-g,7585 +pygments/lexers/minecraft.py,sha256=bBbMbqvsVgTfO7CBxf03oGP_qOAgrw06PSzH1QF7txs,13701 +pygments/lexers/mips.py,sha256=QpgBoMPzsk1t90cIF0W_EV-QZ8LwgTapS-fSISxZcFc,4659 +pygments/lexers/ml.py,sha256=shDPgWARTzTEhGDeblV7ZW2j6yZEeBiYqx2z24SDP7A,35393 +pygments/lexers/modeling.py,sha256=2_ucVFy7Z4yoKx90E9-ASI9t4t1LIoeQ3yHarhghz-c,13764 +pygments/lexers/modula2.py,sha256=dXW0KNFu3ETgABsPMsIhdJI0DKF-KWtT831FKsz2VXw,53075 +pygments/lexers/mojo.py,sha256=imOlg8mQCoKwi_rsuMiifZrzsAsSqT2KckCs_MV5uVE,24236 +pygments/lexers/monte.py,sha256=JTx-jSrFKlWRwA1qzhPd647ETGSElwhP4YMlVrfXyX8,6292 +pygments/lexers/mosel.py,sha256=HEYOmZiANhe9VdkTpBt7mhPi_tPGBUkVsnzTh2HVdVI,9300 +pygments/lexers/ncl.py,sha256=RZXkxOK-iHspLX_sFVT0wc5H6v-pa4kKghP79fJGQPQ,64002 +pygments/lexers/nimrod.py,sha256=L8Ww5CUaP-ojywoQZXnjC08m5Be64fBynSf5aWGIaPw,6416 +pygments/lexers/nit.py,sha256=xwu2P49Hu8YxEL7mgpV5WWFw6OYF1iJg9DVnc7H1SXk,2728 +pygments/lexers/nix.py,sha256=M2E-k--F7pneuMAOae9ag4srJsKhWJ1f1fRueO6PtKM,4424 +pygments/lexers/numbair.py,sha256=KZOw96Tj7Gly-f9F8NA-tdGd53SIt5UgbpokZsfnzWM,1761 +pygments/lexers/oberon.py,sha256=uH1FkPeXCfdd0IQ_--S8SHUNaf2dnjiQZjtIT-jxu4A,4216 +pygments/lexers/objective.py,sha256=KME-J0UL2HJYAk2fHNhf7ApQe4XF8GH6qGOvYA1wPS4,23300 +pygments/lexers/ooc.py,sha256=HEjWHdQDVk7tRb_TuEb1_C5qi-peJwwyYBVAhf49MS0,3005 +pygments/lexers/openscad.py,sha256=te2iL8VkfXul_PYXlz8UK3_QtMjdmNgt0YHCYEvdEtM,3703 +pygments/lexers/other.py,sha256=OAlXzsrVDUx4Ma25fyG98U5LaBEHyt-LJZ2IHvMJJWY,1766 +pygments/lexers/parasail.py,sha256=oPcs7fRYNkV0isPSRmw-2cPfMgzTtNPi6bJOm7vq-9o,2722 +pygments/lexers/parsers.py,sha256=g4tVvf36yhT_aH_b737o0o1joojcf-XckD2HdNoNbsQ,26598 +pygments/lexers/pascal.py,sha256=4dewXkwc12f_iiMfdNDqbxb_oqAEX2dCzb5VIZ62V54,30992 +pygments/lexers/pawn.py,sha256=adsa-7sPuPk7mO7lB08auabEpeZdNewVqTBg6FS8mCk,8256 +pygments/lexers/pddl.py,sha256=a8A2keCF9qNQvVZQirgBV5fH4u3cjrta8-eZJ8tsxmY,2992 +pygments/lexers/perl.py,sha256=uYMj6amZPawLf-KjICg9LLU5-ADKzqEYKqi7VXnvm0k,39195 +pygments/lexers/phix.py,sha256=nEWWt5-OoIDapw4IJ_cNL19Th8Ztt6OIQgeuogVed10,23252 +pygments/lexers/php.py,sha256=U6wmxPM-pS-SY1N3qdv0ebUDBxLH6-EFIFcatUvg74w,13171 +pygments/lexers/pointless.py,sha256=gNcuhhOY9cEpIPyGFjMh2DItNB1UD8CmjceXkDRModE,1977 +pygments/lexers/pony.py,sha256=HKqf5AngUOdXN1N1VvSg0jTj3NUAzGBOPdXwSFamvmM,3282 +pygments/lexers/praat.py,sha256=sfVAd7zfRsI-TcCouuYMWlcU8EBxAwJ4wkh0ZuNsZ34,12679 +pygments/lexers/procfile.py,sha256=fhRTtscyMMPcPbvzjxVtXix1mYlpO-4JuWUL7Db4ENI,1158 +pygments/lexers/prolog.py,sha256=4YvPZbAFLWNZMbZhvtIulWib0PQLA_TB93gjkNywFRA,12869 +pygments/lexers/promql.py,sha256=04fS_R6HBWhpKNe3WPp_QXgIdFvJr5p6KxStZYw7yHU,4741 +pygments/lexers/prql.py,sha256=XJhd8dpEWPBIKmQXx_09-z1NyotLXIskpuzX36HykHo,8750 +pygments/lexers/ptx.py,sha256=dEaNSReAjAymIwVM_MnbdY4hd3muK79q200hYwN72cw,4504 +pygments/lexers/python.py,sha256=79A_yJqjVHp_ZeS1rY8Pcc4cwZ_7-zI2WWkeUmZgUrA,54202 +pygments/lexers/q.py,sha256=2CbJYgRu8uz8wOSp5FMr086KCC7IjSRmIODVY0uKrCA,6939 +pygments/lexers/qlik.py,sha256=9b6Q-6jXeeraIRcWtsKsYWCOlBhfmjNzIN49pUvMR-I,3696 +pygments/lexers/qvt.py,sha256=rpT5oD4awEKMs3uBCbNlzY1zCz7AVeqM-BikLTQHo8k,6106 +pygments/lexers/r.py,sha256=hzgUUH9gCsqqwTG9eCt1JVMd9RW0dy6IY_jTvRntF-U,6477 +pygments/lexers/rdf.py,sha256=FM154fB1lxfOpSpeW6bWOVld5kJBgEuYyT9udt-EcK0,16063 +pygments/lexers/rebol.py,sha256=CQ3pMaAz64UMqiQVrYoREnOgjU5QPeF_H3aaHNdVRC8,18262 +pygments/lexers/rego.py,sha256=Yi0G4secTWOL7CUhLLCzJ9gA6SJzDJpPJNij3hXixC0,1751 +pygments/lexers/rell.py,sha256=0gZStI953aFjFMyMVX_UKfwZmzV3uj3Tnl_5F20BPx0,2487 +pygments/lexers/resource.py,sha256=RDEY7iSv2hgQ3V-I1DeOKVOumNKgFqbV2Me_9Y21o10,2930 +pygments/lexers/ride.py,sha256=1zg7kGYPKRIekyuZY3f6OVqRlXyrX-sR-tHEX1M9nz4,5038 +pygments/lexers/rita.py,sha256=fCNElPik6dDIZzGd96kKgdlY4Qqp_zVI87waTBHMBfg,1130 +pygments/lexers/rnc.py,sha256=PNfnnTlnZjNvvG33BNMCqPzdA9LZEYvDsGoaDd_2mn8,1975 +pygments/lexers/roboconf.py,sha256=l6BeJIS-ZAUb3zc5GEuVIb0edrRpPFmKdffEBx74Zxg,2077 +pygments/lexers/robotframework.py,sha256=8s1U7GldhzRPGR7d8_rMSG0ZJz9ZOm-LGqBTGILwt2U,18451 +pygments/lexers/ruby.py,sha256=BxndG-3gLg7wEGvICiNDAMygQR9Qs0xrmq7DwfoLvMs,22756 +pygments/lexers/rust.py,sha256=7qD3KGVir-tUWf_bPBt4EX8l9aIWe8TblMCFI75oorc,8263 +pygments/lexers/sas.py,sha256=9hmGYhmKo7ri9oCmZdsoEFMz6z3AwKD4LcTUIn52lYU,9459 +pygments/lexers/savi.py,sha256=HCllgBe3rzP_7-2b1hSUEbE2dYz08Iysxc6Dcxj3f2c,4881 +pygments/lexers/scdoc.py,sha256=9n64S-bO1dI4x-2Kzh2jrDM6gEmB5YI7ch6ndPB4-cw,2527 +pygments/lexers/scripting.py,sha256=c-i_cMFhYEHQZZbLgJn0jyiD52vWeZVxUK6wFV2Fib4,82959 +pygments/lexers/sgf.py,sha256=ya_sG4TOvDWwzEM6cIi40XSbhkTuHCqvo-uHnNYfi5Q,1988 +pygments/lexers/shell.py,sha256=si6MAn6S7pHfyX65NkwQdjxtLtEu1M4l7K2lUHvyzWw,36384 +pygments/lexers/sieve.py,sha256=CXR9S1nGeVTln2u5R600hxgkAmJFVVTTTBh_k3aw-pk,2517 +pygments/lexers/slash.py,sha256=molh5sNG8UtheHffQGjjHLxVtyg2oCp4B8cvgDyf004,8487 +pygments/lexers/smalltalk.py,sha256=q52NHegl3pjn1jMkJWA9J0nOA_A5VzUULZAPgb-6uNU,7207 +pygments/lexers/smithy.py,sha256=hqEImo4B-i0hddNo1r0k5eHLOlArtRW4W5PsLBdllk4,2662 +pygments/lexers/smv.py,sha256=D51In9Qr2nWFicYAOX_bJpeFgmJ3BUkEXnvXPmA04D4,2808 +pygments/lexers/snobol.py,sha256=BX_1VPUZi-ckKCYlF2sttQprF2bLUHOi8blhInFHv9s,2781 +pygments/lexers/solidity.py,sha256=pz0DZ0xiHwufhbQ9hdCPrHMjA7NMsa6hxZ1nO-A4Y3E,3166 +pygments/lexers/soong.py,sha256=TVqBJzJxLwCEDmb5Aix2_AVuZILYyPWcAzFbzINNQAM,2342 +pygments/lexers/sophia.py,sha256=MAkWLYxhHJNcc3UUzWD3VDLzVRbf6IAd2uz4q7DY3wI,3379 +pygments/lexers/special.py,sha256=8gpTiLICFNwIcahm282mi8Nqi_6gwYMsAfjJIM2Bq6k,3588 +pygments/lexers/spice.py,sha256=UcrjK2KJDDKSEgEOeLXTayqRwirWdsywrfH9MqwPPIY,2801 +pygments/lexers/sql.py,sha256=zz_TFZtf5R29cvkBFG1PDJ-0KKrY7FjA-RDky-gfNlk,41656 +pygments/lexers/srcinfo.py,sha256=MHj02VB7WP3AcoOz1WVYAqNM_3oFLjEaAda075kXVBU,1749 +pygments/lexers/stata.py,sha256=KojmkxWHlEk-HvmxI313a52nzH_JHNqfv6r3jJoFDy8,6418 +pygments/lexers/supercollider.py,sha256=H-qwP6sUaotemsXxuugpui6KnU3jtIdDfS3bGt3Jvh4,3700 +pygments/lexers/tablegen.py,sha256=ryuzw-ArLdvlY55YJBn6ebOzDo3L8UnbR5xcZB8xMso,4012 +pygments/lexers/tact.py,sha256=YbOWYNp302ZBPM5affzM4-CudJ4xRZyJb-_vSDCozCo,10812 +pygments/lexers/tal.py,sha256=xZYmhv8mBr-A3oeoFkcSP7nDbX1r21kKOnhsLTtL3bE,2907 +pygments/lexers/tcl.py,sha256=MXMAo2wCZeq5oR7VzsNbamawun_vMOk2gOEDnGk6yOc,5515 +pygments/lexers/teal.py,sha256=pgpPi9xWPumSr-heSpGBGDcU5s3qN0aFUj2maHLE3MM,3525 +pygments/lexers/templates.py,sha256=ndJMdue33qQ_thsortAra1fm_-szMN40S5bZXBLY54w,75734 +pygments/lexers/teraterm.py,sha256=YCdvILRq-FdOJ_HfiIkYVToH3c6I2QRbQoH17Q49Hx8,10045 +pygments/lexers/testing.py,sha256=GF5SpanGwjgKKoYari32SdtdOWWXXje8xVX5-ZzTjLg,10813 +pygments/lexers/text.py,sha256=yq6mOLz3PizKNMm4_Y8UHn9vEeBfqEY5W-3M7dk1jYs,1071 +pygments/lexers/textedit.py,sha256=V-Ijh0eULTWtx3S_6vT7JcyjQO-CNirpAbEpkczLGCE,7763 +pygments/lexers/textfmts.py,sha256=WSiJDzNKCcOsBoTU4NCYU33ti5ZHGi0Px29eGjT6pB0,15527 +pygments/lexers/theorem.py,sha256=c-eg6tIYWUbxSYgrOtLZfFxro4g91JcAIwrIJGLPooU,17903 +pygments/lexers/thingsdb.py,sha256=FNKjArS1vadHEN3ZXaZdDLqvcG59GbF6Ak5M8RMsTLU,6257 +pygments/lexers/tlb.py,sha256=vWsFq_MrIrzgG2q-qdzpcoNiv9KkNow4x9ebs9_HAwA,1453 +pygments/lexers/tls.py,sha256=GZ1lvZ8PUk1-LThq1KuiqSEnMqJVqiHo_A-16aKuJOc,1543 +pygments/lexers/tnt.py,sha256=MNYTfkix0x5L1pDV8V8zGLS7GU8z7bt-wpiBap2W09U,10459 +pygments/lexers/trafficscript.py,sha256=1Tawom6bKJM-u1kYey21xFaHep3D9kl62rV3RaHSvcI,1509 +pygments/lexers/typoscript.py,sha256=dWAuOYYk0X4xE_fxKgcXz-sMYlG2wxQR4m_d7PW2PYs,8335 +pygments/lexers/typst.py,sha256=ZtV37NCQCoSqEKrAhffuZtUp1kDahKQx8PxMLrR2G6k,7170 +pygments/lexers/ul4.py,sha256=joM-US0-2BEWRHHK6eyZ-Y--306YgbMrevMY0hg8QTY,10502 +pygments/lexers/unicon.py,sha256=9D-GilKOqIEaowGHmdvHRjhqyigRk320ARAr-G_snew,18628 +pygments/lexers/urbi.py,sha256=j87k6fe60kZdbJ6AJA4g9E0no1EP0bxJY06UVP0YgnU,6085 +pygments/lexers/usd.py,sha256=tP9kHPZUJcE_flPvKEsF7wTck4FuQW75S8ELh4IYJEI,3307 +pygments/lexers/varnish.py,sha256=INCtYbol1yV_NXMh51CMDZDacEHgiB3SmR3H5uW-Fkc,7476 +pygments/lexers/verification.py,sha256=QISJmmz7cmYaXZphW5so4PfdKJo0rdZWuj_de64mafk,3937 +pygments/lexers/verifpal.py,sha256=0iZTQWawF9f7lFZnFNPTwYRiu6L_25PtEI83tuCAmKY,2664 +pygments/lexers/vip.py,sha256=pM5xFoeu-2GPWqAVzZJe6xPYr6WJPlozVI5084SHQ80,5714 +pygments/lexers/vyper.py,sha256=rw2yD9c2L7yPtXqKPd0RdxJSZK-aJbW5jEa0gHfKEO0,5618 +pygments/lexers/web.py,sha256=YMrxoHlKlQwfFJlae9_T9F1Vp03YA7IWsmgqLzh6pgI,916 +pygments/lexers/webassembly.py,sha256=Y48VdBp8b4SI10PEt8biOm-gteYgmNc_bRR-KvulfnQ,5701 +pygments/lexers/webidl.py,sha256=MQMaZFskluB29lMGpiGP2f6fzoja4vV8FtoCGFQH6fM,10519 +pygments/lexers/webmisc.py,sha256=GnSTSHfsAUGy5DYF_qwFmMsyabnzTstaEG96Fub53fk,40567 +pygments/lexers/wgsl.py,sha256=vgDtY_q42TGRvcsyS7viSadU8D3eHvatfoCNNnEP0zE,11883 +pygments/lexers/whiley.py,sha256=MC2V7o1s7LIoLy0Fk-i8OymBoN0dpK2BPjIrdQwsnVk,4020 +pygments/lexers/wowtoc.py,sha256=XnLSSX7p_RkwZIzlmYXO873d4byX9-A3dfvzNL5Eocw,4079 +pygments/lexers/wren.py,sha256=2lhzwpS27xW6nERY_NeODgNN9X6mB7vl3odXSPngqY8,3232 +pygments/lexers/x10.py,sha256=77pAyohtv9PFwPvJPGgq-Vw8tEXo9Sny1MnTchFpTTI,1946 +pygments/lexers/xorg.py,sha256=LUKU91t1Opc3W_F64UGzx0nB-HqKlN3Cn_6tFzt5IYw,928 +pygments/lexers/yang.py,sha256=yIvwteHWBWL2-8zZscnCPJOnq3rMOXh5lxJujywA_Zo,4502 +pygments/lexers/yara.py,sha256=_ISzhko7v9AhJ-1cWINZSyG3my2vJOyFSHxSjm4VdTY,2430 +pygments/lexers/zig.py,sha256=q0tuplpRFAUSS29hg_XiRH22Ctve2EFAT5xo7TNAlrk,3975 +pygments/modeline.py,sha256=me8g5rySidvPtMBOrDy2O1sMqZd02lBHMY5KDZefUws,1008 +pygments/plugin.py,sha256=P6zIw-vkSQ0k1WKHrzbBTjpwlCZPbR-6Qe_dO9Bjdu0,1928 +pygments/regexopt.py,sha256=d2hTvazlow5zzZIOCVnfeEG2CY0GrY_igH1kCSSf7ow,3308 +pygments/scanner.py,sha256=DtoLi1pOKpNu-6jiakJpSQLUi_ep29SQQhv7oAwYWME,3095 +pygments/sphinxext.py,sha256=qmiWv5b7qq6bNUT2Y2wZejV2ImQmvtzGJFg6LcGENl0,7901 +pygments/style.py,sha256=Hrie373bgWU81ZNwVjZM-GNBoxBxVe1W1Tr3MzJLdkY,6411 +pygments/styles/__init__.py,sha256=2vgGKnbyt0nf_CSDEtMhHxppPSAPr2jafzl5FiXFuGs,2009 +pygments/styles/__pycache__/__init__.cpython-312.pyc,, +pygments/styles/__pycache__/_mapping.cpython-312.pyc,, +pygments/styles/__pycache__/abap.cpython-312.pyc,, +pygments/styles/__pycache__/algol.cpython-312.pyc,, +pygments/styles/__pycache__/algol_nu.cpython-312.pyc,, +pygments/styles/__pycache__/arduino.cpython-312.pyc,, +pygments/styles/__pycache__/autumn.cpython-312.pyc,, +pygments/styles/__pycache__/borland.cpython-312.pyc,, +pygments/styles/__pycache__/bw.cpython-312.pyc,, +pygments/styles/__pycache__/coffee.cpython-312.pyc,, +pygments/styles/__pycache__/colorful.cpython-312.pyc,, +pygments/styles/__pycache__/default.cpython-312.pyc,, +pygments/styles/__pycache__/dracula.cpython-312.pyc,, +pygments/styles/__pycache__/emacs.cpython-312.pyc,, +pygments/styles/__pycache__/friendly.cpython-312.pyc,, +pygments/styles/__pycache__/friendly_grayscale.cpython-312.pyc,, +pygments/styles/__pycache__/fruity.cpython-312.pyc,, +pygments/styles/__pycache__/gh_dark.cpython-312.pyc,, +pygments/styles/__pycache__/gruvbox.cpython-312.pyc,, +pygments/styles/__pycache__/igor.cpython-312.pyc,, +pygments/styles/__pycache__/inkpot.cpython-312.pyc,, +pygments/styles/__pycache__/lightbulb.cpython-312.pyc,, +pygments/styles/__pycache__/lilypond.cpython-312.pyc,, +pygments/styles/__pycache__/lovelace.cpython-312.pyc,, +pygments/styles/__pycache__/manni.cpython-312.pyc,, +pygments/styles/__pycache__/material.cpython-312.pyc,, +pygments/styles/__pycache__/monokai.cpython-312.pyc,, +pygments/styles/__pycache__/murphy.cpython-312.pyc,, +pygments/styles/__pycache__/native.cpython-312.pyc,, +pygments/styles/__pycache__/nord.cpython-312.pyc,, +pygments/styles/__pycache__/onedark.cpython-312.pyc,, +pygments/styles/__pycache__/paraiso_dark.cpython-312.pyc,, +pygments/styles/__pycache__/paraiso_light.cpython-312.pyc,, +pygments/styles/__pycache__/pastie.cpython-312.pyc,, +pygments/styles/__pycache__/perldoc.cpython-312.pyc,, +pygments/styles/__pycache__/rainbow_dash.cpython-312.pyc,, +pygments/styles/__pycache__/rrt.cpython-312.pyc,, +pygments/styles/__pycache__/sas.cpython-312.pyc,, +pygments/styles/__pycache__/solarized.cpython-312.pyc,, +pygments/styles/__pycache__/staroffice.cpython-312.pyc,, +pygments/styles/__pycache__/stata_dark.cpython-312.pyc,, +pygments/styles/__pycache__/stata_light.cpython-312.pyc,, +pygments/styles/__pycache__/tango.cpython-312.pyc,, +pygments/styles/__pycache__/trac.cpython-312.pyc,, +pygments/styles/__pycache__/vim.cpython-312.pyc,, +pygments/styles/__pycache__/vs.cpython-312.pyc,, +pygments/styles/__pycache__/xcode.cpython-312.pyc,, +pygments/styles/__pycache__/zenburn.cpython-312.pyc,, +pygments/styles/_mapping.py,sha256=6lovFUE29tz6EsV3XYY4hgozJ7q1JL7cfO3UOlgnS8w,3312 +pygments/styles/abap.py,sha256=T7Ad121Kjz8ZbuphyhulULwXyvXubSS6Czc0MDOFiJ8,752 +pygments/styles/algol.py,sha256=6v6ZXLxPJsm_1qPqOu5ihAuWhVk644NdPXPcZqiOTTI,2265 +pygments/styles/algol_nu.py,sha256=581Lf5db303g7zDb7z-VlvGlFNpbXMIlAnrRBxtjGts,2286 +pygments/styles/arduino.py,sha256=oFF5gjfbQNBigKxFhC7giUIHWl0ieCshhsm9UxCIzqg,4560 +pygments/styles/autumn.py,sha256=P9IX5utjDvdJgevM0kNGwE-wvVysRh1lgMkjO1dhDDE,2198 +pygments/styles/borland.py,sha256=V8qTD8SrS0wLohp2udIirlijbHfPoIlWZkYpqxSdkSw,1614 +pygments/styles/bw.py,sha256=e4Fo6Kyax2aRhqKsjsqvi2CW6lkU9QnjKoPrESZAj-I,1409 +pygments/styles/coffee.py,sha256=0jxdctCEKbR1AqzA59fFQK4sVBdErrRkGCUn6fPF4sg,2311 +pygments/styles/colorful.py,sha256=opkfOcjFTtDn9Fty6ogM7okFMB6bA0iR0VtTdBPE9NE,2835 +pygments/styles/default.py,sha256=WFLDucKJS_ac5Jqutmglbz4ITc4jTTQEOnqLIMpSttA,2591 +pygments/styles/dracula.py,sha256=H-EM1WM3Ixd2OBY9bzoObKl4IzdmhirfDVFbJ9Wsi0Q,2185 +pygments/styles/emacs.py,sha256=iEYWPgQrDQk4RdDf8-g7j4mtoFRzo2xcKvZE1ys9mJU,2538 +pygments/styles/friendly.py,sha256=S43XMczW53tKftmarEL4-nTroPVdN6k8fbNrINzR_QU,2607 +pygments/styles/friendly_grayscale.py,sha256=el2E804DEkKZ6ApXt3N5FTmHAkIhqkJj-omhE1E8mdE,2831 +pygments/styles/fruity.py,sha256=mrNDixp39QIoitRBo-yMOr8tnYLAwGd0EIMw2A6Gd8g,1327 +pygments/styles/gh_dark.py,sha256=yb4EOBAYsQ9g5IGEOqyZ5LKI-MUbwz32l9WB5EcMLaA,3593 +pygments/styles/gruvbox.py,sha256=_QZtRq9y1s3NYADHqek-DXELQqueOnVDXvWJ_1aTGgo,3390 +pygments/styles/igor.py,sha256=QO_M5-Z1xIxVnusEU4LZr4VT--nTERVt-cpfbnOv4yk,740 +pygments/styles/inkpot.py,sha256=bJvNWikqnAWLjx2AH4t_tW4jR0TsrUyVFXFVPAfOgkM,2407 +pygments/styles/lightbulb.py,sha256=CnDv1mF1X5k1msVvRS_diR4tkp06E_jg0tK9cdHEFCQ,3175 +pygments/styles/lilypond.py,sha256=gSCiazPPWfR_9-BV5BYHe6-2gSor4vOHnz3nyFQI2QA,2069 +pygments/styles/lovelace.py,sha256=vZ-S9tS-QUOwfA0RHYBhSTRE3h1ZU-bZnuMW7O6ksyw,3181 +pygments/styles/manni.py,sha256=dpsJC1Zees0e9IcWirqWuSzQxwD0jIbjhFquM23dmWM,2446 +pygments/styles/material.py,sha256=eyyAaJgp1Fnjl4-_D39_l1wH5CW4W0VWScQ3Q0jtAgU,4204 +pygments/styles/monokai.py,sha256=3CwJJm_YVGibzFi8nhK_b5d4cNzgV8z-L6RzC1UVA7Q,5187 +pygments/styles/murphy.py,sha256=HsPd80nObb3Ov3-7yJQbnJr3z9mCyz30Pjn_nkWkMYo,2808 +pygments/styles/native.py,sha256=oazEJUbuaHqqOZR5vx-jIhTPIw7dC6QZnKxW-xLr7fs,2046 +pygments/styles/nord.py,sha256=t7fZj04LUgsLaHpio66FqOGMKb7a2VC3teUlmInPfRg,5394 +pygments/styles/onedark.py,sha256=UXrMzVvA9OVP7OhCQ5gL0CaZrk5jo_I84HJm7LEN5GQ,2126 +pygments/styles/paraiso_dark.py,sha256=rx5_j4gnZRy2j-3hAhFmWpnjnd-Vfz3Z7fcwEXRUGm0,5665 +pygments/styles/paraiso_light.py,sha256=iERjbYRemqgsisv_9TjanBig4bJ_-80xRz5B5puhMc0,5671 +pygments/styles/pastie.py,sha256=mnKebFiJ2do9A_wTPq4tKtgw-TQqpb0lP3kNbemQ2to,2528 +pygments/styles/perldoc.py,sha256=-e1T4QBEQNE_l17RYW-WBowNprg2uL_hFjhSDcaMiDM,2233 +pygments/styles/rainbow_dash.py,sha256=YP2HPVXJLmOy8Yz8p8u5GbkAyuKxLlNmu6ITZBMa1Mc,2393 +pygments/styles/rrt.py,sha256=vs4pnWwmMIQR0eGczp06AYazsYiR0KFcRiRUzSf3rXc,1295 +pygments/styles/sas.py,sha256=ER-JjZgU2wEJWk5K06iHAFPjVYD1iMkvjjE0rlNOt2Y,1443 +pygments/styles/solarized.py,sha256=VdtLYhQ7xDj_iTHP_Ekr6rAdMcE3fRSrglngVSal63M,4250 +pygments/styles/staroffice.py,sha256=peExkIBqPkRhqXFpW99n49t6pMwehI_pDYVUmQwkgdA,834 +pygments/styles/stata_dark.py,sha256=N6imWykqHoT5drOn-sVRUzt1AZMKG3HHU-Q7LKz2d50,1260 +pygments/styles/stata_light.py,sha256=YW7ih4teXoEuUHImOK2rGSCVxfnj43tlcKPXUsrDkRg,1292 +pygments/styles/tango.py,sha256=Af0WVTxpH_cUdHr_ua69JDijTLI5aG1RmBTF7o2KTgk,7140 +pygments/styles/trac.py,sha256=-h5iU-LjSmt-dQ7y5jXwJ5LQ9ABR5QD5RlPRjhnzmv8,1984 +pygments/styles/vim.py,sha256=dWqCVC2YT45dLSpMTwLBPkKYgRsT1-cFf7ZHoc2PQpA,2022 +pygments/styles/vs.py,sha256=7HJehhtiHE5nibjC3r3X-aYHBm5BccL33LgO_4anEiU,1133 +pygments/styles/xcode.py,sha256=HmB6aPkxvmo4za5DijhLb5AXSyv6UbWnwOUoRUNBaZA,1507 +pygments/styles/zenburn.py,sha256=Ax7iBbMzvVQNTpT3YdMwCEkL-nIXnpr60aVrR4Lpa6o,2206 +pygments/token.py,sha256=DVil5T2ltHkTgQTUYy1dMtgyigjavCs1ypE4Qf2CrGo,6229 +pygments/unistring.py,sha256=Z4w4HfOVUhueCURRkfhAqQ2b-69UzkhY4xuHevYEy5g,63211 +pygments/util.py,sha256=zk935tJSpwSA9zxNmV1TuhcEP0MfboXiRZSGQnLPEqk,10046 diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/WHEEL new file mode 100644 index 0000000..b1b94fd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.29.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/entry_points.txt new file mode 100644 index 0000000..15498e3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +pygmentize = pygments.cmdline:main diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/AUTHORS b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/AUTHORS new file mode 100644 index 0000000..909089d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/AUTHORS @@ -0,0 +1,292 @@ +Pygments is written and maintained by Georg Brandl . + +Major developers are Tim Hatch and Armin Ronacher +. + +Other contributors, listed alphabetically, are: + +* Sam Aaron -- Ioke lexer +* Jean Abou Samra -- LilyPond lexer +* João Abecasis -- JSLT lexer +* Ali Afshar -- image formatter +* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript + lexers +* Maxence Ahlouche -- PostgreSQL Explain lexer +* Muthiah Annamalai -- Ezhil lexer +* Nikolay Antipov -- OpenSCAD lexer +* Kumar Appaiah -- Debian control lexer +* Andreas Amann -- AppleScript lexer +* Timothy Armstrong -- Dart lexer fixes +* Jeffrey Arnold -- R/S, Rd, BUGS, Jags, and Stan lexers +* Eiríkr Åsheim -- Uxntal lexer +* Jeremy Ashkenas -- CoffeeScript lexer +* José Joaquín Atria -- Praat lexer +* Stefan Matthias Aust -- Smalltalk lexer +* Lucas Bajolet -- Nit lexer +* Ben Bangert -- Mako lexers +* Max Battcher -- Darcs patch lexer +* Thomas Baruchel -- APL lexer +* Tim Baumann -- (Literate) Agda lexer +* Paul Baumgart, 280 North, Inc. -- Objective-J lexer +* Michael Bayer -- Myghty lexers +* Thomas Beale -- Archetype lexers +* John Benediktsson -- Factor lexer +* David Benjamin, Google LLC -- TLS lexer +* Trevor Bergeron -- mIRC formatter +* Vincent Bernat -- LessCSS lexer +* Christopher Bertels -- Fancy lexer +* Sébastien Bigaret -- QVT Operational lexer +* Jarrett Billingsley -- MiniD lexer +* Adam Blinkinsop -- Haskell, Redcode lexers +* Stéphane Blondon -- Procfile, SGF and Sieve lexers +* Frits van Bommel -- assembler lexers +* Pierre Bourdon -- bugfixes +* Martijn Braam -- Kernel log lexer, BARE lexer +* JD Browne, Google LLC -- GoogleSQL lexer +* Matthias Bussonnier -- ANSI style handling for terminal-256 formatter +* chebee7i -- Python traceback lexer improvements +* Hiram Chirino -- Scaml and Jade lexers +* Mauricio Caceres -- SAS and Stata lexers. +* Michael Camilleri, John Gabriele, sogaiu -- Janet lexer +* Daren Chandisingh -- Gleam lexer +* Ian Cooper -- VGL lexer +* David Corbett -- Inform, Jasmin, JSGF, Snowball, and TADS 3 lexers +* Leaf Corcoran -- MoonScript lexer +* Fraser Cormack -- TableGen lexer +* Gabriel Corona -- ASN.1 lexer +* Christopher Creutzig -- MuPAD lexer +* Daniël W. Crompton -- Pike lexer +* Pete Curry -- bugfixes +* Bryan Davis -- EBNF lexer +* Bruno Deferrari -- Shen lexer +* Walter Dörwald -- UL4 lexer +* Luke Drummond -- Meson lexer +* Giedrius Dubinskas -- HTML formatter improvements +* Owen Durni -- Haxe lexer +* Alexander Dutton, Oxford University Computing Services -- SPARQL lexer +* James Edwards -- Terraform lexer +* Nick Efford -- Python 3 lexer +* Sven Efftinge -- Xtend lexer +* Artem Egorkine -- terminal256 formatter +* Matthew Fernandez -- CAmkES lexer +* Paweł Fertyk -- GDScript lexer, HTML formatter improvements +* Michael Ficarra -- CPSA lexer +* James H. Fisher -- PostScript lexer +* Amanda Fitch, Google LLC -- GoogleSQL lexer +* William S. Fulton -- SWIG lexer +* Carlos Galdino -- Elixir and Elixir Console lexers +* Michael Galloy -- IDL lexer +* Naveen Garg -- Autohotkey lexer +* Simon Garnotel -- FreeFem++ lexer +* Laurent Gautier -- R/S lexer +* Alex Gaynor -- PyPy log lexer +* Richard Gerkin -- Igor Pro lexer +* Alain Gilbert -- TypeScript lexer +* Alex Gilding -- BlitzBasic lexer +* GitHub, Inc -- DASM16, Augeas, TOML, and Slash lexers +* Bertrand Goetzmann -- Groovy lexer +* Krzysiek Goj -- Scala lexer +* Rostyslav Golda -- FloScript lexer +* Andrey Golovizin -- BibTeX lexers +* Matt Good -- Genshi, Cheetah lexers +* Michał Górny -- vim modeline support +* Alex Gosse -- TrafficScript lexer +* Patrick Gotthardt -- PHP namespaces support +* Hubert Gruniaux -- C and C++ lexer improvements +* Olivier Guibe -- Asymptote lexer +* Phil Hagelberg -- Fennel lexer +* Florian Hahn -- Boogie lexer +* Martin Harriman -- SNOBOL lexer +* Matthew Harrison -- SVG formatter +* Steven Hazel -- Tcl lexer +* Dan Michael Heggø -- Turtle lexer +* Aslak Hellesøy -- Gherkin lexer +* Greg Hendershott -- Racket lexer +* Justin Hendrick -- ParaSail lexer +* Jordi Gutiérrez Hermoso -- Octave lexer +* David Hess, Fish Software, Inc. -- Objective-J lexer +* Ken Hilton -- Typographic Number Theory and Arrow lexers +* Varun Hiremath -- Debian control lexer +* Rob Hoelz -- Perl 6 lexer +* Doug Hogan -- Mscgen lexer +* Ben Hollis -- Mason lexer +* Max Horn -- GAP lexer +* Fred Hornsey -- OMG IDL Lexer +* Alastair Houghton -- Lexer inheritance facility +* Tim Howard -- BlitzMax lexer +* Dustin Howett -- Logos lexer +* Ivan Inozemtsev -- Fantom lexer +* Hiroaki Itoh -- Shell console rewrite, Lexers for PowerShell session, + MSDOS session, BC, WDiff +* Brian R. Jackson -- Tea lexer +* Alex Jeffery, ChromaWay AB -- Rell lexer +* Christian Jann -- ShellSession lexer +* Jonas Camillus Jeppesen -- Line numbers and line highlighting for + RTF-formatter +* Dennis Kaarsemaker -- sources.list lexer +* Dmitri Kabak -- Inferno Limbo lexer +* Igor Kalnitsky -- vhdl lexer +* Colin Kennedy - USD lexer +* Alexander Kit -- MaskJS lexer +* Pekka Klärck -- Robot Framework lexer +* Gerwin Klein -- Isabelle lexer +* Eric Knibbe -- Lasso lexer +* Stepan Koltsov -- Clay lexer +* Oliver Kopp - Friendly grayscale style +* Adam Koprowski -- Opa lexer +* Benjamin Kowarsch -- Modula-2 lexer +* Domen Kožar -- Nix lexer +* Oleh Krekel -- Emacs Lisp lexer +* Alexander Kriegisch -- Kconfig and AspectJ lexers +* Marek Kubica -- Scheme lexer +* Jochen Kupperschmidt -- Markdown processor +* Gerd Kurzbach -- Modelica lexer +* Jon Larimer, Google Inc. -- Smali lexer +* Olov Lassus -- Dart lexer +* Matt Layman -- TAP lexer +* Dan Lazin, Google LLC -- GoogleSQL lexer +* Kristian Lyngstøl -- Varnish lexers +* Sylvestre Ledru -- Scilab lexer +* Chee Sing Lee -- Flatline lexer +* Mark Lee -- Vala lexer +* Thomas Linder Puls -- Visual Prolog lexer +* Pete Lomax -- Phix lexer +* Valentin Lorentz -- C++ lexer improvements +* Ben Mabey -- Gherkin lexer +* Angus MacArthur -- QML lexer +* Louis Mandel -- X10 lexer +* Louis Marchand -- Eiffel lexer +* Simone Margaritelli -- Hybris lexer +* Tim Martin - World of Warcraft TOC lexer +* Kirk McDonald -- D lexer +* Gordon McGregor -- SystemVerilog lexer +* Stephen McKamey -- Duel/JBST lexer +* Brian McKenna -- F# lexer +* Charles McLaughlin -- Puppet lexer +* Kurt McKee -- Tera Term macro lexer, PostgreSQL updates, MySQL overhaul, JSON lexer +* Joe Eli McIlvain -- Savi lexer +* Lukas Meuser -- BBCode formatter, Lua lexer +* Cat Miller -- Pig lexer +* Paul Miller -- LiveScript lexer +* Hong Minhee -- HTTP lexer +* Michael Mior -- Awk lexer +* Bruce Mitchener -- Dylan lexer rewrite +* Reuben Morais -- SourcePawn lexer +* Jon Morton -- Rust lexer +* Paulo Moura -- Logtalk lexer +* Mher Movsisyan -- DTD lexer +* Dejan Muhamedagic -- Crmsh lexer +* Adrien Nayrat -- PostgreSQL Explain lexer +* Ana Nelson -- Ragel, ANTLR, R console lexers +* David Neto, Google LLC -- WebGPU Shading Language lexer +* Kurt Neufeld -- Markdown lexer +* Nam T. Nguyen -- Monokai style +* Jesper Noehr -- HTML formatter "anchorlinenos" +* Mike Nolta -- Julia lexer +* Avery Nortonsmith -- Pointless lexer +* Jonas Obrist -- BBCode lexer +* Edward O'Callaghan -- Cryptol lexer +* David Oliva -- Rebol lexer +* Pat Pannuto -- nesC lexer +* Jon Parise -- Protocol buffers and Thrift lexers +* Benjamin Peterson -- Test suite refactoring +* Ronny Pfannschmidt -- BBCode lexer +* Dominik Picheta -- Nimrod lexer +* Andrew Pinkham -- RTF Formatter Refactoring +* Clément Prévost -- UrbiScript lexer +* Tanner Prynn -- cmdline -x option and loading lexers from files +* Oleh Prypin -- Crystal lexer (based on Ruby lexer) +* Nick Psaris -- K and Q lexers +* Xidorn Quan -- Web IDL lexer +* Elias Rabel -- Fortran fixed form lexer +* raichoo -- Idris lexer +* Daniel Ramirez -- GDScript lexer +* Kashif Rasul -- CUDA lexer +* Nathan Reed -- HLSL lexer +* Justin Reidy -- MXML lexer +* Jonathon Reinhart, Google LLC -- Soong lexer +* Norman Richards -- JSON lexer +* Corey Richardson -- Rust lexer updates +* Fabrizio Riguzzi -- cplint leder +* Lubomir Rintel -- GoodData MAQL and CL lexers +* Andre Roberge -- Tango style +* Georg Rollinger -- HSAIL lexer +* Michiel Roos -- TypoScript lexer +* Konrad Rudolph -- LaTeX formatter enhancements +* Mario Ruggier -- Evoque lexers +* Miikka Salminen -- Lovelace style, Hexdump lexer, lexer enhancements +* Stou Sandalski -- NumPy, FORTRAN, tcsh and XSLT lexers +* Matteo Sasso -- Common Lisp lexer +* Joe Schafer -- Ada lexer +* Max Schillinger -- TiddlyWiki5 lexer +* Andrew Schmidt -- X++ lexer +* Ken Schutte -- Matlab lexers +* René Schwaiger -- Rainbow Dash style +* Sebastian Schweizer -- Whiley lexer +* Tassilo Schweyer -- Io, MOOCode lexers +* Pablo Seminario -- PromQL lexer +* Ted Shaw -- AutoIt lexer +* Joerg Sieker -- ABAP lexer +* Robert Simmons -- Standard ML lexer +* Kirill Simonov -- YAML lexer +* Corbin Simpson -- Monte lexer +* Ville Skyttä -- ASCII armored lexer +* Alexander Smishlajev -- Visual FoxPro lexer +* Steve Spigarelli -- XQuery lexer +* Jerome St-Louis -- eC lexer +* Camil Staps -- Clean and NuSMV lexers; Solarized style +* James Strachan -- Kotlin lexer +* Tom Stuart -- Treetop lexer +* Colin Sullivan -- SuperCollider lexer +* Ben Swift -- Extempore lexer +* tatt61880 -- Kuin lexer +* Edoardo Tenani -- Arduino lexer +* Tiberius Teng -- default style overhaul +* Jeremy Thurgood -- Erlang, Squid config lexers +* Brian Tiffin -- OpenCOBOL lexer +* Bob Tolbert -- Hy lexer +* Doug Torrance -- Macaulay2 lexer +* Matthias Trute -- Forth lexer +* Tuoa Spi T4 -- Bdd lexer +* Erick Tryzelaar -- Felix lexer +* Alexander Udalov -- Kotlin lexer improvements +* Thomas Van Doren -- Chapel lexer +* Dave Van Ee -- Uxntal lexer updates +* Daniele Varrazzo -- PostgreSQL lexers +* Abe Voelker -- OpenEdge ABL lexer +* Pepijn de Vos -- HTML formatter CTags support +* Matthias Vallentin -- Bro lexer +* Benoît Vinot -- AMPL lexer +* Linh Vu Hong -- RSL lexer +* Taavi Väänänen -- Debian control, PHP lexers +* Immanuel Washington -- Smithy lexer +* Nathan Weizenbaum -- Haml and Sass lexers +* Nathan Whetsell -- Csound lexers +* Dietmar Winkler -- Modelica lexer +* Nils Winter -- Smalltalk lexer +* Davy Wybiral -- Clojure lexer +* Whitney Young -- ObjectiveC lexer +* Diego Zamboni -- CFengine3 lexer +* Enrique Zamudio -- Ceylon lexer +* Alex Zimin -- Nemerle lexer +* Rob Zimmerman -- Kal lexer +* Evgenii Zheltonozhskii -- Maple lexer +* Vincent Zurczak -- Roboconf lexer +* Hubert Gruniaux -- C and C++ lexer improvements +* Thomas Symalla -- AMDGPU Lexer +* 15b3 -- Image Formatter improvements +* Fabian Neumann -- CDDL lexer +* Thomas Duboucher -- CDDL lexer +* Philipp Imhof -- Pango Markup formatter +* Thomas Voss -- Sed lexer +* Martin Fischer -- WCAG contrast testing +* Marc Auberer -- Spice lexer +* Amr Hesham -- Carbon lexer +* diskdance -- Wikitext lexer +* vanillajonathan -- PRQL lexer +* Nikolay Antipov -- OpenSCAD lexer +* Markus Meyer, Nextron Systems -- YARA lexer +* Hannes Römer -- Mojo lexer +* Jan Frederik Schaefer -- PDDL lexer + +Many thanks for all contributions! diff --git a/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..446a1a8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments-2.20.0.dist-info/licenses/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2022 by the respective authors (see AUTHORS file). +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. + +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/python3.12/site-packages/pygments/__init__.py b/.venv/lib/python3.12/site-packages/pygments/__init__.py new file mode 100644 index 0000000..3a5ba53 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/__init__.py @@ -0,0 +1,82 @@ +""" + 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-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from io import StringIO, BytesIO + +__version__ = '2.20.0' +__docformat__ = 'restructuredtext' + +__all__ = ['lex', 'format', 'highlight'] + + +def lex(code, lexer): + """ + Lex `code` with the `lexer` (must be a `Lexer` instance) + and return an iterable of tokens. Currently, this only calls + `lexer.get_tokens()`. + """ + try: + return lexer.get_tokens(code) + except TypeError: + # Heuristic to catch a common mistake. + from 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 ``tokens`` (an iterable of tokens) with the formatter ``formatter`` + (a `Formatter` instance). + + 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 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): + """ + This is the most high-level highlighting function. It combines `lex` and + `format` in one function. + """ + return format(lex(code, lexer), formatter, outfile) diff --git a/.venv/lib/python3.12/site-packages/pygments/__main__.py b/.venv/lib/python3.12/site-packages/pygments/__main__.py new file mode 100644 index 0000000..818bfc3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/__main__.py @@ -0,0 +1,17 @@ +""" + pygments.__main__ + ~~~~~~~~~~~~~~~~~ + + Main entry point for ``python -m pygments``. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys +import pygments.cmdline + +try: + sys.exit(pygments.cmdline.main(sys.argv)) +except KeyboardInterrupt: + sys.exit(1) diff --git a/.venv/lib/python3.12/site-packages/pygments/cmdline.py b/.venv/lib/python3.12/site-packages/pygments/cmdline.py new file mode 100644 index 0000000..bd4a1fb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/cmdline.py @@ -0,0 +1,668 @@ +""" + pygments.cmdline + ~~~~~~~~~~~~~~~~ + + Command line interface. + + :copyright: Copyright 2006-present 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 pygments import __version__, highlight +from pygments.util import ClassNotFound, OptionError, docstring_headline, \ + guess_decode, guess_decode_from_terminal, terminal_encoding, \ + UnclosingTextIOWrapper +from 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 pygments.lexers.special import TextLexer +from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter +from pygments.formatters import get_all_formatters, get_formatter_by_name, \ + load_formatter_from_file, get_formatter_for_filename, find_formatter_class +from pygments.formatters.terminal import TerminalFormatter +from pygments.formatters.terminal256 import Terminal256Formatter, TerminalTrueColorFormatter +from pygments.filters import get_all_filters, find_filter_class +from 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(f"Help on the {cls.name} lexer:") + print(dedent(cls.__doc__)) + elif what == 'formatter': + cls = find_formatter_class(name) + print(f"Help on the {cls.name} formatter:") + print(dedent(cls.__doc__)) + elif what == 'filter': + cls = find_filter_class(name) + print(f"Help on the {name} filter:") + print(dedent(cls.__doc__)) + return 0 + except (AttributeError, ValueError): + print(f"{what} not found!", 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(('* {}\n {} {}').format(*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(('* {}\n {} {}').format(*i)) + + elif what == 'filter': + print() + print("Filters:") + print("~~~~~~~~") + + for name in get_all_filters(): + cls = find_filter_class(name) + print("* " + name + ':') + print(f" {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(f" {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(f'Pygments version {__version__}, (c) 2006-present by Georg Brandl, Matthäus ' + 'Chajdas and contributors.') + 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 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{})'.format(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/python3.12/site-packages/pygments/console.py b/.venv/lib/python3.12/site-packages/pygments/console.py new file mode 100644 index 0000000..d900916 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/console.py @@ -0,0 +1,70 @@ +""" + pygments.console + ~~~~~~~~~~~~~~~~ + + Format colored console output. + + :copyright: Copyright 2006-present 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 dark, light in zip(dark_colors, light_colors): + codes[dark] = esc + "%im" % x + codes[light] = esc + "%im" % (60 + x) + x += 1 + +del dark, light, 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/python3.12/site-packages/pygments/filter.py b/.venv/lib/python3.12/site-packages/pygments/filter.py new file mode 100644 index 0000000..6c926c2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/filter.py @@ -0,0 +1,70 @@ +""" + pygments.filter + ~~~~~~~~~~~~~~~ + + Module that implements the default filter. + + :copyright: Copyright 2006-present 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(f'{self.__class__.__name__!r} used without bound function') + 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/python3.12/site-packages/pygments/filters/__init__.py b/.venv/lib/python3.12/site-packages/pygments/filters/__init__.py new file mode 100644 index 0000000..c7ba6ad --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/filters/__init__.py @@ -0,0 +1,942 @@ +""" + pygments.filters + ~~~~~~~~~~~~~~~~ + + Module containing filter lookup functions and default + filters. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \ + string_to_tokentype +from pygments.filter import Filter +from pygments.util import get_list_opt, get_int_opt, get_bool_opt, \ + get_choice_opt, ClassNotFound, OptionError +from 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(f'filter {filtername!r} not found') + + +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({})\b'.format('|'.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 into Unicode characters. + + Examples are ``\\`` in Isabelle or + ``\\longrightarrow`` in LaTeX. + + 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', + '\\

' : '\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', + '\\

' : '\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', + '\\
' : '\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. + + This 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): + """Gobble 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): + """Merge consecutive tokens with the same token type in the output stream. + + .. 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/python3.12/site-packages/pygments/formatter.py b/.venv/lib/python3.12/site-packages/pygments/formatter.py new file mode 100644 index 0000000..4fa603d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatter.py @@ -0,0 +1,129 @@ +""" + pygments.formatter + ~~~~~~~~~~~~~~~~~~ + + Base formatter class. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs + +from pygments.util import get_bool_opt +from 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. + + Formatters should have attributes to help selecting them. These + are similar to the corresponding :class:`~pygments.lexer.Lexer` + attributes. + + .. autoattribute:: name + :no-value: + + .. autoattribute:: aliases + :no-value: + + .. autoattribute:: filenames + :no-value: + + You can pass options as keyword arguments to the constructor. + All formatters accept these basic options: + + ``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. + + """ + + #: Full name for the formatter, in human-readable form. + name = None + + #: A list of short, unique identifiers that can be used to lookup + #: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`. + aliases = [] + + #: A list of fnmatch patterns that match filenames for which this + #: formatter can produce output. The patterns in this list should be unique + #: among all formatters. + filenames = [] + + #: If True, this formatter outputs Unicode strings when no encoding + #: option is given. + unicodeoutput = True + + def __init__(self, **options): + """ + As with lexers, this constructor takes arbitrary optional arguments, + and if you override it, you should first process your own options, then + call the base class implementation. + """ + 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=''): + """ + This method must return statements or declarations suitable to define + the current style for subsequent highlighted text (e.g. CSS classes + in the `HTMLFormatter`). + + The optional argument `arg` can be used to modify the generation and + is formatter dependent (it is standardized because it can be given on + the command line). + + This method is called by the ``-S`` :doc:`command-line option `, + the `arg` is then given by the ``-a`` option. + """ + return '' + + def format(self, tokensource, outfile): + """ + This method must format the tokens from the `tokensource` iterable and + write the formatted version to the file object `outfile`. + + Formatter options can control how exactly the tokens are converted. + """ + if self.encoding: + # wrap the outfile in a StreamWriter + outfile = codecs.lookup(self.encoding)[3](outfile) + return self.format_unencoded(tokensource, outfile) + + # Allow writing Formatter[str] or Formatter[bytes]. That's equivalent to + # Formatter. This helps when using third-party type stubs from typeshed. + def __class_getitem__(cls, name): + return cls diff --git a/.venv/lib/python3.12/site-packages/pygments/formatters/__init__.py b/.venv/lib/python3.12/site-packages/pygments/formatters/__init__.py new file mode 100644 index 0000000..829496d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatters/__init__.py @@ -0,0 +1,157 @@ +""" + pygments.formatters + ~~~~~~~~~~~~~~~~~~~ + + Pygments formatters. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +import types +import fnmatch +from os.path import basename + +from pygments.formatters._mapping import FORMATTERS +from pygments.plugin import find_plugin_formatters +from 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 +_pattern_cache = {} + + +def _fn_matches(fn, glob): + """Return whether the supplied file name fn matches pattern filename.""" + if glob not in _pattern_cache: + pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) + return pattern.match(fn) + return _pattern_cache[glob].match(fn) + + +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): + """ + Return an instance of a :class:`.Formatter` subclass that has `alias` in its + aliases list. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that + alias is found. + """ + cls = find_formatter_class(_alias) + if cls is None: + raise ClassNotFound(f"no formatter found for name {_alias!r}") + return cls(**options) + + +def load_formatter_from_file(filename, formattername="CustomFormatter", **options): + """ + Return a `Formatter` subclass instance loaded from the provided file, relative + to the current directory. + + The file is expected to contain a Formatter class named ``formattername`` + (by default, CustomFormatter). Users should be very careful with the input, because + this method is equivalent to running ``eval()`` on the input file. The formatter is + given the `options` at its instantiation. + + :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading + 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(f'no valid {formattername} class found in {filename}') + formatter_class = custom_namespace[formattername] + # And finally instantiate it with the options + return formatter_class(**options) + except OSError as err: + raise ClassNotFound(f'cannot read {filename}: {err}') + except ClassNotFound: + raise + except Exception as err: + raise ClassNotFound(f'error when loading custom formatter: {err}') + + +def get_formatter_for_filename(fn, **options): + """ + Return a :class:`.Formatter` subclass instance that has a filename pattern + matching `fn`. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename + is found. + """ + fn = basename(fn) + for modname, name, _, filenames, _ in FORMATTERS.values(): + for filename in filenames: + if _fn_matches(fn, filename): + if name not in _formatter_cache: + _load_formatters(modname) + return _formatter_cache[name](**options) + for _name, cls in find_plugin_formatters(): + for filename in cls.filenames: + if _fn_matches(fn, filename): + return cls(**options) + raise ClassNotFound(f"no formatter found for file name {fn!r}") + + +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/python3.12/site-packages/pygments/formatters/_mapping.py b/.venv/lib/python3.12/site-packages/pygments/formatters/_mapping.py new file mode 100644 index 0000000..72ca840 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatters/_mapping.py @@ -0,0 +1,23 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e 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. By default, the content is enclosed in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). 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/python3.12/site-packages/pygments/formatters/bbcode.py b/.venv/lib/python3.12/site-packages/pygments/formatters/bbcode.py new file mode 100644 index 0000000..90d8d80 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatters/bbcode.py @@ -0,0 +1,108 @@ +""" + pygments.formatters.bbcode + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + BBcode formatter. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + + +from pygments.formatter import Formatter +from 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=#{}]'.format(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/python3.12/site-packages/pygments/formatters/groff.py b/.venv/lib/python3.12/site-packages/pygments/formatters/groff.py new file mode 100644 index 0000000..f5d3146 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatters/groff.py @@ -0,0 +1,170 @@ +""" + pygments.formatters.groff + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for groff output. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import math +from pygments.formatter import Formatter +from 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[{}]'.format(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[{}]'.format(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 sorted(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/python3.12/site-packages/pygments/formatters/html.py b/.venv/lib/python3.12/site-packages/pygments/formatters/html.py new file mode 100644 index 0000000..17e6741 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pygments/formatters/html.py @@ -0,0 +1,997 @@ +""" + pygments.formatters.html + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for HTML output. + + :copyright: Copyright 2006-present 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 pygments.formatter import Formatter +from pygments.token import Token, Text, STANDARD_TYPES +from pygments.util import get_bool_opt, get_int_opt, get_list_opt + +import html + +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: + # Check if the color can be shortened from 6 to 3 characters + color = color.upper() + if (len(color) == 6 and + ( color[0] == color[1] + and color[2] == color[3] + and color[4] == color[5])): + return f'#{color[0]}{color[2]}{color[4]}' + else: + return f'#{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-present 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. By default, the content is enclosed + in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). + 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). + + 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 ``$)', _handle_cssblock), + + include('keywords'), + include('inline'), + ], + 'keywords': [ + (words(( + '\\define', '\\end', 'caption', 'created', 'modified', 'tags', + 'title', 'type'), prefix=r'^', suffix=r'\b'), + Keyword), + ], + 'inline': [ + # escape + (r'\\.', Text), + # created or modified date + (r'\d{17}', Number.Integer), + # italics + (r'(\s)(//[^/]+//)((?=\W|\n))', + bygroups(Text, Generic.Emph, Text)), + # superscript + (r'(\s)(\^\^[^\^]+\^\^)', bygroups(Text, Generic.Emph)), + # subscript + (r'(\s)(,,[^,]+,,)', bygroups(Text, Generic.Emph)), + # underscore + (r'(\s)(__[^_]+__)', bygroups(Text, Generic.Strong)), + # bold + (r"(\s)(''[^']+'')((?=\W|\n))", + bygroups(Text, Generic.Strong, Text)), + # strikethrough + (r'(\s)(~~[^~]+~~)((?=\W|\n))', + bygroups(Text, Generic.Deleted, Text)), + # TiddlyWiki variables + (r'<<[^>]+>>', Name.Tag), + (r'\$\$[^$]+\$\$', Name.Tag), + (r'\$\([^)]+\)\$', Name.Tag), + # TiddlyWiki style or class + (r'^@@.*$', Name.Tag), + # HTML tags + (r']+>', Name.Tag), + # inline code + (r'`[^`]+`', String.Backtick), + # HTML escaped symbols + (r'&\S*?;', String.Regex), + # Wiki links + (r'(\[{2})([^]\|]+)(\]{2})', bygroups(Text, Name.Tag, Text)), + # External links + (r'(\[{2})([^]\|]+)(\|)([^]\|]+)(\]{2})', + bygroups(Text, Name.Tag, Text, Name.Attribute, Text)), + # Transclusion + (r'(\{{2})([^}]+)(\}{2})', bygroups(Text, Name.Tag, Text)), + # URLs + (r'(\b.?.?tps?://[^\s"]+)', bygroups(Name.Attribute)), + + # general text, must come last! + (r'[\w]+', Text), + (r'.', Text) + ], + } + + def __init__(self, **options): + self.handlecodeblocks = get_bool_opt(options, 'handlecodeblocks', True) + RegexLexer.__init__(self, **options) + + +class WikitextLexer(RegexLexer): + """ + For MediaWiki Wikitext. + + Parsing Wikitext is tricky, and results vary between different MediaWiki + installations, so we only highlight common syntaxes (built-in or from + popular extensions), and also assume templates produce no unbalanced + syntaxes. + """ + name = 'Wikitext' + url = 'https://www.mediawiki.org/wiki/Wikitext' + aliases = ['wikitext', 'mediawiki'] + filenames = [] + mimetypes = ['text/x-wiki'] + version_added = '2.15' + flags = re.MULTILINE + + def nowiki_tag_rules(tag_name): + return [ + (rf'(?i)()', bygroups(Punctuation, + Name.Tag, Whitespace, Punctuation), '#pop'), + include('entity'), + include('text'), + ] + + def plaintext_tag_rules(tag_name): + return [ + (rf'(?si)(.*?)()', bygroups(Text, + Punctuation, Name.Tag, Whitespace, Punctuation), '#pop'), + ] + + def delegate_tag_rules(tag_name, lexer, **lexer_kwargs): + return [ + (rf'(?i)()', bygroups(Punctuation, + Name.Tag, Whitespace, Punctuation), '#pop'), + (rf'(?si).+?(?=)', using(lexer, **lexer_kwargs)), + ] + + def text_rules(token): + return [ + (r'\w+', token), + (r'[^\S\n]+', token), + (r'(?s).', token), + ] + + def handle_syntaxhighlight(self, match, ctx): + from pygments.lexers import get_lexer_by_name + + attr_content = match.group() + start = 0 + index = 0 + while True: + index = attr_content.find('>', start) + # Exclude comment end (-->) + if attr_content[index-2:index] != '--': + break + start = index + 1 + + if index == -1: + # No tag end + yield from self.get_tokens_unprocessed(attr_content, stack=['root', 'attr']) + return + attr = attr_content[:index] + yield from self.get_tokens_unprocessed(attr, stack=['root', 'attr']) + yield match.start(3) + index, Punctuation, '>' + + lexer = None + content = attr_content[index+1:] + lang_match = re.findall(r'\blang=("|\'|)(\w+)(\1)', attr) + + if len(lang_match) >= 1: + # Pick the last match in case of multiple matches + lang = lang_match[-1][1] + try: + lexer = get_lexer_by_name(lang) + except ClassNotFound: + pass + + if lexer is None: + yield match.start() + index + 1, Text, content + else: + yield from lexer.get_tokens_unprocessed(content) + + def handle_score(self, match, ctx): + attr_content = match.group() + start = 0 + index = 0 + while True: + index = attr_content.find('>', start) + # Exclude comment end (-->) + if attr_content[index-2:index] != '--': + break + start = index + 1 + + if index == -1: + # No tag end + yield from self.get_tokens_unprocessed(attr_content, stack=['root', 'attr']) + return + attr = attr_content[:index] + content = attr_content[index+1:] + yield from self.get_tokens_unprocessed(attr, stack=['root', 'attr']) + yield match.start(3) + index, Punctuation, '>' + + lang_match = re.findall(r'\blang=("|\'|)(\w+)(\1)', attr) + # Pick the last match in case of multiple matches + lang = lang_match[-1][1] if len(lang_match) >= 1 else 'lilypond' + + if lang == 'lilypond': # Case sensitive + yield from LilyPondLexer().get_tokens_unprocessed(content) + else: # ABC + # FIXME: Use ABC lexer in the future + yield match.start() + index + 1, Text, content + + # a-z removed to prevent linter from complaining, REMEMBER to use (?i) + title_char = r' %!"$&\'()*,\-./0-9:;=?@A-Z\\\^_`~+\u0080-\uFFFF' + nbsp_char = r'(?:\t| |&\#0*160;|&\#[Xx]0*[Aa]0;|[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000])' + link_address = r'(?:[0-9.]+|\[[0-9a-f:.]+\]|[^\x00-\x20"<>\[\]\x7F\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFFFD])' + link_char_class = r'[^\x00-\x20"<>\[\]\x7F\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFFFD]' + double_slashes_i = { + '__FORCETOC__', '__NOCONTENTCONVERT__', '__NOCC__', '__NOEDITSECTION__', '__NOGALLERY__', + '__NOTITLECONVERT__', '__NOTC__', '__NOTOC__', '__TOC__', + } + double_slashes = { + '__EXPECTUNUSEDCATEGORY__', '__HIDDENCAT__', '__INDEX__', '__NEWSECTIONLINK__', + '__NOINDEX__', '__NONEWSECTIONLINK__', '__STATICREDIRECT__', '__NOGLOBAL__', + '__DISAMBIG__', '__EXPECTED_UNCONNECTED_PAGE__', + } + protocols = { + 'bitcoin:', 'ftp://', 'ftps://', 'geo:', 'git://', 'gopher://', 'http://', 'https://', + 'irc://', 'ircs://', 'magnet:', 'mailto:', 'mms://', 'news:', 'nntp://', 'redis://', + 'sftp://', 'sip:', 'sips:', 'sms:', 'ssh://', 'svn://', 'tel:', 'telnet://', 'urn:', + 'worldwind://', 'xmpp:', '//', + } + non_relative_protocols = protocols - {'//'} + html_tags = { + 'abbr', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center', 'cite', 'code', + 'data', 'dd', 'del', 'dfn', 'div', 'dl', 'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', + 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'link', 'mark', 'meta', 'ol', 'p', 'q', 'rb', 'rp', + 'rt', 'rtc', 'ruby', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', + 'table', 'td', 'th', 'time', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', + } + parser_tags = { + 'graph', 'charinsert', 'rss', 'chem', 'categorytree', 'nowiki', 'inputbox', 'math', + 'hiero', 'score', 'pre', 'ref', 'translate', 'imagemap', 'templatestyles', 'languages', + 'noinclude', 'mapframe', 'section', 'poem', 'syntaxhighlight', 'includeonly', 'tvar', + 'onlyinclude', 'templatedata', 'langconvert', 'timeline', 'dynamicpagelist', 'gallery', + 'maplink', 'ce', 'references', + } + variant_langs = { + # ZhConverter.php + 'zh', 'zh-hans', 'zh-hant', 'zh-cn', 'zh-hk', 'zh-mo', 'zh-my', 'zh-sg', 'zh-tw', + # WuuConverter.php + 'wuu', 'wuu-hans', 'wuu-hant', + # UzConverter.php + 'uz', 'uz-latn', 'uz-cyrl', + # TlyConverter.php + 'tly', 'tly-cyrl', + # TgConverter.php + 'tg', 'tg-latn', + # SrConverter.php + 'sr', 'sr-ec', 'sr-el', + # ShiConverter.php + 'shi', 'shi-tfng', 'shi-latn', + # ShConverter.php + 'sh-latn', 'sh-cyrl', + # KuConverter.php + 'ku', 'ku-arab', 'ku-latn', + # IuConverter.php + 'iu', 'ike-cans', 'ike-latn', + # GanConverter.php + 'gan', 'gan-hans', 'gan-hant', + # EnConverter.php + 'en', 'en-x-piglatin', + # CrhConverter.php + 'crh', 'crh-cyrl', 'crh-latn', + # BanConverter.php + 'ban', 'ban-bali', 'ban-x-dharma', 'ban-x-palmleaf', 'ban-x-pku', + } + magic_vars_i = { + 'ARTICLEPATH', 'INT', 'PAGEID', 'SCRIPTPATH', 'SERVER', 'SERVERNAME', 'STYLEPATH', + } + magic_vars = { + '!', '=', 'BASEPAGENAME', 'BASEPAGENAMEE', 'CASCADINGSOURCES', 'CONTENTLANGUAGE', + 'CONTENTLANG', 'CURRENTDAY', 'CURRENTDAY2', 'CURRENTDAYNAME', 'CURRENTDOW', 'CURRENTHOUR', + 'CURRENTMONTH', 'CURRENTMONTH2', 'CURRENTMONTH1', 'CURRENTMONTHABBREV', 'CURRENTMONTHNAME', + 'CURRENTMONTHNAMEGEN', 'CURRENTTIME', 'CURRENTTIMESTAMP', 'CURRENTVERSION', 'CURRENTWEEK', + 'CURRENTYEAR', 'DIRECTIONMARK', 'DIRMARK', 'FULLPAGENAME', 'FULLPAGENAMEE', 'LOCALDAY', + 'LOCALDAY2', 'LOCALDAYNAME', 'LOCALDOW', 'LOCALHOUR', 'LOCALMONTH', 'LOCALMONTH2', + 'LOCALMONTH1', 'LOCALMONTHABBREV', 'LOCALMONTHNAME', 'LOCALMONTHNAMEGEN', 'LOCALTIME', + 'LOCALTIMESTAMP', 'LOCALWEEK', 'LOCALYEAR', 'NAMESPACE', 'NAMESPACEE', 'NAMESPACENUMBER', + 'NUMBEROFACTIVEUSERS', 'NUMBEROFADMINS', 'NUMBEROFARTICLES', 'NUMBEROFEDITS', + 'NUMBEROFFILES', 'NUMBEROFPAGES', 'NUMBEROFUSERS', 'PAGELANGUAGE', 'PAGENAME', 'PAGENAMEE', + 'REVISIONDAY', 'REVISIONDAY2', 'REVISIONID', 'REVISIONMONTH', 'REVISIONMONTH1', + 'REVISIONSIZE', 'REVISIONTIMESTAMP', 'REVISIONUSER', 'REVISIONYEAR', 'ROOTPAGENAME', + 'ROOTPAGENAMEE', 'SITENAME', 'SUBJECTPAGENAME', 'ARTICLEPAGENAME', 'SUBJECTPAGENAMEE', + 'ARTICLEPAGENAMEE', 'SUBJECTSPACE', 'ARTICLESPACE', 'SUBJECTSPACEE', 'ARTICLESPACEE', + 'SUBPAGENAME', 'SUBPAGENAMEE', 'TALKPAGENAME', 'TALKPAGENAMEE', 'TALKSPACE', 'TALKSPACEE', + } + parser_functions_i = { + 'ANCHORENCODE', 'BIDI', 'CANONICALURL', 'CANONICALURLE', 'FILEPATH', 'FORMATNUM', + 'FULLURL', 'FULLURLE', 'GENDER', 'GRAMMAR', 'INT', r'\#LANGUAGE', 'LC', 'LCFIRST', 'LOCALURL', + 'LOCALURLE', 'NS', 'NSE', 'PADLEFT', 'PADRIGHT', 'PAGEID', 'PLURAL', 'UC', 'UCFIRST', + 'URLENCODE', + } + parser_functions = { + 'BASEPAGENAME', 'BASEPAGENAMEE', 'CASCADINGSOURCES', 'DEFAULTSORT', 'DEFAULTSORTKEY', + 'DEFAULTCATEGORYSORT', 'FULLPAGENAME', 'FULLPAGENAMEE', 'NAMESPACE', 'NAMESPACEE', + 'NAMESPACENUMBER', 'NUMBERINGROUP', 'NUMINGROUP', 'NUMBEROFACTIVEUSERS', 'NUMBEROFADMINS', + 'NUMBEROFARTICLES', 'NUMBEROFEDITS', 'NUMBEROFFILES', 'NUMBEROFPAGES', 'NUMBEROFUSERS', + 'PAGENAME', 'PAGENAMEE', 'PAGESINCATEGORY', 'PAGESINCAT', 'PAGESIZE', 'PROTECTIONEXPIRY', + 'PROTECTIONLEVEL', 'REVISIONDAY', 'REVISIONDAY2', 'REVISIONID', 'REVISIONMONTH', + 'REVISIONMONTH1', 'REVISIONTIMESTAMP', 'REVISIONUSER', 'REVISIONYEAR', 'ROOTPAGENAME', + 'ROOTPAGENAMEE', 'SUBJECTPAGENAME', 'ARTICLEPAGENAME', 'SUBJECTPAGENAMEE', + 'ARTICLEPAGENAMEE', 'SUBJECTSPACE', 'ARTICLESPACE', 'SUBJECTSPACEE', 'ARTICLESPACEE', + 'SUBPAGENAME', 'SUBPAGENAMEE', 'TALKPAGENAME', 'TALKPAGENAMEE', 'TALKSPACE', 'TALKSPACEE', + 'INT', 'DISPLAYTITLE', 'PAGESINNAMESPACE', 'PAGESINNS', + } + + tokens = { + 'root': [ + # Redirects + (r"""(?xi) + (\A\s*?)(\#REDIRECT:?) # may contain a colon + (\s+)(\[\[) (?=[^\]\n]* \]\]$) + """, + bygroups(Whitespace, Keyword, Whitespace, Punctuation), 'redirect-inner'), + # Subheadings + (r'^(={2,6})(.+?)(\1)(\s*$\n)', + bygroups(Generic.Subheading, Generic.Subheading, Generic.Subheading, Whitespace)), + # Headings + (r'^(=.+?=)(\s*$\n)', + bygroups(Generic.Heading, Whitespace)), + # Double-slashed magic words + (words(double_slashes_i, prefix=r'(?i)'), Name.Function.Magic), + (words(double_slashes), Name.Function.Magic), + # Raw URLs + (r'(?i)\b(?:{}){}{}*'.format('|'.join(protocols), + link_address, link_char_class), Name.Label), + # Magic links + (rf'\b(?:RFC|PMID){nbsp_char}+[0-9]+\b', + Name.Function.Magic), + (r"""(?x) + \bISBN {nbsp_char} + (?: 97[89] {nbsp_dash}? )? + (?: [0-9] {nbsp_dash}? ){{9}} # escape format() + [0-9Xx]\b + """.format(nbsp_char=nbsp_char, nbsp_dash=f'(?:-|{nbsp_char})'), Name.Function.Magic), + include('list'), + include('inline'), + include('text'), + ], + 'redirect-inner': [ + (r'(\]\])(\s*?\n)', bygroups(Punctuation, Whitespace), '#pop'), + (r'(\#)([^#]*?)', bygroups(Punctuation, Name.Label)), + (rf'(?i)[{title_char}]+', Name.Tag), + ], + 'list': [ + # Description lists + (r'^;', Keyword, 'dt'), + # Ordered lists, unordered lists and indents + (r'^[#:*]+', Keyword), + # Horizontal rules + (r'^-{4,}', Keyword), + ], + 'inline': [ + # Signatures + (r'~{3,5}', Keyword), + # Entities + include('entity'), + # Bold & italic + (r"('')(''')(?!')", bygroups(Generic.Emph, + Generic.EmphStrong), 'inline-italic-bold'), + (r"'''(?!')", Generic.Strong, 'inline-bold'), + (r"''(?!')", Generic.Emph, 'inline-italic'), + # Comments & parameters & templates + include('replaceable'), + # Media links + ( + r"""(?xi) + (\[\[) + (File|Image) (:) + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*) + (?: (\#) ([{}]*?) )? + """.format(title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label), + 'medialink-inner' + ), + # Wikilinks + ( + r"""(?xi) + (\[\[)(?!{}) # Should not contain URLs + (?: ([{}]*) (:))? + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*?) + (?: (\#) ([{}]*?) )? + (\]\]) + """.format('|'.join(protocols), title_char.replace('/', ''), + title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label, Punctuation) + ), + ( + r"""(?xi) + (\[\[)(?!{}) + (?: ([{}]*) (:))? + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*?) + (?: (\#) ([{}]*?) )? + (\|) + """.format('|'.join(protocols), title_char.replace('/', ''), + title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label, Punctuation), + 'wikilink-inner' + ), + # External links + ( + r"""(?xi) + (\[) + ((?:{}) {} {}*) + (\s*) + """.format('|'.join(protocols), link_address, link_char_class), + bygroups(Punctuation, Name.Label, Whitespace), + 'extlink-inner' + ), + # Tables + (r'^(:*)(\s*?)(\{\|)([^\n]*)$', bygroups(Keyword, + Whitespace, Punctuation, using(this, state=['root', 'attr'])), 'table'), + # HTML tags + (r'(?i)(<)({})\b'.format('|'.join(html_tags)), + bygroups(Punctuation, Name.Tag), 'tag-inner-ordinary'), + (r'(?i)()'.format('|'.join(html_tags)), + bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)), + # + (r'(?i)(<)(nowiki)\b', bygroups(Punctuation, + Name.Tag), ('tag-nowiki', 'tag-inner')), + #
+            (r'(?i)(<)(pre)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-pre', 'tag-inner')),
+            # 
+            (r'(?i)(<)(categorytree)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-categorytree', 'tag-inner')),
+            # 
+            (r'(?i)(<)(hiero)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-hiero', 'tag-inner')),
+            # 
+            (r'(?i)(<)(math)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-math', 'tag-inner')),
+            # 
+            (r'(?i)(<)(chem)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-chem', 'tag-inner')),
+            # 
+            (r'(?i)(<)(ce)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-ce', 'tag-inner')),
+            # 
+            (r'(?i)(<)(charinsert)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-charinsert', 'tag-inner')),
+            # 
+            (r'(?i)(<)(templatedata)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-templatedata', 'tag-inner')),
+            # 
+            (r'(?i)(<)(gallery)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-gallery', 'tag-inner')),
+            # 
+            (r'(?i)(<)(gallery)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-graph', 'tag-inner')),
+            # 
+            (r'(?i)(<)(dynamicpagelist)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-dynamicpagelist', 'tag-inner')),
+            # 
+            (r'(?i)(<)(inputbox)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-inputbox', 'tag-inner')),
+            # 
+            (r'(?i)(<)(rss)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-rss', 'tag-inner')),
+            # 
+            (r'(?i)(<)(imagemap)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-imagemap', 'tag-inner')),
+            # 
+            (r'(?i)()',
+             bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)),
+            (r'(?si)(<)(syntaxhighlight)\b([^>]*?(?.*?)(?=)',
+             bygroups(Punctuation, Name.Tag, handle_syntaxhighlight)),
+            # : Fallback case for self-closing tags
+            (r'(?i)(<)(syntaxhighlight)\b(\s*?)((?:[^>]|-->)*?)(/\s*?(?)*?)(/\s*?(?)*?)(/\s*?(?|\Z)', Comment.Multiline),
+            # Parameters
+            (
+                r"""(?x)
+                (\{{3})
+                    ([^|]*?)
+                    (?=\}{3}|\|)
+                """,
+                bygroups(Punctuation, Name.Variable),
+                'parameter-inner',
+            ),
+            # Magic variables
+            (r'(?i)(\{{\{{)(\s*)({})(\s*)(\}}\}})'.format('|'.join(magic_vars_i)),
+             bygroups(Punctuation, Whitespace, Name.Function, Whitespace, Punctuation)),
+            (r'(\{{\{{)(\s*)({})(\s*)(\}}\}})'.format('|'.join(magic_vars)),
+                bygroups(Punctuation, Whitespace, Name.Function, Whitespace, Punctuation)),
+            # Parser functions & templates
+            (r'\{\{', Punctuation, 'template-begin-space'),
+            #  legacy syntax
+            (r'(?i)(<)(tvar)\b(\|)([^>]*?)(>)', bygroups(Punctuation,
+             Name.Tag, Punctuation, String, Punctuation)),
+            (r'', Punctuation, '#pop'),
+            # 
+            (r'(?i)(<)(tvar)\b', bygroups(Punctuation, Name.Tag), 'tag-inner-ordinary'),
+            (r'(?i)()',
+             bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)),
+        ],
+        'parameter-inner': [
+            (r'\}{3}', Punctuation, '#pop'),
+            (r'\|', Punctuation),
+            include('inline'),
+            include('text'),
+        ],
+        'template-begin-space': [
+            # Templates allow line breaks at the beginning, and due to how MediaWiki handles
+            # comments, an extra state is required to handle things like {{\n\n name}}
+            (r'|\Z)', Comment.Multiline),
+            (r'\s+', Whitespace),
+            # Parser functions
+            (
+                r'(?i)(\#[{}]*?|{})(:)'.format(title_char,
+                                           '|'.join(parser_functions_i)),
+                bygroups(Name.Function, Punctuation), ('#pop', 'template-inner')
+            ),
+            (
+                r'({})(:)'.format('|'.join(parser_functions)),
+                bygroups(Name.Function, Punctuation), ('#pop', 'template-inner')
+            ),
+            # Templates
+            (
+                rf'(?i)([{title_char}]*?)(:)',
+                bygroups(Name.Namespace, Punctuation), ('#pop', 'template-name')
+            ),
+            default(('#pop', 'template-name'),),
+        ],
+        'template-name': [
+            (r'(\s*?)(\|)', bygroups(Text, Punctuation), ('#pop', 'template-inner')),
+            (r'\}\}', Punctuation, '#pop'),
+            (r'\n', Text, '#pop'),
+            include('replaceable'),
+            *text_rules(Name.Tag),
+        ],
+        'template-inner': [
+            (r'\}\}', Punctuation, '#pop'),
+            (r'\|', Punctuation),
+            (
+                r"""(?x)
+                    (?<=\|)
+                    ( (?: (?! \{\{ | \}\} )[^=\|<])*? ) # Exclude templates and tags
+                    (=)
+                """,
+                bygroups(Name.Label, Operator)
+            ),
+            include('inline'),
+            include('text'),
+        ],
+        'table': [
+            # Use [ \t\n\r\0\x0B] instead of \s to follow PHP trim() behavior
+            # Endings
+            (r'^([ \t\n\r\0\x0B]*?)(\|\})',
+             bygroups(Whitespace, Punctuation), '#pop'),
+            # Table rows
+            (r'^([ \t\n\r\0\x0B]*?)(\|-+)(.*)$', bygroups(Whitespace, Punctuation,
+             using(this, state=['root', 'attr']))),
+            # Captions
+            (
+                r"""(?x)
+                ^([ \t\n\r\0\x0B]*?)(\|\+)
+                # Exclude links, template and tags
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|) )?
+                (.*?)$
+                """,
+                bygroups(Whitespace, Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation, Generic.Heading),
+            ),
+            # Table data
+            (
+                r"""(?x)
+                ( ^(?:[ \t\n\r\0\x0B]*?)\| | \|\| )
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|)(?!\|) )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation),
+            ),
+            # Table headers
+            (
+                r"""(?x)
+                ( ^(?:[ \t\n\r\0\x0B]*?)!  )
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|)(?!\|) )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation),
+                'table-header',
+            ),
+            include('list'),
+            include('inline'),
+            include('text'),
+        ],
+        'table-header': [
+            # Requires another state for || handling inside headers
+            (r'\n', Text, '#pop'),
+            (
+                r"""(?x)
+                (!!|\|\|)
+                (?:
+                    ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )
+                    (\|)(?!\|)
+                )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation)
+            ),
+            *text_rules(Generic.Subheading),
+        ],
+        'entity': [
+            (r'&\S*?;', Name.Entity),
+        ],
+        'dt': [
+            (r'\n', Text, '#pop'),
+            include('inline'),
+            (r':', Keyword, '#pop'),
+            include('text'),
+        ],
+        'extlink-inner': [
+            (r'\]', Punctuation, '#pop'),
+            include('inline'),
+            include('text'),
+        ],
+        'nowiki-ish': [
+            include('entity'),
+            include('text'),
+        ],
+        'attr': [
+            include('replaceable'),
+            (r'\s+', Whitespace),
+            (r'(=)(\s*)(")', bygroups(Operator, Whitespace, String.Double), 'attr-val-2'),
+            (r"(=)(\s*)(')", bygroups(Operator, Whitespace, String.Single), 'attr-val-1'),
+            (r'(=)(\s*)', bygroups(Operator, Whitespace), 'attr-val-0'),
+            (r'[\w:-]+', Name.Attribute),
+
+        ],
+        'attr-val-0': [
+            (r'\s', Whitespace, '#pop'),
+            include('replaceable'),
+            *text_rules(String),
+        ],
+        'attr-val-1': [
+            (r"'", String.Single, '#pop'),
+            include('replaceable'),
+            *text_rules(String.Single),
+        ],
+        'attr-val-2': [
+            (r'"', String.Double, '#pop'),
+            include('replaceable'),
+            *text_rules(String.Double),
+        ],
+        'tag-inner-ordinary': [
+            (r'/?\s*>', Punctuation, '#pop'),
+            include('tag-attr'),
+        ],
+        'tag-inner': [
+            # Return to root state for self-closing tags
+            (r'/\s*>', Punctuation, '#pop:2'),
+            (r'\s*>', Punctuation, '#pop'),
+            include('tag-attr'),
+        ],
+        # There states below are just like their non-tag variants, the key difference is
+        # they forcibly quit when encountering tag closing markup
+        'tag-attr': [
+            include('replaceable'),
+            (r'\s+', Whitespace),
+            (r'(=)(\s*)(")', bygroups(Operator,
+             Whitespace, String.Double), 'tag-attr-val-2'),
+            (r"(=)(\s*)(')", bygroups(Operator,
+             Whitespace, String.Single), 'tag-attr-val-1'),
+            (r'(=)(\s*)', bygroups(Operator, Whitespace), 'tag-attr-val-0'),
+            (r'[\w:-]+', Name.Attribute),
+
+        ],
+        'tag-attr-val-0': [
+            (r'\s', Whitespace, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String),
+        ],
+        'tag-attr-val-1': [
+            (r"'", String.Single, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String.Single),
+        ],
+        'tag-attr-val-2': [
+            (r'"', String.Double, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String.Double),
+        ],
+        'tag-nowiki': nowiki_tag_rules('nowiki'),
+        'tag-pre': nowiki_tag_rules('pre'),
+        'tag-categorytree': plaintext_tag_rules('categorytree'),
+        'tag-dynamicpagelist': plaintext_tag_rules('dynamicpagelist'),
+        'tag-hiero': plaintext_tag_rules('hiero'),
+        'tag-inputbox': plaintext_tag_rules('inputbox'),
+        'tag-imagemap': plaintext_tag_rules('imagemap'),
+        'tag-charinsert': plaintext_tag_rules('charinsert'),
+        'tag-timeline': plaintext_tag_rules('timeline'),
+        'tag-gallery': plaintext_tag_rules('gallery'),
+        'tag-graph': plaintext_tag_rules('graph'),
+        'tag-rss': plaintext_tag_rules('rss'),
+        'tag-math': delegate_tag_rules('math', TexLexer, state='math'),
+        'tag-chem': delegate_tag_rules('chem', TexLexer, state='math'),
+        'tag-ce': delegate_tag_rules('ce', TexLexer, state='math'),
+        'tag-templatedata': delegate_tag_rules('templatedata', JsonLexer),
+        'text-italic': text_rules(Generic.Emph),
+        'text-bold': text_rules(Generic.Strong),
+        'text-bold-italic': text_rules(Generic.EmphStrong),
+        'text': text_rules(Text),
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/math.py b/.venv/lib/python3.12/site-packages/pygments/lexers/math.py
new file mode 100644
index 0000000..b38c13e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/math.py
@@ -0,0 +1,21 @@
+"""
+    pygments.lexers.math
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Just export lexers that were contained in this module.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+# ruff: noqa: F401
+from pygments.lexers.python import NumPyLexer
+from pygments.lexers.matlab import MatlabLexer, MatlabSessionLexer, \
+    OctaveLexer, ScilabLexer
+from pygments.lexers.julia import JuliaLexer, JuliaConsoleLexer
+from pygments.lexers.r import RConsoleLexer, SLexer, RdLexer
+from pygments.lexers.modeling import BugsLexer, JagsLexer, StanLexer
+from pygments.lexers.idl import IDLLexer
+from pygments.lexers.algebra import MuPADLexer
+
+__all__ = []
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/matlab.py b/.venv/lib/python3.12/site-packages/pygments/lexers/matlab.py
new file mode 100644
index 0000000..b27f4db
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/matlab.py
@@ -0,0 +1,3307 @@
+"""
+    pygments.lexers.matlab
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Matlab and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, bygroups, default, words, \
+    do_insertions, include
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Generic, Whitespace
+
+from pygments.lexers import _scilab_builtins
+
+__all__ = ['MatlabLexer', 'MatlabSessionLexer', 'OctaveLexer', 'ScilabLexer']
+
+
+class MatlabLexer(RegexLexer):
+    """
+    For Matlab source code.
+    """
+    name = 'Matlab'
+    aliases = ['matlab']
+    filenames = ['*.m']
+    mimetypes = ['text/matlab']
+    url = 'https://www.mathworks.com/products/matlab.html'
+    version_added = '0.10'
+
+    _operators = r'-|==|~=|<=|>=|<|>|&&|&|~|\|\|?|\.\*|\*|\+|\.\^|\^|\.\\|\./|/|\\'
+
+    tokens = {
+        'expressions': [
+            # operators:
+            (_operators, Operator),
+
+            # numbers (must come before punctuation to handle `.5`; cannot use
+            # `\b` due to e.g. `5. + .5`).  The negative lookahead on operators
+            # avoids including the dot in `1./x` (the dot is part of `./`).
+            (rf'(? and then
+            # (equal | open-parenthesis |  | ).
+            (rf'(?:^|(?<=;))(\s*)(\w+)(\s+)(?!=|\(|{_operators}\s|\s)',
+             bygroups(Whitespace, Name, Whitespace), 'commandargs'),
+
+            include('expressions')
+        ],
+        'blockcomment': [
+            (r'^\s*%\}', Comment.Multiline, '#pop'),
+            (r'^.*\n', Comment.Multiline),
+            (r'.', Comment.Multiline),
+        ],
+        'deffunc': [
+            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
+             bygroups(Whitespace, Text, Whitespace, Punctuation,
+                      Whitespace, Name.Function, Punctuation, Text,
+                      Punctuation, Whitespace), '#pop'),
+            # function with no args
+            (r'(\s*)([a-zA-Z_]\w*)',
+             bygroups(Whitespace, Name.Function), '#pop'),
+        ],
+        'propattrs': [
+            (r'(\w+)(\s*)(=)(\s*)(\d+)',
+             bygroups(Name.Builtin, Whitespace, Punctuation, Whitespace,
+                      Number)),
+            (r'(\w+)(\s*)(=)(\s*)([a-zA-Z]\w*)',
+             bygroups(Name.Builtin, Whitespace, Punctuation, Whitespace,
+                      Keyword)),
+            (r',', Punctuation),
+            (r'\)', Punctuation, '#pop'),
+            (r'\s+', Whitespace),
+            (r'.', Text),
+        ],
+        'defprops': [
+            (r'%\{\s*\n', Comment.Multiline, 'blockcomment'),
+            (r'%.*$', Comment),
+            (r'(?.
+    """
+    name = 'Matlab session'
+    aliases = ['matlabsession']
+    url = 'https://www.mathworks.com/products/matlab.html'
+    version_added = '0.10'
+    _example = "matlabsession/matlabsession_sample.txt"
+
+    def get_tokens_unprocessed(self, text):
+        mlexer = MatlabLexer(**self.options)
+
+        curcode = ''
+        insertions = []
+        continuation = False
+
+        for match in line_re.finditer(text):
+            line = match.group()
+
+            if line.startswith('>> '):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:3])]))
+                curcode += line[3:]
+
+            elif line.startswith('>>'):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:2])]))
+                curcode += line[2:]
+
+            elif line.startswith('???'):
+
+                idx = len(curcode)
+
+                # without is showing error on same line as before...?
+                # line = "\n" + line
+                token = (0, Generic.Traceback, line)
+                insertions.append((idx, [token]))
+            elif continuation and insertions:
+                # line_start is the length of the most recent prompt symbol
+                line_start = len(insertions[-1][-1][-1])
+                # Set leading spaces with the length of the prompt to be a generic prompt
+                # This keeps code aligned when prompts are removed, say with some Javascript
+                if line.startswith(' '*line_start):
+                    insertions.append(
+                        (len(curcode), [(0, Generic.Prompt, line[:line_start])]))
+                    curcode += line[line_start:]
+                else:
+                    curcode += line
+            else:
+                if curcode:
+                    yield from do_insertions(
+                        insertions, mlexer.get_tokens_unprocessed(curcode))
+                    curcode = ''
+                    insertions = []
+
+                yield match.start(), Generic.Output, line
+
+            # Does not allow continuation if a comment is included after the ellipses.
+            # Continues any line that ends with ..., even comments (lines that start with %)
+            if line.strip().endswith('...'):
+                continuation = True
+            else:
+                continuation = False
+
+        if curcode:  # or item:
+            yield from do_insertions(
+                insertions, mlexer.get_tokens_unprocessed(curcode))
+
+
+class OctaveLexer(RegexLexer):
+    """
+    For GNU Octave source code.
+    """
+    name = 'Octave'
+    url = 'https://www.gnu.org/software/octave/index'
+    aliases = ['octave']
+    filenames = ['*.m']
+    mimetypes = ['text/octave']
+    version_added = '1.5'
+
+    # These lists are generated automatically.
+    # Run the following in bash shell:
+    #
+    # First dump all of the Octave manual into a plain text file:
+    #
+    #   $ info octave --subnodes -o octave-manual
+    #
+    # Now grep through it:
+
+    # for i in \
+    #     "Built-in Function" "Command" "Function File" \
+    #     "Loadable Function" "Mapping Function";
+    # do
+    #     perl -e '@name = qw('"$i"');
+    #              print lc($name[0]),"_kw = [\n"';
+    #
+    #     perl -n -e 'print "\"$1\",\n" if /-- '"$i"': .* (\w*) \(/;' \
+    #         octave-manual | sort | uniq ;
+    #     echo "]" ;
+    #     echo;
+    # done
+
+    # taken from Octave Mercurial changeset 8cc154f45e37 (30-jan-2011)
+
+    builtin_kw = (
+        "addlistener", "addpath", "addproperty", "all",
+        "and", "any", "argnames", "argv", "assignin",
+        "atexit", "autoload",
+        "available_graphics_toolkits", "beep_on_error",
+        "bitand", "bitmax", "bitor", "bitshift", "bitxor",
+        "cat", "cell", "cellstr", "char", "class", "clc",
+        "columns", "command_line_path",
+        "completion_append_char", "completion_matches",
+        "complex", "confirm_recursive_rmdir", "cputime",
+        "crash_dumps_octave_core", "ctranspose", "cumprod",
+        "cumsum", "debug_on_error", "debug_on_interrupt",
+        "debug_on_warning", "default_save_options",
+        "dellistener", "diag", "diff", "disp",
+        "doc_cache_file", "do_string_escapes", "double",
+        "drawnow", "e", "echo_executing_commands", "eps",
+        "eq", "errno", "errno_list", "error", "eval",
+        "evalin", "exec", "exist", "exit", "eye", "false",
+        "fclear", "fclose", "fcntl", "fdisp", "feof",
+        "ferror", "feval", "fflush", "fgetl", "fgets",
+        "fieldnames", "file_in_loadpath", "file_in_path",
+        "filemarker", "filesep", "find_dir_in_path",
+        "fixed_point_format", "fnmatch", "fopen", "fork",
+        "formula", "fprintf", "fputs", "fread", "freport",
+        "frewind", "fscanf", "fseek", "fskipl", "ftell",
+        "functions", "fwrite", "ge", "genpath", "get",
+        "getegid", "getenv", "geteuid", "getgid",
+        "getpgrp", "getpid", "getppid", "getuid", "glob",
+        "gt", "gui_mode", "history_control",
+        "history_file", "history_size",
+        "history_timestamp_format_string", "home",
+        "horzcat", "hypot", "ifelse",
+        "ignore_function_time_stamp", "inferiorto",
+        "info_file", "info_program", "inline", "input",
+        "intmax", "intmin", "ipermute",
+        "is_absolute_filename", "isargout", "isbool",
+        "iscell", "iscellstr", "ischar", "iscomplex",
+        "isempty", "isfield", "isfloat", "isglobal",
+        "ishandle", "isieee", "isindex", "isinteger",
+        "islogical", "ismatrix", "ismethod", "isnull",
+        "isnumeric", "isobject", "isreal",
+        "is_rooted_relative_filename", "issorted",
+        "isstruct", "isvarname", "kbhit", "keyboard",
+        "kill", "lasterr", "lasterror", "lastwarn",
+        "ldivide", "le", "length", "link", "linspace",
+        "logical", "lstat", "lt", "make_absolute_filename",
+        "makeinfo_program", "max_recursion_depth", "merge",
+        "methods", "mfilename", "minus", "mislocked",
+        "mkdir", "mkfifo", "mkstemp", "mldivide", "mlock",
+        "mouse_wheel_zoom", "mpower", "mrdivide", "mtimes",
+        "munlock", "nargin", "nargout",
+        "native_float_format", "ndims", "ne", "nfields",
+        "nnz", "norm", "not", "numel", "nzmax",
+        "octave_config_info", "octave_core_file_limit",
+        "octave_core_file_name",
+        "octave_core_file_options", "ones", "or",
+        "output_max_field_width", "output_precision",
+        "page_output_immediately", "page_screen_output",
+        "path", "pathsep", "pause", "pclose", "permute",
+        "pi", "pipe", "plus", "popen", "power",
+        "print_empty_dimensions", "printf",
+        "print_struct_array_contents", "prod",
+        "program_invocation_name", "program_name",
+        "putenv", "puts", "pwd", "quit", "rats", "rdivide",
+        "readdir", "readlink", "read_readline_init_file",
+        "realmax", "realmin", "rehash", "rename",
+        "repelems", "re_read_readline_init_file", "reset",
+        "reshape", "resize", "restoredefaultpath",
+        "rethrow", "rmdir", "rmfield", "rmpath", "rows",
+        "save_header_format_string", "save_precision",
+        "saving_history", "scanf", "set", "setenv",
+        "shell_cmd", "sighup_dumps_octave_core",
+        "sigterm_dumps_octave_core", "silent_functions",
+        "single", "size", "size_equal", "sizemax",
+        "sizeof", "sleep", "source", "sparse_auto_mutate",
+        "split_long_rows", "sprintf", "squeeze", "sscanf",
+        "stat", "stderr", "stdin", "stdout", "strcmp",
+        "strcmpi", "string_fill_char", "strncmp",
+        "strncmpi", "struct", "struct_levels_to_print",
+        "strvcat", "subsasgn", "subsref", "sum", "sumsq",
+        "superiorto", "suppress_verbose_help_message",
+        "symlink", "system", "tic", "tilde_expand",
+        "times", "tmpfile", "tmpnam", "toc", "toupper",
+        "transpose", "true", "typeinfo", "umask", "uminus",
+        "uname", "undo_string_escapes", "unlink", "uplus",
+        "upper", "usage", "usleep", "vec", "vectorize",
+        "vertcat", "waitpid", "warning", "warranty",
+        "whos_line_format", "yes_or_no", "zeros",
+        "inf", "Inf", "nan", "NaN")
+
+    command_kw = ("close", "load", "who", "whos")
+
+    function_kw = (
+        "accumarray", "accumdim", "acosd", "acotd",
+        "acscd", "addtodate", "allchild", "ancestor",
+        "anova", "arch_fit", "arch_rnd", "arch_test",
+        "area", "arma_rnd", "arrayfun", "ascii", "asctime",
+        "asecd", "asind", "assert", "atand",
+        "autoreg_matrix", "autumn", "axes", "axis", "bar",
+        "barh", "bartlett", "bartlett_test", "beep",
+        "betacdf", "betainv", "betapdf", "betarnd",
+        "bicgstab", "bicubic", "binary", "binocdf",
+        "binoinv", "binopdf", "binornd", "bitcmp",
+        "bitget", "bitset", "blackman", "blanks",
+        "blkdiag", "bone", "box", "brighten", "calendar",
+        "cast", "cauchy_cdf", "cauchy_inv", "cauchy_pdf",
+        "cauchy_rnd", "caxis", "celldisp", "center", "cgs",
+        "chisquare_test_homogeneity",
+        "chisquare_test_independence", "circshift", "cla",
+        "clabel", "clf", "clock", "cloglog", "closereq",
+        "colon", "colorbar", "colormap", "colperm",
+        "comet", "common_size", "commutation_matrix",
+        "compan", "compare_versions", "compass",
+        "computer", "cond", "condest", "contour",
+        "contourc", "contourf", "contrast", "conv",
+        "convhull", "cool", "copper", "copyfile", "cor",
+        "corrcoef", "cor_test", "cosd", "cotd", "cov",
+        "cplxpair", "cross", "cscd", "cstrcat", "csvread",
+        "csvwrite", "ctime", "cumtrapz", "curl", "cut",
+        "cylinder", "date", "datenum", "datestr",
+        "datetick", "datevec", "dblquad", "deal",
+        "deblank", "deconv", "delaunay", "delaunayn",
+        "delete", "demo", "detrend", "diffpara", "diffuse",
+        "dir", "discrete_cdf", "discrete_inv",
+        "discrete_pdf", "discrete_rnd", "display",
+        "divergence", "dlmwrite", "dos", "dsearch",
+        "dsearchn", "duplication_matrix", "durbinlevinson",
+        "ellipsoid", "empirical_cdf", "empirical_inv",
+        "empirical_pdf", "empirical_rnd", "eomday",
+        "errorbar", "etime", "etreeplot", "example",
+        "expcdf", "expinv", "expm", "exppdf", "exprnd",
+        "ezcontour", "ezcontourf", "ezmesh", "ezmeshc",
+        "ezplot", "ezpolar", "ezsurf", "ezsurfc", "factor",
+        "factorial", "fail", "fcdf", "feather", "fftconv",
+        "fftfilt", "fftshift", "figure", "fileattrib",
+        "fileparts", "fill", "findall", "findobj",
+        "findstr", "finv", "flag", "flipdim", "fliplr",
+        "flipud", "fpdf", "fplot", "fractdiff", "freqz",
+        "freqz_plot", "frnd", "fsolve",
+        "f_test_regression", "ftp", "fullfile", "fzero",
+        "gamcdf", "gaminv", "gampdf", "gamrnd", "gca",
+        "gcbf", "gcbo", "gcf", "genvarname", "geocdf",
+        "geoinv", "geopdf", "geornd", "getfield", "ginput",
+        "glpk", "gls", "gplot", "gradient",
+        "graphics_toolkit", "gray", "grid", "griddata",
+        "griddatan", "gtext", "gunzip", "gzip", "hadamard",
+        "hamming", "hankel", "hanning", "hggroup",
+        "hidden", "hilb", "hist", "histc", "hold", "hot",
+        "hotelling_test", "housh", "hsv", "hurst",
+        "hygecdf", "hygeinv", "hygepdf", "hygernd",
+        "idivide", "ifftshift", "image", "imagesc",
+        "imfinfo", "imread", "imshow", "imwrite", "index",
+        "info", "inpolygon", "inputname", "interpft",
+        "interpn", "intersect", "invhilb", "iqr", "isa",
+        "isdefinite", "isdir", "is_duplicate_entry",
+        "isequal", "isequalwithequalnans", "isfigure",
+        "ishermitian", "ishghandle", "is_leap_year",
+        "isletter", "ismac", "ismember", "ispc", "isprime",
+        "isprop", "isscalar", "issquare", "isstrprop",
+        "issymmetric", "isunix", "is_valid_file_id",
+        "isvector", "jet", "kendall",
+        "kolmogorov_smirnov_cdf",
+        "kolmogorov_smirnov_test", "kruskal_wallis_test",
+        "krylov", "kurtosis", "laplace_cdf", "laplace_inv",
+        "laplace_pdf", "laplace_rnd", "legend", "legendre",
+        "license", "line", "linkprop", "list_primes",
+        "loadaudio", "loadobj", "logistic_cdf",
+        "logistic_inv", "logistic_pdf", "logistic_rnd",
+        "logit", "loglog", "loglogerr", "logm", "logncdf",
+        "logninv", "lognpdf", "lognrnd", "logspace",
+        "lookfor", "ls_command", "lsqnonneg", "magic",
+        "mahalanobis", "manova", "matlabroot",
+        "mcnemar_test", "mean", "meansq", "median", "menu",
+        "mesh", "meshc", "meshgrid", "meshz", "mexext",
+        "mget", "mkpp", "mode", "moment", "movefile",
+        "mpoles", "mput", "namelengthmax", "nargchk",
+        "nargoutchk", "nbincdf", "nbininv", "nbinpdf",
+        "nbinrnd", "nchoosek", "ndgrid", "newplot", "news",
+        "nonzeros", "normcdf", "normest", "norminv",
+        "normpdf", "normrnd", "now", "nthroot", "null",
+        "ocean", "ols", "onenormest", "optimget",
+        "optimset", "orderfields", "orient", "orth",
+        "pack", "pareto", "parseparams", "pascal", "patch",
+        "pathdef", "pcg", "pchip", "pcolor", "pcr",
+        "peaks", "periodogram", "perl", "perms", "pie",
+        "pink", "planerot", "playaudio", "plot",
+        "plotmatrix", "plotyy", "poisscdf", "poissinv",
+        "poisspdf", "poissrnd", "polar", "poly",
+        "polyaffine", "polyarea", "polyderiv", "polyfit",
+        "polygcd", "polyint", "polyout", "polyreduce",
+        "polyval", "polyvalm", "postpad", "powerset",
+        "ppder", "ppint", "ppjumps", "ppplot", "ppval",
+        "pqpnonneg", "prepad", "primes", "print",
+        "print_usage", "prism", "probit", "qp", "qqplot",
+        "quadcc", "quadgk", "quadl", "quadv", "quiver",
+        "qzhess", "rainbow", "randi", "range", "rank",
+        "ranks", "rat", "reallog", "realpow", "realsqrt",
+        "record", "rectangle_lw", "rectangle_sw",
+        "rectint", "refresh", "refreshdata",
+        "regexptranslate", "repmat", "residue", "ribbon",
+        "rindex", "roots", "rose", "rosser", "rotdim",
+        "rref", "run", "run_count", "rundemos", "run_test",
+        "runtests", "saveas", "saveaudio", "saveobj",
+        "savepath", "scatter", "secd", "semilogx",
+        "semilogxerr", "semilogy", "semilogyerr",
+        "setaudio", "setdiff", "setfield", "setxor",
+        "shading", "shift", "shiftdim", "sign_test",
+        "sinc", "sind", "sinetone", "sinewave", "skewness",
+        "slice", "sombrero", "sortrows", "spaugment",
+        "spconvert", "spdiags", "spearman", "spectral_adf",
+        "spectral_xdf", "specular", "speed", "spencer",
+        "speye", "spfun", "sphere", "spinmap", "spline",
+        "spones", "sprand", "sprandn", "sprandsym",
+        "spring", "spstats", "spy", "sqp", "stairs",
+        "statistics", "std", "stdnormal_cdf",
+        "stdnormal_inv", "stdnormal_pdf", "stdnormal_rnd",
+        "stem", "stft", "strcat", "strchr", "strjust",
+        "strmatch", "strread", "strsplit", "strtok",
+        "strtrim", "strtrunc", "structfun", "studentize",
+        "subplot", "subsindex", "subspace", "substr",
+        "substruct", "summer", "surf", "surface", "surfc",
+        "surfl", "surfnorm", "svds", "swapbytes",
+        "sylvester_matrix", "symvar", "synthesis", "table",
+        "tand", "tar", "tcdf", "tempdir", "tempname",
+        "test", "text", "textread", "textscan", "tinv",
+        "title", "toeplitz", "tpdf", "trace", "trapz",
+        "treelayout", "treeplot", "triangle_lw",
+        "triangle_sw", "tril", "trimesh", "triplequad",
+        "triplot", "trisurf", "triu", "trnd", "tsearchn",
+        "t_test", "t_test_regression", "type", "unidcdf",
+        "unidinv", "unidpdf", "unidrnd", "unifcdf",
+        "unifinv", "unifpdf", "unifrnd", "union", "unique",
+        "unix", "unmkpp", "unpack", "untabify", "untar",
+        "unwrap", "unzip", "u_test", "validatestring",
+        "vander", "var", "var_test", "vech", "ver",
+        "version", "view", "voronoi", "voronoin",
+        "waitforbuttonpress", "wavread", "wavwrite",
+        "wblcdf", "wblinv", "wblpdf", "wblrnd", "weekday",
+        "welch_test", "what", "white", "whitebg",
+        "wienrnd", "wilcoxon_test", "wilkinson", "winter",
+        "xlabel", "xlim", "ylabel", "yulewalker", "zip",
+        "zlabel", "z_test")
+
+    loadable_kw = (
+        "airy", "amd", "balance", "besselh", "besseli",
+        "besselj", "besselk", "bessely", "bitpack",
+        "bsxfun", "builtin", "ccolamd", "cellfun",
+        "cellslices", "chol", "choldelete", "cholinsert",
+        "cholinv", "cholshift", "cholupdate", "colamd",
+        "colloc", "convhulln", "convn", "csymamd",
+        "cummax", "cummin", "daspk", "daspk_options",
+        "dasrt", "dasrt_options", "dassl", "dassl_options",
+        "dbclear", "dbdown", "dbstack", "dbstatus",
+        "dbstop", "dbtype", "dbup", "dbwhere", "det",
+        "dlmread", "dmperm", "dot", "eig", "eigs",
+        "endgrent", "endpwent", "etree", "fft", "fftn",
+        "fftw", "filter", "find", "full", "gcd",
+        "getgrent", "getgrgid", "getgrnam", "getpwent",
+        "getpwnam", "getpwuid", "getrusage", "givens",
+        "gmtime", "gnuplot_binary", "hess", "ifft",
+        "ifftn", "inv", "isdebugmode", "issparse", "kron",
+        "localtime", "lookup", "lsode", "lsode_options",
+        "lu", "luinc", "luupdate", "matrix_type", "max",
+        "min", "mktime", "pinv", "qr", "qrdelete",
+        "qrinsert", "qrshift", "qrupdate", "quad",
+        "quad_options", "qz", "rand", "rande", "randg",
+        "randn", "randp", "randperm", "rcond", "regexp",
+        "regexpi", "regexprep", "schur", "setgrent",
+        "setpwent", "sort", "spalloc", "sparse", "spparms",
+        "sprank", "sqrtm", "strfind", "strftime",
+        "strptime", "strrep", "svd", "svd_driver", "syl",
+        "symamd", "symbfact", "symrcm", "time", "tsearch",
+        "typecast", "urlread", "urlwrite")
+
+    mapping_kw = (
+        "abs", "acos", "acosh", "acot", "acoth", "acsc",
+        "acsch", "angle", "arg", "asec", "asech", "asin",
+        "asinh", "atan", "atanh", "beta", "betainc",
+        "betaln", "bincoeff", "cbrt", "ceil", "conj", "cos",
+        "cosh", "cot", "coth", "csc", "csch", "erf", "erfc",
+        "erfcx", "erfinv", "exp", "finite", "fix", "floor",
+        "fmod", "gamma", "gammainc", "gammaln", "imag",
+        "isalnum", "isalpha", "isascii", "iscntrl",
+        "isdigit", "isfinite", "isgraph", "isinf",
+        "islower", "isna", "isnan", "isprint", "ispunct",
+        "isspace", "isupper", "isxdigit", "lcm", "lgamma",
+        "log", "lower", "mod", "real", "rem", "round",
+        "roundb", "sec", "sech", "sign", "sin", "sinh",
+        "sqrt", "tan", "tanh", "toascii", "tolower", "xor")
+
+    builtin_consts = (
+        "EDITOR", "EXEC_PATH", "I", "IMAGE_PATH", "NA",
+        "OCTAVE_HOME", "OCTAVE_VERSION", "PAGER",
+        "PAGER_FLAGS", "SEEK_CUR", "SEEK_END", "SEEK_SET",
+        "SIG", "S_ISBLK", "S_ISCHR", "S_ISDIR", "S_ISFIFO",
+        "S_ISLNK", "S_ISREG", "S_ISSOCK", "WCONTINUE",
+        "WCOREDUMP", "WEXITSTATUS", "WIFCONTINUED",
+        "WIFEXITED", "WIFSIGNALED", "WIFSTOPPED", "WNOHANG",
+        "WSTOPSIG", "WTERMSIG", "WUNTRACED")
+
+    tokens = {
+        'root': [
+            (r'%\{\s*\n', Comment.Multiline, 'percentblockcomment'),
+            (r'#\{\s*\n', Comment.Multiline, 'hashblockcomment'),
+            (r'[%#].*$', Comment),
+            (r'^\s*function\b', Keyword, 'deffunc'),
+
+            # from 'iskeyword' on hg changeset 8cc154f45e37
+            (words((
+                '__FILE__', '__LINE__', 'break', 'case', 'catch', 'classdef',
+                'continue', 'do', 'else', 'elseif', 'end', 'end_try_catch',
+                'end_unwind_protect', 'endclassdef', 'endevents', 'endfor',
+                'endfunction', 'endif', 'endmethods', 'endproperties', 'endswitch',
+                'endwhile', 'events', 'for', 'function', 'get', 'global', 'if',
+                'methods', 'otherwise', 'persistent', 'properties', 'return',
+                'set', 'static', 'switch', 'try', 'until', 'unwind_protect',
+                'unwind_protect_cleanup', 'while'), suffix=r'\b'),
+             Keyword),
+
+            (words(builtin_kw + command_kw + function_kw + loadable_kw + mapping_kw,
+                   suffix=r'\b'),  Name.Builtin),
+
+            (words(builtin_consts, suffix=r'\b'), Name.Constant),
+
+            # operators in Octave but not Matlab:
+            (r'-=|!=|!|/=|--', Operator),
+            # operators:
+            (r'-|==|~=|<|>|<=|>=|&&|&|~|\|\|?', Operator),
+            # operators in Octave but not Matlab requiring escape for re:
+            (r'\*=|\+=|\^=|\/=|\\=|\*\*|\+\+|\.\*\*', Operator),
+            # operators requiring escape for re:
+            (r'\.\*|\*|\+|\.\^|\^|\.\\|\.\/|\/|\\', Operator),
+
+
+            # punctuation:
+            (r'[\[\](){}:@.,]', Punctuation),
+            (r'=|:|;', Punctuation),
+
+            (r'"[^"]*"', String),
+
+            (r'(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?', Number.Float),
+            (r'\d+[eEf][+-]?[0-9]+', Number.Float),
+            (r'\d+', Number.Integer),
+
+            # quote can be transpose, instead of string:
+            # (not great, but handles common cases...)
+            (r'(?<=[\w)\].])\'+', Operator),
+            (r'(?|<=|>=|&&|&|~|\|\|?', Operator),
+            # operators requiring escape for re:
+            (r'\.\*|\*|\+|\.\^|\^|\.\\|\.\/|\/|\\', Operator),
+
+            # punctuation:
+            (r'[\[\](){}@.,=:;]+', Punctuation),
+
+            (r'"[^"]*"', String),
+
+            # quote can be transpose, instead of string:
+            # (not great, but handles common cases...)
+            (r'(?<=[\w)\].])\'+', Operator),
+            (r'(?', r'<', r'|', r'!', r"'")
+
+    operator_words = ('and', 'or', 'not')
+
+    tokens = {
+        'root': [
+            (r'/\*', Comment.Multiline, 'comment'),
+            (r'"(?:[^"\\]|\\.)*"', String),
+            (r'\(|\)|\[|\]|\{|\}', Punctuation),
+            (r'[,;$]', Punctuation),
+            (words (constants), Name.Constant),
+            (words (keywords), Keyword),
+            (words (operators), Operator),
+            (words (operator_words), Operator.Word),
+            (r'''(?x)
+              ((?:[a-zA-Z_#][\w#]*|`[^`]*`)
+              (?:::[a-zA-Z_#][\w#]*|`[^`]*`)*)(\s*)([(])''',
+             bygroups(Name.Function, Text.Whitespace, Punctuation)),
+            (r'''(?x)
+              (?:[a-zA-Z_#%][\w#%]*|`[^`]*`)
+              (?:::[a-zA-Z_#%][\w#%]*|`[^`]*`)*''', Name.Variable),
+            (r'[-+]?(\d*\.\d+([bdefls][-+]?\d+)?|\d+(\.\d*)?[bdefls][-+]?\d+)', Number.Float),
+            (r'[-+]?\d+', Number.Integer),
+            (r'\s+', Text.Whitespace),
+            (r'.', Text)
+        ],
+        'comment': [
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline)
+        ]
+    }
+
+    def analyse_text (text):
+        strength = 0.0
+        # Input expression terminator.
+        if re.search (r'\$\s*$', text, re.MULTILINE):
+            strength += 0.05
+        # Function definition operator.
+        if ':=' in text:
+            strength += 0.02
+        return strength
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/meson.py b/.venv/lib/python3.12/site-packages/pygments/lexers/meson.py
new file mode 100644
index 0000000..154a2de
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/meson.py
@@ -0,0 +1,139 @@
+"""
+    pygments.lexers.meson
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Pygments lexer for the Meson build system
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words, include
+from pygments.token import Comment, Name, Number, Punctuation, Operator, \
+    Keyword, String, Whitespace
+
+__all__ = ['MesonLexer']
+
+
+class MesonLexer(RegexLexer):
+    """Meson language lexer.
+
+    The grammar definition use to transcribe the syntax was retrieved from
+    https://mesonbuild.com/Syntax.html#grammar for version 0.58.
+    Some of those definitions are improperly transcribed, so the Meson++
+    implementation was also checked: https://github.com/dcbaker/meson-plus-plus.
+    """
+
+    # TODO String interpolation @VARNAME@ inner matches
+    # TODO keyword_arg: value inner matches
+
+    name = 'Meson'
+    url = 'https://mesonbuild.com/'
+    aliases = ['meson', 'meson.build']
+    filenames = ['meson.build', 'meson.options', 'meson_options.txt']
+    mimetypes = ['text/x-meson']
+    version_added = '2.10'
+
+    tokens = {
+        'root': [
+            (r'#.*?$', Comment),
+            (r"'''.*'''", String.Single),
+            (r'[1-9][0-9]*', Number.Integer),
+            (r'0o[0-7]+', Number.Oct),
+            (r'0x[a-fA-F0-9]+', Number.Hex),
+            include('string'),
+            include('keywords'),
+            include('expr'),
+            (r'[a-zA-Z_][a-zA-Z_0-9]*', Name),
+            (r'\s+', Whitespace),
+        ],
+        'string': [
+            (r"[']{3}([']{0,2}([^\\']|\\(.|\n)))*[']{3}", String),
+            (r"'.*?(?`_.
+    """
+
+    name = "MCFunction"
+    url = "https://minecraft.wiki/w/Commands"
+    aliases = ["mcfunction", "mcf"]
+    filenames = ["*.mcfunction"]
+    mimetypes = ["text/mcfunction"]
+    version_added = '2.12'
+
+    # Used to denotate the start of a block comment, borrowed from Github's mcfunction
+    _block_comment_prefix = "[>!]"
+
+    tokens = {
+        "root": [
+            include("names"),
+            include("comments"),
+            include("literals"),
+            include("whitespace"),
+            include("property"),
+            include("operators"),
+            include("selectors"),
+        ],
+
+        "names": [
+            # The start of a command (either beginning of line OR after the run keyword)
+            #  We don't encode a list of keywords since mods, plugins, or even pre-processors
+            #  may add new commands, so we have a 'close-enough' regex which catches them.
+            (r"^(\s*)([a-z_]+)", bygroups(Whitespace, Name.Builtin)),
+            (r"(?<=run)\s+[a-z_]+", Name.Builtin),
+
+            # UUID
+            (r"\b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\b", Name.Variable),
+            include("resource-name"),
+            # normal command names and scoreboards
+            #  there's no way to know the differences unfortuntely
+            (r"[A-Za-z_][\w.#%$]+", Keyword.Constant),
+            (r"[#%$][\w.#%$]+", Name.Variable.Magic),
+        ],
+
+        "resource-name": [
+            # resource names have to be lowercase
+            (r"#?[a-z_][a-z_.-]*:[a-z0-9_./-]+", Name.Function),
+            # similar to above except optional `:``
+            #  a `/` must be present "somewhere"
+            (r"#?[a-z0-9_\.\-]+\/[a-z0-9_\.\-\/]+", Name.Function),
+        ],
+
+        "whitespace": [
+            (r"\s+", Whitespace),
+        ],
+
+        "comments": [
+            (rf"^\s*(#{_block_comment_prefix})", Comment.Multiline,
+             ("comments.block", "comments.block.emphasized")),
+            (r"#.*$", Comment.Single),
+        ],
+        "comments.block": [
+            (rf"^\s*#{_block_comment_prefix}", Comment.Multiline,
+             "comments.block.emphasized"),
+            (r"^\s*#", Comment.Multiline, "comments.block.normal"),
+            default("#pop"),
+        ],
+        "comments.block.normal": [
+            include("comments.block.special"),
+            (r"\S+", Comment.Multiline),
+            (r"\n", Text, "#pop"),
+            include("whitespace"),
+        ],
+        "comments.block.emphasized": [
+            include("comments.block.special"),
+            (r"\S+", String.Doc),
+            (r"\n", Text, "#pop"),
+            include("whitespace"),
+        ],
+        "comments.block.special": [
+            # Params
+            (r"@\S+", Name.Decorator),
+
+            include("resource-name"),
+
+            # Scoreboard player names
+            (r"[#%$][\w.#%$]+", Name.Variable.Magic),
+        ],
+
+        "operators": [
+            (r"[\-~%^?!+*<>\\/|&=.]", Operator),
+        ],
+
+        "literals": [
+            (r"\.\.", Literal),
+            (r"(true|false)", Keyword.Pseudo),
+
+            # these are like unquoted strings and appear in many places
+            (r"[A-Za-z_]+", Name.Variable.Class),
+
+            (r"[0-7]b", Number.Byte),
+            (r"[+-]?\d*\.?\d+([eE]?[+-]?\d+)?[df]?\b", Number.Float),
+            (r"[+-]?\d+\b", Number.Integer),
+            (r'"', String.Double, "literals.string-double"),
+            (r"'", String.Single, "literals.string-single"),
+        ],
+        "literals.string-double": [
+            (r"\\.", String.Escape),
+            (r'[^\\"\n]+', String.Double),
+            (r'"', String.Double, "#pop"),
+        ],
+        "literals.string-single": [
+            (r"\\.", String.Escape),
+            (r"[^\\'\n]+", String.Single),
+            (r"'", String.Single, "#pop"),
+        ],
+
+        "selectors": [
+            (r"@[a-z]", Name.Variable),
+        ],
+
+
+        ## Generic Property Container
+        # There are several, differing instances where the language accepts
+        #  specific contained keys or contained key, value pairings.
+        #
+        # Property Maps:
+        # - Starts with either `[` or `{`
+        # - Key separated by `:` or `=`
+        # - Deliminated by `,`
+        #
+        # Property Lists:
+        # - Starts with `[`
+        # - Deliminated by `,`
+        #
+        # For simplicity, these patterns match a generic, nestable structure
+        #  which follow a key, value pattern. For normal lists, there's only keys.
+        # This allow some "illegal" structures, but we'll accept those for
+        #  sake of simplicity
+        #
+        # Examples:
+        # - `[facing=up, powered=true]` (blockstate)
+        # - `[name="hello world", nbt={key: 1b}]` (selector + nbt)
+        # - `[{"text": "value"}, "literal"]` (json)
+        ##
+        "property": [
+            # This state gets included in root and also several substates
+            # We do this to shortcut the starting of new properties
+            #  within other properties. Lists can have sublists and compounds
+            #  and values can start a new property (see the `difficult_1.txt`
+            #  snippet).
+            (r"\{", Punctuation, ("property.curly", "property.key")),
+            (r"\[", Punctuation, ("property.square", "property.key")),
+        ],
+        "property.curly": [
+            include("whitespace"),
+            include("property"),
+            (r"\}", Punctuation, "#pop"),
+        ],
+        "property.square": [
+            include("whitespace"),
+            include("property"),
+            (r"\]", Punctuation, "#pop"),
+
+            # lists can have sequences of items
+            (r",", Punctuation),
+        ],
+        "property.key": [
+            include("whitespace"),
+
+            # resource names (for advancements)
+            #  can omit `:` to default `minecraft:`
+            # must check if there is a future equals sign if `:` is in the name
+            (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+(?=\s*\=)", Name.Attribute, "property.delimiter"),
+            (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Attribute, "property.delimiter"),
+
+            # unquoted NBT key
+            (r"[A-Za-z_\-\+]+", Name.Attribute, "property.delimiter"),
+
+            # quoted JSON or NBT key
+            (r'"', Name.Attribute, "property.delimiter", "literals.string-double"),
+            (r"'", Name.Attribute, "property.delimiter", "literals.string-single"),
+
+            # index for a list
+            (r"-?\d+", Number.Integer, "property.delimiter"),
+
+            default("#pop"),
+        ],
+        "property.key.string-double": [
+            (r"\\.", String.Escape),
+            (r'[^\\"\n]+', Name.Attribute),
+            (r'"', Name.Attribute, "#pop"),
+        ],
+        "property.key.string-single": [
+            (r"\\.", String.Escape),
+            (r"[^\\'\n]+", Name.Attribute),
+            (r"'", Name.Attribute, "#pop"),
+        ],
+        "property.delimiter": [
+            include("whitespace"),
+
+            (r"[:=]!?", Punctuation, "property.value"),
+            (r",", Punctuation),
+
+            default("#pop"),
+        ],
+        "property.value": [
+            include("whitespace"),
+
+            # unquoted resource names are valid literals here
+            (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+", Name.Tag),
+            (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Tag),
+
+            include("literals"),
+            include("property"),
+
+            default("#pop"),
+        ],
+    }
+
+
+class MCSchemaLexer(RegexLexer):
+    """Lexer for Minecraft Add-ons data Schemas, an interface structure standard used in Minecraft
+    """
+
+    name = 'MCSchema'
+    url = 'https://learn.microsoft.com/en-us/minecraft/creator/reference/content/schemasreference/'
+    aliases = ['mcschema']
+    filenames = ['*.mcschema']
+    mimetypes = ['text/mcschema']
+    version_added = '2.14'
+
+    tokens = {
+        'commentsandwhitespace': [
+            (r'\s+', Whitespace),
+            (r'//.*?$', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline)
+        ],
+        'slashstartsregex': [
+            include('commentsandwhitespace'),
+            (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
+             r'([gimuysd]+\b|\B)', String.Regex, '#pop'),
+            (r'(?=/)', Text, ('#pop', 'badregex')),
+            default('#pop')
+        ],
+        'badregex': [
+            (r'\n', Whitespace, '#pop')
+        ],
+        'singlestring': [
+            (r'\\.', String.Escape),
+            (r"'", String.Single, '#pop'),
+            (r"[^\\']+", String.Single),
+        ],
+        'doublestring': [
+            (r'\\.', String.Escape),
+            (r'"', String.Double, '#pop'),
+            (r'[^\\"]+', String.Double),
+        ],
+        'root': [
+            (r'^(?=\s|/|', Comment, '#pop'),
+            (r'[^\-]+|-', Comment),
+        ],
+    }
+
+
+class ReasonLexer(RegexLexer):
+    """
+    For the ReasonML language.
+    """
+
+    name = 'ReasonML'
+    url = 'https://reasonml.github.io/'
+    aliases = ['reasonml', 'reason']
+    filenames = ['*.re', '*.rei']
+    mimetypes = ['text/x-reasonml']
+    version_added = '2.6'
+
+    keywords = (
+        'as', 'assert', 'begin', 'class', 'constraint', 'do', 'done', 'downto',
+        'else', 'end', 'exception', 'external', 'false', 'for', 'fun', 'esfun',
+        'function', 'functor', 'if', 'in', 'include', 'inherit', 'initializer', 'lazy',
+        'let', 'switch', 'module', 'pub', 'mutable', 'new', 'nonrec', 'object', 'of',
+        'open', 'pri', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try',
+        'type', 'val', 'virtual', 'when', 'while', 'with',
+    )
+    keyopts = (
+        '!=', '#', '&', '&&', r'\(', r'\)', r'\*', r'\+', ',', '-',
+        r'-\.', '=>', r'\.', r'\.\.', r'\.\.\.', ':', '::', ':=', ':>', ';', ';;', '<',
+        '<-', '=', '>', '>]', r'>\}', r'\?', r'\?\?', r'\[', r'\[<', r'\[>',
+        r'\[\|', ']', '_', '`', r'\{', r'\{<', r'\|', r'\|\|', r'\|]', r'\}', '~'
+    )
+
+    operators = r'[!$%&*+\./:<=>?@^|~-]'
+    word_operators = ('and', 'asr', 'land', 'lor', 'lsl', 'lsr', 'lxor', 'mod', 'or')
+    prefix_syms = r'[!?~]'
+    infix_syms = r'[=<>@^|&+\*/$%-]'
+    primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array')
+
+    tokens = {
+        'escape-sequence': [
+            (r'\\[\\"\'ntbr]', String.Escape),
+            (r'\\[0-9]{3}', String.Escape),
+            (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        ],
+        'root': [
+            (r'\s+', Text),
+            (r'false|true|\(\)|\[\]', Name.Builtin.Pseudo),
+            (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'),
+            (r'\b([A-Z][\w\']*)', Name.Class),
+            (r'//.*?\n', Comment.Single),
+            (r'\/\*(?!/)', Comment.Multiline, 'comment'),
+            (r'\b({})\b'.format('|'.join(keywords)), Keyword),
+            (r'({})'.format('|'.join(keyopts[::-1])), Operator.Word),
+            (rf'({infix_syms}|{prefix_syms})?{operators}', Operator),
+            (r'\b({})\b'.format('|'.join(word_operators)), Operator.Word),
+            (r'\b({})\b'.format('|'.join(primitives)), Keyword.Type),
+
+            (r"[^\W\d][\w']*", Name),
+
+            (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float),
+            (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex),
+            (r'0[oO][0-7][0-7_]*', Number.Oct),
+            (r'0[bB][01][01_]*', Number.Bin),
+            (r'\d[\d_]*', Number.Integer),
+
+            (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'",
+             String.Char),
+            (r"'.'", String.Char),
+            (r"'", Keyword),
+
+            (r'"', String.Double, 'string'),
+
+            (r'[~?][a-z][\w\']*:', Name.Variable),
+        ],
+        'comment': [
+            (r'[^/*]+', Comment.Multiline),
+            (r'\/\*', Comment.Multiline, '#push'),
+            (r'\*\/', Comment.Multiline, '#pop'),
+            (r'\*', Comment.Multiline),
+        ],
+        'string': [
+            (r'[^\\"]+', String.Double),
+            include('escape-sequence'),
+            (r'\\\n', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'dotted': [
+            (r'\s+', Text),
+            (r'\.', Punctuation),
+            (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace),
+            (r'[A-Z][\w\']*', Name.Class, '#pop'),
+            (r'[a-z_][\w\']*', Name, '#pop'),
+            default('#pop'),
+        ],
+    }
+
+
+class FStarLexer(RegexLexer):
+    """
+    For the F* language.
+    """
+
+    name = 'FStar'
+    url = 'https://www.fstar-lang.org/'
+    aliases = ['fstar']
+    filenames = ['*.fst', '*.fsti']
+    mimetypes = ['text/x-fstar']
+    version_added = '2.7'
+
+    keywords = (
+        'abstract', 'attributes', 'noeq', 'unopteq', 'and'
+        'begin', 'by', 'default', 'effect', 'else', 'end', 'ensures',
+        'exception', 'exists', 'false', 'forall', 'fun', 'function', 'if',
+        'in', 'include', 'inline', 'inline_for_extraction', 'irreducible',
+        'logic', 'match', 'module', 'mutable', 'new', 'new_effect', 'noextract',
+        'of', 'open', 'opaque', 'private', 'range_of', 'reifiable',
+        'reify', 'reflectable', 'requires', 'set_range_of', 'sub_effect',
+        'synth', 'then', 'total', 'true', 'try', 'type', 'unfold', 'unfoldable',
+        'val', 'when', 'with', 'not'
+    )
+    decl_keywords = ('let', 'rec')
+    assume_keywords = ('assume', 'admit', 'assert', 'calc')
+    keyopts = (
+        r'~', r'-', r'/\\', r'\\/', r'<:', r'<@', r'\(\|', r'\|\)', r'#', r'u#',
+        r'&', r'\(', r'\)', r'\(\)', r',', r'~>', r'->', r'<-', r'<--', r'<==>',
+        r'==>', r'\.', r'\?', r'\?\.', r'\.\[', r'\.\(', r'\.\(\|', r'\.\[\|',
+        r'\{:pattern', r':', r'::', r':=', r';', r';;', r'=', r'%\[', r'!\{',
+        r'\[', r'\[@', r'\[\|', r'\|>', r'\]', r'\|\]', r'\{', r'\|', r'\}', r'\$'
+    )
+
+    operators = r'[!$%&*+\./:<=>?@^|~-]'
+    prefix_syms = r'[!?~]'
+    infix_syms = r'[=<>@^|&+\*/$%-]'
+    primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array')
+
+    tokens = {
+        'escape-sequence': [
+            (r'\\[\\"\'ntbr]', String.Escape),
+            (r'\\[0-9]{3}', String.Escape),
+            (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        ],
+        'root': [
+            (r'\s+', Text),
+            (r'false|true|False|True|\(\)|\[\]', Name.Builtin.Pseudo),
+            (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'),
+            (r'\b([A-Z][\w\']*)', Name.Class),
+            (r'\(\*(?![)])', Comment, 'comment'),
+            (r'\/\/.+$', Comment),
+            (r'\b({})\b'.format('|'.join(keywords)), Keyword),
+            (r'\b({})\b'.format('|'.join(assume_keywords)), Name.Exception),
+            (r'\b({})\b'.format('|'.join(decl_keywords)), Keyword.Declaration),
+            (r'({})'.format('|'.join(keyopts[::-1])), Operator),
+            (rf'({infix_syms}|{prefix_syms})?{operators}', Operator),
+            (r'\b({})\b'.format('|'.join(primitives)), Keyword.Type),
+
+            (r"[^\W\d][\w']*", Name),
+
+            (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float),
+            (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex),
+            (r'0[oO][0-7][0-7_]*', Number.Oct),
+            (r'0[bB][01][01_]*', Number.Bin),
+            (r'\d[\d_]*', Number.Integer),
+
+            (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'",
+             String.Char),
+            (r"'.'", String.Char),
+            (r"'", Keyword),  # a stray quote is another syntax element
+            (r"\`([\w\'.]+)\`", Operator.Word),  # for infix applications
+            (r"\`", Keyword),  # for quoting
+            (r'"', String.Double, 'string'),
+
+            (r'[~?][a-z][\w\']*:', Name.Variable),
+        ],
+        'comment': [
+            (r'[^(*)]+', Comment),
+            (r'\(\*', Comment, '#push'),
+            (r'\*\)', Comment, '#pop'),
+            (r'[(*)]', Comment),
+        ],
+        'string': [
+            (r'[^\\"]+', String.Double),
+            include('escape-sequence'),
+            (r'\\\n', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'dotted': [
+            (r'\s+', Text),
+            (r'\.', Punctuation),
+            (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace),
+            (r'[A-Z][\w\']*', Name.Class, '#pop'),
+            (r'[a-z_][\w\']*', Name, '#pop'),
+            default('#pop'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/modeling.py b/.venv/lib/python3.12/site-packages/pygments/lexers/modeling.py
new file mode 100644
index 0000000..4c22aeb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/modeling.py
@@ -0,0 +1,368 @@
+"""
+    pygments.lexers.modeling
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for modeling languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, using, default
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+
+from pygments.lexers.html import HtmlLexer
+from pygments.lexers import _stan_builtins
+
+__all__ = ['ModelicaLexer', 'BugsLexer', 'JagsLexer', 'StanLexer']
+
+
+class ModelicaLexer(RegexLexer):
+    """
+    For Modelica source code.
+    """
+    name = 'Modelica'
+    url = 'http://www.modelica.org/'
+    aliases = ['modelica']
+    filenames = ['*.mo']
+    mimetypes = ['text/x-modelica']
+    version_added = '1.1'
+
+    flags = re.DOTALL | re.MULTILINE
+
+    _name = r"(?:'(?:[^\\']|\\.)+'|[a-zA-Z_]\w*)"
+
+    tokens = {
+        'whitespace': [
+            (r'[\s\ufeff]+', Text),
+            (r'//[^\n]*\n?', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline)
+        ],
+        'root': [
+            include('whitespace'),
+            (r'"', String.Double, 'string'),
+            (r'[()\[\]{},;]+', Punctuation),
+            (r'\.?[*^/+-]|\.|<>|[<>:=]=?', Operator),
+            (r'\d+(\.?\d*[eE][-+]?\d+|\.\d*)', Number.Float),
+            (r'\d+', Number.Integer),
+            (r'(abs|acos|actualStream|array|asin|assert|AssertionLevel|atan|'
+             r'atan2|backSample|Boolean|cardinality|cat|ceil|change|Clock|'
+             r'Connections|cos|cosh|cross|delay|diagonal|div|edge|exp|'
+             r'ExternalObject|fill|floor|getInstanceName|hold|homotopy|'
+             r'identity|inStream|integer|Integer|interval|inverse|isPresent|'
+             r'linspace|log|log10|matrix|max|min|mod|ndims|noClock|noEvent|'
+             r'ones|outerProduct|pre|previous|product|Real|reinit|rem|rooted|'
+             r'sample|scalar|semiLinear|shiftSample|sign|sin|sinh|size|skew|'
+             r'smooth|spatialDistribution|sqrt|StateSelect|String|subSample|'
+             r'sum|superSample|symmetric|tan|tanh|terminal|terminate|time|'
+             r'transpose|vector|zeros)\b', Name.Builtin),
+            (r'(algorithm|annotation|break|connect|constant|constrainedby|der|'
+             r'discrete|each|else|elseif|elsewhen|encapsulated|enumeration|'
+             r'equation|exit|expandable|extends|external|firstTick|final|flow|for|if|'
+             r'import|impure|in|initial|inner|input|interval|loop|nondiscrete|outer|'
+             r'output|parameter|partial|protected|public|pure|redeclare|'
+             r'replaceable|return|stream|then|when|while)\b',
+             Keyword.Reserved),
+            (r'(and|not|or)\b', Operator.Word),
+            (r'(block|class|connector|end|function|model|operator|package|'
+             r'record|type)\b', Keyword.Reserved, 'class'),
+            (r'(false|true)\b', Keyword.Constant),
+            (r'within\b', Keyword.Reserved, 'package-prefix'),
+            (_name, Name)
+        ],
+        'class': [
+            include('whitespace'),
+            (r'(function|record)\b', Keyword.Reserved),
+            (r'(if|for|when|while)\b', Keyword.Reserved, '#pop'),
+            (_name, Name.Class, '#pop'),
+            default('#pop')
+        ],
+        'package-prefix': [
+            include('whitespace'),
+            (_name, Name.Namespace, '#pop'),
+            default('#pop')
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'\\[\'"?\\abfnrtv]', String.Escape),
+            (r'(?i)<\s*html\s*>([^\\"]|\\.)+?(<\s*/\s*html\s*>|(?="))',
+             using(HtmlLexer)),
+            (r'<|\\?[^"\\<]+', String.Double)
+        ]
+    }
+
+
+class BugsLexer(RegexLexer):
+    """
+    Pygments Lexer for OpenBugs and WinBugs
+    models.
+    """
+
+    name = 'BUGS'
+    aliases = ['bugs', 'winbugs', 'openbugs']
+    filenames = ['*.bug']
+    url = 'https://www.mrc-bsu.cam.ac.uk/software/bugs/openbugs'
+    version_added = '1.6'
+
+    _FUNCTIONS = (
+        # Scalar functions
+        'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
+        'cloglog', 'cos', 'cosh', 'cumulative', 'cut', 'density', 'deviance',
+        'equals', 'expr', 'gammap', 'ilogit', 'icloglog', 'integral', 'log',
+        'logfact', 'loggam', 'logit', 'max', 'min', 'phi', 'post.p.value',
+        'pow', 'prior.p.value', 'probit', 'replicate.post', 'replicate.prior',
+        'round', 'sin', 'sinh', 'solution', 'sqrt', 'step', 'tan', 'tanh',
+        'trunc',
+        # Vector functions
+        'inprod', 'interp.lin', 'inverse', 'logdet', 'mean', 'eigen.vals',
+        'ode', 'prod', 'p.valueM', 'rank', 'ranked', 'replicate.postM',
+        'sd', 'sort', 'sum',
+        # Special
+        'D', 'I', 'F', 'T', 'C')
+    """ OpenBUGS built-in functions
+
+    From http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAII
+
+    This also includes
+
+    - T, C, I : Truncation and censoring.
+      ``T`` and ``C`` are in OpenBUGS. ``I`` in WinBUGS.
+    - D : ODE
+    - F : Functional http://www.openbugs.info/Examples/Functionals.html
+
+    """
+
+    _DISTRIBUTIONS = ('dbern', 'dbin', 'dcat', 'dnegbin', 'dpois',
+                      'dhyper', 'dbeta', 'dchisqr', 'ddexp', 'dexp',
+                      'dflat', 'dgamma', 'dgev', 'df', 'dggamma', 'dgpar',
+                      'dloglik', 'dlnorm', 'dlogis', 'dnorm', 'dpar',
+                      'dt', 'dunif', 'dweib', 'dmulti', 'ddirch', 'dmnorm',
+                      'dmt', 'dwish')
+    """ OpenBUGS built-in distributions
+
+    Functions from
+    http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAI
+    """
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'comments': [
+            # Comments
+            (r'#.*$', Comment.Single),
+        ],
+        'root': [
+            # Comments
+            include('comments'),
+            include('whitespace'),
+            # Block start
+            (r'(model)(\s+)(\{)',
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            # Reserved Words
+            (r'(for|in)(?![\w.])', Keyword.Reserved),
+            # Built-in Functions
+            (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS + _DISTRIBUTIONS)),
+             Name.Builtin),
+            # Regular variable names
+            (r'[A-Za-z][\w.]*', Name),
+            # Number Literals
+            (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
+            # Punctuation
+            (r'\[|\]|\(|\)|:|,|;', Punctuation),
+            # Assignment operators
+            # SLexer makes these tokens Operators.
+            (r'<-|~', Operator),
+            # Infix and prefix operators
+            (r'\+|-|\*|/', Operator),
+            # Block
+            (r'[{}]', Punctuation),
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r"^\s*model\s*{", text, re.M):
+            return 0.7
+        else:
+            return 0.0
+
+
+class JagsLexer(RegexLexer):
+    """
+    Pygments Lexer for JAGS.
+    """
+
+    name = 'JAGS'
+    aliases = ['jags']
+    filenames = ['*.jag', '*.bug']
+    url = 'https://mcmc-jags.sourceforge.io'
+    version_added = '1.6'
+
+    # JAGS
+    _FUNCTIONS = (
+        'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
+        'cos', 'cosh', 'cloglog',
+        'equals', 'exp', 'icloglog', 'ifelse', 'ilogit', 'log', 'logfact',
+        'loggam', 'logit', 'phi', 'pow', 'probit', 'round', 'sin', 'sinh',
+        'sqrt', 'step', 'tan', 'tanh', 'trunc', 'inprod', 'interp.lin',
+        'logdet', 'max', 'mean', 'min', 'prod', 'sum', 'sd', 'inverse',
+        'rank', 'sort', 't', 'acos', 'acosh', 'asin', 'asinh', 'atan',
+        # Truncation/Censoring (should I include)
+        'T', 'I')
+    # Distributions with density, probability and quartile functions
+    _DISTRIBUTIONS = tuple(f'[dpq]{x}' for x in
+                           ('bern', 'beta', 'dchiqsqr', 'ddexp', 'dexp',
+                            'df', 'gamma', 'gen.gamma', 'logis', 'lnorm',
+                            'negbin', 'nchisqr', 'norm', 'par', 'pois', 'weib'))
+    # Other distributions without density and probability
+    _OTHER_DISTRIBUTIONS = (
+        'dt', 'dunif', 'dbetabin', 'dbern', 'dbin', 'dcat', 'dhyper',
+        'ddirch', 'dmnorm', 'dwish', 'dmt', 'dmulti', 'dbinom', 'dchisq',
+        'dnbinom', 'dweibull', 'ddirich')
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'names': [
+            # Regular variable names
+            (r'[a-zA-Z][\w.]*\b', Name),
+        ],
+        'comments': [
+            # do not use stateful comments
+            (r'(?s)/\*.*?\*/', Comment.Multiline),
+            # Comments
+            (r'#.*$', Comment.Single),
+        ],
+        'root': [
+            # Comments
+            include('comments'),
+            include('whitespace'),
+            # Block start
+            (r'(model|data)(\s+)(\{)',
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            (r'var(?![\w.])', Keyword.Declaration),
+            # Reserved Words
+            (r'(for|in)(?![\w.])', Keyword.Reserved),
+            # Builtins
+            # Need to use lookahead because . is a valid char
+            (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS
+                                          + _DISTRIBUTIONS
+                                          + _OTHER_DISTRIBUTIONS)),
+             Name.Builtin),
+            # Names
+            include('names'),
+            # Number Literals
+            (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
+            (r'\[|\]|\(|\)|:|,|;', Punctuation),
+            # Assignment operators
+            (r'<-|~', Operator),
+            # # JAGS includes many more than OpenBUGS
+            (r'\+|-|\*|\/|\|\|[&]{2}|[<>=]=?|\^|%.*?%', Operator),
+            (r'[{}]', Punctuation),
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r'^\s*model\s*\{', text, re.M):
+            if re.search(r'^\s*data\s*\{', text, re.M):
+                return 0.9
+            elif re.search(r'^\s*var', text, re.M):
+                return 0.9
+            else:
+                return 0.3
+        else:
+            return 0
+
+
+class StanLexer(RegexLexer):
+    """Pygments Lexer for Stan models.
+
+    The Stan modeling language is specified in the *Stan Modeling Language
+    User's Guide and Reference Manual, v2.17.0*,
+    `pdf `__.
+    """
+
+    name = 'Stan'
+    aliases = ['stan']
+    filenames = ['*.stan']
+    url = 'https://mc-stan.org'
+    version_added = '1.6'
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'comments': [
+            (r'(?s)/\*.*?\*/', Comment.Multiline),
+            # Comments
+            (r'(//|#).*$', Comment.Single),
+        ],
+        'root': [
+            (r'"[^"]*"', String),
+            # Comments
+            include('comments'),
+            # block start
+            include('whitespace'),
+            # Block start
+            (r'({})(\s*)(\{{)'.format(r'|'.join(('functions', 'data', r'transformed\s+?data',
+                        'parameters', r'transformed\s+parameters',
+                        'model', r'generated\s+quantities'))),
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            # target keyword
+            (r'target\s*\+=', Keyword),
+            # jacobian += statement
+            (r'jacobian\s*\+=', Keyword),
+            # Reserved Words
+            (r'({})\b'.format(r'|'.join(_stan_builtins.KEYWORDS)), Keyword),
+            # Truncation
+            (r'T(?=\s*\[)', Keyword),
+            # Data types
+            (r'({})\b'.format(r'|'.join(_stan_builtins.TYPES)), Keyword.Type),
+             # < should be punctuation, but elsewhere I can't tell if it is in
+             # a range constraint
+            (r'(<)(\s*)(upper|lower|offset|multiplier)(\s*)(=)',
+             bygroups(Operator, Whitespace, Keyword, Whitespace, Punctuation)),
+            (r'(,)(\s*)(upper)(\s*)(=)',
+             bygroups(Punctuation, Whitespace, Keyword, Whitespace, Punctuation)),
+            # Punctuation
+            (r"[;,\[\]()]", Punctuation),
+            # Builtin
+            (r'({})(?=\s*\()'.format('|'.join(_stan_builtins.FUNCTIONS)), Name.Builtin),
+            (r'(~)(\s*)({})(?=\s*\()'.format('|'.join(_stan_builtins.DISTRIBUTIONS)),
+                bygroups(Operator, Whitespace, Name.Builtin)),
+            # Special names ending in __, like lp__
+            (r'[A-Za-z]\w*__\b', Name.Builtin.Pseudo),
+            (r'({})\b'.format(r'|'.join(_stan_builtins.RESERVED)), Keyword.Reserved),
+            # user-defined functions
+            (r'[A-Za-z]\w*(?=\s*\()]', Name.Function),
+            # Imaginary Literals
+            (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?i', Number.Float),
+            (r'\.[0-9]+([eE][+-]?[0-9]+)?i', Number.Float),
+            (r'[0-9]+i', Number.Float),
+            # Real Literals
+            (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?', Number.Float),
+            (r'\.[0-9]+([eE][+-]?[0-9]+)?', Number.Float),
+            # Integer Literals
+            (r'[0-9]+', Number.Integer),
+            # Regular variable names
+            (r'[A-Za-z]\w*\b', Name),
+            # Assignment operators
+            (r'<-|(?:\+|-|\.?/|\.?\*|=)?=|~', Operator),
+            # Infix, prefix and postfix operators (and = )
+            (r"\+|-|\.?\*|\.?/|\\|'|\.?\^|!=?|<=?|>=?|\|\||&&|%|\?|:|%/%|!", Operator),
+            # Block delimiters
+            (r'[{}]', Punctuation),
+            # Distribution |
+            (r'\|', Punctuation)
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r'^\s*parameters\s*\{', text, re.M):
+            return 1.0
+        else:
+            return 0.0
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/modula2.py b/.venv/lib/python3.12/site-packages/pygments/lexers/modula2.py
new file mode 100644
index 0000000..c0ebd5c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/modula2.py
@@ -0,0 +1,1579 @@
+"""
+    pygments.lexers.modula2
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Multi-Dialect Lexer for Modula-2.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include
+from pygments.util import get_bool_opt, get_list_opt
+from pygments.token import Text, Comment, Operator, Keyword, Name, \
+    String, Number, Punctuation, Error
+
+__all__ = ['Modula2Lexer']
+
+
+# Multi-Dialect Modula-2 Lexer
+class Modula2Lexer(RegexLexer):
+    """
+    For Modula-2 source code.
+
+    The Modula-2 lexer supports several dialects.  By default, it operates in
+    fallback mode, recognising the *combined* literals, punctuation symbols
+    and operators of all supported dialects, and the *combined* reserved words
+    and builtins of PIM Modula-2, ISO Modula-2 and Modula-2 R10, while not
+    differentiating between library defined identifiers.
+
+    To select a specific dialect, a dialect option may be passed
+    or a dialect tag may be embedded into a source file.
+
+    Dialect Options:
+
+    `m2pim`
+        Select PIM Modula-2 dialect.
+    `m2iso`
+        Select ISO Modula-2 dialect.
+    `m2r10`
+        Select Modula-2 R10 dialect.
+    `objm2`
+        Select Objective Modula-2 dialect.
+
+    The PIM and ISO dialect options may be qualified with a language extension.
+
+    Language Extensions:
+
+    `+aglet`
+        Select Aglet Modula-2 extensions, available with m2iso.
+    `+gm2`
+        Select GNU Modula-2 extensions, available with m2pim.
+    `+p1`
+        Select p1 Modula-2 extensions, available with m2iso.
+    `+xds`
+        Select XDS Modula-2 extensions, available with m2iso.
+
+
+    Passing a Dialect Option via Unix Commandline Interface
+
+    Dialect options may be passed to the lexer using the `dialect` key.
+    Only one such option should be passed. If multiple dialect options are
+    passed, the first valid option is used, any subsequent options are ignored.
+
+    Examples:
+
+    `$ pygmentize -O full,dialect=m2iso -f html -o /path/to/output /path/to/input`
+        Use ISO dialect to render input to HTML output
+    `$ pygmentize -O full,dialect=m2iso+p1 -f rtf -o /path/to/output /path/to/input`
+        Use ISO dialect with p1 extensions to render input to RTF output
+
+
+    Embedding a Dialect Option within a source file
+
+    A dialect option may be embedded in a source file in form of a dialect
+    tag, a specially formatted comment that specifies a dialect option.
+
+    Dialect Tag EBNF::
+
+       dialectTag :
+           OpeningCommentDelim Prefix dialectOption ClosingCommentDelim ;
+
+       dialectOption :
+           'm2pim' | 'm2iso' | 'm2r10' | 'objm2' |
+           'm2iso+aglet' | 'm2pim+gm2' | 'm2iso+p1' | 'm2iso+xds' ;
+
+       Prefix : '!' ;
+
+       OpeningCommentDelim : '(*' ;
+
+       ClosingCommentDelim : '*)' ;
+
+    No whitespace is permitted between the tokens of a dialect tag.
+
+    In the event that a source file contains multiple dialect tags, the first
+    tag that contains a valid dialect option will be used and any subsequent
+    dialect tags will be ignored.  Ideally, a dialect tag should be placed
+    at the beginning of a source file.
+
+    An embedded dialect tag overrides a dialect option set via command line.
+
+    Examples:
+
+    ``(*!m2r10*) DEFINITION MODULE Foobar; ...``
+        Use Modula2 R10 dialect to render this source file.
+    ``(*!m2pim+gm2*) DEFINITION MODULE Bazbam; ...``
+        Use PIM dialect with GNU extensions to render this source file.
+
+
+    Algol Publication Mode:
+
+    In Algol publication mode, source text is rendered for publication of
+    algorithms in scientific papers and academic texts, following the format
+    of the Revised Algol-60 Language Report.  It is activated by passing
+    one of two corresponding styles as an option:
+
+    `algol`
+        render reserved words lowercase underline boldface
+        and builtins lowercase boldface italic
+    `algol_nu`
+        render reserved words lowercase boldface (no underlining)
+        and builtins lowercase boldface italic
+
+    The lexer automatically performs the required lowercase conversion when
+    this mode is activated.
+
+    Example:
+
+    ``$ pygmentize -O full,style=algol -f latex -o /path/to/output /path/to/input``
+        Render input file in Algol publication mode to LaTeX output.
+
+
+    Rendering Mode of First Class ADT Identifiers:
+
+    The rendering of standard library first class ADT identifiers is controlled
+    by option flag "treat_stdlib_adts_as_builtins".
+
+    When this option is turned on, standard library ADT identifiers are rendered
+    as builtins.  When it is turned off, they are rendered as ordinary library
+    identifiers.
+
+    `treat_stdlib_adts_as_builtins` (default: On)
+
+    The option is useful for dialects that support ADTs as first class objects
+    and provide ADTs in the standard library that would otherwise be built-in.
+
+    At present, only Modula-2 R10 supports library ADTs as first class objects
+    and therefore, no ADT identifiers are defined for any other dialects.
+
+    Example:
+
+    ``$ pygmentize -O full,dialect=m2r10,treat_stdlib_adts_as_builtins=Off ...``
+        Render standard library ADTs as ordinary library types.
+
+    .. versionchanged:: 2.1
+       Added multi-dialect support.
+    """
+    name = 'Modula-2'
+    url = 'http://www.modula2.org/'
+    aliases = ['modula2', 'm2']
+    filenames = ['*.def', '*.mod']
+    mimetypes = ['text/x-modula2']
+    version_added = '1.3'
+
+    flags = re.MULTILINE | re.DOTALL
+
+    tokens = {
+        'whitespace': [
+            (r'\n+', Text),  # blank lines
+            (r'\s+', Text),  # whitespace
+        ],
+        'dialecttags': [
+            # PIM Dialect Tag
+            (r'\(\*!m2pim\*\)', Comment.Special),
+            # ISO Dialect Tag
+            (r'\(\*!m2iso\*\)', Comment.Special),
+            # M2R10 Dialect Tag
+            (r'\(\*!m2r10\*\)', Comment.Special),
+            # ObjM2 Dialect Tag
+            (r'\(\*!objm2\*\)', Comment.Special),
+            # Aglet Extensions Dialect Tag
+            (r'\(\*!m2iso\+aglet\*\)', Comment.Special),
+            # GNU Extensions Dialect Tag
+            (r'\(\*!m2pim\+gm2\*\)', Comment.Special),
+            # p1 Extensions Dialect Tag
+            (r'\(\*!m2iso\+p1\*\)', Comment.Special),
+            # XDS Extensions Dialect Tag
+            (r'\(\*!m2iso\+xds\*\)', Comment.Special),
+        ],
+        'identifiers': [
+            (r'([a-zA-Z_$][\w$]*)', Name),
+        ],
+        'prefixed_number_literals': [
+            #
+            # Base-2, whole number
+            (r'0b[01]+(\'[01]+)*', Number.Bin),
+            #
+            # Base-16, whole number
+            (r'0[ux][0-9A-F]+(\'[0-9A-F]+)*', Number.Hex),
+        ],
+        'plain_number_literals': [
+            #
+            # Base-10, real number with exponent
+            (r'[0-9]+(\'[0-9]+)*'  # integral part
+             r'\.[0-9]+(\'[0-9]+)*'  # fractional part
+             r'[eE][+-]?[0-9]+(\'[0-9]+)*',  # exponent
+             Number.Float),
+            #
+            # Base-10, real number without exponent
+            (r'[0-9]+(\'[0-9]+)*'  # integral part
+             r'\.[0-9]+(\'[0-9]+)*',  # fractional part
+             Number.Float),
+            #
+            # Base-10, whole number
+            (r'[0-9]+(\'[0-9]+)*', Number.Integer),
+        ],
+        'suffixed_number_literals': [
+            #
+            # Base-8, whole number
+            (r'[0-7]+B', Number.Oct),
+            #
+            # Base-8, character code
+            (r'[0-7]+C', Number.Oct),
+            #
+            # Base-16, number
+            (r'[0-9A-F]+H', Number.Hex),
+        ],
+        'string_literals': [
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+        ],
+        'digraph_operators': [
+            # Dot Product Operator
+            (r'\*\.', Operator),
+            # Array Concatenation Operator
+            (r'\+>', Operator),  # M2R10 + ObjM2
+            # Inequality Operator
+            (r'<>', Operator),  # ISO + PIM
+            # Less-Or-Equal, Subset
+            (r'<=', Operator),
+            # Greater-Or-Equal, Superset
+            (r'>=', Operator),
+            # Identity Operator
+            (r'==', Operator),  # M2R10 + ObjM2
+            # Type Conversion Operator
+            (r'::', Operator),  # M2R10 + ObjM2
+            # Assignment Symbol
+            (r':=', Operator),
+            # Postfix Increment Mutator
+            (r'\+\+', Operator),  # M2R10 + ObjM2
+            # Postfix Decrement Mutator
+            (r'--', Operator),  # M2R10 + ObjM2
+        ],
+        'unigraph_operators': [
+            # Arithmetic Operators
+            (r'[+-]', Operator),
+            (r'[*/]', Operator),
+            # ISO 80000-2 compliant Set Difference Operator
+            (r'\\', Operator),  # M2R10 + ObjM2
+            # Relational Operators
+            (r'[=#<>]', Operator),
+            # Dereferencing Operator
+            (r'\^', Operator),
+            # Dereferencing Operator Synonym
+            (r'@', Operator),  # ISO
+            # Logical AND Operator Synonym
+            (r'&', Operator),  # PIM + ISO
+            # Logical NOT Operator Synonym
+            (r'~', Operator),  # PIM + ISO
+            # Smalltalk Message Prefix
+            (r'`', Operator),  # ObjM2
+        ],
+        'digraph_punctuation': [
+            # Range Constructor
+            (r'\.\.', Punctuation),
+            # Opening Chevron Bracket
+            (r'<<', Punctuation),  # M2R10 + ISO
+            # Closing Chevron Bracket
+            (r'>>', Punctuation),  # M2R10 + ISO
+            # Blueprint Punctuation
+            (r'->', Punctuation),  # M2R10 + ISO
+            # Distinguish |# and # in M2 R10
+            (r'\|#', Punctuation),
+            # Distinguish ## and # in M2 R10
+            (r'##', Punctuation),
+            # Distinguish |* and * in M2 R10
+            (r'\|\*', Punctuation),
+        ],
+        'unigraph_punctuation': [
+            # Common Punctuation
+            (r'[()\[\]{},.:;|]', Punctuation),
+            # Case Label Separator Synonym
+            (r'!', Punctuation),  # ISO
+            # Blueprint Punctuation
+            (r'\?', Punctuation),  # M2R10 + ObjM2
+        ],
+        'comments': [
+            # Single Line Comment
+            (r'^//.*?\n', Comment.Single),  # M2R10 + ObjM2
+            # Block Comment
+            (r'\(\*([^$].*?)\*\)', Comment.Multiline),
+            # Template Block Comment
+            (r'/\*(.*?)\*/', Comment.Multiline),  # M2R10 + ObjM2
+        ],
+        'pragmas': [
+            # ISO Style Pragmas
+            (r'<\*.*?\*>', Comment.Preproc),  # ISO, M2R10 + ObjM2
+            # Pascal Style Pragmas
+            (r'\(\*\$.*?\*\)', Comment.Preproc),  # PIM
+        ],
+        'root': [
+            include('whitespace'),
+            include('dialecttags'),
+            include('pragmas'),
+            include('comments'),
+            include('identifiers'),
+            include('suffixed_number_literals'),  # PIM + ISO
+            include('prefixed_number_literals'),  # M2R10 + ObjM2
+            include('plain_number_literals'),
+            include('string_literals'),
+            include('digraph_punctuation'),
+            include('digraph_operators'),
+            include('unigraph_punctuation'),
+            include('unigraph_operators'),
+        ]
+    }
+
+#  C o m m o n   D a t a s e t s
+
+    # Common Reserved Words Dataset
+    common_reserved_words = (
+        # 37 common reserved words
+        'AND', 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION', 'DIV',
+        'DO', 'ELSE', 'ELSIF', 'END', 'EXIT', 'FOR', 'FROM', 'IF',
+        'IMPLEMENTATION', 'IMPORT', 'IN', 'LOOP', 'MOD', 'MODULE', 'NOT',
+        'OF', 'OR', 'POINTER', 'PROCEDURE', 'RECORD', 'REPEAT', 'RETURN',
+        'SET', 'THEN', 'TO', 'TYPE', 'UNTIL', 'VAR', 'WHILE',
+    )
+
+    # Common Builtins Dataset
+    common_builtins = (
+        # 16 common builtins
+        'ABS', 'BOOLEAN', 'CARDINAL', 'CHAR', 'CHR', 'FALSE', 'INTEGER',
+        'LONGINT', 'LONGREAL', 'MAX', 'MIN', 'NIL', 'ODD', 'ORD', 'REAL',
+        'TRUE',
+    )
+
+    # Common Pseudo-Module Builtins Dataset
+    common_pseudo_builtins = (
+        # 4 common pseudo builtins
+        'ADDRESS', 'BYTE', 'WORD', 'ADR'
+    )
+
+#  P I M   M o d u l a - 2   D a t a s e t s
+
+    # Lexemes to Mark as Error Tokens for PIM Modula-2
+    pim_lexemes_to_reject = (
+        '!', '`', '@', '$', '%', '?', '\\', '==', '++', '--', '::', '*.',
+        '+>', '->', '<<', '>>', '|#', '##',
+    )
+
+    # PIM Modula-2 Additional Reserved Words Dataset
+    pim_additional_reserved_words = (
+        # 3 additional reserved words
+        'EXPORT', 'QUALIFIED', 'WITH',
+    )
+
+    # PIM Modula-2 Additional Builtins Dataset
+    pim_additional_builtins = (
+        # 16 additional builtins
+        'BITSET', 'CAP', 'DEC', 'DISPOSE', 'EXCL', 'FLOAT', 'HALT', 'HIGH',
+        'INC', 'INCL', 'NEW', 'NIL', 'PROC', 'SIZE', 'TRUNC', 'VAL',
+    )
+
+    # PIM Modula-2 Additional Pseudo-Module Builtins Dataset
+    pim_additional_pseudo_builtins = (
+        # 5 additional pseudo builtins
+        'SYSTEM', 'PROCESS', 'TSIZE', 'NEWPROCESS', 'TRANSFER',
+    )
+
+#  I S O   M o d u l a - 2   D a t a s e t s
+
+    # Lexemes to Mark as Error Tokens for ISO Modula-2
+    iso_lexemes_to_reject = (
+        '`', '$', '%', '?', '\\', '==', '++', '--', '::', '*.', '+>', '->',
+        '<<', '>>', '|#', '##',
+    )
+
+    # ISO Modula-2 Additional Reserved Words Dataset
+    iso_additional_reserved_words = (
+        # 9 additional reserved words (ISO 10514-1)
+        'EXCEPT', 'EXPORT', 'FINALLY', 'FORWARD', 'PACKEDSET', 'QUALIFIED',
+        'REM', 'RETRY', 'WITH',
+        # 10 additional reserved words (ISO 10514-2 & ISO 10514-3)
+        'ABSTRACT', 'AS', 'CLASS', 'GUARD', 'INHERIT', 'OVERRIDE', 'READONLY',
+        'REVEAL', 'TRACED', 'UNSAFEGUARDED',
+    )
+
+    # ISO Modula-2 Additional Builtins Dataset
+    iso_additional_builtins = (
+        # 26 additional builtins (ISO 10514-1)
+        'BITSET', 'CAP', 'CMPLX', 'COMPLEX', 'DEC', 'DISPOSE', 'EXCL', 'FLOAT',
+        'HALT', 'HIGH', 'IM', 'INC', 'INCL', 'INT', 'INTERRUPTIBLE',  'LENGTH',
+        'LFLOAT', 'LONGCOMPLEX', 'NEW', 'PROC', 'PROTECTION', 'RE', 'SIZE',
+        'TRUNC', 'UNINTERRUBTIBLE', 'VAL',
+        # 5 additional builtins (ISO 10514-2 & ISO 10514-3)
+        'CREATE', 'DESTROY', 'EMPTY', 'ISMEMBER', 'SELF',
+    )
+
+    # ISO Modula-2 Additional Pseudo-Module Builtins Dataset
+    iso_additional_pseudo_builtins = (
+        # 14 additional builtins (SYSTEM)
+        'SYSTEM', 'BITSPERLOC', 'LOCSPERBYTE', 'LOCSPERWORD', 'LOC',
+        'ADDADR', 'SUBADR', 'DIFADR', 'MAKEADR', 'ADR',
+        'ROTATE', 'SHIFT', 'CAST', 'TSIZE',
+        # 13 additional builtins (COROUTINES)
+        'COROUTINES', 'ATTACH', 'COROUTINE', 'CURRENT', 'DETACH', 'HANDLER',
+        'INTERRUPTSOURCE', 'IOTRANSFER', 'IsATTACHED', 'LISTEN',
+        'NEWCOROUTINE', 'PROT', 'TRANSFER',
+        # 9 additional builtins (EXCEPTIONS)
+        'EXCEPTIONS', 'AllocateSource', 'CurrentNumber', 'ExceptionNumber',
+        'ExceptionSource', 'GetMessage', 'IsCurrentSource',
+        'IsExceptionalExecution', 'RAISE',
+        # 3 additional builtins (TERMINATION)
+        'TERMINATION', 'IsTerminating', 'HasHalted',
+        # 4 additional builtins (M2EXCEPTION)
+        'M2EXCEPTION', 'M2Exceptions', 'M2Exception', 'IsM2Exception',
+        'indexException', 'rangeException', 'caseSelectException',
+        'invalidLocation', 'functionException', 'wholeValueException',
+        'wholeDivException', 'realValueException', 'realDivException',
+        'complexValueException', 'complexDivException', 'protException',
+        'sysException', 'coException', 'exException',
+    )
+
+#  M o d u l a - 2   R 1 0   D a t a s e t s
+
+    # Lexemes to Mark as Error Tokens for Modula-2 R10
+    m2r10_lexemes_to_reject = (
+        '!', '`', '@', '$', '%', '&', '<>',
+    )
+
+    # Modula-2 R10 reserved words in addition to the common set
+    m2r10_additional_reserved_words = (
+        # 12 additional reserved words
+        'ALIAS', 'ARGLIST', 'BLUEPRINT', 'COPY', 'GENLIB', 'INDETERMINATE',
+        'NEW', 'NONE', 'OPAQUE', 'REFERENTIAL', 'RELEASE', 'RETAIN',
+        # 2 additional reserved words with symbolic assembly option
+        'ASM', 'REG',
+    )
+
+    # Modula-2 R10 builtins in addition to the common set
+    m2r10_additional_builtins = (
+        # 26 additional builtins
+        'CARDINAL', 'COUNT', 'EMPTY', 'EXISTS', 'INSERT', 'LENGTH', 'LONGCARD',
+        'OCTET', 'PTR', 'PRED', 'READ', 'READNEW', 'REMOVE', 'RETRIEVE', 'SORT',
+        'STORE', 'SUBSET', 'SUCC', 'TLIMIT', 'TMAX', 'TMIN', 'TRUE', 'TSIZE',
+        'UNICHAR', 'WRITE', 'WRITEF',
+    )
+
+    # Modula-2 R10 Additional Pseudo-Module Builtins Dataset
+    m2r10_additional_pseudo_builtins = (
+        # 13 additional builtins (TPROPERTIES)
+        'TPROPERTIES', 'PROPERTY', 'LITERAL', 'TPROPERTY', 'TLITERAL',
+        'TBUILTIN', 'TDYN', 'TREFC', 'TNIL', 'TBASE', 'TPRECISION',
+        'TMAXEXP', 'TMINEXP',
+        # 4 additional builtins (CONVERSION)
+        'CONVERSION', 'TSXFSIZE', 'SXF', 'VAL',
+        # 35 additional builtins (UNSAFE)
+        'UNSAFE', 'CAST', 'INTRINSIC', 'AVAIL', 'ADD', 'SUB', 'ADDC', 'SUBC',
+        'FETCHADD', 'FETCHSUB', 'SHL', 'SHR', 'ASHR', 'ROTL', 'ROTR', 'ROTLC',
+        'ROTRC', 'BWNOT', 'BWAND', 'BWOR', 'BWXOR', 'BWNAND', 'BWNOR',
+        'SETBIT', 'TESTBIT', 'LSBIT', 'MSBIT', 'CSBITS', 'BAIL', 'HALT',
+        'TODO', 'FFI', 'ADDR', 'VARGLIST', 'VARGC',
+        # 11 additional builtins (ATOMIC)
+        'ATOMIC', 'INTRINSIC', 'AVAIL', 'SWAP', 'CAS', 'INC', 'DEC', 'BWAND',
+        'BWNAND', 'BWOR', 'BWXOR',
+        # 7 additional builtins (COMPILER)
+        'COMPILER', 'DEBUG', 'MODNAME', 'PROCNAME', 'LINENUM', 'DEFAULT',
+        'HASH',
+        # 5 additional builtins (ASSEMBLER)
+        'ASSEMBLER', 'REGISTER', 'SETREG', 'GETREG', 'CODE',
+    )
+
+#  O b j e c t i v e   M o d u l a - 2   D a t a s e t s
+
+    # Lexemes to Mark as Error Tokens for Objective Modula-2
+    objm2_lexemes_to_reject = (
+        '!', '$', '%', '&', '<>',
+    )
+
+    # Objective Modula-2 Extensions
+    # reserved words in addition to Modula-2 R10
+    objm2_additional_reserved_words = (
+        # 16 additional reserved words
+        'BYCOPY', 'BYREF', 'CLASS', 'CONTINUE', 'CRITICAL', 'INOUT', 'METHOD',
+        'ON', 'OPTIONAL', 'OUT', 'PRIVATE', 'PROTECTED', 'PROTOCOL', 'PUBLIC',
+        'SUPER', 'TRY',
+    )
+
+    # Objective Modula-2 Extensions
+    # builtins in addition to Modula-2 R10
+    objm2_additional_builtins = (
+        # 3 additional builtins
+        'OBJECT', 'NO', 'YES',
+    )
+
+    # Objective Modula-2 Extensions
+    # pseudo-module builtins in addition to Modula-2 R10
+    objm2_additional_pseudo_builtins = (
+        # None
+    )
+
+#  A g l e t   M o d u l a - 2   D a t a s e t s
+
+    # Aglet Extensions
+    # reserved words in addition to ISO Modula-2
+    aglet_additional_reserved_words = (
+        # None
+    )
+
+    # Aglet Extensions
+    # builtins in addition to ISO Modula-2
+    aglet_additional_builtins = (
+        # 9 additional builtins
+        'BITSET8', 'BITSET16', 'BITSET32', 'CARDINAL8', 'CARDINAL16',
+        'CARDINAL32', 'INTEGER8', 'INTEGER16', 'INTEGER32',
+    )
+
+    # Aglet Modula-2 Extensions
+    # pseudo-module builtins in addition to ISO Modula-2
+    aglet_additional_pseudo_builtins = (
+        # None
+    )
+
+#  G N U   M o d u l a - 2   D a t a s e t s
+
+    # GNU Extensions
+    # reserved words in addition to PIM Modula-2
+    gm2_additional_reserved_words = (
+        # 10 additional reserved words
+        'ASM', '__ATTRIBUTE__', '__BUILTIN__', '__COLUMN__', '__DATE__',
+        '__FILE__', '__FUNCTION__', '__LINE__', '__MODULE__', 'VOLATILE',
+    )
+
+    # GNU Extensions
+    # builtins in addition to PIM Modula-2
+    gm2_additional_builtins = (
+        # 21 additional builtins
+        'BITSET8', 'BITSET16', 'BITSET32', 'CARDINAL8', 'CARDINAL16',
+        'CARDINAL32', 'CARDINAL64', 'COMPLEX32', 'COMPLEX64', 'COMPLEX96',
+        'COMPLEX128', 'INTEGER8', 'INTEGER16', 'INTEGER32', 'INTEGER64',
+        'REAL8', 'REAL16', 'REAL32', 'REAL96', 'REAL128', 'THROW',
+    )
+
+    # GNU Extensions
+    # pseudo-module builtins in addition to PIM Modula-2
+    gm2_additional_pseudo_builtins = (
+        # None
+    )
+
+#  p 1   M o d u l a - 2   D a t a s e t s
+
+    # p1 Extensions
+    # reserved words in addition to ISO Modula-2
+    p1_additional_reserved_words = (
+        # None
+    )
+
+    # p1 Extensions
+    # builtins in addition to ISO Modula-2
+    p1_additional_builtins = (
+        # None
+    )
+
+    # p1 Modula-2 Extensions
+    # pseudo-module builtins in addition to ISO Modula-2
+    p1_additional_pseudo_builtins = (
+        # 1 additional builtin
+        'BCD',
+    )
+
+#  X D S   M o d u l a - 2   D a t a s e t s
+
+    # XDS Extensions
+    # reserved words in addition to ISO Modula-2
+    xds_additional_reserved_words = (
+        # 1 additional reserved word
+        'SEQ',
+    )
+
+    # XDS Extensions
+    # builtins in addition to ISO Modula-2
+    xds_additional_builtins = (
+        # 9 additional builtins
+        'ASH', 'ASSERT', 'DIFFADR_TYPE', 'ENTIER', 'INDEX', 'LEN',
+        'LONGCARD', 'SHORTCARD', 'SHORTINT',
+    )
+
+    # XDS Modula-2 Extensions
+    # pseudo-module builtins in addition to ISO Modula-2
+    xds_additional_pseudo_builtins = (
+        # 22 additional builtins (SYSTEM)
+        'PROCESS', 'NEWPROCESS', 'BOOL8', 'BOOL16', 'BOOL32', 'CARD8',
+        'CARD16', 'CARD32', 'INT8', 'INT16', 'INT32', 'REF', 'MOVE',
+        'FILL', 'GET', 'PUT', 'CC', 'int', 'unsigned', 'size_t', 'void'
+        # 3 additional builtins (COMPILER)
+        'COMPILER', 'OPTION', 'EQUATION'
+    )
+
+#  P I M   S t a n d a r d   L i b r a r y   D a t a s e t s
+
+    # PIM Modula-2 Standard Library Modules Dataset
+    pim_stdlib_module_identifiers = (
+        'Terminal', 'FileSystem', 'InOut', 'RealInOut', 'MathLib0', 'Storage',
+    )
+
+    # PIM Modula-2 Standard Library Types Dataset
+    pim_stdlib_type_identifiers = (
+        'Flag', 'FlagSet', 'Response', 'Command', 'Lock', 'Permission',
+        'MediumType', 'File', 'FileProc', 'DirectoryProc', 'FileCommand',
+        'DirectoryCommand',
+    )
+
+    # PIM Modula-2 Standard Library Procedures Dataset
+    pim_stdlib_proc_identifiers = (
+        'Read', 'BusyRead', 'ReadAgain', 'Write', 'WriteString', 'WriteLn',
+        'Create', 'Lookup', 'Close', 'Delete', 'Rename', 'SetRead', 'SetWrite',
+        'SetModify', 'SetOpen', 'Doio', 'SetPos', 'GetPos', 'Length', 'Reset',
+        'Again', 'ReadWord', 'WriteWord', 'ReadChar', 'WriteChar',
+        'CreateMedium', 'DeleteMedium', 'AssignName', 'DeassignName',
+        'ReadMedium', 'LookupMedium', 'OpenInput', 'OpenOutput', 'CloseInput',
+        'CloseOutput', 'ReadString', 'ReadInt', 'ReadCard', 'ReadWrd',
+        'WriteInt', 'WriteCard', 'WriteOct', 'WriteHex', 'WriteWrd',
+        'ReadReal', 'WriteReal', 'WriteFixPt', 'WriteRealOct', 'sqrt', 'exp',
+        'ln', 'sin', 'cos', 'arctan', 'entier', 'ALLOCATE', 'DEALLOCATE',
+    )
+
+    # PIM Modula-2 Standard Library Variables Dataset
+    pim_stdlib_var_identifiers = (
+        'Done', 'termCH', 'in', 'out'
+    )
+
+    # PIM Modula-2 Standard Library Constants Dataset
+    pim_stdlib_const_identifiers = (
+        'EOL',
+    )
+
+#  I S O   S t a n d a r d   L i b r a r y   D a t a s e t s
+
+    # ISO Modula-2 Standard Library Modules Dataset
+    iso_stdlib_module_identifiers = (
+        # TO DO
+    )
+
+    # ISO Modula-2 Standard Library Types Dataset
+    iso_stdlib_type_identifiers = (
+        # TO DO
+    )
+
+    # ISO Modula-2 Standard Library Procedures Dataset
+    iso_stdlib_proc_identifiers = (
+        # TO DO
+    )
+
+    # ISO Modula-2 Standard Library Variables Dataset
+    iso_stdlib_var_identifiers = (
+        # TO DO
+    )
+
+    # ISO Modula-2 Standard Library Constants Dataset
+    iso_stdlib_const_identifiers = (
+        # TO DO
+    )
+
+#  M 2   R 1 0   S t a n d a r d   L i b r a r y   D a t a s e t s
+
+    # Modula-2 R10 Standard Library ADTs Dataset
+    m2r10_stdlib_adt_identifiers = (
+        'BCD', 'LONGBCD', 'BITSET', 'SHORTBITSET', 'LONGBITSET',
+        'LONGLONGBITSET', 'COMPLEX', 'LONGCOMPLEX', 'SHORTCARD', 'LONGLONGCARD',
+        'SHORTINT', 'LONGLONGINT', 'POSINT', 'SHORTPOSINT', 'LONGPOSINT',
+        'LONGLONGPOSINT', 'BITSET8', 'BITSET16', 'BITSET32', 'BITSET64',
+        'BITSET128', 'BS8', 'BS16', 'BS32', 'BS64', 'BS128', 'CARDINAL8',
+        'CARDINAL16', 'CARDINAL32', 'CARDINAL64', 'CARDINAL128', 'CARD8',
+        'CARD16', 'CARD32', 'CARD64', 'CARD128', 'INTEGER8', 'INTEGER16',
+        'INTEGER32', 'INTEGER64', 'INTEGER128', 'INT8', 'INT16', 'INT32',
+        'INT64', 'INT128', 'STRING', 'UNISTRING',
+    )
+
+    # Modula-2 R10 Standard Library Blueprints Dataset
+    m2r10_stdlib_blueprint_identifiers = (
+        'ProtoRoot', 'ProtoComputational', 'ProtoNumeric', 'ProtoScalar',
+        'ProtoNonScalar', 'ProtoCardinal', 'ProtoInteger', 'ProtoReal',
+        'ProtoComplex', 'ProtoVector', 'ProtoTuple', 'ProtoCompArray',
+        'ProtoCollection', 'ProtoStaticArray', 'ProtoStaticSet',
+        'ProtoStaticString', 'ProtoArray', 'ProtoString', 'ProtoSet',
+        'ProtoMultiSet', 'ProtoDictionary', 'ProtoMultiDict', 'ProtoExtension',
+        'ProtoIO', 'ProtoCardMath', 'ProtoIntMath', 'ProtoRealMath',
+    )
+
+    # Modula-2 R10 Standard Library Modules Dataset
+    m2r10_stdlib_module_identifiers = (
+        'ASCII', 'BooleanIO', 'CharIO', 'UnicharIO', 'OctetIO',
+        'CardinalIO', 'LongCardIO', 'IntegerIO', 'LongIntIO', 'RealIO',
+        'LongRealIO', 'BCDIO', 'LongBCDIO', 'CardMath', 'LongCardMath',
+        'IntMath', 'LongIntMath', 'RealMath', 'LongRealMath', 'BCDMath',
+        'LongBCDMath', 'FileIO', 'FileSystem', 'Storage', 'IOSupport',
+    )
+
+    # Modula-2 R10 Standard Library Types Dataset
+    m2r10_stdlib_type_identifiers = (
+        'File', 'Status',
+        # TO BE COMPLETED
+    )
+
+    # Modula-2 R10 Standard Library Procedures Dataset
+    m2r10_stdlib_proc_identifiers = (
+        'ALLOCATE', 'DEALLOCATE', 'SIZE',
+        # TO BE COMPLETED
+    )
+
+    # Modula-2 R10 Standard Library Variables Dataset
+    m2r10_stdlib_var_identifiers = (
+        'stdIn', 'stdOut', 'stdErr',
+    )
+
+    # Modula-2 R10 Standard Library Constants Dataset
+    m2r10_stdlib_const_identifiers = (
+        'pi', 'tau',
+    )
+
+#  D i a l e c t s
+
+    # Dialect modes
+    dialects = (
+        'unknown',
+        'm2pim', 'm2iso', 'm2r10', 'objm2',
+        'm2iso+aglet', 'm2pim+gm2', 'm2iso+p1', 'm2iso+xds',
+    )
+
+#   D a t a b a s e s
+
+    # Lexemes to Mark as Errors Database
+    lexemes_to_reject_db = {
+        # Lexemes to reject for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Lexemes to reject for PIM Modula-2
+        'm2pim': (
+            pim_lexemes_to_reject,
+        ),
+        # Lexemes to reject for ISO Modula-2
+        'm2iso': (
+            iso_lexemes_to_reject,
+        ),
+        # Lexemes to reject for Modula-2 R10
+        'm2r10': (
+            m2r10_lexemes_to_reject,
+        ),
+        # Lexemes to reject for Objective Modula-2
+        'objm2': (
+            objm2_lexemes_to_reject,
+        ),
+        # Lexemes to reject for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_lexemes_to_reject,
+        ),
+        # Lexemes to reject for GNU Modula-2
+        'm2pim+gm2': (
+            pim_lexemes_to_reject,
+        ),
+        # Lexemes to reject for p1 Modula-2
+        'm2iso+p1': (
+            iso_lexemes_to_reject,
+        ),
+        # Lexemes to reject for XDS Modula-2
+        'm2iso+xds': (
+            iso_lexemes_to_reject,
+        ),
+    }
+
+    # Reserved Words Database
+    reserved_words_db = {
+        # Reserved words for unknown dialect
+        'unknown': (
+            common_reserved_words,
+            pim_additional_reserved_words,
+            iso_additional_reserved_words,
+            m2r10_additional_reserved_words,
+        ),
+
+        # Reserved words for PIM Modula-2
+        'm2pim': (
+            common_reserved_words,
+            pim_additional_reserved_words,
+        ),
+
+        # Reserved words for Modula-2 R10
+        'm2iso': (
+            common_reserved_words,
+            iso_additional_reserved_words,
+        ),
+
+        # Reserved words for ISO Modula-2
+        'm2r10': (
+            common_reserved_words,
+            m2r10_additional_reserved_words,
+        ),
+
+        # Reserved words for Objective Modula-2
+        'objm2': (
+            common_reserved_words,
+            m2r10_additional_reserved_words,
+            objm2_additional_reserved_words,
+        ),
+
+        # Reserved words for Aglet Modula-2 Extensions
+        'm2iso+aglet': (
+            common_reserved_words,
+            iso_additional_reserved_words,
+            aglet_additional_reserved_words,
+        ),
+
+        # Reserved words for GNU Modula-2 Extensions
+        'm2pim+gm2': (
+            common_reserved_words,
+            pim_additional_reserved_words,
+            gm2_additional_reserved_words,
+        ),
+
+        # Reserved words for p1 Modula-2 Extensions
+        'm2iso+p1': (
+            common_reserved_words,
+            iso_additional_reserved_words,
+            p1_additional_reserved_words,
+        ),
+
+        # Reserved words for XDS Modula-2 Extensions
+        'm2iso+xds': (
+            common_reserved_words,
+            iso_additional_reserved_words,
+            xds_additional_reserved_words,
+        ),
+    }
+
+    # Builtins Database
+    builtins_db = {
+        # Builtins for unknown dialect
+        'unknown': (
+            common_builtins,
+            pim_additional_builtins,
+            iso_additional_builtins,
+            m2r10_additional_builtins,
+        ),
+
+        # Builtins for PIM Modula-2
+        'm2pim': (
+            common_builtins,
+            pim_additional_builtins,
+        ),
+
+        # Builtins for ISO Modula-2
+        'm2iso': (
+            common_builtins,
+            iso_additional_builtins,
+        ),
+
+        # Builtins for ISO Modula-2
+        'm2r10': (
+            common_builtins,
+            m2r10_additional_builtins,
+        ),
+
+        # Builtins for Objective Modula-2
+        'objm2': (
+            common_builtins,
+            m2r10_additional_builtins,
+            objm2_additional_builtins,
+        ),
+
+        # Builtins for Aglet Modula-2 Extensions
+        'm2iso+aglet': (
+            common_builtins,
+            iso_additional_builtins,
+            aglet_additional_builtins,
+        ),
+
+        # Builtins for GNU Modula-2 Extensions
+        'm2pim+gm2': (
+            common_builtins,
+            pim_additional_builtins,
+            gm2_additional_builtins,
+        ),
+
+        # Builtins for p1 Modula-2 Extensions
+        'm2iso+p1': (
+            common_builtins,
+            iso_additional_builtins,
+            p1_additional_builtins,
+        ),
+
+        # Builtins for XDS Modula-2 Extensions
+        'm2iso+xds': (
+            common_builtins,
+            iso_additional_builtins,
+            xds_additional_builtins,
+        ),
+    }
+
+    # Pseudo-Module Builtins Database
+    pseudo_builtins_db = {
+        # Builtins for unknown dialect
+        'unknown': (
+            common_pseudo_builtins,
+            pim_additional_pseudo_builtins,
+            iso_additional_pseudo_builtins,
+            m2r10_additional_pseudo_builtins,
+        ),
+
+        # Builtins for PIM Modula-2
+        'm2pim': (
+            common_pseudo_builtins,
+            pim_additional_pseudo_builtins,
+        ),
+
+        # Builtins for ISO Modula-2
+        'm2iso': (
+            common_pseudo_builtins,
+            iso_additional_pseudo_builtins,
+        ),
+
+        # Builtins for ISO Modula-2
+        'm2r10': (
+            common_pseudo_builtins,
+            m2r10_additional_pseudo_builtins,
+        ),
+
+        # Builtins for Objective Modula-2
+        'objm2': (
+            common_pseudo_builtins,
+            m2r10_additional_pseudo_builtins,
+            objm2_additional_pseudo_builtins,
+        ),
+
+        # Builtins for Aglet Modula-2 Extensions
+        'm2iso+aglet': (
+            common_pseudo_builtins,
+            iso_additional_pseudo_builtins,
+            aglet_additional_pseudo_builtins,
+        ),
+
+        # Builtins for GNU Modula-2 Extensions
+        'm2pim+gm2': (
+            common_pseudo_builtins,
+            pim_additional_pseudo_builtins,
+            gm2_additional_pseudo_builtins,
+        ),
+
+        # Builtins for p1 Modula-2 Extensions
+        'm2iso+p1': (
+            common_pseudo_builtins,
+            iso_additional_pseudo_builtins,
+            p1_additional_pseudo_builtins,
+        ),
+
+        # Builtins for XDS Modula-2 Extensions
+        'm2iso+xds': (
+            common_pseudo_builtins,
+            iso_additional_pseudo_builtins,
+            xds_additional_pseudo_builtins,
+        ),
+    }
+
+    # Standard Library ADTs Database
+    stdlib_adts_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library ADTs for PIM Modula-2
+        'm2pim': (
+            # No first class library types
+        ),
+
+        # Standard Library ADTs for ISO Modula-2
+        'm2iso': (
+            # No first class library types
+        ),
+
+        # Standard Library ADTs for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_adt_identifiers,
+        ),
+
+        # Standard Library ADTs for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_adt_identifiers,
+        ),
+
+        # Standard Library ADTs for Aglet Modula-2
+        'm2iso+aglet': (
+            # No first class library types
+        ),
+
+        # Standard Library ADTs for GNU Modula-2
+        'm2pim+gm2': (
+            # No first class library types
+        ),
+
+        # Standard Library ADTs for p1 Modula-2
+        'm2iso+p1': (
+            # No first class library types
+        ),
+
+        # Standard Library ADTs for XDS Modula-2
+        'm2iso+xds': (
+            # No first class library types
+        ),
+    }
+
+    # Standard Library Modules Database
+    stdlib_modules_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library Modules for PIM Modula-2
+        'm2pim': (
+            pim_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for ISO Modula-2
+        'm2iso': (
+            iso_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_blueprint_identifiers,
+            m2r10_stdlib_module_identifiers,
+            m2r10_stdlib_adt_identifiers,
+        ),
+
+        # Standard Library Modules for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_blueprint_identifiers,
+            m2r10_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for GNU Modula-2
+        'm2pim+gm2': (
+            pim_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for p1 Modula-2
+        'm2iso+p1': (
+            iso_stdlib_module_identifiers,
+        ),
+
+        # Standard Library Modules for XDS Modula-2
+        'm2iso+xds': (
+            iso_stdlib_module_identifiers,
+        ),
+    }
+
+    # Standard Library Types Database
+    stdlib_types_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library Types for PIM Modula-2
+        'm2pim': (
+            pim_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for ISO Modula-2
+        'm2iso': (
+            iso_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for GNU Modula-2
+        'm2pim+gm2': (
+            pim_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for p1 Modula-2
+        'm2iso+p1': (
+            iso_stdlib_type_identifiers,
+        ),
+
+        # Standard Library Types for XDS Modula-2
+        'm2iso+xds': (
+            iso_stdlib_type_identifiers,
+        ),
+    }
+
+    # Standard Library Procedures Database
+    stdlib_procedures_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library Procedures for PIM Modula-2
+        'm2pim': (
+            pim_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for ISO Modula-2
+        'm2iso': (
+            iso_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for GNU Modula-2
+        'm2pim+gm2': (
+            pim_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for p1 Modula-2
+        'm2iso+p1': (
+            iso_stdlib_proc_identifiers,
+        ),
+
+        # Standard Library Procedures for XDS Modula-2
+        'm2iso+xds': (
+            iso_stdlib_proc_identifiers,
+        ),
+    }
+
+    # Standard Library Variables Database
+    stdlib_variables_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library Variables for PIM Modula-2
+        'm2pim': (
+            pim_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for ISO Modula-2
+        'm2iso': (
+            iso_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for GNU Modula-2
+        'm2pim+gm2': (
+            pim_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for p1 Modula-2
+        'm2iso+p1': (
+            iso_stdlib_var_identifiers,
+        ),
+
+        # Standard Library Variables for XDS Modula-2
+        'm2iso+xds': (
+            iso_stdlib_var_identifiers,
+        ),
+    }
+
+    # Standard Library Constants Database
+    stdlib_constants_db = {
+        # Empty entry for unknown dialect
+        'unknown': (
+            # LEAVE THIS EMPTY
+        ),
+        # Standard Library Constants for PIM Modula-2
+        'm2pim': (
+            pim_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for ISO Modula-2
+        'm2iso': (
+            iso_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for Modula-2 R10
+        'm2r10': (
+            m2r10_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for Objective Modula-2
+        'objm2': (
+            m2r10_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for Aglet Modula-2
+        'm2iso+aglet': (
+            iso_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for GNU Modula-2
+        'm2pim+gm2': (
+            pim_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for p1 Modula-2
+        'm2iso+p1': (
+            iso_stdlib_const_identifiers,
+        ),
+
+        # Standard Library Constants for XDS Modula-2
+        'm2iso+xds': (
+            iso_stdlib_const_identifiers,
+        ),
+    }
+
+#   M e t h o d s
+
+    # initialise a lexer instance
+    def __init__(self, **options):
+        #
+        # check dialect options
+        #
+        dialects = get_list_opt(options, 'dialect', [])
+        #
+        for dialect_option in dialects:
+            if dialect_option in self.dialects[1:-1]:
+                # valid dialect option found
+                self.set_dialect(dialect_option)
+                break
+        #
+        # Fallback Mode (DEFAULT)
+        else:
+            # no valid dialect option
+            self.set_dialect('unknown')
+        #
+        self.dialect_set_by_tag = False
+        #
+        # check style options
+        #
+        styles = get_list_opt(options, 'style', [])
+        #
+        # use lowercase mode for Algol style
+        if 'algol' in styles or 'algol_nu' in styles:
+            self.algol_publication_mode = True
+        else:
+            self.algol_publication_mode = False
+        #
+        # Check option flags
+        #
+        self.treat_stdlib_adts_as_builtins = get_bool_opt(
+            options, 'treat_stdlib_adts_as_builtins', True)
+        #
+        # call superclass initialiser
+        RegexLexer.__init__(self, **options)
+
+    # Set lexer to a specified dialect
+    def set_dialect(self, dialect_id):
+        #
+        # if __debug__:
+        #    print 'entered set_dialect with arg: ', dialect_id
+        #
+        # check dialect name against known dialects
+        if dialect_id not in self.dialects:
+            dialect = 'unknown'  # default
+        else:
+            dialect = dialect_id
+        #
+        # compose lexemes to reject set
+        lexemes_to_reject_set = set()
+        # add each list of reject lexemes for this dialect
+        for list in self.lexemes_to_reject_db[dialect]:
+            lexemes_to_reject_set.update(set(list))
+        #
+        # compose reserved words set
+        reswords_set = set()
+        # add each list of reserved words for this dialect
+        for list in self.reserved_words_db[dialect]:
+            reswords_set.update(set(list))
+        #
+        # compose builtins set
+        builtins_set = set()
+        # add each list of builtins for this dialect excluding reserved words
+        for list in self.builtins_db[dialect]:
+            builtins_set.update(set(list).difference(reswords_set))
+        #
+        # compose pseudo-builtins set
+        pseudo_builtins_set = set()
+        # add each list of builtins for this dialect excluding reserved words
+        for list in self.pseudo_builtins_db[dialect]:
+            pseudo_builtins_set.update(set(list).difference(reswords_set))
+        #
+        # compose ADTs set
+        adts_set = set()
+        # add each list of ADTs for this dialect excluding reserved words
+        for list in self.stdlib_adts_db[dialect]:
+            adts_set.update(set(list).difference(reswords_set))
+        #
+        # compose modules set
+        modules_set = set()
+        # add each list of builtins for this dialect excluding builtins
+        for list in self.stdlib_modules_db[dialect]:
+            modules_set.update(set(list).difference(builtins_set))
+        #
+        # compose types set
+        types_set = set()
+        # add each list of types for this dialect excluding builtins
+        for list in self.stdlib_types_db[dialect]:
+            types_set.update(set(list).difference(builtins_set))
+        #
+        # compose procedures set
+        procedures_set = set()
+        # add each list of procedures for this dialect excluding builtins
+        for list in self.stdlib_procedures_db[dialect]:
+            procedures_set.update(set(list).difference(builtins_set))
+        #
+        # compose variables set
+        variables_set = set()
+        # add each list of variables for this dialect excluding builtins
+        for list in self.stdlib_variables_db[dialect]:
+            variables_set.update(set(list).difference(builtins_set))
+        #
+        # compose constants set
+        constants_set = set()
+        # add each list of constants for this dialect excluding builtins
+        for list in self.stdlib_constants_db[dialect]:
+            constants_set.update(set(list).difference(builtins_set))
+        #
+        # update lexer state
+        self.dialect = dialect
+        self.lexemes_to_reject = lexemes_to_reject_set
+        self.reserved_words = reswords_set
+        self.builtins = builtins_set
+        self.pseudo_builtins = pseudo_builtins_set
+        self.adts = adts_set
+        self.modules = modules_set
+        self.types = types_set
+        self.procedures = procedures_set
+        self.variables = variables_set
+        self.constants = constants_set
+        #
+        # if __debug__:
+        #    print 'exiting set_dialect'
+        #    print ' self.dialect: ', self.dialect
+        #    print ' self.lexemes_to_reject: ', self.lexemes_to_reject
+        #    print ' self.reserved_words: ', self.reserved_words
+        #    print ' self.builtins: ', self.builtins
+        #    print ' self.pseudo_builtins: ', self.pseudo_builtins
+        #    print ' self.adts: ', self.adts
+        #    print ' self.modules: ', self.modules
+        #    print ' self.types: ', self.types
+        #    print ' self.procedures: ', self.procedures
+        #    print ' self.variables: ', self.variables
+        #    print ' self.types: ', self.types
+        #    print ' self.constants: ', self.constants
+
+    # Extracts a dialect name from a dialect tag comment string  and checks
+    # the extracted name against known dialects.  If a match is found,  the
+    # matching name is returned, otherwise dialect id 'unknown' is returned
+    def get_dialect_from_dialect_tag(self, dialect_tag):
+        #
+        # if __debug__:
+        #    print 'entered get_dialect_from_dialect_tag with arg: ', dialect_tag
+        #
+        # constants
+        left_tag_delim = '(*!'
+        right_tag_delim = '*)'
+        left_tag_delim_len = len(left_tag_delim)
+        right_tag_delim_len = len(right_tag_delim)
+        indicator_start = left_tag_delim_len
+        indicator_end = -(right_tag_delim_len)
+        #
+        # check comment string for dialect indicator
+        if len(dialect_tag) > (left_tag_delim_len + right_tag_delim_len) \
+           and dialect_tag.startswith(left_tag_delim) \
+           and dialect_tag.endswith(right_tag_delim):
+            #
+            # if __debug__:
+            #    print 'dialect tag found'
+            #
+            # extract dialect indicator
+            indicator = dialect_tag[indicator_start:indicator_end]
+            #
+            # if __debug__:
+            #    print 'extracted: ', indicator
+            #
+            # check against known dialects
+            for index in range(1, len(self.dialects)):
+                #
+                # if __debug__:
+                #    print 'dialects[', index, ']: ', self.dialects[index]
+                #
+                if indicator == self.dialects[index]:
+                    #
+                    # if __debug__:
+                    #    print 'matching dialect found'
+                    #
+                    # indicator matches known dialect
+                    return indicator
+            else:
+                # indicator does not match any dialect
+                return 'unknown'  # default
+        else:
+            # invalid indicator string
+            return 'unknown'  # default
+
+    # intercept the token stream, modify token attributes and return them
+    def get_tokens_unprocessed(self, text):
+        for index, token, value in RegexLexer.get_tokens_unprocessed(self, text):
+            #
+            # check for dialect tag if dialect has not been set by tag
+            if not self.dialect_set_by_tag and token == Comment.Special:
+                indicated_dialect = self.get_dialect_from_dialect_tag(value)
+                if indicated_dialect != 'unknown':
+                    # token is a dialect indicator
+                    # reset reserved words and builtins
+                    self.set_dialect(indicated_dialect)
+                    self.dialect_set_by_tag = True
+            #
+            # check for reserved words, predefined and stdlib identifiers
+            if token is Name:
+                if value in self.reserved_words:
+                    token = Keyword.Reserved
+                    if self.algol_publication_mode:
+                        value = value.lower()
+                #
+                elif value in self.builtins:
+                    token = Name.Builtin
+                    if self.algol_publication_mode:
+                        value = value.lower()
+                #
+                elif value in self.pseudo_builtins:
+                    token = Name.Builtin.Pseudo
+                    if self.algol_publication_mode:
+                        value = value.lower()
+                #
+                elif value in self.adts:
+                    if not self.treat_stdlib_adts_as_builtins:
+                        token = Name.Namespace
+                    else:
+                        token = Name.Builtin.Pseudo
+                        if self.algol_publication_mode:
+                            value = value.lower()
+                #
+                elif value in self.modules:
+                    token = Name.Namespace
+                #
+                elif value in self.types:
+                    token = Name.Class
+                #
+                elif value in self.procedures:
+                    token = Name.Function
+                #
+                elif value in self.variables:
+                    token = Name.Variable
+                #
+                elif value in self.constants:
+                    token = Name.Constant
+            #
+            elif token in Number:
+                #
+                # mark prefix number literals as error for PIM and ISO dialects
+                if self.dialect not in ('unknown', 'm2r10', 'objm2'):
+                    if "'" in value or value[0:2] in ('0b', '0x', '0u'):
+                        token = Error
+                #
+                elif self.dialect in ('m2r10', 'objm2'):
+                    # mark base-8 number literals as errors for M2 R10 and ObjM2
+                    if token is Number.Oct:
+                        token = Error
+                    # mark suffix base-16 literals as errors for M2 R10 and ObjM2
+                    elif token is Number.Hex and 'H' in value:
+                        token = Error
+                    # mark real numbers with E as errors for M2 R10 and ObjM2
+                    elif token is Number.Float and 'E' in value:
+                        token = Error
+            #
+            elif token in Comment:
+                #
+                # mark single line comment as error for PIM and ISO dialects
+                if token is Comment.Single:
+                    if self.dialect not in ('unknown', 'm2r10', 'objm2'):
+                        token = Error
+                #
+                if token is Comment.Preproc:
+                    # mark ISO pragma as error for PIM dialects
+                    if value.startswith('<*') and \
+                       self.dialect.startswith('m2pim'):
+                        token = Error
+                    # mark PIM pragma as comment for other dialects
+                    elif value.startswith('(*$') and \
+                            self.dialect != 'unknown' and \
+                            not self.dialect.startswith('m2pim'):
+                        token = Comment.Multiline
+            #
+            else:  # token is neither Name nor Comment
+                #
+                # mark lexemes matching the dialect's error token set as errors
+                if value in self.lexemes_to_reject:
+                    token = Error
+                #
+                # substitute lexemes when in Algol mode
+                if self.algol_publication_mode:
+                    if value == '#':
+                        value = '≠'
+                    elif value == '<=':
+                        value = '≤'
+                    elif value == '>=':
+                        value = '≥'
+                    elif value == '==':
+                        value = '≡'
+                    elif value == '*.':
+                        value = '•'
+
+            # return result
+            yield index, token, value
+
+    def analyse_text(text):
+        """It's Pascal-like, but does not use FUNCTION -- uses PROCEDURE
+        instead."""
+
+        # Check if this looks like Pascal, if not, bail out early
+        if not ('(*' in text and '*)' in text and ':=' in text):
+            return
+
+        result = 0
+        # Procedure is in Modula2
+        if re.search(r'\bPROCEDURE\b', text):
+            result += 0.6
+
+        # FUNCTION is only valid in Pascal, but not in Modula2
+        if re.search(r'\bFUNCTION\b', text):
+            result = 0.0
+
+        return result
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/mojo.py b/.venv/lib/python3.12/site-packages/pygments/lexers/mojo.py
new file mode 100644
index 0000000..84aac46
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/mojo.py
@@ -0,0 +1,707 @@
+"""
+    pygments.lexers.mojo
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Mojo and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import keyword
+
+from pygments import unistring as uni
+from pygments.lexer import (
+    RegexLexer,
+    bygroups,
+    combined,
+    default,
+    include,
+    this,
+    using,
+    words,
+)
+from pygments.token import (
+    Comment,
+    # Error,
+    Keyword,
+    Name,
+    Number,
+    Operator,
+    Punctuation,
+    String,
+    Text,
+    Whitespace,
+)
+from pygments.util import shebang_matches
+
+__all__ = ["MojoLexer"]
+
+
+class MojoLexer(RegexLexer):
+    """
+    For Mojo source code (version 24.2.1).
+    """
+
+    name = "Mojo"
+    url = "https://docs.modular.com/mojo/"
+    aliases = ["mojo", "🔥"]
+    filenames = [
+        "*.mojo",
+        "*.🔥",
+    ]
+    mimetypes = [
+        "text/x-mojo",
+        "application/x-mojo",
+    ]
+    version_added = "2.18"
+
+    uni_name = f"[{uni.xid_start}][{uni.xid_continue}]*"
+
+    def innerstring_rules(ttype):
+        return [
+            # the old style '%s' % (...) string formatting (still valid in Py3)
+            (
+                r"%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?"
+                "[hlL]?[E-GXc-giorsaux%]",
+                String.Interpol,
+            ),
+            # the new style '{}'.format(...) string formatting
+            (
+                r"\{"
+                r"((\w+)((\.\w+)|(\[[^\]]+\]))*)?"  # field name
+                r"(\![sra])?"  # conversion
+                r"(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?"
+                r"\}",
+                String.Interpol,
+            ),
+            # backslashes, quotes and formatting signs must be parsed one at a time
+            (r'[^\\\'"%{\n]+', ttype),
+            (r'[\'"\\]', ttype),
+            # unhandled string formatting sign
+            (r"%|(\{{1,2})", ttype),
+            # newlines are an error (use "nl" state)
+        ]
+
+    def fstring_rules(ttype):
+        return [
+            # Assuming that a '}' is the closing brace after format specifier.
+            # Sadly, this means that we won't detect syntax error. But it's
+            # more important to parse correct syntax correctly, than to
+            # highlight invalid syntax.
+            (r"\}", String.Interpol),
+            (r"\{", String.Interpol, "expr-inside-fstring"),
+            # backslashes, quotes and formatting signs must be parsed one at a time
+            (r'[^\\\'"{}\n]+', ttype),
+            (r'[\'"\\]', ttype),
+            # newlines are an error (use "nl" state)
+        ]
+
+    tokens = {
+        "root": [
+            (r"\s+", Whitespace),
+            (
+                r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
+                bygroups(Whitespace, String.Affix, String.Doc),
+            ),
+            (
+                r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
+                bygroups(Whitespace, String.Affix, String.Doc),
+            ),
+            (r"\A#!.+$", Comment.Hashbang),
+            (r"#.*$", Comment.Single),
+            (r"\\\n", Whitespace),
+            (r"\\", Whitespace),
+            include("keywords"),
+            include("soft-keywords"),
+            # In the original PR, all the below here used ((?:\s|\\\s)+) to
+            # designate whitespace, but I can't find any example of this being
+            # needed in the example file, so we're replacing it with `\s+`.
+            (
+                r"(alias)(\s+)",
+                bygroups(Keyword, Whitespace),
+                "varname",  # TODO varname the right fit?
+            ),
+            (r"(var)(\s+)", bygroups(Keyword, Whitespace), "varname"),
+            (r"(def)(\s+)", bygroups(Keyword, Whitespace), "funcname"),
+            (r"(fn)(\s+)", bygroups(Keyword, Whitespace), "funcname"),
+            (
+                r"(class)(\s+)",
+                bygroups(Keyword, Whitespace),
+                "classname",
+            ),  # not implemented yet
+            (r"(struct)(\s+)", bygroups(Keyword, Whitespace), "structname"),
+            (r"(trait)(\s+)", bygroups(Keyword, Whitespace), "structname"),
+            (r"(from)(\s+)", bygroups(Keyword.Namespace, Whitespace), "fromimport"),
+            (r"(import)(\s+)", bygroups(Keyword.Namespace, Whitespace), "import"),
+            include("expr"),
+        ],
+        "expr": [
+            # raw f-strings
+            (
+                '(?i)(rf|fr)(""")',
+                bygroups(String.Affix, String.Double),
+                combined("rfstringescape", "tdqf"),
+            ),
+            (
+                "(?i)(rf|fr)(''')",
+                bygroups(String.Affix, String.Single),
+                combined("rfstringescape", "tsqf"),
+            ),
+            (
+                '(?i)(rf|fr)(")',
+                bygroups(String.Affix, String.Double),
+                combined("rfstringescape", "dqf"),
+            ),
+            (
+                "(?i)(rf|fr)(')",
+                bygroups(String.Affix, String.Single),
+                combined("rfstringescape", "sqf"),
+            ),
+            # non-raw f-strings
+            (
+                '([fF])(""")',
+                bygroups(String.Affix, String.Double),
+                combined("fstringescape", "tdqf"),
+            ),
+            (
+                "([fF])(''')",
+                bygroups(String.Affix, String.Single),
+                combined("fstringescape", "tsqf"),
+            ),
+            (
+                '([fF])(")',
+                bygroups(String.Affix, String.Double),
+                combined("fstringescape", "dqf"),
+            ),
+            (
+                "([fF])(')",
+                bygroups(String.Affix, String.Single),
+                combined("fstringescape", "sqf"),
+            ),
+            # raw bytes and strings
+            ('(?i)(rb|br|r)(""")', bygroups(String.Affix, String.Double), "tdqs"),
+            ("(?i)(rb|br|r)(''')", bygroups(String.Affix, String.Single), "tsqs"),
+            ('(?i)(rb|br|r)(")', bygroups(String.Affix, String.Double), "dqs"),
+            ("(?i)(rb|br|r)(')", bygroups(String.Affix, String.Single), "sqs"),
+            # non-raw strings
+            (
+                '([uU]?)(""")',
+                bygroups(String.Affix, String.Double),
+                combined("stringescape", "tdqs"),
+            ),
+            (
+                "([uU]?)(''')",
+                bygroups(String.Affix, String.Single),
+                combined("stringescape", "tsqs"),
+            ),
+            (
+                '([uU]?)(")',
+                bygroups(String.Affix, String.Double),
+                combined("stringescape", "dqs"),
+            ),
+            (
+                "([uU]?)(')",
+                bygroups(String.Affix, String.Single),
+                combined("stringescape", "sqs"),
+            ),
+            # non-raw bytes
+            (
+                '([bB])(""")',
+                bygroups(String.Affix, String.Double),
+                combined("bytesescape", "tdqs"),
+            ),
+            (
+                "([bB])(''')",
+                bygroups(String.Affix, String.Single),
+                combined("bytesescape", "tsqs"),
+            ),
+            (
+                '([bB])(")',
+                bygroups(String.Affix, String.Double),
+                combined("bytesescape", "dqs"),
+            ),
+            (
+                "([bB])(')",
+                bygroups(String.Affix, String.Single),
+                combined("bytesescape", "sqs"),
+            ),
+            (r"[^\S\n]+", Text),
+            include("numbers"),
+            (r"!=|==|<<|>>|:=|[-~+/*%=<>&^|.]", Operator),
+            (r"([]{}:\(\),;[])+", Punctuation),
+            (r"(in|is|and|or|not)\b", Operator.Word),
+            include("expr-keywords"),
+            include("builtins"),
+            include("magicfuncs"),
+            include("magicvars"),
+            include("name"),
+        ],
+        "expr-inside-fstring": [
+            (r"[{([]", Punctuation, "expr-inside-fstring-inner"),
+            # without format specifier
+            (
+                r"(=\s*)?"  # debug (https://bugs.python.org/issue36817)
+                r"(\![sraf])?"  # conversion
+                r"\}",
+                String.Interpol,
+                "#pop",
+            ),
+            # with format specifier
+            # we'll catch the remaining '}' in the outer scope
+            (
+                r"(=\s*)?"  # debug (https://bugs.python.org/issue36817)
+                r"(\![sraf])?"  # conversion
+                r":",
+                String.Interpol,
+                "#pop",
+            ),
+            (r"\s+", Whitespace),  # allow new lines
+            include("expr"),
+        ],
+        "expr-inside-fstring-inner": [
+            (r"[{([]", Punctuation, "expr-inside-fstring-inner"),
+            (r"[])}]", Punctuation, "#pop"),
+            (r"\s+", Whitespace),  # allow new lines
+            include("expr"),
+        ],
+        "expr-keywords": [
+            # Based on https://docs.python.org/3/reference/expressions.html
+            (
+                words(
+                    (
+                        "async for",  # TODO https://docs.modular.com/mojo/roadmap#no-async-for-or-async-with
+                        "async with",  # TODO https://docs.modular.com/mojo/roadmap#no-async-for-or-async-with
+                        "await",
+                        "else",
+                        "for",
+                        "if",
+                        "lambda",
+                        "yield",
+                        "yield from",
+                    ),
+                    suffix=r"\b",
+                ),
+                Keyword,
+            ),
+            (words(("True", "False", "None"), suffix=r"\b"), Keyword.Constant),
+        ],
+        "keywords": [
+            (
+                words(
+                    (
+                        "assert",
+                        "async",
+                        "await",
+                        "borrowed",
+                        "break",
+                        "continue",
+                        "del",
+                        "elif",
+                        "else",
+                        "except",
+                        "finally",
+                        "for",
+                        "global",
+                        "if",
+                        "lambda",
+                        "pass",
+                        "raise",
+                        "nonlocal",
+                        "return",
+                        "try",
+                        "while",
+                        "yield",
+                        "yield from",
+                        "as",
+                        "with",
+                    ),
+                    suffix=r"\b",
+                ),
+                Keyword,
+            ),
+            (words(("True", "False", "None"), suffix=r"\b"), Keyword.Constant),
+        ],
+        "soft-keywords": [
+            # `match`, `case` and `_` soft keywords
+            (
+                r"(^[ \t]*)"  # at beginning of line + possible indentation
+                r"(match|case)\b"  # a possible keyword
+                r"(?![ \t]*(?:"  # not followed by...
+                r"[:,;=^&|@~)\]}]|(?:" +  # characters and keywords that mean this isn't
+                # pattern matching (but None/True/False is ok)
+                r"|".join(k for k in keyword.kwlist if k[0].islower())
+                + r")\b))",
+                bygroups(Whitespace, Keyword),
+                "soft-keywords-inner",
+            ),
+        ],
+        "soft-keywords-inner": [
+            # optional `_` keyword
+            (r"(\s+)([^\n_]*)(_\b)", bygroups(Whitespace, using(this), Keyword)),
+            default("#pop"),
+        ],
+        "builtins": [
+            (
+                words(
+                    (
+                        "__import__",
+                        "abs",
+                        "aiter",
+                        "all",
+                        "any",
+                        "bin",
+                        "bool",
+                        "bytearray",
+                        "breakpoint",
+                        "bytes",
+                        "callable",
+                        "chr",
+                        "classmethod",
+                        "compile",
+                        "complex",
+                        "delattr",
+                        "dict",
+                        "dir",
+                        "divmod",
+                        "enumerate",
+                        "eval",
+                        "filter",
+                        "float",
+                        "format",
+                        "frozenset",
+                        "getattr",
+                        "globals",
+                        "hasattr",
+                        "hash",
+                        "hex",
+                        "id",
+                        "input",
+                        "int",
+                        "isinstance",
+                        "issubclass",
+                        "iter",
+                        "len",
+                        "list",
+                        "locals",
+                        "map",
+                        "max",
+                        "memoryview",
+                        "min",
+                        "next",
+                        "object",
+                        "oct",
+                        "open",
+                        "ord",
+                        "pow",
+                        "print",
+                        "property",
+                        "range",
+                        "repr",
+                        "reversed",
+                        "round",
+                        "set",
+                        "setattr",
+                        "slice",
+                        "sorted",
+                        "staticmethod",
+                        "str",
+                        "sum",
+                        "super",
+                        "tuple",
+                        "type",
+                        "vars",
+                        "zip",
+                        # Mojo builtin types: https://docs.modular.com/mojo/stdlib/builtin/
+                        "AnyType",
+                        "Coroutine",
+                        "DType",
+                        "Error",
+                        "Int",
+                        "List",
+                        "ListLiteral",
+                        "Scalar",
+                        "Int8",
+                        "UInt8",
+                        "Int16",
+                        "UInt16",
+                        "Int32",
+                        "UInt32",
+                        "Int64",
+                        "UInt64",
+                        "BFloat16",
+                        "Float16",
+                        "Float32",
+                        "Float64",
+                        "SIMD",
+                        "String",
+                        "Tensor",
+                        "Tuple",
+                        "Movable",
+                        "Copyable",
+                        "CollectionElement",
+                    ),
+                    prefix=r"(?>',
+    # Binary augmented
+    '+=', '-=', '*=', '/=', '%=', '**=', '&=', '|=', '^=', '<<=', '>>=',
+    # Comparison
+    '==', '!=', '<', '<=', '>', '>=', '<=>',
+    # Patterns and assignment
+    ':=', '?', '=~', '!~', '=>',
+    # Calls and sends
+    '.', '<-', '->',
+]
+_escape_pattern = (
+    r'(?:\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|'
+    r'\\["\'\\bftnr])')
+# _char = _escape_chars + [('.', String.Char)]
+_identifier = r'[_a-zA-Z]\w*'
+
+_constants = [
+    # Void constants
+    'null',
+    # Bool constants
+    'false', 'true',
+    # Double constants
+    'Infinity', 'NaN',
+    # Special objects
+    'M', 'Ref', 'throw', 'traceln',
+]
+
+_guards = [
+    'Any', 'Binding', 'Bool', 'Bytes', 'Char', 'DeepFrozen', 'Double',
+    'Empty', 'Int', 'List', 'Map', 'Near', 'NullOk', 'Same', 'Selfless',
+    'Set', 'Str', 'SubrangeGuard', 'Transparent', 'Void',
+]
+
+_safeScope = [
+    '_accumulateList', '_accumulateMap', '_auditedBy', '_bind',
+    '_booleanFlow', '_comparer', '_equalizer', '_iterForever', '_loop',
+    '_makeBytes', '_makeDouble', '_makeFinalSlot', '_makeInt', '_makeList',
+    '_makeMap', '_makeMessageDesc', '_makeOrderedSpace', '_makeParamDesc',
+    '_makeProtocolDesc', '_makeSourceSpan', '_makeString', '_makeVarSlot',
+    '_makeVerbFacet', '_mapExtract', '_matchSame', '_quasiMatcher',
+    '_slotToBinding', '_splitList', '_suchThat', '_switchFailed',
+    '_validateFor', 'b__quasiParser', 'eval', 'import', 'm__quasiParser',
+    'makeBrandPair', 'makeLazySlot', 'safeScope', 'simple__quasiParser',
+]
+
+
+class MonteLexer(RegexLexer):
+    """
+    Lexer for the Monte programming language.
+    """
+    name = 'Monte'
+    url = 'https://monte.readthedocs.io/'
+    aliases = ['monte']
+    filenames = ['*.mt']
+    version_added = '2.2'
+
+    tokens = {
+        'root': [
+            # Comments
+            (r'#[^\n]*\n', Comment),
+
+            # Docstrings
+            # Apologies for the non-greedy matcher here.
+            (r'/\*\*.*?\*/', String.Doc),
+
+            # `var` declarations
+            (r'\bvar\b', Keyword.Declaration, 'var'),
+
+            # `interface` declarations
+            (r'\binterface\b', Keyword.Declaration, 'interface'),
+
+            # method declarations
+            (words(_methods, prefix='\\b', suffix='\\b'),
+             Keyword, 'method'),
+
+            # All other declarations
+            (words(_declarations, prefix='\\b', suffix='\\b'),
+             Keyword.Declaration),
+
+            # Keywords
+            (words(_keywords, prefix='\\b', suffix='\\b'), Keyword),
+
+            # Literals
+            ('[+-]?0x[_0-9a-fA-F]+', Number.Hex),
+            (r'[+-]?[_0-9]+\.[_0-9]*([eE][+-]?[_0-9]+)?', Number.Float),
+            ('[+-]?[_0-9]+', Number.Integer),
+            ("'", String.Double, 'char'),
+            ('"', String.Double, 'string'),
+
+            # Quasiliterals
+            ('`', String.Backtick, 'ql'),
+
+            # Operators
+            (words(_operators), Operator),
+
+            # Verb operators
+            (_identifier + '=', Operator.Word),
+
+            # Safe scope constants
+            (words(_constants, prefix='\\b', suffix='\\b'),
+             Keyword.Pseudo),
+
+            # Safe scope guards
+            (words(_guards, prefix='\\b', suffix='\\b'), Keyword.Type),
+
+            # All other safe scope names
+            (words(_safeScope, prefix='\\b', suffix='\\b'),
+             Name.Builtin),
+
+            # Identifiers
+            (_identifier, Name),
+
+            # Punctuation
+            (r'\(|\)|\{|\}|\[|\]|:|,', Punctuation),
+
+            # Whitespace
+            (' +', Whitespace),
+
+            # Definite lexer errors
+            ('=', Error),
+        ],
+        'char': [
+            # It is definitely an error to have a char of width == 0.
+            ("'", Error, 'root'),
+            (_escape_pattern, String.Escape, 'charEnd'),
+            ('.', String.Char, 'charEnd'),
+        ],
+        'charEnd': [
+            ("'", String.Char, '#pop:2'),
+            # It is definitely an error to have a char of width > 1.
+            ('.', Error),
+        ],
+        # The state of things coming into an interface.
+        'interface': [
+            (' +', Whitespace),
+            (_identifier, Name.Class, '#pop'),
+            include('root'),
+        ],
+        # The state of things coming into a method.
+        'method': [
+            (' +', Whitespace),
+            (_identifier, Name.Function, '#pop'),
+            include('root'),
+        ],
+        'string': [
+            ('"', String.Double, 'root'),
+            (_escape_pattern, String.Escape),
+            (r'\n', String.Double),
+            ('.', String.Double),
+        ],
+        'ql': [
+            ('`', String.Backtick, 'root'),
+            (r'\$' + _escape_pattern, String.Escape),
+            (r'\$\$', String.Escape),
+            (r'@@', String.Escape),
+            (r'\$\{', String.Interpol, 'qlNest'),
+            (r'@\{', String.Interpol, 'qlNest'),
+            (r'\$' + _identifier, Name),
+            ('@' + _identifier, Name),
+            ('.', String.Backtick),
+        ],
+        'qlNest': [
+            (r'\}', String.Interpol, '#pop'),
+            include('root'),
+        ],
+        # The state of things immediately following `var`.
+        'var': [
+            (' +', Whitespace),
+            (_identifier, Name.Variable, '#pop'),
+            include('root'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/mosel.py b/.venv/lib/python3.12/site-packages/pygments/lexers/mosel.py
new file mode 100644
index 0000000..912f478
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/mosel.py
@@ -0,0 +1,447 @@
+"""
+    pygments.lexers.mosel
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the mosel language.
+    http://www.fico.com/en/products/fico-xpress-optimization
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['MoselLexer']
+
+FUNCTIONS = (
+    # core functions
+    '_',
+    'abs',
+    'arctan',
+    'asproc',
+    'assert',
+    'bitflip',
+    'bitneg',
+    'bitset',
+    'bitshift',
+    'bittest',
+    'bitval',
+    'ceil',
+    'cos',
+    'create',
+    'currentdate',
+    'currenttime',
+    'cutelt',
+    'cutfirst',
+    'cuthead',
+    'cutlast',
+    'cuttail',
+    'datablock',
+    'delcell',
+    'exists',
+    'exit',
+    'exp',
+    'exportprob',
+    'fclose',
+    'fflush',
+    'finalize',
+    'findfirst',
+    'findlast',
+    'floor',
+    'fopen',
+    'fselect',
+    'fskipline',
+    'fwrite',
+    'fwrite_',
+    'fwriteln',
+    'fwriteln_',
+    'getact',
+    'getcoeff',
+    'getcoeffs',
+    'getdual',
+    'getelt',
+    'getfid',
+    'getfirst',
+    'getfname',
+    'gethead',
+    'getlast',
+    'getobjval',
+    'getparam',
+    'getrcost',
+    'getreadcnt',
+    'getreverse',
+    'getsize',
+    'getslack',
+    'getsol',
+    'gettail',
+    'gettype',
+    'getvars',
+    'isdynamic',
+    'iseof',
+    'isfinite',
+    'ishidden',
+    'isinf',
+    'isnan',
+    'isodd',
+    'ln',
+    'localsetparam',
+    'log',
+    'makesos1',
+    'makesos2',
+    'maxlist',
+    'memoryuse',
+    'minlist',
+    'newmuid',
+    'publish',
+    'random',
+    'read',
+    'readln',
+    'reset',
+    'restoreparam',
+    'reverse',
+    'round',
+    'setcoeff',
+    'sethidden',
+    'setioerr',
+    'setmatherr',
+    'setname',
+    'setparam',
+    'setrandseed',
+    'setrange',
+    'settype',
+    'sin',
+    'splithead',
+    'splittail',
+    'sqrt',
+    'strfmt',
+    'substr',
+    'timestamp',
+    'unpublish',
+    'versionnum',
+    'versionstr',
+    'write',
+    'write_',
+    'writeln',
+    'writeln_',
+
+    # mosel exam mmxprs | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u
+    'addcut',
+    'addcuts',
+    'addmipsol',
+    'basisstability',
+    'calcsolinfo',
+    'clearmipdir',
+    'clearmodcut',
+    'command',
+    'copysoltoinit',
+    'crossoverlpsol',
+    'defdelayedrows',
+    'defsecurevecs',
+    'delcuts',
+    'dropcuts',
+    'estimatemarginals',
+    'fixglobal',
+    'flushmsgq',
+    'getbstat',
+    'getcnlist',
+    'getcplist',
+    'getdualray',
+    'getiis',
+    'getiissense',
+    'getiistype',
+    'getinfcause',
+    'getinfeas',
+    'getlb',
+    'getlct',
+    'getleft',
+    'getloadedlinctrs',
+    'getloadedmpvars',
+    'getname',
+    'getprimalray',
+    'getprobstat',
+    'getrange',
+    'getright',
+    'getsensrng',
+    'getsize',
+    'getsol',
+    'gettype',
+    'getub',
+    'getvars',
+    'gety',
+    'hasfeature',
+    'implies',
+    'indicator',
+    'initglobal',
+    'ishidden',
+    'isiisvalid',
+    'isintegral',
+    'loadbasis',
+    'loadcuts',
+    'loadlpsol',
+    'loadmipsol',
+    'loadprob',
+    'maximise',
+    'maximize',
+    'minimise',
+    'minimize',
+    'postsolve',
+    'readbasis',
+    'readdirs',
+    'readsol',
+    'refinemipsol',
+    'rejectintsol',
+    'repairinfeas',
+    'repairinfeas_deprec',
+    'resetbasis',
+    'resetiis',
+    'resetsol',
+    'savebasis',
+    'savemipsol',
+    'savesol',
+    'savestate',
+    'selectsol',
+    'setarchconsistency',
+    'setbstat',
+    'setcallback',
+    'setcbcutoff',
+    'setgndata',
+    'sethidden',
+    'setlb',
+    'setmipdir',
+    'setmodcut',
+    'setsol',
+    'setub',
+    'setucbdata',
+    'stopoptimise',
+    'stopoptimize',
+    'storecut',
+    'storecuts',
+    'unloadprob',
+    'uselastbarsol',
+    'writebasis',
+    'writedirs',
+    'writeprob',
+    'writesol',
+    'xor',
+    'xprs_addctr',
+    'xprs_addindic',
+
+    # mosel exam mmsystem | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u
+    'addmonths',
+    'copytext',
+    'cuttext',
+    'deltext',
+    'endswith',
+    'erase',
+    'expandpath',
+    'fcopy',
+    'fdelete',
+    'findfiles',
+    'findtext',
+    'fmove',
+    'formattext',
+    'getasnumber',
+    'getchar',
+    'getcwd',
+    'getdate',
+    'getday',
+    'getdaynum',
+    'getdays',
+    'getdirsep',
+    'getdsoparam',
+    'getendparse',
+    'getenv',
+    'getfsize',
+    'getfstat',
+    'getftime',
+    'gethour',
+    'getminute',
+    'getmonth',
+    'getmsec',
+    'getoserrmsg',
+    'getoserror',
+    'getpathsep',
+    'getqtype',
+    'getsecond',
+    'getsepchar',
+    'getsize',
+    'getstart',
+    'getsucc',
+    'getsysinfo',
+    'getsysstat',
+    'gettime',
+    'gettmpdir',
+    'gettrim',
+    'getweekday',
+    'getyear',
+    'inserttext',
+    'isvalid',
+    'jointext',
+    'makedir',
+    'makepath',
+    'newtar',
+    'newzip',
+    'nextfield',
+    'openpipe',
+    'parseextn',
+    'parseint',
+    'parsereal',
+    'parsetext',
+    'pastetext',
+    'pathmatch',
+    'pathsplit',
+    'qsort',
+    'quote',
+    'readtextline',
+    'regmatch',
+    'regreplace',
+    'removedir',
+    'removefiles',
+    'setchar',
+    'setdate',
+    'setday',
+    'setdsoparam',
+    'setendparse',
+    'setenv',
+    'sethour',
+    'setminute',
+    'setmonth',
+    'setmsec',
+    'setoserror',
+    'setqtype',
+    'setsecond',
+    'setsepchar',
+    'setstart',
+    'setsucc',
+    'settime',
+    'settrim',
+    'setyear',
+    'sleep',
+    'splittext',
+    'startswith',
+    'system',
+    'tarlist',
+    'textfmt',
+    'tolower',
+    'toupper',
+    'trim',
+    'untar',
+    'unzip',
+    'ziplist',
+
+    # mosel exam mmjobs | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u
+    'canceltimer',
+    'clearaliases',
+    'compile',
+    'connect',
+    'detach',
+    'disconnect',
+    'dropnextevent',
+    'findxsrvs',
+    'getaliases',
+    'getannidents',
+    'getannotations',
+    'getbanner',
+    'getclass',
+    'getdsoprop',
+    'getdsopropnum',
+    'getexitcode',
+    'getfromgid',
+    'getfromid',
+    'getfromuid',
+    'getgid',
+    'gethostalias',
+    'getid',
+    'getmodprop',
+    'getmodpropnum',
+    'getnextevent',
+    'getnode',
+    'getrmtid',
+    'getstatus',
+    'getsysinfo',
+    'gettimer',
+    'getuid',
+    'getvalue',
+    'isqueueempty',
+    'load',
+    'nullevent',
+    'peeknextevent',
+    'resetmodpar',
+    'run',
+    'send',
+    'setcontrol',
+    'setdefstream',
+    'setgid',
+    'sethostalias',
+    'setmodpar',
+    'settimer',
+    'setuid',
+    'setworkdir',
+    'stop',
+    'unload',
+    'wait',
+    'waitexpired',
+    'waitfor',
+    'waitforend',
+)
+
+
+class MoselLexer(RegexLexer):
+    """
+    For the Mosel optimization language.
+    """
+    name = 'Mosel'
+    aliases = ['mosel']
+    filenames = ['*.mos']
+    url = 'https://www.fico.com/fico-xpress-optimization/docs/latest/mosel/mosel_lang/dhtml/moselreflang.html'
+    version_added = '2.6'
+
+    tokens = {
+        'root': [
+            (r'\n', Text),
+            (r'\s+', Text.Whitespace),
+            (r'!.*?\n', Comment.Single),
+            (r'\(!(.|\n)*?!\)', Comment.Multiline),
+            (words((
+                'and', 'as', 'break', 'case', 'count', 'declarations', 'do',
+                'dynamic', 'elif', 'else', 'end-', 'end', 'evaluation', 'false',
+                'forall', 'forward', 'from', 'function', 'hashmap', 'if',
+                'imports', 'include', 'initialisations', 'initializations', 'inter',
+                'max', 'min', 'model', 'namespace', 'next', 'not', 'nsgroup',
+                'nssearch', 'of', 'options', 'or', 'package', 'parameters',
+                'procedure', 'public', 'prod', 'record', 'repeat', 'requirements',
+                'return', 'sum', 'then', 'to', 'true', 'union', 'until', 'uses',
+                'version', 'while', 'with'), prefix=r'\b', suffix=r'\b'),
+             Keyword.Builtin),
+            (words((
+                'range', 'array', 'set', 'list', 'mpvar', 'mpproblem', 'linctr',
+                'nlctr', 'integer', 'string', 'real', 'boolean', 'text', 'time',
+                'date', 'datetime', 'returned', 'Model', 'Mosel', 'counter',
+                'xmldoc', 'is_sos1', 'is_sos2', 'is_integer', 'is_binary',
+                'is_continuous', 'is_free', 'is_semcont', 'is_semint',
+                'is_partint'), prefix=r'\b', suffix=r'\b'),
+             Keyword.Type),
+            (r'(\+|\-|\*|/|=|<=|>=|\||\^|<|>|<>|\.\.|\.|:=|::|:|in|mod|div)',
+             Operator),
+            (r'[()\[\]{},;]+', Punctuation),
+            (words(FUNCTIONS,  prefix=r'\b', suffix=r'\b'), Name.Function),
+            (r'(\d+\.(?!\.)\d*|\.(?!.)\d+)([eE][+-]?\d+)?', Number.Float),
+            (r'\d+([eE][+-]?\d+)?', Number.Integer),
+            (r'[+-]?Infinity', Number.Integer),
+            (r'0[xX][0-9a-fA-F]+', Number),
+            (r'"', String.Double, 'double_quote'),
+            (r'\'', String.Single, 'single_quote'),
+            (r'(\w+|(\.(?!\.)))', Text),
+        ],
+        'single_quote': [
+            (r'\'', String.Single, '#pop'),
+            (r'[^\']+', String.Single),
+        ],
+        'double_quote': [
+            (r'(\\"|\\[0-7]{1,3}\D|\\[abfnrtv]|\\\\)', String.Escape),
+            (r'\"', String.Double, '#pop'),
+            (r'[^"\\]+', String.Double),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/ncl.py b/.venv/lib/python3.12/site-packages/pygments/lexers/ncl.py
new file mode 100644
index 0000000..a2729ef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/ncl.py
@@ -0,0 +1,894 @@
+"""
+    pygments.lexers.ncl
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for NCAR Command Language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['NCLLexer']
+
+
+class NCLLexer(RegexLexer):
+    """
+    Lexer for NCL code.
+    """
+    name = 'NCL'
+    aliases = ['ncl']
+    filenames = ['*.ncl']
+    mimetypes = ['text/ncl']
+    url = 'https://www.ncl.ucar.edu'
+    version_added = '2.2'
+
+    flags = re.MULTILINE
+
+    tokens = {
+        'root': [
+            (r';.*\n', Comment),
+            include('strings'),
+            include('core'),
+            (r'[a-zA-Z_]\w*', Name),
+            include('nums'),
+            (r'[\s]+', Text),
+        ],
+        'core': [
+            # Statements
+            (words((
+                'begin', 'break', 'continue', 'create', 'defaultapp', 'do',
+                'else', 'end', 'external', 'exit', 'True', 'False', 'file', 'function',
+                'getvalues', 'graphic', 'group', 'if', 'list', 'load', 'local',
+                'new', '_Missing', 'Missing', 'noparent', 'procedure',
+                'quit', 'QUIT', 'Quit', 'record', 'return', 'setvalues', 'stop',
+                'then', 'while'), prefix=r'\b', suffix=r'\s*\b'),
+             Keyword),
+
+            # Data Types
+            (words((
+                'ubyte', 'uint', 'uint64', 'ulong', 'string', 'byte',
+                'character', 'double', 'float', 'integer', 'int64', 'logical',
+                'long', 'short', 'ushort', 'enumeric', 'numeric', 'snumeric'),
+                prefix=r'\b', suffix=r'\s*\b'),
+             Keyword.Type),
+
+            # Operators
+            (r'[\%^*+\-/<>]', Operator),
+
+            # punctuation:
+            (r'[\[\]():@$!&|.,\\{}]', Punctuation),
+            (r'[=:]', Punctuation),
+
+            # Intrinsics
+            (words((
+                'abs', 'acos', 'addfile', 'addfiles', 'all', 'angmom_atm', 'any',
+                'area_conserve_remap', 'area_hi2lores', 'area_poly_sphere',
+                'asciiread', 'asciiwrite', 'asin', 'atan', 'atan2', 'attsetvalues',
+                'avg', 'betainc', 'bin_avg', 'bin_sum', 'bw_bandpass_filter',
+                'cancor', 'cbinread', 'cbinwrite', 'cd_calendar', 'cd_inv_calendar',
+                'cdfbin_p', 'cdfbin_pr', 'cdfbin_s', 'cdfbin_xn', 'cdfchi_p',
+                'cdfchi_x', 'cdfgam_p', 'cdfgam_x', 'cdfnor_p', 'cdfnor_x',
+                'cdft_p', 'cdft_t', 'ceil', 'center_finite_diff',
+                'center_finite_diff_n', 'cfftb', 'cfftf', 'cfftf_frq_reorder',
+                'charactertodouble', 'charactertofloat', 'charactertointeger',
+                'charactertolong', 'charactertoshort', 'charactertostring',
+                'chartodouble', 'chartofloat', 'chartoint', 'chartointeger',
+                'chartolong', 'chartoshort', 'chartostring', 'chiinv', 'clear',
+                'color_index_to_rgba', 'conform', 'conform_dims', 'cos', 'cosh',
+                'count_unique_values', 'covcorm', 'covcorm_xy', 'craybinnumrec',
+                'craybinrecread', 'create_graphic', 'csa1', 'csa1d', 'csa1s',
+                'csa1x', 'csa1xd', 'csa1xs', 'csa2', 'csa2d', 'csa2l', 'csa2ld',
+                'csa2ls', 'csa2lx', 'csa2lxd', 'csa2lxs', 'csa2s', 'csa2x',
+                'csa2xd', 'csa2xs', 'csa3', 'csa3d', 'csa3l', 'csa3ld', 'csa3ls',
+                'csa3lx', 'csa3lxd', 'csa3lxs', 'csa3s', 'csa3x', 'csa3xd',
+                'csa3xs', 'csc2s', 'csgetp', 'css2c', 'cssetp', 'cssgrid', 'csstri',
+                'csvoro', 'cumsum', 'cz2ccm', 'datatondc', 'day_of_week',
+                'day_of_year', 'days_in_month', 'default_fillvalue', 'delete',
+                'depth_to_pres', 'destroy', 'determinant', 'dewtemp_trh',
+                'dgeevx_lapack', 'dim_acumrun_n', 'dim_avg', 'dim_avg_n',
+                'dim_avg_wgt', 'dim_avg_wgt_n', 'dim_cumsum', 'dim_cumsum_n',
+                'dim_gamfit_n', 'dim_gbits', 'dim_max', 'dim_max_n', 'dim_median',
+                'dim_median_n', 'dim_min', 'dim_min_n', 'dim_num', 'dim_num_n',
+                'dim_numrun_n', 'dim_pqsort', 'dim_pqsort_n', 'dim_product',
+                'dim_product_n', 'dim_rmsd', 'dim_rmsd_n', 'dim_rmvmean',
+                'dim_rmvmean_n', 'dim_rmvmed', 'dim_rmvmed_n', 'dim_spi_n',
+                'dim_standardize', 'dim_standardize_n', 'dim_stat4', 'dim_stat4_n',
+                'dim_stddev', 'dim_stddev_n', 'dim_sum', 'dim_sum_n', 'dim_sum_wgt',
+                'dim_sum_wgt_n', 'dim_variance', 'dim_variance_n', 'dimsizes',
+                'doubletobyte', 'doubletochar', 'doubletocharacter',
+                'doubletofloat', 'doubletoint', 'doubletointeger', 'doubletolong',
+                'doubletoshort', 'dpres_hybrid_ccm', 'dpres_plevel', 'draw',
+                'draw_color_palette', 'dsgetp', 'dsgrid2', 'dsgrid2d', 'dsgrid2s',
+                'dsgrid3', 'dsgrid3d', 'dsgrid3s', 'dspnt2', 'dspnt2d', 'dspnt2s',
+                'dspnt3', 'dspnt3d', 'dspnt3s', 'dssetp', 'dtrend', 'dtrend_msg',
+                'dtrend_msg_n', 'dtrend_n', 'dtrend_quadratic',
+                'dtrend_quadratic_msg_n', 'dv2uvf', 'dv2uvg', 'dz_height',
+                'echo_off', 'echo_on', 'eof2data', 'eof_varimax', 'eofcor',
+                'eofcor_pcmsg', 'eofcor_ts', 'eofcov', 'eofcov_pcmsg', 'eofcov_ts',
+                'eofunc', 'eofunc_ts', 'eofunc_varimax', 'equiv_sample_size', 'erf',
+                'erfc', 'esacr', 'esacv', 'esccr', 'esccv', 'escorc', 'escorc_n',
+                'escovc', 'exit', 'exp', 'exp_tapersh', 'exp_tapersh_wgts',
+                'exp_tapershC', 'ezfftb', 'ezfftb_n', 'ezfftf', 'ezfftf_n',
+                'f2fosh', 'f2foshv', 'f2fsh', 'f2fshv', 'f2gsh', 'f2gshv', 'fabs',
+                'fbindirread', 'fbindirwrite', 'fbinnumrec', 'fbinread',
+                'fbinrecread', 'fbinrecwrite', 'fbinwrite', 'fft2db', 'fft2df',
+                'fftshift', 'fileattdef', 'filechunkdimdef', 'filedimdef',
+                'fileexists', 'filegrpdef', 'filevarattdef', 'filevarchunkdef',
+                'filevarcompressleveldef', 'filevardef', 'filevardimsizes',
+                'filwgts_lancos', 'filwgts_lanczos', 'filwgts_normal',
+                'floattobyte', 'floattochar', 'floattocharacter', 'floattoint',
+                'floattointeger', 'floattolong', 'floattoshort', 'floor',
+                'fluxEddy', 'fo2fsh', 'fo2fshv', 'fourier_info', 'frame', 'fspan',
+                'ftcurv', 'ftcurvd', 'ftcurvi', 'ftcurvp', 'ftcurvpi', 'ftcurvps',
+                'ftcurvs', 'ftest', 'ftgetp', 'ftkurv', 'ftkurvd', 'ftkurvp',
+                'ftkurvpd', 'ftsetp', 'ftsurf', 'g2fsh', 'g2fshv', 'g2gsh',
+                'g2gshv', 'gamma', 'gammainc', 'gaus', 'gaus_lobat',
+                'gaus_lobat_wgt', 'gc_aangle', 'gc_clkwise', 'gc_dangle',
+                'gc_inout', 'gc_latlon', 'gc_onarc', 'gc_pnt2gc', 'gc_qarea',
+                'gc_tarea', 'generate_2d_array', 'get_color_index',
+                'get_color_rgba', 'get_cpu_time', 'get_isolines', 'get_ncl_version',
+                'get_script_name', 'get_script_prefix_name', 'get_sphere_radius',
+                'get_unique_values', 'getbitsone', 'getenv', 'getfiledimsizes',
+                'getfilegrpnames', 'getfilepath', 'getfilevaratts',
+                'getfilevarchunkdimsizes', 'getfilevardims', 'getfilevardimsizes',
+                'getfilevarnames', 'getfilevartypes', 'getvaratts', 'getvardims',
+                'gradsf', 'gradsg', 'greg2jul', 'grid2triple', 'hlsrgb', 'hsvrgb',
+                'hydro', 'hyi2hyo', 'idsfft', 'igradsf', 'igradsg', 'ilapsf',
+                'ilapsg', 'ilapvf', 'ilapvg', 'ind', 'ind_resolve', 'int2p',
+                'int2p_n', 'integertobyte', 'integertochar', 'integertocharacter',
+                'integertoshort', 'inttobyte', 'inttochar', 'inttoshort',
+                'inverse_matrix', 'isatt', 'isbigendian', 'isbyte', 'ischar',
+                'iscoord', 'isdefined', 'isdim', 'isdimnamed', 'isdouble',
+                'isenumeric', 'isfile', 'isfilepresent', 'isfilevar',
+                'isfilevaratt', 'isfilevarcoord', 'isfilevardim', 'isfloat',
+                'isfunc', 'isgraphic', 'isint', 'isint64', 'isinteger',
+                'isleapyear', 'islogical', 'islong', 'ismissing', 'isnan_ieee',
+                'isnumeric', 'ispan', 'isproc', 'isshort', 'issnumeric', 'isstring',
+                'isubyte', 'isuint', 'isuint64', 'isulong', 'isunlimited',
+                'isunsigned', 'isushort', 'isvar', 'jul2greg', 'kmeans_as136',
+                'kolsm2_n', 'kron_product', 'lapsf', 'lapsg', 'lapvf', 'lapvg',
+                'latlon2utm', 'lclvl', 'lderuvf', 'lderuvg', 'linint1', 'linint1_n',
+                'linint2', 'linint2_points', 'linmsg', 'linmsg_n', 'linrood_latwgt',
+                'linrood_wgt', 'list_files', 'list_filevars', 'list_hlus',
+                'list_procfuncs', 'list_vars', 'ListAppend', 'ListCount',
+                'ListGetType', 'ListIndex', 'ListIndexFromName', 'ListPop',
+                'ListPush', 'ListSetType', 'loadscript', 'local_max', 'local_min',
+                'log', 'log10', 'longtobyte', 'longtochar', 'longtocharacter',
+                'longtoint', 'longtointeger', 'longtoshort', 'lspoly', 'lspoly_n',
+                'mask', 'max', 'maxind', 'min', 'minind', 'mixed_layer_depth',
+                'mixhum_ptd', 'mixhum_ptrh', 'mjo_cross_coh2pha',
+                'mjo_cross_segment', 'moc_globe_atl', 'monthday', 'natgrid',
+                'natgridd', 'natgrids', 'ncargpath', 'ncargversion', 'ndctodata',
+                'ndtooned', 'new', 'NewList', 'ngezlogo', 'nggcog', 'nggetp',
+                'nglogo', 'ngsetp', 'NhlAddAnnotation', 'NhlAddData',
+                'NhlAddOverlay', 'NhlAddPrimitive', 'NhlAppGetDefaultParentId',
+                'NhlChangeWorkstation', 'NhlClassName', 'NhlClearWorkstation',
+                'NhlDataPolygon', 'NhlDataPolyline', 'NhlDataPolymarker',
+                'NhlDataToNDC', 'NhlDestroy', 'NhlDraw', 'NhlFrame', 'NhlFreeColor',
+                'NhlGetBB', 'NhlGetClassResources', 'NhlGetErrorObjectId',
+                'NhlGetNamedColorIndex', 'NhlGetParentId',
+                'NhlGetParentWorkstation', 'NhlGetWorkspaceObjectId',
+                'NhlIsAllocatedColor', 'NhlIsApp', 'NhlIsDataComm', 'NhlIsDataItem',
+                'NhlIsDataSpec', 'NhlIsTransform', 'NhlIsView', 'NhlIsWorkstation',
+                'NhlName', 'NhlNDCPolygon', 'NhlNDCPolyline', 'NhlNDCPolymarker',
+                'NhlNDCToData', 'NhlNewColor', 'NhlNewDashPattern', 'NhlNewMarker',
+                'NhlPalGetDefined', 'NhlRemoveAnnotation', 'NhlRemoveData',
+                'NhlRemoveOverlay', 'NhlRemovePrimitive', 'NhlSetColor',
+                'NhlSetDashPattern', 'NhlSetMarker', 'NhlUpdateData',
+                'NhlUpdateWorkstation', 'nice_mnmxintvl', 'nngetaspectd',
+                'nngetaspects', 'nngetp', 'nngetsloped', 'nngetslopes', 'nngetwts',
+                'nngetwtsd', 'nnpnt', 'nnpntd', 'nnpntend', 'nnpntendd',
+                'nnpntinit', 'nnpntinitd', 'nnpntinits', 'nnpnts', 'nnsetp', 'num',
+                'obj_anal_ic', 'omega_ccm', 'onedtond', 'overlay', 'paleo_outline',
+                'pdfxy_bin', 'poisson_grid_fill', 'pop_remap', 'potmp_insitu_ocn',
+                'prcwater_dp', 'pres2hybrid', 'pres_hybrid_ccm', 'pres_sigma',
+                'print', 'print_table', 'printFileVarSummary', 'printVarSummary',
+                'product', 'pslec', 'pslhor', 'pslhyp', 'qsort', 'rand',
+                'random_chi', 'random_gamma', 'random_normal', 'random_setallseed',
+                'random_uniform', 'rcm2points', 'rcm2rgrid', 'rdsstoi',
+                'read_colormap_file', 'reg_multlin', 'regcoef', 'regCoef_n',
+                'regline', 'relhum', 'replace_ieeenan', 'reshape', 'reshape_ind',
+                'rgba_to_color_index', 'rgbhls', 'rgbhsv', 'rgbyiq', 'rgrid2rcm',
+                'rhomb_trunc', 'rip_cape_2d', 'rip_cape_3d', 'round', 'rtest',
+                'runave', 'runave_n', 'set_default_fillvalue', 'set_sphere_radius',
+                'setfileoption', 'sfvp2uvf', 'sfvp2uvg', 'shaec', 'shagc',
+                'shgetnp', 'shgetp', 'shgrid', 'shorttobyte', 'shorttochar',
+                'shorttocharacter', 'show_ascii', 'shsec', 'shsetp', 'shsgc',
+                'shsgc_R42', 'sigma2hybrid', 'simpeq', 'simpne', 'sin',
+                'sindex_yrmo', 'sinh', 'sizeof', 'sleep', 'smth9', 'snindex_yrmo',
+                'solve_linsys', 'span_color_indexes', 'span_color_rgba',
+                'sparse_matrix_mult', 'spcorr', 'spcorr_n', 'specx_anal',
+                'specxy_anal', 'spei', 'sprintf', 'sprinti', 'sqrt', 'sqsort',
+                'srand', 'stat2', 'stat4', 'stat_medrng', 'stat_trim',
+                'status_exit', 'stdatmus_p2tdz', 'stdatmus_z2tdp', 'stddev',
+                'str_capital', 'str_concat', 'str_fields_count', 'str_get_cols',
+                'str_get_dq', 'str_get_field', 'str_get_nl', 'str_get_sq',
+                'str_get_tab', 'str_index_of_substr', 'str_insert', 'str_is_blank',
+                'str_join', 'str_left_strip', 'str_lower', 'str_match',
+                'str_match_ic', 'str_match_ic_regex', 'str_match_ind',
+                'str_match_ind_ic', 'str_match_ind_ic_regex', 'str_match_ind_regex',
+                'str_match_regex', 'str_right_strip', 'str_split',
+                'str_split_by_length', 'str_split_csv', 'str_squeeze', 'str_strip',
+                'str_sub_str', 'str_switch', 'str_upper', 'stringtochar',
+                'stringtocharacter', 'stringtodouble', 'stringtofloat',
+                'stringtoint', 'stringtointeger', 'stringtolong', 'stringtoshort',
+                'strlen', 'student_t', 'sum', 'svd_lapack', 'svdcov', 'svdcov_sv',
+                'svdstd', 'svdstd_sv', 'system', 'systemfunc', 'tan', 'tanh',
+                'taper', 'taper_n', 'tdclrs', 'tdctri', 'tdcudp', 'tdcurv',
+                'tddtri', 'tdez2d', 'tdez3d', 'tdgetp', 'tdgrds', 'tdgrid',
+                'tdgtrs', 'tdinit', 'tditri', 'tdlbla', 'tdlblp', 'tdlbls',
+                'tdline', 'tdlndp', 'tdlnpa', 'tdlpdp', 'tdmtri', 'tdotri',
+                'tdpara', 'tdplch', 'tdprpa', 'tdprpi', 'tdprpt', 'tdsetp',
+                'tdsort', 'tdstri', 'tdstrs', 'tdttri', 'thornthwaite', 'tobyte',
+                'tochar', 'todouble', 'tofloat', 'toint', 'toint64', 'tointeger',
+                'tolong', 'toshort', 'tosigned', 'tostring', 'tostring_with_format',
+                'totype', 'toubyte', 'touint', 'touint64', 'toulong', 'tounsigned',
+                'toushort', 'trend_manken', 'tri_trunc', 'triple2grid',
+                'triple2grid2d', 'trop_wmo', 'ttest', 'typeof', 'undef',
+                'unique_string', 'update', 'ushorttoint', 'ut_calendar',
+                'ut_inv_calendar', 'utm2latlon', 'uv2dv_cfd', 'uv2dvf', 'uv2dvg',
+                'uv2sfvpf', 'uv2sfvpg', 'uv2vr_cfd', 'uv2vrdvf', 'uv2vrdvg',
+                'uv2vrf', 'uv2vrg', 'v5d_close', 'v5d_create', 'v5d_setLowLev',
+                'v5d_setUnits', 'v5d_write', 'v5d_write_var', 'variance', 'vhaec',
+                'vhagc', 'vhsec', 'vhsgc', 'vibeta', 'vinth2p', 'vinth2p_ecmwf',
+                'vinth2p_ecmwf_nodes', 'vinth2p_nodes', 'vintp2p_ecmwf', 'vr2uvf',
+                'vr2uvg', 'vrdv2uvf', 'vrdv2uvg', 'wavelet', 'wavelet_default',
+                'weibull', 'wgt_area_smooth', 'wgt_areaave', 'wgt_areaave2',
+                'wgt_arearmse', 'wgt_arearmse2', 'wgt_areasum2', 'wgt_runave',
+                'wgt_runave_n', 'wgt_vert_avg_beta', 'wgt_volave', 'wgt_volave_ccm',
+                'wgt_volrmse', 'wgt_volrmse_ccm', 'where', 'wk_smooth121', 'wmbarb',
+                'wmbarbmap', 'wmdrft', 'wmgetp', 'wmlabs', 'wmsetp', 'wmstnm',
+                'wmvect', 'wmvectmap', 'wmvlbl', 'wrf_avo', 'wrf_cape_2d',
+                'wrf_cape_3d', 'wrf_dbz', 'wrf_eth', 'wrf_helicity', 'wrf_ij_to_ll',
+                'wrf_interp_1d', 'wrf_interp_2d_xy', 'wrf_interp_3d_z',
+                'wrf_latlon_to_ij', 'wrf_ll_to_ij', 'wrf_omega', 'wrf_pvo',
+                'wrf_rh', 'wrf_slp', 'wrf_smooth_2d', 'wrf_td', 'wrf_tk',
+                'wrf_updraft_helicity', 'wrf_uvmet', 'wrf_virtual_temp',
+                'wrf_wetbulb', 'wrf_wps_close_int', 'wrf_wps_open_int',
+                'wrf_wps_rddata_int', 'wrf_wps_rdhead_int', 'wrf_wps_read_int',
+                'wrf_wps_write_int', 'write_matrix', 'write_table', 'yiqrgb',
+                'z2geouv', 'zonal_mpsi', 'addfiles_GetVar', 'advect_variable',
+                'area_conserve_remap_Wrap', 'area_hi2lores_Wrap',
+                'array_append_record', 'assignFillValue', 'byte2flt',
+                'byte2flt_hdf', 'calcDayAnomTLL', 'calcMonAnomLLLT',
+                'calcMonAnomLLT', 'calcMonAnomTLL', 'calcMonAnomTLLL',
+                'calculate_monthly_values', 'cd_convert', 'changeCase',
+                'changeCaseChar', 'clmDayTLL', 'clmDayTLLL', 'clmMon2clmDay',
+                'clmMonLLLT', 'clmMonLLT', 'clmMonTLL', 'clmMonTLLL', 'closest_val',
+                'copy_VarAtts', 'copy_VarCoords', 'copy_VarCoords_1',
+                'copy_VarCoords_2', 'copy_VarMeta', 'copyatt', 'crossp3',
+                'cshstringtolist', 'cssgrid_Wrap', 'dble2flt', 'decimalPlaces',
+                'delete_VarAtts', 'dim_avg_n_Wrap', 'dim_avg_wgt_n_Wrap',
+                'dim_avg_wgt_Wrap', 'dim_avg_Wrap', 'dim_cumsum_n_Wrap',
+                'dim_cumsum_Wrap', 'dim_max_n_Wrap', 'dim_min_n_Wrap',
+                'dim_rmsd_n_Wrap', 'dim_rmsd_Wrap', 'dim_rmvmean_n_Wrap',
+                'dim_rmvmean_Wrap', 'dim_rmvmed_n_Wrap', 'dim_rmvmed_Wrap',
+                'dim_standardize_n_Wrap', 'dim_standardize_Wrap',
+                'dim_stddev_n_Wrap', 'dim_stddev_Wrap', 'dim_sum_n_Wrap',
+                'dim_sum_wgt_n_Wrap', 'dim_sum_wgt_Wrap', 'dim_sum_Wrap',
+                'dim_variance_n_Wrap', 'dim_variance_Wrap', 'dpres_plevel_Wrap',
+                'dtrend_leftdim', 'dv2uvF_Wrap', 'dv2uvG_Wrap', 'eof_north',
+                'eofcor_Wrap', 'eofcov_Wrap', 'eofunc_north', 'eofunc_ts_Wrap',
+                'eofunc_varimax_reorder', 'eofunc_varimax_Wrap', 'eofunc_Wrap',
+                'epsZero', 'f2fosh_Wrap', 'f2foshv_Wrap', 'f2fsh_Wrap',
+                'f2fshv_Wrap', 'f2gsh_Wrap', 'f2gshv_Wrap', 'fbindirSwap',
+                'fbinseqSwap1', 'fbinseqSwap2', 'flt2dble', 'flt2string',
+                'fo2fsh_Wrap', 'fo2fshv_Wrap', 'g2fsh_Wrap', 'g2fshv_Wrap',
+                'g2gsh_Wrap', 'g2gshv_Wrap', 'generate_resample_indices',
+                'generate_sample_indices', 'generate_unique_indices',
+                'genNormalDist', 'get1Dindex', 'get1Dindex_Collapse',
+                'get1Dindex_Exclude', 'get_file_suffix', 'GetFillColor',
+                'GetFillColorIndex', 'getFillValue', 'getind_latlon2d',
+                'getVarDimNames', 'getVarFillValue', 'grib_stime2itime',
+                'hyi2hyo_Wrap', 'ilapsF_Wrap', 'ilapsG_Wrap', 'ind_nearest_coord',
+                'indStrSubset', 'int2dble', 'int2flt', 'int2p_n_Wrap', 'int2p_Wrap',
+                'isMonotonic', 'isStrSubset', 'latGau', 'latGauWgt', 'latGlobeF',
+                'latGlobeFo', 'latRegWgt', 'linint1_n_Wrap', 'linint1_Wrap',
+                'linint2_points_Wrap', 'linint2_Wrap', 'local_max_1d',
+                'local_min_1d', 'lonFlip', 'lonGlobeF', 'lonGlobeFo', 'lonPivot',
+                'merge_levels_sfc', 'mod', 'month_to_annual',
+                'month_to_annual_weighted', 'month_to_season', 'month_to_season12',
+                'month_to_seasonN', 'monthly_total_to_daily_mean', 'nameDim',
+                'natgrid_Wrap', 'NewCosWeight', 'niceLatLon2D', 'NormCosWgtGlobe',
+                'numAsciiCol', 'numAsciiRow', 'numeric2int',
+                'obj_anal_ic_deprecated', 'obj_anal_ic_Wrap', 'omega_ccm_driver',
+                'omega_to_w', 'oneDtostring', 'pack_values', 'pattern_cor', 'pdfx',
+                'pdfxy', 'pdfxy_conform', 'pot_temp', 'pot_vort_hybrid',
+                'pot_vort_isobaric', 'pres2hybrid_Wrap', 'print_clock',
+                'printMinMax', 'quadroots', 'rcm2points_Wrap', 'rcm2rgrid_Wrap',
+                'readAsciiHead', 'readAsciiTable', 'reg_multlin_stats',
+                'region_ind', 'regline_stats', 'relhum_ttd', 'replaceSingleChar',
+                'RGBtoCmap', 'rgrid2rcm_Wrap', 'rho_mwjf', 'rm_single_dims',
+                'rmAnnCycle1D', 'rmInsufData', 'rmMonAnnCycLLLT', 'rmMonAnnCycLLT',
+                'rmMonAnnCycTLL', 'runave_n_Wrap', 'runave_Wrap', 'short2flt',
+                'short2flt_hdf', 'shsgc_R42_Wrap', 'sign_f90', 'sign_matlab',
+                'smth9_Wrap', 'smthClmDayTLL', 'smthClmDayTLLL', 'SqrtCosWeight',
+                'stat_dispersion', 'static_stability', 'stdMonLLLT', 'stdMonLLT',
+                'stdMonTLL', 'stdMonTLLL', 'symMinMaxPlt', 'table_attach_columns',
+                'table_attach_rows', 'time_to_newtime', 'transpose',
+                'triple2grid_Wrap', 'ut_convert', 'uv2dvF_Wrap', 'uv2dvG_Wrap',
+                'uv2vrF_Wrap', 'uv2vrG_Wrap', 'vr2uvF_Wrap', 'vr2uvG_Wrap',
+                'w_to_omega', 'wallClockElapseTime', 'wave_number_spc',
+                'wgt_areaave_Wrap', 'wgt_runave_leftdim', 'wgt_runave_n_Wrap',
+                'wgt_runave_Wrap', 'wgt_vertical_n', 'wind_component',
+                'wind_direction', 'yyyyddd_to_yyyymmdd', 'yyyymm_time',
+                'yyyymm_to_yyyyfrac', 'yyyymmdd_time', 'yyyymmdd_to_yyyyddd',
+                'yyyymmdd_to_yyyyfrac', 'yyyymmddhh_time', 'yyyymmddhh_to_yyyyfrac',
+                'zonal_mpsi_Wrap', 'zonalAve', 'calendar_decode2', 'cd_string',
+                'kf_filter', 'run_cor', 'time_axis_labels', 'ut_string',
+                'wrf_contour', 'wrf_map', 'wrf_map_overlay', 'wrf_map_overlays',
+                'wrf_map_resources', 'wrf_map_zoom', 'wrf_overlay', 'wrf_overlays',
+                'wrf_user_getvar', 'wrf_user_ij_to_ll', 'wrf_user_intrp2d',
+                'wrf_user_intrp3d', 'wrf_user_latlon_to_ij', 'wrf_user_list_times',
+                'wrf_user_ll_to_ij', 'wrf_user_unstagger', 'wrf_user_vert_interp',
+                'wrf_vector', 'gsn_add_annotation', 'gsn_add_polygon',
+                'gsn_add_polyline', 'gsn_add_polymarker',
+                'gsn_add_shapefile_polygons', 'gsn_add_shapefile_polylines',
+                'gsn_add_shapefile_polymarkers', 'gsn_add_text', 'gsn_attach_plots',
+                'gsn_blank_plot', 'gsn_contour', 'gsn_contour_map',
+                'gsn_contour_shade', 'gsn_coordinates', 'gsn_create_labelbar',
+                'gsn_create_legend', 'gsn_create_text',
+                'gsn_csm_attach_zonal_means', 'gsn_csm_blank_plot',
+                'gsn_csm_contour', 'gsn_csm_contour_map', 'gsn_csm_contour_map_ce',
+                'gsn_csm_contour_map_overlay', 'gsn_csm_contour_map_polar',
+                'gsn_csm_hov', 'gsn_csm_lat_time', 'gsn_csm_map', 'gsn_csm_map_ce',
+                'gsn_csm_map_polar', 'gsn_csm_pres_hgt',
+                'gsn_csm_pres_hgt_streamline', 'gsn_csm_pres_hgt_vector',
+                'gsn_csm_streamline', 'gsn_csm_streamline_contour_map',
+                'gsn_csm_streamline_contour_map_ce',
+                'gsn_csm_streamline_contour_map_polar', 'gsn_csm_streamline_map',
+                'gsn_csm_streamline_map_ce', 'gsn_csm_streamline_map_polar',
+                'gsn_csm_streamline_scalar', 'gsn_csm_streamline_scalar_map',
+                'gsn_csm_streamline_scalar_map_ce',
+                'gsn_csm_streamline_scalar_map_polar', 'gsn_csm_time_lat',
+                'gsn_csm_vector', 'gsn_csm_vector_map', 'gsn_csm_vector_map_ce',
+                'gsn_csm_vector_map_polar', 'gsn_csm_vector_scalar',
+                'gsn_csm_vector_scalar_map', 'gsn_csm_vector_scalar_map_ce',
+                'gsn_csm_vector_scalar_map_polar', 'gsn_csm_x2y', 'gsn_csm_x2y2',
+                'gsn_csm_xy', 'gsn_csm_xy2', 'gsn_csm_xy3', 'gsn_csm_y',
+                'gsn_define_colormap', 'gsn_draw_colormap', 'gsn_draw_named_colors',
+                'gsn_histogram', 'gsn_labelbar_ndc', 'gsn_legend_ndc', 'gsn_map',
+                'gsn_merge_colormaps', 'gsn_open_wks', 'gsn_panel', 'gsn_polygon',
+                'gsn_polygon_ndc', 'gsn_polyline', 'gsn_polyline_ndc',
+                'gsn_polymarker', 'gsn_polymarker_ndc', 'gsn_retrieve_colormap',
+                'gsn_reverse_colormap', 'gsn_streamline', 'gsn_streamline_map',
+                'gsn_streamline_scalar', 'gsn_streamline_scalar_map', 'gsn_table',
+                'gsn_text', 'gsn_text_ndc', 'gsn_vector', 'gsn_vector_map',
+                'gsn_vector_scalar', 'gsn_vector_scalar_map', 'gsn_xy', 'gsn_y',
+                'hsv2rgb', 'maximize_output', 'namedcolor2rgb', 'namedcolor2rgba',
+                'reset_device_coordinates', 'span_named_colors'), prefix=r'\b'),
+             Name.Builtin),
+
+            # Resources
+            (words((
+                'amDataXF', 'amDataYF', 'amJust', 'amOn', 'amOrthogonalPosF',
+                'amParallelPosF', 'amResizeNotify', 'amSide', 'amTrackData',
+                'amViewId', 'amZone', 'appDefaultParent', 'appFileSuffix',
+                'appResources', 'appSysDir', 'appUsrDir', 'caCopyArrays',
+                'caXArray', 'caXCast', 'caXMaxV', 'caXMinV', 'caXMissingV',
+                'caYArray', 'caYCast', 'caYMaxV', 'caYMinV', 'caYMissingV',
+                'cnCellFillEdgeColor', 'cnCellFillMissingValEdgeColor',
+                'cnConpackParams', 'cnConstFEnableFill', 'cnConstFLabelAngleF',
+                'cnConstFLabelBackgroundColor', 'cnConstFLabelConstantSpacingF',
+                'cnConstFLabelFont', 'cnConstFLabelFontAspectF',
+                'cnConstFLabelFontColor', 'cnConstFLabelFontHeightF',
+                'cnConstFLabelFontQuality', 'cnConstFLabelFontThicknessF',
+                'cnConstFLabelFormat', 'cnConstFLabelFuncCode', 'cnConstFLabelJust',
+                'cnConstFLabelOn', 'cnConstFLabelOrthogonalPosF',
+                'cnConstFLabelParallelPosF', 'cnConstFLabelPerimColor',
+                'cnConstFLabelPerimOn', 'cnConstFLabelPerimSpaceF',
+                'cnConstFLabelPerimThicknessF', 'cnConstFLabelSide',
+                'cnConstFLabelString', 'cnConstFLabelTextDirection',
+                'cnConstFLabelZone', 'cnConstFUseInfoLabelRes',
+                'cnExplicitLabelBarLabelsOn', 'cnExplicitLegendLabelsOn',
+                'cnExplicitLineLabelsOn', 'cnFillBackgroundColor', 'cnFillColor',
+                'cnFillColors', 'cnFillDotSizeF', 'cnFillDrawOrder', 'cnFillMode',
+                'cnFillOn', 'cnFillOpacityF', 'cnFillPalette', 'cnFillPattern',
+                'cnFillPatterns', 'cnFillScaleF', 'cnFillScales', 'cnFixFillBleed',
+                'cnGridBoundFillColor', 'cnGridBoundFillPattern',
+                'cnGridBoundFillScaleF', 'cnGridBoundPerimColor',
+                'cnGridBoundPerimDashPattern', 'cnGridBoundPerimOn',
+                'cnGridBoundPerimThicknessF', 'cnHighLabelAngleF',
+                'cnHighLabelBackgroundColor', 'cnHighLabelConstantSpacingF',
+                'cnHighLabelCount', 'cnHighLabelFont', 'cnHighLabelFontAspectF',
+                'cnHighLabelFontColor', 'cnHighLabelFontHeightF',
+                'cnHighLabelFontQuality', 'cnHighLabelFontThicknessF',
+                'cnHighLabelFormat', 'cnHighLabelFuncCode', 'cnHighLabelPerimColor',
+                'cnHighLabelPerimOn', 'cnHighLabelPerimSpaceF',
+                'cnHighLabelPerimThicknessF', 'cnHighLabelString', 'cnHighLabelsOn',
+                'cnHighLowLabelOverlapMode', 'cnHighUseLineLabelRes',
+                'cnInfoLabelAngleF', 'cnInfoLabelBackgroundColor',
+                'cnInfoLabelConstantSpacingF', 'cnInfoLabelFont',
+                'cnInfoLabelFontAspectF', 'cnInfoLabelFontColor',
+                'cnInfoLabelFontHeightF', 'cnInfoLabelFontQuality',
+                'cnInfoLabelFontThicknessF', 'cnInfoLabelFormat',
+                'cnInfoLabelFuncCode', 'cnInfoLabelJust', 'cnInfoLabelOn',
+                'cnInfoLabelOrthogonalPosF', 'cnInfoLabelParallelPosF',
+                'cnInfoLabelPerimColor', 'cnInfoLabelPerimOn',
+                'cnInfoLabelPerimSpaceF', 'cnInfoLabelPerimThicknessF',
+                'cnInfoLabelSide', 'cnInfoLabelString', 'cnInfoLabelTextDirection',
+                'cnInfoLabelZone', 'cnLabelBarEndLabelsOn', 'cnLabelBarEndStyle',
+                'cnLabelDrawOrder', 'cnLabelMasking', 'cnLabelScaleFactorF',
+                'cnLabelScaleValueF', 'cnLabelScalingMode', 'cnLegendLevelFlags',
+                'cnLevelCount', 'cnLevelFlag', 'cnLevelFlags', 'cnLevelSelectionMode',
+                'cnLevelSpacingF', 'cnLevels', 'cnLineColor', 'cnLineColors',
+                'cnLineDashPattern', 'cnLineDashPatterns', 'cnLineDashSegLenF',
+                'cnLineDrawOrder', 'cnLineLabelAngleF', 'cnLineLabelBackgroundColor',
+                'cnLineLabelConstantSpacingF', 'cnLineLabelCount',
+                'cnLineLabelDensityF', 'cnLineLabelFont', 'cnLineLabelFontAspectF',
+                'cnLineLabelFontColor', 'cnLineLabelFontColors',
+                'cnLineLabelFontHeightF', 'cnLineLabelFontQuality',
+                'cnLineLabelFontThicknessF', 'cnLineLabelFormat',
+                'cnLineLabelFuncCode', 'cnLineLabelInterval', 'cnLineLabelPerimColor',
+                'cnLineLabelPerimOn', 'cnLineLabelPerimSpaceF',
+                'cnLineLabelPerimThicknessF', 'cnLineLabelPlacementMode',
+                'cnLineLabelStrings', 'cnLineLabelsOn', 'cnLinePalette',
+                'cnLineThicknessF', 'cnLineThicknesses', 'cnLinesOn',
+                'cnLowLabelAngleF', 'cnLowLabelBackgroundColor',
+                'cnLowLabelConstantSpacingF', 'cnLowLabelCount', 'cnLowLabelFont',
+                'cnLowLabelFontAspectF', 'cnLowLabelFontColor',
+                'cnLowLabelFontHeightF', 'cnLowLabelFontQuality',
+                'cnLowLabelFontThicknessF', 'cnLowLabelFormat', 'cnLowLabelFuncCode',
+                'cnLowLabelPerimColor', 'cnLowLabelPerimOn', 'cnLowLabelPerimSpaceF',
+                'cnLowLabelPerimThicknessF', 'cnLowLabelString', 'cnLowLabelsOn',
+                'cnLowUseHighLabelRes', 'cnMaxDataValueFormat', 'cnMaxLevelCount',
+                'cnMaxLevelValF', 'cnMaxPointDistanceF', 'cnMinLevelValF',
+                'cnMissingValFillColor', 'cnMissingValFillPattern',
+                'cnMissingValFillScaleF', 'cnMissingValPerimColor',
+                'cnMissingValPerimDashPattern', 'cnMissingValPerimGridBoundOn',
+                'cnMissingValPerimOn', 'cnMissingValPerimThicknessF',
+                'cnMonoFillColor', 'cnMonoFillPattern', 'cnMonoFillScale',
+                'cnMonoLevelFlag', 'cnMonoLineColor', 'cnMonoLineDashPattern',
+                'cnMonoLineLabelFontColor', 'cnMonoLineThickness', 'cnNoDataLabelOn',
+                'cnNoDataLabelString', 'cnOutOfRangeFillColor',
+                'cnOutOfRangeFillPattern', 'cnOutOfRangeFillScaleF',
+                'cnOutOfRangePerimColor', 'cnOutOfRangePerimDashPattern',
+                'cnOutOfRangePerimOn', 'cnOutOfRangePerimThicknessF',
+                'cnRasterCellSizeF', 'cnRasterMinCellSizeF', 'cnRasterModeOn',
+                'cnRasterSampleFactorF', 'cnRasterSmoothingOn', 'cnScalarFieldData',
+                'cnSmoothingDistanceF', 'cnSmoothingOn', 'cnSmoothingTensionF',
+                'cnSpanFillPalette', 'cnSpanLinePalette', 'ctCopyTables',
+                'ctXElementSize', 'ctXMaxV', 'ctXMinV', 'ctXMissingV', 'ctXTable',
+                'ctXTableLengths', 'ctXTableType', 'ctYElementSize', 'ctYMaxV',
+                'ctYMinV', 'ctYMissingV', 'ctYTable', 'ctYTableLengths',
+                'ctYTableType', 'dcDelayCompute', 'errBuffer',
+                'errFileName', 'errFilePtr', 'errLevel', 'errPrint', 'errUnitNumber',
+                'gsClipOn', 'gsColors', 'gsEdgeColor', 'gsEdgeDashPattern',
+                'gsEdgeDashSegLenF', 'gsEdgeThicknessF', 'gsEdgesOn',
+                'gsFillBackgroundColor', 'gsFillColor', 'gsFillDotSizeF',
+                'gsFillIndex', 'gsFillLineThicknessF', 'gsFillOpacityF',
+                'gsFillScaleF', 'gsFont', 'gsFontAspectF', 'gsFontColor',
+                'gsFontHeightF', 'gsFontOpacityF', 'gsFontQuality',
+                'gsFontThicknessF', 'gsLineColor', 'gsLineDashPattern',
+                'gsLineDashSegLenF', 'gsLineLabelConstantSpacingF', 'gsLineLabelFont',
+                'gsLineLabelFontAspectF', 'gsLineLabelFontColor',
+                'gsLineLabelFontHeightF', 'gsLineLabelFontQuality',
+                'gsLineLabelFontThicknessF', 'gsLineLabelFuncCode',
+                'gsLineLabelString', 'gsLineOpacityF', 'gsLineThicknessF',
+                'gsMarkerColor', 'gsMarkerIndex', 'gsMarkerOpacityF', 'gsMarkerSizeF',
+                'gsMarkerThicknessF', 'gsSegments', 'gsTextAngleF',
+                'gsTextConstantSpacingF', 'gsTextDirection', 'gsTextFuncCode',
+                'gsTextJustification', 'gsnAboveYRefLineBarColors',
+                'gsnAboveYRefLineBarFillScales', 'gsnAboveYRefLineBarPatterns',
+                'gsnAboveYRefLineColor', 'gsnAddCyclic', 'gsnAttachBorderOn',
+                'gsnAttachPlotsXAxis', 'gsnBelowYRefLineBarColors',
+                'gsnBelowYRefLineBarFillScales', 'gsnBelowYRefLineBarPatterns',
+                'gsnBelowYRefLineColor', 'gsnBoxMargin', 'gsnCenterString',
+                'gsnCenterStringFontColor', 'gsnCenterStringFontHeightF',
+                'gsnCenterStringFuncCode', 'gsnCenterStringOrthogonalPosF',
+                'gsnCenterStringParallelPosF', 'gsnContourLineThicknessesScale',
+                'gsnContourNegLineDashPattern', 'gsnContourPosLineDashPattern',
+                'gsnContourZeroLineThicknessF', 'gsnDebugWriteFileName', 'gsnDraw',
+                'gsnFrame', 'gsnHistogramBarWidthPercent', 'gsnHistogramBinIntervals',
+                'gsnHistogramBinMissing', 'gsnHistogramBinWidth',
+                'gsnHistogramClassIntervals', 'gsnHistogramCompare',
+                'gsnHistogramComputePercentages',
+                'gsnHistogramComputePercentagesNoMissing',
+                'gsnHistogramDiscreteBinValues', 'gsnHistogramDiscreteClassValues',
+                'gsnHistogramHorizontal', 'gsnHistogramMinMaxBinsOn',
+                'gsnHistogramNumberOfBins', 'gsnHistogramPercentSign',
+                'gsnHistogramSelectNiceIntervals', 'gsnLeftString',
+                'gsnLeftStringFontColor', 'gsnLeftStringFontHeightF',
+                'gsnLeftStringFuncCode', 'gsnLeftStringOrthogonalPosF',
+                'gsnLeftStringParallelPosF', 'gsnMajorLatSpacing',
+                'gsnMajorLonSpacing', 'gsnMaskLambertConformal',
+                'gsnMaskLambertConformalOutlineOn', 'gsnMaximize',
+                'gsnMinorLatSpacing', 'gsnMinorLonSpacing', 'gsnPanelBottom',
+                'gsnPanelCenter', 'gsnPanelDebug', 'gsnPanelFigureStrings',
+                'gsnPanelFigureStringsBackgroundFillColor',
+                'gsnPanelFigureStringsFontHeightF', 'gsnPanelFigureStringsJust',
+                'gsnPanelFigureStringsPerimOn', 'gsnPanelLabelBar', 'gsnPanelLeft',
+                'gsnPanelMainFont', 'gsnPanelMainFontColor',
+                'gsnPanelMainFontHeightF', 'gsnPanelMainString', 'gsnPanelRight',
+                'gsnPanelRowSpec', 'gsnPanelScalePlotIndex', 'gsnPanelTop',
+                'gsnPanelXF', 'gsnPanelXWhiteSpacePercent', 'gsnPanelYF',
+                'gsnPanelYWhiteSpacePercent', 'gsnPaperHeight', 'gsnPaperMargin',
+                'gsnPaperOrientation', 'gsnPaperWidth', 'gsnPolar',
+                'gsnPolarLabelDistance', 'gsnPolarLabelFont',
+                'gsnPolarLabelFontHeightF', 'gsnPolarLabelSpacing', 'gsnPolarTime',
+                'gsnPolarUT', 'gsnRightString', 'gsnRightStringFontColor',
+                'gsnRightStringFontHeightF', 'gsnRightStringFuncCode',
+                'gsnRightStringOrthogonalPosF', 'gsnRightStringParallelPosF',
+                'gsnScalarContour', 'gsnScale', 'gsnShape', 'gsnSpreadColorEnd',
+                'gsnSpreadColorStart', 'gsnSpreadColors', 'gsnStringFont',
+                'gsnStringFontColor', 'gsnStringFontHeightF', 'gsnStringFuncCode',
+                'gsnTickMarksOn', 'gsnXAxisIrregular2Linear', 'gsnXAxisIrregular2Log',
+                'gsnXRefLine', 'gsnXRefLineColor', 'gsnXRefLineDashPattern',
+                'gsnXRefLineThicknessF', 'gsnXYAboveFillColors', 'gsnXYBarChart',
+                'gsnXYBarChartBarWidth', 'gsnXYBarChartColors',
+                'gsnXYBarChartColors2', 'gsnXYBarChartFillDotSizeF',
+                'gsnXYBarChartFillLineThicknessF', 'gsnXYBarChartFillOpacityF',
+                'gsnXYBarChartFillScaleF', 'gsnXYBarChartOutlineOnly',
+                'gsnXYBarChartOutlineThicknessF', 'gsnXYBarChartPatterns',
+                'gsnXYBarChartPatterns2', 'gsnXYBelowFillColors', 'gsnXYFillColors',
+                'gsnXYFillOpacities', 'gsnXYLeftFillColors', 'gsnXYRightFillColors',
+                'gsnYAxisIrregular2Linear', 'gsnYAxisIrregular2Log', 'gsnYRefLine',
+                'gsnYRefLineColor', 'gsnYRefLineColors', 'gsnYRefLineDashPattern',
+                'gsnYRefLineDashPatterns', 'gsnYRefLineThicknessF',
+                'gsnYRefLineThicknesses', 'gsnZonalMean', 'gsnZonalMeanXMaxF',
+                'gsnZonalMeanXMinF', 'gsnZonalMeanYRefLine', 'lbAutoManage',
+                'lbBottomMarginF', 'lbBoxCount', 'lbBoxEndCapStyle', 'lbBoxFractions',
+                'lbBoxLineColor', 'lbBoxLineDashPattern', 'lbBoxLineDashSegLenF',
+                'lbBoxLineThicknessF', 'lbBoxLinesOn', 'lbBoxMajorExtentF',
+                'lbBoxMinorExtentF', 'lbBoxSeparatorLinesOn', 'lbBoxSizing',
+                'lbFillBackground', 'lbFillColor', 'lbFillColors', 'lbFillDotSizeF',
+                'lbFillLineThicknessF', 'lbFillPattern', 'lbFillPatterns',
+                'lbFillScaleF', 'lbFillScales', 'lbJustification', 'lbLabelAlignment',
+                'lbLabelAngleF', 'lbLabelAutoStride', 'lbLabelBarOn',
+                'lbLabelConstantSpacingF', 'lbLabelDirection', 'lbLabelFont',
+                'lbLabelFontAspectF', 'lbLabelFontColor', 'lbLabelFontHeightF',
+                'lbLabelFontQuality', 'lbLabelFontThicknessF', 'lbLabelFuncCode',
+                'lbLabelJust', 'lbLabelOffsetF', 'lbLabelPosition', 'lbLabelStride',
+                'lbLabelStrings', 'lbLabelsOn', 'lbLeftMarginF', 'lbMaxLabelLenF',
+                'lbMinLabelSpacingF', 'lbMonoFillColor', 'lbMonoFillPattern',
+                'lbMonoFillScale', 'lbOrientation', 'lbPerimColor',
+                'lbPerimDashPattern', 'lbPerimDashSegLenF', 'lbPerimFill',
+                'lbPerimFillColor', 'lbPerimOn', 'lbPerimThicknessF',
+                'lbRasterFillOn', 'lbRightMarginF', 'lbTitleAngleF',
+                'lbTitleConstantSpacingF', 'lbTitleDirection', 'lbTitleExtentF',
+                'lbTitleFont', 'lbTitleFontAspectF', 'lbTitleFontColor',
+                'lbTitleFontHeightF', 'lbTitleFontQuality', 'lbTitleFontThicknessF',
+                'lbTitleFuncCode', 'lbTitleJust', 'lbTitleOffsetF', 'lbTitleOn',
+                'lbTitlePosition', 'lbTitleString', 'lbTopMarginF', 'lgAutoManage',
+                'lgBottomMarginF', 'lgBoxBackground', 'lgBoxLineColor',
+                'lgBoxLineDashPattern', 'lgBoxLineDashSegLenF', 'lgBoxLineThicknessF',
+                'lgBoxLinesOn', 'lgBoxMajorExtentF', 'lgBoxMinorExtentF',
+                'lgDashIndex', 'lgDashIndexes', 'lgItemCount', 'lgItemOrder',
+                'lgItemPlacement', 'lgItemPositions', 'lgItemType', 'lgItemTypes',
+                'lgJustification', 'lgLabelAlignment', 'lgLabelAngleF',
+                'lgLabelAutoStride', 'lgLabelConstantSpacingF', 'lgLabelDirection',
+                'lgLabelFont', 'lgLabelFontAspectF', 'lgLabelFontColor',
+                'lgLabelFontHeightF', 'lgLabelFontQuality', 'lgLabelFontThicknessF',
+                'lgLabelFuncCode', 'lgLabelJust', 'lgLabelOffsetF', 'lgLabelPosition',
+                'lgLabelStride', 'lgLabelStrings', 'lgLabelsOn', 'lgLeftMarginF',
+                'lgLegendOn', 'lgLineColor', 'lgLineColors', 'lgLineDashSegLenF',
+                'lgLineDashSegLens', 'lgLineLabelConstantSpacingF', 'lgLineLabelFont',
+                'lgLineLabelFontAspectF', 'lgLineLabelFontColor',
+                'lgLineLabelFontColors', 'lgLineLabelFontHeightF',
+                'lgLineLabelFontHeights', 'lgLineLabelFontQuality',
+                'lgLineLabelFontThicknessF', 'lgLineLabelFuncCode',
+                'lgLineLabelStrings', 'lgLineLabelsOn', 'lgLineThicknessF',
+                'lgLineThicknesses', 'lgMarkerColor', 'lgMarkerColors',
+                'lgMarkerIndex', 'lgMarkerIndexes', 'lgMarkerSizeF', 'lgMarkerSizes',
+                'lgMarkerThicknessF', 'lgMarkerThicknesses', 'lgMonoDashIndex',
+                'lgMonoItemType', 'lgMonoLineColor', 'lgMonoLineDashSegLen',
+                'lgMonoLineLabelFontColor', 'lgMonoLineLabelFontHeight',
+                'lgMonoLineThickness', 'lgMonoMarkerColor', 'lgMonoMarkerIndex',
+                'lgMonoMarkerSize', 'lgMonoMarkerThickness', 'lgOrientation',
+                'lgPerimColor', 'lgPerimDashPattern', 'lgPerimDashSegLenF',
+                'lgPerimFill', 'lgPerimFillColor', 'lgPerimOn', 'lgPerimThicknessF',
+                'lgRightMarginF', 'lgTitleAngleF', 'lgTitleConstantSpacingF',
+                'lgTitleDirection', 'lgTitleExtentF', 'lgTitleFont',
+                'lgTitleFontAspectF', 'lgTitleFontColor', 'lgTitleFontHeightF',
+                'lgTitleFontQuality', 'lgTitleFontThicknessF', 'lgTitleFuncCode',
+                'lgTitleJust', 'lgTitleOffsetF', 'lgTitleOn', 'lgTitlePosition',
+                'lgTitleString', 'lgTopMarginF', 'mpAreaGroupCount',
+                'mpAreaMaskingOn', 'mpAreaNames', 'mpAreaTypes', 'mpBottomAngleF',
+                'mpBottomMapPosF', 'mpBottomNDCF', 'mpBottomNPCF',
+                'mpBottomPointLatF', 'mpBottomPointLonF', 'mpBottomWindowF',
+                'mpCenterLatF', 'mpCenterLonF', 'mpCenterRotF', 'mpCountyLineColor',
+                'mpCountyLineDashPattern', 'mpCountyLineDashSegLenF',
+                'mpCountyLineThicknessF', 'mpDataBaseVersion', 'mpDataResolution',
+                'mpDataSetName', 'mpDefaultFillColor', 'mpDefaultFillPattern',
+                'mpDefaultFillScaleF', 'mpDynamicAreaGroups', 'mpEllipticalBoundary',
+                'mpFillAreaSpecifiers', 'mpFillBoundarySets', 'mpFillColor',
+                'mpFillColors', 'mpFillColors-default', 'mpFillDotSizeF',
+                'mpFillDrawOrder', 'mpFillOn', 'mpFillPatternBackground',
+                'mpFillPattern', 'mpFillPatterns', 'mpFillPatterns-default',
+                'mpFillScaleF', 'mpFillScales', 'mpFillScales-default',
+                'mpFixedAreaGroups', 'mpGeophysicalLineColor',
+                'mpGeophysicalLineDashPattern', 'mpGeophysicalLineDashSegLenF',
+                'mpGeophysicalLineThicknessF', 'mpGreatCircleLinesOn',
+                'mpGridAndLimbDrawOrder', 'mpGridAndLimbOn', 'mpGridLatSpacingF',
+                'mpGridLineColor', 'mpGridLineDashPattern', 'mpGridLineDashSegLenF',
+                'mpGridLineThicknessF', 'mpGridLonSpacingF', 'mpGridMaskMode',
+                'mpGridMaxLatF', 'mpGridPolarLonSpacingF', 'mpGridSpacingF',
+                'mpInlandWaterFillColor', 'mpInlandWaterFillPattern',
+                'mpInlandWaterFillScaleF', 'mpLabelDrawOrder', 'mpLabelFontColor',
+                'mpLabelFontHeightF', 'mpLabelsOn', 'mpLambertMeridianF',
+                'mpLambertParallel1F', 'mpLambertParallel2F', 'mpLandFillColor',
+                'mpLandFillPattern', 'mpLandFillScaleF', 'mpLeftAngleF',
+                'mpLeftCornerLatF', 'mpLeftCornerLonF', 'mpLeftMapPosF',
+                'mpLeftNDCF', 'mpLeftNPCF', 'mpLeftPointLatF',
+                'mpLeftPointLonF', 'mpLeftWindowF', 'mpLimbLineColor',
+                'mpLimbLineDashPattern', 'mpLimbLineDashSegLenF',
+                'mpLimbLineThicknessF', 'mpLimitMode', 'mpMaskAreaSpecifiers',
+                'mpMaskOutlineSpecifiers', 'mpMaxLatF', 'mpMaxLonF',
+                'mpMinLatF', 'mpMinLonF', 'mpMonoFillColor', 'mpMonoFillPattern',
+                'mpMonoFillScale', 'mpNationalLineColor', 'mpNationalLineDashPattern',
+                'mpNationalLineThicknessF', 'mpOceanFillColor', 'mpOceanFillPattern',
+                'mpOceanFillScaleF', 'mpOutlineBoundarySets', 'mpOutlineDrawOrder',
+                'mpOutlineMaskingOn', 'mpOutlineOn', 'mpOutlineSpecifiers',
+                'mpPerimDrawOrder', 'mpPerimLineColor', 'mpPerimLineDashPattern',
+                'mpPerimLineDashSegLenF', 'mpPerimLineThicknessF', 'mpPerimOn',
+                'mpPolyMode', 'mpProjection', 'mpProvincialLineColor',
+                'mpProvincialLineDashPattern', 'mpProvincialLineDashSegLenF',
+                'mpProvincialLineThicknessF', 'mpRelativeCenterLat',
+                'mpRelativeCenterLon', 'mpRightAngleF', 'mpRightCornerLatF',
+                'mpRightCornerLonF', 'mpRightMapPosF', 'mpRightNDCF',
+                'mpRightNPCF', 'mpRightPointLatF', 'mpRightPointLonF',
+                'mpRightWindowF', 'mpSatelliteAngle1F', 'mpSatelliteAngle2F',
+                'mpSatelliteDistF', 'mpShapeMode', 'mpSpecifiedFillColors',
+                'mpSpecifiedFillDirectIndexing', 'mpSpecifiedFillPatterns',
+                'mpSpecifiedFillPriority', 'mpSpecifiedFillScales',
+                'mpTopAngleF', 'mpTopMapPosF', 'mpTopNDCF', 'mpTopNPCF',
+                'mpTopPointLatF', 'mpTopPointLonF', 'mpTopWindowF',
+                'mpUSStateLineColor', 'mpUSStateLineDashPattern',
+                'mpUSStateLineDashSegLenF', 'mpUSStateLineThicknessF',
+                'pmAnnoManagers', 'pmAnnoViews', 'pmLabelBarDisplayMode',
+                'pmLabelBarHeightF', 'pmLabelBarKeepAspect', 'pmLabelBarOrthogonalPosF',
+                'pmLabelBarParallelPosF', 'pmLabelBarSide', 'pmLabelBarWidthF',
+                'pmLabelBarZone', 'pmLegendDisplayMode', 'pmLegendHeightF',
+                'pmLegendKeepAspect', 'pmLegendOrthogonalPosF',
+                'pmLegendParallelPosF', 'pmLegendSide', 'pmLegendWidthF',
+                'pmLegendZone', 'pmOverlaySequenceIds', 'pmTickMarkDisplayMode',
+                'pmTickMarkZone', 'pmTitleDisplayMode', 'pmTitleZone',
+                'prGraphicStyle', 'prPolyType', 'prXArray', 'prYArray',
+                'sfCopyData', 'sfDataArray', 'sfDataMaxV', 'sfDataMinV',
+                'sfElementNodes', 'sfExchangeDimensions', 'sfFirstNodeIndex',
+                'sfMissingValueV', 'sfXArray', 'sfXCActualEndF', 'sfXCActualStartF',
+                'sfXCEndIndex', 'sfXCEndSubsetV', 'sfXCEndV', 'sfXCStartIndex',
+                'sfXCStartSubsetV', 'sfXCStartV', 'sfXCStride', 'sfXCellBounds',
+                'sfYArray', 'sfYCActualEndF', 'sfYCActualStartF', 'sfYCEndIndex',
+                'sfYCEndSubsetV', 'sfYCEndV', 'sfYCStartIndex', 'sfYCStartSubsetV',
+                'sfYCStartV', 'sfYCStride', 'sfYCellBounds', 'stArrowLengthF',
+                'stArrowStride', 'stCrossoverCheckCount',
+                'stExplicitLabelBarLabelsOn', 'stLabelBarEndLabelsOn',
+                'stLabelFormat', 'stLengthCheckCount', 'stLevelColors',
+                'stLevelCount', 'stLevelPalette', 'stLevelSelectionMode',
+                'stLevelSpacingF', 'stLevels', 'stLineColor', 'stLineOpacityF',
+                'stLineStartStride', 'stLineThicknessF', 'stMapDirection',
+                'stMaxLevelCount', 'stMaxLevelValF', 'stMinArrowSpacingF',
+                'stMinDistanceF', 'stMinLevelValF', 'stMinLineSpacingF',
+                'stMinStepFactorF', 'stMonoLineColor', 'stNoDataLabelOn',
+                'stNoDataLabelString', 'stScalarFieldData', 'stScalarMissingValColor',
+                'stSpanLevelPalette', 'stStepSizeF', 'stStreamlineDrawOrder',
+                'stUseScalarArray', 'stVectorFieldData', 'stZeroFLabelAngleF',
+                'stZeroFLabelBackgroundColor', 'stZeroFLabelConstantSpacingF',
+                'stZeroFLabelFont', 'stZeroFLabelFontAspectF',
+                'stZeroFLabelFontColor', 'stZeroFLabelFontHeightF',
+                'stZeroFLabelFontQuality', 'stZeroFLabelFontThicknessF',
+                'stZeroFLabelFuncCode', 'stZeroFLabelJust', 'stZeroFLabelOn',
+                'stZeroFLabelOrthogonalPosF', 'stZeroFLabelParallelPosF',
+                'stZeroFLabelPerimColor', 'stZeroFLabelPerimOn',
+                'stZeroFLabelPerimSpaceF', 'stZeroFLabelPerimThicknessF',
+                'stZeroFLabelSide', 'stZeroFLabelString', 'stZeroFLabelTextDirection',
+                'stZeroFLabelZone', 'tfDoNDCOverlay', 'tfPlotManagerOn',
+                'tfPolyDrawList', 'tfPolyDrawOrder', 'tiDeltaF', 'tiMainAngleF',
+                'tiMainConstantSpacingF', 'tiMainDirection', 'tiMainFont',
+                'tiMainFontAspectF', 'tiMainFontColor', 'tiMainFontHeightF',
+                'tiMainFontQuality', 'tiMainFontThicknessF', 'tiMainFuncCode',
+                'tiMainJust', 'tiMainOffsetXF', 'tiMainOffsetYF', 'tiMainOn',
+                'tiMainPosition', 'tiMainSide', 'tiMainString', 'tiUseMainAttributes',
+                'tiXAxisAngleF', 'tiXAxisConstantSpacingF', 'tiXAxisDirection',
+                'tiXAxisFont', 'tiXAxisFontAspectF', 'tiXAxisFontColor',
+                'tiXAxisFontHeightF', 'tiXAxisFontQuality', 'tiXAxisFontThicknessF',
+                'tiXAxisFuncCode', 'tiXAxisJust', 'tiXAxisOffsetXF',
+                'tiXAxisOffsetYF', 'tiXAxisOn', 'tiXAxisPosition', 'tiXAxisSide',
+                'tiXAxisString', 'tiYAxisAngleF', 'tiYAxisConstantSpacingF',
+                'tiYAxisDirection', 'tiYAxisFont', 'tiYAxisFontAspectF',
+                'tiYAxisFontColor', 'tiYAxisFontHeightF', 'tiYAxisFontQuality',
+                'tiYAxisFontThicknessF', 'tiYAxisFuncCode', 'tiYAxisJust',
+                'tiYAxisOffsetXF', 'tiYAxisOffsetYF', 'tiYAxisOn', 'tiYAxisPosition',
+                'tiYAxisSide', 'tiYAxisString', 'tmBorderLineColor',
+                'tmBorderThicknessF', 'tmEqualizeXYSizes', 'tmLabelAutoStride',
+                'tmSciNoteCutoff', 'tmXBAutoPrecision', 'tmXBBorderOn',
+                'tmXBDataLeftF', 'tmXBDataRightF', 'tmXBFormat', 'tmXBIrrTensionF',
+                'tmXBIrregularPoints', 'tmXBLabelAngleF', 'tmXBLabelConstantSpacingF',
+                'tmXBLabelDeltaF', 'tmXBLabelDirection', 'tmXBLabelFont',
+                'tmXBLabelFontAspectF', 'tmXBLabelFontColor', 'tmXBLabelFontHeightF',
+                'tmXBLabelFontQuality', 'tmXBLabelFontThicknessF',
+                'tmXBLabelFuncCode', 'tmXBLabelJust', 'tmXBLabelStride', 'tmXBLabels',
+                'tmXBLabelsOn', 'tmXBMajorLengthF', 'tmXBMajorLineColor',
+                'tmXBMajorOutwardLengthF', 'tmXBMajorThicknessF', 'tmXBMaxLabelLenF',
+                'tmXBMaxTicks', 'tmXBMinLabelSpacingF', 'tmXBMinorLengthF',
+                'tmXBMinorLineColor', 'tmXBMinorOn', 'tmXBMinorOutwardLengthF',
+                'tmXBMinorPerMajor', 'tmXBMinorThicknessF', 'tmXBMinorValues',
+                'tmXBMode', 'tmXBOn', 'tmXBPrecision', 'tmXBStyle', 'tmXBTickEndF',
+                'tmXBTickSpacingF', 'tmXBTickStartF', 'tmXBValues', 'tmXMajorGrid',
+                'tmXMajorGridLineColor', 'tmXMajorGridLineDashPattern',
+                'tmXMajorGridThicknessF', 'tmXMinorGrid', 'tmXMinorGridLineColor',
+                'tmXMinorGridLineDashPattern', 'tmXMinorGridThicknessF',
+                'tmXTAutoPrecision', 'tmXTBorderOn', 'tmXTDataLeftF',
+                'tmXTDataRightF', 'tmXTFormat', 'tmXTIrrTensionF',
+                'tmXTIrregularPoints', 'tmXTLabelAngleF', 'tmXTLabelConstantSpacingF',
+                'tmXTLabelDeltaF', 'tmXTLabelDirection', 'tmXTLabelFont',
+                'tmXTLabelFontAspectF', 'tmXTLabelFontColor', 'tmXTLabelFontHeightF',
+                'tmXTLabelFontQuality', 'tmXTLabelFontThicknessF',
+                'tmXTLabelFuncCode', 'tmXTLabelJust', 'tmXTLabelStride', 'tmXTLabels',
+                'tmXTLabelsOn', 'tmXTMajorLengthF', 'tmXTMajorLineColor',
+                'tmXTMajorOutwardLengthF', 'tmXTMajorThicknessF', 'tmXTMaxLabelLenF',
+                'tmXTMaxTicks', 'tmXTMinLabelSpacingF', 'tmXTMinorLengthF',
+                'tmXTMinorLineColor', 'tmXTMinorOn', 'tmXTMinorOutwardLengthF',
+                'tmXTMinorPerMajor', 'tmXTMinorThicknessF', 'tmXTMinorValues',
+                'tmXTMode', 'tmXTOn', 'tmXTPrecision', 'tmXTStyle', 'tmXTTickEndF',
+                'tmXTTickSpacingF', 'tmXTTickStartF', 'tmXTValues', 'tmXUseBottom',
+                'tmYLAutoPrecision', 'tmYLBorderOn', 'tmYLDataBottomF',
+                'tmYLDataTopF', 'tmYLFormat', 'tmYLIrrTensionF',
+                'tmYLIrregularPoints', 'tmYLLabelAngleF', 'tmYLLabelConstantSpacingF',
+                'tmYLLabelDeltaF', 'tmYLLabelDirection', 'tmYLLabelFont',
+                'tmYLLabelFontAspectF', 'tmYLLabelFontColor', 'tmYLLabelFontHeightF',
+                'tmYLLabelFontQuality', 'tmYLLabelFontThicknessF',
+                'tmYLLabelFuncCode', 'tmYLLabelJust', 'tmYLLabelStride', 'tmYLLabels',
+                'tmYLLabelsOn', 'tmYLMajorLengthF', 'tmYLMajorLineColor',
+                'tmYLMajorOutwardLengthF', 'tmYLMajorThicknessF', 'tmYLMaxLabelLenF',
+                'tmYLMaxTicks', 'tmYLMinLabelSpacingF', 'tmYLMinorLengthF',
+                'tmYLMinorLineColor', 'tmYLMinorOn', 'tmYLMinorOutwardLengthF',
+                'tmYLMinorPerMajor', 'tmYLMinorThicknessF', 'tmYLMinorValues',
+                'tmYLMode', 'tmYLOn', 'tmYLPrecision', 'tmYLStyle', 'tmYLTickEndF',
+                'tmYLTickSpacingF', 'tmYLTickStartF', 'tmYLValues', 'tmYMajorGrid',
+                'tmYMajorGridLineColor', 'tmYMajorGridLineDashPattern',
+                'tmYMajorGridThicknessF', 'tmYMinorGrid', 'tmYMinorGridLineColor',
+                'tmYMinorGridLineDashPattern', 'tmYMinorGridThicknessF',
+                'tmYRAutoPrecision', 'tmYRBorderOn', 'tmYRDataBottomF',
+                'tmYRDataTopF', 'tmYRFormat', 'tmYRIrrTensionF',
+                'tmYRIrregularPoints', 'tmYRLabelAngleF', 'tmYRLabelConstantSpacingF',
+                'tmYRLabelDeltaF', 'tmYRLabelDirection', 'tmYRLabelFont',
+                'tmYRLabelFontAspectF', 'tmYRLabelFontColor', 'tmYRLabelFontHeightF',
+                'tmYRLabelFontQuality', 'tmYRLabelFontThicknessF',
+                'tmYRLabelFuncCode', 'tmYRLabelJust', 'tmYRLabelStride', 'tmYRLabels',
+                'tmYRLabelsOn', 'tmYRMajorLengthF', 'tmYRMajorLineColor',
+                'tmYRMajorOutwardLengthF', 'tmYRMajorThicknessF', 'tmYRMaxLabelLenF',
+                'tmYRMaxTicks', 'tmYRMinLabelSpacingF', 'tmYRMinorLengthF',
+                'tmYRMinorLineColor', 'tmYRMinorOn', 'tmYRMinorOutwardLengthF',
+                'tmYRMinorPerMajor', 'tmYRMinorThicknessF', 'tmYRMinorValues',
+                'tmYRMode', 'tmYROn', 'tmYRPrecision', 'tmYRStyle', 'tmYRTickEndF',
+                'tmYRTickSpacingF', 'tmYRTickStartF', 'tmYRValues', 'tmYUseLeft',
+                'trGridType', 'trLineInterpolationOn',
+                'trXAxisType', 'trXCoordPoints', 'trXInterPoints', 'trXLog',
+                'trXMaxF', 'trXMinF', 'trXReverse', 'trXSamples', 'trXTensionF',
+                'trYAxisType', 'trYCoordPoints', 'trYInterPoints', 'trYLog',
+                'trYMaxF', 'trYMinF', 'trYReverse', 'trYSamples', 'trYTensionF',
+                'txAngleF', 'txBackgroundFillColor', 'txConstantSpacingF', 'txDirection',
+                'txFont', 'HLU-Fonts', 'txFontAspectF', 'txFontColor',
+                'txFontHeightF', 'txFontOpacityF', 'txFontQuality',
+                'txFontThicknessF', 'txFuncCode', 'txJust', 'txPerimColor',
+                'txPerimDashLengthF', 'txPerimDashPattern', 'txPerimOn',
+                'txPerimSpaceF', 'txPerimThicknessF', 'txPosXF', 'txPosYF',
+                'txString', 'vcExplicitLabelBarLabelsOn', 'vcFillArrowEdgeColor',
+                'vcFillArrowEdgeThicknessF', 'vcFillArrowFillColor',
+                'vcFillArrowHeadInteriorXF', 'vcFillArrowHeadMinFracXF',
+                'vcFillArrowHeadMinFracYF', 'vcFillArrowHeadXF', 'vcFillArrowHeadYF',
+                'vcFillArrowMinFracWidthF', 'vcFillArrowWidthF', 'vcFillArrowsOn',
+                'vcFillOverEdge', 'vcGlyphOpacityF', 'vcGlyphStyle',
+                'vcLabelBarEndLabelsOn', 'vcLabelFontColor', 'vcLabelFontHeightF',
+                'vcLabelsOn', 'vcLabelsUseVectorColor', 'vcLevelColors',
+                'vcLevelCount', 'vcLevelPalette', 'vcLevelSelectionMode',
+                'vcLevelSpacingF', 'vcLevels', 'vcLineArrowColor',
+                'vcLineArrowHeadMaxSizeF', 'vcLineArrowHeadMinSizeF',
+                'vcLineArrowThicknessF', 'vcMagnitudeFormat',
+                'vcMagnitudeScaleFactorF', 'vcMagnitudeScaleValueF',
+                'vcMagnitudeScalingMode', 'vcMapDirection', 'vcMaxLevelCount',
+                'vcMaxLevelValF', 'vcMaxMagnitudeF', 'vcMinAnnoAngleF',
+                'vcMinAnnoArrowAngleF', 'vcMinAnnoArrowEdgeColor',
+                'vcMinAnnoArrowFillColor', 'vcMinAnnoArrowLineColor',
+                'vcMinAnnoArrowMinOffsetF', 'vcMinAnnoArrowSpaceF',
+                'vcMinAnnoArrowUseVecColor', 'vcMinAnnoBackgroundColor',
+                'vcMinAnnoConstantSpacingF', 'vcMinAnnoExplicitMagnitudeF',
+                'vcMinAnnoFont', 'vcMinAnnoFontAspectF', 'vcMinAnnoFontColor',
+                'vcMinAnnoFontHeightF', 'vcMinAnnoFontQuality',
+                'vcMinAnnoFontThicknessF', 'vcMinAnnoFuncCode', 'vcMinAnnoJust',
+                'vcMinAnnoOn', 'vcMinAnnoOrientation', 'vcMinAnnoOrthogonalPosF',
+                'vcMinAnnoParallelPosF', 'vcMinAnnoPerimColor', 'vcMinAnnoPerimOn',
+                'vcMinAnnoPerimSpaceF', 'vcMinAnnoPerimThicknessF', 'vcMinAnnoSide',
+                'vcMinAnnoString1', 'vcMinAnnoString1On', 'vcMinAnnoString2',
+                'vcMinAnnoString2On', 'vcMinAnnoTextDirection', 'vcMinAnnoZone',
+                'vcMinDistanceF', 'vcMinFracLengthF', 'vcMinLevelValF',
+                'vcMinMagnitudeF', 'vcMonoFillArrowEdgeColor',
+                'vcMonoFillArrowFillColor', 'vcMonoLineArrowColor',
+                'vcMonoWindBarbColor', 'vcNoDataLabelOn', 'vcNoDataLabelString',
+                'vcPositionMode', 'vcRefAnnoAngleF', 'vcRefAnnoArrowAngleF',
+                'vcRefAnnoArrowEdgeColor', 'vcRefAnnoArrowFillColor',
+                'vcRefAnnoArrowLineColor', 'vcRefAnnoArrowMinOffsetF',
+                'vcRefAnnoArrowSpaceF', 'vcRefAnnoArrowUseVecColor',
+                'vcRefAnnoBackgroundColor', 'vcRefAnnoConstantSpacingF',
+                'vcRefAnnoExplicitMagnitudeF', 'vcRefAnnoFont',
+                'vcRefAnnoFontAspectF', 'vcRefAnnoFontColor', 'vcRefAnnoFontHeightF',
+                'vcRefAnnoFontQuality', 'vcRefAnnoFontThicknessF',
+                'vcRefAnnoFuncCode', 'vcRefAnnoJust', 'vcRefAnnoOn',
+                'vcRefAnnoOrientation', 'vcRefAnnoOrthogonalPosF',
+                'vcRefAnnoParallelPosF', 'vcRefAnnoPerimColor', 'vcRefAnnoPerimOn',
+                'vcRefAnnoPerimSpaceF', 'vcRefAnnoPerimThicknessF', 'vcRefAnnoSide',
+                'vcRefAnnoString1', 'vcRefAnnoString1On', 'vcRefAnnoString2',
+                'vcRefAnnoString2On', 'vcRefAnnoTextDirection', 'vcRefAnnoZone',
+                'vcRefLengthF', 'vcRefMagnitudeF', 'vcScalarFieldData',
+                'vcScalarMissingValColor', 'vcScalarValueFormat',
+                'vcScalarValueScaleFactorF', 'vcScalarValueScaleValueF',
+                'vcScalarValueScalingMode', 'vcSpanLevelPalette', 'vcUseRefAnnoRes',
+                'vcUseScalarArray', 'vcVectorDrawOrder', 'vcVectorFieldData',
+                'vcWindBarbCalmCircleSizeF', 'vcWindBarbColor',
+                'vcWindBarbLineThicknessF', 'vcWindBarbScaleFactorF',
+                'vcWindBarbTickAngleF', 'vcWindBarbTickLengthF',
+                'vcWindBarbTickSpacingF', 'vcZeroFLabelAngleF',
+                'vcZeroFLabelBackgroundColor', 'vcZeroFLabelConstantSpacingF',
+                'vcZeroFLabelFont', 'vcZeroFLabelFontAspectF',
+                'vcZeroFLabelFontColor', 'vcZeroFLabelFontHeightF',
+                'vcZeroFLabelFontQuality', 'vcZeroFLabelFontThicknessF',
+                'vcZeroFLabelFuncCode', 'vcZeroFLabelJust', 'vcZeroFLabelOn',
+                'vcZeroFLabelOrthogonalPosF', 'vcZeroFLabelParallelPosF',
+                'vcZeroFLabelPerimColor', 'vcZeroFLabelPerimOn',
+                'vcZeroFLabelPerimSpaceF', 'vcZeroFLabelPerimThicknessF',
+                'vcZeroFLabelSide', 'vcZeroFLabelString', 'vcZeroFLabelTextDirection',
+                'vcZeroFLabelZone', 'vfCopyData', 'vfDataArray',
+                'vfExchangeDimensions', 'vfExchangeUVData', 'vfMagMaxV', 'vfMagMinV',
+                'vfMissingUValueV', 'vfMissingVValueV', 'vfPolarData',
+                'vfSingleMissingValue', 'vfUDataArray', 'vfUMaxV', 'vfUMinV',
+                'vfVDataArray', 'vfVMaxV', 'vfVMinV', 'vfXArray', 'vfXCActualEndF',
+                'vfXCActualStartF', 'vfXCEndIndex', 'vfXCEndSubsetV', 'vfXCEndV',
+                'vfXCStartIndex', 'vfXCStartSubsetV', 'vfXCStartV', 'vfXCStride',
+                'vfYArray', 'vfYCActualEndF', 'vfYCActualStartF', 'vfYCEndIndex',
+                'vfYCEndSubsetV', 'vfYCEndV', 'vfYCStartIndex', 'vfYCStartSubsetV',
+                'vfYCStartV', 'vfYCStride', 'vpAnnoManagerId', 'vpClipOn',
+                'vpHeightF', 'vpKeepAspect', 'vpOn', 'vpUseSegments', 'vpWidthF',
+                'vpXF', 'vpYF', 'wkAntiAlias', 'wkBackgroundColor', 'wkBackgroundOpacityF',
+                'wkColorMapLen', 'wkColorMap', 'wkColorModel', 'wkDashTableLength',
+                'wkDefGraphicStyleId', 'wkDeviceLowerX', 'wkDeviceLowerY',
+                'wkDeviceUpperX', 'wkDeviceUpperY', 'wkFileName', 'wkFillTableLength',
+                'wkForegroundColor', 'wkFormat', 'wkFullBackground', 'wkGksWorkId',
+                'wkHeight', 'wkMarkerTableLength', 'wkMetaName', 'wkOrientation',
+                'wkPDFFileName', 'wkPDFFormat', 'wkPDFResolution', 'wkPSFileName',
+                'wkPSFormat', 'wkPSResolution', 'wkPaperHeightF', 'wkPaperSize',
+                'wkPaperWidthF', 'wkPause', 'wkTopLevelViews', 'wkViews',
+                'wkVisualType', 'wkWidth', 'wkWindowId', 'wkXColorMode', 'wsCurrentSize',
+                'wsMaximumSize', 'wsThresholdSize', 'xyComputeXMax',
+                'xyComputeXMin', 'xyComputeYMax', 'xyComputeYMin', 'xyCoordData',
+                'xyCoordDataSpec', 'xyCurveDrawOrder', 'xyDashPattern',
+                'xyDashPatterns', 'xyExplicitLabels', 'xyExplicitLegendLabels',
+                'xyLabelMode', 'xyLineColor', 'xyLineColors', 'xyLineDashSegLenF',
+                'xyLineLabelConstantSpacingF', 'xyLineLabelFont',
+                'xyLineLabelFontAspectF', 'xyLineLabelFontColor',
+                'xyLineLabelFontColors', 'xyLineLabelFontHeightF',
+                'xyLineLabelFontQuality', 'xyLineLabelFontThicknessF',
+                'xyLineLabelFuncCode', 'xyLineThicknessF', 'xyLineThicknesses',
+                'xyMarkLineMode', 'xyMarkLineModes', 'xyMarker', 'xyMarkerColor',
+                'xyMarkerColors', 'xyMarkerSizeF', 'xyMarkerSizes',
+                'xyMarkerThicknessF', 'xyMarkerThicknesses', 'xyMarkers',
+                'xyMonoDashPattern', 'xyMonoLineColor', 'xyMonoLineLabelFontColor',
+                'xyMonoLineThickness', 'xyMonoMarkLineMode', 'xyMonoMarker',
+                'xyMonoMarkerColor', 'xyMonoMarkerSize', 'xyMonoMarkerThickness',
+                'xyXIrrTensionF', 'xyXIrregularPoints', 'xyXStyle', 'xyYIrrTensionF',
+                'xyYIrregularPoints', 'xyYStyle'), prefix=r'\b'),
+             Name.Builtin),
+
+            # Booleans
+            (r'\.(True|False)\.', Name.Builtin),
+            # Comparing Operators
+            (r'\.(eq|ne|lt|le|gt|ge|not|and|or|xor)\.', Operator.Word),
+        ],
+
+        'strings': [
+            (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
+        ],
+
+        'nums': [
+            (r'\d+(?![.e])(_[a-z]\w+)?', Number.Integer),
+            (r'[+-]?\d*\.\d+(e[-+]?\d+)?(_[a-z]\w+)?', Number.Float),
+            (r'[+-]?\d+\.\d*(e[-+]?\d+)?(_[a-z]\w+)?', Number.Float),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/nimrod.py b/.venv/lib/python3.12/site-packages/pygments/lexers/nimrod.py
new file mode 100644
index 0000000..b99e246
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/nimrod.py
@@ -0,0 +1,199 @@
+"""
+    pygments.lexers.nimrod
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for the Nim language (formerly known as Nimrod).
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, default, bygroups
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Error
+
+__all__ = ['NimrodLexer']
+
+
+class NimrodLexer(RegexLexer):
+    """
+    For Nim source code.
+    """
+
+    name = 'Nimrod'
+    url = 'http://nim-lang.org/'
+    aliases = ['nimrod', 'nim']
+    filenames = ['*.nim', '*.nimrod']
+    mimetypes = ['text/x-nim']
+    version_added = '1.5'
+
+    flags = re.MULTILINE | re.IGNORECASE
+
+    def underscorize(words):
+        newWords = []
+        new = []
+        for word in words:
+            for ch in word:
+                new.append(ch)
+                new.append("_?")
+            newWords.append(''.join(new))
+            new = []
+        return "|".join(newWords)
+
+    keywords = [
+        'addr', 'and', 'as', 'asm', 'bind', 'block', 'break', 'case',
+        'cast', 'concept', 'const', 'continue', 'converter', 'defer', 'discard',
+        'distinct', 'div', 'do', 'elif', 'else', 'end', 'enum', 'except',
+        'export', 'finally', 'for', 'if', 'in', 'yield', 'interface',
+        'is', 'isnot', 'iterator', 'let', 'mixin', 'mod',
+        'not', 'notin', 'object', 'of', 'or', 'out', 'ptr', 'raise',
+        'ref', 'return', 'shl', 'shr', 'static', 'try',
+        'tuple', 'type', 'using', 'when', 'while', 'xor'
+    ]
+
+    keywordsPseudo = [
+        'nil', 'true', 'false'
+    ]
+
+    opWords = [
+        'and', 'or', 'not', 'xor', 'shl', 'shr', 'div', 'mod', 'in',
+        'notin', 'is', 'isnot'
+    ]
+
+    types = [
+        'int', 'int8', 'int16', 'int32', 'int64', 'float', 'float32', 'float64',
+        'bool', 'char', 'range', 'array', 'seq', 'set', 'string'
+    ]
+
+    tokens = {
+        'root': [
+            # Comments
+            (r'##\[', String.Doc, 'doccomment'),
+            (r'##.*$', String.Doc),
+            (r'#\[', Comment.Multiline, 'comment'),
+            (r'#.*$', Comment),
+
+            # Pragmas
+            (r'\{\.', String.Other, 'pragma'),
+
+            # Operators
+            (r'[*=><+\-/@$~&%!?|\\\[\]]', Operator),
+            (r'\.\.|\.|,|\[\.|\.\]|\{\.|\.\}|\(\.|\.\)|\{|\}|\(|\)|:|\^|`|;',
+             Punctuation),
+
+            # Case statement branch
+            (r'(\n\s*)(of)(\s)', bygroups(Text.Whitespace, Keyword,
+                                          Text.Whitespace), 'casebranch'),
+
+            # Strings
+            (r'(?:[\w]+)"', String, 'rdqs'),
+            (r'"""', String.Double, 'tdqs'),
+            ('"', String, 'dqs'),
+
+            # Char
+            ("'", String.Char, 'chars'),
+
+            # Keywords
+            (rf'({underscorize(opWords)})\b', Operator.Word),
+            (r'(proc|func|method|macro|template)(\s)(?![(\[\]])',
+             bygroups(Keyword, Text.Whitespace), 'funcname'),
+            (rf'({underscorize(keywords)})\b', Keyword),
+            (r'({})\b'.format(underscorize(['from', 'import', 'include', 'export'])),
+             Keyword.Namespace),
+            (r'(v_?a_?r)\b', Keyword.Declaration),
+            (rf'({underscorize(types)})\b', Name.Builtin),
+            (rf'({underscorize(keywordsPseudo)})\b', Keyword.Pseudo),
+
+            # Identifiers
+            (r'\b((?![_\d])\w)(((?!_)\w)|(_(?!_)\w))*', Name),
+
+            # Numbers
+            (r'[0-9][0-9_]*(?=([e.]|\'f(32|64)))',
+             Number.Float, ('float-suffix', 'float-number')),
+            (r'0x[a-f0-9][a-f0-9_]*', Number.Hex, 'int-suffix'),
+            (r'0b[01][01_]*', Number.Bin, 'int-suffix'),
+            (r'0o[0-7][0-7_]*', Number.Oct, 'int-suffix'),
+            (r'[0-9][0-9_]*', Number.Integer, 'int-suffix'),
+
+            # Whitespace
+            (r'\s+', Text.Whitespace),
+            (r'.+$', Error),
+        ],
+        'chars': [
+            (r'\\([\\abcefnrtvl"\']|x[a-f0-9]{2}|[0-9]{1,3})', String.Escape),
+            (r"'", String.Char, '#pop'),
+            (r".", String.Char)
+        ],
+        'strings': [
+            (r'(?|>=|>>|>|<=|<<|<|\+|-|=|/|\*|%|\+=|-=|!|@', Operator),
+            (r'\(|\)|\[|\]|,|\.\.\.|\.\.|\.|::|:', Punctuation),
+            (r'`\{[^`]*`\}', Text),  # Extern blocks won't be Lexed by Nit
+            (r'[\r\n\t ]+', Text),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/nix.py b/.venv/lib/python3.12/site-packages/pygments/lexers/nix.py
new file mode 100644
index 0000000..16572af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/nix.py
@@ -0,0 +1,144 @@
+"""
+    pygments.lexers.nix
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the NixOS Nix language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Literal
+
+__all__ = ['NixLexer']
+
+
+class NixLexer(RegexLexer):
+    """
+    For the Nix language.
+    """
+
+    name = 'Nix'
+    url = 'http://nixos.org/nix/'
+    aliases = ['nixos', 'nix']
+    filenames = ['*.nix']
+    mimetypes = ['text/x-nix']
+    version_added = '2.0'
+
+    keywords = ['rec', 'with', 'let', 'in', 'inherit', 'assert', 'if',
+                'else', 'then', '...']
+    builtins = ['import', 'abort', 'baseNameOf', 'dirOf', 'isNull', 'builtins',
+                'map', 'removeAttrs', 'throw', 'toString', 'derivation']
+    operators = ['++', '+', '?', '.', '!', '//', '==', '/',
+                 '!=', '&&', '||', '->', '=', '<', '>', '*', '-']
+
+    punctuations = ["(", ")", "[", "]", ";", "{", "}", ":", ",", "@"]
+
+    tokens = {
+        'root': [
+            # comments starting with #
+            (r'#.*$', Comment.Single),
+
+            # multiline comments
+            (r'/\*', Comment.Multiline, 'comment'),
+
+            # whitespace
+            (r'\s+', Text),
+
+            # keywords
+            ('({})'.format('|'.join(re.escape(entry) + '\\b' for entry in keywords)), Keyword),
+
+            # highlight the builtins
+            ('({})'.format('|'.join(re.escape(entry) + '\\b' for entry in builtins)),
+             Name.Builtin),
+
+            (r'\b(true|false|null)\b', Name.Constant),
+
+            # floats
+            (r'-?(\d+\.\d*|\.\d+)([eE][-+]?\d+)?', Number.Float),
+
+            # integers
+            (r'-?[0-9]+', Number.Integer),
+
+            # paths
+            (r'[\w.+-]*(\/[\w.+-]+)+', Literal),
+            (r'~(\/[\w.+-]+)+', Literal),
+            (r'\<[\w.+-]+(\/[\w.+-]+)*\>', Literal),
+
+            # operators
+            ('({})'.format('|'.join(re.escape(entry) for entry in operators)),
+             Operator),
+
+            # word operators
+            (r'\b(or|and)\b', Operator.Word),
+
+            (r'\{', Punctuation, 'block'),
+
+            # punctuations
+            ('({})'.format('|'.join(re.escape(entry) for entry in punctuations)), Punctuation),
+
+            # strings
+            (r'"', String.Double, 'doublequote'),
+            (r"''", String.Multiline, 'multiline'),
+
+            # urls
+            (r'[a-zA-Z][a-zA-Z0-9\+\-\.]*\:[\w%/?:@&=+$,\\.!~*\'-]+', Literal),
+
+            # names of variables
+            (r'[\w-]+(?=\s*=)', String.Symbol),
+            (r'[a-zA-Z_][\w\'-]*', Text),
+
+            (r"\$\{", String.Interpol, 'antiquote'),
+        ],
+        'comment': [
+            (r'[^/*]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline),
+        ],
+        'multiline': [
+            (r"''(\$|'|\\n|\\r|\\t|\\)", String.Escape),
+            (r"''", String.Multiline, '#pop'),
+            (r'\$\{', String.Interpol, 'antiquote'),
+            (r"[^'\$]+", String.Multiline),
+            (r"\$[^\{']", String.Multiline),
+            (r"'[^']", String.Multiline),
+            (r"\$(?=')", String.Multiline),
+        ],
+        'doublequote': [
+            (r'\\(\\|"|\$|n)', String.Escape),
+            (r'"', String.Double, '#pop'),
+            (r'\$\{', String.Interpol, 'antiquote'),
+            (r'[^"\\\$]+', String.Double),
+            (r'\$[^\{"]', String.Double),
+            (r'\$(?=")', String.Double),
+            (r'\\', String.Double),
+        ],
+        'antiquote': [
+            (r"\}", String.Interpol, '#pop'),
+            # TODO: we should probably escape also here ''${ \${
+            (r"\$\{", String.Interpol, '#push'),
+            include('root'),
+        ],
+        'block': [
+            (r"\}", Punctuation, '#pop'),
+            include('root'),
+        ],
+    }
+
+    def analyse_text(text):
+        rv = 0.0
+        # TODO: let/in
+        if re.search(r'import.+?<[^>]+>', text):
+            rv += 0.4
+        if re.search(r'mkDerivation\s+(\(|\{|rec)', text):
+            rv += 0.4
+        if re.search(r'=\s+mkIf\s+', text):
+            rv += 0.4
+        if re.search(r'\{[a-zA-Z,\s]+\}:', text):
+            rv += 0.1
+        return rv
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/numbair.py b/.venv/lib/python3.12/site-packages/pygments/lexers/numbair.py
new file mode 100644
index 0000000..28395b4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/numbair.py
@@ -0,0 +1,63 @@
+"""
+    pygments.lexers.numbair
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for other Numba Intermediate Representation.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, include, bygroups, words
+from pygments.token import Whitespace, Name, String,  Punctuation, Keyword, \
+    Operator, Number
+
+__all__ = ["NumbaIRLexer"]
+
+class NumbaIRLexer(RegexLexer):
+    """
+    Lexer for Numba IR
+    """
+    name = 'Numba_IR'
+    url = "https://numba.readthedocs.io/en/stable/developer/architecture.html#stage-2-generate-the-numba-ir"
+    aliases = ['numba_ir', 'numbair']
+    filenames = ['*.numba_ir']
+    mimetypes = ['text/x-numba_ir', 'text/x-numbair']
+    version_added = '2.19'
+
+    identifier = r'\$[a-zA-Z0-9._]+'
+    fun_or_var = r'([a-zA-Z_]+[a-zA-Z0-9]*)'
+
+    tokens = {
+        'root' : [
+            (r'(label)(\ [0-9]+)(:)$',
+                bygroups(Keyword, Name.Label, Punctuation)),
+
+            (r'=', Operator),
+            include('whitespace'),
+            include('keyword'),
+
+            (identifier, Name.Variable),
+            (fun_or_var + r'(\()',
+                bygroups(Name.Function, Punctuation)),
+            (fun_or_var + r'(\=)',
+                bygroups(Name.Attribute, Punctuation)),
+            (fun_or_var, Name.Constant),
+            (r'[0-9]+', Number),
+
+            # 
+            (r'<[^>\n]*>', String),
+
+            (r'[=<>{}\[\]()*.,!\':]|x\b', Punctuation)
+        ],
+
+        'keyword':[
+            (words((
+                'del', 'jump', 'call', 'branch',
+            ), suffix=' '), Keyword),
+        ],
+
+        'whitespace': [
+            (r'(\n|\s)+', Whitespace),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/oberon.py b/.venv/lib/python3.12/site-packages/pygments/lexers/oberon.py
new file mode 100644
index 0000000..11cf571
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/oberon.py
@@ -0,0 +1,120 @@
+"""
+    pygments.lexers.oberon
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Oberon family languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['ComponentPascalLexer']
+
+
+class ComponentPascalLexer(RegexLexer):
+    """
+    For Component Pascal source code.
+    """
+    name = 'Component Pascal'
+    aliases = ['componentpascal', 'cp']
+    filenames = ['*.cp', '*.cps']
+    mimetypes = ['text/x-component-pascal']
+    url = 'https://blackboxframework.org'
+    version_added = '2.1'
+
+    flags = re.MULTILINE | re.DOTALL
+
+    tokens = {
+        'root': [
+            include('whitespace'),
+            include('comments'),
+            include('punctuation'),
+            include('numliterals'),
+            include('strings'),
+            include('operators'),
+            include('builtins'),
+            include('identifiers'),
+        ],
+        'whitespace': [
+            (r'\n+', Text),  # blank lines
+            (r'\s+', Text),  # whitespace
+        ],
+        'comments': [
+            (r'\(\*([^$].*?)\*\)', Comment.Multiline),
+            # TODO: nested comments (* (* ... *) ... (* ... *) *) not supported!
+        ],
+        'punctuation': [
+            (r'[()\[\]{},.:;|]', Punctuation),
+        ],
+        'numliterals': [
+            (r'[0-9A-F]+X\b', Number.Hex),                 # char code
+            (r'[0-9A-F]+[HL]\b', Number.Hex),              # hexadecimal number
+            (r'[0-9]+\.[0-9]+E[+-][0-9]+', Number.Float),  # real number
+            (r'[0-9]+\.[0-9]+', Number.Float),             # real number
+            (r'[0-9]+', Number.Integer),                   # decimal whole number
+        ],
+        'strings': [
+            (r"'[^\n']*'", String),  # single quoted string
+            (r'"[^\n"]*"', String),  # double quoted string
+        ],
+        'operators': [
+            # Arithmetic Operators
+            (r'[+-]', Operator),
+            (r'[*/]', Operator),
+            # Relational Operators
+            (r'[=#<>]', Operator),
+            # Dereferencing Operator
+            (r'\^', Operator),
+            # Logical AND Operator
+            (r'&', Operator),
+            # Logical NOT Operator
+            (r'~', Operator),
+            # Assignment Symbol
+            (r':=', Operator),
+            # Range Constructor
+            (r'\.\.', Operator),
+            (r'\$', Operator),
+        ],
+        'identifiers': [
+            (r'([a-zA-Z_$][\w$]*)', Name),
+        ],
+        'builtins': [
+            (words((
+                'ANYPTR', 'ANYREC', 'BOOLEAN', 'BYTE', 'CHAR', 'INTEGER', 'LONGINT',
+                'REAL', 'SET', 'SHORTCHAR', 'SHORTINT', 'SHORTREAL'
+                ), suffix=r'\b'), Keyword.Type),
+            (words((
+                'ABS', 'ABSTRACT', 'ARRAY', 'ASH', 'ASSERT', 'BEGIN', 'BITS', 'BY',
+                'CAP', 'CASE', 'CHR', 'CLOSE', 'CONST', 'DEC', 'DIV', 'DO', 'ELSE',
+                'ELSIF', 'EMPTY', 'END', 'ENTIER', 'EXCL', 'EXIT', 'EXTENSIBLE', 'FOR',
+                'HALT', 'IF', 'IMPORT', 'IN', 'INC', 'INCL', 'IS', 'LEN', 'LIMITED',
+                'LONG', 'LOOP', 'MAX', 'MIN', 'MOD', 'MODULE', 'NEW', 'ODD', 'OF',
+                'OR', 'ORD', 'OUT', 'POINTER', 'PROCEDURE', 'RECORD', 'REPEAT', 'RETURN',
+                'SHORT', 'SHORTCHAR', 'SHORTINT', 'SIZE', 'THEN', 'TYPE', 'TO', 'UNTIL',
+                'VAR', 'WHILE', 'WITH'
+                ), suffix=r'\b'), Keyword.Reserved),
+            (r'(TRUE|FALSE|NIL|INF)\b', Keyword.Constant),
+        ]
+    }
+
+    def analyse_text(text):
+        """The only other lexer using .cp is the C++ one, so we check if for
+        a few common Pascal keywords here. Those are unfortunately quite
+        common across various business languages as well."""
+        result = 0
+        if 'BEGIN' in text:
+            result += 0.01
+        if 'END' in text:
+            result += 0.01
+        if 'PROCEDURE' in text:
+            result += 0.01
+        if 'MODULE' in text:
+            result += 0.01
+
+        return result
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/objective.py b/.venv/lib/python3.12/site-packages/pygments/lexers/objective.py
new file mode 100644
index 0000000..ce281fb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/objective.py
@@ -0,0 +1,513 @@
+"""
+    pygments.lexers.objective
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Objective-C family languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, using, this, words, \
+    inherit, default
+from pygments.token import Text, Keyword, Name, String, Operator, \
+    Number, Punctuation, Literal, Comment, Whitespace
+
+from pygments.lexers.c_cpp import CLexer, CppLexer
+
+__all__ = ['ObjectiveCLexer', 'ObjectiveCppLexer', 'LogosLexer', 'SwiftLexer']
+
+
+def objective(baselexer):
+    """
+    Generate a subclass of baselexer that accepts the Objective-C syntax
+    extensions.
+    """
+
+    # Have to be careful not to accidentally match JavaDoc/Doxygen syntax here,
+    # since that's quite common in ordinary C/C++ files.  It's OK to match
+    # JavaDoc/Doxygen keywords that only apply to Objective-C, mind.
+    #
+    # The upshot of this is that we CANNOT match @class or @interface
+    _oc_keywords = re.compile(r'@(?:end|implementation|protocol)')
+
+    # Matches [ ? identifier  ( identifier ? ] |  identifier? : )
+    # (note the identifier is *optional* when there is a ':'!)
+    _oc_message = re.compile(r'\[\s*[a-zA-Z_]\w*\s+'
+                             r'(?:[a-zA-Z_]\w*\s*\]|'
+                             r'(?:[a-zA-Z_]\w*)?:)')
+
+    class GeneratedObjectiveCVariant(baselexer):
+        """
+        Implements Objective-C syntax on top of an existing C family lexer.
+        """
+
+        tokens = {
+            'statements': [
+                (r'@"', String, 'string'),
+                (r'@(YES|NO)', Number),
+                (r"@'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char),
+                (r'@(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[lL]?', Number.Float),
+                (r'@(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float),
+                (r'@0x[0-9a-fA-F]+[Ll]?', Number.Hex),
+                (r'@0[0-7]+[Ll]?', Number.Oct),
+                (r'@\d+[Ll]?', Number.Integer),
+                (r'@\(', Literal, 'literal_number'),
+                (r'@\[', Literal, 'literal_array'),
+                (r'@\{', Literal, 'literal_dictionary'),
+                (words((
+                    '@selector', '@private', '@protected', '@public', '@encode',
+                    '@synchronized', '@try', '@throw', '@catch', '@finally',
+                    '@end', '@property', '@synthesize', '__bridge', '__bridge_transfer',
+                    '__autoreleasing', '__block', '__weak', '__strong', 'weak', 'strong',
+                    'copy', 'retain', 'assign', 'unsafe_unretained', 'atomic', 'nonatomic',
+                    'readonly', 'readwrite', 'setter', 'getter', 'typeof', 'in',
+                    'out', 'inout', 'release', 'class', '@dynamic', '@optional',
+                    '@required', '@autoreleasepool', '@import'), suffix=r'\b'),
+                 Keyword),
+                (words(('id', 'instancetype', 'Class', 'IMP', 'SEL', 'BOOL',
+                        'IBOutlet', 'IBAction', 'unichar'), suffix=r'\b'),
+                 Keyword.Type),
+                (r'@(true|false|YES|NO)\n', Name.Builtin),
+                (r'(YES|NO|nil|self|super)\b', Name.Builtin),
+                # Carbon types
+                (r'(Boolean|UInt8|SInt8|UInt16|SInt16|UInt32|SInt32)\b', Keyword.Type),
+                # Carbon built-ins
+                (r'(TRUE|FALSE)\b', Name.Builtin),
+                (r'(@interface|@implementation)(\s+)', bygroups(Keyword, Text),
+                 ('#pop', 'oc_classname')),
+                (r'(@class|@protocol)(\s+)', bygroups(Keyword, Text),
+                 ('#pop', 'oc_forward_classname')),
+                # @ can also prefix other expressions like @{...} or @(...)
+                (r'@', Punctuation),
+                inherit,
+            ],
+            'oc_classname': [
+                # interface definition that inherits
+                (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?(\s*)(\{)',
+                 bygroups(Name.Class, Text, Name.Class, Text, Punctuation),
+                 ('#pop', 'oc_ivars')),
+                (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?',
+                 bygroups(Name.Class, Text, Name.Class), '#pop'),
+                # interface definition for a category
+                (r'([a-zA-Z$_][\w$]*)(\s*)(\([a-zA-Z$_][\w$]*\))(\s*)(\{)',
+                 bygroups(Name.Class, Text, Name.Label, Text, Punctuation),
+                 ('#pop', 'oc_ivars')),
+                (r'([a-zA-Z$_][\w$]*)(\s*)(\([a-zA-Z$_][\w$]*\))',
+                 bygroups(Name.Class, Text, Name.Label), '#pop'),
+                # simple interface / implementation
+                (r'([a-zA-Z$_][\w$]*)(\s*)(\{)',
+                 bygroups(Name.Class, Text, Punctuation), ('#pop', 'oc_ivars')),
+                (r'([a-zA-Z$_][\w$]*)', Name.Class, '#pop')
+            ],
+            'oc_forward_classname': [
+                (r'([a-zA-Z$_][\w$]*)(\s*,\s*)',
+                 bygroups(Name.Class, Text), 'oc_forward_classname'),
+                (r'([a-zA-Z$_][\w$]*)(\s*;?)',
+                 bygroups(Name.Class, Text), '#pop')
+            ],
+            'oc_ivars': [
+                include('whitespace'),
+                include('statements'),
+                (';', Punctuation),
+                (r'\{', Punctuation, '#push'),
+                (r'\}', Punctuation, '#pop'),
+            ],
+            'root': [
+                # methods
+                (r'^([-+])(\s*)'                         # method marker
+                 r'(\(.*?\))?(\s*)'                      # return type
+                 r'([a-zA-Z$_][\w$]*:?)',        # begin of method name
+                 bygroups(Punctuation, Text, using(this),
+                          Text, Name.Function),
+                 'method'),
+                inherit,
+            ],
+            'method': [
+                include('whitespace'),
+                # TODO unsure if ellipses are allowed elsewhere, see
+                # discussion in Issue 789
+                (r',', Punctuation),
+                (r'\.\.\.', Punctuation),
+                (r'(\(.*?\))(\s*)([a-zA-Z$_][\w$]*)',
+                 bygroups(using(this), Text, Name.Variable)),
+                (r'[a-zA-Z$_][\w$]*:', Name.Function),
+                (';', Punctuation, '#pop'),
+                (r'\{', Punctuation, 'function'),
+                default('#pop'),
+            ],
+            'literal_number': [
+                (r'\(', Punctuation, 'literal_number_inner'),
+                (r'\)', Literal, '#pop'),
+                include('statement'),
+            ],
+            'literal_number_inner': [
+                (r'\(', Punctuation, '#push'),
+                (r'\)', Punctuation, '#pop'),
+                include('statement'),
+            ],
+            'literal_array': [
+                (r'\[', Punctuation, 'literal_array_inner'),
+                (r'\]', Literal, '#pop'),
+                include('statement'),
+            ],
+            'literal_array_inner': [
+                (r'\[', Punctuation, '#push'),
+                (r'\]', Punctuation, '#pop'),
+                include('statement'),
+            ],
+            'literal_dictionary': [
+                (r'\}', Literal, '#pop'),
+                include('statement'),
+            ],
+        }
+
+        def analyse_text(text):
+            if _oc_keywords.search(text):
+                return 1.0
+            elif '@"' in text:  # strings
+                return 0.8
+            elif re.search('@[0-9]+', text):
+                return 0.7
+            elif _oc_message.search(text):
+                return 0.8
+            return 0
+
+        def get_tokens_unprocessed(self, text, stack=('root',)):
+            from pygments.lexers._cocoa_builtins import COCOA_INTERFACES, \
+                COCOA_PROTOCOLS, COCOA_PRIMITIVES
+
+            for index, token, value in \
+                    baselexer.get_tokens_unprocessed(self, text, stack):
+                if token is Name or token is Name.Class:
+                    if value in COCOA_INTERFACES or value in COCOA_PROTOCOLS \
+                       or value in COCOA_PRIMITIVES:
+                        token = Name.Builtin.Pseudo
+
+                yield index, token, value
+
+    return GeneratedObjectiveCVariant
+
+
+class ObjectiveCLexer(objective(CLexer)):
+    """
+    For Objective-C source code with preprocessor directives.
+    """
+
+    name = 'Objective-C'
+    url = 'https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html'
+    aliases = ['objective-c', 'objectivec', 'obj-c', 'objc']
+    filenames = ['*.m', '*.h']
+    mimetypes = ['text/x-objective-c']
+    version_added = ''
+    priority = 0.05    # Lower than C
+
+
+class ObjectiveCppLexer(objective(CppLexer)):
+    """
+    For Objective-C++ source code with preprocessor directives.
+    """
+
+    name = 'Objective-C++'
+    aliases = ['objective-c++', 'objectivec++', 'obj-c++', 'objc++']
+    filenames = ['*.mm', '*.hh']
+    mimetypes = ['text/x-objective-c++']
+    version_added = ''
+    priority = 0.05    # Lower than C++
+
+
+class LogosLexer(ObjectiveCppLexer):
+    """
+    For Logos + Objective-C source code with preprocessor directives.
+    """
+
+    name = 'Logos'
+    aliases = ['logos']
+    filenames = ['*.x', '*.xi', '*.xm', '*.xmi']
+    mimetypes = ['text/x-logos']
+    version_added = '1.6'
+    priority = 0.25
+
+    tokens = {
+        'statements': [
+            (r'(%orig|%log)\b', Keyword),
+            (r'(%c)\b(\()(\s*)([a-zA-Z$_][\w$]*)(\s*)(\))',
+             bygroups(Keyword, Punctuation, Text, Name.Class, Text, Punctuation)),
+            (r'(%init)\b(\()',
+             bygroups(Keyword, Punctuation), 'logos_init_directive'),
+            (r'(%init)(?=\s*;)', bygroups(Keyword)),
+            (r'(%hook|%group)(\s+)([a-zA-Z$_][\w$]+)',
+             bygroups(Keyword, Text, Name.Class), '#pop'),
+            (r'(%subclass)(\s+)', bygroups(Keyword, Text),
+             ('#pop', 'logos_classname')),
+            inherit,
+        ],
+        'logos_init_directive': [
+            (r'\s+', Text),
+            (',', Punctuation, ('logos_init_directive', '#pop')),
+            (r'([a-zA-Z$_][\w$]*)(\s*)(=)(\s*)([^);]*)',
+             bygroups(Name.Class, Text, Punctuation, Text, Text)),
+            (r'([a-zA-Z$_][\w$]*)', Name.Class),
+            (r'\)', Punctuation, '#pop'),
+        ],
+        'logos_classname': [
+            (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?',
+             bygroups(Name.Class, Text, Name.Class), '#pop'),
+            (r'([a-zA-Z$_][\w$]*)', Name.Class, '#pop')
+        ],
+        'root': [
+            (r'(%subclass)(\s+)', bygroups(Keyword, Text),
+             'logos_classname'),
+            (r'(%hook|%group)(\s+)([a-zA-Z$_][\w$]+)',
+             bygroups(Keyword, Text, Name.Class)),
+            (r'(%config)(\s*\(\s*)(\w+)(\s*=)(.*?)(\)\s*)',
+             bygroups(Keyword, Text, Name.Variable, Text, String, Text)),
+            (r'(%ctor)(\s*)(\{)', bygroups(Keyword, Text, Punctuation),
+             'function'),
+            (r'(%new)(\s*)(\()(.*?)(\))',
+             bygroups(Keyword, Text, Keyword, String, Keyword)),
+            (r'(\s*)(%end)(\s*)', bygroups(Text, Keyword, Text)),
+            inherit,
+        ],
+    }
+
+    _logos_keywords = re.compile(r'%(?:hook|ctor|init|c\()')
+
+    def analyse_text(text):
+        if LogosLexer._logos_keywords.search(text):
+            return 1.0
+        return 0
+
+
+class SwiftLexer(RegexLexer):
+    """
+    For Swift source.
+    """
+    name = 'Swift'
+    url = 'https://www.swift.org/'
+    filenames = ['*.swift']
+    aliases = ['swift']
+    mimetypes = ['text/x-swift']
+    version_added = '2.0'
+
+    tokens = {
+        'root': [
+            # Whitespace and Comments
+            (r'\n', Text),
+            (r'\s+', Whitespace),
+            (r'//', Comment.Single, 'comment-single'),
+            (r'/\*', Comment.Multiline, 'comment-multi'),
+            (r'#(if|elseif|else|endif|available)\b', Comment.Preproc, 'preproc'),
+
+            # Keywords
+            include('keywords'),
+
+            # Global Types
+            (words((
+                'Array', 'AutoreleasingUnsafeMutablePointer', 'BidirectionalReverseView',
+                'Bit', 'Bool', 'CFunctionPointer', 'COpaquePointer', 'CVaListPointer',
+                'Character', 'ClosedInterval', 'CollectionOfOne', 'ContiguousArray',
+                'Dictionary', 'DictionaryGenerator', 'DictionaryIndex', 'Double',
+                'EmptyCollection', 'EmptyGenerator', 'EnumerateGenerator',
+                'EnumerateSequence', 'FilterCollectionView',
+                'FilterCollectionViewIndex', 'FilterGenerator', 'FilterSequenceView',
+                'Float', 'Float80', 'FloatingPointClassification', 'GeneratorOf',
+                'GeneratorOfOne', 'GeneratorSequence', 'HalfOpenInterval', 'HeapBuffer',
+                'HeapBufferStorage', 'ImplicitlyUnwrappedOptional', 'IndexingGenerator',
+                'Int', 'Int16', 'Int32', 'Int64', 'Int8', 'LazyBidirectionalCollection',
+                'LazyForwardCollection', 'LazyRandomAccessCollection',
+                'LazySequence', 'MapCollectionView', 'MapSequenceGenerator',
+                'MapSequenceView', 'MirrorDisposition', 'ObjectIdentifier', 'OnHeap',
+                'Optional', 'PermutationGenerator', 'QuickLookObject',
+                'RandomAccessReverseView', 'Range', 'RangeGenerator', 'RawByte', 'Repeat',
+                'ReverseBidirectionalIndex', 'ReverseRandomAccessIndex', 'SequenceOf',
+                'SinkOf', 'Slice', 'StaticString', 'StrideThrough', 'StrideThroughGenerator',
+                'StrideTo', 'StrideToGenerator', 'String', 'UInt', 'UInt16', 'UInt32',
+                'UInt64', 'UInt8', 'UTF16', 'UTF32', 'UTF8', 'UnicodeDecodingResult',
+                'UnicodeScalar', 'Unmanaged', 'UnsafeBufferPointer',
+                'UnsafeBufferPointerGenerator', 'UnsafeMutableBufferPointer',
+                'UnsafeMutablePointer', 'UnsafePointer', 'Zip2', 'ZipGenerator2',
+                # Protocols
+                'AbsoluteValuable', 'AnyObject', 'ArrayLiteralConvertible',
+                'BidirectionalIndexType', 'BitwiseOperationsType',
+                'BooleanLiteralConvertible', 'BooleanType', 'CVarArgType',
+                'CollectionType', 'Comparable', 'DebugPrintable',
+                'DictionaryLiteralConvertible', 'Equatable',
+                'ExtendedGraphemeClusterLiteralConvertible',
+                'ExtensibleCollectionType', 'FloatLiteralConvertible',
+                'FloatingPointType', 'ForwardIndexType', 'GeneratorType', 'Hashable',
+                'IntegerArithmeticType', 'IntegerLiteralConvertible', 'IntegerType',
+                'IntervalType', 'MirrorType', 'MutableCollectionType', 'MutableSliceable',
+                'NilLiteralConvertible', 'OutputStreamType', 'Printable',
+                'RandomAccessIndexType', 'RangeReplaceableCollectionType',
+                'RawOptionSetType', 'RawRepresentable', 'Reflectable', 'SequenceType',
+                'SignedIntegerType', 'SignedNumberType', 'SinkType', 'Sliceable',
+                'Streamable', 'Strideable', 'StringInterpolationConvertible',
+                'StringLiteralConvertible', 'UnicodeCodecType',
+                'UnicodeScalarLiteralConvertible', 'UnsignedIntegerType',
+                '_ArrayBufferType', '_BidirectionalIndexType', '_CocoaStringType',
+                '_CollectionType', '_Comparable', '_ExtensibleCollectionType',
+                '_ForwardIndexType', '_Incrementable', '_IntegerArithmeticType',
+                '_IntegerType', '_ObjectiveCBridgeable', '_RandomAccessIndexType',
+                '_RawOptionSetType', '_SequenceType', '_Sequence_Type',
+                '_SignedIntegerType', '_SignedNumberType', '_Sliceable', '_Strideable',
+                '_SwiftNSArrayRequiredOverridesType', '_SwiftNSArrayType',
+                '_SwiftNSCopyingType', '_SwiftNSDictionaryRequiredOverridesType',
+                '_SwiftNSDictionaryType', '_SwiftNSEnumeratorType',
+                '_SwiftNSFastEnumerationType', '_SwiftNSStringRequiredOverridesType',
+                '_SwiftNSStringType', '_UnsignedIntegerType',
+                # Variables
+                'C_ARGC', 'C_ARGV', 'Process',
+                # Typealiases
+                'Any', 'AnyClass', 'BooleanLiteralType', 'CBool', 'CChar', 'CChar16',
+                'CChar32', 'CDouble', 'CFloat', 'CInt', 'CLong', 'CLongLong', 'CShort',
+                'CSignedChar', 'CUnsignedInt', 'CUnsignedLong', 'CUnsignedShort',
+                'CWideChar', 'ExtendedGraphemeClusterType', 'Float32', 'Float64',
+                'FloatLiteralType', 'IntMax', 'IntegerLiteralType', 'StringLiteralType',
+                'UIntMax', 'UWord', 'UnicodeScalarType', 'Void', 'Word',
+                # Foundation/Cocoa
+                'NSErrorPointer', 'NSObjectProtocol', 'Selector'), suffix=r'\b'),
+             Name.Builtin),
+            # Functions
+            (words((
+                'abs', 'advance', 'alignof', 'alignofValue', 'assert', 'assertionFailure',
+                'contains', 'count', 'countElements', 'debugPrint', 'debugPrintln',
+                'distance', 'dropFirst', 'dropLast', 'dump', 'enumerate', 'equal',
+                'extend', 'fatalError', 'filter', 'find', 'first', 'getVaList', 'indices',
+                'insert', 'isEmpty', 'join', 'last', 'lazy', 'lexicographicalCompare',
+                'map', 'max', 'maxElement', 'min', 'minElement', 'numericCast', 'overlaps',
+                'partition', 'precondition', 'preconditionFailure', 'prefix', 'print',
+                'println', 'reduce', 'reflect', 'removeAll', 'removeAtIndex', 'removeLast',
+                'removeRange', 'reverse', 'sizeof', 'sizeofValue', 'sort', 'sorted',
+                'splice', 'split', 'startsWith', 'stride', 'strideof', 'strideofValue',
+                'suffix', 'swap', 'toDebugString', 'toString', 'transcode',
+                'underestimateCount', 'unsafeAddressOf', 'unsafeBitCast', 'unsafeDowncast',
+                'withExtendedLifetime', 'withUnsafeMutablePointer',
+                'withUnsafeMutablePointers', 'withUnsafePointer', 'withUnsafePointers',
+                'withVaList'), suffix=r'\b'),
+             Name.Builtin.Pseudo),
+
+            # Implicit Block Variables
+            (r'\$\d+', Name.Variable),
+
+            # Binary Literal
+            (r'0b[01_]+', Number.Bin),
+            # Octal Literal
+            (r'0o[0-7_]+', Number.Oct),
+            # Hexadecimal Literal
+            (r'0x[0-9a-fA-F_]+', Number.Hex),
+            # Decimal Literal
+            (r'[0-9][0-9_]*(\.[0-9_]+[eE][+\-]?[0-9_]+|'
+             r'\.[0-9_]*|[eE][+\-]?[0-9_]+)', Number.Float),
+            (r'[0-9][0-9_]*', Number.Integer),
+            # String Literal
+            (r'"""', String, 'string-multi'),
+            (r'"', String, 'string'),
+
+            # Operators and Punctuation
+            (r'[(){}\[\].,:;=@#`?]|->|[<&?](?=\w)|(?<=\w)[>!?]', Punctuation),
+            (r'[/=\-+!*%<>&|^?~]+', Operator),
+
+            # Identifier
+            (r'[a-zA-Z_]\w*', Name)
+        ],
+        'keywords': [
+            (words((
+                'as', 'async', 'await', 'break', 'case', 'catch', 'continue', 'default', 'defer',
+                'do', 'else', 'fallthrough', 'for', 'guard', 'if', 'in', 'is',
+                'repeat', 'return', '#selector', 'switch', 'throw', 'try',
+                'where', 'while'), suffix=r'\b'),
+             Keyword),
+            (r'@availability\([^)]+\)', Keyword.Reserved),
+            (words((
+                'associativity', 'convenience', 'dynamic', 'didSet', 'final',
+                'get', 'indirect', 'infix', 'inout', 'lazy', 'left', 'mutating',
+                'none', 'nonmutating', 'optional', 'override', 'postfix',
+                'precedence', 'prefix', 'Protocol', 'required', 'rethrows',
+                'right', 'set', 'throws', 'Type', 'unowned', 'weak', 'willSet',
+                '@availability', '@autoclosure', '@noreturn',
+                '@NSApplicationMain', '@NSCopying', '@NSManaged', '@objc',
+                '@UIApplicationMain', '@IBAction', '@IBDesignable',
+                '@IBInspectable', '@IBOutlet'), suffix=r'\b'),
+             Keyword.Reserved),
+            (r'(as|dynamicType|false|is|nil|self|Self|super|true|__COLUMN__'
+             r'|__FILE__|__FUNCTION__|__LINE__|_'
+             r'|#(?:file|line|column|function))\b', Keyword.Constant),
+            (r'import\b', Keyword.Declaration, 'module'),
+            (r'(class|enum|extension|struct|protocol)(\s+)([a-zA-Z_]\w*)',
+             bygroups(Keyword.Declaration, Whitespace, Name.Class)),
+            (r'(func)(\s+)([a-zA-Z_]\w*)',
+             bygroups(Keyword.Declaration, Whitespace, Name.Function)),
+            (r'(var|let)(\s+)([a-zA-Z_]\w*)', bygroups(Keyword.Declaration,
+             Whitespace, Name.Variable)),
+            (words((
+                'actor', 'associatedtype', 'class', 'deinit', 'enum', 'extension', 'func', 'import',
+                'init', 'internal', 'let', 'operator', 'private', 'protocol', 'public',
+                'static', 'struct', 'subscript', 'typealias', 'var'), suffix=r'\b'),
+             Keyword.Declaration)
+        ],
+        'comment': [
+            (r':param: [a-zA-Z_]\w*|:returns?:|(FIXME|MARK|TODO):',
+             Comment.Special)
+        ],
+
+        # Nested
+        'comment-single': [
+            (r'\n', Whitespace, '#pop'),
+            include('comment'),
+            (r'[^\n]+', Comment.Single)
+        ],
+        'comment-multi': [
+            include('comment'),
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]+', Comment.Multiline)
+        ],
+        'module': [
+            (r'\n', Whitespace, '#pop'),
+            (r'[a-zA-Z_]\w*', Name.Class),
+            include('root')
+        ],
+        'preproc': [
+            (r'\n', Whitespace, '#pop'),
+            include('keywords'),
+            (r'[A-Za-z]\w*', Comment.Preproc),
+            include('root')
+        ],
+        'string': [
+            (r'"', String, '#pop'),
+            include("string-common"),
+        ],
+        'string-multi': [
+            (r'"""', String, '#pop'),
+            include("string-common"),
+        ],
+        'string-common': [
+            (r'\\\(', String.Interpol, 'string-intp'),
+            (r"""\\['"\\nrt]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}"""
+             r"""|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}""", String.Escape),
+            (r'[^\\"]+', String),
+            (r'\\', String)
+        ],
+        'string-intp': [
+            (r'\(', String.Interpol, '#push'),
+            (r'\)', String.Interpol, '#pop'),
+            include('root')
+        ]
+    }
+
+    def get_tokens_unprocessed(self, text):
+        from pygments.lexers._cocoa_builtins import COCOA_INTERFACES, \
+            COCOA_PROTOCOLS, COCOA_PRIMITIVES
+
+        for index, token, value in \
+                RegexLexer.get_tokens_unprocessed(self, text):
+            if token is Name or token is Name.Class:
+                if value in COCOA_INTERFACES or value in COCOA_PROTOCOLS \
+                   or value in COCOA_PRIMITIVES:
+                    token = Name.Builtin.Pseudo
+
+            yield index, token, value
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/ooc.py b/.venv/lib/python3.12/site-packages/pygments/lexers/ooc.py
new file mode 100644
index 0000000..f6aea8c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/ooc.py
@@ -0,0 +1,84 @@
+"""
+    pygments.lexers.ooc
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Ooc language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['OocLexer']
+
+
+class OocLexer(RegexLexer):
+    """
+    For Ooc source code
+    """
+    name = 'Ooc'
+    url = 'https://ooc-lang.github.io/'
+    aliases = ['ooc']
+    filenames = ['*.ooc']
+    mimetypes = ['text/x-ooc']
+    version_added = '1.2'
+
+    tokens = {
+        'root': [
+            (words((
+                'class', 'interface', 'implement', 'abstract', 'extends', 'from',
+                'this', 'super', 'new', 'const', 'final', 'static', 'import',
+                'use', 'extern', 'inline', 'proto', 'break', 'continue',
+                'fallthrough', 'operator', 'if', 'else', 'for', 'while', 'do',
+                'switch', 'case', 'as', 'in', 'version', 'return', 'true',
+                'false', 'null'), prefix=r'\b', suffix=r'\b'),
+             Keyword),
+            (r'include\b', Keyword, 'include'),
+            (r'(cover)([ \t]+)(from)([ \t]+)(\w+[*@]?)',
+             bygroups(Keyword, Text, Keyword, Text, Name.Class)),
+            (r'(func)((?:[ \t]|\\\n)+)(~[a-z_]\w*)',
+             bygroups(Keyword, Text, Name.Function)),
+            (r'\bfunc\b', Keyword),
+            # Note: %= not listed on https://ooc-lang.github.io/docs/lang/operators/
+            (r'//.*', Comment),
+            (r'(?s)/\*.*?\*/', Comment.Multiline),
+            (r'(==?|\+=?|-[=>]?|\*=?|/=?|:=|!=?|%=?|\?|>{1,3}=?|<{1,3}=?|\.\.|'
+             r'&&?|\|\|?|\^=?)', Operator),
+            (r'(\.)([ \t]*)([a-z]\w*)', bygroups(Operator, Text,
+                                                 Name.Function)),
+            (r'[A-Z][A-Z0-9_]+', Name.Constant),
+            (r'[A-Z]\w*([@*]|\[[ \t]*\])?', Name.Class),
+
+            (r'([a-z]\w*(?:~[a-z]\w*)?)((?:[ \t]|\\\n)*)(?=\()',
+             bygroups(Name.Function, Text)),
+            (r'[a-z]\w*', Name.Variable),
+
+            # : introduces types
+            (r'[:(){}\[\];,]', Punctuation),
+
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'0c[0-9]+', Number.Oct),
+            (r'0b[01]+', Number.Bin),
+            (r'[0-9_]\.[0-9_]*(?!\.)', Number.Float),
+            (r'[0-9_]+', Number.Decimal),
+
+            (r'"(?:\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\"])*"',
+             String.Double),
+            (r"'(?:\\.|\\[0-9]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'",
+             String.Char),
+            (r'@', Punctuation),  # pointer dereference
+            (r'\.', Punctuation),  # imports or chain operator
+
+            (r'\\[ \t\n]', Text),
+            (r'[ \t]+', Text),
+        ],
+        'include': [
+            (r'[\w/]+', Name),
+            (r',', Punctuation),
+            (r'[ \t]', Text),
+            (r'[;\n]', Text, '#pop'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/openscad.py b/.venv/lib/python3.12/site-packages/pygments/lexers/openscad.py
new file mode 100644
index 0000000..1ec8fea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/openscad.py
@@ -0,0 +1,96 @@
+"""
+    pygments.lexers.openscad
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the OpenSCAD languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, words, include
+from pygments.token import Text, Comment, Punctuation, Operator, Keyword, Name, Number, Whitespace, Literal, String
+
+__all__ = ['OpenScadLexer']
+
+
+class OpenScadLexer(RegexLexer):
+    """For openSCAD code.
+    """
+    name = "OpenSCAD"
+    url = "https://openscad.org/"
+    aliases = ["openscad"]
+    filenames = ["*.scad"]
+    mimetypes = ["application/x-openscad"]
+    version_added = '2.16'
+
+    tokens = {
+        "root": [
+            (r"[^\S\n]+", Whitespace),
+            (r'//', Comment.Single, 'comment-single'),
+            (r'/\*', Comment.Multiline, 'comment-multi'),
+            (r"[{}\[\]\(\),;:]", Punctuation),
+            (r"[*!#%\-+=?/]", Operator),
+            (r"<=|<|==|!=|>=|>|&&|\|\|", Operator),
+            (r"\$(f[asn]|t|vp[rtd]|children)", Operator),
+            (r"(undef|PI)\b", Keyword.Constant),
+            (
+                r"(use|include)((?:\s|\\\\s)+)",
+                bygroups(Keyword.Namespace, Text),
+                "includes",
+            ),
+            (r"(module)(\s*)([^\s\(]+)",
+             bygroups(Keyword.Namespace, Whitespace, Name.Namespace)),
+            (r"(function)(\s*)([^\s\(]+)",
+             bygroups(Keyword.Declaration, Whitespace, Name.Function)),
+            (words(("true", "false"), prefix=r"\b", suffix=r"\b"), Literal),
+            (words((
+                "function", "module", "include", "use", "for",
+                "intersection_for", "if", "else", "return"
+                ), prefix=r"\b", suffix=r"\b"), Keyword
+            ),
+            (words((
+                "circle", "square", "polygon", "text", "sphere", "cube",
+                "cylinder", "polyhedron", "translate", "rotate", "scale",
+                "resize", "mirror", "multmatrix", "color", "offset", "hull",
+                "minkowski", "union", "difference", "intersection", "abs",
+                "sign", "sin", "cos", "tan", "acos", "asin", "atan", "atan2",
+                "floor", "round", "ceil", "ln", "log", "pow", "sqrt", "exp",
+                "rands", "min", "max", "concat", "lookup", "str", "chr",
+                "search", "version", "version_num", "norm", "cross",
+                "parent_module", "echo", "import", "import_dxf",
+                "dxf_linear_extrude", "linear_extrude", "rotate_extrude",
+                "surface", "projection", "render", "dxf_cross",
+                "dxf_dim", "let", "assign", "len"
+                ), prefix=r"\b", suffix=r"\b"),
+                Name.Builtin
+            ),
+            (r"\bchildren\b", Name.Builtin.Pseudo),
+            (r'""".*?"""', String.Double),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"-?\d+(\.\d+)?(e[+-]?\d+)?", Number),
+            (r"\w+", Name),
+        ],
+        "includes": [
+            (
+                r"(<)([^>]*)(>)",
+                bygroups(Punctuation, Comment.PreprocFile, Punctuation),
+            ),
+        ],
+        'comment': [
+            (r':param: [a-zA-Z_]\w*|:returns?:|(FIXME|MARK|TODO):',
+             Comment.Special)
+        ],
+        'comment-single': [
+            (r'\n', Text, '#pop'),
+            include('comment'),
+            (r'[^\n]+', Comment.Single)
+        ],
+        'comment-multi': [
+            include('comment'),
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline)
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/other.py b/.venv/lib/python3.12/site-packages/pygments/lexers/other.py
new file mode 100644
index 0000000..8767d73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/other.py
@@ -0,0 +1,41 @@
+"""
+    pygments.lexers.other
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Just export lexer classes previously contained in this module.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+# ruff: noqa: F401
+from pygments.lexers.sql import SqlLexer, MySqlLexer, SqliteConsoleLexer
+from pygments.lexers.shell import BashLexer, BashSessionLexer, BatchLexer, \
+    TcshLexer
+from pygments.lexers.robotframework import RobotFrameworkLexer
+from pygments.lexers.testing import GherkinLexer
+from pygments.lexers.esoteric import BrainfuckLexer, BefungeLexer, RedcodeLexer
+from pygments.lexers.prolog import LogtalkLexer
+from pygments.lexers.snobol import SnobolLexer
+from pygments.lexers.rebol import RebolLexer
+from pygments.lexers.configs import KconfigLexer, Cfengine3Lexer
+from pygments.lexers.modeling import ModelicaLexer
+from pygments.lexers.scripting import AppleScriptLexer, MOOCodeLexer, \
+    HybrisLexer
+from pygments.lexers.graphics import PostScriptLexer, GnuplotLexer, \
+    AsymptoteLexer, PovrayLexer
+from pygments.lexers.business import ABAPLexer, OpenEdgeLexer, \
+    GoodDataCLLexer, MaqlLexer
+from pygments.lexers.automation import AutoItLexer, AutohotkeyLexer
+from pygments.lexers.dsls import ProtoBufLexer, BroLexer, PuppetLexer, \
+    MscgenLexer, VGLLexer
+from pygments.lexers.basic import CbmBasicV2Lexer
+from pygments.lexers.pawn import SourcePawnLexer, PawnLexer
+from pygments.lexers.ecl import ECLLexer
+from pygments.lexers.urbi import UrbiscriptLexer
+from pygments.lexers.smalltalk import SmalltalkLexer, NewspeakLexer
+from pygments.lexers.installers import NSISLexer, RPMSpecLexer
+from pygments.lexers.textedit import AwkLexer
+from pygments.lexers.smv import NuSMVLexer
+
+__all__ = []
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/parasail.py b/.venv/lib/python3.12/site-packages/pygments/lexers/parasail.py
new file mode 100644
index 0000000..9b40960
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/parasail.py
@@ -0,0 +1,78 @@
+"""
+    pygments.lexers.parasail
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for ParaSail.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Literal
+
+__all__ = ['ParaSailLexer']
+
+
+class ParaSailLexer(RegexLexer):
+    """
+    For ParaSail source code.
+    """
+
+    name = 'ParaSail'
+    url = 'http://www.parasail-lang.org'
+    aliases = ['parasail']
+    filenames = ['*.psi', '*.psl']
+    mimetypes = ['text/x-parasail']
+    version_added = '2.1'
+
+    flags = re.MULTILINE
+
+    tokens = {
+        'root': [
+            (r'[^\S\n]+', Text),
+            (r'//.*?\n', Comment.Single),
+            (r'\b(and|or|xor)=', Operator.Word),
+            (r'\b(and(\s+then)?|or(\s+else)?|xor|rem|mod|'
+             r'(is|not)\s+null)\b',
+             Operator.Word),
+            # Keywords
+            (r'\b(abs|abstract|all|block|class|concurrent|const|continue|'
+             r'each|end|exit|extends|exports|forward|func|global|implements|'
+             r'import|in|interface|is|lambda|locked|new|not|null|of|op|'
+             r'optional|private|queued|ref|return|reverse|separate|some|'
+             r'type|until|var|with|'
+             # Control flow
+             r'if|then|else|elsif|case|for|while|loop)\b',
+             Keyword.Reserved),
+            (r'(abstract\s+)?(interface|class|op|func|type)',
+             Keyword.Declaration),
+            # Literals
+            (r'"[^"]*"', String),
+            (r'\\[\'ntrf"0]', String.Escape),
+            (r'#[a-zA-Z]\w*', Literal),       # Enumeration
+            include('numbers'),
+            (r"'[^']'", String.Char),
+            (r'[a-zA-Z]\w*', Name),
+            # Operators and Punctuation
+            (r'(<==|==>|<=>|\*\*=|<\|=|<<=|>>=|==|!=|=\?|<=|>=|'
+             r'\*\*|<<|>>|=>|:=|\+=|-=|\*=|\|=|\||/=|\+|-|\*|/|'
+             r'\.\.|<\.\.|\.\.<|<\.\.<)',
+             Operator),
+            (r'(<|>|\[|\]|\(|\)|\||:|;|,|.|\{|\}|->)',
+             Punctuation),
+            (r'\n+', Text),
+        ],
+        'numbers': [
+            (r'\d[0-9_]*#[0-9a-fA-F][0-9a-fA-F_]*#', Number.Hex),  # any base
+            (r'0[xX][0-9a-fA-F][0-9a-fA-F_]*', Number.Hex),        # C-like hex
+            (r'0[bB][01][01_]*', Number.Bin),                      # C-like bin
+            (r'\d[0-9_]*\.\d[0-9_]*[eE][+-]\d[0-9_]*',             # float exp
+             Number.Float),
+            (r'\d[0-9_]*\.\d[0-9_]*', Number.Float),               # float
+            (r'\d[0-9_]*', Number.Integer),                        # integer
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/parsers.py b/.venv/lib/python3.12/site-packages/pygments/lexers/parsers.py
new file mode 100644
index 0000000..baa49b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/parsers.py
@@ -0,0 +1,798 @@
+"""
+    pygments.lexers.parsers
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for parser generators.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, DelegatingLexer, \
+    include, bygroups, using
+from pygments.token import Punctuation, Other, Text, Comment, Operator, \
+    Keyword, Name, String, Number, Whitespace
+from pygments.lexers.jvm import JavaLexer
+from pygments.lexers.c_cpp import CLexer, CppLexer
+from pygments.lexers.objective import ObjectiveCLexer
+from pygments.lexers.d import DLexer
+from pygments.lexers.dotnet import CSharpLexer
+from pygments.lexers.ruby import RubyLexer
+from pygments.lexers.python import PythonLexer
+from pygments.lexers.perl import PerlLexer
+
+__all__ = ['RagelLexer', 'RagelEmbeddedLexer', 'RagelCLexer', 'RagelDLexer',
+           'RagelCppLexer', 'RagelObjectiveCLexer', 'RagelRubyLexer',
+           'RagelJavaLexer', 'AntlrLexer', 'AntlrPythonLexer',
+           'AntlrPerlLexer', 'AntlrRubyLexer', 'AntlrCppLexer',
+           'AntlrCSharpLexer', 'AntlrObjectiveCLexer',
+           'AntlrJavaLexer', 'AntlrActionScriptLexer',
+           'TreetopLexer', 'EbnfLexer']
+
+
+class RagelLexer(RegexLexer):
+    """A pure `Ragel `_ lexer.
+
+    Use this for fragments of Ragel.  For ``.rl`` files, use
+    :class:`RagelEmbeddedLexer` instead (or one of the language-specific
+    subclasses).
+    """
+
+    name = 'Ragel'
+    url = 'http://www.colm.net/open-source/ragel/'
+    aliases = ['ragel']
+    filenames = []
+    version_added = '1.1'
+
+    tokens = {
+        'whitespace': [
+            (r'\s+', Whitespace)
+        ],
+        'comments': [
+            (r'\#.*$', Comment),
+        ],
+        'keywords': [
+            (r'(access|action|alphtype)\b', Keyword),
+            (r'(getkey|write|machine|include)\b', Keyword),
+            (r'(any|ascii|extend|alpha|digit|alnum|lower|upper)\b', Keyword),
+            (r'(xdigit|cntrl|graph|print|punct|space|zlen|empty)\b', Keyword)
+        ],
+        'numbers': [
+            (r'0x[0-9A-Fa-f]+', Number.Hex),
+            (r'[+-]?[0-9]+', Number.Integer),
+        ],
+        'literals': [
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+            (r'\[(\\\\|\\[^\\]|[^\\\]])*\]', String),          # square bracket literals
+            (r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/', String.Regex),  # regular expressions
+        ],
+        'identifiers': [
+            (r'[a-zA-Z_]\w*', Name.Variable),
+        ],
+        'operators': [
+            (r',', Operator),                           # Join
+            (r'\||&|--?', Operator),                    # Union, Intersection and Subtraction
+            (r'\.|<:|:>>?', Operator),                  # Concatention
+            (r':', Operator),                           # Label
+            (r'->', Operator),                          # Epsilon Transition
+            (r'(>|\$|%|<|@|<>)(/|eof\b)', Operator),    # EOF Actions
+            (r'(>|\$|%|<|@|<>)(!|err\b)', Operator),    # Global Error Actions
+            (r'(>|\$|%|<|@|<>)(\^|lerr\b)', Operator),  # Local Error Actions
+            (r'(>|\$|%|<|@|<>)(~|to\b)', Operator),     # To-State Actions
+            (r'(>|\$|%|<|@|<>)(\*|from\b)', Operator),  # From-State Actions
+            (r'>|@|\$|%', Operator),                    # Transition Actions and Priorities
+            (r'\*|\?|\+|\{[0-9]*,[0-9]*\}', Operator),  # Repetition
+            (r'!|\^', Operator),                        # Negation
+            (r'\(|\)', Operator),                       # Grouping
+        ],
+        'root': [
+            include('literals'),
+            include('whitespace'),
+            include('comments'),
+            include('keywords'),
+            include('numbers'),
+            include('identifiers'),
+            include('operators'),
+            (r'\{', Punctuation, 'host'),
+            (r'=', Operator),
+            (r';', Punctuation),
+        ],
+        'host': [
+            (r'(' + r'|'.join((  # keep host code in largest possible chunks
+                r'[^{}\'"/#]+',  # exclude unsafe characters
+                r'[^\\]\\[{}]',  # allow escaped { or }
+
+                # strings and comments may safely contain unsafe characters
+                r'"(\\\\|\\[^\\]|[^"\\])*"',
+                r"'(\\\\|\\[^\\]|[^'\\])*'",
+                r'//.*$\n?',            # single line comment
+                r'/\*(.|\n)*?\*/',      # multi-line javadoc-style comment
+                r'\#.*$\n?',            # ruby comment
+
+                # regular expression: There's no reason for it to start
+                # with a * and this stops confusion with comments.
+                r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/',
+
+                # / is safe now that we've handled regex and javadoc comments
+                r'/',
+            )) + r')+', Other),
+
+            (r'\{', Punctuation, '#push'),
+            (r'\}', Punctuation, '#pop'),
+        ],
+    }
+
+
+class RagelEmbeddedLexer(RegexLexer):
+    """
+    A lexer for Ragel embedded in a host language file.
+
+    This will only highlight Ragel statements. If you want host language
+    highlighting then call the language-specific Ragel lexer.
+    """
+
+    name = 'Embedded Ragel'
+    aliases = ['ragel-em']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    tokens = {
+        'root': [
+            (r'(' + r'|'.join((   # keep host code in largest possible chunks
+                r'[^%\'"/#]+',    # exclude unsafe characters
+                r'%(?=[^%]|$)',   # a single % sign is okay, just not 2 of them
+
+                # strings and comments may safely contain unsafe characters
+                r'"(\\\\|\\[^\\]|[^"\\])*"',
+                r"'(\\\\|\\[^\\]|[^'\\])*'",
+                r'/\*(.|\n)*?\*/',      # multi-line javadoc-style comment
+                r'//.*$\n?',  # single line comment
+                r'\#.*$\n?',  # ruby/ragel comment
+                r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/',  # regular expression
+
+                # / is safe now that we've handled regex and javadoc comments
+                r'/',
+            )) + r')+', Other),
+
+            # Single Line FSM.
+            # Please don't put a quoted newline in a single line FSM.
+            # That's just mean. It will break this.
+            (r'(%%)(?![{%])(.*)($|;)(\n?)', bygroups(Punctuation,
+                                                     using(RagelLexer),
+                                                     Punctuation, Text)),
+
+            # Multi Line FSM.
+            (r'(%%%%|%%)\{', Punctuation, 'multi-line-fsm'),
+        ],
+        'multi-line-fsm': [
+            (r'(' + r'|'.join((  # keep ragel code in largest possible chunks.
+                r'(' + r'|'.join((
+                    r'[^}\'"\[/#]',   # exclude unsafe characters
+                    r'\}(?=[^%]|$)',   # } is okay as long as it's not followed by %
+                    r'\}%(?=[^%]|$)',  # ...well, one %'s okay, just not two...
+                    r'[^\\]\\[{}]',   # ...and } is okay if it's escaped
+
+                    # allow / if it's preceded with one of these symbols
+                    # (ragel EOF actions)
+                    r'(>|\$|%|<|@|<>)/',
+
+                    # specifically allow regex followed immediately by *
+                    # so it doesn't get mistaken for a comment
+                    r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/\*',
+
+                    # allow / as long as it's not followed by another / or by a *
+                    r'/(?=[^/*]|$)',
+
+                    # We want to match as many of these as we can in one block.
+                    # Not sure if we need the + sign here,
+                    # does it help performance?
+                )) + r')+',
+
+                # strings and comments may safely contain unsafe characters
+                r'"(\\\\|\\[^\\]|[^"\\])*"',
+                r"'(\\\\|\\[^\\]|[^'\\])*'",
+                r"\[(\\\\|\\[^\\]|[^\]\\])*\]",  # square bracket literal
+                r'/\*(.|\n)*?\*/',          # multi-line javadoc-style comment
+                r'//.*$\n?',                # single line comment
+                r'\#.*$\n?',                # ruby/ragel comment
+            )) + r')+', using(RagelLexer)),
+
+            (r'\}%%', Punctuation, '#pop'),
+        ]
+    }
+
+    def analyse_text(text):
+        return '@LANG: indep' in text
+
+
+class RagelRubyLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in a Ruby host file.
+    """
+
+    name = 'Ragel in Ruby Host'
+    aliases = ['ragel-ruby', 'ragel-rb']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(RubyLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: ruby' in text
+
+
+class RagelCLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in a C host file.
+    """
+
+    name = 'Ragel in C Host'
+    aliases = ['ragel-c']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(CLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: c' in text
+
+
+class RagelDLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in a D host file.
+    """
+
+    name = 'Ragel in D Host'
+    aliases = ['ragel-d']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(DLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: d' in text
+
+
+class RagelCppLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in a C++ host file.
+    """
+
+    name = 'Ragel in CPP Host'
+    aliases = ['ragel-cpp']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(CppLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: c++' in text
+
+
+class RagelObjectiveCLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in an Objective C host file.
+    """
+
+    name = 'Ragel in Objective C Host'
+    aliases = ['ragel-objc']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(ObjectiveCLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: objc' in text
+
+
+class RagelJavaLexer(DelegatingLexer):
+    """
+    A lexer for Ragel in a Java host file.
+    """
+
+    name = 'Ragel in Java Host'
+    aliases = ['ragel-java']
+    filenames = ['*.rl']
+    url = 'http://www.colm.net/open-source/ragel/'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(JavaLexer, RagelEmbeddedLexer, **options)
+
+    def analyse_text(text):
+        return '@LANG: java' in text
+
+
+class AntlrLexer(RegexLexer):
+    """
+    Generic ANTLR Lexer.
+    Should not be called directly, instead
+    use DelegatingLexer for your target language.
+    """
+
+    name = 'ANTLR'
+    aliases = ['antlr']
+    filenames = []
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    _id = r'[A-Za-z]\w*'
+    _TOKEN_REF = r'[A-Z]\w*'
+    _RULE_REF = r'[a-z]\w*'
+    _STRING_LITERAL = r'\'(?:\\\\|\\\'|[^\']*)\''
+    _INT = r'[0-9]+'
+
+    tokens = {
+        'whitespace': [
+            (r'\s+', Whitespace),
+        ],
+        'comments': [
+            (r'//.*$', Comment),
+            (r'/\*(.|\n)*?\*/', Comment),
+        ],
+        'root': [
+            include('whitespace'),
+            include('comments'),
+
+            (r'(lexer|parser|tree)?(\s*)(grammar\b)(\s*)(' + _id + ')(;)',
+             bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Class,
+                      Punctuation)),
+            # optionsSpec
+            (r'options\b', Keyword, 'options'),
+            # tokensSpec
+            (r'tokens\b', Keyword, 'tokens'),
+            # attrScope
+            (r'(scope)(\s*)(' + _id + r')(\s*)(\{)',
+             bygroups(Keyword, Whitespace, Name.Variable, Whitespace,
+                      Punctuation), 'action'),
+            # exception
+            (r'(catch|finally)\b', Keyword, 'exception'),
+            # action
+            (r'(@' + _id + r')(\s*)(::)?(\s*)(' + _id + r')(\s*)(\{)',
+             bygroups(Name.Label, Whitespace, Punctuation, Whitespace,
+                      Name.Label, Whitespace, Punctuation), 'action'),
+            # rule
+            (r'((?:protected|private|public|fragment)\b)?(\s*)(' + _id + ')(!)?',
+             bygroups(Keyword, Whitespace, Name.Label, Punctuation),
+             ('rule-alts', 'rule-prelims')),
+        ],
+        'exception': [
+            (r'\n', Whitespace, '#pop'),
+            (r'\s', Whitespace),
+            include('comments'),
+
+            (r'\[', Punctuation, 'nested-arg-action'),
+            (r'\{', Punctuation, 'action'),
+        ],
+        'rule-prelims': [
+            include('whitespace'),
+            include('comments'),
+
+            (r'returns\b', Keyword),
+            (r'\[', Punctuation, 'nested-arg-action'),
+            (r'\{', Punctuation, 'action'),
+            # throwsSpec
+            (r'(throws)(\s+)(' + _id + ')',
+             bygroups(Keyword, Whitespace, Name.Label)),
+            (r'(,)(\s*)(' + _id + ')',
+             bygroups(Punctuation, Whitespace, Name.Label)),  # Additional throws
+            # optionsSpec
+            (r'options\b', Keyword, 'options'),
+            # ruleScopeSpec - scope followed by target language code or name of action
+            # TODO finish implementing other possibilities for scope
+            # L173 ANTLRv3.g from ANTLR book
+            (r'(scope)(\s+)(\{)', bygroups(Keyword, Whitespace, Punctuation),
+             'action'),
+            (r'(scope)(\s+)(' + _id + r')(\s*)(;)',
+             bygroups(Keyword, Whitespace, Name.Label, Whitespace, Punctuation)),
+            # ruleAction
+            (r'(@' + _id + r')(\s*)(\{)',
+             bygroups(Name.Label, Whitespace, Punctuation), 'action'),
+            # finished prelims, go to rule alts!
+            (r':', Punctuation, '#pop')
+        ],
+        'rule-alts': [
+            include('whitespace'),
+            include('comments'),
+
+            # These might need to go in a separate 'block' state triggered by (
+            (r'options\b', Keyword, 'options'),
+            (r':', Punctuation),
+
+            # literals
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+            (r'<<([^>]|>[^>])>>', String),
+            # identifiers
+            # Tokens start with capital letter.
+            (r'\$?[A-Z_]\w*', Name.Constant),
+            # Rules start with small letter.
+            (r'\$?[a-z_]\w*', Name.Variable),
+            # operators
+            (r'(\+|\||->|=>|=|\(|\)|\.\.|\.|\?|\*|\^|!|\#|~)', Operator),
+            (r',', Punctuation),
+            (r'\[', Punctuation, 'nested-arg-action'),
+            (r'\{', Punctuation, 'action'),
+            (r';', Punctuation, '#pop')
+        ],
+        'tokens': [
+            include('whitespace'),
+            include('comments'),
+            (r'\{', Punctuation),
+            (r'(' + _TOKEN_REF + r')(\s*)(=)?(\s*)(' + _STRING_LITERAL
+             + r')?(\s*)(;)',
+             bygroups(Name.Label, Whitespace, Punctuation, Whitespace,
+                      String, Whitespace, Punctuation)),
+            (r'\}', Punctuation, '#pop'),
+        ],
+        'options': [
+            include('whitespace'),
+            include('comments'),
+            (r'\{', Punctuation),
+            (r'(' + _id + r')(\s*)(=)(\s*)(' +
+             '|'.join((_id, _STRING_LITERAL, _INT, r'\*')) + r')(\s*)(;)',
+             bygroups(Name.Variable, Whitespace, Punctuation, Whitespace,
+                      Text, Whitespace, Punctuation)),
+            (r'\}', Punctuation, '#pop'),
+        ],
+        'action': [
+            (r'(' + r'|'.join((    # keep host code in largest possible chunks
+                r'[^${}\'"/\\]+',  # exclude unsafe characters
+
+                # strings and comments may safely contain unsafe characters
+                r'"(\\\\|\\[^\\]|[^"\\])*"',
+                r"'(\\\\|\\[^\\]|[^'\\])*'",
+                r'//.*$\n?',            # single line comment
+                r'/\*(.|\n)*?\*/',      # multi-line javadoc-style comment
+
+                # regular expression: There's no reason for it to start
+                # with a * and this stops confusion with comments.
+                r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/',
+
+                # backslashes are okay, as long as we are not backslashing a %
+                r'\\(?!%)',
+
+                # Now that we've handled regex and javadoc comments
+                # it's safe to let / through.
+                r'/',
+            )) + r')+', Other),
+            (r'(\\)(%)', bygroups(Punctuation, Other)),
+            (r'(\$[a-zA-Z]+)(\.?)(text|value)?',
+             bygroups(Name.Variable, Punctuation, Name.Property)),
+            (r'\{', Punctuation, '#push'),
+            (r'\}', Punctuation, '#pop'),
+        ],
+        'nested-arg-action': [
+            (r'(' + r'|'.join((    # keep host code in largest possible chunks.
+                r'[^$\[\]\'"/]+',  # exclude unsafe characters
+
+                # strings and comments may safely contain unsafe characters
+                r'"(\\\\|\\[^\\]|[^"\\])*"',
+                r"'(\\\\|\\[^\\]|[^'\\])*'",
+                r'//.*$\n?',            # single line comment
+                r'/\*(.|\n)*?\*/',      # multi-line javadoc-style comment
+
+                # regular expression: There's no reason for it to start
+                # with a * and this stops confusion with comments.
+                r'/(?!\*)(\\\\|\\[^\\]|[^/\\])*/',
+
+                # Now that we've handled regex and javadoc comments
+                # it's safe to let / through.
+                r'/',
+            )) + r')+', Other),
+
+
+            (r'\[', Punctuation, '#push'),
+            (r'\]', Punctuation, '#pop'),
+            (r'(\$[a-zA-Z]+)(\.?)(text|value)?',
+             bygroups(Name.Variable, Punctuation, Name.Property)),
+            (r'(\\\\|\\\]|\\\[|[^\[\]])+', Other),
+        ]
+    }
+
+    def analyse_text(text):
+        return re.search(r'^\s*grammar\s+[a-zA-Z0-9]+\s*;', text, re.M)
+
+
+# http://www.antlr.org/wiki/display/ANTLR3/Code+Generation+Targets
+
+class AntlrCppLexer(DelegatingLexer):
+    """
+    ANTLR with C++ Target
+    """
+
+    name = 'ANTLR With CPP Target'
+    aliases = ['antlr-cpp']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(CppLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*C\s*;', text, re.M)
+
+
+class AntlrObjectiveCLexer(DelegatingLexer):
+    """
+    ANTLR with Objective-C Target
+    """
+
+    name = 'ANTLR With ObjectiveC Target'
+    aliases = ['antlr-objc']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(ObjectiveCLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*ObjC\s*;', text)
+
+
+class AntlrCSharpLexer(DelegatingLexer):
+    """
+    ANTLR with C# Target
+    """
+
+    name = 'ANTLR With C# Target'
+    aliases = ['antlr-csharp', 'antlr-c#']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(CSharpLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*CSharp2\s*;', text, re.M)
+
+
+class AntlrPythonLexer(DelegatingLexer):
+    """
+    ANTLR with Python Target
+    """
+
+    name = 'ANTLR With Python Target'
+    aliases = ['antlr-python']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(PythonLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*Python\s*;', text, re.M)
+
+
+class AntlrJavaLexer(DelegatingLexer):
+    """
+    ANTLR with Java Target
+    """
+
+    name = 'ANTLR With Java Target'
+    aliases = ['antlr-java']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(JavaLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        # Antlr language is Java by default
+        return AntlrLexer.analyse_text(text) and 0.9
+
+
+class AntlrRubyLexer(DelegatingLexer):
+    """
+    ANTLR with Ruby Target
+    """
+
+    name = 'ANTLR With Ruby Target'
+    aliases = ['antlr-ruby', 'antlr-rb']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(RubyLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*Ruby\s*;', text, re.M)
+
+
+class AntlrPerlLexer(DelegatingLexer):
+    """
+    ANTLR with Perl Target
+    """
+
+    name = 'ANTLR With Perl Target'
+    aliases = ['antlr-perl']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        super().__init__(PerlLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*Perl5\s*;', text, re.M)
+
+
+class AntlrActionScriptLexer(DelegatingLexer):
+    """
+    ANTLR with ActionScript Target
+    """
+
+    name = 'ANTLR With ActionScript Target'
+    aliases = ['antlr-actionscript', 'antlr-as']
+    filenames = ['*.G', '*.g']
+    url = 'https://www.antlr.org'
+    version_added = '1.1'
+
+    def __init__(self, **options):
+        from pygments.lexers.actionscript import ActionScriptLexer
+        super().__init__(ActionScriptLexer, AntlrLexer, **options)
+
+    def analyse_text(text):
+        return AntlrLexer.analyse_text(text) and \
+            re.search(r'^\s*language\s*=\s*ActionScript\s*;', text, re.M)
+
+
+class TreetopBaseLexer(RegexLexer):
+    """
+    A base lexer for `Treetop `_ grammars.
+    Not for direct use; use :class:`TreetopLexer` instead.
+
+    .. versionadded:: 1.6
+    """
+
+    tokens = {
+        'root': [
+            include('space'),
+            (r'require[ \t]+[^\n\r]+[\n\r]', Other),
+            (r'module\b', Keyword.Namespace, 'module'),
+            (r'grammar\b', Keyword, 'grammar'),
+        ],
+        'module': [
+            include('space'),
+            include('end'),
+            (r'module\b', Keyword, '#push'),
+            (r'grammar\b', Keyword, 'grammar'),
+            (r'[A-Z]\w*(?:::[A-Z]\w*)*', Name.Namespace),
+        ],
+        'grammar': [
+            include('space'),
+            include('end'),
+            (r'rule\b', Keyword, 'rule'),
+            (r'include\b', Keyword, 'include'),
+            (r'[A-Z]\w*', Name),
+        ],
+        'include': [
+            include('space'),
+            (r'[A-Z]\w*(?:::[A-Z]\w*)*', Name.Class, '#pop'),
+        ],
+        'rule': [
+            include('space'),
+            include('end'),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+            (r'([A-Za-z_]\w*)(:)', bygroups(Name.Label, Punctuation)),
+            (r'[A-Za-z_]\w*', Name),
+            (r'[()]', Punctuation),
+            (r'[?+*/&!~]', Operator),
+            (r'\[(?:\\.|\[:\^?[a-z]+:\]|[^\\\]])+\]', String.Regex),
+            (r'([0-9]*)(\.\.)([0-9]*)',
+             bygroups(Number.Integer, Operator, Number.Integer)),
+            (r'(<)([^>]+)(>)', bygroups(Punctuation, Name.Class, Punctuation)),
+            (r'\{', Punctuation, 'inline_module'),
+            (r'\.', String.Regex),
+        ],
+        'inline_module': [
+            (r'\{', Other, 'ruby'),
+            (r'\}', Punctuation, '#pop'),
+            (r'[^{}]+', Other),
+        ],
+        'ruby': [
+            (r'\{', Other, '#push'),
+            (r'\}', Other, '#pop'),
+            (r'[^{}]+', Other),
+        ],
+        'space': [
+            (r'[ \t\n\r]+', Whitespace),
+            (r'#[^\n]*', Comment.Single),
+        ],
+        'end': [
+            (r'end\b', Keyword, '#pop'),
+        ],
+    }
+
+
+class TreetopLexer(DelegatingLexer):
+    """
+    A lexer for Treetop grammars.
+    """
+
+    name = 'Treetop'
+    aliases = ['treetop']
+    filenames = ['*.treetop', '*.tt']
+    url = 'https://cjheath.github.io/treetop'
+    version_added = '1.6'
+
+    def __init__(self, **options):
+        super().__init__(RubyLexer, TreetopBaseLexer, **options)
+
+
+class EbnfLexer(RegexLexer):
+    """
+    Lexer for `ISO/IEC 14977 EBNF
+    `_
+    grammars.
+    """
+
+    name = 'EBNF'
+    aliases = ['ebnf']
+    filenames = ['*.ebnf']
+    mimetypes = ['text/x-ebnf']
+    url = 'https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form'
+    version_added = '2.0'
+
+    tokens = {
+        'root': [
+            include('whitespace'),
+            include('comment_start'),
+            include('identifier'),
+            (r'=', Operator, 'production'),
+        ],
+        'production': [
+            include('whitespace'),
+            include('comment_start'),
+            include('identifier'),
+            (r'"[^"]*"', String.Double),
+            (r"'[^']*'", String.Single),
+            (r'(\?[^?]*\?)', Name.Entity),
+            (r'[\[\]{}(),|]', Punctuation),
+            (r'-', Operator),
+            (r';', Punctuation, '#pop'),
+            (r'\.', Punctuation, '#pop'),
+        ],
+        'whitespace': [
+            (r'\s+', Text),
+        ],
+        'comment_start': [
+            (r'\(\*', Comment.Multiline, 'comment'),
+        ],
+        'comment': [
+            (r'[^*)]', Comment.Multiline),
+            include('comment_start'),
+            (r'\*\)', Comment.Multiline, '#pop'),
+            (r'[*)]', Comment.Multiline),
+        ],
+        'identifier': [
+            (r'([a-zA-Z][\w \-]*)', Keyword),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/pascal.py b/.venv/lib/python3.12/site-packages/pygments/lexers/pascal.py
new file mode 100644
index 0000000..ad8a0ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/pascal.py
@@ -0,0 +1,644 @@
+"""
+    pygments.lexers.pascal
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Pascal family languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer
+from pygments.util import get_bool_opt, get_list_opt
+from pygments.token import Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Error, Whitespace
+from pygments.scanner import Scanner
+
+# compatibility import
+from pygments.lexers.modula2 import Modula2Lexer # noqa: F401
+
+__all__ = ['DelphiLexer', 'PortugolLexer']
+
+
+class PortugolLexer(Lexer):
+    """For Portugol, a Pascal dialect with keywords in Portuguese."""
+    name = 'Portugol'
+    aliases = ['portugol']
+    filenames = ['*.alg', '*.portugol']
+    mimetypes = []
+    url = "https://www.apoioinformatica.inf.br/produtos/visualg/linguagem"
+    version_added = ''
+
+    def __init__(self, **options):
+        Lexer.__init__(self, **options)
+        self.lexer = DelphiLexer(**options, portugol=True)
+
+    def get_tokens_unprocessed(self, text):
+        return self.lexer.get_tokens_unprocessed(text)
+
+
+class DelphiLexer(Lexer):
+    """
+    For Delphi (Borland Object Pascal),
+    Turbo Pascal and Free Pascal source code.
+
+    Additional options accepted:
+
+    `turbopascal`
+        Highlight Turbo Pascal specific keywords (default: ``True``).
+    `delphi`
+        Highlight Borland Delphi specific keywords (default: ``True``).
+    `freepascal`
+        Highlight Free Pascal specific keywords (default: ``True``).
+    `units`
+        A list of units that should be considered builtin, supported are
+        ``System``, ``SysUtils``, ``Classes`` and ``Math``.
+        Default is to consider all of them builtin.
+    """
+    name = 'Delphi'
+    aliases = ['delphi', 'pas', 'pascal', 'objectpascal']
+    filenames = ['*.pas', '*.dpr']
+    mimetypes = ['text/x-pascal']
+    url = 'https://www.embarcadero.com/products/delphi'
+    version_added = ''
+
+    TURBO_PASCAL_KEYWORDS = (
+        'absolute', 'and', 'array', 'asm', 'begin', 'break', 'case',
+        'const', 'constructor', 'continue', 'destructor', 'div', 'do',
+        'downto', 'else', 'end', 'file', 'for', 'function', 'goto',
+        'if', 'implementation', 'in', 'inherited', 'inline', 'interface',
+        'label', 'mod', 'nil', 'not', 'object', 'of', 'on', 'operator',
+        'or', 'packed', 'procedure', 'program', 'record', 'reintroduce',
+        'repeat', 'self', 'set', 'shl', 'shr', 'string', 'then', 'to',
+        'type', 'unit', 'until', 'uses', 'var', 'while', 'with', 'xor'
+    )
+
+    DELPHI_KEYWORDS = (
+        'as', 'class', 'except', 'exports', 'finalization', 'finally',
+        'initialization', 'is', 'library', 'on', 'property', 'raise',
+        'threadvar', 'try'
+    )
+
+    FREE_PASCAL_KEYWORDS = (
+        'dispose', 'exit', 'false', 'new', 'true'
+    )
+
+    BLOCK_KEYWORDS = {
+        'begin', 'class', 'const', 'constructor', 'destructor', 'end',
+        'finalization', 'function', 'implementation', 'initialization',
+        'label', 'library', 'operator', 'procedure', 'program', 'property',
+        'record', 'threadvar', 'type', 'unit', 'uses', 'var'
+    }
+
+    FUNCTION_MODIFIERS = {
+        'alias', 'cdecl', 'export', 'inline', 'interrupt', 'nostackframe',
+        'pascal', 'register', 'safecall', 'softfloat', 'stdcall',
+        'varargs', 'name', 'dynamic', 'near', 'virtual', 'external',
+        'override', 'assembler'
+    }
+
+    # XXX: those aren't global. but currently we know no way for defining
+    #      them just for the type context.
+    DIRECTIVES = {
+        'absolute', 'abstract', 'assembler', 'cppdecl', 'default', 'far',
+        'far16', 'forward', 'index', 'oldfpccall', 'private', 'protected',
+        'published', 'public'
+    }
+
+    BUILTIN_TYPES = {
+        'ansichar', 'ansistring', 'bool', 'boolean', 'byte', 'bytebool',
+        'cardinal', 'char', 'comp', 'currency', 'double', 'dword',
+        'extended', 'int64', 'integer', 'iunknown', 'longbool', 'longint',
+        'longword', 'pansichar', 'pansistring', 'pbool', 'pboolean',
+        'pbyte', 'pbytearray', 'pcardinal', 'pchar', 'pcomp', 'pcurrency',
+        'pdate', 'pdatetime', 'pdouble', 'pdword', 'pextended', 'phandle',
+        'pint64', 'pinteger', 'plongint', 'plongword', 'pointer',
+        'ppointer', 'pshortint', 'pshortstring', 'psingle', 'psmallint',
+        'pstring', 'pvariant', 'pwidechar', 'pwidestring', 'pword',
+        'pwordarray', 'pwordbool', 'real', 'real48', 'shortint',
+        'shortstring', 'single', 'smallint', 'string', 'tclass', 'tdate',
+        'tdatetime', 'textfile', 'thandle', 'tobject', 'ttime', 'variant',
+        'widechar', 'widestring', 'word', 'wordbool'
+    }
+
+    BUILTIN_UNITS = {
+        'System': (
+            'abs', 'acquireexceptionobject', 'addr', 'ansitoutf8',
+            'append', 'arctan', 'assert', 'assigned', 'assignfile',
+            'beginthread', 'blockread', 'blockwrite', 'break', 'chdir',
+            'chr', 'close', 'closefile', 'comptocurrency', 'comptodouble',
+            'concat', 'continue', 'copy', 'cos', 'dec', 'delete',
+            'dispose', 'doubletocomp', 'endthread', 'enummodules',
+            'enumresourcemodules', 'eof', 'eoln', 'erase', 'exceptaddr',
+            'exceptobject', 'exclude', 'exit', 'exp', 'filepos', 'filesize',
+            'fillchar', 'finalize', 'findclasshinstance', 'findhinstance',
+            'findresourcehinstance', 'flush', 'frac', 'freemem',
+            'get8087cw', 'getdir', 'getlasterror', 'getmem',
+            'getmemorymanager', 'getmodulefilename', 'getvariantmanager',
+            'halt', 'hi', 'high', 'inc', 'include', 'initialize', 'insert',
+            'int', 'ioresult', 'ismemorymanagerset', 'isvariantmanagerset',
+            'length', 'ln', 'lo', 'low', 'mkdir', 'move', 'new', 'odd',
+            'olestrtostring', 'olestrtostrvar', 'ord', 'paramcount',
+            'paramstr', 'pi', 'pos', 'pred', 'ptr', 'pucs4chars', 'random',
+            'randomize', 'read', 'readln', 'reallocmem',
+            'releaseexceptionobject', 'rename', 'reset', 'rewrite', 'rmdir',
+            'round', 'runerror', 'seek', 'seekeof', 'seekeoln',
+            'set8087cw', 'setlength', 'setlinebreakstyle',
+            'setmemorymanager', 'setstring', 'settextbuf',
+            'setvariantmanager', 'sin', 'sizeof', 'slice', 'sqr', 'sqrt',
+            'str', 'stringofchar', 'stringtoolestr', 'stringtowidechar',
+            'succ', 'swap', 'trunc', 'truncate', 'typeinfo',
+            'ucs4stringtowidestring', 'unicodetoutf8', 'uniquestring',
+            'upcase', 'utf8decode', 'utf8encode', 'utf8toansi',
+            'utf8tounicode', 'val', 'vararrayredim', 'varclear',
+            'widecharlentostring', 'widecharlentostrvar',
+            'widechartostring', 'widechartostrvar',
+            'widestringtoucs4string', 'write', 'writeln'
+        ),
+        'SysUtils': (
+            'abort', 'addexitproc', 'addterminateproc', 'adjustlinebreaks',
+            'allocmem', 'ansicomparefilename', 'ansicomparestr',
+            'ansicomparetext', 'ansidequotedstr', 'ansiextractquotedstr',
+            'ansilastchar', 'ansilowercase', 'ansilowercasefilename',
+            'ansipos', 'ansiquotedstr', 'ansisamestr', 'ansisametext',
+            'ansistrcomp', 'ansistricomp', 'ansistrlastchar', 'ansistrlcomp',
+            'ansistrlicomp', 'ansistrlower', 'ansistrpos', 'ansistrrscan',
+            'ansistrscan', 'ansistrupper', 'ansiuppercase',
+            'ansiuppercasefilename', 'appendstr', 'assignstr', 'beep',
+            'booltostr', 'bytetocharindex', 'bytetocharlen', 'bytetype',
+            'callterminateprocs', 'changefileext', 'charlength',
+            'chartobyteindex', 'chartobytelen', 'comparemem', 'comparestr',
+            'comparetext', 'createdir', 'createguid', 'currentyear',
+            'currtostr', 'currtostrf', 'date', 'datetimetofiledate',
+            'datetimetostr', 'datetimetostring', 'datetimetosystemtime',
+            'datetimetotimestamp', 'datetostr', 'dayofweek', 'decodedate',
+            'decodedatefully', 'decodetime', 'deletefile', 'directoryexists',
+            'diskfree', 'disksize', 'disposestr', 'encodedate', 'encodetime',
+            'exceptionerrormessage', 'excludetrailingbackslash',
+            'excludetrailingpathdelimiter', 'expandfilename',
+            'expandfilenamecase', 'expanduncfilename', 'extractfiledir',
+            'extractfiledrive', 'extractfileext', 'extractfilename',
+            'extractfilepath', 'extractrelativepath', 'extractshortpathname',
+            'fileage', 'fileclose', 'filecreate', 'filedatetodatetime',
+            'fileexists', 'filegetattr', 'filegetdate', 'fileisreadonly',
+            'fileopen', 'fileread', 'filesearch', 'fileseek', 'filesetattr',
+            'filesetdate', 'filesetreadonly', 'filewrite', 'finalizepackage',
+            'findclose', 'findcmdlineswitch', 'findfirst', 'findnext',
+            'floattocurr', 'floattodatetime', 'floattodecimal', 'floattostr',
+            'floattostrf', 'floattotext', 'floattotextfmt', 'fmtloadstr',
+            'fmtstr', 'forcedirectories', 'format', 'formatbuf', 'formatcurr',
+            'formatdatetime', 'formatfloat', 'freeandnil', 'getcurrentdir',
+            'getenvironmentvariable', 'getfileversion', 'getformatsettings',
+            'getlocaleformatsettings', 'getmodulename', 'getpackagedescription',
+            'getpackageinfo', 'gettime', 'guidtostring', 'incamonth',
+            'includetrailingbackslash', 'includetrailingpathdelimiter',
+            'incmonth', 'initializepackage', 'interlockeddecrement',
+            'interlockedexchange', 'interlockedexchangeadd',
+            'interlockedincrement', 'inttohex', 'inttostr', 'isdelimiter',
+            'isequalguid', 'isleapyear', 'ispathdelimiter', 'isvalidident',
+            'languages', 'lastdelimiter', 'loadpackage', 'loadstr',
+            'lowercase', 'msecstotimestamp', 'newstr', 'nextcharindex', 'now',
+            'outofmemoryerror', 'quotedstr', 'raiselastoserror',
+            'raiselastwin32error', 'removedir', 'renamefile', 'replacedate',
+            'replacetime', 'safeloadlibrary', 'samefilename', 'sametext',
+            'setcurrentdir', 'showexception', 'sleep', 'stralloc', 'strbufsize',
+            'strbytetype', 'strcat', 'strcharlength', 'strcomp', 'strcopy',
+            'strdispose', 'strecopy', 'strend', 'strfmt', 'stricomp',
+            'stringreplace', 'stringtoguid', 'strlcat', 'strlcomp', 'strlcopy',
+            'strlen', 'strlfmt', 'strlicomp', 'strlower', 'strmove', 'strnew',
+            'strnextchar', 'strpas', 'strpcopy', 'strplcopy', 'strpos',
+            'strrscan', 'strscan', 'strtobool', 'strtobooldef', 'strtocurr',
+            'strtocurrdef', 'strtodate', 'strtodatedef', 'strtodatetime',
+            'strtodatetimedef', 'strtofloat', 'strtofloatdef', 'strtoint',
+            'strtoint64', 'strtoint64def', 'strtointdef', 'strtotime',
+            'strtotimedef', 'strupper', 'supports', 'syserrormessage',
+            'systemtimetodatetime', 'texttofloat', 'time', 'timestamptodatetime',
+            'timestamptomsecs', 'timetostr', 'trim', 'trimleft', 'trimright',
+            'tryencodedate', 'tryencodetime', 'tryfloattocurr', 'tryfloattodatetime',
+            'trystrtobool', 'trystrtocurr', 'trystrtodate', 'trystrtodatetime',
+            'trystrtofloat', 'trystrtoint', 'trystrtoint64', 'trystrtotime',
+            'unloadpackage', 'uppercase', 'widecomparestr', 'widecomparetext',
+            'widefmtstr', 'wideformat', 'wideformatbuf', 'widelowercase',
+            'widesamestr', 'widesametext', 'wideuppercase', 'win32check',
+            'wraptext'
+        ),
+        'Classes': (
+            'activateclassgroup', 'allocatehwnd', 'bintohex', 'checksynchronize',
+            'collectionsequal', 'countgenerations', 'deallocatehwnd', 'equalrect',
+            'extractstrings', 'findclass', 'findglobalcomponent', 'getclass',
+            'groupdescendantswith', 'hextobin', 'identtoint',
+            'initinheritedcomponent', 'inttoident', 'invalidpoint',
+            'isuniqueglobalcomponentname', 'linestart', 'objectbinarytotext',
+            'objectresourcetotext', 'objecttexttobinary', 'objecttexttoresource',
+            'pointsequal', 'readcomponentres', 'readcomponentresex',
+            'readcomponentresfile', 'rect', 'registerclass', 'registerclassalias',
+            'registerclasses', 'registercomponents', 'registerintegerconsts',
+            'registernoicon', 'registernonactivex', 'smallpoint', 'startclassgroup',
+            'teststreamformat', 'unregisterclass', 'unregisterclasses',
+            'unregisterintegerconsts', 'unregistermoduleclasses',
+            'writecomponentresfile'
+        ),
+        'Math': (
+            'arccos', 'arccosh', 'arccot', 'arccoth', 'arccsc', 'arccsch', 'arcsec',
+            'arcsech', 'arcsin', 'arcsinh', 'arctan2', 'arctanh', 'ceil',
+            'comparevalue', 'cosecant', 'cosh', 'cot', 'cotan', 'coth', 'csc',
+            'csch', 'cycletodeg', 'cycletograd', 'cycletorad', 'degtocycle',
+            'degtograd', 'degtorad', 'divmod', 'doubledecliningbalance',
+            'ensurerange', 'floor', 'frexp', 'futurevalue', 'getexceptionmask',
+            'getprecisionmode', 'getroundmode', 'gradtocycle', 'gradtodeg',
+            'gradtorad', 'hypot', 'inrange', 'interestpayment', 'interestrate',
+            'internalrateofreturn', 'intpower', 'isinfinite', 'isnan', 'iszero',
+            'ldexp', 'lnxp1', 'log10', 'log2', 'logn', 'max', 'maxintvalue',
+            'maxvalue', 'mean', 'meanandstddev', 'min', 'minintvalue', 'minvalue',
+            'momentskewkurtosis', 'netpresentvalue', 'norm', 'numberofperiods',
+            'payment', 'periodpayment', 'poly', 'popnstddev', 'popnvariance',
+            'power', 'presentvalue', 'radtocycle', 'radtodeg', 'radtograd',
+            'randg', 'randomrange', 'roundto', 'samevalue', 'sec', 'secant',
+            'sech', 'setexceptionmask', 'setprecisionmode', 'setroundmode',
+            'sign', 'simpleroundto', 'sincos', 'sinh', 'slndepreciation', 'stddev',
+            'sum', 'sumint', 'sumofsquares', 'sumsandsquares', 'syddepreciation',
+            'tan', 'tanh', 'totalvariance', 'variance'
+        )
+    }
+
+    ASM_REGISTERS = {
+        'ah', 'al', 'ax', 'bh', 'bl', 'bp', 'bx', 'ch', 'cl', 'cr0',
+        'cr1', 'cr2', 'cr3', 'cr4', 'cs', 'cx', 'dh', 'di', 'dl', 'dr0',
+        'dr1', 'dr2', 'dr3', 'dr4', 'dr5', 'dr6', 'dr7', 'ds', 'dx',
+        'eax', 'ebp', 'ebx', 'ecx', 'edi', 'edx', 'es', 'esi', 'esp',
+        'fs', 'gs', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6',
+        'mm7', 'si', 'sp', 'ss', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5',
+        'st6', 'st7', 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5',
+        'xmm6', 'xmm7'
+    }
+
+    ASM_INSTRUCTIONS = {
+        'aaa', 'aad', 'aam', 'aas', 'adc', 'add', 'and', 'arpl', 'bound',
+        'bsf', 'bsr', 'bswap', 'bt', 'btc', 'btr', 'bts', 'call', 'cbw',
+        'cdq', 'clc', 'cld', 'cli', 'clts', 'cmc', 'cmova', 'cmovae',
+        'cmovb', 'cmovbe', 'cmovc', 'cmovcxz', 'cmove', 'cmovg',
+        'cmovge', 'cmovl', 'cmovle', 'cmovna', 'cmovnae', 'cmovnb',
+        'cmovnbe', 'cmovnc', 'cmovne', 'cmovng', 'cmovnge', 'cmovnl',
+        'cmovnle', 'cmovno', 'cmovnp', 'cmovns', 'cmovnz', 'cmovo',
+        'cmovp', 'cmovpe', 'cmovpo', 'cmovs', 'cmovz', 'cmp', 'cmpsb',
+        'cmpsd', 'cmpsw', 'cmpxchg', 'cmpxchg486', 'cmpxchg8b', 'cpuid',
+        'cwd', 'cwde', 'daa', 'das', 'dec', 'div', 'emms', 'enter', 'hlt',
+        'ibts', 'icebp', 'idiv', 'imul', 'in', 'inc', 'insb', 'insd',
+        'insw', 'int', 'int01', 'int03', 'int1', 'int3', 'into', 'invd',
+        'invlpg', 'iret', 'iretd', 'iretw', 'ja', 'jae', 'jb', 'jbe',
+        'jc', 'jcxz', 'jcxz', 'je', 'jecxz', 'jg', 'jge', 'jl', 'jle',
+        'jmp', 'jna', 'jnae', 'jnb', 'jnbe', 'jnc', 'jne', 'jng', 'jnge',
+        'jnl', 'jnle', 'jno', 'jnp', 'jns', 'jnz', 'jo', 'jp', 'jpe',
+        'jpo', 'js', 'jz', 'lahf', 'lar', 'lcall', 'lds', 'lea', 'leave',
+        'les', 'lfs', 'lgdt', 'lgs', 'lidt', 'ljmp', 'lldt', 'lmsw',
+        'loadall', 'loadall286', 'lock', 'lodsb', 'lodsd', 'lodsw',
+        'loop', 'loope', 'loopne', 'loopnz', 'loopz', 'lsl', 'lss', 'ltr',
+        'mov', 'movd', 'movq', 'movsb', 'movsd', 'movsw', 'movsx',
+        'movzx', 'mul', 'neg', 'nop', 'not', 'or', 'out', 'outsb', 'outsd',
+        'outsw', 'pop', 'popa', 'popad', 'popaw', 'popf', 'popfd', 'popfw',
+        'push', 'pusha', 'pushad', 'pushaw', 'pushf', 'pushfd', 'pushfw',
+        'rcl', 'rcr', 'rdmsr', 'rdpmc', 'rdshr', 'rdtsc', 'rep', 'repe',
+        'repne', 'repnz', 'repz', 'ret', 'retf', 'retn', 'rol', 'ror',
+        'rsdc', 'rsldt', 'rsm', 'sahf', 'sal', 'salc', 'sar', 'sbb',
+        'scasb', 'scasd', 'scasw', 'seta', 'setae', 'setb', 'setbe',
+        'setc', 'setcxz', 'sete', 'setg', 'setge', 'setl', 'setle',
+        'setna', 'setnae', 'setnb', 'setnbe', 'setnc', 'setne', 'setng',
+        'setnge', 'setnl', 'setnle', 'setno', 'setnp', 'setns', 'setnz',
+        'seto', 'setp', 'setpe', 'setpo', 'sets', 'setz', 'sgdt', 'shl',
+        'shld', 'shr', 'shrd', 'sidt', 'sldt', 'smi', 'smint', 'smintold',
+        'smsw', 'stc', 'std', 'sti', 'stosb', 'stosd', 'stosw', 'str',
+        'sub', 'svdc', 'svldt', 'svts', 'syscall', 'sysenter', 'sysexit',
+        'sysret', 'test', 'ud1', 'ud2', 'umov', 'verr', 'verw', 'wait',
+        'wbinvd', 'wrmsr', 'wrshr', 'xadd', 'xbts', 'xchg', 'xlat',
+        'xlatb', 'xor'
+    }
+
+    PORTUGOL_KEYWORDS = (
+        'aleatorio',
+        'algoritmo',
+        'arquivo',
+        'ate',
+        'caso',
+        'cronometro',
+        'debug',
+        'e',
+        'eco',
+        'enquanto',
+        'entao',
+        'escolha',
+        'escreva',
+        'escreval',
+        'faca',
+        'falso',
+        'fimalgoritmo',
+        'fimenquanto',
+        'fimescolha',
+        'fimfuncao',
+        'fimpara',
+        'fimprocedimento',
+        'fimrepita',
+        'fimse',
+        'funcao',
+        'inicio',
+        'int',
+        'interrompa',
+        'leia',
+        'limpatela',
+        'mod',
+        'nao',
+        'ou',
+        'outrocaso',
+        'para',
+        'passo',
+        'pausa',
+        'procedimento',
+        'repita',
+        'retorne',
+        'se',
+        'senao',
+        'timer',
+        'var',
+        'vetor',
+        'verdadeiro',
+        'xou',
+        'div',
+        'mod',
+        'abs',
+        'arccos',
+        'arcsen',
+        'arctan',
+        'cos',
+        'cotan',
+        'Exp',
+        'grauprad',
+        'int',
+        'log',
+        'logn',
+        'pi',
+        'quad',
+        'radpgrau',
+        'raizq',
+        'rand',
+        'randi',
+        'sen',
+        'Tan',
+        'asc',
+        'carac',
+        'caracpnum',
+        'compr',
+        'copia',
+        'maiusc',
+        'minusc',
+        'numpcarac',
+        'pos',
+    )
+
+    PORTUGOL_BUILTIN_TYPES = {
+        'inteiro', 'real', 'caractere', 'logico'
+    }
+
+    def __init__(self, **options):
+        Lexer.__init__(self, **options)
+        self.keywords = set()
+        self.builtins = set()
+        if get_bool_opt(options, 'portugol', False):
+            self.keywords.update(self.PORTUGOL_KEYWORDS)
+            self.builtins.update(self.PORTUGOL_BUILTIN_TYPES)
+            self.is_portugol = True
+        else:
+            self.is_portugol = False
+
+            if get_bool_opt(options, 'turbopascal', True):
+                self.keywords.update(self.TURBO_PASCAL_KEYWORDS)
+            if get_bool_opt(options, 'delphi', True):
+                self.keywords.update(self.DELPHI_KEYWORDS)
+            if get_bool_opt(options, 'freepascal', True):
+                self.keywords.update(self.FREE_PASCAL_KEYWORDS)
+            for unit in get_list_opt(options, 'units', list(self.BUILTIN_UNITS)):
+                self.builtins.update(self.BUILTIN_UNITS[unit])
+
+    def get_tokens_unprocessed(self, text):
+        scanner = Scanner(text, re.DOTALL | re.MULTILINE | re.IGNORECASE)
+        stack = ['initial']
+        in_function_block = False
+        in_property_block = False
+        was_dot = False
+        next_token_is_function = False
+        next_token_is_property = False
+        collect_labels = False
+        block_labels = set()
+        brace_balance = [0, 0]
+
+        while not scanner.eos:
+            token = Error
+
+            if stack[-1] == 'initial':
+                if scanner.scan(r'\s+'):
+                    token = Whitespace
+                elif not self.is_portugol and scanner.scan(r'\{.*?\}|\(\*.*?\*\)'):
+                    if scanner.match.startswith('$'):
+                        token = Comment.Preproc
+                    else:
+                        token = Comment.Multiline
+                elif scanner.scan(r'//.*?$'):
+                    token = Comment.Single
+                elif self.is_portugol and scanner.scan(r'(<\-)|(>=)|(<=)|%|<|>|-|\+|\*|\=|(<>)|\/|\.|:|,'):
+                    token = Operator
+                elif not self.is_portugol and scanner.scan(r'[-+*\/=<>:;,.@\^]'):
+                    token = Operator
+                    # stop label highlighting on next ";"
+                    if collect_labels and scanner.match == ';':
+                        collect_labels = False
+                elif scanner.scan(r'[\(\)\[\]]+'):
+                    token = Punctuation
+                    # abort function naming ``foo = Function(...)``
+                    next_token_is_function = False
+                    # if we are in a function block we count the open
+                    # braces because ootherwise it's impossible to
+                    # determine the end of the modifier context
+                    if in_function_block or in_property_block:
+                        if scanner.match == '(':
+                            brace_balance[0] += 1
+                        elif scanner.match == ')':
+                            brace_balance[0] -= 1
+                        elif scanner.match == '[':
+                            brace_balance[1] += 1
+                        elif scanner.match == ']':
+                            brace_balance[1] -= 1
+                elif scanner.scan(r'[A-Za-z_][A-Za-z_0-9]*'):
+                    lowercase_name = scanner.match.lower()
+                    if lowercase_name == 'result':
+                        token = Name.Builtin.Pseudo
+                    elif lowercase_name in self.keywords:
+                        token = Keyword
+                        # if we are in a special block and a
+                        # block ending keyword occurs (and the parenthesis
+                        # is balanced) we end the current block context
+                        if self.is_portugol:
+                            if lowercase_name in ('funcao', 'procedimento'):
+                                in_function_block = True
+                                next_token_is_function = True
+                        else:
+                            if (in_function_block or in_property_block) and \
+                                    lowercase_name in self.BLOCK_KEYWORDS and \
+                                    brace_balance[0] <= 0 and \
+                                    brace_balance[1] <= 0:
+                                in_function_block = False
+                                in_property_block = False
+                                brace_balance = [0, 0]
+                                block_labels = set()
+                            if lowercase_name in ('label', 'goto'):
+                                collect_labels = True
+                            elif lowercase_name == 'asm':
+                                stack.append('asm')
+                            elif lowercase_name == 'property':
+                                in_property_block = True
+                                next_token_is_property = True
+                            elif lowercase_name in ('procedure', 'operator',
+                                                    'function', 'constructor',
+                                                    'destructor'):
+                                in_function_block = True
+                                next_token_is_function = True
+                    # we are in a function block and the current name
+                    # is in the set of registered modifiers. highlight
+                    # it as pseudo keyword
+                    elif not self.is_portugol and in_function_block and \
+                            lowercase_name in self.FUNCTION_MODIFIERS:
+                        token = Keyword.Pseudo
+                    # if we are in a property highlight some more
+                    # modifiers
+                    elif not self.is_portugol and in_property_block and \
+                            lowercase_name in ('read', 'write'):
+                        token = Keyword.Pseudo
+                        next_token_is_function = True
+                    # if the last iteration set next_token_is_function
+                    # to true we now want this name highlighted as
+                    # function. so do that and reset the state
+                    elif next_token_is_function:
+                        # Look if the next token is a dot. If yes it's
+                        # not a function, but a class name and the
+                        # part after the dot a function name
+                        if not self.is_portugol and scanner.test(r'\s*\.\s*'):
+                            token = Name.Class
+                        # it's not a dot, our job is done
+                        else:
+                            token = Name.Function
+                            next_token_is_function = False
+
+                            if self.is_portugol:
+                                block_labels.add(scanner.match.lower())
+
+                    # same for properties
+                    elif not self.is_portugol and next_token_is_property:
+                        token = Name.Property
+                        next_token_is_property = False
+                    # Highlight this token as label and add it
+                    # to the list of known labels
+                    elif not self.is_portugol and collect_labels:
+                        token = Name.Label
+                        block_labels.add(scanner.match.lower())
+                    # name is in list of known labels
+                    elif lowercase_name in block_labels:
+                        token = Name.Label
+                    elif self.is_portugol and lowercase_name in self.PORTUGOL_BUILTIN_TYPES:
+                        token = Keyword.Type
+                    elif not self.is_portugol and lowercase_name in self.BUILTIN_TYPES:
+                        token = Keyword.Type
+                    elif not self.is_portugol and lowercase_name in self.DIRECTIVES:
+                        token = Keyword.Pseudo
+                    # builtins are just builtins if the token
+                    # before isn't a dot
+                    elif not self.is_portugol and not was_dot and lowercase_name in self.builtins:
+                        token = Name.Builtin
+                    else:
+                        token = Name
+                elif self.is_portugol and scanner.scan(r"\""):
+                    token = String
+                    stack.append('string')
+                elif not self.is_portugol and scanner.scan(r"'"):
+                    token = String
+                    stack.append('string')
+                elif not self.is_portugol and scanner.scan(r'\#(\d+|\$[0-9A-Fa-f]+)'):
+                    token = String.Char
+                elif not self.is_portugol and scanner.scan(r'\$[0-9A-Fa-f]+'):
+                    token = Number.Hex
+                elif scanner.scan(r'\d+(?![eE]|\.[^.])'):
+                    token = Number.Integer
+                elif scanner.scan(r'\d+(\.\d+([eE][+-]?\d+)?|[eE][+-]?\d+)'):
+                    token = Number.Float
+                else:
+                    # if the stack depth is deeper than once, pop
+                    if len(stack) > 1:
+                        stack.pop()
+                    scanner.get_char()
+
+            elif stack[-1] == 'string':
+                if self.is_portugol:
+                    if scanner.scan(r"''"):
+                        token = String.Escape
+                    elif scanner.scan(r"\""):
+                        token = String
+                        stack.pop()
+                    elif scanner.scan(r"[^\"]*"):
+                        token = String
+                    else:
+                        scanner.get_char()
+                        stack.pop()
+                else:
+                    if scanner.scan(r"''"):
+                        token = String.Escape
+                    elif scanner.scan(r"'"):
+                        token = String
+                        stack.pop()
+                    elif scanner.scan(r"[^']*"):
+                        token = String
+                    else:
+                        scanner.get_char()
+                        stack.pop()
+            elif not self.is_portugol and stack[-1] == 'asm':
+                if scanner.scan(r'\s+'):
+                    token = Whitespace
+                elif scanner.scan(r'end'):
+                    token = Keyword
+                    stack.pop()
+                elif scanner.scan(r'\{.*?\}|\(\*.*?\*\)'):
+                    if scanner.match.startswith('$'):
+                        token = Comment.Preproc
+                    else:
+                        token = Comment.Multiline
+                elif scanner.scan(r'//.*?$'):
+                    token = Comment.Single
+                elif scanner.scan(r"'"):
+                    token = String
+                    stack.append('string')
+                elif scanner.scan(r'@@[A-Za-z_][A-Za-z_0-9]*'):
+                    token = Name.Label
+                elif scanner.scan(r'[A-Za-z_][A-Za-z_0-9]*'):
+                    lowercase_name = scanner.match.lower()
+                    if lowercase_name in self.ASM_INSTRUCTIONS:
+                        token = Keyword
+                    elif lowercase_name in self.ASM_REGISTERS:
+                        token = Name.Builtin
+                    else:
+                        token = Name
+                elif scanner.scan(r'[-+*\/=<>:;,.@\^]+'):
+                    token = Operator
+                elif scanner.scan(r'[\(\)\[\]]+'):
+                    token = Punctuation
+                elif scanner.scan(r'\$[0-9A-Fa-f]+'):
+                    token = Number.Hex
+                elif scanner.scan(r'\d+(?![eE]|\.[^.])'):
+                    token = Number.Integer
+                elif scanner.scan(r'\d+(\.\d+([eE][+-]?\d+)?|[eE][+-]?\d+)'):
+                    token = Number.Float
+                else:
+                    scanner.get_char()
+                    stack.pop()
+
+            # save the dot!!!11
+            if not self.is_portugol and scanner.match.strip():
+                was_dot = scanner.match == '.'
+
+            yield scanner.start_pos, token, scanner.match or ''
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/pawn.py b/.venv/lib/python3.12/site-packages/pygments/lexers/pawn.py
new file mode 100644
index 0000000..9b4234c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/pawn.py
@@ -0,0 +1,202 @@
+"""
+    pygments.lexers.pawn
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Pawn languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+from pygments.util import get_bool_opt
+
+__all__ = ['SourcePawnLexer', 'PawnLexer']
+
+
+class SourcePawnLexer(RegexLexer):
+    """
+    For SourcePawn source code with preprocessor directives.
+    """
+    name = 'SourcePawn'
+    aliases = ['sp']
+    filenames = ['*.sp']
+    mimetypes = ['text/x-sourcepawn']
+    url = 'https://github.com/alliedmodders/sourcepawn'
+    version_added = '1.6'
+
+    #: optional Comment or Whitespace
+    _ws = r'(?:\s|//.*?\n|/\*.*?\*/)+'
+    #: only one /* */ style comment
+    _ws1 = r'\s*(?:/[*].*?[*]/\s*)*'
+
+    tokens = {
+        'root': [
+            # preprocessor directives: without whitespace
+            (r'^#if\s+0', Comment.Preproc, 'if0'),
+            ('^#', Comment.Preproc, 'macro'),
+            # or with whitespace
+            ('^' + _ws1 + r'#if\s+0', Comment.Preproc, 'if0'),
+            ('^' + _ws1 + '#', Comment.Preproc, 'macro'),
+            (r'\n', Text),
+            (r'\s+', Text),
+            (r'\\\n', Text),  # line continuation
+            (r'/(\\\n)?/(\n|(.|\n)*?[^\\]\n)', Comment.Single),
+            (r'/(\\\n)?\*(.|\n)*?\*(\\\n)?/', Comment.Multiline),
+            (r'[{}]', Punctuation),
+            (r'L?"', String, 'string'),
+            (r"L?'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char),
+            (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[LlUu]*', Number.Float),
+            (r'(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float),
+            (r'0x[0-9a-fA-F]+[LlUu]*', Number.Hex),
+            (r'0[0-7]+[LlUu]*', Number.Oct),
+            (r'\d+[LlUu]*', Number.Integer),
+            (r'[~!%^&*+=|?:<>/-]', Operator),
+            (r'[()\[\],.;]', Punctuation),
+            (r'(case|const|continue|native|'
+             r'default|else|enum|for|if|new|operator|'
+             r'public|return|sizeof|static|decl|struct|switch)\b', Keyword),
+            (r'(bool|Float)\b', Keyword.Type),
+            (r'(true|false)\b', Keyword.Constant),
+            (r'[a-zA-Z_]\w*', Name),
+        ],
+        'string': [
+            (r'"', String, '#pop'),
+            (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape),
+            (r'[^\\"\n]+', String),  # all other characters
+            (r'\\\n', String),       # line continuation
+            (r'\\', String),         # stray backslash
+        ],
+        'macro': [
+            (r'[^/\n]+', Comment.Preproc),
+            (r'/\*(.|\n)*?\*/', Comment.Multiline),
+            (r'//.*?\n', Comment.Single, '#pop'),
+            (r'/', Comment.Preproc),
+            (r'(?<=\\)\n', Comment.Preproc),
+            (r'\n', Comment.Preproc, '#pop'),
+        ],
+        'if0': [
+            (r'^\s*#if.*?(?/-]', Operator),
+            (r'[()\[\],.;]', Punctuation),
+            (r'(switch|case|default|const|new|static|char|continue|break|'
+             r'if|else|for|while|do|operator|enum|'
+             r'public|return|sizeof|tagof|state|goto)\b', Keyword),
+            (r'(bool|Float)\b', Keyword.Type),
+            (r'(true|false)\b', Keyword.Constant),
+            (r'[a-zA-Z_]\w*', Name),
+        ],
+        'string': [
+            (r'"', String, '#pop'),
+            (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape),
+            (r'[^\\"\n]+', String),  # all other characters
+            (r'\\\n', String),       # line continuation
+            (r'\\', String),         # stray backslash
+        ],
+        'macro': [
+            (r'[^/\n]+', Comment.Preproc),
+            (r'/\*(.|\n)*?\*/', Comment.Multiline),
+            (r'//.*?\n', Comment.Single, '#pop'),
+            (r'/', Comment.Preproc),
+            (r'(?<=\\)\n', Comment.Preproc),
+            (r'\n', Comment.Preproc, '#pop'),
+        ],
+        'if0': [
+            (r'^\s*#if.*?(?<-]', Operator),
+            (r'[a-zA-Z][a-zA-Z0-9_-]*', Name),
+            (r'\?[a-zA-Z][a-zA-Z0-9_-]*', Name.Variable),
+            (r'[0-9]+\.[0-9]+', Number.Float),
+            (r'[0-9]+', Number.Integer),
+        ],
+        'keywords': [
+            (words((
+                ':requirements', ':types', ':constants',
+                ':predicates', ':functions', ':action', ':agent',
+                ':parameters', ':precondition', ':effect',
+                ':durative-action', ':duration', ':condition',
+                ':derived', ':domain', ':objects', ':init',
+                ':goal', ':metric', ':length', ':serial', ':parallel',
+                # the following are requirements
+                ':strips', ':typing', ':negative-preconditions',
+                ':disjunctive-preconditions', ':equality',
+                ':existential-preconditions', ':universal-preconditions',
+                ':conditional-effects', ':fluents', ':numeric-fluents',
+                ':object-fluents', ':adl', ':durative-actions',
+                ':continuous-effects', ':derived-predicates',
+                ':time-intial-literals', ':preferences',
+                ':constraints', ':action-costs', ':multi-agent',
+                ':unfactored-privacy', ':factored-privacy',
+                ':non-deterministic'
+                ), suffix=r'\b'), Keyword)
+        ],
+        'builtins': [
+            (words((
+                'define', 'domain', 'object', 'either', 'and',
+                'forall', 'preference', 'imply', 'or', 'exists',
+                'not', 'when', 'assign', 'scale-up', 'scale-down',
+                'increase', 'decrease', 'at', 'over', 'start',
+                'end', 'all', 'problem', 'always', 'sometime',
+                'within', 'at-most-once', 'sometime-after',
+                'sometime-before', 'always-within', 'hold-during',
+                'hold-after', 'minimize', 'maximize',
+                'total-time', 'is-violated'), suffix=r'\b'),
+                Name.Builtin)
+        ]
+    }
+
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/perl.py b/.venv/lib/python3.12/site-packages/pygments/lexers/perl.py
new file mode 100644
index 0000000..7448d2a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/perl.py
@@ -0,0 +1,733 @@
+"""
+    pygments.lexers.perl
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Perl, Raku and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, ExtendedRegexLexer, include, bygroups, \
+    using, this, default, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+from pygments.util import shebang_matches
+
+__all__ = ['PerlLexer', 'Perl6Lexer']
+
+
+class PerlLexer(RegexLexer):
+    """
+    For Perl source code.
+    """
+
+    name = 'Perl'
+    url = 'https://www.perl.org'
+    aliases = ['perl', 'pl']
+    filenames = ['*.pl', '*.pm', '*.t', '*.perl']
+    mimetypes = ['text/x-perl', 'application/x-perl']
+    version_added = ''
+
+    flags = re.DOTALL | re.MULTILINE
+    # TODO: give this to a perl guy who knows how to parse perl...
+    tokens = {
+        'balanced-regex': [
+            (r'/(\\\\|\\[^\\]|[^\\/])*/[egimosx]*', String.Regex, '#pop'),
+            (r'!(\\\\|\\[^\\]|[^\\!])*![egimosx]*', String.Regex, '#pop'),
+            (r'\\(\\\\|[^\\])*\\[egimosx]*', String.Regex, '#pop'),
+            (r'\{(\\\\|\\[^\\]|[^\\}])*\}[egimosx]*', String.Regex, '#pop'),
+            (r'<(\\\\|\\[^\\]|[^\\>])*>[egimosx]*', String.Regex, '#pop'),
+            (r'\[(\\\\|\\[^\\]|[^\\\]])*\][egimosx]*', String.Regex, '#pop'),
+            (r'\((\\\\|\\[^\\]|[^\\)])*\)[egimosx]*', String.Regex, '#pop'),
+            (r'@(\\\\|\\[^\\]|[^\\@])*@[egimosx]*', String.Regex, '#pop'),
+            (r'%(\\\\|\\[^\\]|[^\\%])*%[egimosx]*', String.Regex, '#pop'),
+            (r'\$(\\\\|\\[^\\]|[^\\$])*\$[egimosx]*', String.Regex, '#pop'),
+        ],
+        'root': [
+            (r'\A\#!.+?$', Comment.Hashbang),
+            (r'\#.*?$', Comment.Single),
+            (r'^=[a-zA-Z0-9]+\s+.*?\n=cut', Comment.Multiline),
+            (words((
+                'case', 'continue', 'do', 'else', 'elsif', 'for', 'foreach',
+                'if', 'last', 'my', 'next', 'our', 'redo', 'reset', 'then',
+                'unless', 'until', 'while', 'print', 'new', 'BEGIN',
+                'CHECK', 'INIT', 'END', 'return'), suffix=r'\b'),
+             Keyword),
+            (r'(format)(\s+)(\w+)(\s*)(=)(\s*\n)',
+             bygroups(Keyword, Whitespace, Name, Whitespace, Punctuation, Whitespace), 'format'),
+            (r'(eq|lt|gt|le|ge|ne|not|and|or|cmp)\b', Operator.Word),
+            # common delimiters
+            (r's/(\\\\|\\[^\\]|[^\\/])*/(\\\\|\\[^\\]|[^\\/])*/[egimosx]*',
+                String.Regex),
+            (r's!(\\\\|\\!|[^!])*!(\\\\|\\!|[^!])*![egimosx]*', String.Regex),
+            (r's\\(\\\\|[^\\])*\\(\\\\|[^\\])*\\[egimosx]*', String.Regex),
+            (r's@(\\\\|\\[^\\]|[^\\@])*@(\\\\|\\[^\\]|[^\\@])*@[egimosx]*',
+                String.Regex),
+            (r's%(\\\\|\\[^\\]|[^\\%])*%(\\\\|\\[^\\]|[^\\%])*%[egimosx]*',
+                String.Regex),
+            # balanced delimiters
+            (r's\{(\\\\|\\[^\\]|[^\\}])*\}\s*', String.Regex, 'balanced-regex'),
+            (r's<(\\\\|\\[^\\]|[^\\>])*>\s*', String.Regex, 'balanced-regex'),
+            (r's\[(\\\\|\\[^\\]|[^\\\]])*\]\s*', String.Regex,
+                'balanced-regex'),
+            (r's\((\\\\|\\[^\\]|[^\\)])*\)\s*', String.Regex,
+                'balanced-regex'),
+
+            (r'm?/(\\\\|\\[^\\]|[^\\/\n])*/[gcimosx]*', String.Regex),
+            (r'm(?=[/!\\{<\[(@%$])', String.Regex, 'balanced-regex'),
+            (r'((?<==~)|(?<=\())\s*/(\\\\|\\[^\\]|[^\\/])*/[gcimosx]*',
+                String.Regex),
+            (r'\s+', Whitespace),
+            (words((
+                'abs', 'accept', 'alarm', 'atan2', 'bind', 'binmode', 'bless', 'caller', 'chdir',
+                'chmod', 'chomp', 'chop', 'chown', 'chr', 'chroot', 'close', 'closedir', 'connect',
+                'continue', 'cos', 'crypt', 'dbmclose', 'dbmopen', 'defined', 'delete', 'die',
+                'dump', 'each', 'endgrent', 'endhostent', 'endnetent', 'endprotoent',
+                'endpwent', 'endservent', 'eof', 'eval', 'exec', 'exists', 'exit', 'exp', 'fcntl',
+                'fileno', 'flock', 'fork', 'format', 'formline', 'getc', 'getgrent', 'getgrgid',
+                'getgrnam', 'gethostbyaddr', 'gethostbyname', 'gethostent', 'getlogin',
+                'getnetbyaddr', 'getnetbyname', 'getnetent', 'getpeername', 'getpgrp',
+                'getppid', 'getpriority', 'getprotobyname', 'getprotobynumber',
+                'getprotoent', 'getpwent', 'getpwnam', 'getpwuid', 'getservbyname',
+                'getservbyport', 'getservent', 'getsockname', 'getsockopt', 'glob', 'gmtime',
+                'goto', 'grep', 'hex', 'import', 'index', 'int', 'ioctl', 'join', 'keys', 'kill', 'last',
+                'lc', 'lcfirst', 'length', 'link', 'listen', 'local', 'localtime', 'log', 'lstat',
+                'map', 'mkdir', 'msgctl', 'msgget', 'msgrcv', 'msgsnd', 'my', 'next', 'oct', 'open',
+                'opendir', 'ord', 'our', 'pack', 'pipe', 'pop', 'pos', 'printf',
+                'prototype', 'push', 'quotemeta', 'rand', 'read', 'readdir',
+                'readline', 'readlink', 'readpipe', 'recv', 'redo', 'ref', 'rename',
+                'reverse', 'rewinddir', 'rindex', 'rmdir', 'scalar', 'seek', 'seekdir',
+                'select', 'semctl', 'semget', 'semop', 'send', 'setgrent', 'sethostent', 'setnetent',
+                'setpgrp', 'setpriority', 'setprotoent', 'setpwent', 'setservent',
+                'setsockopt', 'shift', 'shmctl', 'shmget', 'shmread', 'shmwrite', 'shutdown',
+                'sin', 'sleep', 'socket', 'socketpair', 'sort', 'splice', 'split', 'sprintf', 'sqrt',
+                'srand', 'stat', 'study', 'substr', 'symlink', 'syscall', 'sysopen', 'sysread',
+                'sysseek', 'system', 'syswrite', 'tell', 'telldir', 'tie', 'tied', 'time', 'times', 'tr',
+                'truncate', 'uc', 'ucfirst', 'umask', 'undef', 'unlink', 'unpack', 'unshift', 'untie',
+                'utime', 'values', 'vec', 'wait', 'waitpid', 'wantarray', 'warn', 'write'), suffix=r'\b'),
+             Name.Builtin),
+            (r'((__(DATA|DIE|WARN)__)|(STD(IN|OUT|ERR)))\b', Name.Builtin.Pseudo),
+            (r'(<<)([\'"]?)([a-zA-Z_]\w*)(\2;?\n.*?\n)(\3)(\n)',
+             bygroups(String, String, String.Delimiter, String, String.Delimiter, Whitespace)),
+            (r'__END__', Comment.Preproc, 'end-part'),
+            (r'\$\^[ADEFHILMOPSTWX]', Name.Variable.Global),
+            (r"\$[\\\"\[\]'&`+*.,;=%~?@$!<>(^|/-](?!\w)", Name.Variable.Global),
+            (r'[$@%#]+', Name.Variable, 'varname'),
+            (r'0_?[0-7]+(_[0-7]+)*', Number.Oct),
+            (r'0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*', Number.Hex),
+            (r'0b[01]+(_[01]+)*', Number.Bin),
+            (r'(?i)(\d*(_\d*)*\.\d+(_\d*)*|\d+(_\d*)*\.\d+(_\d*)*)(e[+-]?\d+)?',
+             Number.Float),
+            (r'(?i)\d+(_\d*)*e[+-]?\d+(_\d*)*', Number.Float),
+            (r'\d+(_\d+)*', Number.Integer),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+            (r'`(\\\\|\\[^\\]|[^`\\])*`', String.Backtick),
+            (r'<([^\s>]+)>', String.Regex),
+            (r'(q|qq|qw|qr|qx)\{', String.Other, 'cb-string'),
+            (r'(q|qq|qw|qr|qx)\(', String.Other, 'rb-string'),
+            (r'(q|qq|qw|qr|qx)\[', String.Other, 'sb-string'),
+            (r'(q|qq|qw|qr|qx)\<', String.Other, 'lt-string'),
+            (r'(q|qq|qw|qr|qx)([\W_])(.|\n)*?\2', String.Other),
+            (r'(package)(\s+)([a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*)',
+             bygroups(Keyword, Whitespace, Name.Namespace)),
+            (r'(use|require|no)(\s+)([a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*)',
+             bygroups(Keyword, Whitespace, Name.Namespace)),
+            (r'(sub)(\s+)', bygroups(Keyword, Whitespace), 'funcname'),
+            (words((
+                'no', 'package', 'require', 'use'), suffix=r'\b'),
+             Keyword),
+            (r'(\[\]|\*\*|::|<<|>>|>=|<=>|<=|={3}|!=|=~|'
+             r'!~|&&?|\|\||\.{1,3})', Operator),
+            (r'[-+/*%=<>&^|!\\~]=?', Operator),
+            (r'[()\[\]:;,<>/?{}]', Punctuation),  # yes, there's no shortage
+                                                  # of punctuation in Perl!
+            (r'(?=\w)', Name, 'name'),
+        ],
+        'format': [
+            (r'\.\n', String.Interpol, '#pop'),
+            (r'[^\n]*\n', String.Interpol),
+        ],
+        'varname': [
+            (r'\s+', Whitespace),
+            (r'\{', Punctuation, '#pop'),    # hash syntax?
+            (r'\)|,', Punctuation, '#pop'),  # argument specifier
+            (r'\w+::', Name.Namespace),
+            (r'[\w:]+', Name.Variable, '#pop'),
+        ],
+        'name': [
+            (r'[a-zA-Z_]\w*(::[a-zA-Z_]\w*)*(::)?(?=\s*->)', Name.Namespace, '#pop'),
+            (r'[a-zA-Z_]\w*(::[a-zA-Z_]\w*)*::', Name.Namespace, '#pop'),
+            (r'[\w:]+', Name, '#pop'),
+            (r'[A-Z_]+(?=\W)', Name.Constant, '#pop'),
+            (r'(?=\W)', Text, '#pop'),
+        ],
+        'funcname': [
+            (r'[a-zA-Z_]\w*[!?]?', Name.Function),
+            (r'\s+', Whitespace),
+            # argument declaration
+            (r'(\([$@%]*\))(\s*)', bygroups(Punctuation, Whitespace)),
+            (r';', Punctuation, '#pop'),
+            (r'.*?\{', Punctuation, '#pop'),
+        ],
+        'cb-string': [
+            (r'\\[{}\\]', String.Other),
+            (r'\\', String.Other),
+            (r'\{', String.Other, 'cb-string'),
+            (r'\}', String.Other, '#pop'),
+            (r'[^{}\\]+', String.Other)
+        ],
+        'rb-string': [
+            (r'\\[()\\]', String.Other),
+            (r'\\', String.Other),
+            (r'\(', String.Other, 'rb-string'),
+            (r'\)', String.Other, '#pop'),
+            (r'[^()]+', String.Other)
+        ],
+        'sb-string': [
+            (r'\\[\[\]\\]', String.Other),
+            (r'\\', String.Other),
+            (r'\[', String.Other, 'sb-string'),
+            (r'\]', String.Other, '#pop'),
+            (r'[^\[\]]+', String.Other)
+        ],
+        'lt-string': [
+            (r'\\[<>\\]', String.Other),
+            (r'\\', String.Other),
+            (r'\<', String.Other, 'lt-string'),
+            (r'\>', String.Other, '#pop'),
+            (r'[^<>]+', String.Other)
+        ],
+        'end-part': [
+            (r'.+', Comment.Preproc, '#pop')
+        ]
+    }
+
+    def analyse_text(text):
+        if shebang_matches(text, r'perl'):
+            return True
+
+        result = 0
+
+        if re.search(r'(?:my|our)\s+[$@%(]', text):
+            result += 0.9
+
+        if ':=' in text:
+            # := is not valid Perl, but it appears in unicon, so we should
+            # become less confident if we think we found Perl with :=
+            result /= 2
+
+        return result
+
+
+class Perl6Lexer(ExtendedRegexLexer):
+    """
+    For Raku (a.k.a. Perl 6) source code.
+    """
+
+    name = 'Perl6'
+    url = 'https://www.raku.org'
+    aliases = ['perl6', 'pl6', 'raku']
+    filenames = ['*.pl', '*.pm', '*.nqp', '*.p6', '*.6pl', '*.p6l', '*.pl6',
+                 '*.6pm', '*.p6m', '*.pm6', '*.t', '*.raku', '*.rakumod',
+                 '*.rakutest', '*.rakudoc']
+    mimetypes = ['text/x-perl6', 'application/x-perl6']
+    version_added = '2.0'
+    flags = re.MULTILINE | re.DOTALL
+
+    PERL6_IDENTIFIER_RANGE = r"['\w:-]"
+
+    PERL6_KEYWORDS = (
+        #Phasers
+        'BEGIN','CATCH','CHECK','CLOSE','CONTROL','DOC','END','ENTER','FIRST',
+        'INIT','KEEP','LAST','LEAVE','NEXT','POST','PRE','QUIT','UNDO',
+        #Keywords
+        'anon','augment','but','class','constant','default','does','else',
+        'elsif','enum','for','gather','given','grammar','has','if','import',
+        'is','let','loop','made','make','method','module','multi','my','need',
+        'orwith','our','proceed','proto','repeat','require','return',
+        'return-rw','returns','role','rule','state','sub','submethod','subset',
+        'succeed','supersede','token','try','unit','unless','until','use',
+        'when','while','with','without',
+        #Traits
+        'export','native','repr','required','rw','symbol',
+    )
+
+    PERL6_BUILTINS = (
+        'ACCEPTS','abs','abs2rel','absolute','accept','accessed','acos',
+        'acosec','acosech','acosh','acotan','acotanh','acquire','act','action',
+        'actions','add','add_attribute','add_enum_value','add_fallback',
+        'add_method','add_parent','add_private_method','add_role','add_trustee',
+        'adverb','after','all','allocate','allof','allowed','alternative-names',
+        'annotations','antipair','antipairs','any','anyof','app_lifetime',
+        'append','arch','archname','args','arity','Array','asec','asech','asin',
+        'asinh','ASSIGN-KEY','ASSIGN-POS','assuming','ast','at','atan','atan2',
+        'atanh','AT-KEY','atomic-assign','atomic-dec-fetch','atomic-fetch',
+        'atomic-fetch-add','atomic-fetch-dec','atomic-fetch-inc',
+        'atomic-fetch-sub','atomic-inc-fetch','AT-POS','attributes','auth',
+        'await','backtrace','Bag','BagHash','bail-out','base','basename',
+        'base-repeating','batch','BIND-KEY','BIND-POS','bind-stderr',
+        'bind-stdin','bind-stdout','bind-udp','bits','bless','block','Bool',
+        'bool-only','bounds','break','Bridge','broken','BUILD','build-date',
+        'bytes','cache','callframe','calling-package','CALL-ME','callsame',
+        'callwith','can','cancel','candidates','cando','can-ok','canonpath',
+        'caps','caption','Capture','cas','catdir','categorize','categorize-list',
+        'catfile','catpath','cause','ceiling','cglobal','changed','Channel',
+        'chars','chdir','child','child-name','child-typename','chmod','chomp',
+        'chop','chr','chrs','chunks','cis','classify','classify-list','cleanup',
+        'clone','close','closed','close-stdin','cmp-ok','code','codes','collate',
+        'column','comb','combinations','command','comment','compiler','Complex',
+        'compose','compose_type','composer','condition','config',
+        'configure_destroy','configure_type_checking','conj','connect',
+        'constraints','construct','contains','contents','copy','cos','cosec',
+        'cosech','cosh','cotan','cotanh','count','count-only','cpu-cores',
+        'cpu-usage','CREATE','create_type','cross','cue','curdir','curupdir','d',
+        'Date','DateTime','day','daycount','day-of-month','day-of-week',
+        'day-of-year','days-in-month','declaration','decode','decoder','deepmap',
+        'default','defined','DEFINITE','delayed','DELETE-KEY','DELETE-POS',
+        'denominator','desc','DESTROY','destroyers','devnull','diag',
+        'did-you-mean','die','dies-ok','dir','dirname','dir-sep','DISTROnames',
+        'do','does','does-ok','done','done-testing','duckmap','dynamic','e',
+        'eager','earlier','elems','emit','enclosing','encode','encoder',
+        'encoding','end','ends-with','enum_from_value','enum_value_list',
+        'enum_values','enums','eof','EVAL','eval-dies-ok','EVALFILE',
+        'eval-lives-ok','exception','excludes-max','excludes-min','EXISTS-KEY',
+        'EXISTS-POS','exit','exitcode','exp','expected','explicitly-manage',
+        'expmod','extension','f','fail','fails-like','fc','feature','file',
+        'filename','find_method','find_method_qualified','finish','first','flat',
+        'flatmap','flip','floor','flunk','flush','fmt','format','formatter',
+        'freeze','from','from-list','from-loop','from-posix','full',
+        'full-barrier','get','get_value','getc','gist','got','grab','grabpairs',
+        'grep','handle','handled','handles','hardware','has_accessor','Hash',
+        'head','headers','hh-mm-ss','hidden','hides','hour','how','hyper','id',
+        'illegal','im','in','indent','index','indices','indir','infinite',
+        'infix','infix:<+>','infix:<->','install_method_cache','Instant',
+        'instead','Int','int-bounds','interval','in-timezone','invalid-str',
+        'invert','invocant','IO','IO::Notification.watch-path','is_trusted',
+        'is_type','isa','is-absolute','isa-ok','is-approx','is-deeply',
+        'is-hidden','is-initial-thread','is-int','is-lazy','is-leap-year',
+        'isNaN','isnt','is-prime','is-relative','is-routine','is-setting',
+        'is-win','item','iterator','join','keep','kept','KERNELnames','key',
+        'keyof','keys','kill','kv','kxxv','l','lang','last','lastcall','later',
+        'lazy','lc','leading','level','like','line','lines','link','List',
+        'listen','live','lives-ok','local','lock','log','log10','lookup','lsb',
+        'made','MAIN','make','Map','match','max','maxpairs','merge','message',
+        'method','method_table','methods','migrate','min','minmax','minpairs',
+        'minute','misplaced','Mix','MixHash','mkdir','mode','modified','month',
+        'move','mro','msb','multi','multiness','my','name','named','named_names',
+        'narrow','nativecast','native-descriptor','nativesizeof','new','new_type',
+        'new-from-daycount','new-from-pairs','next','nextcallee','next-handle',
+        'nextsame','nextwith','NFC','NFD','NFKC','NFKD','nl-in','nl-out',
+        'nodemap','nok','none','norm','not','note','now','nude','Num',
+        'numerator','Numeric','of','offset','offset-in-hours','offset-in-minutes',
+        'ok','old','on-close','one','on-switch','open','opened','operation',
+        'optional','ord','ords','orig','os-error','osname','out-buffer','pack',
+        'package','package-kind','package-name','packages','pair','pairs',
+        'pairup','parameter','params','parent','parent-name','parents','parse',
+        'parse-base','parsefile','parse-names','parts','pass','path','path-sep',
+        'payload','peer-host','peer-port','periods','perl','permutations','phaser',
+        'pick','pickpairs','pid','placeholder','plan','plus','polar','poll',
+        'polymod','pop','pos','positional','posix','postfix','postmatch',
+        'precomp-ext','precomp-target','pred','prefix','prematch','prepend',
+        'print','printf','print-nl','print-to','private','private_method_table',
+        'proc','produce','Promise','prompt','protect','pull-one','push',
+        'push-all','push-at-least','push-exactly','push-until-lazy','put',
+        'qualifier-type','quit','r','race','radix','rand','range','Rat','raw',
+        're','read','readchars','readonly','ready','Real','reallocate','reals',
+        'reason','rebless','receive','recv','redispatcher','redo','reduce',
+        'rel2abs','relative','release','rename','repeated','replacement',
+        'report','reserved','resolve','restore','result','resume','rethrow',
+        'reverse','right','rindex','rmdir','role','roles_to_compose','rolish',
+        'roll','rootdir','roots','rotate','rotor','round','roundrobin',
+        'routine-type','run','rwx','s','samecase','samemark','samewith','say',
+        'schedule-on','scheduler','scope','sec','sech','second','seek','self',
+        'send','Set','set_hidden','set_name','set_package','set_rw','set_value',
+        'SetHash','set-instruments','setup_finalization','shape','share','shell',
+        'shift','sibling','sigil','sign','signal','signals','signature','sin',
+        'sinh','sink','sink-all','skip','skip-at-least','skip-at-least-pull-one',
+        'skip-one','skip-rest','sleep','sleep-timer','sleep-until','Slip','slurp',
+        'slurp-rest','slurpy','snap','snapper','so','socket-host','socket-port',
+        'sort','source','source-package','spawn','SPEC','splice','split',
+        'splitdir','splitpath','sprintf','spurt','sqrt','squish','srand','stable',
+        'start','started','starts-with','status','stderr','stdout','Str',
+        'sub_signature','subbuf','subbuf-rw','subname','subparse','subst',
+        'subst-mutate','substr','substr-eq','substr-rw','subtest','succ','sum',
+        'Supply','symlink','t','tail','take','take-rw','tan','tanh','tap',
+        'target','target-name','tc','tclc','tell','then','throttle','throw',
+        'throws-like','timezone','tmpdir','to','today','todo','toggle','to-posix',
+        'total','trailing','trans','tree','trim','trim-leading','trim-trailing',
+        'truncate','truncated-to','trusts','try_acquire','trying','twigil','type',
+        'type_captures','typename','uc','udp','uncaught_handler','unimatch',
+        'uniname','uninames','uniparse','uniprop','uniprops','unique','unival',
+        'univals','unlike','unlink','unlock','unpack','unpolar','unshift',
+        'unwrap','updir','USAGE','use-ok','utc','val','value','values','VAR',
+        'variable','verbose-config','version','VMnames','volume','vow','w','wait',
+        'warn','watch','watch-path','week','weekday-of-month','week-number',
+        'week-year','WHAT','when','WHERE','WHEREFORE','WHICH','WHO',
+        'whole-second','WHY','wordcase','words','workaround','wrap','write',
+        'write-to','x','yada','year','yield','yyyy-mm-dd','z','zip','zip-latest',
+
+    )
+
+    PERL6_BUILTIN_CLASSES = (
+        #Booleans
+        'False','True',
+        #Classes
+        'Any','Array','Associative','AST','atomicint','Attribute','Backtrace',
+        'Backtrace::Frame','Bag','Baggy','BagHash','Blob','Block','Bool','Buf',
+        'Callable','CallFrame','Cancellation','Capture','CArray','Channel','Code',
+        'compiler','Complex','ComplexStr','Cool','CurrentThreadScheduler',
+        'Cursor','Date','Dateish','DateTime','Distro','Duration','Encoding',
+        'Exception','Failure','FatRat','Grammar','Hash','HyperWhatever','Instant',
+        'Int','int16','int32','int64','int8','IntStr','IO','IO::ArgFiles',
+        'IO::CatHandle','IO::Handle','IO::Notification','IO::Path',
+        'IO::Path::Cygwin','IO::Path::QNX','IO::Path::Unix','IO::Path::Win32',
+        'IO::Pipe','IO::Socket','IO::Socket::Async','IO::Socket::INET','IO::Spec',
+        'IO::Spec::Cygwin','IO::Spec::QNX','IO::Spec::Unix','IO::Spec::Win32',
+        'IO::Special','Iterable','Iterator','Junction','Kernel','Label','List',
+        'Lock','Lock::Async','long','longlong','Macro','Map','Match',
+        'Metamodel::AttributeContainer','Metamodel::C3MRO','Metamodel::ClassHOW',
+        'Metamodel::EnumHOW','Metamodel::Finalization','Metamodel::MethodContainer',
+        'Metamodel::MROBasedMethodDispatch','Metamodel::MultipleInheritance',
+        'Metamodel::Naming','Metamodel::Primitives','Metamodel::PrivateMethodContainer',
+        'Metamodel::RoleContainer','Metamodel::Trusting','Method','Mix','MixHash',
+        'Mixy','Mu','NFC','NFD','NFKC','NFKD','Nil','Num','num32','num64',
+        'Numeric','NumStr','ObjAt','Order','Pair','Parameter','Perl','Pod::Block',
+        'Pod::Block::Code','Pod::Block::Comment','Pod::Block::Declarator',
+        'Pod::Block::Named','Pod::Block::Para','Pod::Block::Table','Pod::Heading',
+        'Pod::Item','Pointer','Positional','PositionalBindFailover','Proc',
+        'Proc::Async','Promise','Proxy','PseudoStash','QuantHash','Range','Rat',
+        'Rational','RatStr','Real','Regex','Routine','Scalar','Scheduler',
+        'Semaphore','Seq','Set','SetHash','Setty','Signature','size_t','Slip',
+        'Stash','Str','StrDistance','Stringy','Sub','Submethod','Supplier',
+        'Supplier::Preserving','Supply','Systemic','Tap','Telemetry',
+        'Telemetry::Instrument::Thread','Telemetry::Instrument::Usage',
+        'Telemetry::Period','Telemetry::Sampler','Thread','ThreadPoolScheduler',
+        'UInt','uint16','uint32','uint64','uint8','Uni','utf8','Variable',
+        'Version','VM','Whatever','WhateverCode','WrapHandle'
+    )
+
+    PERL6_OPERATORS = (
+        'X', 'Z', 'after', 'also', 'and', 'andthen', 'before', 'cmp', 'div',
+        'eq', 'eqv', 'extra', 'ff', 'fff', 'ge', 'gt', 'le', 'leg', 'lt', 'm',
+        'mm', 'mod', 'ne', 'or', 'orelse', 'rx', 's', 'tr', 'x', 'xor', 'xx',
+        '++', '--', '**', '!', '+', '-', '~', '?', '|', '||', '+^', '~^', '?^',
+        '^', '*', '/', '%', '%%', '+&', '+<', '+>', '~&', '~<', '~>', '?&',
+        'gcd', 'lcm', '+', '-', '+|', '+^', '~|', '~^', '?|', '?^',
+        '~', '&', '^', 'but', 'does', '<=>', '..', '..^', '^..', '^..^',
+        '!=', '==', '<', '<=', '>', '>=', '~~', '===', '!eqv',
+        '&&', '||', '^^', '//', 'min', 'max', '??', '!!', 'ff', 'fff', 'so',
+        'not', '<==', '==>', '<<==', '==>>','unicmp',
+    )
+
+    # Perl 6 has a *lot* of possible bracketing characters
+    # this list was lifted from STD.pm6 (https://github.com/perl6/std)
+    PERL6_BRACKETS = {
+        '\u0028': '\u0029', '\u003c': '\u003e', '\u005b': '\u005d',
+        '\u007b': '\u007d', '\u00ab': '\u00bb', '\u0f3a': '\u0f3b',
+        '\u0f3c': '\u0f3d', '\u169b': '\u169c', '\u2018': '\u2019',
+        '\u201a': '\u2019', '\u201b': '\u2019', '\u201c': '\u201d',
+        '\u201e': '\u201d', '\u201f': '\u201d', '\u2039': '\u203a',
+        '\u2045': '\u2046', '\u207d': '\u207e', '\u208d': '\u208e',
+        '\u2208': '\u220b', '\u2209': '\u220c', '\u220a': '\u220d',
+        '\u2215': '\u29f5', '\u223c': '\u223d', '\u2243': '\u22cd',
+        '\u2252': '\u2253', '\u2254': '\u2255', '\u2264': '\u2265',
+        '\u2266': '\u2267', '\u2268': '\u2269', '\u226a': '\u226b',
+        '\u226e': '\u226f', '\u2270': '\u2271', '\u2272': '\u2273',
+        '\u2274': '\u2275', '\u2276': '\u2277', '\u2278': '\u2279',
+        '\u227a': '\u227b', '\u227c': '\u227d', '\u227e': '\u227f',
+        '\u2280': '\u2281', '\u2282': '\u2283', '\u2284': '\u2285',
+        '\u2286': '\u2287', '\u2288': '\u2289', '\u228a': '\u228b',
+        '\u228f': '\u2290', '\u2291': '\u2292', '\u2298': '\u29b8',
+        '\u22a2': '\u22a3', '\u22a6': '\u2ade', '\u22a8': '\u2ae4',
+        '\u22a9': '\u2ae3', '\u22ab': '\u2ae5', '\u22b0': '\u22b1',
+        '\u22b2': '\u22b3', '\u22b4': '\u22b5', '\u22b6': '\u22b7',
+        '\u22c9': '\u22ca', '\u22cb': '\u22cc', '\u22d0': '\u22d1',
+        '\u22d6': '\u22d7', '\u22d8': '\u22d9', '\u22da': '\u22db',
+        '\u22dc': '\u22dd', '\u22de': '\u22df', '\u22e0': '\u22e1',
+        '\u22e2': '\u22e3', '\u22e4': '\u22e5', '\u22e6': '\u22e7',
+        '\u22e8': '\u22e9', '\u22ea': '\u22eb', '\u22ec': '\u22ed',
+        '\u22f0': '\u22f1', '\u22f2': '\u22fa', '\u22f3': '\u22fb',
+        '\u22f4': '\u22fc', '\u22f6': '\u22fd', '\u22f7': '\u22fe',
+        '\u2308': '\u2309', '\u230a': '\u230b', '\u2329': '\u232a',
+        '\u23b4': '\u23b5', '\u2768': '\u2769', '\u276a': '\u276b',
+        '\u276c': '\u276d', '\u276e': '\u276f', '\u2770': '\u2771',
+        '\u2772': '\u2773', '\u2774': '\u2775', '\u27c3': '\u27c4',
+        '\u27c5': '\u27c6', '\u27d5': '\u27d6', '\u27dd': '\u27de',
+        '\u27e2': '\u27e3', '\u27e4': '\u27e5', '\u27e6': '\u27e7',
+        '\u27e8': '\u27e9', '\u27ea': '\u27eb', '\u2983': '\u2984',
+        '\u2985': '\u2986', '\u2987': '\u2988', '\u2989': '\u298a',
+        '\u298b': '\u298c', '\u298d': '\u298e', '\u298f': '\u2990',
+        '\u2991': '\u2992', '\u2993': '\u2994', '\u2995': '\u2996',
+        '\u2997': '\u2998', '\u29c0': '\u29c1', '\u29c4': '\u29c5',
+        '\u29cf': '\u29d0', '\u29d1': '\u29d2', '\u29d4': '\u29d5',
+        '\u29d8': '\u29d9', '\u29da': '\u29db', '\u29f8': '\u29f9',
+        '\u29fc': '\u29fd', '\u2a2b': '\u2a2c', '\u2a2d': '\u2a2e',
+        '\u2a34': '\u2a35', '\u2a3c': '\u2a3d', '\u2a64': '\u2a65',
+        '\u2a79': '\u2a7a', '\u2a7d': '\u2a7e', '\u2a7f': '\u2a80',
+        '\u2a81': '\u2a82', '\u2a83': '\u2a84', '\u2a8b': '\u2a8c',
+        '\u2a91': '\u2a92', '\u2a93': '\u2a94', '\u2a95': '\u2a96',
+        '\u2a97': '\u2a98', '\u2a99': '\u2a9a', '\u2a9b': '\u2a9c',
+        '\u2aa1': '\u2aa2', '\u2aa6': '\u2aa7', '\u2aa8': '\u2aa9',
+        '\u2aaa': '\u2aab', '\u2aac': '\u2aad', '\u2aaf': '\u2ab0',
+        '\u2ab3': '\u2ab4', '\u2abb': '\u2abc', '\u2abd': '\u2abe',
+        '\u2abf': '\u2ac0', '\u2ac1': '\u2ac2', '\u2ac3': '\u2ac4',
+        '\u2ac5': '\u2ac6', '\u2acd': '\u2ace', '\u2acf': '\u2ad0',
+        '\u2ad1': '\u2ad2', '\u2ad3': '\u2ad4', '\u2ad5': '\u2ad6',
+        '\u2aec': '\u2aed', '\u2af7': '\u2af8', '\u2af9': '\u2afa',
+        '\u2e02': '\u2e03', '\u2e04': '\u2e05', '\u2e09': '\u2e0a',
+        '\u2e0c': '\u2e0d', '\u2e1c': '\u2e1d', '\u2e20': '\u2e21',
+        '\u3008': '\u3009', '\u300a': '\u300b', '\u300c': '\u300d',
+        '\u300e': '\u300f', '\u3010': '\u3011', '\u3014': '\u3015',
+        '\u3016': '\u3017', '\u3018': '\u3019', '\u301a': '\u301b',
+        '\u301d': '\u301e', '\ufd3e': '\ufd3f', '\ufe17': '\ufe18',
+        '\ufe35': '\ufe36', '\ufe37': '\ufe38', '\ufe39': '\ufe3a',
+        '\ufe3b': '\ufe3c', '\ufe3d': '\ufe3e', '\ufe3f': '\ufe40',
+        '\ufe41': '\ufe42', '\ufe43': '\ufe44', '\ufe47': '\ufe48',
+        '\ufe59': '\ufe5a', '\ufe5b': '\ufe5c', '\ufe5d': '\ufe5e',
+        '\uff08': '\uff09', '\uff1c': '\uff1e', '\uff3b': '\uff3d',
+        '\uff5b': '\uff5d', '\uff5f': '\uff60', '\uff62': '\uff63',
+    }
+
+    def _build_word_match(words, boundary_regex_fragment=None, prefix='', suffix=''):
+        if boundary_regex_fragment is None:
+            return r'\b(' + prefix + r'|'.join(re.escape(x) for x in words) + \
+                suffix + r')\b'
+        else:
+            return r'(? 0:
+                    next_open_pos = text.find(opening_chars, search_pos + n_chars)
+                    next_close_pos = text.find(closing_chars, search_pos + n_chars)
+
+                    if next_close_pos == -1:
+                        next_close_pos = len(text)
+                        nesting_level = 0
+                    elif next_open_pos != -1 and next_open_pos < next_close_pos:
+                        nesting_level += 1
+                        search_pos = next_open_pos
+                    else:  # next_close_pos < next_open_pos
+                        nesting_level -= 1
+                        search_pos = next_close_pos
+
+                end_pos = next_close_pos
+
+            if end_pos < 0:     # if we didn't find a closer, just highlight the
+                                # rest of the text in this class
+                end_pos = len(text)
+
+            if adverbs is not None and re.search(r':to\b', adverbs):
+                heredoc_terminator = text[match.start('delimiter') + n_chars:end_pos]
+                end_heredoc = re.search(r'^\s*' + re.escape(heredoc_terminator) +
+                                        r'\s*$', text[end_pos:], re.MULTILINE)
+
+                if end_heredoc:
+                    end_pos += end_heredoc.end()
+                else:
+                    end_pos = len(text)
+
+            yield match.start(), token_class, text[match.start():end_pos + n_chars]
+            context.pos = end_pos + n_chars
+
+        return callback
+
+    def opening_brace_callback(lexer, match, context):
+        stack = context.stack
+
+        yield match.start(), Text, context.text[match.start():match.end()]
+        context.pos = match.end()
+
+        # if we encounter an opening brace and we're one level
+        # below a token state, it means we need to increment
+        # the nesting level for braces so we know later when
+        # we should return to the token rules.
+        if len(stack) > 2 and stack[-2] == 'token':
+            context.perl6_token_nesting_level += 1
+
+    def closing_brace_callback(lexer, match, context):
+        stack = context.stack
+
+        yield match.start(), Text, context.text[match.start():match.end()]
+        context.pos = match.end()
+
+        # if we encounter a free closing brace and we're one level
+        # below a token state, it means we need to check the nesting
+        # level to see if we need to return to the token state.
+        if len(stack) > 2 and stack[-2] == 'token':
+            context.perl6_token_nesting_level -= 1
+            if context.perl6_token_nesting_level == 0:
+                stack.pop()
+
+    def embedded_perl6_callback(lexer, match, context):
+        context.perl6_token_nesting_level = 1
+        yield match.start(), Text, context.text[match.start():match.end()]
+        context.pos = match.end()
+        context.stack.append('root')
+
+    # If you're modifying these rules, be careful if you need to process '{' or '}'
+    # characters. We have special logic for processing these characters (due to the fact
+    # that you can nest Perl 6 code in regex blocks), so if you need to process one of
+    # them, make sure you also process the corresponding one!
+    tokens = {
+        'common': [
+            (r'#[`|=](?P(?P[' + ''.join(PERL6_BRACKETS) + r'])(?P=first_char)*)',
+             brackets_callback(Comment.Multiline)),
+            (r'#[^\n]*$', Comment.Single),
+            (r'^(\s*)=begin\s+(\w+)\b.*?^\1=end\s+\2', Comment.Multiline),
+            (r'^(\s*)=for.*?\n\s*?\n', Comment.Multiline),
+            (r'^=.*?\n\s*?\n', Comment.Multiline),
+            (r'(regex|token|rule)(\s*' + PERL6_IDENTIFIER_RANGE + '+:sym)',
+             bygroups(Keyword, Name), 'token-sym-brackets'),
+            (r'(regex|token|rule)(?!' + PERL6_IDENTIFIER_RANGE + r')(\s*' + PERL6_IDENTIFIER_RANGE + '+)?',
+             bygroups(Keyword, Name), 'pre-token'),
+            # deal with a special case in the Perl 6 grammar (role q { ... })
+            (r'(role)(\s+)(q)(\s*)', bygroups(Keyword, Whitespace, Name, Whitespace)),
+            (_build_word_match(PERL6_KEYWORDS, PERL6_IDENTIFIER_RANGE), Keyword),
+            (_build_word_match(PERL6_BUILTIN_CLASSES, PERL6_IDENTIFIER_RANGE, suffix='(?::[UD])?'),
+             Name.Builtin),
+            (_build_word_match(PERL6_BUILTINS, PERL6_IDENTIFIER_RANGE), Name.Builtin),
+            # copied from PerlLexer
+            (r'[$@%&][.^:?=!~]?' + PERL6_IDENTIFIER_RANGE + '+(?:<<.*?>>|<.*?>|«.*?»)*',
+             Name.Variable),
+            (r'\$[!/](?:<<.*?>>|<.*?>|«.*?»)*', Name.Variable.Global),
+            (r'::\?\w+', Name.Variable.Global),
+            (r'[$@%&]\*' + PERL6_IDENTIFIER_RANGE + '+(?:<<.*?>>|<.*?>|«.*?»)*',
+             Name.Variable.Global),
+            (r'\$(?:<.*?>)+', Name.Variable),
+            (r'(?:q|qq|Q)[a-zA-Z]?\s*(?P:[\w\s:]+)?\s*(?P(?P[^0-9a-zA-Z:\s])'
+             r'(?P=first_char)*)', brackets_callback(String)),
+            # copied from PerlLexer
+            (r'0_?[0-7]+(_[0-7]+)*', Number.Oct),
+            (r'0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*', Number.Hex),
+            (r'0b[01]+(_[01]+)*', Number.Bin),
+            (r'(?i)(\d*(_\d*)*\.\d+(_\d*)*|\d+(_\d*)*\.\d+(_\d*)*)(e[+-]?\d+)?',
+             Number.Float),
+            (r'(?i)\d+(_\d*)*e[+-]?\d+(_\d*)*', Number.Float),
+            (r'\d+(_\d+)*', Number.Integer),
+            (r'(?<=~~)\s*/(?:\\\\|\\/|.)*?/', String.Regex),
+            (r'(?<=[=(,])\s*/(?:\\\\|\\/|.)*?/', String.Regex),
+            (r'm\w+(?=\()', Name),
+            (r'(?:m|ms|rx)\s*(?P:[\w\s:]+)?\s*(?P(?P[^\w:\s])'
+             r'(?P=first_char)*)', brackets_callback(String.Regex)),
+            (r'(?:s|ss|tr)\s*(?::[\w\s:]+)?\s*/(?:\\\\|\\/|.)*?/(?:\\\\|\\/|.)*?/',
+             String.Regex),
+            (r'<[^\s=].*?\S>', String),
+            (_build_word_match(PERL6_OPERATORS), Operator),
+            (r'\w' + PERL6_IDENTIFIER_RANGE + '*', Name),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+        ],
+        'root': [
+            include('common'),
+            (r'\{', opening_brace_callback),
+            (r'\}', closing_brace_callback),
+            (r'.+?', Text),
+        ],
+        'pre-token': [
+            include('common'),
+            (r'\{', Text, ('#pop', 'token')),
+            (r'.+?', Text),
+        ],
+        'token-sym-brackets': [
+            (r'(?P(?P[' + ''.join(PERL6_BRACKETS) + '])(?P=first_char)*)',
+             brackets_callback(Name), ('#pop', 'pre-token')),
+            default(('#pop', 'pre-token')),
+        ],
+        'token': [
+            (r'\}', Text, '#pop'),
+            (r'(?<=:)(?:my|our|state|constant|temp|let).*?;', using(this)),
+            # make sure that quotes in character classes aren't treated as strings
+            (r'<(?:[-!?+.]\s*)?\[.*?\]>', String.Regex),
+            # make sure that '#' characters in quotes aren't treated as comments
+            (r"(?my|our)\s+)?(?:module|class|role|enum|grammar)', line)
+            if class_decl:
+                if saw_perl_decl or class_decl.group('scope') is not None:
+                    return True
+                rating = 0.05
+                continue
+            break
+
+        if ':=' in text:
+            # Same logic as above for PerlLexer
+            rating /= 2
+
+        return rating
+
+    def __init__(self, **options):
+        super().__init__(**options)
+        self.encoding = options.get('encoding', 'utf-8')
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/phix.py b/.venv/lib/python3.12/site-packages/pygments/lexers/phix.py
new file mode 100644
index 0000000..c4cb35d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/phix.py
@@ -0,0 +1,363 @@
+"""
+    pygments.lexers.phix
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Phix.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Whitespace
+
+__all__ = ['PhixLexer']
+
+
+class PhixLexer(RegexLexer):
+    """
+    Pygments Lexer for Phix files (.exw).
+    See http://phix.x10.mx
+    """
+
+    name = 'Phix'
+    url = 'http://phix.x10.mx'
+    aliases = ['phix']
+    filenames = ['*.exw']
+    mimetypes = ['text/x-phix']
+    version_added = '2.14'
+
+    flags = re.MULTILINE    # nb: **NOT** re.DOTALL! (totally spanners comment handling)
+
+    preproc = (
+        'ifdef', 'elsifdef', 'elsedef'
+    )
+    # Note these lists are auto-generated by pwa/p2js.exw, when pwa\src\p2js_keywords.e (etc)
+    #     change, though of course subsequent copy/commit/pull requests are all manual steps.
+    types = (
+        'string', 'nullable_string', 'atom_string', 'atom', 'bool', 'boolean',
+        'cdCanvan', 'cdCanvas', 'complex', 'CURLcode', 'dictionary', 'int',
+        'integer', 'Ihandle', 'Ihandles', 'Ihandln', 'mpfr', 'mpq', 'mpz',
+        'mpz_or_string', 'number', 'rid_string', 'seq', 'sequence', 'timedate',
+        'object'
+    )
+    keywords = (
+        'abstract', 'class', 'continue', 'export', 'extends', 'nullable',
+        'private', 'public', 'static', 'struct', 'trace',
+        'and', 'break', 'by', 'case', 'catch', 'const', 'constant', 'debug',
+        'default', 'do', 'else', 'elsif', 'end', 'enum', 'exit', 'fallthru',
+        'fallthrough', 'for', 'forward', 'function', 'global', 'if', 'in',
+        'include', 'js', 'javascript', 'javascript_semantics', 'let', 'not',
+        'or', 'procedure', 'profile', 'profile_time', 'return', 'safe_mode',
+        'switch', 'then', 'to', 'try', 'type', 'type_check', 'until', 'warning',
+        'while', 'with', 'without', 'xor'
+    )
+    routines = (
+        'abort', 'abs', 'adjust_timedate', 'and_bits', 'and_bitsu', 'apply',
+        'append', 'arccos', 'arcsin', 'arctan', 'assert', 'atan2',
+        'atom_to_float32', 'atom_to_float64', 'bankers_rounding', 'beep',
+        'begins', 'binary_search', 'bits_to_int', 'bk_color', 'bytes_to_int',
+        'call_func', 'call_proc', 'cdCanvasActivate', 'cdCanvasArc',
+        'cdCanvasBegin', 'cdCanvasBox', 'cdCanvasChord', 'cdCanvasCircle',
+        'cdCanvasClear', 'cdCanvasEnd', 'cdCanvasFlush', 'cdCanvasFont',
+        'cdCanvasGetImageRGB', 'cdCanvasGetSize', 'cdCanvasGetTextAlignment',
+        'cdCanvasGetTextSize', 'cdCanvasLine', 'cdCanvasMark',
+        'cdCanvasMarkSize', 'cdCanvasMultiLineVectorText', 'cdCanvasPixel',
+        'cdCanvasRect', 'cdCanvasRoundedBox', 'cdCanvasRoundedRect',
+        'cdCanvasSector', 'cdCanvasSetAttribute', 'cdCanvasSetBackground',
+        'cdCanvasSetFillMode', 'cdCanvasSetForeground',
+        'cdCanvasSetInteriorStyle', 'cdCanvasSetLineStyle',
+        'cdCanvasSetLineWidth', 'cdCanvasSetTextAlignment', 'cdCanvasText',
+        'cdCanvasSetTextOrientation', 'cdCanvasGetTextOrientation',
+        'cdCanvasVectorText', 'cdCanvasVectorTextDirection',
+        'cdCanvasVectorTextSize', 'cdCanvasVertex', 'cdCreateCanvas',
+        'cdDecodeAlpha', 'cdDecodeColor', 'cdDecodeColorAlpha', 'cdEncodeAlpha',
+        'cdEncodeColor', 'cdEncodeColorAlpha', 'cdKillCanvas', 'cdVersion',
+        'cdVersionDate', 'ceil', 'change_timezone', 'choose', 'clear_screen',
+        'columnize', 'command_line', 'compare', 'complex_abs', 'complex_add',
+        'complex_arg', 'complex_conjugate', 'complex_cos', 'complex_cosh',
+        'complex_div', 'complex_exp', 'complex_imag', 'complex_inv',
+        'complex_log', 'complex_mul', 'complex_neg', 'complex_new',
+        'complex_norm', 'complex_power', 'complex_rho', 'complex_real',
+        'complex_round', 'complex_sin', 'complex_sinh', 'complex_sprint',
+        'complex_sqrt', 'complex_sub', 'complex_theta', 'concat', 'cos',
+        'crash', 'custom_sort', 'date', 'day_of_week', 'day_of_year',
+        'days_in_month', 'decode_base64', 'decode_flags', 'deep_copy', 'deld',
+        'deserialize', 'destroy_dict', 'destroy_queue', 'destroy_stack',
+        'dict_name', 'dict_size', 'elapsed', 'elapsed_short', 'encode_base64',
+        'equal', 'even', 'exp', 'extract', 'factorial', 'factors',
+        'file_size_k', 'find', 'find_all', 'find_any', 'find_replace', 'filter',
+        'flatten', 'float32_to_atom', 'float64_to_atom', 'floor',
+        'format_timedate', 'free_console', 'from_polar', 'gcd', 'get_file_base',
+        'get_file_extension', 'get_file_name', 'get_file_name_and_path',
+        'get_file_path', 'get_file_path_and_name', 'get_maxprime', 'get_prime',
+        'get_primes', 'get_primes_le', 'get_proper_dir', 'get_proper_path',
+        'get_rand', 'get_routine_info', 'get_test_abort', 'get_test_logfile',
+        'get_test_pause', 'get_test_verbosity', 'get_tzid', 'getd', 'getdd',
+        'getd_all_keys', 'getd_by_index', 'getd_index', 'getd_partial_key',
+        'glAttachShader', 'glBindBuffer', 'glBindTexture', 'glBufferData',
+        'glCanvasSpecialText', 'glClear', 'glClearColor', 'glColor',
+        'glCompileShader', 'glCreateBuffer', 'glCreateProgram',
+        'glCreateShader', 'glCreateTexture', 'glDeleteProgram',
+        'glDeleteShader', 'glDrawArrays', 'glEnable',
+        'glEnableVertexAttribArray', 'glFloat32Array', 'glInt32Array',
+        'glFlush', 'glGetAttribLocation', 'glGetError', 'glGetProgramInfoLog',
+        'glGetProgramParameter', 'glGetShaderInfoLog', 'glGetShaderParameter',
+        'glGetUniformLocation', 'glLinkProgram', 'glLoadIdentity',
+        'glMatrixMode', 'glOrtho', 'glRotatef', 'glShadeModel',
+        'glShaderSource', 'glSimpleA7texcoords', 'glTexImage2Dc',
+        'glTexParameteri', 'glTranslate', 'glUniform1f', 'glUniform1i',
+        'glUniformMatrix4fv', 'glUseProgram', 'glVertex',
+        'glVertexAttribPointer', 'glViewport', 'head', 'hsv_to_rgb', 'iff',
+        'iif', 'include_file', 'incl0de_file', 'insert', 'instance',
+        'int_to_bits', 'int_to_bytes', 'is_dict', 'is_integer', 's_leap_year',
+        'is_prime', 'is_prime2', 'islower', 'isupper', 'Icallback',
+        'iup_isdouble', 'iup_isprint', 'iup_XkeyBase', 'IupAppend', 'IupAlarm',
+        'IupBackgroundBox', 'IupButton', 'IupCalendar', 'IupCanvas',
+        'IupClipboard', 'IupClose', 'IupCloseOnEscape', 'IupControlsOpen',
+        'IupDatePick', 'IupDestroy', 'IupDialog', 'IupDrawArc', 'IupDrawBegin',
+        'IupDrawEnd', 'IupDrawGetSize', 'IupDrawGetTextSize', 'IupDrawLine',
+        'IupDrawRectangle', 'IupDrawText', 'IupExpander', 'IupFill',
+        'IupFlatLabel', 'IupFlatList', 'IupFlatTree', 'IupFlush', 'IupFrame',
+        'IupGetAttribute', 'IupGetAttributeId', 'IupGetAttributePtr',
+        'IupGetBrother', 'IupGetChild', 'IupGetChildCount', 'IupGetClassName',
+        'IupGetDialog', 'IupGetDialogChild', 'IupGetDouble', 'IupGetFocus',
+        'IupGetGlobal', 'IupGetGlobalInt', 'IupGetGlobalIntInt', 'IupGetInt',
+        'IupGetInt2', 'IupGetIntId', 'IupGetIntInt', 'IupGetParent',
+        'IupGLCanvas', 'IupGLCanvasOpen', 'IupGLMakeCurrent', 'IupGraph',
+        'IupHbox', 'IupHide', 'IupImage', 'IupImageRGBA', 'IupItem',
+        'iupKeyCodeToName', 'IupLabel', 'IupLink', 'IupList', 'IupMap',
+        'IupMenu', 'IupMenuItem', 'IupMessage', 'IupMessageDlg', 'IupMultiBox',
+        'IupMultiLine', 'IupNextField', 'IupNormaliser', 'IupOpen',
+        'IupPlayInput', 'IupPopup', 'IupPreviousField', 'IupProgressBar',
+        'IupRadio', 'IupRecordInput', 'IupRedraw', 'IupRefresh',
+        'IupRefreshChildren', 'IupSeparator', 'IupSetAttribute',
+        'IupSetAttributes', 'IupSetAttributeHandle', 'IupSetAttributeId',
+        'IupSetAttributePtr', 'IupSetCallback', 'IupSetCallbacks',
+        'IupSetDouble', 'IupSetFocus', 'IupSetGlobal', 'IupSetGlobalInt',
+        'IupSetGlobalFunction', 'IupSetHandle', 'IupSetInt',
+        'IupSetStrAttribute', 'IupSetStrGlobal', 'IupShow', 'IupShowXY',
+        'IupSplit', 'IupStoreAttribute', 'IupSubmenu', 'IupTable',
+        'IupTableClearSelected', 'IupTableClick_cb', 'IupTableGetSelected',
+        'IupTableResize_cb', 'IupTableSetData', 'IupTabs', 'IupText',
+        'IupTimer', 'IupToggle', 'IupTreeAddNodes', 'IupTreeView', 'IupUpdate',
+        'IupValuator', 'IupVbox', 'join', 'join_by', 'join_path', 'k_perm',
+        'largest', 'lcm', 'length', 'log', 'log10', 'log2', 'lower',
+        'm4_crossProduct', 'm4_inverse', 'm4_lookAt', 'm4_multiply',
+        'm4_normalize', 'm4_perspective', 'm4_subtractVectors', 'm4_xRotate',
+        'm4_yRotate', 'machine_bits', 'machine_word', 'match', 'match_all',
+        'match_replace', 'max', 'maxsq', 'min', 'minsq', 'mod', 'mpfr_add',
+        'mpfr_ceil', 'mpfr_cmp', 'mpfr_cmp_si', 'mpfr_const_pi', 'mpfr_div',
+        'mpfr_div_si', 'mpfr_div_z', 'mpfr_floor', 'mpfr_free', 'mpfr_get_d',
+        'mpfr_get_default_precision', 'mpfr_get_default_rounding_mode',
+        'mpfr_get_fixed', 'mpfr_get_precision', 'mpfr_get_si', 'mpfr_init',
+        'mpfr_inits', 'mpfr_init_set', 'mpfr_init_set_q', 'mpfr_init_set_z',
+        'mpfr_mul', 'mpfr_mul_si', 'mpfr_pow_si', 'mpfr_set', 'mpfr_set_d',
+        'mpfr_set_default_precision', 'mpfr_set_default_rounding_mode',
+        'mpfr_set_precision', 'mpfr_set_q', 'mpfr_set_si', 'mpfr_set_str',
+        'mpfr_set_z', 'mpfr_si_div', 'mpfr_si_sub', 'mpfr_sqrt', 'mpfr_sub',
+        'mpfr_sub_si', 'mpq_abs', 'mpq_add', 'mpq_add_si', 'mpq_canonicalize',
+        'mpq_cmp', 'mpq_cmp_si', 'mpq_div', 'mpq_div_2exp', 'mpq_free',
+        'mpq_get_den', 'mpq_get_num', 'mpq_get_str', 'mpq_init', 'mpq_init_set',
+        'mpq_init_set_si', 'mpq_init_set_str', 'mpq_init_set_z', 'mpq_inits',
+        'mpq_inv', 'mpq_mul', 'mpq_neg', 'mpq_set', 'mpq_set_si', 'mpq_set_str',
+        'mpq_set_z', 'mpq_sub', 'mpz_abs', 'mpz_add', 'mpz_addmul',
+        'mpz_addmul_ui', 'mpz_addmul_si', 'mpz_add_si', 'mpz_add_ui', 'mpz_and',
+        'mpz_bin_uiui', 'mpz_cdiv_q', 'mpz_cmp', 'mpz_cmp_si', 'mpz_divexact',
+        'mpz_divexact_ui', 'mpz_divisible_p', 'mpz_divisible_ui_p', 'mpz_even',
+        'mpz_fac_ui', 'mpz_factorstring', 'mpz_fdiv_q', 'mpz_fdiv_q_2exp',
+        'mpz_fdiv_q_ui', 'mpz_fdiv_qr', 'mpz_fdiv_r', 'mpz_fdiv_ui',
+        'mpz_fib_ui', 'mpz_fib2_ui', 'mpz_fits_atom', 'mpz_fits_integer',
+        'mpz_free', 'mpz_gcd', 'mpz_gcd_ui', 'mpz_get_atom', 'mpz_get_integer',
+        'mpz_get_short_str', 'mpz_get_str', 'mpz_init', 'mpz_init_set',
+        'mpz_inits', 'mpz_invert', 'mpz_lcm', 'mpz_lcm_ui', 'mpz_max',
+        'mpz_min', 'mpz_mod', 'mpz_mod_ui', 'mpz_mul', 'mpz_mul_2exp',
+        'mpz_mul_d', 'mpz_mul_si', 'mpz_neg', 'mpz_nthroot', 'mpz_odd',
+        'mpz_pollard_rho', 'mpz_pow_ui', 'mpz_powm', 'mpz_powm_ui', 'mpz_prime',
+        'mpz_prime_factors', 'mpz_prime_mr', 'mpz_rand', 'mpz_rand_ui',
+        'mpz_re_compose', 'mpz_remove', 'mpz_scan0', 'mpz_scan1', 'mpz_set',
+        'mpz_set_d', 'mpz_set_si', 'mpz_set_str', 'mpz_set_v', 'mpz_sign',
+        'mpz_sizeinbase', 'mpz_sqrt', 'mpz_sub', 'mpz_sub_si', 'mpz_sub_ui',
+        'mpz_si_sub', 'mpz_tdiv_q_2exp', 'mpz_tdiv_r_2exp', 'mpz_tstbit',
+        'mpz_ui_pow_ui', 'mpz_xor', 'named_dict', 'new_dict', 'new_queue',
+        'new_stack', 'not_bits', 'not_bitsu', 'odd', 'or_all', 'or_allu',
+        'or_bits', 'or_bitsu', 'ord', 'ordinal', 'ordinant',
+        'override_timezone', 'pad', 'pad_head', 'pad_tail', 'parse_date_string',
+        'papply', 'peep', 'peepn', 'peep_dict', 'permute', 'permutes',
+        'platform', 'pop', 'popn', 'pop_dict', 'power', 'pp', 'ppEx', 'ppExf',
+        'ppf', 'ppOpt', 'pq_add', 'pq_destroy', 'pq_empty', 'pq_new', 'pq_peek',
+        'pq_pop', 'pq_pop_data', 'pq_size', 'prepend', 'prime_factors',
+        'printf', 'product', 'proper', 'push', 'pushn', 'putd', 'puts',
+        'queue_empty', 'queue_size', 'rand', 'rand_range', 'reinstate',
+        'remainder', 'remove', 'remove_all', 'repeat', 'repeatch', 'replace',
+        'requires', 'reverse', 'rfind', 'rgb', 'rmatch', 'rmdr', 'rnd', 'round',
+        'routine_id', 'scanf', 'serialize', 'series', 'set_rand',
+        'set_test_abort', 'set_test_logfile', 'set_test_module',
+        'set_test_pause', 'set_test_verbosity', 'set_timedate_formats',
+        'set_timezone', 'setd', 'setd_default', 'shorten', 'sha256',
+        'shift_bits', 'shuffle', 'sign', 'sin', 'smallest', 'sort',
+        'sort_columns', 'speak', 'splice', 'split', 'split_any', 'split_by',
+        'sprint', 'sprintf', 'sq_abs', 'sq_add', 'sq_and', 'sq_and_bits',
+        'sq_arccos', 'sq_arcsin', 'sq_arctan', 'sq_atom', 'sq_ceil', 'sq_cmp',
+        'sq_cos', 'sq_div', 'sq_even', 'sq_eq', 'sq_floor', 'sq_floor_div',
+        'sq_ge', 'sq_gt', 'sq_int', 'sq_le', 'sq_log', 'sq_log10', 'sq_log2',
+        'sq_lt', 'sq_max', 'sq_min', 'sq_mod', 'sq_mul', 'sq_ne', 'sq_not',
+        'sq_not_bits', 'sq_odd', 'sq_or', 'sq_or_bits', 'sq_power', 'sq_rand',
+        'sq_remainder', 'sq_rmdr', 'sq_rnd', 'sq_round', 'sq_seq', 'sq_sign',
+        'sq_sin', 'sq_sqrt', 'sq_str', 'sq_sub', 'sq_tan', 'sq_trunc',
+        'sq_uminus', 'sq_xor', 'sq_xor_bits', 'sqrt', 'square_free',
+        'stack_empty', 'stack_size', 'substitute', 'substitute_all', 'sum',
+        'tail', 'tan', 'test_equal', 'test_fail', 'test_false',
+        'test_not_equal', 'test_pass', 'test_summary', 'test_true',
+        'text_color', 'throw', 'time', 'timedate_diff', 'timedelta',
+        'to_integer', 'to_number', 'to_rgb', 'to_string', 'traverse_dict',
+        'traverse_dict_partial_key', 'trim', 'trim_head', 'trim_tail', 'trunc',
+        'tagset', 'tagstart', 'typeof', 'unique', 'unix_dict', 'upper',
+        'utf8_to_utf32', 'utf32_to_utf8', 'version', 'vlookup', 'vslice',
+        'wglGetProcAddress', 'wildcard_file', 'wildcard_match', 'with_rho',
+        'with_theta', 'xml_new_doc', 'xml_new_element', 'xml_set_attribute',
+        'xml_sprint', 'xor_bits', 'xor_bitsu',
+        'accept', 'allocate', 'allocate_string', 'allow_break', 'ARM',
+        'atom_to_float80', 'c_func', 'c_proc', 'call_back', 'chdir',
+        'check_break', 'clearDib', 'close', 'closesocket', 'console',
+        'copy_file', 'create', 'create_directory', 'create_thread',
+        'curl_easy_cleanup', 'curl_easy_get_file', 'curl_easy_init',
+        'curl_easy_perform', 'curl_easy_perform_ex', 'curl_easy_setopt',
+        'curl_easy_strerror', 'curl_global_cleanup', 'curl_global_init',
+        'curl_slist_append', 'curl_slist_free_all', 'current_dir', 'cursor',
+        'define_c_func', 'define_c_proc', 'delete', 'delete_cs', 'delete_file',
+        'dir', 'DLL', 'drawDib', 'drawShadedPolygonToDib', 'ELF32', 'ELF64',
+        'enter_cs', 'eval', 'exit_thread', 'free', 'file_exists', 'final',
+        'float80_to_atom', 'format', 'get_bytes', 'get_file_date',
+        'get_file_size', 'get_file_type', 'get_interpreter', 'get_key',
+        'get_socket_error', 'get_text', 'get_thread_exitcode', 'get_thread_id',
+        'getc', 'getenv', 'gets', 'getsockaddr', 'glBegin', 'glCallList',
+        'glFrustum', 'glGenLists', 'glGetString', 'glLight', 'glMaterial',
+        'glNewList', 'glNormal', 'glPopMatrix', 'glPushMatrix', 'glRotate',
+        'glEnd', 'glEndList', 'glTexImage2D', 'goto', 'GUI', 'icons', 'ilASM',
+        'include_files', 'include_paths', 'init_cs', 'ip_to_string',
+        'IupConfig', 'IupConfigDialogClosed', 'IupConfigDialogShow',
+        'IupConfigGetVariableInt', 'IupConfigLoad', 'IupConfigSave',
+        'IupConfigSetVariableInt', 'IupExitLoop', 'IupFileDlg', 'IupFileList',
+        'IupGLSwapBuffers', 'IupHelp', 'IupLoopStep', 'IupMainLoop',
+        'IupNormalizer', 'IupPlot', 'IupPlotAdd', 'IupPlotBegin', 'IupPlotEnd',
+        'IupPlotInsert', 'IupSaveImage', 'IupTreeGetUserId', 'IupUser',
+        'IupVersion', 'IupVersionDate', 'IupVersionNumber', 'IupVersionShow',
+        'killDib', 'leave_cs', 'listen', 'manifest', 'mem_copy', 'mem_set',
+        'mpfr_gamma', 'mpfr_printf', 'mpfr_sprintf', 'mpz_export', 'mpz_import',
+        'namespace', 'new', 'newDib', 'open', 'open_dll', 'PE32', 'PE64',
+        'peek', 'peek_string', 'peek1s', 'peek1u', 'peek2s', 'peek2u', 'peek4s',
+        'peek4u', 'peek8s', 'peek8u', 'peekNS', 'peekns', 'peeknu', 'poke',
+        'poke2', 'poke4', 'poke8', 'pokeN', 'poke_string', 'poke_wstring',
+        'position', 'progress', 'prompt_number', 'prompt_string', 'read_file',
+        'read_lines', 'recv', 'resume_thread', 'seek', 'select', 'send',
+        'setHandler', 'shutdown', 'sleep', 'SO', 'sockaddr_in', 'socket',
+        'split_path', 'suspend_thread', 'system', 'system_exec', 'system_open',
+        'system_wait', 'task_clock_start', 'task_clock_stop', 'task_create',
+        'task_delay', 'task_list', 'task_schedule', 'task_self', 'task_status',
+        'task_suspend', 'task_yield', 'thread_safe_string', 'try_cs',
+        'utf8_to_utf16', 'utf16_to_utf8', 'utf16_to_utf32', 'utf32_to_utf16',
+        'video_config', 'WSACleanup', 'wait_thread', 'walk_dir', 'where',
+        'write_lines', 'wait_key'
+    )
+    constants = (
+        'ANY_QUEUE', 'ASCENDING', 'BLACK', 'BLOCK_CURSOR', 'BLUE',
+        'BRIGHT_CYAN', 'BRIGHT_BLUE', 'BRIGHT_GREEN', 'BRIGHT_MAGENTA',
+        'BRIGHT_RED', 'BRIGHT_WHITE', 'BROWN', 'C_DWORD', 'C_INT', 'C_POINTER',
+        'C_USHORT', 'C_WORD', 'CD_AMBER', 'CD_BLACK', 'CD_BLUE', 'CD_BOLD',
+        'CD_BOLD_ITALIC', 'CD_BOX', 'CD_CENTER', 'CD_CIRCLE', 'CD_CLOSED_LINES',
+        'CD_CONTINUOUS', 'CD_CUSTOM', 'CD_CYAN', 'CD_DARK_BLUE', 'CD_DARK_CYAN',
+        'CD_DARK_GRAY', 'CD_DARK_GREY', 'CD_DARK_GREEN', 'CD_DARK_MAGENTA',
+        'CD_DARK_RED', 'CD_DARK_YELLOW', 'CD_DASH_DOT', 'CD_DASH_DOT_DOT',
+        'CD_DASHED', 'CD_DBUFFER', 'CD_DEG2RAD', 'CD_DIAMOND', 'CD_DOTTED',
+        'CD_EAST', 'CD_EVENODD', 'CD_FILL', 'CD_GL', 'CD_GRAY', 'CD_GREY',
+        'CD_GREEN', 'CD_HATCH', 'CD_HOLLOW', 'CD_HOLLOW_BOX',
+        'CD_HOLLOW_CIRCLE', 'CD_HOLLOW_DIAMOND', 'CD_INDIGO', 'CD_ITALIC',
+        'CD_IUP', 'CD_IUPDBUFFER', 'CD_LIGHT_BLUE', 'CD_LIGHT_GRAY',
+        'CD_LIGHT_GREY', 'CD_LIGHT_GREEN', 'CD_LIGHT_PARCHMENT', 'CD_MAGENTA',
+        'CD_NAVY', 'CD_NORTH', 'CD_NORTH_EAST', 'CD_NORTH_WEST', 'CD_OLIVE',
+        'CD_OPEN_LINES', 'CD_ORANGE', 'CD_PARCHMENT', 'CD_PATTERN',
+        'CD_PRINTER', 'CD_PURPLE', 'CD_PLAIN', 'CD_PLUS', 'CD_QUERY',
+        'CD_RAD2DEG', 'CD_RED', 'CD_SILVER', 'CD_SOLID', 'CD_SOUTH_EAST',
+        'CD_SOUTH_WEST', 'CD_STAR', 'CD_STIPPLE', 'CD_STRIKEOUT',
+        'CD_UNDERLINE', 'CD_WEST', 'CD_WHITE', 'CD_WINDING', 'CD_VIOLET',
+        'CD_X', 'CD_YELLOW', 'CURLE_OK', 'CURLOPT_MAIL_FROM',
+        'CURLOPT_MAIL_RCPT', 'CURLOPT_PASSWORD', 'CURLOPT_READDATA',
+        'CURLOPT_READFUNCTION', 'CURLOPT_SSL_VERIFYPEER',
+        'CURLOPT_SSL_VERIFYHOST', 'CURLOPT_UPLOAD', 'CURLOPT_URL',
+        'CURLOPT_USE_SSL', 'CURLOPT_USERNAME', 'CURLOPT_VERBOSE',
+        'CURLOPT_WRITEFUNCTION', 'CURLUSESSL_ALL', 'CYAN', 'D_NAME',
+        'D_ATTRIBUTES', 'D_SIZE', 'D_YEAR', 'D_MONTH', 'D_DAY', 'D_HOUR',
+        'D_MINUTE', 'D_SECOND', 'D_CREATION', 'D_LASTACCESS', 'D_MODIFICATION',
+        'DT_YEAR', 'DT_MONTH', 'DT_DAY', 'DT_HOUR', 'DT_MINUTE', 'DT_SECOND',
+        'DT_DOW', 'DT_MSEC', 'DT_DOY', 'DT_GMT', 'EULER', 'E_CODE', 'E_ADDR',
+        'E_LINE', 'E_RTN', 'E_NAME', 'E_FILE', 'E_PATH', 'E_USER', 'false',
+        'False', 'FALSE', 'FIFO_QUEUE', 'FILETYPE_DIRECTORY', 'FILETYPE_FILE',
+        'GET_EOF', 'GET_FAIL', 'GET_IGNORE', 'GET_SUCCESS',
+        'GL_AMBIENT_AND_DIFFUSE', 'GL_ARRAY_BUFFER', 'GL_CLAMP',
+        'GL_CLAMP_TO_BORDER', 'GL_CLAMP_TO_EDGE', 'GL_COLOR_BUFFER_BIT',
+        'GL_COMPILE', 'GL_COMPILE_STATUS', 'GL_CULL_FACE',
+        'GL_DEPTH_BUFFER_BIT', 'GL_DEPTH_TEST', 'GL_EXTENSIONS', 'GL_FLAT',
+        'GL_FLOAT', 'GL_FRAGMENT_SHADER', 'GL_FRONT', 'GL_LIGHT0',
+        'GL_LIGHTING', 'GL_LINEAR', 'GL_LINK_STATUS', 'GL_MODELVIEW',
+        'GL_NEAREST', 'GL_NO_ERROR', 'GL_NORMALIZE', 'GL_POSITION',
+        'GL_PROJECTION', 'GL_QUAD_STRIP', 'GL_QUADS', 'GL_RENDERER',
+        'GL_REPEAT', 'GL_RGB', 'GL_RGBA', 'GL_SMOOTH', 'GL_STATIC_DRAW',
+        'GL_TEXTURE_2D', 'GL_TEXTURE_MAG_FILTER', 'GL_TEXTURE_MIN_FILTER',
+        'GL_TEXTURE_WRAP_S', 'GL_TEXTURE_WRAP_T', 'GL_TRIANGLES',
+        'GL_UNSIGNED_BYTE', 'GL_VENDOR', 'GL_VERSION', 'GL_VERTEX_SHADER',
+        'GRAY', 'GREEN', 'GT_LF_STRIPPED', 'GT_WHOLE_FILE', 'INVLN10',
+        'IUP_CLOSE', 'IUP_CONTINUE', 'IUP_DEFAULT', 'IUP_BLACK', 'IUP_BLUE',
+        'IUP_BUTTON1', 'IUP_BUTTON3', 'IUP_CENTER', 'IUP_CYAN', 'IUP_DARK_BLUE',
+        'IUP_DARK_CYAN', 'IUP_DARK_GRAY', 'IUP_DARK_GREY', 'IUP_DARK_GREEN',
+        'IUP_DARK_MAGENTA', 'IUP_DARK_RED', 'IUP_GRAY', 'IUP_GREY', 'IUP_GREEN',
+        'IUP_IGNORE', 'IUP_INDIGO', 'IUP_MAGENTA', 'IUP_MASK_INT',
+        'IUP_MASK_UINT', 'IUP_MOUSEPOS', 'IUP_NAVY', 'IUP_OLIVE', 'IUP_RECTEXT',
+        'IUP_RED', 'IUP_LIGHT_BLUE', 'IUP_LIGHT_GRAY', 'IUP_LIGHT_GREY',
+        'IUP_LIGHT_GREEN', 'IUP_ORANGE', 'IUP_PARCHMENT', 'IUP_PURPLE',
+        'IUP_SILVER', 'IUP_TEAL', 'IUP_VIOLET', 'IUP_WHITE', 'IUP_YELLOW',
+        'K_BS', 'K_cA', 'K_cC', 'K_cD', 'K_cF5', 'K_cK', 'K_cM', 'K_cN', 'K_cO',
+        'K_cP', 'K_cR', 'K_cS', 'K_cT', 'K_cW', 'K_CR', 'K_DEL', 'K_DOWN',
+        'K_END', 'K_ESC', 'K_F1', 'K_F2', 'K_F3', 'K_F4', 'K_F5', 'K_F6',
+        'K_F7', 'K_F8', 'K_F9', 'K_F10', 'K_F11', 'K_F12', 'K_HOME', 'K_INS',
+        'K_LEFT', 'K_MIDDLE', 'K_PGDN', 'K_PGUP', 'K_RIGHT', 'K_SP', 'K_TAB',
+        'K_UP', 'K_h', 'K_i', 'K_j', 'K_p', 'K_r', 'K_s', 'JS', 'LIFO_QUEUE',
+        'LINUX', 'MAX_HEAP', 'MAGENTA', 'MIN_HEAP', 'Nan', 'NO_CURSOR', 'null',
+        'NULL', 'PI', 'pp_Ascii', 'pp_Brkt', 'pp_Date', 'pp_File', 'pp_FltFmt',
+        'pp_Indent', 'pp_IntCh', 'pp_IntFmt', 'pp_Maxlen', 'pp_Nest',
+        'pp_Pause', 'pp_Q22', 'pp_StrFmt', 'RED', 'SEEK_OK', 'SLASH',
+        'TEST_ABORT', 'TEST_CRASH', 'TEST_PAUSE', 'TEST_PAUSE_FAIL',
+        'TEST_QUIET', 'TEST_SHOW_ALL', 'TEST_SHOW_FAILED', 'TEST_SUMMARY',
+        'true', 'True', 'TRUE', 'VC_SCRNLINES', 'WHITE', 'WINDOWS', 'YELLOW'
+    )
+
+    tokens = {
+        'root': [
+            (r"\s+", Whitespace),
+            (r'/\*|--/\*|#\[', Comment.Multiline, 'comment'),
+            (r'(?://|--|#!).*$', Comment.Single),
+#Alt:
+#           (r'//.*$|--.*$|#!.*$', Comment.Single),
+            (r'"([^"\\]|\\.)*"', String.Other),
+            (r'\'[^\']*\'', String.Other),
+            (r'`[^`]*`', String.Other),
+
+            (words(types, prefix=r'\b', suffix=r'\b'), Name.Function),
+            (words(routines, prefix=r'\b', suffix=r'\b'), Name.Function),
+            (words(preproc, prefix=r'\b', suffix=r'\b'), Keyword.Declaration),
+            (words(keywords, prefix=r'\b', suffix=r'\b'), Keyword.Declaration),
+            (words(constants, prefix=r'\b', suffix=r'\b'), Name.Constant),
+            # Aside: Phix only supports/uses the ascii/non-unicode tilde
+            (r'!=|==|<<|>>|:=|[-~+/*%=<>&^|\.(){},?:\[\]$\\;#]', Operator),
+            (r'[\w-]+', Text)
+        ],
+        'comment': [
+            (r'[^*/#]+', Comment.Multiline),
+            (r'/\*|#\[', Comment.Multiline, '#push'),
+            (r'\*/|#\]', Comment.Multiline, '#pop'),
+            (r'[*/#]', Comment.Multiline)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/php.py b/.venv/lib/python3.12/site-packages/pygments/lexers/php.py
new file mode 100644
index 0000000..ac935c4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/php.py
@@ -0,0 +1,335 @@
+"""
+    pygments.lexers.php
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for PHP and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, include, bygroups, default, \
+    using, this, words, do_insertions, line_re
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Other, Generic
+from pygments.util import get_bool_opt, get_list_opt, shebang_matches
+
+__all__ = ['ZephirLexer', 'PsyshConsoleLexer', 'PhpLexer']
+
+
+class ZephirLexer(RegexLexer):
+    """
+    For Zephir language source code.
+
+    Zephir is a compiled high level language aimed
+    to the creation of C-extensions for PHP.
+    """
+
+    name = 'Zephir'
+    url = 'http://zephir-lang.com/'
+    aliases = ['zephir']
+    filenames = ['*.zep']
+    version_added = '2.0'
+
+    zephir_keywords = ['fetch', 'echo', 'isset', 'empty']
+    zephir_type = ['bit', 'bits', 'string']
+
+    flags = re.DOTALL | re.MULTILINE
+
+    tokens = {
+        'commentsandwhitespace': [
+            (r'\s+', Text),
+            (r'//.*?\n', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline)
+        ],
+        'slashstartsregex': [
+            include('commentsandwhitespace'),
+            (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
+             r'([gim]+\b|\B)', String.Regex, '#pop'),
+            (r'/', Operator, '#pop'),
+            default('#pop')
+        ],
+        'badregex': [
+            (r'\n', Text, '#pop')
+        ],
+        'root': [
+            (r'^(?=\s|/)', Text, 'slashstartsregex'),
+            include('commentsandwhitespace'),
+            (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|'
+             r'(<<|>>>?|==?|!=?|->|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'),
+            (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
+            (r'[})\].]', Punctuation),
+            (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|loop|'
+             r'require|inline|throw|try|catch|finally|new|delete|typeof|instanceof|void|'
+             r'namespace|use|extends|this|fetch|isset|unset|echo|fetch|likely|unlikely|'
+             r'empty)\b', Keyword, 'slashstartsregex'),
+            (r'(var|let|with|function)\b', Keyword.Declaration, 'slashstartsregex'),
+            (r'(abstract|boolean|bool|char|class|const|double|enum|export|extends|final|'
+             r'native|goto|implements|import|int|string|interface|long|ulong|char|uchar|'
+             r'float|unsigned|private|protected|public|short|static|self|throws|reverse|'
+             r'transient|volatile|readonly)\b', Keyword.Reserved),
+            (r'(true|false|null|undefined)\b', Keyword.Constant),
+            (r'(Array|Boolean|Date|_REQUEST|_COOKIE|_SESSION|'
+             r'_GET|_POST|_SERVER|this|stdClass|range|count|iterator|'
+             r'window)\b', Name.Builtin),
+            (r'[$a-zA-Z_][\w\\]*', Name.Other),
+            (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'[0-9]+', Number.Integer),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+        ]
+    }
+
+
+class PsyshConsoleLexer(Lexer):
+    """
+    For PsySH console output, such as:
+
+    .. sourcecode:: psysh
+
+        >>> $greeting = function($name): string {
+        ...     return "Hello, {$name}";
+        ... };
+        => Closure($name): string {#2371 …3}
+        >>> $greeting('World')
+        => "Hello, World"
+    """
+    name = 'PsySH console session for PHP'
+    url = 'https://psysh.org/'
+    aliases = ['psysh']
+    version_added = '2.7'
+
+    def __init__(self, **options):
+        options['startinline'] = True
+        Lexer.__init__(self, **options)
+
+    def get_tokens_unprocessed(self, text):
+        phplexer = PhpLexer(**self.options)
+        curcode = ''
+        insertions = []
+        for match in line_re.finditer(text):
+            line = match.group()
+            if line.startswith('>>> ') or line.startswith('... '):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:4])]))
+                curcode += line[4:]
+            elif line.rstrip() == '...':
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, '...')]))
+                curcode += line[3:]
+            else:
+                if curcode:
+                    yield from do_insertions(
+                        insertions, phplexer.get_tokens_unprocessed(curcode))
+                    curcode = ''
+                    insertions = []
+                yield match.start(), Generic.Output, line
+        if curcode:
+            yield from do_insertions(insertions,
+                                     phplexer.get_tokens_unprocessed(curcode))
+
+
+class PhpLexer(RegexLexer):
+    """
+    For PHP source code.
+    For PHP embedded in HTML, use the `HtmlPhpLexer`.
+
+    Additional options accepted:
+
+    `startinline`
+        If given and ``True`` the lexer starts highlighting with
+        php code (i.e.: no starting ``>> from pygments.lexers._php_builtins import MODULES
+            >>> MODULES.keys()
+            ['PHP Options/Info', 'Zip', 'dba', ...]
+
+        In fact the names of those modules match the module names from
+        the php documentation.
+    """
+
+    name = 'PHP'
+    url = 'https://www.php.net/'
+    aliases = ['php', 'php3', 'php4', 'php5']
+    filenames = ['*.php', '*.php[345]', '*.inc']
+    mimetypes = ['text/x-php']
+    version_added = ''
+
+    # Note that a backslash is included, PHP uses a backslash as a namespace
+    # separator.
+    _ident_inner = r'(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*'
+    # But not inside strings.
+    _ident_nons = r'(?:[_a-z]|[^\x00-\x7f])(?:\w|[^\x00-\x7f])*'
+
+    flags = re.IGNORECASE | re.DOTALL | re.MULTILINE
+    tokens = {
+        'root': [
+            (r'<\?(php)?', Comment.Preproc, 'php'),
+            (r'[^<]+', Other),
+            (r'<', Other)
+        ],
+        'php': [
+            (r'\?>', Comment.Preproc, '#pop'),
+            (r'(<<<)([\'"]?)(' + _ident_nons + r')(\2\n.*?\n\s*)(\3)(;?)(\n)',
+             bygroups(String, String, String.Delimiter, String, String.Delimiter,
+                      Punctuation, Text)),
+            (r'\s+', Text),
+            (r'#\[', Punctuation, 'attribute'),
+            (r'#.*?\n', Comment.Single),
+            (r'//.*?\n', Comment.Single),
+            # put the empty comment here, it is otherwise seen as
+            # the start of a docstring
+            (r'/\*\*/', Comment.Multiline),
+            (r'/\*\*.*?\*/', String.Doc),
+            (r'/\*.*?\*/', Comment.Multiline),
+            (r'(->|::)(\s*)(' + _ident_nons + ')',
+             bygroups(Operator, Text, Name.Attribute)),
+            (r'[~!%^&*+=|:.<>/@-]+', Operator),
+            (r'\?', Operator),  # don't add to the charclass above!
+            (r'[\[\]{}();,]+', Punctuation),
+            (r'(new)(\s+)(class)\b', bygroups(Keyword, Text, Keyword)),
+            (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+            (r'(function)(\s*)(?=\()', bygroups(Keyword, Text)),
+            (r'(function)(\s+)(&?)(\s*)',
+             bygroups(Keyword, Text, Operator, Text), 'functionname'),
+            (r'(const)(\s+)(' + _ident_inner + ')',
+             bygroups(Keyword, Text, Name.Constant)),
+            # source: https://www.php.net/manual/en/reserved.keywords.php
+            (r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|'
+             r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|'
+             r'FALSE|print|for|require|continue|foreach|require_once|'
+             r'declare|return|default|static|do|switch|die|stdClass|'
+             r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|'
+             r'virtual|endfor|include_once|while|endforeach|global|'
+             r'endif|list|endswitch|new|endwhile|not|'
+             r'array|E_ALL|NULL|final|php_user_filter|interface|'
+             r'implements|public|private|protected|abstract|clone|try|'
+             r'catch|throw|this|use|namespace|trait|yield( from)?|'
+             r'finally|match|readonly)\b', Keyword),
+            (r'(true|false|null)\b', Keyword.Constant),
+            include('magicconstants'),
+            (r'\$\{', Name.Variable, 'variablevariable'),
+            (r'\$+' + _ident_inner, Name.Variable),
+            (_ident_inner, Name.Other),
+            (r'(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?', Number.Float),
+            (r'\d+e[+-]?[0-9]+', Number.Float),
+            (r'0[0-7]+', Number.Oct),
+            (r'0x[a-f0-9]+', Number.Hex),
+            (r'\d+', Number.Integer),
+            (r'0b[01]+', Number.Bin),
+            (r"'([^'\\]*(?:\\.[^'\\]*)*)'", String.Single),
+            (r'`([^`\\]*(?:\\.[^`\\]*)*)`', String.Backtick),
+            (r'"', String.Double, 'string'),
+        ],
+        'variablevariable': [
+            (r'\}', Name.Variable, '#pop'),
+            include('php')
+        ],
+        'magicfuncs': [
+            # source: http://php.net/manual/en/language.oop5.magic.php
+            (words((
+                '__construct', '__destruct', '__call', '__callStatic', '__get', '__set',
+                '__isset', '__unset', '__sleep', '__wakeup', '__toString', '__invoke',
+                '__set_state', '__clone', '__debugInfo',), suffix=r'\b'),
+             Name.Function.Magic),
+        ],
+        'magicconstants': [
+            # source: https://www.php.net/manual/en/language.constants.magic.php
+            (words((
+                '__LINE__', '__FILE__', '__DIR__', '__FUNCTION__', '__CLASS__',
+                '__TRAIT__', '__METHOD__', '__NAMESPACE__', '__PROPERTY__',),
+                suffix=r'\b'),
+             Name.Constant),
+        ],
+        'classname': [
+            (_ident_inner, Name.Class, '#pop')
+        ],
+        'functionname': [
+            include('magicfuncs'),
+            (_ident_inner, Name.Function, '#pop'),
+            default('#pop')
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'[^{$"\\]+', String.Double),
+            (r'\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})', String.Escape),
+            (r'\$' + _ident_nons + r'(\[\S+?\]|->' + _ident_nons + ')?',
+             String.Interpol),
+            (r'(\{\$\{)(.*?)(\}\})',
+             bygroups(String.Interpol, using(this, _startinline=True),
+                      String.Interpol)),
+            (r'(\{)(\$.*?)(\})',
+             bygroups(String.Interpol, using(this, _startinline=True),
+                      String.Interpol)),
+            (r'(\$\{)(\S+)(\})',
+             bygroups(String.Interpol, Name.Variable, String.Interpol)),
+            (r'[${\\]', String.Double)
+        ],
+        'attribute': [
+            (r'\]', Punctuation, '#pop'),
+            (r'\(', Punctuation, 'attributeparams'),
+            (_ident_inner, Name.Decorator),
+            include('php')
+        ],
+        'attributeparams': [
+            (r'\)', Punctuation, '#pop'),
+            include('php')
+        ],
+    }
+
+    def __init__(self, **options):
+        self.funcnamehighlighting = get_bool_opt(
+            options, 'funcnamehighlighting', True)
+        self.disabledmodules = get_list_opt(
+            options, 'disabledmodules', ['unknown'])
+        self.startinline = get_bool_opt(options, 'startinline', False)
+
+        # private option argument for the lexer itself
+        if '_startinline' in options:
+            self.startinline = options.pop('_startinline')
+
+        # collect activated functions in a set
+        self._functions = set()
+        if self.funcnamehighlighting:
+            from pygments.lexers._php_builtins import MODULES
+            for key, value in MODULES.items():
+                if key not in self.disabledmodules:
+                    self._functions.update(value)
+        RegexLexer.__init__(self, **options)
+
+    def get_tokens_unprocessed(self, text):
+        stack = ['root']
+        if self.startinline:
+            stack.append('php')
+        for index, token, value in \
+                RegexLexer.get_tokens_unprocessed(self, text, stack):
+            if token is Name.Other:
+                if value in self._functions:
+                    yield index, Name.Builtin, value
+                    continue
+            yield index, token, value
+
+    def analyse_text(text):
+        if shebang_matches(text, r'php'):
+            return True
+        rv = 0.0
+        if re.search(r'<\?(?!xml)', text):
+            rv += 0.3
+        return rv
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/pointless.py b/.venv/lib/python3.12/site-packages/pygments/lexers/pointless.py
new file mode 100644
index 0000000..aab0a56
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/pointless.py
@@ -0,0 +1,70 @@
+"""
+    pygments.lexers.pointless
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Pointless.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Comment, Error, Keyword, Name, Number, Operator, \
+    Punctuation, String, Text
+
+__all__ = ['PointlessLexer']
+
+
+class PointlessLexer(RegexLexer):
+    """
+    For Pointless source code.
+    """
+
+    name = 'Pointless'
+    url = 'https://ptls.dev'
+    aliases = ['pointless']
+    filenames = ['*.ptls']
+    version_added = '2.7'
+
+    ops = words([
+        "+", "-", "*", "/", "**", "%", "+=", "-=", "*=",
+        "/=", "**=", "%=", "|>", "=", "==", "!=", "<", ">",
+        "<=", ">=", "=>", "$", "++",
+    ])
+
+    keywords = words([
+        "if", "then", "else", "where", "with", "cond",
+        "case", "and", "or", "not", "in", "as", "for",
+        "requires", "throw", "try", "catch", "when",
+        "yield", "upval",
+    ], suffix=r'\b')
+
+    tokens = {
+        'root': [
+            (r'[ \n\r]+', Text),
+            (r'--.*$', Comment.Single),
+            (r'"""', String, 'multiString'),
+            (r'"', String, 'string'),
+            (r'[\[\](){}:;,.]', Punctuation),
+            (ops, Operator),
+            (keywords, Keyword),
+            (r'\d+|\d*\.\d+', Number),
+            (r'(true|false)\b', Name.Builtin),
+            (r'[A-Z][a-zA-Z0-9]*\b', String.Symbol),
+            (r'output\b', Name.Variable.Magic),
+            (r'(export|import)\b', Keyword.Namespace),
+            (r'[a-z][a-zA-Z0-9]*\b', Name.Variable)
+        ],
+        'multiString': [
+            (r'\\.', String.Escape),
+            (r'"""', String, '#pop'),
+            (r'"', String),
+            (r'[^\\"]+', String),
+        ],
+        'string': [
+            (r'\\.', String.Escape),
+            (r'"', String, '#pop'),
+            (r'\n', Error),
+            (r'[^\\"]+', String),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/pony.py b/.venv/lib/python3.12/site-packages/pygments/lexers/pony.py
new file mode 100644
index 0000000..62d16d7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/pony.py
@@ -0,0 +1,93 @@
+"""
+    pygments.lexers.pony
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Pony and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['PonyLexer']
+
+
+class PonyLexer(RegexLexer):
+    """
+    For Pony source code.
+    """
+
+    name = 'Pony'
+    aliases = ['pony']
+    filenames = ['*.pony']
+    url = 'https://www.ponylang.io'
+    version_added = '2.4'
+
+    _caps = r'(iso|trn|ref|val|box|tag)'
+
+    tokens = {
+        'root': [
+            (r'\n', Text),
+            (r'[^\S\n]+', Text),
+            (r'//.*\n', Comment.Single),
+            (r'/\*', Comment.Multiline, 'nested_comment'),
+            (r'"""(?:.|\n)*?"""', String.Doc),
+            (r'"', String, 'string'),
+            (r'\'.*\'', String.Char),
+            (r'=>|[]{}:().~;,|&!^?[]', Punctuation),
+            (words((
+                'addressof', 'and', 'as', 'consume', 'digestof', 'is', 'isnt',
+                'not', 'or'),
+                suffix=r'\b'),
+             Operator.Word),
+            (r'!=|==|<<|>>|[-+/*%=<>]', Operator),
+            (words((
+                'box', 'break', 'compile_error', 'compile_intrinsic',
+                'continue', 'do', 'else', 'elseif', 'embed', 'end', 'error',
+                'for', 'if', 'ifdef', 'in', 'iso', 'lambda', 'let', 'match',
+                'object', 'recover', 'ref', 'repeat', 'return', 'tag', 'then',
+                'this', 'trn', 'try', 'until', 'use', 'var', 'val', 'where',
+                'while', 'with', '#any', '#read', '#send', '#share'),
+                suffix=r'\b'),
+             Keyword),
+            (r'(actor|class|struct|primitive|interface|trait|type)((?:\s)+)',
+             bygroups(Keyword, Text), 'typename'),
+            (r'(new|fun|be)((?:\s)+)', bygroups(Keyword, Text), 'methodname'),
+            (words((
+                'I8', 'U8', 'I16', 'U16', 'I32', 'U32', 'I64', 'U64', 'I128',
+                'U128', 'ILong', 'ULong', 'ISize', 'USize', 'F32', 'F64',
+                'Bool', 'Pointer', 'None', 'Any', 'Array', 'String',
+                'Iterator'),
+                suffix=r'\b'),
+             Name.Builtin.Type),
+            (r'_?[A-Z]\w*', Name.Type),
+            (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+', Number.Float),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'\d+', Number.Integer),
+            (r'(true|false)\b', Name.Builtin),
+            (r'_\d*', Name),
+            (r'_?[a-z][\w\']*', Name)
+        ],
+        'typename': [
+            (_caps + r'?((?:\s)*)(_?[A-Z]\w*)',
+             bygroups(Keyword, Text, Name.Class), '#pop')
+        ],
+        'methodname': [
+            (_caps + r'?((?:\s)*)(_?[a-z]\w*)',
+             bygroups(Keyword, Text, Name.Function), '#pop')
+        ],
+        'nested_comment': [
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline)
+        ],
+        'string': [
+            (r'"', String, '#pop'),
+            (r'\\"', String),
+            (r'[^\\"]+', String)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/praat.py b/.venv/lib/python3.12/site-packages/pygments/lexers/praat.py
new file mode 100644
index 0000000..c50e927
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/praat.py
@@ -0,0 +1,303 @@
+"""
+    pygments.lexers.praat
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Praat
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words, bygroups, include
+from pygments.token import Name, Text, Comment, Keyword, String, Punctuation, \
+    Number, Operator, Whitespace
+
+__all__ = ['PraatLexer']
+
+
+class PraatLexer(RegexLexer):
+    """
+    For Praat scripts.
+    """
+
+    name = 'Praat'
+    url = 'http://www.praat.org'
+    aliases = ['praat']
+    filenames = ['*.praat', '*.proc', '*.psc']
+    version_added = '2.1'
+
+    keywords = (
+        'if', 'then', 'else', 'elsif', 'elif', 'endif', 'fi', 'for', 'from', 'to',
+        'endfor', 'endproc', 'while', 'endwhile', 'repeat', 'until', 'select', 'plus',
+        'minus', 'demo', 'assert', 'stopwatch', 'nocheck', 'nowarn', 'noprogress',
+        'editor', 'endeditor', 'clearinfo',
+    )
+
+    functions_string = (
+        'backslashTrigraphsToUnicode', 'chooseDirectory', 'chooseReadFile',
+        'chooseWriteFile', 'date', 'demoKey', 'do', 'environment', 'extractLine',
+        'extractWord', 'fixed', 'info', 'left', 'mid', 'percent', 'readFile', 'replace',
+        'replace_regex', 'right', 'selected', 'string', 'unicodeToBackslashTrigraphs',
+    )
+
+    functions_numeric = (
+        'abs', 'appendFile', 'appendFileLine', 'appendInfo', 'appendInfoLine', 'arccos',
+        'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', 'barkToHertz',
+        'beginPause', 'beginSendPraat', 'besselI', 'besselK', 'beta', 'beta2',
+        'binomialP', 'binomialQ', 'boolean', 'ceiling', 'chiSquareP', 'chiSquareQ',
+        'choice', 'comment', 'cos', 'cosh', 'createDirectory', 'deleteFile',
+        'demoClicked', 'demoClickedIn', 'demoCommandKeyPressed',
+        'demoExtraControlKeyPressed', 'demoInput', 'demoKeyPressed',
+        'demoOptionKeyPressed', 'demoShiftKeyPressed', 'demoShow', 'demoWaitForInput',
+        'demoWindowTitle', 'demoX', 'demoY', 'differenceLimensToPhon', 'do', 'editor',
+        'endPause', 'endSendPraat', 'endsWith', 'erb', 'erbToHertz', 'erf', 'erfc',
+        'exitScript', 'exp', 'extractNumber', 'fileReadable', 'fisherP', 'fisherQ',
+        'floor', 'gaussP', 'gaussQ', 'hertzToBark', 'hertzToErb', 'hertzToMel',
+        'hertzToSemitones', 'imax', 'imin', 'incompleteBeta', 'incompleteGammaP', 'index',
+        'index_regex', 'integer', 'invBinomialP', 'invBinomialQ', 'invChiSquareQ', 'invFisherQ',
+        'invGaussQ', 'invSigmoid', 'invStudentQ', 'length', 'ln', 'lnBeta', 'lnGamma',
+        'log10', 'log2', 'max', 'melToHertz', 'min', 'minusObject', 'natural', 'number',
+        'numberOfColumns', 'numberOfRows', 'numberOfSelected', 'objectsAreIdentical',
+        'option', 'optionMenu', 'pauseScript', 'phonToDifferenceLimens', 'plusObject',
+        'positive', 'randomBinomial', 'randomGauss', 'randomInteger', 'randomPoisson',
+        'randomUniform', 'real', 'readFile', 'removeObject', 'rindex', 'rindex_regex',
+        'round', 'runScript', 'runSystem', 'runSystem_nocheck', 'selectObject',
+        'selected', 'semitonesToHertz', 'sentence', 'sentencetext', 'sigmoid', 'sin', 'sinc',
+        'sincpi', 'sinh', 'soundPressureToPhon', 'sqrt', 'startsWith', 'studentP',
+        'studentQ', 'tan', 'tanh', 'text', 'variableExists', 'word', 'writeFile', 'writeFileLine',
+        'writeInfo', 'writeInfoLine',
+    )
+
+    functions_array = (
+        'linear', 'randomGauss', 'randomInteger', 'randomUniform', 'zero',
+    )
+
+    objects = (
+        'Activation', 'AffineTransform', 'AmplitudeTier', 'Art', 'Artword',
+        'Autosegment', 'BarkFilter', 'BarkSpectrogram', 'CCA', 'Categories',
+        'Cepstrogram', 'Cepstrum', 'Cepstrumc', 'ChebyshevSeries', 'ClassificationTable',
+        'Cochleagram', 'Collection', 'ComplexSpectrogram', 'Configuration', 'Confusion',
+        'ContingencyTable', 'Corpus', 'Correlation', 'Covariance',
+        'CrossCorrelationTable', 'CrossCorrelationTables', 'DTW', 'DataModeler',
+        'Diagonalizer', 'Discriminant', 'Dissimilarity', 'Distance', 'Distributions',
+        'DurationTier', 'EEG', 'ERP', 'ERPTier', 'EditCostsTable', 'EditDistanceTable',
+        'Eigen', 'Excitation', 'Excitations', 'ExperimentMFC', 'FFNet', 'FeatureWeights',
+        'FileInMemory', 'FilesInMemory', 'Formant', 'FormantFilter', 'FormantGrid',
+        'FormantModeler', 'FormantPoint', 'FormantTier', 'GaussianMixture', 'HMM',
+        'HMM_Observation', 'HMM_ObservationSequence', 'HMM_State', 'HMM_StateSequence',
+        'Harmonicity', 'ISpline', 'Index', 'Intensity', 'IntensityTier', 'IntervalTier',
+        'KNN', 'KlattGrid', 'KlattTable', 'LFCC', 'LPC', 'Label', 'LegendreSeries',
+        'LinearRegression', 'LogisticRegression', 'LongSound', 'Ltas', 'MFCC', 'MSpline',
+        'ManPages', 'Manipulation', 'Matrix', 'MelFilter', 'MelSpectrogram',
+        'MixingMatrix', 'Movie', 'Network', 'Object', 'OTGrammar', 'OTHistory', 'OTMulti',
+        'PCA', 'PairDistribution', 'ParamCurve', 'Pattern', 'Permutation', 'Photo',
+        'Pitch', 'PitchModeler', 'PitchTier', 'PointProcess', 'Polygon', 'Polynomial',
+        'PowerCepstrogram', 'PowerCepstrum', 'Procrustes', 'RealPoint', 'RealTier',
+        'ResultsMFC', 'Roots', 'SPINET', 'SSCP', 'SVD', 'Salience', 'ScalarProduct',
+        'Similarity', 'SimpleString', 'SortedSetOfString', 'Sound', 'Speaker',
+        'Spectrogram', 'Spectrum', 'SpectrumTier', 'SpeechSynthesizer', 'SpellingChecker',
+        'Strings', 'StringsIndex', 'Table', 'TableOfReal', 'TextGrid', 'TextInterval',
+        'TextPoint', 'TextTier', 'Tier', 'Transition', 'VocalTract', 'VocalTractTier',
+        'Weight', 'WordList',
+    )
+
+    variables_numeric = (
+        'macintosh', 'windows', 'unix', 'praatVersion', 'pi', 'e', 'undefined',
+    )
+
+    variables_string = (
+        'praatVersion', 'tab', 'shellDirectory', 'homeDirectory',
+        'preferencesDirectory', 'newline', 'temporaryDirectory',
+        'defaultDirectory',
+    )
+
+    object_attributes = (
+        'ncol', 'nrow', 'xmin', 'ymin', 'xmax', 'ymax', 'nx', 'ny', 'dx', 'dy',
+    )
+
+    tokens = {
+        'root': [
+            (r'(\s+)(#.*?$)',  bygroups(Whitespace, Comment.Single)),
+            (r'^#.*?$',        Comment.Single),
+            (r';[^\n]*',       Comment.Single),
+            (r'\s+',           Whitespace),
+
+            (r'\bprocedure\b', Keyword,       'procedure_definition'),
+            (r'\bcall\b',      Keyword,       'procedure_call'),
+            (r'@',             Name.Function, 'procedure_call'),
+
+            include('function_call'),
+
+            (words(keywords, suffix=r'\b'), Keyword),
+
+            (r'(\bform\b)(\s+)([^\n]+)',
+             bygroups(Keyword, Whitespace, String), 'old_form'),
+
+            (r'(print(?:line|tab)?|echo|exit|asserterror|pause|send(?:praat|socket)|'
+             r'include|execute|system(?:_nocheck)?)(\s+)',
+             bygroups(Keyword, Whitespace), 'string_unquoted'),
+
+            (r'(goto|label)(\s+)(\w+)', bygroups(Keyword, Whitespace, Name.Label)),
+
+            include('variable_name'),
+            include('number'),
+
+            (r'"', String, 'string'),
+
+            (words((objects), suffix=r'(?=\s+\S+\n)'), Name.Class, 'string_unquoted'),
+
+            (r'\b[A-Z]', Keyword, 'command'),
+            (r'(\.{3}|[)(,])', Punctuation),
+        ],
+        'command': [
+            (r'( ?[\w()-]+ ?)', Keyword),
+
+            include('string_interpolated'),
+
+            (r'\.{3}', Keyword, ('#pop', 'old_arguments')),
+            (r':', Keyword, ('#pop', 'comma_list')),
+            (r'\s', Whitespace, '#pop'),
+        ],
+        'procedure_call': [
+            (r'\s+', Whitespace),
+            (r'([\w.]+)(?:(:)|(?:(\s*)(\()))',
+             bygroups(Name.Function, Punctuation,
+                      Text.Whitespace, Punctuation), '#pop'),
+            (r'([\w.]+)', Name.Function, ('#pop', 'old_arguments')),
+        ],
+        'procedure_definition': [
+            (r'\s', Whitespace),
+            (r'([\w.]+)(\s*?[(:])',
+             bygroups(Name.Function, Whitespace), '#pop'),
+            (r'([\w.]+)([^\n]*)',
+             bygroups(Name.Function, Text), '#pop'),
+        ],
+        'function_call': [
+            (words(functions_string, suffix=r'\$(?=\s*[:(])'), Name.Function, 'function'),
+            (words(functions_array, suffix=r'#(?=\s*[:(])'),   Name.Function, 'function'),
+            (words(functions_numeric, suffix=r'(?=\s*[:(])'),  Name.Function, 'function'),
+        ],
+        'function': [
+            (r'\s+',   Whitespace),
+            (r':',     Punctuation, ('#pop', 'comma_list')),
+            (r'\s*\(', Punctuation, ('#pop', 'comma_list')),
+        ],
+        'comma_list': [
+            (r'(\s*\n\s*)(\.{3})', bygroups(Whitespace, Punctuation)),
+
+            (r'(\s*)(?:([)\]])|(\n))', bygroups(
+                Whitespace, Punctuation, Whitespace), '#pop'),
+
+            (r'\s+', Whitespace),
+            (r'"',   String, 'string'),
+            (r'\b(if|then|else|fi|endif)\b', Keyword),
+
+            include('function_call'),
+            include('variable_name'),
+            include('operator'),
+            include('number'),
+
+            (r'[()]', Text),
+            (r',', Punctuation),
+        ],
+        'old_arguments': [
+            (r'\n', Whitespace, '#pop'),
+
+            include('variable_name'),
+            include('operator'),
+            include('number'),
+
+            (r'"', String, 'string'),
+            (r'[^\n]', Text),
+        ],
+        'number': [
+            (r'\n', Whitespace, '#pop'),
+            (r'\b\d+(\.\d*)?([eE][-+]?\d+)?%?', Number),
+        ],
+        'object_reference': [
+            include('string_interpolated'),
+            (r'([a-z][a-zA-Z0-9_]*|\d+)', Name.Builtin),
+
+            (words(object_attributes, prefix=r'\.'), Name.Builtin, '#pop'),
+
+            (r'\$', Name.Builtin),
+            (r'\[', Text, '#pop'),
+        ],
+        'variable_name': [
+            include('operator'),
+            include('number'),
+
+            (words(variables_string,  suffix=r'\$'), Name.Variable.Global),
+            (words(variables_numeric,
+             suffix=r'(?=[^a-zA-Z0-9_."\'$#\[:(]|\s|^|$)'),
+             Name.Variable.Global),
+
+            (words(objects, prefix=r'\b', suffix=r"(_)"),
+             bygroups(Name.Builtin, Name.Builtin),
+             'object_reference'),
+
+            (r'\.?_?[a-z][\w.]*(\$|#)?', Text),
+            (r'[\[\]]', Punctuation, 'comma_list'),
+
+            include('string_interpolated'),
+        ],
+        'operator': [
+            (r'([+\/*<>=!-]=?|[&*|][&*|]?|\^|<>)',       Operator),
+            (r'(?', Punctuation),
+            (r'"(?:\\x[0-9a-fA-F]+\\|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|'
+             r'\\[0-7]+\\|\\["\\abcefnrstv]|[^\\"])*"', String.Double),
+            (r"'(?:''|[^'])*'", String.Atom),  # quoted atom
+            # Needs to not be followed by an atom.
+            # (r'=(?=\s|[a-zA-Z\[])', Operator),
+            (r'is\b', Operator),
+            (r'(<|>|=<|>=|==|=:=|=|/|//|\*|\+|-)(?=\s|[a-zA-Z0-9\[])',
+             Operator),
+            (r'(mod|div|not)\b', Operator),
+            (r'_', Keyword),  # The don't-care variable
+            (r'([a-z]+)(:)', bygroups(Name.Namespace, Punctuation)),
+            (r'([a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]'
+             r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*)'
+             r'(\s*)(:-|-->)',
+             bygroups(Name.Function, Text, Operator)),  # function defn
+            (r'([a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]'
+             r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*)'
+             r'(\s*)(\()',
+             bygroups(Name.Function, Text, Punctuation)),
+            (r'[a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]'
+             r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*',
+             String.Atom),  # atom, characters
+            # This one includes !
+            (r'[#&*+\-./:<=>?@\\^~\u00a1-\u00bf\u2010-\u303f]+',
+             String.Atom),  # atom, graphics
+            (r'[A-Z_]\w*', Name.Variable),
+            (r'\s+|[\u2000-\u200f\ufff0-\ufffe\uffef]', Text),
+        ],
+        'nested-comment': [
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'[^*/]+', Comment.Multiline),
+            (r'[*/]', Comment.Multiline),
+        ],
+    }
+
+    def analyse_text(text):
+        """Competes with IDL and Visual Prolog on *.pro"""
+        if ':-' in text:
+            # Visual Prolog also uses :-
+            return 0.5
+        else:
+            return 0
+
+
+class LogtalkLexer(RegexLexer):
+    """
+    For Logtalk source code.
+    """
+
+    name = 'Logtalk'
+    url = 'http://logtalk.org/'
+    aliases = ['logtalk']
+    filenames = ['*.lgt', '*.logtalk']
+    mimetypes = ['text/x-logtalk']
+    version_added = '0.10'
+
+    tokens = {
+        'root': [
+            # Directives
+            (r'^\s*:-\s', Punctuation, 'directive'),
+            # Comments
+            (r'%.*?\n', Comment),
+            (r'/\*(.|\n)*?\*/', Comment),
+            # Whitespace
+            (r'\n', Text),
+            (r'\s+', Text),
+            # Numbers
+            (r"0'[\\]?.", Number),
+            (r'0b[01]+', Number.Bin),
+            (r'0o[0-7]+', Number.Oct),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'\d+\.?\d*((e|E)(\+|-)?\d+)?', Number),
+            # Variables
+            (r'([A-Z_][a-zA-Z0-9_]*)', Name.Variable),
+            # Event handlers
+            (r'(after|before)(?=[(])', Keyword),
+            # Message forwarding handler
+            (r'forward(?=[(])', Keyword),
+            # Execution-context methods
+            (r'(context|parameter|this|se(lf|nder))(?=[(])', Keyword),
+            # Reflection
+            (r'(current_predicate|predicate_property)(?=[(])', Keyword),
+            # DCGs and term expansion
+            (r'(expand_(goal|term)|(goal|term)_expansion|phrase)(?=[(])', Keyword),
+            # Entity
+            (r'(abolish|c(reate|urrent))_(object|protocol|category)(?=[(])', Keyword),
+            (r'(object|protocol|category)_property(?=[(])', Keyword),
+            # Entity relations
+            (r'co(mplements_object|nforms_to_protocol)(?=[(])', Keyword),
+            (r'extends_(object|protocol|category)(?=[(])', Keyword),
+            (r'imp(lements_protocol|orts_category)(?=[(])', Keyword),
+            (r'(instantiat|specializ)es_class(?=[(])', Keyword),
+            # Events
+            (r'(current_event|(abolish|define)_events)(?=[(])', Keyword),
+            # Flags
+            (r'(create|current|set)_logtalk_flag(?=[(])', Keyword),
+            # Compiling, loading, and library paths
+            (r'logtalk_(compile|l(ibrary_path|oad|oad_context)|make(_target_action)?)(?=[(])', Keyword),
+            (r'\blogtalk_make\b', Keyword),
+            # Database
+            (r'(clause|retract(all)?)(?=[(])', Keyword),
+            (r'a(bolish|ssert(a|z))(?=[(])', Keyword),
+            # Control constructs
+            (r'(ca(ll|tch)|throw)(?=[(])', Keyword),
+            (r'(fa(il|lse)|true|(instantiation|system)_error)\b', Keyword),
+            (r'(uninstantiation|type|domain|existence|permission|representation|evaluation|resource|syntax)_error(?=[(])', Keyword),
+            # All solutions
+            (r'((bag|set)of|f(ind|or)all)(?=[(])', Keyword),
+            # Multi-threading predicates
+            (r'threaded(_(ca(ll|ncel)|once|ignore|exit|peek|wait|notify))?(?=[(])', Keyword),
+            # Engine predicates
+            (r'threaded_engine(_(create|destroy|self|next|next_reified|yield|post|fetch))?(?=[(])', Keyword),
+            # Term unification
+            (r'(subsumes_term|unify_with_occurs_check)(?=[(])', Keyword),
+            # Term creation and decomposition
+            (r'(functor|arg|copy_term|numbervars|term_variables)(?=[(])', Keyword),
+            # Evaluable functors
+            (r'(div|rem|m(ax|in|od)|abs|sign)(?=[(])', Keyword),
+            (r'float(_(integer|fractional)_part)?(?=[(])', Keyword),
+            (r'(floor|t(an|runcate)|round|ceiling)(?=[(])', Keyword),
+            # Other arithmetic functors
+            (r'(cos|a(cos|sin|tan|tan2)|exp|log|s(in|qrt)|xor)(?=[(])', Keyword),
+            # Term testing
+            (r'(var|atom(ic)?|integer|float|c(allable|ompound)|n(onvar|umber)|ground|acyclic_term)(?=[(])', Keyword),
+            # Term comparison
+            (r'compare(?=[(])', Keyword),
+            # Stream selection and control
+            (r'(curren|se)t_(in|out)put(?=[(])', Keyword),
+            (r'(open|close)(?=[(])', Keyword),
+            (r'flush_output(?=[(])', Keyword),
+            (r'(at_end_of_stream|flush_output)\b', Keyword),
+            (r'(stream_property|at_end_of_stream|set_stream_position)(?=[(])', Keyword),
+            # Character and byte input/output
+            (r'(nl|(get|peek|put)_(byte|c(har|ode)))(?=[(])', Keyword),
+            (r'\bnl\b', Keyword),
+            # Term input/output
+            (r'read(_term)?(?=[(])', Keyword),
+            (r'write(q|_(canonical|term))?(?=[(])', Keyword),
+            (r'(current_)?op(?=[(])', Keyword),
+            (r'(current_)?char_conversion(?=[(])', Keyword),
+            # Atomic term processing
+            (r'atom_(length|c(hars|o(ncat|des)))(?=[(])', Keyword),
+            (r'(char_code|sub_atom)(?=[(])', Keyword),
+            (r'number_c(har|ode)s(?=[(])', Keyword),
+            # Implementation defined hooks functions
+            (r'(se|curren)t_prolog_flag(?=[(])', Keyword),
+            (r'\bhalt\b', Keyword),
+            (r'halt(?=[(])', Keyword),
+            # Message sending operators
+            (r'(::|:|\^\^)', Operator),
+            # External call
+            (r'[{}]', Keyword),
+            # Logic and control
+            (r'(ignore|once)(?=[(])', Keyword),
+            (r'\brepeat\b', Keyword),
+            # Sorting
+            (r'(key)?sort(?=[(])', Keyword),
+            # Bitwise functors
+            (r'(>>|<<|/\\|\\\\|\\)', Operator),
+            # Predicate aliases
+            (r'\bas\b', Operator),
+            # Arithmetic evaluation
+            (r'\bis\b', Keyword),
+            # Arithmetic comparison
+            (r'(=:=|=\\=|<|=<|>=|>)', Operator),
+            # Term creation and decomposition
+            (r'=\.\.', Operator),
+            # Term unification
+            (r'(=|\\=)', Operator),
+            # Term comparison
+            (r'(==|\\==|@=<|@<|@>=|@>)', Operator),
+            # Evaluable functors
+            (r'(//|[-+*/])', Operator),
+            (r'\b(e|pi|div|mod|rem)\b', Operator),
+            # Other arithmetic functors
+            (r'\b\*\*\b', Operator),
+            # DCG rules
+            (r'-->', Operator),
+            # Control constructs
+            (r'([!;]|->)', Operator),
+            # Logic and control
+            (r'\\+', Operator),
+            # Mode operators
+            (r'[?@]', Operator),
+            # Existential quantifier
+            (r'\^', Operator),
+            # Punctuation
+            (r'[()\[\],.|]', Text),
+            # Atoms
+            (r"[a-z][a-zA-Z0-9_]*", Text),
+            (r"'", String, 'quoted_atom'),
+            # Double-quoted terms
+            (r'"', String, 'double_quoted_term'),
+        ],
+
+        'quoted_atom': [
+            (r"''", String),
+            (r"'", String, '#pop'),
+            (r'\\([\\abfnrtv"\']|(x[a-fA-F0-9]+|[0-7]+)\\)', String.Escape),
+            (r"[^\\'\n]+", String),
+            (r'\\', String),
+        ],
+
+        'double_quoted_term': [
+            (r'""', String),
+            (r'"', String, '#pop'),
+            (r'\\([\\abfnrtv"\']|(x[a-fA-F0-9]+|[0-7]+)\\)', String.Escape),
+            (r'[^\\"\n]+', String),
+            (r'\\', String),
+        ],
+
+        'directive': [
+            # Conditional compilation directives
+            (r'(el)?if(?=[(])', Keyword, 'root'),
+            (r'(e(lse|ndif))(?=[.])', Keyword, 'root'),
+            # Entity directives
+            (r'(category|object|protocol)(?=[(])', Keyword, 'entityrelations'),
+            (r'(end_(category|object|protocol))(?=[.])', Keyword, 'root'),
+            # Predicate scope directives
+            (r'(public|protected|private)(?=[(])', Keyword, 'root'),
+            # Other directives
+            (r'e(n(coding|sure_loaded)|xport)(?=[(])', Keyword, 'root'),
+            (r'in(clude|itialization|fo)(?=[(])', Keyword, 'root'),
+            (r'(built_in|dynamic|synchronized|threaded)(?=[.])', Keyword, 'root'),
+            (r'(alias|d(ynamic|iscontiguous)|m(eta_(non_terminal|predicate)|ode|ultifile)|s(et_(logtalk|prolog)_flag|ynchronized))(?=[(])', Keyword, 'root'),
+            (r'op(?=[(])', Keyword, 'root'),
+            (r'(c(alls|oinductive)|module|reexport|use(s|_module))(?=[(])', Keyword, 'root'),
+            (r'[a-z][a-zA-Z0-9_]*(?=[(])', Text, 'root'),
+            (r'[a-z][a-zA-Z0-9_]*(?=[.])', Text, 'root'),
+        ],
+
+        'entityrelations': [
+            (r'(complements|extends|i(nstantiates|mp(lements|orts))|specializes)(?=[(])', Keyword),
+            # Numbers
+            (r"0'[\\]?.", Number),
+            (r'0b[01]+', Number.Bin),
+            (r'0o[0-7]+', Number.Oct),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'\d+\.?\d*((e|E)(\+|-)?\d+)?', Number),
+            # Variables
+            (r'([A-Z_][a-zA-Z0-9_]*)', Name.Variable),
+            # Atoms
+            (r"[a-z][a-zA-Z0-9_]*", Text),
+            (r"'", String, 'quoted_atom'),
+            # Double-quoted terms
+            (r'"', String, 'double_quoted_term'),
+            # End of entity-opening directive
+            (r'([)]\.)', Text, 'root'),
+            # Scope operator
+            (r'(::)', Operator),
+            # Punctuation
+            (r'[()\[\],.|]', Text),
+            # Comments
+            (r'%.*?\n', Comment),
+            (r'/\*(.|\n)*?\*/', Comment),
+            # Whitespace
+            (r'\n', Text),
+            (r'\s+', Text),
+        ]
+    }
+
+    def analyse_text(text):
+        if ':- object(' in text:
+            return 1.0
+        elif ':- protocol(' in text:
+            return 1.0
+        elif ':- category(' in text:
+            return 1.0
+        elif re.search(r'^:-\s[a-z]', text, re.M):
+            return 0.9
+        else:
+            return 0.0
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/promql.py b/.venv/lib/python3.12/site-packages/pygments/lexers/promql.py
new file mode 100644
index 0000000..5cfa73f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/promql.py
@@ -0,0 +1,176 @@
+"""
+    pygments.lexers.promql
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Prometheus Query Language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, default, words
+from pygments.token import Comment, Keyword, Name, Number, Operator, \
+    Punctuation, String, Whitespace
+
+__all__ = ["PromQLLexer"]
+
+
+class PromQLLexer(RegexLexer):
+    """
+    For PromQL queries.
+
+    For details about the grammar see:
+    https://github.com/prometheus/prometheus/tree/master/promql/parser
+
+    .. versionadded: 2.7
+    """
+
+    name = "PromQL"
+    url = 'https://prometheus.io/docs/prometheus/latest/querying/basics/'
+    aliases = ["promql"]
+    filenames = ["*.promql"]
+    version_added = ''
+
+    base_keywords = (
+        words(
+            (
+                "bool",
+                "by",
+                "group_left",
+                "group_right",
+                "ignoring",
+                "offset",
+                "on",
+                "without",
+            ),
+            suffix=r"\b",
+        ),
+        Keyword,
+    )
+
+    aggregator_keywords = (
+        words(
+            (
+                "sum",
+                "min",
+                "max",
+                "avg",
+                "group",
+                "stddev",
+                "stdvar",
+                "count",
+                "count_values",
+                "bottomk",
+                "topk",
+                "quantile",
+            ),
+            suffix=r"\b",
+        ),
+        Keyword,
+    )
+
+    function_keywords = (
+        words(
+            (
+                "abs",
+                "absent",
+                "absent_over_time",
+                "avg_over_time",
+                "ceil",
+                "changes",
+                "clamp_max",
+                "clamp_min",
+                "count_over_time",
+                "day_of_month",
+                "day_of_week",
+                "days_in_month",
+                "delta",
+                "deriv",
+                "exp",
+                "floor",
+                "histogram_quantile",
+                "holt_winters",
+                "hour",
+                "idelta",
+                "increase",
+                "irate",
+                "label_join",
+                "label_replace",
+                "ln",
+                "log10",
+                "log2",
+                "max_over_time",
+                "min_over_time",
+                "minute",
+                "month",
+                "predict_linear",
+                "quantile_over_time",
+                "rate",
+                "resets",
+                "round",
+                "scalar",
+                "sort",
+                "sort_desc",
+                "sqrt",
+                "stddev_over_time",
+                "stdvar_over_time",
+                "sum_over_time",
+                "time",
+                "timestamp",
+                "vector",
+                "year",
+            ),
+            suffix=r"\b",
+        ),
+        Keyword.Reserved,
+    )
+
+    tokens = {
+        "root": [
+            (r"\n", Whitespace),
+            (r"\s+", Whitespace),
+            (r",", Punctuation),
+            # Keywords
+            base_keywords,
+            aggregator_keywords,
+            function_keywords,
+            # Offsets
+            (r"[1-9][0-9]*[smhdwy]", String),
+            # Numbers
+            (r"-?[0-9]+\.[0-9]+", Number.Float),
+            (r"-?[0-9]+", Number.Integer),
+            # Comments
+            (r"#.*?$", Comment.Single),
+            # Operators
+            (r"(\+|\-|\*|\/|\%|\^)", Operator),
+            (r"==|!=|>=|<=|<|>", Operator),
+            (r"and|or|unless", Operator.Word),
+            # Metrics
+            (r"[_a-zA-Z][a-zA-Z0-9_]+", Name.Variable),
+            # Params
+            (r'(["\'])(.*?)(["\'])', bygroups(Punctuation, String, Punctuation)),
+            # Other states
+            (r"\(", Operator, "function"),
+            (r"\)", Operator),
+            (r"\{", Punctuation, "labels"),
+            (r"\[", Punctuation, "range"),
+        ],
+        "labels": [
+            (r"\}", Punctuation, "#pop"),
+            (r"\n", Whitespace),
+            (r"\s+", Whitespace),
+            (r",", Punctuation),
+            (r'([_a-zA-Z][a-zA-Z0-9_]*?)(\s*?)(=~|!=|=|!~)(\s*?)("|\')(.*?)("|\')',
+             bygroups(Name.Label, Whitespace, Operator, Whitespace,
+                      Punctuation, String, Punctuation)),
+        ],
+        "range": [
+            (r"\]", Punctuation, "#pop"),
+            (r"[1-9][0-9]*[smhdwy]", String),
+        ],
+        "function": [
+            (r"\)", Operator, "#pop"),
+            (r"\(", Operator, "#push"),
+            default("#pop"),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/prql.py b/.venv/lib/python3.12/site-packages/pygments/lexers/prql.py
new file mode 100644
index 0000000..a62cb6b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/prql.py
@@ -0,0 +1,251 @@
+"""
+    pygments.lexers.prql
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for the PRQL query language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, combined, words, include, bygroups
+from pygments.token import Comment, Literal, Keyword, Name, Number, Operator, \
+    Punctuation, String, Text, Whitespace
+
+__all__ = ['PrqlLexer']
+
+
+class PrqlLexer(RegexLexer):
+    """
+    For PRQL source code.
+
+    grammar: https://github.com/PRQL/prql/tree/main/grammars
+    """
+
+    name = 'PRQL'
+    url = 'https://prql-lang.org/'
+    aliases = ['prql']
+    filenames = ['*.prql']
+    mimetypes = ['application/prql', 'application/x-prql']
+    version_added = '2.17'
+
+    builtinTypes = words((
+        "bool",
+        "int",
+        "int8", "int16", "int32", "int64", "int128",
+        "float",
+        "text",
+        "set"), suffix=r'\b')
+
+    def innerstring_rules(ttype):
+        return [
+            # the new style '{}'.format(...) string formatting
+            (r'\{'
+             r'((\w+)((\.\w+)|(\[[^\]]+\]))*)?'  # field name
+             r'(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?'
+             r'\}', String.Interpol),
+
+            (r'[^\\\'"%{\n]+', ttype),
+            (r'[\'"\\]', ttype),
+            (r'%|(\{{1,2})', ttype)
+        ]
+
+    def fstring_rules(ttype):
+        return [
+            (r'\}', String.Interpol),
+            (r'\{', String.Interpol, 'expr-inside-fstring'),
+            (r'[^\\\'"{}\n]+', ttype),
+            (r'[\'"\\]', ttype),
+        ]
+
+    tokens = {
+        'root': [
+
+            # Comments
+            (r'#!.*', String.Doc),
+            (r'#.*', Comment.Single),
+
+            # Whitespace
+            (r'\s+', Whitespace),
+
+            # Modules
+            (r'^(\s*)(module)(\s*)',
+             bygroups(Whitespace, Keyword.Namespace, Whitespace),
+             'imports'),
+
+            (builtinTypes, Keyword.Type),
+
+            # Main
+            (r'^prql ', Keyword.Reserved),
+
+            ('let', Keyword.Declaration),
+
+            include('keywords'),
+            include('expr'),
+
+            # Transforms
+            (r'^[A-Za-z_][a-zA-Z0-9_]*', Keyword),
+        ],
+        'expr': [
+            # non-raw f-strings
+            ('(f)(""")', bygroups(String.Affix, String.Double),
+             combined('fstringescape', 'tdqf')),
+            ("(f)(''')", bygroups(String.Affix, String.Single),
+             combined('fstringescape', 'tsqf')),
+            ('(f)(")', bygroups(String.Affix, String.Double),
+             combined('fstringescape', 'dqf')),
+            ("(f)(')", bygroups(String.Affix, String.Single),
+             combined('fstringescape', 'sqf')),
+
+            # non-raw s-strings
+            ('(s)(""")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'tdqf')),
+            ("(s)(''')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'tsqf')),
+            ('(s)(")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'dqf')),
+            ("(s)(')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'sqf')),
+
+            # raw strings
+            ('(?i)(r)(""")',
+             bygroups(String.Affix, String.Double), 'tdqs'),
+            ("(?i)(r)(''')",
+             bygroups(String.Affix, String.Single), 'tsqs'),
+            ('(?i)(r)(")',
+             bygroups(String.Affix, String.Double), 'dqs'),
+            ("(?i)(r)(')",
+             bygroups(String.Affix, String.Single), 'sqs'),
+
+            # non-raw strings
+            ('"""', String.Double, combined('stringescape', 'tdqs')),
+            ("'''", String.Single, combined('stringescape', 'tsqs')),
+            ('"', String.Double, combined('stringescape', 'dqs')),
+            ("'", String.Single, combined('stringescape', 'sqs')),
+
+            # Time and dates
+            (r'@\d{4}-\d{2}-\d{2}T\d{2}(:\d{2})?(:\d{2})?(\.\d{1,6})?(Z|[+-]\d{1,2}(:\d{1,2})?)?', Literal.Date),
+            (r'@\d{4}-\d{2}-\d{2}', Literal.Date),
+            (r'@\d{2}(:\d{2})?(:\d{2})?(\.\d{1,6})?(Z|[+-]\d{1,2}(:\d{1,2})?)?', Literal.Date),
+
+            (r'[^\S\n]+', Text),
+            include('numbers'),
+            (r'->|=>|==|!=|>=|<=|~=|&&|\|\||\?\?|\/\/', Operator),
+            (r'[-~+/*%=<>&^|.@]', Operator),
+            (r'[]{}:(),;[]', Punctuation),
+            include('functions'),
+
+            # Variable Names
+            (r'[A-Za-z_][a-zA-Z0-9_]*', Name.Variable),
+        ],
+        'numbers': [
+            (r'(\d(?:_?\d)*\.(?:\d(?:_?\d)*)?|(?:\d(?:_?\d)*)?\.\d(?:_?\d)*)'
+             r'([eE][+-]?\d(?:_?\d)*)?', Number.Float),
+            (r'\d(?:_?\d)*[eE][+-]?\d(?:_?\d)*j?', Number.Float),
+            (r'0[oO](?:_?[0-7])+', Number.Oct),
+            (r'0[bB](?:_?[01])+', Number.Bin),
+            (r'0[xX](?:_?[a-fA-F0-9])+', Number.Hex),
+            (r'\d(?:_?\d)*', Number.Integer),
+        ],
+        'fstringescape': [
+            include('stringescape'),
+        ],
+        'bytesescape': [
+            (r'\\([\\bfnrt"\']|\n|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
+        ],
+        'stringescape': [
+            (r'\\(N\{.*?\}|u\{[a-fA-F0-9]{1,6}\})', String.Escape),
+            include('bytesescape')
+        ],
+        'fstrings-single': fstring_rules(String.Single),
+        'fstrings-double': fstring_rules(String.Double),
+        'strings-single': innerstring_rules(String.Single),
+        'strings-double': innerstring_rules(String.Double),
+        'dqf': [
+            (r'"', String.Double, '#pop'),
+            (r'\\\\|\\"|\\\n', String.Escape),  # included here for raw strings
+            include('fstrings-double')
+        ],
+        'sqf': [
+            (r"'", String.Single, '#pop'),
+            (r"\\\\|\\'|\\\n", String.Escape),  # included here for raw strings
+            include('fstrings-single')
+        ],
+        'dqs': [
+            (r'"', String.Double, '#pop'),
+            (r'\\\\|\\"|\\\n', String.Escape),  # included here for raw strings
+            include('strings-double')
+        ],
+        'sqs': [
+            (r"'", String.Single, '#pop'),
+            (r"\\\\|\\'|\\\n", String.Escape),  # included here for raw strings
+            include('strings-single')
+        ],
+        'tdqf': [
+            (r'"""', String.Double, '#pop'),
+            include('fstrings-double'),
+            (r'\n', String.Double)
+        ],
+        'tsqf': [
+            (r"'''", String.Single, '#pop'),
+            include('fstrings-single'),
+            (r'\n', String.Single)
+        ],
+        'tdqs': [
+            (r'"""', String.Double, '#pop'),
+            include('strings-double'),
+            (r'\n', String.Double)
+        ],
+        'tsqs': [
+            (r"'''", String.Single, '#pop'),
+            include('strings-single'),
+            (r'\n', String.Single)
+        ],
+
+        'expr-inside-fstring': [
+            (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
+            # without format specifier
+            (r'(=\s*)?'         # debug (https://bugs.python.org/issue36817)
+             r'\}', String.Interpol, '#pop'),
+            # with format specifier
+            # we'll catch the remaining '}' in the outer scope
+            (r'(=\s*)?'         # debug (https://bugs.python.org/issue36817)
+             r':', String.Interpol, '#pop'),
+            (r'\s+', Whitespace),  # allow new lines
+            include('expr'),
+        ],
+        'expr-inside-fstring-inner': [
+            (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
+            (r'[])}]', Punctuation, '#pop'),
+            (r'\s+', Whitespace),  # allow new lines
+            include('expr'),
+        ],
+        'keywords': [
+            (words((
+                'into', 'case', 'type', 'module', 'internal',
+            ), suffix=r'\b'),
+                Keyword),
+            (words(('true', 'false', 'null'), suffix=r'\b'), Keyword.Constant),
+        ],
+        'functions': [
+            (words((
+                "min", "max", "sum", "average", "stddev", "every", "any",
+                "concat_array", "count", "lag", "lead", "first", "last",
+                "rank", "rank_dense", "row_number", "round", "as", "in",
+                "tuple_every", "tuple_map", "tuple_zip", "_eq", "_is_null",
+                "from_text", "lower", "upper", "read_parquet", "read_csv"),
+                suffix=r'\b'),
+             Name.Function),
+        ],
+
+        'comment': [
+            (r'-(?!\})', Comment.Multiline),
+            (r'\{-', Comment.Multiline, 'comment'),
+            (r'[^-}]', Comment.Multiline),
+            (r'-\}', Comment.Multiline, '#pop'),
+        ],
+
+        'imports': [
+            (r'\w+(\.\w+)*', Name.Class, '#pop'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/ptx.py b/.venv/lib/python3.12/site-packages/pygments/lexers/ptx.py
new file mode 100644
index 0000000..685c735
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/ptx.py
@@ -0,0 +1,119 @@
+"""
+    pygments.lexers.ptx
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexer for other PTX language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, include, words
+from pygments.token import Comment, Keyword, Name, String, Number, \
+    Punctuation, Whitespace, Operator
+
+__all__ = ["PtxLexer"]
+
+
+class PtxLexer(RegexLexer):
+    """
+    For NVIDIA `PTX `_
+    source.
+    """
+    name = 'PTX'
+    url = "https://docs.nvidia.com/cuda/parallel-thread-execution/"
+    filenames = ['*.ptx']
+    aliases = ['ptx']
+    mimetypes = ['text/x-ptx']
+    version_added = '2.16'
+
+    #: optional Comment or Whitespace
+    string = r'"[^"]*?"'
+    followsym = r'[a-zA-Z0-9_$]'
+    identifier = r'([-a-zA-Z$._][\w\-$.]*|' + string + ')'
+    block_label = r'(' + identifier + r'|(\d+))'
+
+    tokens = {
+        'root': [
+            include('whitespace'),
+
+            (block_label + r'\s*:', Name.Label),
+
+            include('keyword'),
+
+            (r'%' + identifier, Name.Variable),
+            (r'%\d+', Name.Variable.Anonymous),
+            (r'c?' + string, String),
+            (identifier, Name.Variable),
+            (r';', Punctuation),
+            (r'[*+-/]', Operator),
+
+            (r'0[xX][a-fA-F0-9]+', Number),
+            (r'-?\d+(?:[.]\d+)?(?:[eE][-+]?\d+(?:[.]\d+)?)?', Number),
+
+            (r'[=<>{}\[\]()*.,!]|x\b', Punctuation)
+
+        ],
+        'whitespace': [
+            (r'(\n|\s+)+', Whitespace),
+            (r'//.*?\n', Comment)
+        ],
+
+        'keyword': [
+            # Instruction keywords
+            (words((
+                'abs', 'discard', 'min', 'shf', 'vadd',
+                'activemask', 'div', 'mma', 'shfl', 'vadd2',
+                'add', 'dp2a', 'mov', 'shl', 'vadd4',
+                'addc', 'dp4a', 'movmatrix', 'shr', 'vavrg2',
+                'alloca', 'elect', 'mul', 'sin', 'vavrg4',
+                'and', 'ex2', 'mul24', 'slct', 'vmad',
+                'applypriority', 'exit', 'multimem', 'sqrt', 'vmax',
+                'atom', 'fence', 'nanosleep', 'st', 'vmax2',
+                'bar', 'fma', 'neg', 'stackrestore', 'vmax4',
+                'barrier', 'fns', 'not', 'stacksave', 'vmin',
+                'bfe', 'getctarank', 'or', 'stmatrix', 'vmin2',
+                'bfi', 'griddepcontrol', 'pmevent', 'sub', 'vmin4',
+                'bfind', 'isspacep', 'popc', 'subc', 'vote',
+                'bmsk', 'istypep', 'prefetch', 'suld', 'vset',
+                'bra', 'ld', 'prefetchu', 'suq', 'vset2',
+                'brev', 'ldmatrix', 'prmt', 'sured', 'vset4',
+                'brkpt', 'ldu', 'rcp', 'sust', 'vshl',
+                'brx', 'lg2', 'red', 'szext', 'vshr',
+                'call', 'lop3', 'redux', 'tanh', 'vsub',
+                'clz', 'mad', 'rem', 'testp', 'vsub2',
+                'cnot', 'mad24', 'ret', 'tex', 'vsub4',
+                'copysign', 'madc', 'rsqrt', 'tld4', 'wgmma',
+                'cos', 'mapa', 'sad', 'trap', 'wmma',
+                'cp', 'match', 'selp', 'txq', 'xor',
+                'createpolicy', 'max', 'set', 'vabsdiff', 'cvt',
+                'mbarrier', 'setmaxnreg', 'vabsdiff2', 'cvta',
+                'membar', 'setp', 'vabsdiff4')), Keyword),
+            # State Spaces and Suffixes
+            (words((
+                'reg', '.sreg', '.const', '.global',
+                '.local', '.param', '.shared', '.tex',
+                '.wide', '.loc'
+            )), Keyword.Pseudo),
+            # PTX Directives
+            (words((
+                '.address_size', '.explicitcluster', '.maxnreg', '.section',
+                '.alias', '.extern', '.maxntid', '.shared',
+                '.align', '.file', '.minnctapersm', '.sreg',
+                '.branchtargets', '.func', '.noreturn', '.target',
+                '.callprototype', '.global', '.param', '.tex',
+                '.calltargets', '.loc', '.pragma', '.version',
+                '.common', '.local', '.reg', '.visible',
+                '.const', '.maxclusterrank', '.reqnctapercluster', '.weak',
+                '.entry', '.maxnctapersm', '.reqntid')), Keyword.Reserved),
+            # Fundamental Types
+            (words((
+                '.s8', '.s16', '.s32', '.s64',
+                '.u8', '.u16', '.u32', '.u64',
+                '.f16', '.f16x2', '.f32', '.f64',
+                '.b8', '.b16', '.b32', '.b64',
+                '.pred'
+            )), Keyword.Type)
+        ],
+
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/python.py b/.venv/lib/python3.12/site-packages/pygments/lexers/python.py
new file mode 100644
index 0000000..b296c8d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/python.py
@@ -0,0 +1,1204 @@
+"""
+    pygments.lexers.python
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Python and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import keyword
+
+from pygments.lexer import DelegatingLexer, RegexLexer, include, \
+    bygroups, using, default, words, combined, this
+from pygments.util import get_bool_opt, shebang_matches
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Generic, Other, Error, Whitespace
+from pygments import unistring as uni
+
+__all__ = ['PythonLexer', 'PythonConsoleLexer', 'PythonTracebackLexer',
+           'Python2Lexer', 'Python2TracebackLexer',
+           'CythonLexer', 'DgLexer', 'NumPyLexer']
+
+
+class PythonLexer(RegexLexer):
+    """
+    For Python source code (version 3.x).
+
+    .. versionchanged:: 2.5
+       This is now the default ``PythonLexer``.  It is still available as the
+       alias ``Python3Lexer``.
+    """
+
+    name = 'Python'
+    url = 'https://www.python.org'
+    aliases = ['python', 'py', 'sage', 'python3', 'py3', 'bazel', 'starlark', 'pyi']
+    filenames = [
+        '*.py',
+        '*.pyw',
+        # Type stubs
+        '*.pyi',
+        # Jython
+        '*.jy',
+        # Sage
+        '*.sage',
+        # SCons
+        '*.sc',
+        'SConstruct',
+        'SConscript',
+        # Skylark/Starlark (used by Bazel, Buck, and Pants)
+        '*.bzl',
+        'BUCK',
+        'BUILD',
+        'BUILD.bazel',
+        'WORKSPACE',
+        # Twisted Application infrastructure
+        '*.tac',
+        # Execubot level format
+        '*.pye',
+    ]
+    mimetypes = ['text/x-python', 'application/x-python',
+                 'text/x-python3', 'application/x-python3']
+    version_added = '0.10'
+
+    uni_name = f"[{uni.xid_start}][{uni.xid_continue}]*"
+
+    def innerstring_rules(ttype):
+        return [
+            # the old style '%s' % (...) string formatting (still valid in Py3)
+            (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
+             '[hlL]?[E-GXc-giorsaux%]', String.Interpol),
+            # the new style '{}'.format(...) string formatting
+            (r'\{'
+             r'((\w+)((\.\w+)|(\[[^\]]+\]))*)?'  # field name
+             r'(\![sra])?'                       # conversion
+             r'(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?'
+             r'\}', String.Interpol),
+
+            # backslashes, quotes and formatting signs must be parsed one at a time
+            (r'[^\\\'"%{\n]+', ttype),
+            (r'[\'"\\]', ttype),
+            # unhandled string formatting sign
+            (r'%|(\{{1,2})', ttype)
+            # newlines are an error (use "nl" state)
+        ]
+
+    def fstring_rules(ttype):
+        return [
+            # Assuming that a '}' is the closing brace after format specifier.
+            # Sadly, this means that we won't detect syntax error. But it's
+            # more important to parse correct syntax correctly, than to
+            # highlight invalid syntax.
+            (r'\}', String.Interpol),
+            (r'\{', String.Interpol, 'expr-inside-fstring'),
+            # backslashes, quotes and formatting signs must be parsed one at a time
+            (r'[^\\\'"{}\n]+', ttype),
+            (r'[\'"\\]', ttype),
+            # newlines are an error (use "nl" state)
+        ]
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")',
+             bygroups(Whitespace, String.Affix, String.Doc)),
+            (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')",
+             bygroups(Whitespace, String.Affix, String.Doc)),
+            (r'\A#!.+$', Comment.Hashbang),
+            (r'#.*$', Comment.Single),
+            (r'\\\n', Text),
+            (r'\\', Text),
+            include('keywords'),
+            include('soft-keywords'),
+            (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'),
+            (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'),
+            (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
+             'fromimport'),
+            (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
+             'import'),
+            include('expr'),
+        ],
+        'expr': [
+            # raw f-strings and t-strings
+            ('(?i)(r[ft]|[ft]r)(""")',
+             bygroups(String.Affix, String.Double),
+             combined('rfstringescape', 'tdqf')),
+            ("(?i)(r[ft]|[ft]r)(''')",
+             bygroups(String.Affix, String.Single),
+             combined('rfstringescape', 'tsqf')),
+            ('(?i)(r[ft]|[ft]r)(")',
+             bygroups(String.Affix, String.Double),
+             combined('rfstringescape', 'dqf')),
+            ("(?i)(r[ft]|[ft]r)(')",
+             bygroups(String.Affix, String.Single),
+             combined('rfstringescape', 'sqf')),
+            # non-raw f-strings and t-strings
+            ('([fFtT])(""")', bygroups(String.Affix, String.Double),
+             combined('fstringescape', 'tdqf')),
+            ("([fFtT])(''')", bygroups(String.Affix, String.Single),
+             combined('fstringescape', 'tsqf')),
+            ('([fFtT])(")', bygroups(String.Affix, String.Double),
+             combined('fstringescape', 'dqf')),
+            ("([fFtT])(')", bygroups(String.Affix, String.Single),
+             combined('fstringescape', 'sqf')),
+            # raw bytes and strings
+            ('(?i)(rb|br|r)(""")',
+             bygroups(String.Affix, String.Double), 'tdqs'),
+            ("(?i)(rb|br|r)(''')",
+             bygroups(String.Affix, String.Single), 'tsqs'),
+            ('(?i)(rb|br|r)(")',
+             bygroups(String.Affix, String.Double), 'dqs'),
+            ("(?i)(rb|br|r)(')",
+             bygroups(String.Affix, String.Single), 'sqs'),
+            # non-raw strings
+            ('([uU]?)(""")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'tdqs')),
+            ("([uU]?)(''')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'tsqs')),
+            ('([uU]?)(")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'dqs')),
+            ("([uU]?)(')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'sqs')),
+            # non-raw bytes
+            ('([bB])(""")', bygroups(String.Affix, String.Double),
+             combined('bytesescape', 'tdqs')),
+            ("([bB])(''')", bygroups(String.Affix, String.Single),
+             combined('bytesescape', 'tsqs')),
+            ('([bB])(")', bygroups(String.Affix, String.Double),
+             combined('bytesescape', 'dqs')),
+            ("([bB])(')", bygroups(String.Affix, String.Single),
+             combined('bytesescape', 'sqs')),
+
+            (r'[^\S\n]+', Text),
+            include('numbers'),
+            (r'!=|==|<<|>>|:=|[-~+/*%=<>&^|.]', Operator),
+            (r'[]{}:(),;[]', Punctuation),
+            (r'(in|is|and|or|not)\b', Operator.Word),
+            include('expr-keywords'),
+            include('builtins'),
+            include('magicfuncs'),
+            include('magicvars'),
+            include('name'),
+        ],
+        'expr-inside-fstring': [
+            (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
+            # without format specifier
+            (r'(=\s*)?'         # debug (https://bugs.python.org/issue36817)
+             r'(\![sraf])?'     # conversion
+             r'\}', String.Interpol, '#pop'),
+            # with format specifier
+            # we'll catch the remaining '}' in the outer scope
+            (r'(=\s*)?'         # debug (https://bugs.python.org/issue36817)
+             r'(\![sraf])?'     # conversion
+             r':', String.Interpol, '#pop'),
+            (r'\s+', Whitespace),  # allow new lines
+            include('expr'),
+        ],
+        'expr-inside-fstring-inner': [
+            (r'[{([]', Punctuation, 'expr-inside-fstring-inner'),
+            (r'[])}]', Punctuation, '#pop'),
+            (r'\s+', Whitespace),  # allow new lines
+            include('expr'),
+        ],
+        'expr-keywords': [
+            # Based on https://docs.python.org/3/reference/expressions.html
+            (words((
+                'async for', 'await', 'else', 'for', 'if', 'lambda',
+                'yield', 'yield from'), suffix=r'\b'),
+             Keyword),
+            (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant),
+        ],
+        'keywords': [
+            (words((
+                'assert', 'async', 'await', 'break', 'continue', 'del', 'elif',
+                'else', 'except', 'finally', 'for', 'global', 'if', 'lambda',
+                'pass', 'raise', 'nonlocal', 'return', 'try', 'while', 'yield',
+                'yield from', 'as', 'with'), suffix=r'\b'),
+             Keyword),
+            (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant),
+        ],
+        'soft-keywords': [
+            # `match`, `case` and `_` soft keywords
+            (r'(^[ \t]*)'              # at beginning of line + possible indentation
+             r'(match|case)\b'         # a possible keyword
+             r'(?![ \t]*(?:'           # not followed by...
+             r'[:,;=^&|@~)\]}]|(?:' +  # characters and keywords that mean this isn't
+                                       # pattern matching (but None/True/False is ok)
+             r'|'.join(k for k in keyword.kwlist if k[0].islower()) + r')\b))',
+             bygroups(Text, Keyword), 'soft-keywords-inner'),
+        ],
+        'soft-keywords-inner': [
+            # optional `_` keyword
+            (r'(\s+)([^\n_]*)(_\b)', bygroups(Whitespace, using(this), Keyword)),
+            default('#pop')
+        ],
+        'builtins': [
+            (words((
+                '__import__', 'abs', 'aiter', 'all', 'any', 'bin', 'bool', 'bytearray',
+                'breakpoint', 'bytes', 'callable', 'chr', 'classmethod', 'compile',
+                'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
+                'filter', 'float', 'format', 'frozenset', 'getattr', 'globals',
+                'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'isinstance',
+                'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max',
+                'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow',
+                'print', 'property', 'range', 'repr', 'reversed', 'round', 'set',
+                'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
+                'tuple', 'type', 'vars', 'zip'), prefix=r'(?>|[-~+/*%=<>&^|.]', Operator),
+            include('keywords'),
+            (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'funcname'),
+            (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Whitespace), 'classname'),
+            (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
+             'fromimport'),
+            (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Whitespace),
+             'import'),
+            include('builtins'),
+            include('magicfuncs'),
+            include('magicvars'),
+            include('backtick'),
+            ('([rR]|[uUbB][rR]|[rR][uUbB])(""")',
+             bygroups(String.Affix, String.Double), 'tdqs'),
+            ("([rR]|[uUbB][rR]|[rR][uUbB])(''')",
+             bygroups(String.Affix, String.Single), 'tsqs'),
+            ('([rR]|[uUbB][rR]|[rR][uUbB])(")',
+             bygroups(String.Affix, String.Double), 'dqs'),
+            ("([rR]|[uUbB][rR]|[rR][uUbB])(')",
+             bygroups(String.Affix, String.Single), 'sqs'),
+            ('([uUbB]?)(""")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'tdqs')),
+            ("([uUbB]?)(''')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'tsqs')),
+            ('([uUbB]?)(")', bygroups(String.Affix, String.Double),
+             combined('stringescape', 'dqs')),
+            ("([uUbB]?)(')", bygroups(String.Affix, String.Single),
+             combined('stringescape', 'sqs')),
+            include('name'),
+            include('numbers'),
+        ],
+        'keywords': [
+            (words((
+                'assert', 'break', 'continue', 'del', 'elif', 'else', 'except',
+                'exec', 'finally', 'for', 'global', 'if', 'lambda', 'pass',
+                'print', 'raise', 'return', 'try', 'while', 'yield',
+                'yield from', 'as', 'with'), suffix=r'\b'),
+             Keyword),
+        ],
+        'builtins': [
+            (words((
+                '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin',
+                'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
+                'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod',
+                'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float',
+                'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id',
+                'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len',
+                'list', 'locals', 'long', 'map', 'max', 'min', 'next', 'object',
+                'oct', 'open', 'ord', 'pow', 'property', 'range', 'raw_input', 'reduce',
+                'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
+                'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type',
+                'unichr', 'unicode', 'vars', 'xrange', 'zip'),
+                prefix=r'(?>> )(.*\n)', bygroups(Generic.Prompt, Other.Code), 'continuations'),
+            # This happens, e.g., when tracebacks are embedded in documentation;
+            # trailing whitespaces are often stripped in such contexts.
+            (r'(>>>)(\n)', bygroups(Generic.Prompt, Whitespace)),
+            (r'(\^C)?Traceback \(most recent call last\):\n', Other.Traceback, 'traceback'),
+            # SyntaxError starts with this
+            (r'  File "[^"]+", line \d+', Other.Traceback, 'traceback'),
+            (r'.*\n', Generic.Output),
+        ],
+        'continuations': [
+            (r'(\.\.\. )(.*\n)', bygroups(Generic.Prompt, Other.Code)),
+            # See above.
+            (r'(\.\.\.)(\n)', bygroups(Generic.Prompt, Whitespace)),
+            default('#pop'),
+        ],
+        'traceback': [
+            # As soon as we see a traceback, consume everything until the next
+            # >>> prompt.
+            (r'(?=>>>( |$))', Text, '#pop'),
+            (r'(KeyboardInterrupt)(\n)', bygroups(Name.Class, Whitespace)),
+            (r'.*\n', Other.Traceback),
+        ],
+    }
+
+
+class PythonConsoleLexer(DelegatingLexer):
+    """
+    For Python console output or doctests, such as:
+
+    .. sourcecode:: pycon
+
+        >>> a = 'foo'
+        >>> print(a)
+        foo
+        >>> 1 / 0
+        Traceback (most recent call last):
+          File "", line 1, in 
+        ZeroDivisionError: integer division or modulo by zero
+
+    Additional options:
+
+    `python3`
+        Use Python 3 lexer for code.  Default is ``True``.
+
+        .. versionadded:: 1.0
+        .. versionchanged:: 2.5
+           Now defaults to ``True``.
+    """
+
+    name = 'Python console session'
+    aliases = ['pycon', 'python-console']
+    mimetypes = ['text/x-python-doctest']
+    url = 'https://python.org'
+    version_added = ''
+
+    def __init__(self, **options):
+        python3 = get_bool_opt(options, 'python3', True)
+        if python3:
+            pylexer = PythonLexer
+            tblexer = PythonTracebackLexer
+        else:
+            pylexer = Python2Lexer
+            tblexer = Python2TracebackLexer
+        # We have two auxiliary lexers. Use DelegatingLexer twice with
+        # different tokens.  TODO: DelegatingLexer should support this
+        # directly, by accepting a tuplet of auxiliary lexers and a tuple of
+        # distinguishing tokens. Then we wouldn't need this intermediary
+        # class.
+        class _ReplaceInnerCode(DelegatingLexer):
+            def __init__(self, **options):
+                super().__init__(pylexer, _PythonConsoleLexerBase, Other.Code, **options)
+        super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options)
+
+
+class PythonTracebackLexer(RegexLexer):
+    """
+    For Python 3.x tracebacks, with support for chained exceptions.
+
+    .. versionchanged:: 2.5
+       This is now the default ``PythonTracebackLexer``.  It is still available
+       as the alias ``Python3TracebackLexer``.
+    """
+
+    name = 'Python Traceback'
+    aliases = ['pytb', 'py3tb']
+    filenames = ['*.pytb', '*.py3tb']
+    mimetypes = ['text/x-python-traceback', 'text/x-python3-traceback']
+    url = 'https://python.org'
+    version_added = '1.0'
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'^(\^C)?Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'),
+            (r'^During handling of the above exception, another '
+             r'exception occurred:\n\n', Generic.Traceback),
+            (r'^The above exception was the direct cause of the '
+             r'following exception:\n\n', Generic.Traceback),
+            (r'^(?=  File "[^"]+", line \d+)', Generic.Traceback, 'intb'),
+            (r'^.*\n', Other),
+        ],
+        'intb': [
+            (r'^(  File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
+             bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
+            (r'^(  File )("[^"]+")(, line )(\d+)(\n)',
+             bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
+            (r'^(    )(.+)(\n)',
+             bygroups(Whitespace, using(PythonLexer), Whitespace), 'markers'),
+            (r'^([ \t]*)(\.\.\.)(\n)',
+             bygroups(Whitespace, Comment, Whitespace)),  # for doctests...
+            (r'^([^:]+)(: )(.+)(\n)',
+             bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
+            (r'^([a-zA-Z_][\w.]*)(:?\n)',
+             bygroups(Generic.Error, Whitespace), '#pop'),
+            default('#pop'),
+        ],
+        'markers': [
+            # Either `PEP 657 `
+            # error locations in Python 3.11+, or single-caret markers
+            # for syntax errors before that.
+            (r'^( {4,})([~^]+)(\n)',
+             bygroups(Whitespace, Punctuation.Marker, Whitespace),
+             '#pop'),
+            default('#pop'),
+        ],
+    }
+
+
+Python3TracebackLexer = PythonTracebackLexer
+
+
+class Python2TracebackLexer(RegexLexer):
+    """
+    For Python tracebacks.
+
+    .. versionchanged:: 2.5
+       This class has been renamed from ``PythonTracebackLexer``.
+       ``PythonTracebackLexer`` now refers to the Python 3 variant.
+    """
+
+    name = 'Python 2.x Traceback'
+    aliases = ['py2tb']
+    filenames = ['*.py2tb']
+    mimetypes = ['text/x-python2-traceback']
+    url = 'https://python.org'
+    version_added = '0.7'
+
+    tokens = {
+        'root': [
+            # Cover both (most recent call last) and (innermost last)
+            # The optional ^C allows us to catch keyboard interrupt signals.
+            (r'^(\^C)?(Traceback.*\n)',
+             bygroups(Text, Generic.Traceback), 'intb'),
+            # SyntaxError starts with this.
+            (r'^(?=  File "[^"]+", line \d+)', Generic.Traceback, 'intb'),
+            (r'^.*\n', Other),
+        ],
+        'intb': [
+            (r'^(  File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)',
+             bygroups(Text, Name.Builtin, Text, Number, Text, Name, Whitespace)),
+            (r'^(  File )("[^"]+")(, line )(\d+)(\n)',
+             bygroups(Text, Name.Builtin, Text, Number, Whitespace)),
+            (r'^(    )(.+)(\n)',
+             bygroups(Text, using(Python2Lexer), Whitespace), 'marker'),
+            (r'^([ \t]*)(\.\.\.)(\n)',
+             bygroups(Text, Comment, Whitespace)),  # for doctests...
+            (r'^([^:]+)(: )(.+)(\n)',
+             bygroups(Generic.Error, Text, Name, Whitespace), '#pop'),
+            (r'^([a-zA-Z_]\w*)(:?\n)',
+             bygroups(Generic.Error, Whitespace), '#pop')
+        ],
+        'marker': [
+            # For syntax errors.
+            (r'( {4,})(\^)', bygroups(Text, Punctuation.Marker), '#pop'),
+            default('#pop'),
+        ],
+    }
+
+
+class CythonLexer(RegexLexer):
+    """
+    For Pyrex and Cython source code.
+    """
+
+    name = 'Cython'
+    url = 'https://cython.org'
+    aliases = ['cython', 'pyx', 'pyrex']
+    filenames = ['*.pyx', '*.pxd', '*.pxi']
+    mimetypes = ['text/x-cython', 'application/x-cython']
+    version_added = '1.1'
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'^(\s*)("""(?:.|\n)*?""")', bygroups(Whitespace, String.Doc)),
+            (r"^(\s*)('''(?:.|\n)*?''')", bygroups(Whitespace, String.Doc)),
+            (r'[^\S\n]+', Text),
+            (r'#.*$', Comment),
+            (r'[]{}:(),;[]', Punctuation),
+            (r'\\\n', Whitespace),
+            (r'\\', Text),
+            (r'(in|is|and|or|not)\b', Operator.Word),
+            (r'(<)([a-zA-Z0-9.?]+)(>)',
+             bygroups(Punctuation, Keyword.Type, Punctuation)),
+            (r'!=|==|<<|>>|[-~+/*%=<>&^|.?]', Operator),
+            (r'(from)(\d+)(<=)(\s+)(<)(\d+)(:)',
+             bygroups(Keyword, Number.Integer, Operator, Whitespace, Operator,
+                      Name, Punctuation)),
+            include('keywords'),
+            (r'(def|property)(\s+)', bygroups(Keyword, Whitespace), 'funcname'),
+            (r'(cp?def)(\s+)', bygroups(Keyword, Whitespace), 'cdef'),
+            # (should actually start a block with only cdefs)
+            (r'(cdef)(:)', bygroups(Keyword, Punctuation)),
+            (r'(class|cppclass|struct)(\s+)', bygroups(Keyword, Whitespace), 'classname'),
+            (r'(from)(\s+)', bygroups(Keyword, Whitespace), 'fromimport'),
+            (r'(c?import)(\s+)', bygroups(Keyword, Whitespace), 'import'),
+            include('builtins'),
+            include('backtick'),
+            ('(?:[rR]|[uU][rR]|[rR][uU])"""', String, 'tdqs'),
+            ("(?:[rR]|[uU][rR]|[rR][uU])'''", String, 'tsqs'),
+            ('(?:[rR]|[uU][rR]|[rR][uU])"', String, 'dqs'),
+            ("(?:[rR]|[uU][rR]|[rR][uU])'", String, 'sqs'),
+            ('[uU]?"""', String, combined('stringescape', 'tdqs')),
+            ("[uU]?'''", String, combined('stringescape', 'tsqs')),
+            ('[uU]?"', String, combined('stringescape', 'dqs')),
+            ("[uU]?'", String, combined('stringescape', 'sqs')),
+            include('name'),
+            include('numbers'),
+        ],
+        'keywords': [
+            (words((
+                'assert', 'async', 'await', 'break', 'by', 'continue', 'ctypedef', 'del',
+                'elif', 'else', 'except', 'except?', 'exec', 'finally', 'for', 'fused', 'gil',
+                'global', 'if', 'include', 'lambda', 'namespace', 'new', 'noexcept','nogil',
+                'pass', 'print', 'raise', 'return', 'try', 'while', 'yield', 'as', 'with'),
+             suffix=r'\b'),
+             Keyword),
+             (words(('True', 'False', 'None', 'NULL'), suffix=r'\b'), Keyword.Constant),
+            (r'(DEF|IF|ELIF|ELSE)\b', Comment.Preproc),
+        ],
+        'builtins': [
+            (words((
+                '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bint',
+                'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'char', 'chr',
+                'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'delattr',
+                'dict', 'dir', 'divmod', 'double', 'enumerate', 'eval', 'execfile', 'exit',
+                'file', 'filter', 'float', 'frozenset', 'getattr', 'globals',
+                'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance',
+                'issubclass', 'iter', 'len', 'list', 'locals', 'long', 'map', 'max',
+                'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'property',
+                'Py_ssize_t', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed',
+                'round', 'set', 'setattr', 'size_t', 'slice', 'sorted', 'staticmethod',
+                'ssize_t', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode',
+                'unsigned', 'vars', 'xrange', 'zip'), prefix=r'(??/\\:']?:)(\s*)(\{)",
+             bygroups(Name.Function, Whitespace, Operator, Whitespace, Punctuation),
+             "functions"),
+            # Variable Names
+            (r"([.]?[a-zA-Z][\w.]*)(\s*)([-.~=!@#$%^&*_+|,<>?/\\:']?:)",
+             bygroups(Name.Variable, Whitespace, Operator)),
+            # Functions
+            (r"\{", Punctuation, "functions"),
+            # Parentheses
+            (r"\(", Punctuation, "parentheses"),
+            # Brackets
+            (r"\[", Punctuation, "brackets"),
+            # Errors
+            (r"'`([a-zA-Z][\w.]*)?", Name.Exception),
+            # File Symbols
+            (r"`:([a-zA-Z/][\w./]*)?", String.Symbol),
+            # Symbols
+            (r"`([a-zA-Z][\w.]*)?", String.Symbol),
+            # Numbers
+            include("numbers"),
+            # Variable Names
+            (r"[a-zA-Z][\w.]*", Name),
+            # Operators
+            (r"[-=+*#$%@!~^&:.,<>'\\|/?_]", Operator),
+            # Punctuation
+            (r";", Punctuation),
+        ],
+        "functions": [
+            include("root"),
+            (r"\}", Punctuation, "#pop"),
+        ],
+        "parentheses": [
+            include("root"),
+            (r"\)", Punctuation, "#pop"),
+        ],
+        "brackets": [
+            include("root"),
+            (r"\]", Punctuation, "#pop"),
+        ],
+        "numbers": [
+            # Binary Values
+            (r"[01]+b", Number.Bin),
+            # Nulls/Infinities
+            (r"0[nNwW][cefghijmndzuvtp]?", Number),
+            # Timestamps
+            ((r"(?:[0-9]{4}[.][0-9]{2}[.][0-9]{2}|[0-9]+)"
+              "D(?:[0-9](?:[0-9](?::[0-9]{2}"
+              "(?::[0-9]{2}(?:[.][0-9]*)?)?)?)?)?"), Literal.Date),
+            # Datetimes
+            ((r"[0-9]{4}[.][0-9]{2}"
+              "(?:m|[.][0-9]{2}(?:T(?:[0-9]{2}:[0-9]{2}"
+              "(?::[0-9]{2}(?:[.][0-9]*)?)?)?)?)"), Literal.Date),
+            # Times
+            (r"[0-9]{2}:[0-9]{2}(?::[0-9]{2}(?:[.][0-9]{1,3})?)?",
+             Literal.Date),
+            # GUIDs
+            (r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
+             Number.Hex),
+            # Byte Vectors
+            (r"0x[0-9a-fA-F]+", Number.Hex),
+            # Floats
+            (r"([0-9]*[.]?[0-9]+|[0-9]+[.]?[0-9]*)[eE][+-]?[0-9]+[ef]?",
+             Number.Float),
+            (r"([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)[ef]?", Number.Float),
+            (r"[0-9]+[ef]", Number.Float),
+            # Characters
+            (r"[0-9]+c", Number),
+            # Integers
+            (r"[0-9]+[ihtuv]", Number.Integer),
+            # Long Integers
+            (r"[0-9]+[jnp]?", Number.Integer.Long),
+        ],
+        "comments": [
+            (r"[^\\]+", Comment.Multiline),
+            (r"^\\", Comment.Multiline, "#pop"),
+            (r"\\", Comment.Multiline),
+        ],
+        "strings": [
+            (r'[^"\\]+', String.Double),
+            (r"\\.", String.Escape),
+            (r'"', String.Double, "#pop"),
+        ],
+    }
+
+
+class QLexer(KLexer):
+    """
+    For `Q `_ source code.
+    """
+
+    name = "Q"
+    aliases = ["q"]
+    filenames = ["*.q"]
+    version_added = '2.12'
+
+    tokens = {
+        "root": [
+            (words(("aj", "aj0", "ajf", "ajf0", "all", "and", "any", "asc",
+                    "asof", "attr", "avgs", "ceiling", "cols", "count", "cross",
+                    "csv", "cut", "deltas", "desc", "differ", "distinct", "dsave",
+                    "each", "ej", "ema", "eval", "except", "fby", "fills", "first",
+                    "fkeys", "flip", "floor", "get", "group", "gtime", "hclose",
+                    "hcount", "hdel", "hsym", "iasc", "idesc", "ij", "ijf",
+                    "inter", "inv", "key", "keys", "lj", "ljf", "load", "lower",
+                    "lsq", "ltime", "ltrim", "mavg", "maxs", "mcount", "md5",
+                    "mdev", "med", "meta", "mins", "mmax", "mmin", "mmu", "mod",
+                    "msum", "neg", "next", "not", "null", "or", "over", "parse",
+                    "peach", "pj", "prds", "prior", "prev", "rand", "rank", "ratios",
+                    "raze", "read0", "read1", "reciprocal", "reval", "reverse",
+                    "rload", "rotate", "rsave", "rtrim", "save", "scan", "scov",
+                    "sdev", "set", "show", "signum", "ssr", "string", "sublist",
+                    "sums", "sv", "svar", "system", "tables", "til", "trim", "txf",
+                    "type", "uj", "ujf", "ungroup", "union", "upper", "upsert",
+                    "value", "view", "views", "vs", "where", "wj", "wj1", "ww",
+                    "xasc", "xbar", "xcol", "xcols", "xdesc", "xgroup", "xkey",
+                    "xlog", "xprev", "xrank"),
+                    suffix=r"\b"), Name.Builtin,
+            ),
+            inherit,
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/qlik.py b/.venv/lib/python3.12/site-packages/pygments/lexers/qlik.py
new file mode 100644
index 0000000..6972ed9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/qlik.py
@@ -0,0 +1,117 @@
+"""
+    pygments.lexers.qlik
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for the qlik scripting language
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, words
+from pygments.token import Comment, Keyword, Name, Number, Operator, \
+    Punctuation, String, Text
+from pygments.lexers._qlik_builtins import OPERATORS_LIST, STATEMENT_LIST, \
+    SCRIPT_FUNCTIONS, CONSTANT_LIST
+
+__all__ = ["QlikLexer"]
+
+
+class QlikLexer(RegexLexer):
+    """
+    Lexer for qlik code, including .qvs files
+    """
+
+    name = "Qlik"
+    aliases = ["qlik", "qlikview", "qliksense", "qlikscript"]
+    filenames = ["*.qvs", "*.qvw"]
+    url = "https://qlik.com"
+    version_added = '2.12'
+
+    flags = re.IGNORECASE
+
+    tokens = {
+        # Handle multi-line comments
+        "comment": [
+            (r"\*/", Comment.Multiline, "#pop"),
+            (r"[^*]+", Comment.Multiline),
+        ],
+        # Handle numbers
+        "numerics": [
+            (r"\b\d+\.\d+(e\d+)?[fd]?\b", Number.Float),
+            (r"\b\d+\b", Number.Integer),
+        ],
+        # Handle variable names in things
+        "interp": [
+            (
+                r"(\$\()(\w+)(\))",
+                bygroups(String.Interpol, Name.Variable, String.Interpol),
+            ),
+        ],
+        # Handle strings
+        "string": [
+            (r"'", String, "#pop"),
+            include("interp"),
+            (r"[^'$]+", String),
+            (r"\$", String),
+        ],
+        #
+        "assignment": [
+            (r";", Punctuation, "#pop"),
+            include("root"),
+        ],
+        "field_name_quote": [
+            (r'"', String.Symbol, "#pop"),
+            include("interp"),
+            (r"[^\"$]+", String.Symbol),
+            (r"\$", String.Symbol),
+        ],
+        "field_name_bracket": [
+            (r"\]", String.Symbol, "#pop"),
+            include("interp"),
+            (r"[^\]$]+", String.Symbol),
+            (r"\$", String.Symbol),
+        ],
+        "function": [(r"\)", Punctuation, "#pop"), include("root")],
+        "root": [
+            # Whitespace and comments
+            (r"\s+", Text.Whitespace),
+            (r"/\*", Comment.Multiline, "comment"),
+            (r"//.*\n", Comment.Single),
+            # variable assignment
+            (r"(let|set)(\s+)", bygroups(Keyword.Declaration, Text.Whitespace),
+             "assignment"),
+            # Word operators
+            (words(OPERATORS_LIST["words"], prefix=r"\b", suffix=r"\b"),
+             Operator.Word),
+            # Statements
+            (words(STATEMENT_LIST, suffix=r"\b"), Keyword),
+            # Table names
+            (r"[a-z]\w*:", Keyword.Declaration),
+            # Constants
+            (words(CONSTANT_LIST, suffix=r"\b"), Keyword.Constant),
+            # Functions
+            (words(SCRIPT_FUNCTIONS, suffix=r"(?=\s*\()"), Name.Builtin,
+             "function"),
+            # interpolation - e.g. $(variableName)
+            include("interp"),
+            # Quotes denote a field/file name
+            (r'"', String.Symbol, "field_name_quote"),
+            # Square brackets denote a field/file name
+            (r"\[", String.Symbol, "field_name_bracket"),
+            # Strings
+            (r"'", String, "string"),
+            # Numbers
+            include("numerics"),
+            # Operator symbols
+            (words(OPERATORS_LIST["symbols"]), Operator),
+            # Strings denoted by single quotes
+            (r"'.+?'", String),
+            # Words as text
+            (r"\b\w+\b", Text),
+            # Basic punctuation
+            (r"[,;.()\\/]", Punctuation),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/qvt.py b/.venv/lib/python3.12/site-packages/pygments/lexers/qvt.py
new file mode 100644
index 0000000..02d6fcf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/qvt.py
@@ -0,0 +1,153 @@
+"""
+    pygments.lexers.qvt
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexer for QVT Operational language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, include, combined, default, \
+    words
+from pygments.token import Text, Comment, Operator, Keyword, Punctuation, \
+    Name, String, Number
+
+__all__ = ['QVToLexer']
+
+
+class QVToLexer(RegexLexer):
+    """
+    For the QVT Operational Mapping language.
+
+    Reference for implementing this: «Meta Object Facility (MOF) 2.0
+    Query/View/Transformation Specification», Version 1.1 - January 2011
+    (https://www.omg.org/spec/QVT/1.1/), see §8.4, «Concrete Syntax» in
+    particular.
+
+    Notable tokens assignments:
+
+    - Name.Class is assigned to the identifier following any of the following
+      keywords: metamodel, class, exception, primitive, enum, transformation
+      or library
+
+    - Name.Function is assigned to the names of mappings and queries
+
+    - Name.Builtin.Pseudo is assigned to the pre-defined variables 'this',
+      'self' and 'result'.
+    """
+    # With obvious borrowings & inspiration from the Java, Python and C lexers
+
+    name = 'QVTO'
+    aliases = ['qvto', 'qvt']
+    filenames = ['*.qvto']
+    url = 'https://www.omg.org/spec/QVT/1.1'
+    version_added = ''
+
+    tokens = {
+        'root': [
+            (r'\n', Text),
+            (r'[^\S\n]+', Text),
+            (r'(--|//)(\s*)(directive:)?(.*)$',
+             bygroups(Comment, Comment, Comment.Preproc, Comment)),
+            # Uncomment the following if you want to distinguish between
+            # '/*' and '/**', à la javadoc
+            # (r'/[*]{2}(.|\n)*?[*]/', Comment.Multiline),
+            (r'/[*](.|\n)*?[*]/', Comment.Multiline),
+            (r'\\\n', Text),
+            (r'(and|not|or|xor|##?)\b', Operator.Word),
+            (r'(:{1,2}=|[-+]=)\b', Operator.Word),
+            (r'(@|<<|>>)\b', Keyword),  # stereotypes
+            (r'!=|<>|==|=|!->|->|>=|<=|[.]{3}|[+/*%=<>&|.~]', Operator),
+            (r'[]{}:(),;[]', Punctuation),
+            (r'(true|false|unlimited|null)\b', Keyword.Constant),
+            (r'(this|self|result)\b', Name.Builtin.Pseudo),
+            (r'(var)\b', Keyword.Declaration),
+            (r'(from|import)\b', Keyword.Namespace, 'fromimport'),
+            (r'(metamodel|class|exception|primitive|enum|transformation|'
+             r'library)(\s+)(\w+)',
+             bygroups(Keyword.Word, Text, Name.Class)),
+            (r'(exception)(\s+)(\w+)',
+             bygroups(Keyword.Word, Text, Name.Exception)),
+            (r'(main)\b', Name.Function),
+            (r'(mapping|helper|query)(\s+)',
+             bygroups(Keyword.Declaration, Text), 'operation'),
+            (r'(assert)(\s+)\b', bygroups(Keyword, Text), 'assert'),
+            (r'(Bag|Collection|Dict|OrderedSet|Sequence|Set|Tuple|List)\b',
+             Keyword.Type),
+            include('keywords'),
+            ('"', String, combined('stringescape', 'dqs')),
+            ("'", String, combined('stringescape', 'sqs')),
+            include('name'),
+            include('numbers'),
+            # (r'([a-zA-Z_]\w*)(::)([a-zA-Z_]\w*)',
+            # bygroups(Text, Text, Text)),
+        ],
+
+        'fromimport': [
+            (r'(?:[ \t]|\\\n)+', Text),
+            (r'[a-zA-Z_][\w.]*', Name.Namespace),
+            default('#pop'),
+        ],
+
+        'operation': [
+            (r'::', Text),
+            (r'(.*::)([a-zA-Z_]\w*)([ \t]*)(\()',
+             bygroups(Text, Name.Function, Text, Punctuation), '#pop')
+        ],
+
+        'assert': [
+            (r'(warning|error|fatal)\b', Keyword, '#pop'),
+            default('#pop'),  # all else: go back
+        ],
+
+        'keywords': [
+            (words((
+                'abstract', 'access', 'any', 'assert', 'blackbox', 'break',
+                'case', 'collect', 'collectNested', 'collectOne', 'collectselect',
+                'collectselectOne', 'composes', 'compute', 'configuration',
+                'constructor', 'continue', 'datatype', 'default', 'derived',
+                'disjuncts', 'do', 'elif', 'else', 'end', 'endif', 'except',
+                'exists', 'extends', 'forAll', 'forEach', 'forOne', 'from', 'if',
+                'implies', 'in', 'inherits', 'init', 'inout', 'intermediate',
+                'invresolve', 'invresolveIn', 'invresolveone', 'invresolveoneIn',
+                'isUnique', 'iterate', 'late', 'let', 'literal', 'log', 'map',
+                'merges', 'modeltype', 'new', 'object', 'one', 'ordered', 'out',
+                'package', 'population', 'property', 'raise', 'readonly',
+                'references', 'refines', 'reject', 'resolve', 'resolveIn',
+                'resolveone', 'resolveoneIn', 'return', 'select', 'selectOne',
+                'sortedBy', 'static', 'switch', 'tag', 'then', 'try', 'typedef',
+                'unlimited', 'uses', 'when', 'where', 'while', 'with', 'xcollect',
+                'xmap', 'xselect'), suffix=r'\b'), Keyword),
+        ],
+
+        # There is no need to distinguish between String.Single and
+        # String.Double: 'strings' is factorised for 'dqs' and 'sqs'
+        'strings': [
+            (r'[^\\\'"\n]+', String),
+            # quotes, percents and backslashes must be parsed one at a time
+            (r'[\'"\\]', String),
+        ],
+        'stringescape': [
+            (r'\\([\\btnfr"\']|u[0-3][0-7]{2}|u[0-7]{1,2})', String.Escape)
+        ],
+        'dqs': [  # double-quoted string
+            (r'"', String, '#pop'),
+            (r'\\\\|\\"', String.Escape),
+            include('strings')
+        ],
+        'sqs': [  # single-quoted string
+            (r"'", String, '#pop'),
+            (r"\\\\|\\'", String.Escape),
+            include('strings')
+        ],
+        'name': [
+            (r'[a-zA-Z_]\w*', Name),
+        ],
+        # numbers: excerpt taken from the python lexer
+        'numbers': [
+            (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', Number.Float),
+            (r'\d+[eE][+-]?[0-9]+', Number.Float),
+            (r'\d+', Number.Integer)
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/r.py b/.venv/lib/python3.12/site-packages/pygments/lexers/r.py
new file mode 100644
index 0000000..7dcc148
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/r.py
@@ -0,0 +1,196 @@
+"""
+    pygments.lexers.r
+    ~~~~~~~~~~~~~~~~~
+
+    Lexers for the R/S languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, include, do_insertions
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Generic, Whitespace
+
+__all__ = ['RConsoleLexer', 'SLexer', 'RdLexer']
+
+
+line_re  = re.compile('.*?\n')
+
+
+class RConsoleLexer(Lexer):
+    """
+    For R console transcripts or R CMD BATCH output files.
+    """
+
+    name = 'RConsole'
+    aliases = ['rconsole', 'rout']
+    filenames = ['*.Rout']
+    url = 'https://www.r-project.org'
+    version_added = ''
+    _example = "rconsole/r-console-transcript.Rout"
+
+    def get_tokens_unprocessed(self, text):
+        slexer = SLexer(**self.options)
+
+        current_code_block = ''
+        insertions = []
+
+        for match in line_re.finditer(text):
+            line = match.group()
+            if line.startswith('>') or line.startswith('+'):
+                # Colorize the prompt as such,
+                # then put rest of line into current_code_block
+                insertions.append((len(current_code_block),
+                                   [(0, Generic.Prompt, line[:2])]))
+                current_code_block += line[2:]
+            else:
+                # We have reached a non-prompt line!
+                # If we have stored prompt lines, need to process them first.
+                if current_code_block:
+                    # Weave together the prompts and highlight code.
+                    yield from do_insertions(
+                        insertions, slexer.get_tokens_unprocessed(current_code_block))
+                    # Reset vars for next code block.
+                    current_code_block = ''
+                    insertions = []
+                # Now process the actual line itself, this is output from R.
+                yield match.start(), Generic.Output, line
+
+        # If we happen to end on a code block with nothing after it, need to
+        # process the last code block. This is neither elegant nor DRY so
+        # should be changed.
+        if current_code_block:
+            yield from do_insertions(
+                insertions, slexer.get_tokens_unprocessed(current_code_block))
+
+
+class SLexer(RegexLexer):
+    """
+    For S, S-plus, and R source code.
+    """
+
+    name = 'S'
+    aliases = ['splus', 's', 'r']
+    filenames = ['*.S', '*.R', '.Rhistory', '.Rprofile', '.Renviron']
+    mimetypes = ['text/S-plus', 'text/S', 'text/x-r-source', 'text/x-r',
+                 'text/x-R', 'text/x-r-history', 'text/x-r-profile']
+    url = 'https://www.r-project.org'
+    version_added = '0.10'
+
+    valid_name = r'`[^`\\]*(?:\\.[^`\\]*)*`|(?:[a-zA-Z]|\.[A-Za-z_.])[\w.]*|\.'
+    tokens = {
+        'comments': [
+            (r'#.*$', Comment.Single),
+        ],
+        'valid_name': [
+            (valid_name, Name),
+        ],
+        'function_name': [
+            (rf'({valid_name})\s*(?=\()', Name.Function),
+        ],
+        'punctuation': [
+            (r'\[{1,2}|\]{1,2}|\(|\)|;|,', Punctuation),
+        ],
+        'keywords': [
+            (r'(if|else|for|while|repeat|in|next|break|return|switch|function)'
+             r'(?![\w.])',
+             Keyword.Reserved),
+        ],
+        'operators': [
+            (r'<>?|-|==|<=|>=|\|>|<|>|&&?|!=|\|\|?|\?', Operator),
+            (r'\*|\+|\^|/|!|%[^%]*%|=|~|\$|@|:{1,3}', Operator),
+        ],
+        'builtin_symbols': [
+            (r'(NULL|NA(_(integer|real|complex|character)_)?|'
+             r'letters|LETTERS|Inf|TRUE|FALSE|NaN|pi|\.\.(\.|[0-9]+))'
+             r'(?![\w.])',
+             Keyword.Constant),
+            (r'(T|F)\b', Name.Builtin.Pseudo),
+        ],
+        'numbers': [
+            # hex number
+            (r'0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?', Number.Hex),
+            # decimal number
+            (r'[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+|\.)([eE][+-]?[0-9]+)?[Li]?',
+             Number),
+        ],
+        'statements': [
+            include('comments'),
+            # whitespaces
+            (r'\s+', Whitespace),
+            (r'\'', String, 'string_squote'),
+            (r'\"', String, 'string_dquote'),
+            include('builtin_symbols'),
+            include('keywords'),
+            include('function_name'),
+            include('valid_name'),
+            include('numbers'),
+            include('punctuation'),
+            include('operators'),
+        ],
+        'root': [
+            # calls:
+            include('statements'),
+            # blocks:
+            (r'\{|\}', Punctuation),
+            # (r'\{', Punctuation, 'block'),
+            (r'.', Text),
+        ],
+        # 'block': [
+        #    include('statements'),
+        #    ('\{', Punctuation, '#push'),
+        #    ('\}', Punctuation, '#pop')
+        # ],
+        'string_squote': [
+            (r'([^\'\\]|\\.)*\'', String, '#pop'),
+        ],
+        'string_dquote': [
+            (r'([^"\\]|\\.)*"', String, '#pop'),
+        ],
+    }
+
+    def analyse_text(text):
+        if re.search(r'[a-z0-9_\])\s]<-(?!-)', text):
+            return 0.11
+
+
+class RdLexer(RegexLexer):
+    """
+    Pygments Lexer for R documentation (Rd) files
+
+    This is a very minimal implementation, highlighting little more
+    than the macros. A description of Rd syntax is found in `Writing R
+    Extensions `_
+    and `Parsing Rd files `_.
+    """
+    name = 'Rd'
+    aliases = ['rd']
+    filenames = ['*.Rd']
+    mimetypes = ['text/x-r-doc']
+    url = 'http://cran.r-project.org/doc/manuals/R-exts.html'
+    version_added = '1.6'
+
+    # To account for verbatim / LaTeX-like / and R-like areas
+    # would require parsing.
+    tokens = {
+        'root': [
+            # catch escaped brackets and percent sign
+            (r'\\[\\{}%]', String.Escape),
+            # comments
+            (r'%.*$', Comment),
+            # special macros with no arguments
+            (r'\\(?:cr|l?dots|R|tab)\b', Keyword.Constant),
+            # macros
+            (r'\\[a-zA-Z]+\b', Keyword),
+            # special preprocessor macros
+            (r'^\s*#(?:ifn?def|endif).*\b', Comment.Preproc),
+            # non-escaped brackets
+            (r'[{}]', Name.Builtin),
+            # everything else
+            (r'[^\\%\n{}]+', Text),
+            (r'.', Text),
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rdf.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rdf.py
new file mode 100644
index 0000000..f2ecf7a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rdf.py
@@ -0,0 +1,468 @@
+"""
+    pygments.lexers.rdf
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for semantic web and RDF query languages and markup.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, bygroups, default
+from pygments.token import Keyword, Punctuation, String, Number, Operator, \
+    Generic, Whitespace, Name, Literal, Comment, Text
+
+__all__ = ['SparqlLexer', 'TurtleLexer', 'ShExCLexer']
+
+
+class SparqlLexer(RegexLexer):
+    """
+    Lexer for SPARQL query language.
+    """
+    name = 'SPARQL'
+    aliases = ['sparql']
+    filenames = ['*.rq', '*.sparql']
+    mimetypes = ['application/sparql-query']
+    url = 'https://www.w3.org/TR/sparql11-query'
+    version_added = '2.0'
+
+    # character group definitions ::
+
+    PN_CHARS_BASE_GRP = ('a-zA-Z'
+                         '\u00c0-\u00d6'
+                         '\u00d8-\u00f6'
+                         '\u00f8-\u02ff'
+                         '\u0370-\u037d'
+                         '\u037f-\u1fff'
+                         '\u200c-\u200d'
+                         '\u2070-\u218f'
+                         '\u2c00-\u2fef'
+                         '\u3001-\ud7ff'
+                         '\uf900-\ufdcf'
+                         '\ufdf0-\ufffd')
+
+    PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_')
+
+    PN_CHARS_GRP = (PN_CHARS_U_GRP +
+                    r'\-' +
+                    r'0-9' +
+                    '\u00b7' +
+                    '\u0300-\u036f' +
+                    '\u203f-\u2040')
+
+    HEX_GRP = '0-9A-Fa-f'
+
+    PN_LOCAL_ESC_CHARS_GRP = r' _~.\-!$&"()*+,;=/?#@%'
+
+    # terminal productions ::
+
+    PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']'
+
+    PN_CHARS_U = '[' + PN_CHARS_U_GRP + ']'
+
+    PN_CHARS = '[' + PN_CHARS_GRP + ']'
+
+    HEX = '[' + HEX_GRP + ']'
+
+    PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']'
+
+    IRIREF = r'<(?:[^<>"{}|^`\\\x00-\x20])*>'
+
+    BLANK_NODE_LABEL = '_:[0-9' + PN_CHARS_U_GRP + '](?:[' + PN_CHARS_GRP + \
+                       '.]*' + PN_CHARS + ')?'
+
+    PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?'
+
+    VARNAME = '[0-9' + PN_CHARS_U_GRP + '][' + PN_CHARS_U_GRP + \
+              '0-9\u00b7\u0300-\u036f\u203f-\u2040]*'
+
+    PERCENT = '%' + HEX + HEX
+
+    PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS
+
+    PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')'
+
+    PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' +
+                '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' +
+                PN_CHARS_GRP + ':]|' + PLX + '))?')
+
+    EXPONENT = r'[eE][+-]?\d+'
+
+    # Lexer token definitions ::
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            # keywords ::
+            (r'(?i)(select|construct|describe|ask|where|filter|group\s+by|minus|'
+             r'distinct|reduced|from\s+named|from|order\s+by|desc|asc|limit|'
+             r'offset|values|bindings|load|into|clear|drop|create|add|move|copy|'
+             r'insert\s+data|delete\s+data|delete\s+where|with|delete|insert|'
+             r'using\s+named|using|graph|default|named|all|optional|service|'
+             r'silent|bind|undef|union|not\s+in|in|as|having|to|prefix|base)\b', Keyword),
+            (r'(a)\b', Keyword),
+            # IRIs ::
+            ('(' + IRIREF + ')', Name.Label),
+            # blank nodes ::
+            ('(' + BLANK_NODE_LABEL + ')', Name.Label),
+            #  # variables ::
+            ('[?$]' + VARNAME, Name.Variable),
+            # prefixed names ::
+            (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + r')?',
+             bygroups(Name.Namespace, Punctuation, Name.Tag)),
+            # function names ::
+            (r'(?i)(str|lang|langmatches|datatype|bound|iri|uri|bnode|rand|abs|'
+             r'ceil|floor|round|concat|strlen|ucase|lcase|encode_for_uri|'
+             r'contains|strstarts|strends|strbefore|strafter|year|month|day|'
+             r'hours|minutes|seconds|timezone|tz|now|uuid|struuid|md5|sha1|sha256|sha384|'
+             r'sha512|coalesce|if|strlang|strdt|sameterm|isiri|isuri|isblank|'
+             r'isliteral|isnumeric|regex|substr|replace|exists|not\s+exists|'
+             r'count|sum|min|max|avg|sample|group_concat|separator)\b',
+             Name.Function),
+            # boolean literals ::
+            (r'(true|false)', Keyword.Constant),
+            # double literals ::
+            (r'[+\-]?(\d+\.\d*' + EXPONENT + r'|\.?\d+' + EXPONENT + ')', Number.Float),
+            # decimal literals ::
+            (r'[+\-]?(\d+\.\d*|\.\d+)', Number.Float),
+            # integer literals ::
+            (r'[+\-]?\d+', Number.Integer),
+            # operators ::
+            (r'(\|\||&&|=|\*|\-|\+|/|!=|<=|>=|!|<|>)', Operator),
+            # punctuation characters ::
+            (r'[(){}.;,:^\[\]]', Punctuation),
+            # line comments ::
+            (r'#[^\n]*', Comment),
+            # strings ::
+            (r'"""', String, 'triple-double-quoted-string'),
+            (r'"', String, 'single-double-quoted-string'),
+            (r"'''", String, 'triple-single-quoted-string'),
+            (r"'", String, 'single-single-quoted-string'),
+        ],
+        'triple-double-quoted-string': [
+            (r'"""', String, 'end-of-string'),
+            (r'[^\\]+', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'single-double-quoted-string': [
+            (r'"', String, 'end-of-string'),
+            (r'[^"\\\n]+', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'triple-single-quoted-string': [
+            (r"'''", String, 'end-of-string'),
+            (r'[^\\]+', String),
+            (r'\\', String.Escape, 'string-escape'),
+        ],
+        'single-single-quoted-string': [
+            (r"'", String, 'end-of-string'),
+            (r"[^'\\\n]+", String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'string-escape': [
+            (r'u' + HEX + '{4}', String.Escape, '#pop'),
+            (r'U' + HEX + '{8}', String.Escape, '#pop'),
+            (r'.', String.Escape, '#pop'),
+        ],
+        'end-of-string': [
+            (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)',
+             bygroups(Operator, Name.Function), '#pop:2'),
+            (r'\^\^', Operator, '#pop:2'),
+            default('#pop:2'),
+        ],
+    }
+
+
+class TurtleLexer(RegexLexer):
+    """
+    Lexer for Turtle data language.
+    """
+    name = 'Turtle'
+    aliases = ['turtle']
+    filenames = ['*.ttl']
+    mimetypes = ['text/turtle', 'application/x-turtle']
+    url = 'https://www.w3.org/TR/turtle'
+    version_added = '2.1'
+
+    # character group definitions ::
+    PN_CHARS_BASE_GRP = ('a-zA-Z'
+                         '\u00c0-\u00d6'
+                         '\u00d8-\u00f6'
+                         '\u00f8-\u02ff'
+                         '\u0370-\u037d'
+                         '\u037f-\u1fff'
+                         '\u200c-\u200d'
+                         '\u2070-\u218f'
+                         '\u2c00-\u2fef'
+                         '\u3001-\ud7ff'
+                         '\uf900-\ufdcf'
+                         '\ufdf0-\ufffd')
+
+    PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_')
+
+    PN_CHARS_GRP = (PN_CHARS_U_GRP +
+                    r'\-' +
+                    r'0-9' +
+                    '\u00b7' +
+                    '\u0300-\u036f' +
+                    '\u203f-\u2040')
+
+    PN_CHARS = '[' + PN_CHARS_GRP + ']'
+
+    PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']'
+
+    PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?'
+
+    HEX_GRP = '0-9A-Fa-f'
+
+    HEX = '[' + HEX_GRP + ']'
+
+    PERCENT = '%' + HEX + HEX
+
+    PN_LOCAL_ESC_CHARS_GRP = r' _~.\-!$&"()*+,;=/?#@%'
+
+    PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']'
+
+    PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS
+
+    PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')'
+
+    PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' +
+                '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' +
+                PN_CHARS_GRP + ':]|' + PLX + '))?')
+
+    patterns = {
+        'PNAME_NS': r'((?:[a-zA-Z][\w-]*)?\:)',  # Simplified character range
+        'IRIREF': r'(<[^<>"{}|^`\\\x00-\x20]*>)'
+    }
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+
+            # Base / prefix
+            (r'(@base|BASE)(\s+){IRIREF}(\s*)(\.?)'.format(**patterns),
+             bygroups(Keyword, Whitespace, Name.Variable, Whitespace,
+                      Punctuation)),
+            (r'(@prefix|PREFIX)(\s+){PNAME_NS}(\s+){IRIREF}(\s*)(\.?)'.format(**patterns),
+             bygroups(Keyword, Whitespace, Name.Namespace, Whitespace,
+                      Name.Variable, Whitespace, Punctuation)),
+
+            # The shorthand predicate 'a'
+            (r'(?<=\s)a(?=\s)', Keyword.Type),
+
+            # IRIREF
+            (r'{IRIREF}'.format(**patterns), Name.Variable),
+
+            # PrefixedName
+            (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + r')?',
+             bygroups(Name.Namespace, Punctuation, Name.Tag)),
+
+            # BlankNodeLabel
+            (r'(_)(:)([' + PN_CHARS_U_GRP + r'0-9]([' + PN_CHARS_GRP + r'.]*' + PN_CHARS + ')?)',
+             bygroups(Name.Namespace, Punctuation, Name.Tag)),
+
+            # Comment
+            (r'#([^\n]+|$)', Comment),
+
+            (r'\b(true|false)\b', Literal),
+            (r'[+\-]?\d*\.\d+', Number.Float),
+            (r'[+\-]?\d*(:?\.\d+)?E[+\-]?\d+', Number.Float),
+            (r'[+\-]?\d+', Number.Integer),
+            (r'[\[\](){}.;,:^]', Punctuation),
+
+            (r'"""', String, 'triple-double-quoted-string'),
+            (r'"', String, 'single-double-quoted-string'),
+            (r"'''", String, 'triple-single-quoted-string'),
+            (r"'", String, 'single-single-quoted-string'),
+        ],
+        'triple-double-quoted-string': [
+            (r'"""', String, 'end-of-string'),
+            (r'[^\\]+(?=""")', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'single-double-quoted-string': [
+            (r'"', String, 'end-of-string'),
+            (r'[^"\\\n]+', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'triple-single-quoted-string': [
+            (r"'''", String, 'end-of-string'),
+            (r"[^\\]+(?=''')", String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'single-single-quoted-string': [
+            (r"'", String, 'end-of-string'),
+            (r"[^'\\\n]+", String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'string-escape': [
+            (r'.', String, '#pop'),
+        ],
+        'end-of-string': [
+            (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)',
+             bygroups(Operator, Generic.Emph), '#pop:2'),
+
+            (r'(\^\^){IRIREF}'.format(**patterns), bygroups(Operator, Generic.Emph), '#pop:2'),
+
+            default('#pop:2'),
+
+        ],
+    }
+
+    # Turtle and Tera Term macro files share the same file extension
+    # but each has a recognizable and distinct syntax.
+    def analyse_text(text):
+        for t in ('@base ', 'BASE ', '@prefix ', 'PREFIX '):
+            if re.search(rf'^\s*{t}', text):
+                return 0.80
+
+
+class ShExCLexer(RegexLexer):
+    """
+    Lexer for ShExC shape expressions language syntax.
+    """
+    name = 'ShExC'
+    aliases = ['shexc', 'shex']
+    filenames = ['*.shex']
+    mimetypes = ['text/shex']
+    url = 'https://shex.io/shex-semantics/#shexc'
+    version_added = ''
+
+    # character group definitions ::
+
+    PN_CHARS_BASE_GRP = ('a-zA-Z'
+                         '\u00c0-\u00d6'
+                         '\u00d8-\u00f6'
+                         '\u00f8-\u02ff'
+                         '\u0370-\u037d'
+                         '\u037f-\u1fff'
+                         '\u200c-\u200d'
+                         '\u2070-\u218f'
+                         '\u2c00-\u2fef'
+                         '\u3001-\ud7ff'
+                         '\uf900-\ufdcf'
+                         '\ufdf0-\ufffd')
+
+    PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_')
+
+    PN_CHARS_GRP = (PN_CHARS_U_GRP +
+                    r'\-' +
+                    r'0-9' +
+                    '\u00b7' +
+                    '\u0300-\u036f' +
+                    '\u203f-\u2040')
+
+    HEX_GRP = '0-9A-Fa-f'
+
+    PN_LOCAL_ESC_CHARS_GRP = r"_~.\-!$&'()*+,;=/?#@%"
+
+    # terminal productions ::
+
+    PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']'
+
+    PN_CHARS_U = '[' + PN_CHARS_U_GRP + ']'
+
+    PN_CHARS = '[' + PN_CHARS_GRP + ']'
+
+    HEX = '[' + HEX_GRP + ']'
+
+    PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']'
+
+    UCHAR_NO_BACKSLASH = '(?:u' + HEX + '{4}|U' + HEX + '{8})'
+
+    UCHAR = r'\\' + UCHAR_NO_BACKSLASH
+
+    IRIREF = r'<(?:[^\x00-\x20<>"{}|^`\\]|' + UCHAR + ')*>'
+
+    BLANK_NODE_LABEL = '_:[0-9' + PN_CHARS_U_GRP + '](?:[' + PN_CHARS_GRP + \
+                       '.]*' + PN_CHARS + ')?'
+
+    PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?'
+
+    PERCENT = '%' + HEX + HEX
+
+    PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS
+
+    PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')'
+
+    PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' +
+                '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' +
+                PN_CHARS_GRP + ':]|' + PLX + '))?')
+
+    EXPONENT = r'[eE][+-]?\d+'
+
+    # Lexer token definitions ::
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            # keywords ::
+            (r'(?i)(base|prefix|start|external|'
+             r'literal|iri|bnode|nonliteral|length|minlength|maxlength|'
+             r'mininclusive|minexclusive|maxinclusive|maxexclusive|'
+             r'totaldigits|fractiondigits|'
+             r'closed|extra)\b', Keyword),
+            (r'(a)\b', Keyword),
+            # IRIs ::
+            ('(' + IRIREF + ')', Name.Label),
+            # blank nodes ::
+            ('(' + BLANK_NODE_LABEL + ')', Name.Label),
+            # prefixed names ::
+            (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + ')?',
+             bygroups(Name.Namespace, Punctuation, Name.Tag)),
+            # boolean literals ::
+            (r'(true|false)', Keyword.Constant),
+            # double literals ::
+            (r'[+\-]?(\d+\.\d*' + EXPONENT + r'|\.?\d+' + EXPONENT + ')', Number.Float),
+            # decimal literals ::
+            (r'[+\-]?(\d+\.\d*|\.\d+)', Number.Float),
+            # integer literals ::
+            (r'[+\-]?\d+', Number.Integer),
+            # operators ::
+            (r'[@|$&=*+?^\-~]', Operator),
+            # operator keywords ::
+            (r'(?i)(and|or|not)\b', Operator.Word),
+            # punctuation characters ::
+            (r'[(){}.;,:^\[\]]', Punctuation),
+            # line comments ::
+            (r'#[^\n]*', Comment),
+            # strings ::
+            (r'"""', String, 'triple-double-quoted-string'),
+            (r'"', String, 'single-double-quoted-string'),
+            (r"'''", String, 'triple-single-quoted-string'),
+            (r"'", String, 'single-single-quoted-string'),
+        ],
+        'triple-double-quoted-string': [
+            (r'"""', String, 'end-of-string'),
+            (r'[^\\]+', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'single-double-quoted-string': [
+            (r'"', String, 'end-of-string'),
+            (r'[^"\\\n]+', String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'triple-single-quoted-string': [
+            (r"'''", String, 'end-of-string'),
+            (r'[^\\]+', String),
+            (r'\\', String.Escape, 'string-escape'),
+        ],
+        'single-single-quoted-string': [
+            (r"'", String, 'end-of-string'),
+            (r"[^'\\\n]+", String),
+            (r'\\', String, 'string-escape'),
+        ],
+        'string-escape': [
+            (UCHAR_NO_BACKSLASH, String.Escape, '#pop'),
+            (r'.', String.Escape, '#pop'),
+        ],
+        'end-of-string': [
+            (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)',
+             bygroups(Operator, Name.Function), '#pop:2'),
+            (r'\^\^', Operator, '#pop:2'),
+            default('#pop:2'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rebol.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rebol.py
new file mode 100644
index 0000000..fcff62b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rebol.py
@@ -0,0 +1,419 @@
+"""
+    pygments.lexers.rebol
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the REBOL and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Generic, Whitespace
+
+__all__ = ['RebolLexer', 'RedLexer']
+
+
+class RebolLexer(RegexLexer):
+    """
+    A REBOL lexer.
+    """
+    name = 'REBOL'
+    aliases = ['rebol']
+    filenames = ['*.r', '*.r3', '*.reb']
+    mimetypes = ['text/x-rebol']
+    url = 'http://www.rebol.com'
+    version_added = '1.1'
+
+    flags = re.IGNORECASE | re.MULTILINE
+
+    escape_re = r'(?:\^\([0-9a-f]{1,4}\)*)'
+
+    def word_callback(lexer, match):
+        word = match.group()
+
+        if re.match(".*:$", word):
+            yield match.start(), Generic.Subheading, word
+        elif re.match(
+            r'(native|alias|all|any|as-string|as-binary|bind|bound\?|case|'
+            r'catch|checksum|comment|debase|dehex|exclude|difference|disarm|'
+            r'either|else|enbase|foreach|remove-each|form|free|get|get-env|if|'
+            r'in|intersect|loop|minimum-of|maximum-of|mold|new-line|'
+            r'new-line\?|not|now|prin|print|reduce|compose|construct|repeat|'
+            r'reverse|save|script\?|set|shift|switch|throw|to-hex|trace|try|'
+            r'type\?|union|unique|unless|unprotect|unset|until|use|value\?|'
+            r'while|compress|decompress|secure|open|close|read|read-io|'
+            r'write-io|write|update|query|wait|input\?|exp|log-10|log-2|'
+            r'log-e|square-root|cosine|sine|tangent|arccosine|arcsine|'
+            r'arctangent|protect|lowercase|uppercase|entab|detab|connected\?|'
+            r'browse|launch|stats|get-modes|set-modes|to-local-file|'
+            r'to-rebol-file|encloak|decloak|create-link|do-browser|bind\?|'
+            r'hide|draw|show|size-text|textinfo|offset-to-caret|'
+            r'caret-to-offset|local-request-file|rgb-to-hsv|hsv-to-rgb|'
+            r'crypt-strength\?|dh-make-key|dh-generate-key|dh-compute-key|'
+            r'dsa-make-key|dsa-generate-key|dsa-make-signature|'
+            r'dsa-verify-signature|rsa-make-key|rsa-generate-key|'
+            r'rsa-encrypt)$', word):
+            yield match.start(), Name.Builtin, word
+        elif re.match(
+            r'(add|subtract|multiply|divide|remainder|power|and~|or~|xor~|'
+            r'minimum|maximum|negate|complement|absolute|random|head|tail|'
+            r'next|back|skip|at|pick|first|second|third|fourth|fifth|sixth|'
+            r'seventh|eighth|ninth|tenth|last|path|find|select|make|to|copy\*|'
+            r'insert|remove|change|poke|clear|trim|sort|min|max|abs|cp|'
+            r'copy)$', word):
+            yield match.start(), Name.Function, word
+        elif re.match(
+            r'(error|source|input|license|help|install|echo|Usage|with|func|'
+            r'throw-on-error|function|does|has|context|probe|\?\?|as-pair|'
+            r'mod|modulo|round|repend|about|set-net|append|join|rejoin|reform|'
+            r'remold|charset|array|replace|move|extract|forskip|forall|alter|'
+            r'first+|also|take|for|forever|dispatch|attempt|what-dir|'
+            r'change-dir|clean-path|list-dir|dirize|rename|split-path|delete|'
+            r'make-dir|delete-dir|in-dir|confirm|dump-obj|upgrade|what|'
+            r'build-tag|process-source|build-markup|decode-cgi|read-cgi|'
+            r'write-user|save-user|set-user-name|protect-system|parse-xml|'
+            r'cvs-date|cvs-version|do-boot|get-net-info|desktop|layout|'
+            r'scroll-para|get-face|alert|set-face|uninstall|unfocus|'
+            r'request-dir|center-face|do-events|net-error|decode-url|'
+            r'parse-header|parse-header-date|parse-email-addrs|import-email|'
+            r'send|build-attach-body|resend|show-popup|hide-popup|open-events|'
+            r'find-key-face|do-face|viewtop|confine|find-window|'
+            r'insert-event-func|remove-event-func|inform|dump-pane|dump-face|'
+            r'flag-face|deflag-face|clear-fields|read-net|vbug|path-thru|'
+            r'read-thru|load-thru|do-thru|launch-thru|load-image|'
+            r'request-download|do-face-alt|set-font|set-para|get-style|'
+            r'set-style|make-face|stylize|choose|hilight-text|hilight-all|'
+            r'unlight-text|focus|scroll-drag|clear-face|reset-face|scroll-face|'
+            r'resize-face|load-stock|load-stock-block|notify|request|flash|'
+            r'request-color|request-pass|request-text|request-list|'
+            r'request-date|request-file|dbug|editor|link-relative-path|'
+            r'emailer|parse-error)$', word):
+            yield match.start(), Keyword.Namespace, word
+        elif re.match(
+            r'(halt|quit|do|load|q|recycle|call|run|ask|parse|view|unview|'
+            r'return|exit|break)$', word):
+            yield match.start(), Name.Exception, word
+        elif re.match('REBOL$', word):
+            yield match.start(), Generic.Heading, word
+        elif re.match("to-.*", word):
+            yield match.start(), Keyword, word
+        elif re.match(r'(\+|-|\*|/|//|\*\*|and|or|xor|=\?|=|==|<>|<|>|<=|>=)$',
+                      word):
+            yield match.start(), Operator, word
+        elif re.match(r".*\?$", word):
+            yield match.start(), Keyword, word
+        elif re.match(r".*\!$", word):
+            yield match.start(), Keyword.Type, word
+        elif re.match("'.*", word):
+            yield match.start(), Name.Variable.Instance, word  # lit-word
+        elif re.match("#.*", word):
+            yield match.start(), Name.Label, word  # issue
+        elif re.match("%.*", word):
+            yield match.start(), Name.Decorator, word  # file
+        else:
+            yield match.start(), Name.Variable, word
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            (r'#"', String.Char, 'char'),
+            (r'#\{[0-9a-f]*\}', Number.Hex),
+            (r'2#\{', Number.Hex, 'bin2'),
+            (r'64#\{[0-9a-z+/=\s]*\}', Number.Hex),
+            (r'"', String, 'string'),
+            (r'\{', String, 'string2'),
+            (r';#+.*\n', Comment.Special),
+            (r';\*+.*\n', Comment.Preproc),
+            (r';.*\n', Comment),
+            (r'%"', Name.Decorator, 'stringFile'),
+            (r'%[^(^{")\s\[\]]+', Name.Decorator),
+            (r'[+-]?([a-z]{1,3})?\$\d+(\.\d+)?', Number.Float),  # money
+            (r'[+-]?\d+\:\d+(\:\d+)?(\.\d+)?', String.Other),    # time
+            (r'\d+[\-/][0-9a-z]+[\-/]\d+(\/\d+\:\d+((\:\d+)?'
+             r'([.\d+]?([+-]?\d+:\d+)?)?)?)?', String.Other),   # date
+            (r'\d+(\.\d+)+\.\d+', Keyword.Constant),             # tuple
+            (r'\d+X\d+', Keyword.Constant),                   # pair
+            (r'[+-]?\d+(\'\d+)?([.,]\d*)?E[+-]?\d+', Number.Float),
+            (r'[+-]?\d+(\'\d+)?[.,]\d*', Number.Float),
+            (r'[+-]?\d+(\'\d+)?', Number),
+            (r'[\[\]()]', Generic.Strong),
+            (r'[a-z]+[^(^{"\s:)]*://[^(^{"\s)]*', Name.Decorator),  # url
+            (r'mailto:[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator),  # url
+            (r'[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator),         # email
+            (r'comment\s"', Comment, 'commentString1'),
+            (r'comment\s\{', Comment, 'commentString2'),
+            (r'comment\s\[', Comment, 'commentBlock'),
+            (r'comment\s[^(\s{"\[]+', Comment),
+            (r'/[^(^{")\s/[\]]*', Name.Attribute),
+            (r'([^(^{")\s/[\]]+)(?=[:({"\s/\[\]])', word_callback),
+            (r'<[\w:.-]*>', Name.Tag),
+            (r'<[^(<>\s")]+', Name.Tag, 'tag'),
+            (r'([^(^{")\s]+)', Text),
+        ],
+        'string': [
+            (r'[^(^")]+', String),
+            (escape_re, String.Escape),
+            (r'[(|)]+', String),
+            (r'\^.', String.Escape),
+            (r'"', String, '#pop'),
+        ],
+        'string2': [
+            (r'[^(^{})]+', String),
+            (escape_re, String.Escape),
+            (r'[(|)]+', String),
+            (r'\^.', String.Escape),
+            (r'\{', String, '#push'),
+            (r'\}', String, '#pop'),
+        ],
+        'stringFile': [
+            (r'[^(^")]+', Name.Decorator),
+            (escape_re, Name.Decorator),
+            (r'\^.', Name.Decorator),
+            (r'"', Name.Decorator, '#pop'),
+        ],
+        'char': [
+            (escape_re + '"', String.Char, '#pop'),
+            (r'\^."', String.Char, '#pop'),
+            (r'."', String.Char, '#pop'),
+        ],
+        'tag': [
+            (escape_re, Name.Tag),
+            (r'"', Name.Tag, 'tagString'),
+            (r'[^(<>\r\n")]+', Name.Tag),
+            (r'>', Name.Tag, '#pop'),
+        ],
+        'tagString': [
+            (r'[^(^")]+', Name.Tag),
+            (escape_re, Name.Tag),
+            (r'[(|)]+', Name.Tag),
+            (r'\^.', Name.Tag),
+            (r'"', Name.Tag, '#pop'),
+        ],
+        'tuple': [
+            (r'(\d+\.)+', Keyword.Constant),
+            (r'\d+', Keyword.Constant, '#pop'),
+        ],
+        'bin2': [
+            (r'\s+', Number.Hex),
+            (r'([01]\s*){8}', Number.Hex),
+            (r'\}', Number.Hex, '#pop'),
+        ],
+        'commentString1': [
+            (r'[^(^")]+', Comment),
+            (escape_re, Comment),
+            (r'[(|)]+', Comment),
+            (r'\^.', Comment),
+            (r'"', Comment, '#pop'),
+        ],
+        'commentString2': [
+            (r'[^(^{})]+', Comment),
+            (escape_re, Comment),
+            (r'[(|)]+', Comment),
+            (r'\^.', Comment),
+            (r'\{', Comment, '#push'),
+            (r'\}', Comment, '#pop'),
+        ],
+        'commentBlock': [
+            (r'\[', Comment, '#push'),
+            (r'\]', Comment, '#pop'),
+            (r'"', Comment, "commentString1"),
+            (r'\{', Comment, "commentString2"),
+            (r'[^(\[\]"{)]+', Comment),
+        ],
+    }
+
+    def analyse_text(text):
+        """
+        Check if code contains REBOL header and so it probably not R code
+        """
+        if re.match(r'^\s*REBOL\s*\[', text, re.IGNORECASE):
+            # The code starts with REBOL header
+            return 1.0
+        elif re.search(r'\s*REBOL\s*\[', text, re.IGNORECASE):
+            # The code contains REBOL header but also some text before it
+            return 0.5
+
+
+class RedLexer(RegexLexer):
+    """
+    A Red-language lexer.
+    """
+    name = 'Red'
+    aliases = ['red', 'red/system']
+    filenames = ['*.red', '*.reds']
+    mimetypes = ['text/x-red', 'text/x-red-system']
+    url = 'https://www.red-lang.org'
+    version_added = '2.0'
+
+    flags = re.IGNORECASE | re.MULTILINE
+
+    escape_re = r'(?:\^\([0-9a-f]{1,4}\)*)'
+
+    def word_callback(lexer, match):
+        word = match.group()
+
+        if re.match(".*:$", word):
+            yield match.start(), Generic.Subheading, word
+        elif re.match(r'(if|unless|either|any|all|while|until|loop|repeat|'
+                      r'foreach|forall|func|function|does|has|switch|'
+                      r'case|reduce|compose|get|set|print|prin|equal\?|'
+                      r'not-equal\?|strict-equal\?|lesser\?|greater\?|lesser-or-equal\?|'
+                      r'greater-or-equal\?|same\?|not|type\?|stats|'
+                      r'bind|union|replace|charset|routine)$', word):
+            yield match.start(), Name.Builtin, word
+        elif re.match(r'(make|random|reflect|to|form|mold|absolute|add|divide|multiply|negate|'
+                      r'power|remainder|round|subtract|even\?|odd\?|and~|complement|or~|xor~|'
+                      r'append|at|back|change|clear|copy|find|head|head\?|index\?|insert|'
+                      r'length\?|next|pick|poke|remove|reverse|select|sort|skip|swap|tail|tail\?|'
+                      r'take|trim|create|close|delete|modify|open|open\?|query|read|rename|'
+                      r'update|write)$', word):
+            yield match.start(), Name.Function, word
+        elif re.match(r'(yes|on|no|off|true|false|tab|cr|lf|newline|escape|slash|sp|space|null|'
+                      r'none|crlf|dot|null-byte)$', word):
+            yield match.start(), Name.Builtin.Pseudo, word
+        elif re.match(r'(#system-global|#include|#enum|#define|#either|#if|#import|#export|'
+                      r'#switch|#default|#get-definition)$', word):
+            yield match.start(), Keyword.Namespace, word
+        elif re.match(r'(system|halt|quit|quit-return|do|load|q|recycle|call|run|ask|parse|'
+                      r'raise-error|return|exit|break|alias|push|pop|probe|\?\?|spec-of|body-of|'
+                      r'quote|forever)$', word):
+            yield match.start(), Name.Exception, word
+        elif re.match(r'(action\?|block\?|char\?|datatype\?|file\?|function\?|get-path\?|zero\?|'
+                      r'get-word\?|integer\?|issue\?|lit-path\?|lit-word\?|logic\?|native\?|'
+                      r'op\?|paren\?|path\?|refinement\?|set-path\?|set-word\?|string\?|unset\?|'
+                      r'any-struct\?|none\?|word\?|any-series\?)$', word):
+            yield match.start(), Keyword, word
+        elif re.match(r'(JNICALL|stdcall|cdecl|infix)$', word):
+            yield match.start(), Keyword.Namespace, word
+        elif re.match("to-.*", word):
+            yield match.start(), Keyword, word
+        elif re.match(r'(\+|-\*\*|-|\*\*|//|/|\*|and|or|xor|=\?|===|==|=|<>|<=|>=|'
+                      r'<<<|>>>|<<|>>|<|>%)$', word):
+            yield match.start(), Operator, word
+        elif re.match(r".*\!$", word):
+            yield match.start(), Keyword.Type, word
+        elif re.match("'.*", word):
+            yield match.start(), Name.Variable.Instance, word  # lit-word
+        elif re.match("#.*", word):
+            yield match.start(), Name.Label, word  # issue
+        elif re.match("%.*", word):
+            yield match.start(), Name.Decorator, word  # file
+        elif re.match(":.*", word):
+            yield match.start(), Generic.Subheading, word  # get-word
+        else:
+            yield match.start(), Name.Variable, word
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            (r'#"', String.Char, 'char'),
+            (r'#\{[0-9a-f\s]*\}', Number.Hex),
+            (r'2#\{', Number.Hex, 'bin2'),
+            (r'64#\{[0-9a-z+/=\s]*\}', Number.Hex),
+            (r'([0-9a-f]+)(h)((\s)|(?=[\[\]{}"()]))',
+             bygroups(Number.Hex, Name.Variable, Whitespace)),
+            (r'"', String, 'string'),
+            (r'\{', String, 'string2'),
+            (r';#+.*\n', Comment.Special),
+            (r';\*+.*\n', Comment.Preproc),
+            (r';.*\n', Comment),
+            (r'%"', Name.Decorator, 'stringFile'),
+            (r'%[^(^{")\s\[\]]+', Name.Decorator),
+            (r'[+-]?([a-z]{1,3})?\$\d+(\.\d+)?', Number.Float),  # money
+            (r'[+-]?\d+\:\d+(\:\d+)?(\.\d+)?', String.Other),    # time
+            (r'\d+[\-/][0-9a-z]+[\-/]\d+(/\d+:\d+((:\d+)?'
+             r'([\.\d+]?([+-]?\d+:\d+)?)?)?)?', String.Other),   # date
+            (r'\d+(\.\d+)+\.\d+', Keyword.Constant),             # tuple
+            (r'\d+X\d+', Keyword.Constant),                   # pair
+            (r'[+-]?\d+(\'\d+)?([.,]\d*)?E[+-]?\d+', Number.Float),
+            (r'[+-]?\d+(\'\d+)?[.,]\d*', Number.Float),
+            (r'[+-]?\d+(\'\d+)?', Number),
+            (r'[\[\]()]', Generic.Strong),
+            (r'[a-z]+[^(^{"\s:)]*://[^(^{"\s)]*', Name.Decorator),  # url
+            (r'mailto:[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator),  # url
+            (r'[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator),         # email
+            (r'comment\s"', Comment, 'commentString1'),
+            (r'comment\s\{', Comment, 'commentString2'),
+            (r'comment\s\[', Comment, 'commentBlock'),
+            (r'comment\s[^(\s{"\[]+', Comment),
+            (r'/[^(^{^")\s/[\]]*', Name.Attribute),
+            (r'([^(^{^")\s/[\]]+)(?=[:({"\s/\[\]])', word_callback),
+            (r'<[\w:.-]*>', Name.Tag),
+            (r'<[^(<>\s")]+', Name.Tag, 'tag'),
+            (r'([^(^{")\s]+)', Text),
+        ],
+        'string': [
+            (r'[^(^")]+', String),
+            (escape_re, String.Escape),
+            (r'[(|)]+', String),
+            (r'\^.', String.Escape),
+            (r'"', String, '#pop'),
+        ],
+        'string2': [
+            (r'[^(^{})]+', String),
+            (escape_re, String.Escape),
+            (r'[(|)]+', String),
+            (r'\^.', String.Escape),
+            (r'\{', String, '#push'),
+            (r'\}', String, '#pop'),
+        ],
+        'stringFile': [
+            (r'[^(^")]+', Name.Decorator),
+            (escape_re, Name.Decorator),
+            (r'\^.', Name.Decorator),
+            (r'"', Name.Decorator, '#pop'),
+        ],
+        'char': [
+            (escape_re + '"', String.Char, '#pop'),
+            (r'\^."', String.Char, '#pop'),
+            (r'."', String.Char, '#pop'),
+        ],
+        'tag': [
+            (escape_re, Name.Tag),
+            (r'"', Name.Tag, 'tagString'),
+            (r'[^(<>\r\n")]+', Name.Tag),
+            (r'>', Name.Tag, '#pop'),
+        ],
+        'tagString': [
+            (r'[^(^")]+', Name.Tag),
+            (escape_re, Name.Tag),
+            (r'[(|)]+', Name.Tag),
+            (r'\^.', Name.Tag),
+            (r'"', Name.Tag, '#pop'),
+        ],
+        'tuple': [
+            (r'(\d+\.)+', Keyword.Constant),
+            (r'\d+', Keyword.Constant, '#pop'),
+        ],
+        'bin2': [
+            (r'\s+', Number.Hex),
+            (r'([01]\s*){8}', Number.Hex),
+            (r'\}', Number.Hex, '#pop'),
+        ],
+        'commentString1': [
+            (r'[^(^")]+', Comment),
+            (escape_re, Comment),
+            (r'[(|)]+', Comment),
+            (r'\^.', Comment),
+            (r'"', Comment, '#pop'),
+        ],
+        'commentString2': [
+            (r'[^(^{})]+', Comment),
+            (escape_re, Comment),
+            (r'[(|)]+', Comment),
+            (r'\^.', Comment),
+            (r'\{', Comment, '#push'),
+            (r'\}', Comment, '#pop'),
+        ],
+        'commentBlock': [
+            (r'\[', Comment, '#push'),
+            (r'\]', Comment, '#pop'),
+            (r'"', Comment, "commentString1"),
+            (r'\{', Comment, "commentString2"),
+            (r'[^(\[\]"{)]+', Comment),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rego.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rego.py
new file mode 100644
index 0000000..b2e8b75
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rego.py
@@ -0,0 +1,57 @@
+"""
+    pygments.lexers.rego
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Rego policy languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Comment, Operator, Keyword, Name, String, Number, Punctuation, Whitespace
+
+class RegoLexer(RegexLexer):
+    """
+    For Rego source.
+    """
+    name = 'Rego'
+    url = 'https://www.openpolicyagent.org/docs/latest/policy-language/'
+    filenames = ['*.rego']
+    aliases = ['rego']
+    mimetypes = ['text/x-rego']
+    version_added = '2.19'
+
+    reserved_words = (
+        'as', 'contains', 'data', 'default', 'else', 'every', 'false',
+        'if', 'in', 'import', 'package', 'not', 'null',
+        'some', 'true', 'with'
+    )
+
+    builtins = (
+        # https://www.openpolicyagent.org/docs/latest/philosophy/#the-opa-document-model
+        'data',  # Global variable for accessing base and virtual documents
+        'input', # Represents synchronously pushed base documents
+    )
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'\s+', Whitespace),
+            (r'#.*?$', Comment.Single),
+            (words(reserved_words, suffix=r'\b'), Keyword),
+            (words(builtins, suffix=r'\b'), Name.Builtin),
+            (r'[a-zA-Z_][a-zA-Z0-9_]*', Name),
+            (r'"(\\\\|\\"|[^"])*"', String.Double),
+            (r'`[^`]*`', String.Backtick),
+            (r'-?\d+(\.\d+)?', Number),
+            (r'(==|!=|<=|>=|:=)', Operator),  # Compound operators
+            (r'[=<>+\-*/%&|]', Operator),     # Single-character operators
+            (r'[\[\]{}(),.:;]', Punctuation),
+        ]
+    }
+
+__all__ = ['RegoLexer']
+
+
+
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rell.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rell.py
new file mode 100644
index 0000000..8d54ed2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rell.py
@@ -0,0 +1,68 @@
+"""
+    pygments.lexers.rell
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Rell language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, default, words
+from pygments.token import Comment, Keyword, Name, String, Number, \
+        Punctuation, Whitespace
+
+__all__ = ['RellLexer']
+
+
+class RellLexer(RegexLexer):
+    """
+    A Lexer for Rell.
+    """
+    name = 'Rell'
+    url = 'https://docs.chromia.com/rell/rell-intro'
+    aliases = ['rell']
+    filenames = ['*.rell']
+    mimetypes = ['text/x-rell']
+    version_added = '2.20'
+
+    ident = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+    tokens = {
+        'root': [
+            (words((
+                'big_integer', 'boolean', 'byte_array', 'decimal', 'gtv',
+                'integer', 'json', 'list', 'map', 'mutable', 'set', 'text',
+                'virtual'), suffix=r'\b'),
+             Keyword.Type),
+            (r'(false|true|null)\b', Keyword.Constant),
+            (r'(entity|enum|namespace|object|struct)\b', Keyword.Declaration),
+            (r'(function|operation|query)\b', Keyword.Declaration, 'function'),
+            (words((
+                'abstract', 'and', 'break', 'continue', 'create', 'delete',
+                'else', 'for', 'if', 'import', 'in', 'index', 'key', 'limit',
+                'module', 'not', 'offset', 'or', 'override', 'return', 'update',
+                'val', 'var', 'when', 'while'), suffix=r'\b'),
+             Keyword.Reserved),
+            (r'//.*?$', Comment.Single),
+            (r'/\*(.|\n|\r)*?\*/', Comment.Multiline),
+            (r'"(\\\\|\\"|[^"])*"', String.Double),
+            (r'\'(\\\\|\\\'|[^\\\'])*\'', String.Single),
+            (r'-?[0-9]*\.[0-9]+([eE][+-][0-9]+)?', Number.Float),
+            (r'-?[0-9]+([eE][+-][0-9]+|[lL])?', Number.Integer),
+            (r'x(\'[a-fA-F0-9]*\'|"[a-fA-F0-9]*")', String.Binary),
+            (r'(\.)([ \n\t\r]*)(' + ident + ')',
+                bygroups(Punctuation, Whitespace, Name.Attribute)),
+            (r'[{}():;,]+', Punctuation),
+            (r'[ \n\t\r]+', Whitespace),
+            (r'@[a-zA-Z_][a-zA-Z0-9_]*', Name.Decorator),
+            (r'[~^*!%&\[\]<>|+=/?\-@\$]', Punctuation.Marker),
+            (ident, Name),
+            (r'(\.)+', Punctuation),
+        ],
+        'function': [
+            (r'[ \n\t\r]+', Whitespace),
+            (ident, Name.Function, '#pop'),
+            default('#pop'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/resource.py b/.venv/lib/python3.12/site-packages/pygments/lexers/resource.py
new file mode 100644
index 0000000..eb6cb4a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/resource.py
@@ -0,0 +1,83 @@
+"""
+    pygments.lexers.resource
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for resource definition files.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, bygroups, words
+from pygments.token import Comment, String, Number, Operator, Text, \
+    Keyword, Name
+
+__all__ = ['ResourceLexer']
+
+
+class ResourceLexer(RegexLexer):
+    """Lexer for ICU Resource bundles.
+    """
+    name = 'ResourceBundle'
+    aliases = ['resourcebundle', 'resource']
+    filenames = []
+    url = 'https://unicode-org.github.io/icu/userguide/locale/resources.html'
+    version_added = '2.0'
+
+    _types = (':table', ':array', ':string', ':bin', ':import', ':intvector',
+              ':int', ':alias')
+
+    flags = re.MULTILINE | re.IGNORECASE
+    tokens = {
+        'root': [
+            (r'//.*?$', Comment),
+            (r'"', String, 'string'),
+            (r'-?\d+', Number.Integer),
+            (r'[,{}]', Operator),
+            (r'([^\s{{:]+)(\s*)({}?)'.format('|'.join(_types)),
+             bygroups(Name, Text, Keyword)),
+            (r'\s+', Text),
+            (words(_types), Keyword),
+        ],
+        'string': [
+            (r'(\\x[0-9a-f]{2}|\\u[0-9a-f]{4}|\\U00[0-9a-f]{6}|'
+             r'\\[0-7]{1,3}|\\c.|\\[abtnvfre\'"?\\]|\\\{|[^"{\\])+', String),
+            (r'\{', String.Escape, 'msgname'),
+            (r'"', String, '#pop')
+        ],
+        'msgname': [
+            (r'([^{},]+)(\s*)', bygroups(Name, String.Escape), ('#pop', 'message'))
+        ],
+        'message': [
+            (r'\{', String.Escape, 'msgname'),
+            (r'\}', String.Escape, '#pop'),
+            (r'(,)(\s*)([a-z]+)(\s*\})',
+             bygroups(Operator, String.Escape, Keyword, String.Escape), '#pop'),
+            (r'(,)(\s*)([a-z]+)(\s*)(,)(\s*)(offset)(\s*)(:)(\s*)(-?\d+)(\s*)',
+             bygroups(Operator, String.Escape, Keyword, String.Escape, Operator,
+                      String.Escape, Operator.Word, String.Escape, Operator,
+                      String.Escape, Number.Integer, String.Escape), 'choice'),
+            (r'(,)(\s*)([a-z]+)(\s*)(,)(\s*)',
+             bygroups(Operator, String.Escape, Keyword, String.Escape, Operator,
+                      String.Escape), 'choice'),
+            (r'\s+', String.Escape)
+        ],
+        'choice': [
+            (r'(=|<|>|<=|>=|!=)(-?\d+)(\s*\{)',
+             bygroups(Operator, Number.Integer, String.Escape), 'message'),
+            (r'([a-z]+)(\s*\{)', bygroups(Keyword.Type, String.Escape), 'str'),
+            (r'\}', String.Escape, ('#pop', '#pop')),
+            (r'\s+', String.Escape)
+        ],
+        'str': [
+            (r'\}', String.Escape, '#pop'),
+            (r'\{', String.Escape, 'msgname'),
+            (r'[^{}]+', String)
+        ]
+    }
+
+    def analyse_text(text):
+        if text.startswith('root:table'):
+            return 1.0
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/ride.py b/.venv/lib/python3.12/site-packages/pygments/lexers/ride.py
new file mode 100644
index 0000000..1ed8f38
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/ride.py
@@ -0,0 +1,138 @@
+"""
+    pygments.lexers.ride
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for the Ride programming language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words, include
+from pygments.token import Comment, Keyword, Name, Number, Punctuation, \
+    String, Text
+
+__all__ = ['RideLexer']
+
+
+class RideLexer(RegexLexer):
+    """
+    For Ride source code.
+    """
+
+    name = 'Ride'
+    aliases = ['ride']
+    filenames = ['*.ride']
+    mimetypes = ['text/x-ride']
+    url = 'https://docs.waves.tech/en/ride'
+    version_added = '2.6'
+
+    validName = r'[a-zA-Z_][a-zA-Z0-9_\']*'
+
+    builtinOps = (
+        '||', '|', '>=', '>', '==', '!',
+        '=', '<=', '<', '::', ':+', ':', '!=', '/',
+        '.', '=>', '-', '+', '*', '&&', '%', '++',
+    )
+
+    globalVariablesName = (
+        'NOALG', 'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512',
+        'SHA3224', 'SHA3256', 'SHA3384', 'SHA3512', 'nil', 'this', 'unit',
+        'height', 'lastBlock', 'Buy', 'Sell', 'CEILING', 'FLOOR', 'DOWN',
+        'HALFDOWN', 'HALFEVEN', 'HALFUP', 'UP',
+    )
+
+    typesName = (
+        'Unit', 'Int', 'Boolean', 'ByteVector', 'String', 'Address', 'Alias',
+        'Transfer', 'AssetPair', 'DataEntry', 'Order', 'Transaction',
+        'GenesisTransaction', 'PaymentTransaction', 'ReissueTransaction',
+        'BurnTransaction', 'MassTransferTransaction', 'ExchangeTransaction',
+        'TransferTransaction', 'SetAssetScriptTransaction',
+        'InvokeScriptTransaction', 'IssueTransaction', 'LeaseTransaction',
+        'LeaseCancelTransaction', 'CreateAliasTransaction',
+        'SetScriptTransaction', 'SponsorFeeTransaction', 'DataTransaction',
+        'WriteSet', 'AttachedPayment', 'ScriptTransfer', 'TransferSet',
+        'ScriptResult', 'Invocation', 'Asset', 'BlockInfo', 'Issue', 'Reissue',
+        'Burn', 'NoAlg', 'Md5', 'Sha1', 'Sha224', 'Sha256', 'Sha384', 'Sha512',
+        'Sha3224', 'Sha3256', 'Sha3384', 'Sha3512', 'BinaryEntry',
+        'BooleanEntry', 'IntegerEntry', 'StringEntry', 'List', 'Ceiling',
+        'Down', 'Floor', 'HalfDown', 'HalfEven', 'HalfUp', 'Up',
+    )
+
+    functionsName = (
+        'fraction', 'size', 'toBytes', 'take', 'drop', 'takeRight', 'dropRight',
+        'toString', 'isDefined', 'extract', 'throw', 'getElement', 'value',
+        'cons', 'toUtf8String', 'toInt', 'indexOf', 'lastIndexOf', 'split',
+        'parseInt', 'parseIntValue', 'keccak256', 'blake2b256', 'sha256',
+        'sigVerify', 'toBase58String', 'fromBase58String', 'toBase64String',
+        'fromBase64String', 'transactionById', 'transactionHeightById',
+        'getInteger', 'getBoolean', 'getBinary', 'getString',
+        'addressFromPublicKey', 'addressFromString', 'addressFromRecipient',
+        'assetBalance', 'wavesBalance', 'getIntegerValue', 'getBooleanValue',
+        'getBinaryValue', 'getStringValue', 'addressFromStringValue',
+        'assetInfo', 'rsaVerify', 'checkMerkleProof', 'median',
+        'valueOrElse', 'valueOrErrorMessage', 'contains', 'log', 'pow',
+        'toBase16String', 'fromBase16String', 'blockInfoByHeight',
+        'transferTransactionById',
+    )
+
+    reservedWords = words((
+        'match', 'case', 'else', 'func', 'if',
+        'let', 'then', '@Callable', '@Verifier',
+    ), suffix=r'\b')
+
+    tokens = {
+        'root': [
+            # Comments
+            (r'#.*', Comment.Single),
+            # Whitespace
+            (r'\s+', Text),
+            # Strings
+            (r'"', String, 'doublequote'),
+            (r'utf8\'', String, 'utf8quote'),
+            (r'base(58|64|16)\'', String, 'singlequote'),
+            # Keywords
+            (reservedWords, Keyword.Reserved),
+            (r'\{-#.*?#-\}', Keyword.Reserved),
+            (r'FOLD<\d+>', Keyword.Reserved),
+            # Types
+            (words(typesName), Keyword.Type),
+            # Main
+            # (specialName, Keyword.Reserved),
+            # Prefix Operators
+            (words(builtinOps, prefix=r'\(', suffix=r'\)'), Name.Function),
+            # Infix Operators
+            (words(builtinOps), Name.Function),
+            (words(globalVariablesName), Name.Function),
+            (words(functionsName), Name.Function),
+            # Numbers
+            include('numbers'),
+            # Variable Names
+            (validName, Name.Variable),
+            # Parens
+            (r'[,()\[\]{}]', Punctuation),
+        ],
+
+        'doublequote': [
+            (r'\\u[0-9a-fA-F]{4}', String.Escape),
+            (r'\\[nrfvb\\"]', String.Escape),
+            (r'[^"]', String),
+            (r'"', String, '#pop'),
+        ],
+
+        'utf8quote': [
+            (r'\\u[0-9a-fA-F]{4}', String.Escape),
+            (r'\\[nrfvb\\\']', String.Escape),
+            (r'[^\']', String),
+            (r'\'', String, '#pop'),
+        ],
+
+        'singlequote': [
+            (r'[^\']', String),
+            (r'\'', String, '#pop'),
+        ],
+
+        'numbers': [
+            (r'_?\d+', Number.Integer),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rita.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rita.py
new file mode 100644
index 0000000..99a574c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rita.py
@@ -0,0 +1,42 @@
+"""
+    pygments.lexers.rita
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for RITA language
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer
+from pygments.token import Comment, Operator, Keyword, Name, Literal, \
+    Punctuation, Whitespace
+
+__all__ = ['RitaLexer']
+
+
+class RitaLexer(RegexLexer):
+    """
+    Lexer for RITA.
+    """
+    name = 'Rita'
+    url = 'https://github.com/zaibacu/rita-dsl'
+    filenames = ['*.rita']
+    aliases = ['rita']
+    mimetypes = ['text/rita']
+    version_added = '2.11'
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'\s+', Whitespace),
+            (r'#(.*?)\n', Comment.Single),
+            (r'@(.*?)\n', Operator),  # Yes, whole line as an operator
+            (r'"(\w|\d|\s|(\\")|[\'_\-./,\?\!])+?"', Literal),
+            (r'\'(\w|\d|\s|(\\\')|["_\-./,\?\!])+?\'', Literal),
+            (r'([A-Z_]+)', Keyword),
+            (r'([a-z0-9_]+)', Name),
+            (r'((->)|[!?+*|=])', Operator),
+            (r'[\(\),\{\}]', Punctuation)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rnc.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rnc.py
new file mode 100644
index 0000000..7ddd33e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rnc.py
@@ -0,0 +1,66 @@
+"""
+    pygments.lexers.rnc
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Relax-NG Compact syntax
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Punctuation
+
+__all__ = ['RNCCompactLexer']
+
+
+class RNCCompactLexer(RegexLexer):
+    """
+    For RelaxNG-compact syntax.
+    """
+
+    name = 'Relax-NG Compact'
+    url = 'http://relaxng.org'
+    aliases = ['rng-compact', 'rnc']
+    filenames = ['*.rnc']
+    version_added = '2.2'
+
+    tokens = {
+        'root': [
+            (r'namespace\b', Keyword.Namespace),
+            (r'(?:default|datatypes)\b', Keyword.Declaration),
+            (r'##.*$', Comment.Preproc),
+            (r'#.*$', Comment.Single),
+            (r'"[^"]*"', String.Double),
+            # TODO single quoted strings and escape sequences outside of
+            # double-quoted strings
+            (r'(?:element|attribute|mixed)\b', Keyword.Declaration, 'variable'),
+            (r'(text\b|xsd:[^ ]+)', Keyword.Type, 'maybe_xsdattributes'),
+            (r'[,?&*=|~]|>>', Operator),
+            (r'[(){}]', Punctuation),
+            (r'.', Text),
+        ],
+
+        # a variable has been declared using `element` or `attribute`
+        'variable': [
+            (r'[^{]+', Name.Variable),
+            (r'\{', Punctuation, '#pop'),
+        ],
+
+        # after an xsd: declaration there may be attributes
+        'maybe_xsdattributes': [
+            (r'\{', Punctuation, 'xsdattributes'),
+            (r'\}', Punctuation, '#pop'),
+            (r'.', Text),
+        ],
+
+        # attributes take the form { key1 = value1 key2 = value2 ... }
+        'xsdattributes': [
+            (r'[^ =}]', Name.Attribute),
+            (r'=', Operator),
+            (r'"[^"]*"', String.Double),
+            (r'\}', Punctuation, '#pop'),
+            (r'.', Text),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/roboconf.py b/.venv/lib/python3.12/site-packages/pygments/lexers/roboconf.py
new file mode 100644
index 0000000..bf0570c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/roboconf.py
@@ -0,0 +1,81 @@
+"""
+    pygments.lexers.roboconf
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Roboconf DSL.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words, re
+from pygments.token import Text, Operator, Keyword, Name, Comment
+
+__all__ = ['RoboconfGraphLexer', 'RoboconfInstancesLexer']
+
+
+class RoboconfGraphLexer(RegexLexer):
+    """
+    Lexer for Roboconf graph files.
+    """
+    name = 'Roboconf Graph'
+    aliases = ['roboconf-graph']
+    filenames = ['*.graph']
+    url = 'https://roboconf.github.io/en/user-guide/graph-definition.html'
+    version_added = '2.1'
+
+    flags = re.IGNORECASE | re.MULTILINE
+    tokens = {
+        'root': [
+            # Skip white spaces
+            (r'\s+', Text),
+
+            # There is one operator
+            (r'=', Operator),
+
+            # Keywords
+            (words(('facet', 'import'), suffix=r'\s*\b', prefix=r'\b'), Keyword),
+            (words((
+                'installer', 'extends', 'exports', 'imports', 'facets',
+                'children'), suffix=r'\s*:?', prefix=r'\b'), Name),
+
+            # Comments
+            (r'#.*\n', Comment),
+
+            # Default
+            (r'[^#]', Text),
+            (r'.*\n', Text)
+        ]
+    }
+
+
+class RoboconfInstancesLexer(RegexLexer):
+    """
+    Lexer for Roboconf instances files.
+    """
+    name = 'Roboconf Instances'
+    aliases = ['roboconf-instances']
+    filenames = ['*.instances']
+    url = 'https://roboconf.github.io'
+    version_added = '2.1'
+
+    flags = re.IGNORECASE | re.MULTILINE
+    tokens = {
+        'root': [
+
+            # Skip white spaces
+            (r'\s+', Text),
+
+            # Keywords
+            (words(('instance of', 'import'), suffix=r'\s*\b', prefix=r'\b'), Keyword),
+            (words(('name', 'count'), suffix=r's*:?', prefix=r'\b'), Name),
+            (r'\s*[\w.-]+\s*:', Name),
+
+            # Comments
+            (r'#.*\n', Comment),
+
+            # Default
+            (r'[^#]', Text),
+            (r'.*\n', Text)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/robotframework.py b/.venv/lib/python3.12/site-packages/pygments/lexers/robotframework.py
new file mode 100644
index 0000000..a481136
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/robotframework.py
@@ -0,0 +1,551 @@
+"""
+    pygments.lexers.robotframework
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Robot Framework.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+#  Copyright 2012 Nokia Siemens Networks Oyj
+#
+#  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 re
+
+from pygments.lexer import Lexer
+from pygments.token import Token
+
+__all__ = ['RobotFrameworkLexer']
+
+
+HEADING = Token.Generic.Heading
+SETTING = Token.Keyword.Namespace
+IMPORT = Token.Name.Namespace
+TC_KW_NAME = Token.Generic.Subheading
+KEYWORD = Token.Name.Function
+ARGUMENT = Token.String
+VARIABLE = Token.Name.Variable
+COMMENT = Token.Comment
+SEPARATOR = Token.Punctuation
+SYNTAX = Token.Punctuation
+GHERKIN = Token.Generic.Emph
+ERROR = Token.Error
+
+
+def normalize(string, remove=''):
+    string = string.lower()
+    for char in remove + ' ':
+        if char in string:
+            string = string.replace(char, '')
+    return string
+
+
+class RobotFrameworkLexer(Lexer):
+    """
+    For Robot Framework test data.
+
+    Supports both space and pipe separated plain text formats.
+    """
+    name = 'RobotFramework'
+    url = 'http://robotframework.org'
+    aliases = ['robotframework']
+    filenames = ['*.robot', '*.resource']
+    mimetypes = ['text/x-robotframework']
+    version_added = '1.6'
+
+    def __init__(self, **options):
+        options['tabsize'] = 2
+        options['encoding'] = 'UTF-8'
+        Lexer.__init__(self, **options)
+
+    def get_tokens_unprocessed(self, text):
+        row_tokenizer = RowTokenizer()
+        var_tokenizer = VariableTokenizer()
+        index = 0
+        for row in text.splitlines():
+            for value, token in row_tokenizer.tokenize(row):
+                for value, token in var_tokenizer.tokenize(value, token):
+                    if value:
+                        yield index, token, str(value)
+                        index += len(value)
+
+
+class VariableTokenizer:
+
+    def tokenize(self, string, token):
+        var = VariableSplitter(string, identifiers='$@%&')
+        if var.start < 0 or token in (COMMENT, ERROR):
+            yield string, token
+            return
+        for value, token in self._tokenize(var, string, token):
+            if value:
+                yield value, token
+
+    def _tokenize(self, var, string, orig_token):
+        before = string[:var.start]
+        yield before, orig_token
+        yield var.identifier + '{', SYNTAX
+        yield from self.tokenize(var.base, VARIABLE)
+        yield '}', SYNTAX
+        if var.index is not None:
+            yield '[', SYNTAX
+            yield from self.tokenize(var.index, VARIABLE)
+            yield ']', SYNTAX
+        yield from self.tokenize(string[var.end:], orig_token)
+
+
+class RowTokenizer:
+
+    def __init__(self):
+        self._table = UnknownTable()
+        self._splitter = RowSplitter()
+        testcases = TestCaseTable()
+        settings = SettingTable(testcases.set_default_template)
+        variables = VariableTable()
+        keywords = KeywordTable()
+        self._tables = {'settings': settings, 'setting': settings,
+                        'metadata': settings,
+                        'variables': variables, 'variable': variables,
+                        'testcases': testcases, 'testcase': testcases,
+                        'tasks': testcases, 'task': testcases,
+                        'keywords': keywords, 'keyword': keywords,
+                        'userkeywords': keywords, 'userkeyword': keywords}
+
+    def tokenize(self, row):
+        commented = False
+        heading = False
+        for index, value in enumerate(self._splitter.split(row)):
+            # First value, and every second after that, is a separator.
+            index, separator = divmod(index-1, 2)
+            if value.startswith('#'):
+                commented = True
+            elif index == 0 and value.startswith('*'):
+                self._table = self._start_table(value)
+                heading = True
+            yield from self._tokenize(value, index, commented,
+                                      separator, heading)
+        self._table.end_row()
+
+    def _start_table(self, header):
+        name = normalize(header, remove='*')
+        return self._tables.get(name, UnknownTable())
+
+    def _tokenize(self, value, index, commented, separator, heading):
+        if commented:
+            yield value, COMMENT
+        elif separator:
+            yield value, SEPARATOR
+        elif heading:
+            yield value, HEADING
+        else:
+            yield from self._table.tokenize(value, index)
+
+
+class RowSplitter:
+    _space_splitter = re.compile('( {2,})')
+    _pipe_splitter = re.compile(r'((?:^| +)\|(?: +|$))')
+
+    def split(self, row):
+        splitter = (row.startswith('| ') and self._split_from_pipes
+                    or self._split_from_spaces)
+        yield from splitter(row)
+        yield '\n'
+
+    def _split_from_spaces(self, row):
+        yield ''  # Start with (pseudo)separator similarly as with pipes
+        yield from self._space_splitter.split(row)
+
+    def _split_from_pipes(self, row):
+        _, separator, rest = self._pipe_splitter.split(row, 1)
+        yield separator
+        while self._pipe_splitter.search(rest):
+            cell, separator, rest = self._pipe_splitter.split(rest, 1)
+            yield cell
+            yield separator
+        yield rest
+
+
+class Tokenizer:
+    _tokens = None
+
+    def __init__(self):
+        self._index = 0
+
+    def tokenize(self, value):
+        values_and_tokens = self._tokenize(value, self._index)
+        self._index += 1
+        if isinstance(values_and_tokens, type(Token)):
+            values_and_tokens = [(value, values_and_tokens)]
+        return values_and_tokens
+
+    def _tokenize(self, value, index):
+        index = min(index, len(self._tokens) - 1)
+        return self._tokens[index]
+
+    def _is_assign(self, value):
+        if value.endswith('='):
+            value = value[:-1].strip()
+        var = VariableSplitter(value, identifiers='$@&')
+        return var.start == 0 and var.end == len(value)
+
+
+class Comment(Tokenizer):
+    _tokens = (COMMENT,)
+
+
+class Setting(Tokenizer):
+    _tokens = (SETTING, ARGUMENT)
+    _keyword_settings = ('suitesetup', 'suiteprecondition', 'suiteteardown',
+                         'suitepostcondition', 'testsetup', 'tasksetup', 'testprecondition',
+                         'testteardown','taskteardown', 'testpostcondition', 'testtemplate', 'tasktemplate')
+    _import_settings = ('library', 'resource', 'variables')
+    _other_settings = ('documentation', 'metadata', 'forcetags', 'defaulttags',
+                       'testtimeout','tasktimeout')
+    _custom_tokenizer = None
+
+    def __init__(self, template_setter=None):
+        Tokenizer.__init__(self)
+        self._template_setter = template_setter
+
+    def _tokenize(self, value, index):
+        if index == 1 and self._template_setter:
+            self._template_setter(value)
+        if index == 0:
+            normalized = normalize(value)
+            if normalized in self._keyword_settings:
+                self._custom_tokenizer = KeywordCall(support_assign=False)
+            elif normalized in self._import_settings:
+                self._custom_tokenizer = ImportSetting()
+            elif normalized not in self._other_settings:
+                return ERROR
+        elif self._custom_tokenizer:
+            return self._custom_tokenizer.tokenize(value)
+        return Tokenizer._tokenize(self, value, index)
+
+
+class ImportSetting(Tokenizer):
+    _tokens = (IMPORT, ARGUMENT)
+
+
+class TestCaseSetting(Setting):
+    _keyword_settings = ('setup', 'precondition', 'teardown', 'postcondition',
+                         'template')
+    _import_settings = ()
+    _other_settings = ('documentation', 'tags', 'timeout')
+
+    def _tokenize(self, value, index):
+        if index == 0:
+            type = Setting._tokenize(self, value[1:-1], index)
+            return [('[', SYNTAX), (value[1:-1], type), (']', SYNTAX)]
+        return Setting._tokenize(self, value, index)
+
+
+class KeywordSetting(TestCaseSetting):
+    _keyword_settings = ('teardown',)
+    _other_settings = ('documentation', 'arguments', 'return', 'timeout', 'tags')
+
+
+class Variable(Tokenizer):
+    _tokens = (SYNTAX, ARGUMENT)
+
+    def _tokenize(self, value, index):
+        if index == 0 and not self._is_assign(value):
+            return ERROR
+        return Tokenizer._tokenize(self, value, index)
+
+
+class KeywordCall(Tokenizer):
+    _tokens = (KEYWORD, ARGUMENT)
+
+    def __init__(self, support_assign=True):
+        Tokenizer.__init__(self)
+        self._keyword_found = not support_assign
+        self._assigns = 0
+
+    def _tokenize(self, value, index):
+        if not self._keyword_found and self._is_assign(value):
+            self._assigns += 1
+            return SYNTAX  # VariableTokenizer tokenizes this later.
+        if self._keyword_found:
+            return Tokenizer._tokenize(self, value, index - self._assigns)
+        self._keyword_found = True
+        return GherkinTokenizer().tokenize(value, KEYWORD)
+
+
+class GherkinTokenizer:
+    _gherkin_prefix = re.compile('^(Given|When|Then|And|But) ', re.IGNORECASE)
+
+    def tokenize(self, value, token):
+        match = self._gherkin_prefix.match(value)
+        if not match:
+            return [(value, token)]
+        end = match.end()
+        return [(value[:end], GHERKIN), (value[end:], token)]
+
+
+class TemplatedKeywordCall(Tokenizer):
+    _tokens = (ARGUMENT,)
+
+
+class ForLoop(Tokenizer):
+
+    def __init__(self):
+        Tokenizer.__init__(self)
+        self._in_arguments = False
+
+    def _tokenize(self, value, index):
+        token = self._in_arguments and ARGUMENT or SYNTAX
+        if value.upper() in ('IN', 'IN RANGE'):
+            self._in_arguments = True
+        return token
+
+
+class _Table:
+    _tokenizer_class = None
+
+    def __init__(self, prev_tokenizer=None):
+        self._tokenizer = self._tokenizer_class()
+        self._prev_tokenizer = prev_tokenizer
+        self._prev_values_on_row = []
+
+    def tokenize(self, value, index):
+        if self._continues(value, index):
+            self._tokenizer = self._prev_tokenizer
+            yield value, SYNTAX
+        else:
+            yield from self._tokenize(value, index)
+        self._prev_values_on_row.append(value)
+
+    def _continues(self, value, index):
+        return value == '...' and all(self._is_empty(t)
+                                      for t in self._prev_values_on_row)
+
+    def _is_empty(self, value):
+        return value in ('', '\\')
+
+    def _tokenize(self, value, index):
+        return self._tokenizer.tokenize(value)
+
+    def end_row(self):
+        self.__init__(prev_tokenizer=self._tokenizer)
+
+
+class UnknownTable(_Table):
+    _tokenizer_class = Comment
+
+    def _continues(self, value, index):
+        return False
+
+
+class VariableTable(_Table):
+    _tokenizer_class = Variable
+
+
+class SettingTable(_Table):
+    _tokenizer_class = Setting
+
+    def __init__(self, template_setter, prev_tokenizer=None):
+        _Table.__init__(self, prev_tokenizer)
+        self._template_setter = template_setter
+
+    def _tokenize(self, value, index):
+        if index == 0 and normalize(value) == 'testtemplate':
+            self._tokenizer = Setting(self._template_setter)
+        return _Table._tokenize(self, value, index)
+
+    def end_row(self):
+        self.__init__(self._template_setter, prev_tokenizer=self._tokenizer)
+
+
+class TestCaseTable(_Table):
+    _setting_class = TestCaseSetting
+    _test_template = None
+    _default_template = None
+
+    @property
+    def _tokenizer_class(self):
+        if self._test_template or (self._default_template and
+                                   self._test_template is not False):
+            return TemplatedKeywordCall
+        return KeywordCall
+
+    def _continues(self, value, index):
+        return index > 0 and _Table._continues(self, value, index)
+
+    def _tokenize(self, value, index):
+        if index == 0:
+            if value:
+                self._test_template = None
+            return GherkinTokenizer().tokenize(value, TC_KW_NAME)
+        if index == 1 and self._is_setting(value):
+            if self._is_template(value):
+                self._test_template = False
+                self._tokenizer = self._setting_class(self.set_test_template)
+            else:
+                self._tokenizer = self._setting_class()
+        if index == 1 and self._is_for_loop(value):
+            self._tokenizer = ForLoop()
+        if index == 1 and self._is_empty(value):
+            return [(value, SYNTAX)]
+        return _Table._tokenize(self, value, index)
+
+    def _is_setting(self, value):
+        return value.startswith('[') and value.endswith(']')
+
+    def _is_template(self, value):
+        return normalize(value) == '[template]'
+
+    def _is_for_loop(self, value):
+        return value.startswith(':') and normalize(value, remove=':') == 'for'
+
+    def set_test_template(self, template):
+        self._test_template = self._is_template_set(template)
+
+    def set_default_template(self, template):
+        self._default_template = self._is_template_set(template)
+
+    def _is_template_set(self, template):
+        return normalize(template) not in ('', '\\', 'none', '${empty}')
+
+
+class KeywordTable(TestCaseTable):
+    _tokenizer_class = KeywordCall
+    _setting_class = KeywordSetting
+
+    def _is_template(self, value):
+        return False
+
+
+# Following code copied directly from Robot Framework 2.7.5.
+
+class VariableSplitter:
+
+    def __init__(self, string, identifiers):
+        self.identifier = None
+        self.base = None
+        self.index = None
+        self.start = -1
+        self.end = -1
+        self._identifiers = identifiers
+        self._may_have_internal_variables = False
+        try:
+            self._split(string)
+        except ValueError:
+            pass
+        else:
+            self._finalize()
+
+    def get_replaced_base(self, variables):
+        if self._may_have_internal_variables:
+            return variables.replace_string(self.base)
+        return self.base
+
+    def _finalize(self):
+        self.identifier = self._variable_chars[0]
+        self.base = ''.join(self._variable_chars[2:-1])
+        self.end = self.start + len(self._variable_chars)
+        if self._has_list_or_dict_variable_index():
+            self.index = ''.join(self._list_and_dict_variable_index_chars[1:-1])
+            self.end += len(self._list_and_dict_variable_index_chars)
+
+    def _has_list_or_dict_variable_index(self):
+        return self._list_and_dict_variable_index_chars\
+        and self._list_and_dict_variable_index_chars[-1] == ']'
+
+    def _split(self, string):
+        start_index, max_index = self._find_variable(string)
+        self.start = start_index
+        self._open_curly = 1
+        self._state = self._variable_state
+        self._variable_chars = [string[start_index], '{']
+        self._list_and_dict_variable_index_chars = []
+        self._string = string
+        start_index += 2
+        for index, char in enumerate(string[start_index:]):
+            index += start_index  # Giving start to enumerate only in Py 2.6+
+            try:
+                self._state(char, index)
+            except StopIteration:
+                return
+            if index  == max_index and not self._scanning_list_variable_index():
+                return
+
+    def _scanning_list_variable_index(self):
+        return self._state in [self._waiting_list_variable_index_state,
+                               self._list_variable_index_state]
+
+    def _find_variable(self, string):
+        max_end_index = string.rfind('}')
+        if max_end_index == -1:
+            raise ValueError('No variable end found')
+        if self._is_escaped(string, max_end_index):
+            return self._find_variable(string[:max_end_index])
+        start_index = self._find_start_index(string, 1, max_end_index)
+        if start_index == -1:
+            raise ValueError('No variable start found')
+        return start_index, max_end_index
+
+    def _find_start_index(self, string, start, end):
+        index = string.find('{', start, end) - 1
+        if index < 0:
+            return -1
+        if self._start_index_is_ok(string, index):
+            return index
+        return self._find_start_index(string, index+2, end)
+
+    def _start_index_is_ok(self, string, index):
+        return string[index] in self._identifiers\
+        and not self._is_escaped(string, index)
+
+    def _is_escaped(self, string, index):
+        escaped = False
+        while index > 0 and string[index-1] == '\\':
+            index -= 1
+            escaped = not escaped
+        return escaped
+
+    def _variable_state(self, char, index):
+        self._variable_chars.append(char)
+        if char == '}' and not self._is_escaped(self._string, index):
+            self._open_curly -= 1
+            if self._open_curly == 0:
+                if not self._is_list_or_dict_variable():
+                    raise StopIteration
+                self._state = self._waiting_list_variable_index_state
+        elif char in self._identifiers:
+            self._state = self._internal_variable_start_state
+
+    def _is_list_or_dict_variable(self):
+        return self._variable_chars[0] in ('@','&')
+
+    def _internal_variable_start_state(self, char, index):
+        self._state = self._variable_state
+        if char == '{':
+            self._variable_chars.append(char)
+            self._open_curly += 1
+            self._may_have_internal_variables = True
+        else:
+            self._variable_state(char, index)
+
+    def _waiting_list_variable_index_state(self, char, index):
+        if char != '[':
+            raise StopIteration
+        self._list_and_dict_variable_index_chars.append(char)
+        self._state = self._list_variable_index_state
+
+    def _list_variable_index_state(self, char, index):
+        self._list_and_dict_variable_index_chars.append(char)
+        if char == ']':
+            raise StopIteration
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/ruby.py b/.venv/lib/python3.12/site-packages/pygments/lexers/ruby.py
new file mode 100644
index 0000000..6832652
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/ruby.py
@@ -0,0 +1,518 @@
+"""
+    pygments.lexers.ruby
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Ruby and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, ExtendedRegexLexer, include, \
+    bygroups, default, LexerContext, do_insertions, words, line_re
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Error, Generic, Whitespace
+from pygments.util import shebang_matches
+
+__all__ = ['RubyLexer', 'RubyConsoleLexer', 'FancyLexer']
+
+
+RUBY_OPERATORS = (
+    '*', '**', '-', '+', '-@', '+@', '/', '%', '&', '|', '^', '`', '~',
+    '[]', '[]=', '<<', '>>', '<', '<>', '<=>', '>', '>=', '==', '==='
+)
+
+
+class RubyLexer(ExtendedRegexLexer):
+    """
+    For Ruby source code.
+    """
+
+    name = 'Ruby'
+    url = 'http://www.ruby-lang.org'
+    aliases = ['ruby', 'rb', 'duby']
+    filenames = ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec',
+                 '*.rbx', '*.duby', 'Gemfile', 'Vagrantfile']
+    mimetypes = ['text/x-ruby', 'application/x-ruby']
+    version_added = ''
+
+    flags = re.DOTALL | re.MULTILINE
+
+    def heredoc_callback(self, match, ctx):
+        # okay, this is the hardest part of parsing Ruby...
+        # match: 1 = <<[-~]?, 2 = quote? 3 = name 4 = quote? 5 = rest of line
+
+        start = match.start(1)
+        yield start, Operator, match.group(1)        # <<[-~]?
+        yield match.start(2), String.Heredoc, match.group(2)   # quote ", ', `
+        yield match.start(3), String.Delimiter, match.group(3) # heredoc name
+        yield match.start(4), String.Heredoc, match.group(4)   # quote again
+
+        heredocstack = ctx.__dict__.setdefault('heredocstack', [])
+        outermost = not bool(heredocstack)
+        heredocstack.append((match.group(1) in ('<<-', '<<~'), match.group(3)))
+
+        ctx.pos = match.start(5)
+        ctx.end = match.end(5)
+        # this may find other heredocs, so limit the recursion depth
+        if len(heredocstack) < 100:
+            yield from self.get_tokens_unprocessed(context=ctx)
+        else:
+            yield ctx.pos, String.Heredoc, match.group(5)
+        ctx.pos = match.end()
+
+        if outermost:
+            # this is the outer heredoc again, now we can process them all
+            for tolerant, hdname in heredocstack:
+                lines = []
+                for match in line_re.finditer(ctx.text, ctx.pos):
+                    if tolerant:
+                        check = match.group().strip()
+                    else:
+                        check = match.group().rstrip()
+                    if check == hdname:
+                        for amatch in lines:
+                            yield amatch.start(), String.Heredoc, amatch.group()
+                        yield match.start(), String.Delimiter, match.group()
+                        ctx.pos = match.end()
+                        break
+                    else:
+                        lines.append(match)
+                else:
+                    # end of heredoc not found -- error!
+                    for amatch in lines:
+                        yield amatch.start(), Error, amatch.group()
+            ctx.end = len(ctx.text)
+            del heredocstack[:]
+
+    def gen_rubystrings_rules():
+        def intp_regex_callback(self, match, ctx):
+            yield match.start(1), String.Regex, match.group(1)  # begin
+            nctx = LexerContext(match.group(3), 0, ['interpolated-regex'])
+            for i, t, v in self.get_tokens_unprocessed(context=nctx):
+                yield match.start(3)+i, t, v
+            yield match.start(4), String.Regex, match.group(4)  # end[mixounse]*
+            ctx.pos = match.end()
+
+        def intp_string_callback(self, match, ctx):
+            yield match.start(1), String.Other, match.group(1)
+            nctx = LexerContext(match.group(3), 0, ['interpolated-string'])
+            for i, t, v in self.get_tokens_unprocessed(context=nctx):
+                yield match.start(3)+i, t, v
+            yield match.start(4), String.Other, match.group(4)  # end
+            ctx.pos = match.end()
+
+        states = {}
+        states['strings'] = [
+            # easy ones
+            (r'\:@{0,2}[a-zA-Z_]\w*[!?]?', String.Symbol),
+            (words(RUBY_OPERATORS, prefix=r'\:@{0,2}'), String.Symbol),
+            (r":'(\\\\|\\[^\\]|[^'\\])*'", String.Symbol),
+            (r':"', String.Symbol, 'simple-sym'),
+            (r'([a-zA-Z_]\w*)(:)(?!:)',
+             bygroups(String.Symbol, Punctuation)),  # Since Ruby 1.9
+            (r'"', String.Double, 'simple-string-double'),
+            (r"'", String.Single, 'simple-string-single'),
+            (r'(?', '<>', 'ab'):
+            states[name+'-intp-string'] = [
+                (r'\\[\\' + bracecc + ']', String.Other),
+                (lbrace, String.Other, '#push'),
+                (rbrace, String.Other, '#pop'),
+                include('string-intp-escaped'),
+                (r'[\\#' + bracecc + ']', String.Other),
+                (r'[^\\#' + bracecc + ']+', String.Other),
+            ]
+            states['strings'].append((r'%[QWx]?' + lbrace, String.Other,
+                                      name+'-intp-string'))
+            states[name+'-string'] = [
+                (r'\\[\\' + bracecc + ']', String.Other),
+                (lbrace, String.Other, '#push'),
+                (rbrace, String.Other, '#pop'),
+                (r'[\\#' + bracecc + ']', String.Other),
+                (r'[^\\#' + bracecc + ']+', String.Other),
+            ]
+            states['strings'].append((r'%[qsw]' + lbrace, String.Other,
+                                      name+'-string'))
+            states[name+'-regex'] = [
+                (r'\\[\\' + bracecc + ']', String.Regex),
+                (lbrace, String.Regex, '#push'),
+                (rbrace + '[mixounse]*', String.Regex, '#pop'),
+                include('string-intp'),
+                (r'[\\#' + bracecc + ']', String.Regex),
+                (r'[^\\#' + bracecc + ']+', String.Regex),
+            ]
+            states['strings'].append((r'%r' + lbrace, String.Regex,
+                                      name+'-regex'))
+
+        # these must come after %!
+        states['strings'] += [
+            # %r regex
+            (r'(%r([\W_]))((?:\\\2|(?!\2).)*)(\2[mixounse]*)',
+             intp_regex_callback),
+            # regular fancy strings with qsw
+            (r'%[qsw]([\W_])((?:\\\1|(?!\1).)*)\1', String.Other),
+            (r'(%[QWx]([\W_]))((?:\\\2|(?!\2).)*)(\2)',
+             intp_string_callback),
+            # special forms of fancy strings after operators or
+            # in method calls with braces
+            (r'(?<=[-+/*%=<>&!^|~,(])(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)',
+             bygroups(Whitespace, String.Other, None)),
+            # and because of fixed width lookbehinds the whole thing a
+            # second time for line startings...
+            (r'^(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)',
+             bygroups(Whitespace, String.Other, None)),
+            # all regular fancy strings without qsw
+            (r'(%([^a-zA-Z0-9\s]))((?:\\\2|(?!\2).)*)(\2)',
+             intp_string_callback),
+        ]
+
+        return states
+
+    tokens = {
+        'root': [
+            (r'\A#!.+?$', Comment.Hashbang),
+            (r'#.*?$', Comment.Single),
+            (r'=begin\s.*?\n=end.*?$', Comment.Multiline),
+            # keywords
+            (words((
+                'BEGIN', 'END', 'alias', 'begin', 'break', 'case', 'defined?',
+                'do', 'else', 'elsif', 'end', 'ensure', 'for', 'if', 'in', 'next', 'redo',
+                'rescue', 'raise', 'retry', 'return', 'super', 'then', 'undef',
+                'unless', 'until', 'when', 'while', 'yield'), suffix=r'\b'),
+             Keyword),
+            # start of function, class and module names
+            (r'(module)(\s+)([a-zA-Z_]\w*'
+             r'(?:::[a-zA-Z_]\w*)*)',
+             bygroups(Keyword, Whitespace, Name.Namespace)),
+            (r'(def)(\s+)', bygroups(Keyword, Whitespace), 'funcname'),
+            (r'def(?=[*%&^`~+-/\[<>=])', Keyword, 'funcname'),
+            (r'(class)(\s+)', bygroups(Keyword, Whitespace), 'classname'),
+            # special methods
+            (words((
+                'initialize', 'new', 'loop', 'include', 'extend', 'raise', 'attr_reader',
+                'attr_writer', 'attr_accessor', 'attr', 'catch', 'throw', 'private',
+                'module_function', 'public', 'protected', 'true', 'false', 'nil'),
+                suffix=r'\b'),
+             Keyword.Pseudo),
+            (r'(not|and|or)\b', Operator.Word),
+            (words((
+                'autoload', 'block_given', 'const_defined', 'eql', 'equal', 'frozen', 'include',
+                'instance_of', 'is_a', 'iterator', 'kind_of', 'method_defined', 'nil',
+                'private_method_defined', 'protected_method_defined',
+                'public_method_defined', 'respond_to', 'tainted'), suffix=r'\?'),
+             Name.Builtin),
+            (r'(chomp|chop|exit|gsub|sub)!', Name.Builtin),
+            (words((
+                'Array', 'Float', 'Integer', 'String', '__id__', '__send__', 'abort',
+                'ancestors', 'at_exit', 'autoload', 'binding', 'callcc', 'caller',
+                'catch', 'chomp', 'chop', 'class_eval', 'class_variables',
+                'clone', 'const_defined?', 'const_get', 'const_missing', 'const_set',
+                'constants', 'display', 'dup', 'eval', 'exec', 'exit', 'extend', 'fail', 'fork',
+                'format', 'freeze', 'getc', 'gets', 'global_variables', 'gsub',
+                'hash', 'id', 'included_modules', 'inspect', 'instance_eval',
+                'instance_method', 'instance_methods',
+                'instance_variable_get', 'instance_variable_set', 'instance_variables',
+                'lambda', 'load', 'local_variables', 'loop',
+                'method', 'method_missing', 'methods', 'module_eval', 'name',
+                'object_id', 'open', 'p', 'print', 'printf', 'private_class_method',
+                'private_instance_methods',
+                'private_methods', 'proc', 'protected_instance_methods',
+                'protected_methods', 'public_class_method',
+                'public_instance_methods', 'public_methods',
+                'putc', 'puts', 'raise', 'rand', 'readline', 'readlines', 'require',
+                'scan', 'select', 'self', 'send', 'set_trace_func', 'singleton_methods', 'sleep',
+                'split', 'sprintf', 'srand', 'sub', 'syscall', 'system', 'taint',
+                'test', 'throw', 'to_a', 'to_s', 'trace_var', 'trap', 'untaint',
+                'untrace_var', 'warn'), prefix=r'(?~!:])|'
+             r'(?<=(?:\s|;)when\s)|'
+             r'(?<=(?:\s|;)or\s)|'
+             r'(?<=(?:\s|;)and\s)|'
+             r'(?<=\.index\s)|'
+             r'(?<=\.scan\s)|'
+             r'(?<=\.sub\s)|'
+             r'(?<=\.sub!\s)|'
+             r'(?<=\.gsub\s)|'
+             r'(?<=\.gsub!\s)|'
+             r'(?<=\.match\s)|'
+             r'(?<=(?:\s|;)if\s)|'
+             r'(?<=(?:\s|;)elsif\s)|'
+             r'(?<=^when\s)|'
+             r'(?<=^index\s)|'
+             r'(?<=^scan\s)|'
+             r'(?<=^sub\s)|'
+             r'(?<=^gsub\s)|'
+             r'(?<=^sub!\s)|'
+             r'(?<=^gsub!\s)|'
+             r'(?<=^match\s)|'
+             r'(?<=^if\s)|'
+             r'(?<=^elsif\s)'
+             r')(\s*)(/)', bygroups(Text, String.Regex), 'multiline-regex'),
+            # multiline regex (in method calls or subscripts)
+            (r'(?<=\(|,|\[)/', String.Regex, 'multiline-regex'),
+            # multiline regex (this time the funny no whitespace rule)
+            (r'(\s+)(/)(?![\s=])', bygroups(Whitespace, String.Regex),
+             'multiline-regex'),
+            # lex numbers and ignore following regular expressions which
+            # are division operators in fact (grrrr. i hate that. any
+            # better ideas?)
+            # since pygments 0.7 we also eat a "?" operator after numbers
+            # so that the char operator does not work. Chars are not allowed
+            # there so that you can use the ternary operator.
+            # stupid example:
+            #   x>=0?n[x]:""
+            (r'(0_?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?',
+             bygroups(Number.Oct, Whitespace, Operator)),
+            (r'(0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?',
+             bygroups(Number.Hex, Whitespace, Operator)),
+            (r'(0b[01]+(?:_[01]+)*)(\s*)([/?])?',
+             bygroups(Number.Bin, Whitespace, Operator)),
+            (r'([\d]+(?:_\d+)*)(\s*)([/?])?',
+             bygroups(Number.Integer, Whitespace, Operator)),
+            # Names
+            (r'@@[a-zA-Z_]\w*', Name.Variable.Class),
+            (r'@[a-zA-Z_]\w*', Name.Variable.Instance),
+            (r'\$\w+', Name.Variable.Global),
+            (r'\$[!@&`\'+~=/\\,;.<>_*$?:"^-]', Name.Variable.Global),
+            (r'\$-[0adFiIlpvw]', Name.Variable.Global),
+            (r'::', Operator),
+            include('strings'),
+            # chars
+            (r'\?(\\[MC]-)*'  # modifiers
+             r'(\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})|\S)'
+             r'(?!\w)',
+             String.Char),
+            (r'[A-Z]\w+', Name.Constant),
+            # this is needed because ruby attributes can look
+            # like keywords (class) or like this: ` ?!?
+            (words(RUBY_OPERATORS, prefix=r'(\.|::)'),
+             bygroups(Operator, Name.Operator)),
+            (r'(\.|::)([a-zA-Z_]\w*[!?]?|[*%&^`~+\-/\[<>=])',
+             bygroups(Operator, Name)),
+            (r'[a-zA-Z_]\w*[!?]?', Name),
+            (r'(\[|\]|\*\*|<>?|>=|<=|<=>|=~|={3}|'
+             r'!~|&&?|\|\||\.{1,3})', Operator),
+            (r'[-+/*%=<>&!^|~]=?', Operator),
+            (r'[(){};,/?:\\]', Punctuation),
+            (r'\s+', Whitespace)
+        ],
+        'funcname': [
+            (r'\(', Punctuation, 'defexpr'),
+            (r'(?:([a-zA-Z_]\w*)(\.))?'  # optional scope name, like "self."
+             r'('
+                r'[a-zA-Z\u0080-\uffff][a-zA-Z0-9_\u0080-\uffff]*[!?=]?'  # method name
+                r'|!=|!~|=~|\*\*?|[-+!~]@?|[/%&|^]|<=>|<[<=]?|>[>=]?|===?'  # or operator override
+                r'|\[\]=?'  # or element reference/assignment override
+                r'|`'  # or the undocumented backtick override
+             r')',
+             bygroups(Name.Class, Operator, Name.Function), '#pop'),
+            default('#pop')
+        ],
+        'classname': [
+            (r'\(', Punctuation, 'defexpr'),
+            (r'<<', Operator, '#pop'),
+            (r'[A-Z_]\w*', Name.Class, '#pop'),
+            default('#pop')
+        ],
+        'defexpr': [
+            (r'(\))(\.|::)?', bygroups(Punctuation, Operator), '#pop'),
+            (r'\(', Operator, '#push'),
+            include('root')
+        ],
+        'in-intp': [
+            (r'\{', String.Interpol, '#push'),
+            (r'\}', String.Interpol, '#pop'),
+            include('root'),
+        ],
+        'string-intp': [
+            (r'#\{', String.Interpol, 'in-intp'),
+            (r'#@@?[a-zA-Z_]\w*', String.Interpol),
+            (r'#\$[a-zA-Z_]\w*', String.Interpol)
+        ],
+        'string-intp-escaped': [
+            include('string-intp'),
+            (r'\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})',
+             String.Escape)
+        ],
+        'interpolated-regex': [
+            include('string-intp'),
+            (r'[\\#]', String.Regex),
+            (r'[^\\#]+', String.Regex),
+        ],
+        'interpolated-string': [
+            include('string-intp'),
+            (r'[\\#]', String.Other),
+            (r'[^\\#]+', String.Other),
+        ],
+        'multiline-regex': [
+            include('string-intp'),
+            (r'\\\\', String.Regex),
+            (r'\\/', String.Regex),
+            (r'[\\#]', String.Regex),
+            (r'[^\\/#]+', String.Regex),
+            (r'/[mixounse]*', String.Regex, '#pop'),
+        ],
+        'end-part': [
+            (r'.+', Comment.Preproc, '#pop')
+        ]
+    }
+    tokens.update(gen_rubystrings_rules())
+
+    def analyse_text(text):
+        return shebang_matches(text, r'ruby(1\.\d)?')
+
+
+class RubyConsoleLexer(Lexer):
+    """
+    For Ruby interactive console (**irb**) output.
+    """
+    name = 'Ruby irb session'
+    aliases = ['rbcon', 'irb']
+    mimetypes = ['text/x-ruby-shellsession']
+    url = 'https://www.ruby-lang.org'
+    version_added = ''
+    _example = 'rbcon/console'
+
+    _prompt_re = re.compile(r'irb\([a-zA-Z_]\w*\):\d{3}:\d+[>*"\'] '
+                            r'|>> |\?> ')
+
+    def get_tokens_unprocessed(self, text):
+        rblexer = RubyLexer(**self.options)
+
+        curcode = ''
+        insertions = []
+        for match in line_re.finditer(text):
+            line = match.group()
+            m = self._prompt_re.match(line)
+            if m is not None:
+                end = m.end()
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:end])]))
+                curcode += line[end:]
+            else:
+                if curcode:
+                    yield from do_insertions(
+                        insertions, rblexer.get_tokens_unprocessed(curcode))
+                    curcode = ''
+                    insertions = []
+                yield match.start(), Generic.Output, line
+        if curcode:
+            yield from do_insertions(
+                insertions, rblexer.get_tokens_unprocessed(curcode))
+
+
+class FancyLexer(RegexLexer):
+    """
+    Pygments Lexer For Fancy.
+
+    Fancy is a self-hosted, pure object-oriented, dynamic,
+    class-based, concurrent general-purpose programming language
+    running on Rubinius, the Ruby VM.
+    """
+    name = 'Fancy'
+    url = 'https://github.com/bakkdoor/fancy'
+    filenames = ['*.fy', '*.fancypack']
+    aliases = ['fancy', 'fy']
+    mimetypes = ['text/x-fancysrc']
+    version_added = '1.5'
+
+    tokens = {
+        # copied from PerlLexer:
+        'balanced-regex': [
+            (r'/(\\\\|\\[^\\]|[^/\\])*/[egimosx]*', String.Regex, '#pop'),
+            (r'!(\\\\|\\[^\\]|[^!\\])*![egimosx]*', String.Regex, '#pop'),
+            (r'\\(\\\\|[^\\])*\\[egimosx]*', String.Regex, '#pop'),
+            (r'\{(\\\\|\\[^\\]|[^}\\])*\}[egimosx]*', String.Regex, '#pop'),
+            (r'<(\\\\|\\[^\\]|[^>\\])*>[egimosx]*', String.Regex, '#pop'),
+            (r'\[(\\\\|\\[^\\]|[^\]\\])*\][egimosx]*', String.Regex, '#pop'),
+            (r'\((\\\\|\\[^\\]|[^)\\])*\)[egimosx]*', String.Regex, '#pop'),
+            (r'@(\\\\|\\[^\\]|[^@\\])*@[egimosx]*', String.Regex, '#pop'),
+            (r'%(\\\\|\\[^\\]|[^%\\])*%[egimosx]*', String.Regex, '#pop'),
+            (r'\$(\\\\|\\[^\\]|[^$\\])*\$[egimosx]*', String.Regex, '#pop'),
+        ],
+        'root': [
+            (r'\s+', Whitespace),
+
+            # balanced delimiters (copied from PerlLexer):
+            (r's\{(\\\\|\\[^\\]|[^}\\])*\}\s*', String.Regex, 'balanced-regex'),
+            (r's<(\\\\|\\[^\\]|[^>\\])*>\s*', String.Regex, 'balanced-regex'),
+            (r's\[(\\\\|\\[^\\]|[^\]\\])*\]\s*', String.Regex, 'balanced-regex'),
+            (r's\((\\\\|\\[^\\]|[^)\\])*\)\s*', String.Regex, 'balanced-regex'),
+            (r'm?/(\\\\|\\[^\\]|[^///\n])*/[gcimosx]*', String.Regex),
+            (r'm(?=[/!\\{<\[(@%$])', String.Regex, 'balanced-regex'),
+
+            # Comments
+            (r'#(.*?)\n', Comment.Single),
+            # Symbols
+            (r'\'([^\'\s\[\](){}]+|\[\])', String.Symbol),
+            # Multi-line DoubleQuotedString
+            (r'"""(\\\\|\\[^\\]|[^\\])*?"""', String),
+            # DoubleQuotedString
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+            # keywords
+            (r'(def|class|try|catch|finally|retry|return|return_local|match|'
+             r'case|->|=>)\b', Keyword),
+            # constants
+            (r'(self|super|nil|false|true)\b', Name.Constant),
+            (r'[(){};,/?|:\\]', Punctuation),
+            # names
+            (words((
+                'Object', 'Array', 'Hash', 'Directory', 'File', 'Class', 'String',
+                'Number', 'Enumerable', 'FancyEnumerable', 'Block', 'TrueClass',
+                'NilClass', 'FalseClass', 'Tuple', 'Symbol', 'Stack', 'Set',
+                'FancySpec', 'Method', 'Package', 'Range'), suffix=r'\b'),
+             Name.Builtin),
+            # functions
+            (r'[a-zA-Z](\w|[-+?!=*/^><%])*:', Name.Function),
+            # operators, must be below functions
+            (r'[-+*/~,<>=&!?%^\[\].$]+', Operator),
+            (r'[A-Z]\w*', Name.Constant),
+            (r'@[a-zA-Z_]\w*', Name.Variable.Instance),
+            (r'@@[a-zA-Z_]\w*', Name.Variable.Class),
+            ('@@?', Operator),
+            (r'[a-zA-Z_]\w*', Name),
+            # numbers - / checks are necessary to avoid mismarking regexes,
+            # see comment in RubyLexer
+            (r'(0[oO]?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?',
+             bygroups(Number.Oct, Whitespace, Operator)),
+            (r'(0[xX][0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?',
+             bygroups(Number.Hex, Whitespace, Operator)),
+            (r'(0[bB][01]+(?:_[01]+)*)(\s*)([/?])?',
+             bygroups(Number.Bin, Whitespace, Operator)),
+            (r'([\d]+(?:_\d+)*)(\s*)([/?])?',
+             bygroups(Number.Integer, Whitespace, Operator)),
+            (r'\d+([eE][+-]?[0-9]+)|\d+\.\d+([eE][+-]?[0-9]+)?', Number.Float),
+            (r'\d+', Number.Integer)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/rust.py b/.venv/lib/python3.12/site-packages/pygments/lexers/rust.py
new file mode 100644
index 0000000..6c9e621
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/rust.py
@@ -0,0 +1,222 @@
+"""
+    pygments.lexers.rust
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Rust language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, include, bygroups, words, default
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+
+__all__ = ['RustLexer']
+
+
+class RustLexer(RegexLexer):
+    """
+    Lexer for the Rust programming language (version 1.47).
+    """
+    name = 'Rust'
+    url = 'https://www.rust-lang.org/'
+    filenames = ['*.rs', '*.rs.in']
+    aliases = ['rust', 'rs']
+    mimetypes = ['text/rust', 'text/x-rust']
+    version_added = '1.6'
+
+    keyword_types = (words((
+        'u8', 'u16', 'u32', 'u64', 'u128', 'i8', 'i16', 'i32', 'i64', 'i128',
+        'usize', 'isize', 'f32', 'f64', 'char', 'str', 'bool',
+    ), suffix=r'\b'), Keyword.Type)
+
+    builtin_funcs_types = (words((
+        'Copy', 'Send', 'Sized', 'Sync', 'Unpin',
+        'Drop', 'Fn', 'FnMut', 'FnOnce', 'drop',
+        'Box', 'ToOwned', 'Clone',
+        'PartialEq', 'PartialOrd', 'Eq', 'Ord',
+        'AsRef', 'AsMut', 'Into', 'From', 'Default',
+        'Iterator', 'Extend', 'IntoIterator', 'DoubleEndedIterator',
+        'ExactSizeIterator',
+        'Option', 'Some', 'None',
+        'Result', 'Ok', 'Err',
+        'String', 'ToString', 'Vec',
+    ), suffix=r'\b'), Name.Builtin)
+
+    builtin_macros = (words((
+        'asm', 'assert', 'assert_eq', 'assert_ne', 'cfg', 'column',
+        'compile_error', 'concat', 'concat_idents', 'dbg', 'debug_assert',
+        'debug_assert_eq', 'debug_assert_ne', 'env', 'eprint', 'eprintln',
+        'file', 'format', 'format_args', 'format_args_nl', 'global_asm',
+        'include', 'include_bytes', 'include_str',
+        'is_aarch64_feature_detected',
+        'is_arm_feature_detected',
+        'is_mips64_feature_detected',
+        'is_mips_feature_detected',
+        'is_powerpc64_feature_detected',
+        'is_powerpc_feature_detected',
+        'is_x86_feature_detected',
+        'line', 'llvm_asm', 'log_syntax', 'macro_rules', 'matches',
+        'module_path', 'option_env', 'panic', 'print', 'println', 'stringify',
+        'thread_local', 'todo', 'trace_macros', 'unimplemented', 'unreachable',
+        'vec', 'write', 'writeln',
+    ), suffix=r'!'), Name.Function.Magic)
+
+    tokens = {
+        'root': [
+            # rust allows a file to start with a shebang, but if the first line
+            # starts with #![ then it's not a shebang but a crate attribute.
+            (r'#![^[\r\n].*$', Comment.Preproc),
+            default('base'),
+        ],
+        'base': [
+            # Whitespace and Comments
+            (r'\n', Whitespace),
+            (r'\s+', Whitespace),
+            (r'//!.*?\n', String.Doc),
+            (r'///(\n|[^/].*?\n)', String.Doc),
+            (r'//(.*?)\n', Comment.Single),
+            (r'/\*\*(\n|[^/*])', String.Doc, 'doccomment'),
+            (r'/\*!', String.Doc, 'doccomment'),
+            (r'/\*', Comment.Multiline, 'comment'),
+
+            # Macro parameters
+            (r"""\$([a-zA-Z_]\w*|\(,?|\),?|,?)""", Comment.Preproc),
+            # Keywords
+            (words(('as', 'async', 'await', 'box', 'const', 'crate', 'dyn',
+                    'else', 'extern', 'for', 'if', 'impl', 'in', 'loop',
+                    'match', 'move', 'mut', 'pub', 'ref', 'return', 'static',
+                    'super', 'trait', 'unsafe', 'use', 'where', 'while'),
+                   suffix=r'\b'), Keyword),
+            (words(('abstract', 'become', 'do', 'final', 'macro', 'override',
+                    'priv', 'typeof', 'try', 'unsized', 'virtual', 'yield'),
+                   suffix=r'\b'), Keyword.Reserved),
+            (r'(true|false)\b', Keyword.Constant),
+            (r'self\b', Name.Builtin.Pseudo),
+            (r'mod\b', Keyword, 'modname'),
+            (r'let\b', Keyword.Declaration),
+            (r'fn\b', Keyword, 'funcname'),
+            (r'(struct|enum|type|union)\b', Keyword, 'typename'),
+            (r'(default)(\s+)(type|fn)\b', bygroups(Keyword, Whitespace, Keyword)),
+            keyword_types,
+            (r'[sS]elf\b', Name.Builtin.Pseudo),
+            # Prelude (taken from Rust's src/libstd/prelude.rs)
+            builtin_funcs_types,
+            builtin_macros,
+            # Path separators, so types don't catch them.
+            (r'::\b', Punctuation),
+            # Types in positions.
+            (r'(?::|->)', Punctuation, 'typename'),
+            # Labels
+            (r'(break|continue)(\b\s*)(\'[A-Za-z_]\w*)?',
+             bygroups(Keyword, Text.Whitespace, Name.Label)),
+
+            # Character literals
+            (r"""'(\\['"\\nrt]|\\x[0-7][0-9a-fA-F]|\\0"""
+             r"""|\\u\{[0-9a-fA-F]{1,6}\}|.)'""",
+             String.Char),
+            (r"""b'(\\['"\\nrt]|\\x[0-9a-fA-F]{2}|\\0"""
+             r"""|\\u\{[0-9a-fA-F]{1,6}\}|.)'""",
+             String.Char),
+
+            # Binary literals
+            (r'0b[01_]+', Number.Bin, 'number_lit'),
+            # Octal literals
+            (r'0o[0-7_]+', Number.Oct, 'number_lit'),
+            # Hexadecimal literals
+            (r'0[xX][0-9a-fA-F_]+', Number.Hex, 'number_lit'),
+            # Decimal literals
+            (r'[0-9][0-9_]*(\.[0-9_]+[eE][+\-]?[0-9_]+|'
+             r'\.[0-9_]*(?!\.)|[eE][+\-]?[0-9_]+)', Number.Float,
+             'number_lit'),
+            (r'[0-9][0-9_]*', Number.Integer, 'number_lit'),
+
+            # String literals
+            (r'b"', String, 'bytestring'),
+            (r'"', String, 'string'),
+            (r'(?s)b?r(#*)".*?"\1', String),
+
+            # Lifetime names
+            (r"'", Operator, 'lifetime'),
+
+            # Operators and Punctuation
+            (r'\.\.=?', Operator),
+            (r'[{}()\[\],.;]', Punctuation),
+            (r'[+\-*/%&|<>^!~@=:?]', Operator),
+
+            # Identifiers
+            (r'[a-zA-Z_]\w*', Name),
+            # Raw identifiers
+            (r'r#[a-zA-Z_]\w*', Name),
+
+            # Attributes
+            (r'#!?\[', Comment.Preproc, 'attribute['),
+
+            # Misc
+            # Lone hashes: not used in Rust syntax, but allowed in macro
+            # arguments, most famously for quote::quote!()
+            (r'#', Punctuation),
+        ],
+        'comment': [
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline),
+        ],
+        'doccomment': [
+            (r'[^*/]+', String.Doc),
+            (r'/\*', String.Doc, '#push'),
+            (r'\*/', String.Doc, '#pop'),
+            (r'[*/]', String.Doc),
+        ],
+        'modname': [
+            (r'\s+', Whitespace),
+            (r'[a-zA-Z_]\w*', Name.Namespace, '#pop'),
+            default('#pop'),
+        ],
+        'funcname': [
+            (r'\s+', Whitespace),
+            (r'[a-zA-Z_]\w*', Name.Function, '#pop'),
+            default('#pop'),
+        ],
+        'typename': [
+            (r'\s+', Whitespace),
+            (r'&', Keyword.Pseudo),
+            (r"'", Operator, 'lifetime'),
+            builtin_funcs_types,
+            keyword_types,
+            (r'[a-zA-Z_]\w*', Name.Class, '#pop'),
+            default('#pop'),
+        ],
+        'lifetime': [
+            (r"(static|_)", Name.Builtin),
+            (r"[a-zA-Z_]+\w*", Name.Attribute),
+            default('#pop'),
+        ],
+        'number_lit': [
+            (r'[ui](8|16|32|64|size)', Keyword, '#pop'),
+            (r'f(32|64)', Keyword, '#pop'),
+            default('#pop'),
+        ],
+        'string': [
+            (r'"', String, '#pop'),
+            (r"""\\['"\\nrt]|\\x[0-7][0-9a-fA-F]|\\0"""
+             r"""|\\u\{[0-9a-fA-F]{1,6}\}""", String.Escape),
+            (r'[^\\"]+', String),
+            (r'\\', String),
+        ],
+        'bytestring': [
+            (r"""\\x[89a-fA-F][0-9a-fA-F]""", String.Escape),
+            include('string'),
+        ],
+        'attribute_common': [
+            (r'"', String, 'string'),
+            (r'\[', Comment.Preproc, 'attribute['),
+        ],
+        'attribute[': [
+            include('attribute_common'),
+            (r'\]', Comment.Preproc, '#pop'),
+            (r'[^"\]\[]+', Comment.Preproc),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/sas.py b/.venv/lib/python3.12/site-packages/pygments/lexers/sas.py
new file mode 100644
index 0000000..35ee854
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/sas.py
@@ -0,0 +1,227 @@
+"""
+    pygments.lexers.sas
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexer for SAS.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+from pygments.lexer import RegexLexer, include, words
+from pygments.token import Comment, Keyword, Name, Number, String, Text, \
+    Other, Generic
+
+__all__ = ['SASLexer']
+
+
+class SASLexer(RegexLexer):
+    """
+    For SAS files.
+    """
+    # Syntax from syntax/sas.vim by James Kidd 
+
+    name      = 'SAS'
+    aliases   = ['sas']
+    filenames = ['*.SAS', '*.sas']
+    mimetypes = ['text/x-sas', 'text/sas', 'application/x-sas']
+    url = 'https://en.wikipedia.org/wiki/SAS_(software)'
+    version_added = '2.2'
+    flags     = re.IGNORECASE | re.MULTILINE
+
+    builtins_macros = (
+        "bquote", "nrbquote", "cmpres", "qcmpres", "compstor", "datatyp",
+        "display", "do", "else", "end", "eval", "global", "goto", "if",
+        "index", "input", "keydef", "label", "left", "length", "let",
+        "local", "lowcase", "macro", "mend", "nrquote",
+        "nrstr", "put", "qleft", "qlowcase", "qscan",
+        "qsubstr", "qsysfunc", "qtrim", "quote", "qupcase", "scan",
+        "str", "substr", "superq", "syscall", "sysevalf", "sysexec",
+        "sysfunc", "sysget", "syslput", "sysprod", "sysrc", "sysrput",
+        "then", "to", "trim", "unquote", "until", "upcase", "verify",
+        "while", "window"
+    )
+
+    builtins_conditionals = (
+        "do", "if", "then", "else", "end", "until", "while"
+    )
+
+    builtins_statements = (
+        "abort", "array", "attrib", "by", "call", "cards", "cards4",
+        "catname", "continue", "datalines", "datalines4", "delete", "delim",
+        "delimiter", "display", "dm", "drop", "endsas", "error", "file",
+        "filename", "footnote", "format", "goto", "in", "infile", "informat",
+        "input", "keep", "label", "leave", "length", "libname", "link",
+        "list", "lostcard", "merge", "missing", "modify", "options", "output",
+        "out", "page", "put", "redirect", "remove", "rename", "replace",
+        "retain", "return", "select", "set", "skip", "startsas", "stop",
+        "title", "update", "waitsas", "where", "window", "x", "systask"
+    )
+
+    builtins_sql = (
+        "add", "and", "alter", "as", "cascade", "check", "create",
+        "delete", "describe", "distinct", "drop", "foreign", "from",
+        "group", "having", "index", "insert", "into", "in", "key", "like",
+        "message", "modify", "msgtype", "not", "null", "on", "or",
+        "order", "primary", "references", "reset", "restrict", "select",
+        "set", "table", "unique", "update", "validate", "view", "where"
+    )
+
+    builtins_functions = (
+        "abs", "addr", "airy", "arcos", "arsin", "atan", "attrc",
+        "attrn", "band", "betainv", "blshift", "bnot", "bor",
+        "brshift", "bxor", "byte", "cdf", "ceil", "cexist", "cinv",
+        "close", "cnonct", "collate", "compbl", "compound",
+        "compress", "cos", "cosh", "css", "curobs", "cv", "daccdb",
+        "daccdbsl", "daccsl", "daccsyd", "dacctab", "dairy", "date",
+        "datejul", "datepart", "datetime", "day", "dclose", "depdb",
+        "depdbsl", "depsl", "depsyd",
+        "deptab", "dequote", "dhms", "dif", "digamma",
+        "dim", "dinfo", "dnum", "dopen", "doptname", "doptnum",
+        "dread", "dropnote", "dsname", "erf", "erfc", "exist", "exp",
+        "fappend", "fclose", "fcol", "fdelete", "fetch", "fetchobs",
+        "fexist", "fget", "fileexist", "filename", "fileref",
+        "finfo", "finv", "fipname", "fipnamel", "fipstate", "floor",
+        "fnonct", "fnote", "fopen", "foptname", "foptnum", "fpoint",
+        "fpos", "fput", "fread", "frewind", "frlen", "fsep", "fuzz",
+        "fwrite", "gaminv", "gamma", "getoption", "getvarc", "getvarn",
+        "hbound", "hms", "hosthelp", "hour", "ibessel", "index",
+        "indexc", "indexw", "input", "inputc", "inputn", "int",
+        "intck", "intnx", "intrr", "irr", "jbessel", "juldate",
+        "kurtosis", "lag", "lbound", "left", "length", "lgamma",
+        "libname", "libref", "log", "log10", "log2", "logpdf", "logpmf",
+        "logsdf", "lowcase", "max", "mdy", "mean", "min", "minute",
+        "mod", "month", "mopen", "mort", "n", "netpv", "nmiss",
+        "normal", "note", "npv", "open", "ordinal", "pathname",
+        "pdf", "peek", "peekc", "pmf", "point", "poisson", "poke",
+        "probbeta", "probbnml", "probchi", "probf", "probgam",
+        "probhypr", "probit", "probnegb", "probnorm", "probt",
+        "put", "putc", "putn", "qtr", "quote", "ranbin", "rancau",
+        "ranexp", "rangam", "range", "rank", "rannor", "ranpoi",
+        "rantbl", "rantri", "ranuni", "repeat", "resolve", "reverse",
+        "rewind", "right", "round", "saving", "scan", "sdf", "second",
+        "sign", "sin", "sinh", "skewness", "soundex", "spedis",
+        "sqrt", "std", "stderr", "stfips", "stname", "stnamel",
+        "substr", "sum", "symget", "sysget", "sysmsg", "sysprod",
+        "sysrc", "system", "tan", "tanh", "time", "timepart", "tinv",
+        "tnonct", "today", "translate", "tranwrd", "trigamma",
+        "trim", "trimn", "trunc", "uniform", "upcase", "uss", "var",
+        "varfmt", "varinfmt", "varlabel", "varlen", "varname",
+        "varnum", "varray", "varrayx", "vartype", "verify", "vformat",
+        "vformatd", "vformatdx", "vformatn", "vformatnx", "vformatw",
+        "vformatwx", "vformatx", "vinarray", "vinarrayx", "vinformat",
+        "vinformatd", "vinformatdx", "vinformatn", "vinformatnx",
+        "vinformatw", "vinformatwx", "vinformatx", "vlabel",
+        "vlabelx", "vlength", "vlengthx", "vname", "vnamex", "vtype",
+        "vtypex", "weekday", "year", "yyq", "zipfips", "zipname",
+        "zipnamel", "zipstate"
+    )
+
+    tokens = {
+        'root': [
+            include('comments'),
+            include('proc-data'),
+            include('cards-datalines'),
+            include('logs'),
+            include('general'),
+            (r'.', Text),
+        ],
+        # SAS is multi-line regardless, but * is ended by ;
+        'comments': [
+            (r'^\s*\*.*?;', Comment),
+            (r'/\*.*?\*/', Comment),
+            (r'^\s*\*(.|\n)*?;', Comment.Multiline),
+            (r'/[*](.|\n)*?[*]/', Comment.Multiline),
+        ],
+        # Special highlight for proc, data, quit, run
+        'proc-data': [
+            (r'(^|;)\s*(proc \w+|data|run|quit)[\s;]',
+             Keyword.Reserved),
+        ],
+        # Special highlight cards and datalines
+        'cards-datalines': [
+            (r'^\s*(datalines|cards)\s*;\s*$', Keyword, 'data'),
+        ],
+        'data': [
+            (r'(.|\n)*^\s*;\s*$', Other, '#pop'),
+        ],
+        # Special highlight for put NOTE|ERROR|WARNING (order matters)
+        'logs': [
+            (r'\n?^\s*%?put ', Keyword, 'log-messages'),
+        ],
+        'log-messages': [
+            (r'NOTE(:|-).*', Generic, '#pop'),
+            (r'WARNING(:|-).*', Generic.Emph, '#pop'),
+            (r'ERROR(:|-).*', Generic.Error, '#pop'),
+            include('general'),
+        ],
+        'general': [
+            include('keywords'),
+            include('vars-strings'),
+            include('special'),
+            include('numbers'),
+        ],
+        # Keywords, statements, functions, macros
+        'keywords': [
+            (words(builtins_statements,
+                   prefix = r'\b',
+                   suffix = r'\b'),
+             Keyword),
+            (words(builtins_sql,
+                   prefix = r'\b',
+                   suffix = r'\b'),
+             Keyword),
+            (words(builtins_conditionals,
+                   prefix = r'\b',
+                   suffix = r'\b'),
+             Keyword),
+            (words(builtins_macros,
+                   prefix = r'%',
+                   suffix = r'\b'),
+             Name.Builtin),
+            (words(builtins_functions,
+                   prefix = r'\b',
+                   suffix = r'\('),
+             Name.Builtin),
+        ],
+        # Strings and user-defined variables and macros (order matters)
+        'vars-strings': [
+            (r'&[a-z_]\w{0,31}\.?', Name.Variable),
+            (r'%[a-z_]\w{0,31}', Name.Function),
+            (r'\'', String, 'string_squote'),
+            (r'"', String, 'string_dquote'),
+        ],
+        'string_squote': [
+            ('\'', String, '#pop'),
+            (r'\\\\|\\"|\\\n', String.Escape),
+            # AFAIK, macro variables are not evaluated in single quotes
+            # (r'&', Name.Variable, 'validvar'),
+            (r'[^$\'\\]+', String),
+            (r'[$\'\\]', String),
+        ],
+        'string_dquote': [
+            (r'"', String, '#pop'),
+            (r'\\\\|\\"|\\\n', String.Escape),
+            (r'&', Name.Variable, 'validvar'),
+            (r'[^$&"\\]+', String),
+            (r'[$"\\]', String),
+        ],
+        'validvar': [
+            (r'[a-z_]\w{0,31}\.?', Name.Variable, '#pop'),
+        ],
+        # SAS numbers and special variables
+        'numbers': [
+            (r'\b[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+|\.)(E[+-]?[0-9]+)?i?\b',
+             Number),
+        ],
+        'special': [
+            (r'(null|missing|_all_|_automatic_|_character_|_n_|'
+             r'_infile_|_name_|_null_|_numeric_|_user_|_webout_)',
+             Keyword.Constant),
+        ],
+        # 'operators': [
+        #     (r'(-|=|<=|>=|<|>|<>|&|!=|'
+        #      r'\||\*|\+|\^|/|!|~|~=)', Operator)
+        # ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/savi.py b/.venv/lib/python3.12/site-packages/pygments/lexers/savi.py
new file mode 100644
index 0000000..548a056
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/savi.py
@@ -0,0 +1,171 @@
+"""
+    pygments.lexers.savi
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Savi.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, include
+from pygments.token import Whitespace, Keyword, Name, String, Number, \
+  Operator, Punctuation, Comment, Generic, Error
+
+__all__ = ['SaviLexer']
+
+
+# The canonical version of this file can be found in the following repository,
+# where it is kept in sync with any language changes, as well as the other
+# pygments-like lexers that are maintained for use with other tools:
+# - https://github.com/savi-lang/savi/blob/main/tooling/pygments/lexers/savi.py
+#
+# If you're changing this file in the pygments repository, please ensure that
+# any changes you make are also propagated to the official Savi repository,
+# in order to avoid accidental clobbering of your changes later when an update
+# from the Savi repository flows forward into the pygments repository.
+#
+# If you're changing this file in the Savi repository, please ensure that
+# any changes you make are also reflected in the other pygments-like lexers
+# (rouge, vscode, etc) so that all of the lexers can be kept cleanly in sync.
+
+class SaviLexer(RegexLexer):
+    """
+    For Savi source code.
+
+    .. versionadded: 2.10
+    """
+
+    name = 'Savi'
+    url = 'https://github.com/savi-lang/savi'
+    aliases = ['savi']
+    filenames = ['*.savi']
+    version_added = ''
+
+    tokens = {
+      "root": [
+        # Line Comment
+        (r'//.*?$', Comment.Single),
+
+        # Doc Comment
+        (r'::.*?$', Comment.Single),
+
+        # Capability Operator
+        (r'(\')(\w+)(?=[^\'])', bygroups(Operator, Name)),
+
+        # Double-Quote String
+        (r'\w?"', String.Double, "string.double"),
+
+        # Single-Char String
+        (r"'", String.Char, "string.char"),
+
+        # Type Name
+        (r'(_?[A-Z]\w*)', Name.Class),
+
+        # Nested Type Name
+        (r'(\.)(\s*)(_?[A-Z]\w*)', bygroups(Punctuation, Whitespace, Name.Class)),
+
+        # Declare
+        (r'^([ \t]*)(:\w+)',
+          bygroups(Whitespace, Name.Tag),
+          "decl"),
+
+        # Error-Raising Calls/Names
+        (r'((\w+|\+|\-|\*)\!)', Generic.Deleted),
+
+        # Numeric Values
+        (r'\b\d([\d_]*(\.[\d_]+)?)\b', Number),
+
+        # Hex Numeric Values
+        (r'\b0x([0-9a-fA-F_]+)\b', Number.Hex),
+
+        # Binary Numeric Values
+        (r'\b0b([01_]+)\b', Number.Bin),
+
+        # Function Call (with braces)
+        (r'\w+(?=\()', Name.Function),
+
+        # Function Call (with receiver)
+        (r'(\.)(\s*)(\w+)', bygroups(Punctuation, Whitespace, Name.Function)),
+
+        # Function Call (with self receiver)
+        (r'(@)(\w+)', bygroups(Punctuation, Name.Function)),
+
+        # Parenthesis
+        (r'\(', Punctuation, "root"),
+        (r'\)', Punctuation, "#pop"),
+
+        # Brace
+        (r'\{', Punctuation, "root"),
+        (r'\}', Punctuation, "#pop"),
+
+        # Bracket
+        (r'\[', Punctuation, "root"),
+        (r'(\])(\!)', bygroups(Punctuation, Generic.Deleted), "#pop"),
+        (r'\]', Punctuation, "#pop"),
+
+        # Punctuation
+        (r'[,;:\.@]', Punctuation),
+
+        # Piping Operators
+        (r'(\|\>)', Operator),
+
+        # Branching Operators
+        (r'(\&\&|\|\||\?\?|\&\?|\|\?|\.\?)', Operator),
+
+        # Comparison Operators
+        (r'(\<\=\>|\=\~|\=\=|\<\=|\>\=|\<|\>)', Operator),
+
+        # Arithmetic Operators
+        (r'(\+|\-|\/|\*|\%)', Operator),
+
+        # Assignment Operators
+        (r'(\=)', Operator),
+
+        # Other Operators
+        (r'(\!|\<\<|\<|\&|\|)', Operator),
+
+        # Identifiers
+        (r'\b\w+\b', Name),
+
+        # Whitespace
+        (r'[ \t\r]+\n*|\n+', Whitespace),
+      ],
+
+      # Declare (nested rules)
+      "decl": [
+        (r'\b[a-z_]\w*\b(?!\!)', Keyword.Declaration),
+        (r':', Punctuation, "#pop"),
+        (r'\n', Whitespace, "#pop"),
+        include("root"),
+      ],
+
+      # Double-Quote String (nested rules)
+      "string.double": [
+        (r'\\\(', String.Interpol, "string.interpolation"),
+        (r'\\u[0-9a-fA-F]{4}', String.Escape),
+        (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        (r'\\[bfnrt\\\']', String.Escape),
+        (r'\\"', String.Escape),
+        (r'"', String.Double, "#pop"),
+        (r'[^\\"]+', String.Double),
+        (r'.', Error),
+      ],
+
+      # Single-Char String (nested rules)
+      "string.char": [
+        (r'\\u[0-9a-fA-F]{4}', String.Escape),
+        (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        (r'\\[bfnrt\\\']', String.Escape),
+        (r"\\'", String.Escape),
+        (r"'", String.Char, "#pop"),
+        (r"[^\\']+", String.Char),
+        (r'.', Error),
+      ],
+
+      # Interpolation inside String (nested rules)
+      "string.interpolation": [
+        (r"\)", String.Interpol, "#pop"),
+        include("root"),
+      ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/scdoc.py b/.venv/lib/python3.12/site-packages/pygments/lexers/scdoc.py
new file mode 100644
index 0000000..9b2e01d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/scdoc.py
@@ -0,0 +1,85 @@
+"""
+    pygments.lexers.scdoc
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for scdoc, a simple man page generator.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, using, this
+from pygments.token import Text, Comment, Keyword, String, Generic
+
+__all__ = ['ScdocLexer']
+
+
+class ScdocLexer(RegexLexer):
+    """
+    `scdoc` is a simple man page generator for POSIX systems written in C99.
+    """
+    name = 'scdoc'
+    url = 'https://git.sr.ht/~sircmpwn/scdoc'
+    aliases = ['scdoc', 'scd']
+    filenames = ['*.scd', '*.scdoc']
+    version_added = '2.5'
+    flags = re.MULTILINE
+
+    tokens = {
+        'root': [
+            # comment
+            (r'^(;.+\n)', bygroups(Comment)),
+
+            # heading with pound prefix
+            (r'^(#)([^#].+\n)', bygroups(Generic.Heading, Text)),
+            (r'^(#{2})(.+\n)', bygroups(Generic.Subheading, Text)),
+            # bulleted lists
+            (r'^(\s*)([*-])(\s)(.+\n)',
+            bygroups(Text, Keyword, Text, using(this, state='inline'))),
+            # numbered lists
+            (r'^(\s*)(\.+\.)( .+\n)',
+            bygroups(Text, Keyword, using(this, state='inline'))),
+            # quote
+            (r'^(\s*>\s)(.+\n)', bygroups(Keyword, Generic.Emph)),
+            # text block
+            (r'^(```\n)([\w\W]*?)(^```$)', bygroups(String, Text, String)),
+
+            include('inline'),
+        ],
+        'inline': [
+            # escape
+            (r'\\.', Text),
+            # underlines
+            (r'(\s)(_[^_]+_)(\W|\n)', bygroups(Text, Generic.Emph, Text)),
+            # bold
+            (r'(\s)(\*[^*]+\*)(\W|\n)', bygroups(Text, Generic.Strong, Text)),
+            # inline code
+            (r'`[^`]+`', String.Backtick),
+
+            # general text, must come last!
+            (r'[^\\\s]+', Text),
+            (r'.', Text),
+        ],
+    }
+
+    def analyse_text(text):
+        """We checks for bold and underline text with * and _. Also
+        every scdoc file must start with a strictly defined first line."""
+        result = 0
+
+        if '*' in text:
+            result += 0.01
+
+        if '_' in text:
+            result += 0.01
+
+        # name(section) ["left_footer" ["center_header"]]
+        first_line = text.partition('\n')[0]
+        scdoc_preamble_pattern = r'^.*\([1-7]\)( "[^"]+"){0,2}$'
+
+        if re.search(scdoc_preamble_pattern, first_line):
+            result += 0.5
+
+        return result
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/scripting.py b/.venv/lib/python3.12/site-packages/pygments/lexers/scripting.py
new file mode 100644
index 0000000..2adb7bb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/scripting.py
@@ -0,0 +1,1638 @@
+"""
+    pygments.lexers.scripting
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for scripting and embedded languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, default, combined, \
+    words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Error, Whitespace, Other
+from pygments.util import get_bool_opt, get_list_opt
+
+__all__ = ['LuaLexer', 'LuauLexer', 'MoonScriptLexer', 'ChaiscriptLexer', 'LSLLexer',
+           'AppleScriptLexer', 'RexxLexer', 'MOOCodeLexer', 'HybrisLexer',
+           'EasytrieveLexer', 'JclLexer', 'MiniScriptLexer']
+
+
+def all_lua_builtins():
+    from pygments.lexers._lua_builtins import MODULES
+    return [w for values in MODULES.values() for w in values]
+
+class LuaLexer(RegexLexer):
+    """
+    For Lua source code.
+
+    Additional options accepted:
+
+    `func_name_highlighting`
+        If given and ``True``, highlight builtin function names
+        (default: ``True``).
+    `disabled_modules`
+        If given, must be a list of module names whose function names
+        should not be highlighted. By default all modules are highlighted.
+
+        To get a list of allowed modules have a look into the
+        `_lua_builtins` module:
+
+        .. sourcecode:: pycon
+
+            >>> from pygments.lexers._lua_builtins import MODULES
+            >>> MODULES.keys()
+            ['string', 'coroutine', 'modules', 'io', 'basic', ...]
+    """
+
+    name = 'Lua'
+    url = 'https://www.lua.org/'
+    aliases = ['lua']
+    filenames = ['*.lua', '*.wlua']
+    mimetypes = ['text/x-lua', 'application/x-lua']
+    version_added = ''
+
+    _comment_multiline = r'(?:--\[(?P=*)\[[\w\W]*?\](?P=level)\])'
+    _comment_single = r'(?:--.*$)'
+    _space = r'(?:\s+(?!\s))'
+    _s = rf'(?:{_comment_multiline}|{_comment_single}|{_space})'
+    # A lookahead-safe version of _s that avoids catastrophic backtracking.
+    # The _comment_multiline pattern contains [\w\W]*? which, when used
+    # inside a lookahead with a * quantifier, causes exponential blowup.
+    # This version skips only whitespace; comments between an identifier
+    # and a following [.:] or ( are rare enough to sacrifice.
+    _s_la = r'\s'
+    _name = r'(?:[^\W\d]\w*)'
+
+    tokens = {
+        'root': [
+            # Lua allows a file to start with a shebang.
+            (r'#!.*', Comment.Preproc),
+            default('base'),
+        ],
+        'ws': [
+            (_comment_multiline, Comment.Multiline),
+            (_comment_single, Comment.Single),
+            (_space, Whitespace),
+        ],
+        'base': [
+            include('ws'),
+
+            (r'(?i)0x[\da-f]*(\.[\da-f]*)?(p[+-]?\d+)?', Number.Hex),
+            (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number.Float),
+            (r'(?i)\d+e[+-]?\d+', Number.Float),
+            (r'\d+', Number.Integer),
+
+            # multiline strings
+            (r'(?s)\[(=*)\[.*?\]\1\]', String),
+
+            (r'::', Punctuation, 'label'),
+            (r'\.{3}', Punctuation),
+            (r'[=<>|~&+\-*/%#^]+|\.\.', Operator),
+            (r'[\[\]{}().,:;]+', Punctuation),
+            (r'(and|or|not)\b', Operator.Word),
+
+            (words([
+                'break', 'do', 'else', 'elseif', 'end', 'for', 'if', 'in',
+                'repeat', 'return', 'then', 'until', 'while'
+            ], suffix=r'\b'), Keyword.Reserved),
+            (r'goto\b', Keyword.Reserved, 'goto'),
+            (r'(local)\b', Keyword.Declaration),
+            (r'(true|false|nil)\b', Keyword.Constant),
+
+            (r'(function)\b', Keyword.Reserved, 'funcname'),
+
+            (words(all_lua_builtins(), suffix=r"\b"), Name.Builtin),
+            (fr'[A-Za-z_]\w*(?={_s_la}*[.:])', Name.Variable, 'varname'),
+            (fr'[A-Za-z_]\w*(?={_s_la}*\()', Name.Function),
+            (r'[A-Za-z_]\w*', Name.Variable),
+
+            ("'", String.Single, combined('stringescape', 'sqs')),
+            ('"', String.Double, combined('stringescape', 'dqs'))
+        ],
+
+        'varname': [
+            include('ws'),
+            (r'\.\.', Operator, '#pop'),
+            (r'[.:]', Punctuation),
+            (rf'{_name}(?={_s_la}*[.:])', Name.Property),
+            (rf'{_name}(?={_s_la}*\()', Name.Function, '#pop'),
+            (_name, Name.Property, '#pop'),
+        ],
+
+        'funcname': [
+            include('ws'),
+            (r'[.:]', Punctuation),
+            (rf'{_name}(?={_s_la}*[.:])', Name.Class),
+            (_name, Name.Function, '#pop'),
+            # inline function
+            (r'\(', Punctuation, '#pop'),
+        ],
+
+        'goto': [
+            include('ws'),
+            (_name, Name.Label, '#pop'),
+        ],
+
+        'label': [
+            include('ws'),
+            (r'::', Punctuation, '#pop'),
+            (_name, Name.Label),
+        ],
+
+        'stringescape': [
+            (r'\\([abfnrtv\\"\']|[\r\n]{1,2}|z\s*|x[0-9a-fA-F]{2}|\d{1,3}|'
+             r'u\{[0-9a-fA-F]+\})', String.Escape),
+        ],
+
+        'sqs': [
+            (r"'", String.Single, '#pop'),
+            (r"[^\\']+", String.Single),
+        ],
+
+        'dqs': [
+            (r'"', String.Double, '#pop'),
+            (r'[^\\"]+', String.Double),
+        ]
+    }
+
+    def __init__(self, **options):
+        self.func_name_highlighting = get_bool_opt(
+            options, 'func_name_highlighting', True)
+        self.disabled_modules = get_list_opt(options, 'disabled_modules', [])
+
+        self._functions = set()
+        if self.func_name_highlighting:
+            from pygments.lexers._lua_builtins import MODULES
+            for mod, func in MODULES.items():
+                if mod not in self.disabled_modules:
+                    self._functions.update(func)
+        RegexLexer.__init__(self, **options)
+
+    def get_tokens_unprocessed(self, text):
+        for index, token, value in \
+                RegexLexer.get_tokens_unprocessed(self, text):
+            if token is Name.Builtin and value not in self._functions:
+                if '.' in value:
+                    a, b = value.split('.')
+                    yield index, Name, a
+                    yield index + len(a), Punctuation, '.'
+                    yield index + len(a) + 1, Name, b
+                else:
+                    yield index, Name, value
+                continue
+            yield index, token, value
+
+def _luau_make_expression(should_pop, _s, _s_la):
+    temp_list = [
+        (r'0[xX][\da-fA-F_]*', Number.Hex, '#pop'),
+        (r'0[bB][\d_]*', Number.Bin, '#pop'),
+        (r'\.?\d[\d_]*(?:\.[\d_]*)?(?:[eE][+-]?[\d_]+)?', Number.Float, '#pop'),
+
+        (words((
+            'true', 'false', 'nil'
+        ), suffix=r'\b'), Keyword.Constant, '#pop'),
+
+        (r'\[(=*)\[[.\n]*?\]\1\]', String, '#pop'),
+
+        (r'(\.)([a-zA-Z_]\w*)(?=%s*[({"\'])', bygroups(Punctuation, Name.Function), '#pop'),
+        (r'(\.)([a-zA-Z_]\w*)', bygroups(Punctuation, Name.Variable), '#pop'),
+
+        (rf'[a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*(?={_s_la}*[({{"\'])', Name.Other, '#pop'),
+        (r'[a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*', Name, '#pop'),
+    ]
+    if should_pop:
+        return temp_list
+    return [entry[:2] for entry in temp_list]
+
+def _luau_make_expression_special(should_pop):
+    temp_list = [
+        (r'\{', Punctuation, ('#pop', 'closing_brace_base', 'expression')),
+        (r'\(', Punctuation, ('#pop', 'closing_parenthesis_base', 'expression')),
+
+        (r'::?', Punctuation, ('#pop', 'type_end', 'type_start')),
+
+        (r"'", String.Single, ('#pop', 'string_single')),
+        (r'"', String.Double, ('#pop', 'string_double')),
+        (r'`', String.Backtick, ('#pop', 'string_interpolated')),
+    ]
+    if should_pop:
+        return temp_list
+    return [(entry[0], entry[1], entry[2][1:]) for entry in temp_list]
+
+class LuauLexer(RegexLexer):
+    """
+    For Luau source code.
+
+    Additional options accepted:
+
+    `include_luau_builtins`
+        If given and ``True``, automatically highlight Luau builtins
+        (default: ``True``).
+    `include_roblox_builtins`
+        If given and ``True``, automatically highlight Roblox-specific builtins
+        (default: ``False``).
+    `additional_builtins`
+        If given, must be a list of additional builtins to highlight.
+    `disabled_builtins`
+        If given, must be a list of builtins that will not be highlighted.
+    """
+
+    name = 'Luau'
+    url = 'https://luau-lang.org/'
+    aliases = ['luau']
+    filenames = ['*.luau']
+    version_added = '2.18'
+
+    _comment_multiline = r'(?:--\[(?P=*)\[[\w\W]*?\](?P=level)\])'
+    _comment_single = r'(?:--.*$)'
+    _s = r'(?:{}|{}|{})'.format(_comment_multiline, _comment_single, r'\s+')
+    # Lookahead-safe version — avoids catastrophic backtracking from
+    # [\w\W]*? inside _comment_multiline when combined with * quantifier.
+    _s_la = r'\s'
+
+    tokens = {
+        'root': [
+            (r'#!.*', Comment.Hashbang, 'base'),
+            default('base'),
+        ],
+
+        'ws': [
+            (_comment_multiline, Comment.Multiline),
+            (_comment_single, Comment.Single),
+            (r'\s+', Whitespace),
+        ],
+
+        'base': [
+            include('ws'),
+
+            *_luau_make_expression_special(False),
+            (r'\.\.\.', Punctuation),
+
+            (rf'type\b(?={_s}+[a-zA-Z_])', Keyword.Reserved, 'type_declaration'),
+            (rf'export\b(?={_s}+[a-zA-Z_])', Keyword.Reserved),
+
+            (r'(?:\.\.|//|[+\-*\/%^<>=])=?', Operator, 'expression'),
+            (r'~=', Operator, 'expression'),
+
+            (words((
+                'and', 'or', 'not'
+            ), suffix=r'\b'), Operator.Word, 'expression'),
+
+            (words((
+                'elseif', 'for', 'if', 'in', 'repeat', 'return', 'until',
+                'while'), suffix=r'\b'), Keyword.Reserved, 'expression'),
+            (r'local\b', Keyword.Declaration, 'expression'),
+
+            (r'function\b', Keyword.Reserved, ('expression', 'func_name')),
+
+            (r'[\])};]+', Punctuation),
+
+            include('expression_static'),
+            *_luau_make_expression(False, _s, _s_la),
+
+            (r'[\[.,]', Punctuation, 'expression'),
+        ],
+        'expression_static': [
+            (words((
+                'break', 'continue', 'do', 'else', 'elseif', 'end', 'for',
+                'if', 'in', 'repeat', 'return', 'then', 'until', 'while'),
+                suffix=r'\b'), Keyword.Reserved),
+        ],
+        'expression': [
+            include('ws'),
+
+            (r'if\b', Keyword.Reserved, ('ternary', 'expression')),
+
+            (r'local\b', Keyword.Declaration),
+            *_luau_make_expression_special(True),
+            (r'\.\.\.', Punctuation, '#pop'),
+
+            (r'function\b', Keyword.Reserved, 'func_name'),
+
+            include('expression_static'),
+            *_luau_make_expression(True, _s, _s_la),
+
+            default('#pop'),
+        ],
+        'ternary': [
+            include('ws'),
+
+            (r'else\b', Keyword.Reserved, '#pop'),
+            (words((
+                'then', 'elseif',
+            ), suffix=r'\b'), Operator.Reserved, 'expression'),
+
+            default('#pop'),
+        ],
+
+        'closing_brace_pop': [
+            (r'\}', Punctuation, '#pop'),
+        ],
+        'closing_parenthesis_pop': [
+            (r'\)', Punctuation, '#pop'),
+        ],
+        'closing_gt_pop': [
+            (r'>', Punctuation, '#pop'),
+        ],
+
+        'closing_parenthesis_base': [
+            include('closing_parenthesis_pop'),
+            include('base'),
+        ],
+        'closing_parenthesis_type': [
+            include('closing_parenthesis_pop'),
+            include('type'),
+        ],
+        'closing_brace_base': [
+            include('closing_brace_pop'),
+            include('base'),
+        ],
+        'closing_brace_type': [
+            include('closing_brace_pop'),
+            include('type'),
+        ],
+        'closing_gt_type': [
+            include('closing_gt_pop'),
+            include('type'),
+        ],
+
+        'string_escape': [
+            (r'\\z\s*', String.Escape),
+            (r'\\(?:[abfnrtvz\\"\'`\{\n])|[\r\n]{1,2}|x[\da-fA-F]{2}|\d{1,3}|'
+             r'u\{\}[\da-fA-F]*\}', String.Escape),
+        ],
+        'string_single': [
+            include('string_escape'),
+
+            (r"'", String.Single, "#pop"),
+            (r"[^\\']+", String.Single),
+        ],
+        'string_double': [
+            include('string_escape'),
+
+            (r'"', String.Double, "#pop"),
+            (r'[^\\"]+', String.Double),
+        ],
+        'string_interpolated': [
+            include('string_escape'),
+
+            (r'\{', Punctuation, ('closing_brace_base', 'expression')),
+
+            (r'`', String.Backtick, "#pop"),
+            (r'[^\\`\{]+', String.Backtick),
+        ],
+
+        'func_name': [
+            include('ws'),
+
+            (r'[.:]', Punctuation),
+            (rf'[a-zA-Z_]\w*(?={_s_la}*[.:])', Name.Class),
+            (r'[a-zA-Z_]\w*', Name.Function),
+
+            (r'<', Punctuation, 'closing_gt_type'),
+
+            (r'\(', Punctuation, '#pop'),
+        ],
+
+        'type': [
+            include('ws'),
+
+            (r'\(', Punctuation, 'closing_parenthesis_type'),
+            (r'\{', Punctuation, 'closing_brace_type'),
+            (r'<', Punctuation, 'closing_gt_type'),
+
+            (r"'", String.Single, 'string_single'),
+            (r'"', String.Double, 'string_double'),
+
+            (r'[|&\.,\[\]:=]+', Punctuation),
+            (r'->', Punctuation),
+
+            (r'typeof\(', Name.Builtin, ('closing_parenthesis_base',
+                                         'expression')),
+            (r'[a-zA-Z_]\w*', Name.Class),
+        ],
+        'type_start': [
+            include('ws'),
+
+            (r'\(', Punctuation, ('#pop', 'closing_parenthesis_type')),
+            (r'\{', Punctuation, ('#pop', 'closing_brace_type')),
+            (r'<', Punctuation, ('#pop', 'closing_gt_type')),
+
+            (r"'", String.Single, ('#pop', 'string_single')),
+            (r'"', String.Double, ('#pop', 'string_double')),
+
+            (r'typeof\(', Name.Builtin, ('#pop', 'closing_parenthesis_base',
+                                         'expression')),
+            (r'[a-zA-Z_]\w*', Name.Class, '#pop'),
+        ],
+        'type_end': [
+            include('ws'),
+
+            (r'[|&\.]', Punctuation, 'type_start'),
+            (r'->', Punctuation, 'type_start'),
+
+            (r'<', Punctuation, 'closing_gt_type'),
+
+            default('#pop'),
+        ],
+        'type_declaration': [
+            include('ws'),
+
+            (r'[a-zA-Z_]\w*', Name.Class),
+            (r'<', Punctuation, 'closing_gt_type'),
+
+            (r'=', Punctuation, ('#pop', 'type_end', 'type_start')),
+        ],
+    }
+
+    def __init__(self, **options):
+        self.include_luau_builtins = get_bool_opt(
+            options, 'include_luau_builtins', True)
+        self.include_roblox_builtins = get_bool_opt(
+            options, 'include_roblox_builtins', False)
+        self.additional_builtins = get_list_opt(options, 'additional_builtins', [])
+        self.disabled_builtins = get_list_opt(options, 'disabled_builtins', [])
+
+        self._builtins = set(self.additional_builtins)
+        if self.include_luau_builtins:
+            from pygments.lexers._luau_builtins import LUAU_BUILTINS
+            self._builtins.update(LUAU_BUILTINS)
+        if self.include_roblox_builtins:
+            from pygments.lexers._luau_builtins import ROBLOX_BUILTINS
+            self._builtins.update(ROBLOX_BUILTINS)
+        if self.additional_builtins:
+            self._builtins.update(self.additional_builtins)
+        self._builtins.difference_update(self.disabled_builtins)
+
+        RegexLexer.__init__(self, **options)
+
+    def get_tokens_unprocessed(self, text):
+        for index, token, value in \
+                RegexLexer.get_tokens_unprocessed(self, text):
+            if token is Name or token is Name.Other:
+                split_value = value.split('.')
+                complete_value = []
+                new_index = index
+                for position in range(len(split_value), 0, -1):
+                    potential_string = '.'.join(split_value[:position])
+                    if potential_string in self._builtins:
+                        yield index, Name.Builtin, potential_string
+                        new_index += len(potential_string)
+
+                        if complete_value:
+                            yield new_index, Punctuation, '.'
+                            new_index += 1
+                        break
+                    complete_value.insert(0, split_value[position - 1])
+
+                for position, substring in enumerate(complete_value):
+                    if position + 1 == len(complete_value):
+                        if token is Name:
+                            yield new_index, Name.Variable, substring
+                            continue
+                        yield new_index, Name.Function, substring
+                        continue
+                    yield new_index, Name.Variable, substring
+                    new_index += len(substring)
+                    yield new_index, Punctuation, '.'
+                    new_index += 1
+
+                continue
+            yield index, token, value
+
+class MoonScriptLexer(LuaLexer):
+    """
+    For MoonScript source code.
+    """
+
+    name = 'MoonScript'
+    url = 'http://moonscript.org'
+    aliases = ['moonscript', 'moon']
+    filenames = ['*.moon']
+    mimetypes = ['text/x-moonscript', 'application/x-moonscript']
+    version_added = '1.5'
+
+    tokens = {
+        'root': [
+            (r'#!(.*?)$', Comment.Preproc),
+            default('base'),
+        ],
+        'base': [
+            ('--.*$', Comment.Single),
+            (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number.Float),
+            (r'(?i)\d+e[+-]?\d+', Number.Float),
+            (r'(?i)0x[0-9a-f]*', Number.Hex),
+            (r'\d+', Number.Integer),
+            (r'\n', Whitespace),
+            (r'[^\S\n]+', Text),
+            (r'(?s)\[(=*)\[.*?\]\1\]', String),
+            (r'(->|=>)', Name.Function),
+            (r':[a-zA-Z_]\w*', Name.Variable),
+            (r'(==|!=|~=|<=|>=|\.\.\.|\.\.|[=+\-*/%^<>#!.\\:])', Operator),
+            (r'[;,]', Punctuation),
+            (r'[\[\]{}()]', Keyword.Type),
+            (r'[a-zA-Z_]\w*:', Name.Variable),
+            (words((
+                'class', 'extends', 'if', 'then', 'super', 'do', 'with',
+                'import', 'export', 'while', 'elseif', 'return', 'for', 'in',
+                'from', 'when', 'using', 'else', 'and', 'or', 'not', 'switch',
+                'break'), suffix=r'\b'),
+             Keyword),
+            (r'(true|false|nil)\b', Keyword.Constant),
+            (r'(and|or|not)\b', Operator.Word),
+            (r'(self)\b', Name.Builtin.Pseudo),
+            (r'@@?([a-zA-Z_]\w*)?', Name.Variable.Class),
+            (r'[A-Z]\w*', Name.Class),  # proper name
+            (words(all_lua_builtins(), suffix=r"\b"), Name.Builtin),
+            (r'[A-Za-z_]\w*', Name),
+            ("'", String.Single, combined('stringescape', 'sqs')),
+            ('"', String.Double, combined('stringescape', 'dqs'))
+        ],
+        'stringescape': [
+            (r'''\\([abfnrtv\\"']|\d{1,3})''', String.Escape)
+        ],
+        'strings': [
+            (r'[^#\\\'"]+', String),
+            # note that strings are multi-line.
+            # hashmarks, quotes and backslashes must be parsed one at a time
+        ],
+        'interpoling_string': [
+            (r'\}', String.Interpol, "#pop"),
+            include('base')
+        ],
+        'dqs': [
+            (r'"', String.Double, '#pop'),
+            (r'\\.|\'', String),  # double-quoted string don't need ' escapes
+            (r'#\{', String.Interpol, "interpoling_string"),
+            (r'#', String),
+            include('strings')
+        ],
+        'sqs': [
+            (r"'", String.Single, '#pop'),
+            (r'#|\\.|"', String),  # single quoted strings don't need " escapses
+            include('strings')
+        ]
+    }
+
+    def get_tokens_unprocessed(self, text):
+        # set . as Operator instead of Punctuation
+        for index, token, value in LuaLexer.get_tokens_unprocessed(self, text):
+            if token == Punctuation and value == ".":
+                token = Operator
+            yield index, token, value
+
+
+class ChaiscriptLexer(RegexLexer):
+    """
+    For ChaiScript source code.
+    """
+
+    name = 'ChaiScript'
+    url = 'http://chaiscript.com/'
+    aliases = ['chaiscript', 'chai']
+    filenames = ['*.chai']
+    mimetypes = ['text/x-chaiscript', 'application/x-chaiscript']
+    version_added = '2.0'
+
+    flags = re.DOTALL | re.MULTILINE
+
+    tokens = {
+        'commentsandwhitespace': [
+            (r'\s+', Text),
+            (r'//.*?\n', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline),
+            (r'^\#.*?\n', Comment.Single)
+        ],
+        'slashstartsregex': [
+            include('commentsandwhitespace'),
+            (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
+             r'([gim]+\b|\B)', String.Regex, '#pop'),
+            (r'(?=/)', Text, ('#pop', 'badregex')),
+            default('#pop')
+        ],
+        'badregex': [
+            (r'\n', Text, '#pop')
+        ],
+        'root': [
+            include('commentsandwhitespace'),
+            (r'\n', Text),
+            (r'[^\S\n]+', Text),
+            (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|\.\.'
+             r'(<<|>>>?|==?|!=?|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'),
+            (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
+            (r'[})\].]', Punctuation),
+            (r'[=+\-*/]', Operator),
+            (r'(for|in|while|do|break|return|continue|if|else|'
+             r'throw|try|catch'
+             r')\b', Keyword, 'slashstartsregex'),
+            (r'(var)\b', Keyword.Declaration, 'slashstartsregex'),
+            (r'(attr|def|fun)\b', Keyword.Reserved),
+            (r'(true|false)\b', Keyword.Constant),
+            (r'(eval|throw)\b', Name.Builtin),
+            (r'`\S+`', Name.Builtin),
+            (r'[$a-zA-Z_]\w*', Name.Other),
+            (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'[0-9]+', Number.Integer),
+            (r'"', String.Double, 'dqstring'),
+            (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
+        ],
+        'dqstring': [
+            (r'\$\{[^"}]+?\}', String.Interpol),
+            (r'\$', String.Double),
+            (r'\\\\', String.Double),
+            (r'\\"', String.Double),
+            (r'[^\\"$]+', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+    }
+
+
+class LSLLexer(RegexLexer):
+    """
+    For Second Life's Linden Scripting Language source code.
+    """
+
+    name = 'LSL'
+    aliases = ['lsl']
+    filenames = ['*.lsl']
+    mimetypes = ['text/x-lsl']
+    url = 'https://wiki.secondlife.com/wiki/Linden_Scripting_Language'
+    version_added = '2.0'
+
+    flags = re.MULTILINE
+
+    lsl_keywords = r'\b(?:do|else|for|if|jump|return|while)\b'
+    lsl_types = r'\b(?:float|integer|key|list|quaternion|rotation|string|vector)\b'
+    lsl_states = r'\b(?:(?:state)\s+\w+|default)\b'
+    lsl_events = r'\b(?:state_(?:entry|exit)|touch(?:_(?:start|end))?|(?:land_)?collision(?:_(?:start|end))?|timer|listen|(?:no_)?sensor|control|(?:not_)?at_(?:rot_)?target|money|email|run_time_permissions|changed|attach|dataserver|moving_(?:start|end)|link_message|(?:on|object)_rez|remote_data|http_re(?:sponse|quest)|path_update|transaction_result)\b'
+    lsl_functions_builtin = r'\b(?:ll(?:ReturnObjectsBy(?:ID|Owner)|Json(?:2List|[GS]etValue|ValueType)|Sin|Cos|Tan|Atan2|Sqrt|Pow|Abs|Fabs|Frand|Floor|Ceil|Round|Vec(?:Mag|Norm|Dist)|Rot(?:Between|2(?:Euler|Fwd|Left|Up))|(?:Euler|Axes)2Rot|Whisper|(?:Region|Owner)?Say|Shout|Listen(?:Control|Remove)?|Sensor(?:Repeat|Remove)?|Detected(?:Name|Key|Owner|Type|Pos|Vel|Grab|Rot|Group|LinkNumber)|Die|Ground|Wind|(?:[GS]et)(?:AnimationOverride|MemoryLimit|PrimMediaParams|ParcelMusicURL|Object(?:Desc|Name)|PhysicsMaterial|Status|Scale|Color|Alpha|Texture|Pos|Rot|Force|Torque)|ResetAnimationOverride|(?:Scale|Offset|Rotate)Texture|(?:Rot)?Target(?:Remove)?|(?:Stop)?MoveToTarget|Apply(?:Rotational)?Impulse|Set(?:KeyframedMotion|ContentType|RegionPos|(?:Angular)?Velocity|Buoyancy|HoverHeight|ForceAndTorque|TimerEvent|ScriptState|Damage|TextureAnim|Sound(?:Queueing|Radius)|Vehicle(?:Type|(?:Float|Vector|Rotation)Param)|(?:Touch|Sit)?Text|Camera(?:Eye|At)Offset|PrimitiveParams|ClickAction|Link(?:Alpha|Color|PrimitiveParams(?:Fast)?|Texture(?:Anim)?|Camera|Media)|RemoteScriptAccessPin|PayPrice|LocalRot)|ScaleByFactor|Get(?:(?:Max|Min)ScaleFactor|ClosestNavPoint|StaticPath|SimStats|Env|PrimitiveParams|Link(?:PrimitiveParams|Number(?:OfSides)?|Key|Name|Media)|HTTPHeader|FreeURLs|Object(?:Details|PermMask|PrimCount)|Parcel(?:MaxPrims|Details|Prim(?:Count|Owners))|Attached|(?:SPMax|Free|Used)Memory|Region(?:Name|TimeDilation|FPS|Corner|AgentCount)|Root(?:Position|Rotation)|UnixTime|(?:Parcel|Region)Flags|(?:Wall|GMT)clock|SimulatorHostname|BoundingBox|GeometricCenter|Creator|NumberOf(?:Prims|NotecardLines|Sides)|Animation(?:List)?|(?:Camera|Local)(?:Pos|Rot)|Vel|Accel|Omega|Time(?:stamp|OfDay)|(?:Object|CenterOf)?Mass|MassMKS|Energy|Owner|(?:Owner)?Key|SunDirection|Texture(?:Offset|Scale|Rot)|Inventory(?:Number|Name|Key|Type|Creator|PermMask)|Permissions(?:Key)?|StartParameter|List(?:Length|EntryType)|Date|Agent(?:Size|Info|Language|List)|LandOwnerAt|NotecardLine|Script(?:Name|State))|(?:Get|Reset|GetAndReset)Time|PlaySound(?:Slave)?|LoopSound(?:Master|Slave)?|(?:Trigger|Stop|Preload)Sound|(?:(?:Get|Delete)Sub|Insert)String|To(?:Upper|Lower)|Give(?:InventoryList|Money)|RezObject|(?:Stop)?LookAt|Sleep|CollisionFilter|(?:Take|Release)Controls|DetachFromAvatar|AttachToAvatar(?:Temp)?|InstantMessage|(?:GetNext)?Email|StopHover|MinEventDelay|RotLookAt|String(?:Length|Trim)|(?:Start|Stop)Animation|TargetOmega|RequestPermissions|(?:Create|Break)Link|BreakAllLinks|(?:Give|Remove)Inventory|Water|PassTouches|Request(?:Agent|Inventory)Data|TeleportAgent(?:Home|GlobalCoords)?|ModifyLand|CollisionSound|ResetScript|MessageLinked|PushObject|PassCollisions|AxisAngle2Rot|Rot2(?:Axis|Angle)|A(?:cos|sin)|AngleBetween|AllowInventoryDrop|SubStringIndex|List2(?:CSV|Integer|Json|Float|String|Key|Vector|Rot|List(?:Strided)?)|DeleteSubList|List(?:Statistics|Sort|Randomize|(?:Insert|Find|Replace)List)|EdgeOfWorld|AdjustSoundVolume|Key2Name|TriggerSoundLimited|EjectFromLand|(?:CSV|ParseString)2List|OverMyLand|SameGroup|UnSit|Ground(?:Slope|Normal|Contour)|GroundRepel|(?:Set|Remove)VehicleFlags|(?:AvatarOn)?(?:Link)?SitTarget|Script(?:Danger|Profiler)|Dialog|VolumeDetect|ResetOtherScript|RemoteLoadScriptPin|(?:Open|Close)RemoteDataChannel|SendRemoteData|RemoteDataReply|(?:Integer|String)ToBase64|XorBase64|Log(?:10)?|Base64To(?:String|Integer)|ParseStringKeepNulls|RezAtRoot|RequestSimulatorData|ForceMouselook|(?:Load|Release|(?:E|Une)scape)URL|ParcelMedia(?:CommandList|Query)|ModPow|MapDestination|(?:RemoveFrom|AddTo|Reset)Land(?:Pass|Ban)List|(?:Set|Clear)CameraParams|HTTP(?:Request|Response)|TextBox|DetectedTouch(?:UV|Face|Pos|(?:N|Bin)ormal|ST)|(?:MD5|SHA1|DumpList2)String|Request(?:Secure)?URL|Clear(?:Prim|Link)Media|(?:Link)?ParticleSystem|(?:Get|Request)(?:Username|DisplayName)|RegionSayTo|CastRay|GenerateKey|TransferLindenDollars|ManageEstateAccess|(?:Create|Delete)Character|ExecCharacterCmd|Evade|FleeFrom|NavigateTo|PatrolPoints|Pursue|UpdateCharacter|WanderWithin))\b'
+    lsl_constants_float = r'\b(?:DEG_TO_RAD|PI(?:_BY_TWO)?|RAD_TO_DEG|SQRT2|TWO_PI)\b'
+    lsl_constants_integer = r'\b(?:JSON_APPEND|STATUS_(?:PHYSICS|ROTATE_[XYZ]|PHANTOM|SANDBOX|BLOCK_GRAB(?:_OBJECT)?|(?:DIE|RETURN)_AT_EDGE|CAST_SHADOWS|OK|MALFORMED_PARAMS|TYPE_MISMATCH|BOUNDS_ERROR|NOT_(?:FOUND|SUPPORTED)|INTERNAL_ERROR|WHITELIST_FAILED)|AGENT(?:_(?:BY_(?:LEGACY_|USER)NAME|FLYING|ATTACHMENTS|SCRIPTED|MOUSELOOK|SITTING|ON_OBJECT|AWAY|WALKING|IN_AIR|TYPING|CROUCHING|BUSY|ALWAYS_RUN|AUTOPILOT|LIST_(?:PARCEL(?:_OWNER)?|REGION)))?|CAMERA_(?:PITCH|DISTANCE|BEHINDNESS_(?:ANGLE|LAG)|(?:FOCUS|POSITION)(?:_(?:THRESHOLD|LOCKED|LAG))?|FOCUS_OFFSET|ACTIVE)|ANIM_ON|LOOP|REVERSE|PING_PONG|SMOOTH|ROTATE|SCALE|ALL_SIDES|LINK_(?:ROOT|SET|ALL_(?:OTHERS|CHILDREN)|THIS)|ACTIVE|PASSIVE|SCRIPTED|CONTROL_(?:FWD|BACK|(?:ROT_)?(?:LEFT|RIGHT)|UP|DOWN|(?:ML_)?LBUTTON)|PERMISSION_(?:RETURN_OBJECTS|DEBIT|OVERRIDE_ANIMATIONS|SILENT_ESTATE_MANAGEMENT|TAKE_CONTROLS|TRIGGER_ANIMATION|ATTACH|CHANGE_LINKS|(?:CONTROL|TRACK)_CAMERA|TELEPORT)|INVENTORY_(?:TEXTURE|SOUND|OBJECT|SCRIPT|LANDMARK|CLOTHING|NOTECARD|BODYPART|ANIMATION|GESTURE|ALL|NONE)|CHANGED_(?:INVENTORY|COLOR|SHAPE|SCALE|TEXTURE|LINK|ALLOWED_DROP|OWNER|REGION(?:_START)?|TELEPORT|MEDIA)|OBJECT_(?:(?:PHYSICS|SERVER|STREAMING)_COST|UNKNOWN_DETAIL|CHARACTER_TIME|PHANTOM|PHYSICS|TEMP_ON_REZ|NAME|DESC|POS|PRIM_EQUIVALENCE|RETURN_(?:PARCEL(?:_OWNER)?|REGION)|ROO?T|VELOCITY|OWNER|GROUP|CREATOR|ATTACHED_POINT|RENDER_WEIGHT|PATHFINDING_TYPE|(?:RUNNING|TOTAL)_SCRIPT_COUNT|SCRIPT_(?:MEMORY|TIME))|TYPE_(?:INTEGER|FLOAT|STRING|KEY|VECTOR|ROTATION|INVALID)|(?:DEBUG|PUBLIC)_CHANNEL|ATTACH_(?:AVATAR_CENTER|CHEST|HEAD|BACK|PELVIS|MOUTH|CHIN|NECK|NOSE|BELLY|[LR](?:SHOULDER|HAND|FOOT|EAR|EYE|[UL](?:ARM|LEG)|HIP)|(?:LEFT|RIGHT)_PEC|HUD_(?:CENTER_[12]|TOP_(?:RIGHT|CENTER|LEFT)|BOTTOM(?:_(?:RIGHT|LEFT))?))|LAND_(?:LEVEL|RAISE|LOWER|SMOOTH|NOISE|REVERT)|DATA_(?:ONLINE|NAME|BORN|SIM_(?:POS|STATUS|RATING)|PAYINFO)|PAYMENT_INFO_(?:ON_FILE|USED)|REMOTE_DATA_(?:CHANNEL|REQUEST|REPLY)|PSYS_(?:PART_(?:BF_(?:ZERO|ONE(?:_MINUS_(?:DEST_COLOR|SOURCE_(ALPHA|COLOR)))?|DEST_COLOR|SOURCE_(ALPHA|COLOR))|BLEND_FUNC_(DEST|SOURCE)|FLAGS|(?:START|END)_(?:COLOR|ALPHA|SCALE|GLOW)|MAX_AGE|(?:RIBBON|WIND|INTERP_(?:COLOR|SCALE)|BOUNCE|FOLLOW_(?:SRC|VELOCITY)|TARGET_(?:POS|LINEAR)|EMISSIVE)_MASK)|SRC_(?:MAX_AGE|PATTERN|ANGLE_(?:BEGIN|END)|BURST_(?:RATE|PART_COUNT|RADIUS|SPEED_(?:MIN|MAX))|ACCEL|TEXTURE|TARGET_KEY|OMEGA|PATTERN_(?:DROP|EXPLODE|ANGLE(?:_CONE(?:_EMPTY)?)?)))|VEHICLE_(?:REFERENCE_FRAME|TYPE_(?:NONE|SLED|CAR|BOAT|AIRPLANE|BALLOON)|(?:LINEAR|ANGULAR)_(?:FRICTION_TIMESCALE|MOTOR_DIRECTION)|LINEAR_MOTOR_OFFSET|HOVER_(?:HEIGHT|EFFICIENCY|TIMESCALE)|BUOYANCY|(?:LINEAR|ANGULAR)_(?:DEFLECTION_(?:EFFICIENCY|TIMESCALE)|MOTOR_(?:DECAY_)?TIMESCALE)|VERTICAL_ATTRACTION_(?:EFFICIENCY|TIMESCALE)|BANKING_(?:EFFICIENCY|MIX|TIMESCALE)|FLAG_(?:NO_DEFLECTION_UP|LIMIT_(?:ROLL_ONLY|MOTOR_UP)|HOVER_(?:(?:WATER|TERRAIN|UP)_ONLY|GLOBAL_HEIGHT)|MOUSELOOK_(?:STEER|BANK)|CAMERA_DECOUPLED))|PRIM_(?:TYPE(?:_(?:BOX|CYLINDER|PRISM|SPHERE|TORUS|TUBE|RING|SCULPT))?|HOLE_(?:DEFAULT|CIRCLE|SQUARE|TRIANGLE)|MATERIAL(?:_(?:STONE|METAL|GLASS|WOOD|FLESH|PLASTIC|RUBBER))?|SHINY_(?:NONE|LOW|MEDIUM|HIGH)|BUMP_(?:NONE|BRIGHT|DARK|WOOD|BARK|BRICKS|CHECKER|CONCRETE|TILE|STONE|DISKS|GRAVEL|BLOBS|SIDING|LARGETILE|STUCCO|SUCTION|WEAVE)|TEXGEN_(?:DEFAULT|PLANAR)|SCULPT_(?:TYPE_(?:SPHERE|TORUS|PLANE|CYLINDER|MASK)|FLAG_(?:MIRROR|INVERT))|PHYSICS(?:_(?:SHAPE_(?:CONVEX|NONE|PRIM|TYPE)))?|(?:POS|ROT)_LOCAL|SLICE|TEXT|FLEXIBLE|POINT_LIGHT|TEMP_ON_REZ|PHANTOM|POSITION|SIZE|ROTATION|TEXTURE|NAME|OMEGA|DESC|LINK_TARGET|COLOR|BUMP_SHINY|FULLBRIGHT|TEXGEN|GLOW|MEDIA_(?:ALT_IMAGE_ENABLE|CONTROLS|(?:CURRENT|HOME)_URL|AUTO_(?:LOOP|PLAY|SCALE|ZOOM)|FIRST_CLICK_INTERACT|(?:WIDTH|HEIGHT)_PIXELS|WHITELIST(?:_ENABLE)?|PERMS_(?:INTERACT|CONTROL)|PARAM_MAX|CONTROLS_(?:STANDARD|MINI)|PERM_(?:NONE|OWNER|GROUP|ANYONE)|MAX_(?:URL_LENGTH|WHITELIST_(?:SIZE|COUNT)|(?:WIDTH|HEIGHT)_PIXELS)))|MASK_(?:BASE|OWNER|GROUP|EVERYONE|NEXT)|PERM_(?:TRANSFER|MODIFY|COPY|MOVE|ALL)|PARCEL_(?:MEDIA_COMMAND_(?:STOP|PAUSE|PLAY|LOOP|TEXTURE|URL|TIME|AGENT|UNLOAD|AUTO_ALIGN|TYPE|SIZE|DESC|LOOP_SET)|FLAG_(?:ALLOW_(?:FLY|(?:GROUP_)?SCRIPTS|LANDMARK|TERRAFORM|DAMAGE|CREATE_(?:GROUP_)?OBJECTS)|USE_(?:ACCESS_(?:GROUP|LIST)|BAN_LIST|LAND_PASS_LIST)|LOCAL_SOUND_ONLY|RESTRICT_PUSHOBJECT|ALLOW_(?:GROUP|ALL)_OBJECT_ENTRY)|COUNT_(?:TOTAL|OWNER|GROUP|OTHER|SELECTED|TEMP)|DETAILS_(?:NAME|DESC|OWNER|GROUP|AREA|ID|SEE_AVATARS))|LIST_STAT_(?:MAX|MIN|MEAN|MEDIAN|STD_DEV|SUM(?:_SQUARES)?|NUM_COUNT|GEOMETRIC_MEAN|RANGE)|PAY_(?:HIDE|DEFAULT)|REGION_FLAG_(?:ALLOW_DAMAGE|FIXED_SUN|BLOCK_TERRAFORM|SANDBOX|DISABLE_(?:COLLISIONS|PHYSICS)|BLOCK_FLY|ALLOW_DIRECT_TELEPORT|RESTRICT_PUSHOBJECT)|HTTP_(?:METHOD|MIMETYPE|BODY_(?:MAXLENGTH|TRUNCATED)|CUSTOM_HEADER|PRAGMA_NO_CACHE|VERBOSE_THROTTLE|VERIFY_CERT)|STRING_(?:TRIM(?:_(?:HEAD|TAIL))?)|CLICK_ACTION_(?:NONE|TOUCH|SIT|BUY|PAY|OPEN(?:_MEDIA)?|PLAY|ZOOM)|TOUCH_INVALID_FACE|PROFILE_(?:NONE|SCRIPT_MEMORY)|RC_(?:DATA_FLAGS|DETECT_PHANTOM|GET_(?:LINK_NUM|NORMAL|ROOT_KEY)|MAX_HITS|REJECT_(?:TYPES|AGENTS|(?:NON)?PHYSICAL|LAND))|RCERR_(?:CAST_TIME_EXCEEDED|SIM_PERF_LOW|UNKNOWN)|ESTATE_ACCESS_(?:ALLOWED_(?:AGENT|GROUP)_(?:ADD|REMOVE)|BANNED_AGENT_(?:ADD|REMOVE))|DENSITY|FRICTION|RESTITUTION|GRAVITY_MULTIPLIER|KFM_(?:COMMAND|CMD_(?:PLAY|STOP|PAUSE|SET_MODE)|MODE|FORWARD|LOOP|PING_PONG|REVERSE|DATA|ROTATION|TRANSLATION)|ERR_(?:GENERIC|PARCEL_PERMISSIONS|MALFORMED_PARAMS|RUNTIME_PERMISSIONS|THROTTLED)|CHARACTER_(?:CMD_(?:(?:SMOOTH_)?STOP|JUMP)|DESIRED_(?:TURN_)?SPEED|RADIUS|STAY_WITHIN_PARCEL|LENGTH|ORIENTATION|ACCOUNT_FOR_SKIPPED_FRAMES|AVOIDANCE_MODE|TYPE(?:_(?:[A-D]|NONE))?|MAX_(?:DECEL|TURN_RADIUS|(?:ACCEL|SPEED)))|PURSUIT_(?:OFFSET|FUZZ_FACTOR|GOAL_TOLERANCE|INTERCEPT)|REQUIRE_LINE_OF_SIGHT|FORCE_DIRECT_PATH|VERTICAL|HORIZONTAL|AVOID_(?:CHARACTERS|DYNAMIC_OBSTACLES|NONE)|PU_(?:EVADE_(?:HIDDEN|SPOTTED)|FAILURE_(?:DYNAMIC_PATHFINDING_DISABLED|INVALID_(?:GOAL|START)|NO_(?:NAVMESH|VALID_DESTINATION)|OTHER|TARGET_GONE|(?:PARCEL_)?UNREACHABLE)|(?:GOAL|SLOWDOWN_DISTANCE)_REACHED)|TRAVERSAL_TYPE(?:_(?:FAST|NONE|SLOW))?|CONTENT_TYPE_(?:ATOM|FORM|HTML|JSON|LLSD|RSS|TEXT|XHTML|XML)|GCNP_(?:RADIUS|STATIC)|(?:PATROL|WANDER)_PAUSE_AT_WAYPOINTS|OPT_(?:AVATAR|CHARACTER|EXCLUSION_VOLUME|LEGACY_LINKSET|MATERIAL_VOLUME|OTHER|STATIC_OBSTACLE|WALKABLE)|SIM_STAT_PCT_CHARS_STEPPED)\b'
+    lsl_constants_integer_boolean = r'\b(?:FALSE|TRUE)\b'
+    lsl_constants_rotation = r'\b(?:ZERO_ROTATION)\b'
+    lsl_constants_string = r'\b(?:EOF|JSON_(?:ARRAY|DELETE|FALSE|INVALID|NULL|NUMBER|OBJECT|STRING|TRUE)|NULL_KEY|TEXTURE_(?:BLANK|DEFAULT|MEDIA|PLYWOOD|TRANSPARENT)|URL_REQUEST_(?:GRANTED|DENIED))\b'
+    lsl_constants_vector = r'\b(?:TOUCH_INVALID_(?:TEXCOORD|VECTOR)|ZERO_VECTOR)\b'
+    lsl_invalid_broken = r'\b(?:LAND_(?:LARGE|MEDIUM|SMALL)_BRUSH)\b'
+    lsl_invalid_deprecated = r'\b(?:ATTACH_[LR]PEC|DATA_RATING|OBJECT_ATTACHMENT_(?:GEOMETRY_BYTES|SURFACE_AREA)|PRIM_(?:CAST_SHADOWS|MATERIAL_LIGHT|TYPE_LEGACY)|PSYS_SRC_(?:INNER|OUTER)ANGLE|VEHICLE_FLAG_NO_FLY_UP|ll(?:Cloud|Make(?:Explosion|Fountain|Smoke|Fire)|RemoteDataSetRegion|Sound(?:Preload)?|XorBase64Strings(?:Correct)?))\b'
+    lsl_invalid_illegal = r'\b(?:event)\b'
+    lsl_invalid_unimplemented = r'\b(?:CHARACTER_(?:MAX_ANGULAR_(?:ACCEL|SPEED)|TURN_SPEED_MULTIPLIER)|PERMISSION_(?:CHANGE_(?:JOINTS|PERMISSIONS)|RELEASE_OWNERSHIP|REMAP_CONTROLS)|PRIM_PHYSICS_MATERIAL|PSYS_SRC_OBJ_REL_MASK|ll(?:CollisionSprite|(?:Stop)?PointAt|(?:(?:Refresh|Set)Prim)URL|(?:Take|Release)Camera|RemoteLoadScript))\b'
+    lsl_reserved_godmode = r'\b(?:ll(?:GodLikeRezObject|Set(?:Inventory|Object)PermMask))\b'
+    lsl_reserved_log = r'\b(?:print)\b'
+    lsl_operators = r'\+\+|\-\-|<<|>>|&&?|\|\|?|\^|~|[!%<>=*+\-/]=?'
+
+    tokens = {
+        'root':
+        [
+            (r'//.*?\n',                          Comment.Single),
+            (r'/\*',                              Comment.Multiline, 'comment'),
+            (r'"',                                String.Double, 'string'),
+            (lsl_keywords,                        Keyword),
+            (lsl_types,                           Keyword.Type),
+            (lsl_states,                          Name.Class),
+            (lsl_events,                          Name.Builtin),
+            (lsl_functions_builtin,               Name.Function),
+            (lsl_constants_float,                 Keyword.Constant),
+            (lsl_constants_integer,               Keyword.Constant),
+            (lsl_constants_integer_boolean,       Keyword.Constant),
+            (lsl_constants_rotation,              Keyword.Constant),
+            (lsl_constants_string,                Keyword.Constant),
+            (lsl_constants_vector,                Keyword.Constant),
+            (lsl_invalid_broken,                  Error),
+            (lsl_invalid_deprecated,              Error),
+            (lsl_invalid_illegal,                 Error),
+            (lsl_invalid_unimplemented,           Error),
+            (lsl_reserved_godmode,                Keyword.Reserved),
+            (lsl_reserved_log,                    Keyword.Reserved),
+            (r'\b([a-zA-Z_]\w*)\b',     Name.Variable),
+            (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d*', Number.Float),
+            (r'(\d+\.\d*|\.\d+)',                 Number.Float),
+            (r'0[xX][0-9a-fA-F]+',                Number.Hex),
+            (r'\d+',                              Number.Integer),
+            (lsl_operators,                       Operator),
+            (r':=?',                              Error),
+            (r'[,;{}()\[\]]',                     Punctuation),
+            (r'\n+',                              Whitespace),
+            (r'\s+',                              Whitespace)
+        ],
+        'comment':
+        [
+            (r'[^*/]+',                           Comment.Multiline),
+            (r'/\*',                              Comment.Multiline, '#push'),
+            (r'\*/',                              Comment.Multiline, '#pop'),
+            (r'[*/]',                             Comment.Multiline)
+        ],
+        'string':
+        [
+            (r'\\([nt"\\])',                      String.Escape),
+            (r'"',                                String.Double, '#pop'),
+            (r'\\.',                              Error),
+            (r'[^"\\]+',                          String.Double),
+        ]
+    }
+
+
+class AppleScriptLexer(RegexLexer):
+    """
+    For AppleScript source code,
+    including `AppleScript Studio
+    `_.
+    Contributed by Andreas Amann .
+    """
+
+    name = 'AppleScript'
+    url = 'https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html'
+    aliases = ['applescript']
+    filenames = ['*.applescript']
+    version_added = '1.0'
+
+    flags = re.MULTILINE | re.DOTALL
+
+    Identifiers = r'[a-zA-Z]\w*'
+
+    # XXX: use words() for all of these
+    Literals = ('AppleScript', 'current application', 'false', 'linefeed',
+                'missing value', 'pi', 'quote', 'result', 'return', 'space',
+                'tab', 'text item delimiters', 'true', 'version')
+    Classes = ('alias ', 'application ', 'boolean ', 'class ', 'constant ',
+               'date ', 'file ', 'integer ', 'list ', 'number ', 'POSIX file ',
+               'real ', 'record ', 'reference ', 'RGB color ', 'script ',
+               'text ', 'unit types', '(?:Unicode )?text', 'string')
+    BuiltIn = ('attachment', 'attribute run', 'character', 'day', 'month',
+               'paragraph', 'word', 'year')
+    HandlerParams = ('about', 'above', 'against', 'apart from', 'around',
+                     'aside from', 'at', 'below', 'beneath', 'beside',
+                     'between', 'for', 'given', 'instead of', 'on', 'onto',
+                     'out of', 'over', 'since')
+    Commands = ('ASCII (character|number)', 'activate', 'beep', 'choose URL',
+                'choose application', 'choose color', 'choose file( name)?',
+                'choose folder', 'choose from list',
+                'choose remote application', 'clipboard info',
+                'close( access)?', 'copy', 'count', 'current date', 'delay',
+                'delete', 'display (alert|dialog)', 'do shell script',
+                'duplicate', 'exists', 'get eof', 'get volume settings',
+                'info for', 'launch', 'list (disks|folder)', 'load script',
+                'log', 'make', 'mount volume', 'new', 'offset',
+                'open( (for access|location))?', 'path to', 'print', 'quit',
+                'random number', 'read', 'round', 'run( script)?',
+                'say', 'scripting components',
+                'set (eof|the clipboard to|volume)', 'store script',
+                'summarize', 'system attribute', 'system info',
+                'the clipboard', 'time to GMT', 'write', 'quoted form')
+    References = ('(in )?back of', '(in )?front of', '[0-9]+(st|nd|rd|th)',
+                  'first', 'second', 'third', 'fourth', 'fifth', 'sixth',
+                  'seventh', 'eighth', 'ninth', 'tenth', 'after', 'back',
+                  'before', 'behind', 'every', 'front', 'index', 'last',
+                  'middle', 'some', 'that', 'through', 'thru', 'where', 'whose')
+    Operators = ("and", "or", "is equal", "equals", "(is )?equal to", "is not",
+                 "isn't", "isn't equal( to)?", "is not equal( to)?",
+                 "doesn't equal", "does not equal", "(is )?greater than",
+                 "comes after", "is not less than or equal( to)?",
+                 "isn't less than or equal( to)?", "(is )?less than",
+                 "comes before", "is not greater than or equal( to)?",
+                 "isn't greater than or equal( to)?",
+                 "(is  )?greater than or equal( to)?", "is not less than",
+                 "isn't less than", "does not come before",
+                 "doesn't come before", "(is )?less than or equal( to)?",
+                 "is not greater than", "isn't greater than",
+                 "does not come after", "doesn't come after", "starts? with",
+                 "begins? with", "ends? with", "contains?", "does not contain",
+                 "doesn't contain", "is in", "is contained by", "is not in",
+                 "is not contained by", "isn't contained by", "div", "mod",
+                 "not", "(a  )?(ref( to)?|reference to)", "is", "does")
+    Control = ('considering', 'else', 'error', 'exit', 'from', 'if',
+               'ignoring', 'in', 'repeat', 'tell', 'then', 'times', 'to',
+               'try', 'until', 'using terms from', 'while', 'whith',
+               'with timeout( of)?', 'with transaction', 'by', 'continue',
+               'end', 'its?', 'me', 'my', 'return', 'of', 'as')
+    Declarations = ('global', 'local', 'prop(erty)?', 'set', 'get')
+    Reserved = ('but', 'put', 'returning', 'the')
+    StudioClasses = ('action cell', 'alert reply', 'application', 'box',
+                     'browser( cell)?', 'bundle', 'button( cell)?', 'cell',
+                     'clip view', 'color well', 'color-panel',
+                     'combo box( item)?', 'control',
+                     'data( (cell|column|item|row|source))?', 'default entry',
+                     'dialog reply', 'document', 'drag info', 'drawer',
+                     'event', 'font(-panel)?', 'formatter',
+                     'image( (cell|view))?', 'matrix', 'menu( item)?', 'item',
+                     'movie( view)?', 'open-panel', 'outline view', 'panel',
+                     'pasteboard', 'plugin', 'popup button',
+                     'progress indicator', 'responder', 'save-panel',
+                     'scroll view', 'secure text field( cell)?', 'slider',
+                     'sound', 'split view', 'stepper', 'tab view( item)?',
+                     'table( (column|header cell|header view|view))',
+                     'text( (field( cell)?|view))?', 'toolbar( item)?',
+                     'user-defaults', 'view', 'window')
+    StudioEvents = ('accept outline drop', 'accept table drop', 'action',
+                    'activated', 'alert ended', 'awake from nib', 'became key',
+                    'became main', 'begin editing', 'bounds changed',
+                    'cell value', 'cell value changed', 'change cell value',
+                    'change item value', 'changed', 'child of item',
+                    'choose menu item', 'clicked', 'clicked toolbar item',
+                    'closed', 'column clicked', 'column moved',
+                    'column resized', 'conclude drop', 'data representation',
+                    'deminiaturized', 'dialog ended', 'document nib name',
+                    'double clicked', 'drag( (entered|exited|updated))?',
+                    'drop', 'end editing', 'exposed', 'idle', 'item expandable',
+                    'item value', 'item value changed', 'items changed',
+                    'keyboard down', 'keyboard up', 'launched',
+                    'load data representation', 'miniaturized', 'mouse down',
+                    'mouse dragged', 'mouse entered', 'mouse exited',
+                    'mouse moved', 'mouse up', 'moved',
+                    'number of browser rows', 'number of items',
+                    'number of rows', 'open untitled', 'opened', 'panel ended',
+                    'parameters updated', 'plugin loaded', 'prepare drop',
+                    'prepare outline drag', 'prepare outline drop',
+                    'prepare table drag', 'prepare table drop',
+                    'read from file', 'resigned active', 'resigned key',
+                    'resigned main', 'resized( sub views)?',
+                    'right mouse down', 'right mouse dragged',
+                    'right mouse up', 'rows changed', 'scroll wheel',
+                    'selected tab view item', 'selection changed',
+                    'selection changing', 'should begin editing',
+                    'should close', 'should collapse item',
+                    'should end editing', 'should expand item',
+                    'should open( untitled)?',
+                    'should quit( after last window closed)?',
+                    'should select column', 'should select item',
+                    'should select row', 'should select tab view item',
+                    'should selection change', 'should zoom', 'shown',
+                    'update menu item', 'update parameters',
+                    'update toolbar item', 'was hidden', 'was miniaturized',
+                    'will become active', 'will close', 'will dismiss',
+                    'will display browser cell', 'will display cell',
+                    'will display item cell', 'will display outline cell',
+                    'will finish launching', 'will hide', 'will miniaturize',
+                    'will move', 'will open', 'will pop up', 'will quit',
+                    'will resign active', 'will resize( sub views)?',
+                    'will select tab view item', 'will show', 'will zoom',
+                    'write to file', 'zoomed')
+    StudioCommands = ('animate', 'append', 'call method', 'center',
+                      'close drawer', 'close panel', 'display',
+                      'display alert', 'display dialog', 'display panel', 'go',
+                      'hide', 'highlight', 'increment', 'item for',
+                      'load image', 'load movie', 'load nib', 'load panel',
+                      'load sound', 'localized string', 'lock focus', 'log',
+                      'open drawer', 'path for', 'pause', 'perform action',
+                      'play', 'register', 'resume', 'scroll', 'select( all)?',
+                      'show', 'size to fit', 'start', 'step back',
+                      'step forward', 'stop', 'synchronize', 'unlock focus',
+                      'update')
+    StudioProperties = ('accepts arrow key', 'action method', 'active',
+                        'alignment', 'allowed identifiers',
+                        'allows branch selection', 'allows column reordering',
+                        'allows column resizing', 'allows column selection',
+                        'allows customization',
+                        'allows editing text attributes',
+                        'allows empty selection', 'allows mixed state',
+                        'allows multiple selection', 'allows reordering',
+                        'allows undo', 'alpha( value)?', 'alternate image',
+                        'alternate increment value', 'alternate title',
+                        'animation delay', 'associated file name',
+                        'associated object', 'auto completes', 'auto display',
+                        'auto enables items', 'auto repeat',
+                        'auto resizes( outline column)?',
+                        'auto save expanded items', 'auto save name',
+                        'auto save table columns', 'auto saves configuration',
+                        'auto scroll', 'auto sizes all columns to fit',
+                        'auto sizes cells', 'background color', 'bezel state',
+                        'bezel style', 'bezeled', 'border rect', 'border type',
+                        'bordered', 'bounds( rotation)?', 'box type',
+                        'button returned', 'button type',
+                        'can choose directories', 'can choose files',
+                        'can draw', 'can hide',
+                        'cell( (background color|size|type))?', 'characters',
+                        'class', 'click count', 'clicked( data)? column',
+                        'clicked data item', 'clicked( data)? row',
+                        'closeable', 'collating', 'color( (mode|panel))',
+                        'command key down', 'configuration',
+                        'content(s| (size|view( margins)?))?', 'context',
+                        'continuous', 'control key down', 'control size',
+                        'control tint', 'control view',
+                        'controller visible', 'coordinate system',
+                        'copies( on scroll)?', 'corner view', 'current cell',
+                        'current column', 'current( field)?  editor',
+                        'current( menu)? item', 'current row',
+                        'current tab view item', 'data source',
+                        'default identifiers', 'delta (x|y|z)',
+                        'destination window', 'directory', 'display mode',
+                        'displayed cell', 'document( (edited|rect|view))?',
+                        'double value', 'dragged column', 'dragged distance',
+                        'dragged items', 'draws( cell)? background',
+                        'draws grid', 'dynamically scrolls', 'echos bullets',
+                        'edge', 'editable', 'edited( data)? column',
+                        'edited data item', 'edited( data)? row', 'enabled',
+                        'enclosing scroll view', 'ending page',
+                        'error handling', 'event number', 'event type',
+                        'excluded from windows menu', 'executable path',
+                        'expanded', 'fax number', 'field editor', 'file kind',
+                        'file name', 'file type', 'first responder',
+                        'first visible column', 'flipped', 'floating',
+                        'font( panel)?', 'formatter', 'frameworks path',
+                        'frontmost', 'gave up', 'grid color', 'has data items',
+                        'has horizontal ruler', 'has horizontal scroller',
+                        'has parent data item', 'has resize indicator',
+                        'has shadow', 'has sub menu', 'has vertical ruler',
+                        'has vertical scroller', 'header cell', 'header view',
+                        'hidden', 'hides when deactivated', 'highlights by',
+                        'horizontal line scroll', 'horizontal page scroll',
+                        'horizontal ruler view', 'horizontally resizable',
+                        'icon image', 'id', 'identifier',
+                        'ignores multiple clicks',
+                        'image( (alignment|dims when disabled|frame style|scaling))?',
+                        'imports graphics', 'increment value',
+                        'indentation per level', 'indeterminate', 'index',
+                        'integer value', 'intercell spacing', 'item height',
+                        'key( (code|equivalent( modifier)?|window))?',
+                        'knob thickness', 'label', 'last( visible)? column',
+                        'leading offset', 'leaf', 'level', 'line scroll',
+                        'loaded', 'localized sort', 'location', 'loop mode',
+                        'main( (bunde|menu|window))?', 'marker follows cell',
+                        'matrix mode', 'maximum( content)? size',
+                        'maximum visible columns',
+                        'menu( form representation)?', 'miniaturizable',
+                        'miniaturized', 'minimized image', 'minimized title',
+                        'minimum column width', 'minimum( content)? size',
+                        'modal', 'modified', 'mouse down state',
+                        'movie( (controller|file|rect))?', 'muted', 'name',
+                        'needs display', 'next state', 'next text',
+                        'number of tick marks', 'only tick mark values',
+                        'opaque', 'open panel', 'option key down',
+                        'outline table column', 'page scroll', 'pages across',
+                        'pages down', 'palette label', 'pane splitter',
+                        'parent data item', 'parent window', 'pasteboard',
+                        'path( (names|separator))?', 'playing',
+                        'plays every frame', 'plays selection only', 'position',
+                        'preferred edge', 'preferred type', 'pressure',
+                        'previous text', 'prompt', 'properties',
+                        'prototype cell', 'pulls down', 'rate',
+                        'released when closed', 'repeated',
+                        'requested print time', 'required file type',
+                        'resizable', 'resized column', 'resource path',
+                        'returns records', 'reuses columns', 'rich text',
+                        'roll over', 'row height', 'rulers visible',
+                        'save panel', 'scripts path', 'scrollable',
+                        'selectable( identifiers)?', 'selected cell',
+                        'selected( data)? columns?', 'selected data items?',
+                        'selected( data)? rows?', 'selected item identifier',
+                        'selection by rect', 'send action on arrow key',
+                        'sends action when done editing', 'separates columns',
+                        'separator item', 'sequence number', 'services menu',
+                        'shared frameworks path', 'shared support path',
+                        'sheet', 'shift key down', 'shows alpha',
+                        'shows state by', 'size( mode)?',
+                        'smart insert delete enabled', 'sort case sensitivity',
+                        'sort column', 'sort order', 'sort type',
+                        'sorted( data rows)?', 'sound', 'source( mask)?',
+                        'spell checking enabled', 'starting page', 'state',
+                        'string value', 'sub menu', 'super menu', 'super view',
+                        'tab key traverses cells', 'tab state', 'tab type',
+                        'tab view', 'table view', 'tag', 'target( printer)?',
+                        'text color', 'text container insert',
+                        'text container origin', 'text returned',
+                        'tick mark position', 'time stamp',
+                        'title(d| (cell|font|height|position|rect))?',
+                        'tool tip', 'toolbar', 'trailing offset', 'transparent',
+                        'treat packages as directories', 'truncated labels',
+                        'types', 'unmodified characters', 'update views',
+                        'use sort indicator', 'user defaults',
+                        'uses data source', 'uses ruler',
+                        'uses threaded animation',
+                        'uses title from previous column', 'value wraps',
+                        'version',
+                        'vertical( (line scroll|page scroll|ruler view))?',
+                        'vertically resizable', 'view',
+                        'visible( document rect)?', 'volume', 'width', 'window',
+                        'windows menu', 'wraps', 'zoomable', 'zoomed')
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            (r'¬\n', String.Escape),
+            (r"'s\s+", Text),  # This is a possessive, consider moving
+            (r'(--|#).*?$', Comment),
+            (r'\(\*', Comment.Multiline, 'comment'),
+            (r'[(){}!,.:]', Punctuation),
+            (r'(«)([^»]+)(»)',
+             bygroups(Text, Name.Builtin, Text)),
+            (r'\b((?:considering|ignoring)\s*)'
+             r'(application responses|case|diacriticals|hyphens|'
+             r'numeric strings|punctuation|white space)',
+             bygroups(Keyword, Name.Builtin)),
+            (r'(-|\*|\+|&|≠|>=?|<=?|=|≥|≤|/|÷|\^)', Operator),
+            (r"\b({})\b".format('|'.join(Operators)), Operator.Word),
+            (r'^(\s*(?:on|end)\s+)'
+             r'({})'.format('|'.join(StudioEvents[::-1])),
+             bygroups(Keyword, Name.Function)),
+            (r'^(\s*)(in|on|script|to)(\s+)', bygroups(Text, Keyword, Text)),
+            (r'\b(as )({})\b'.format('|'.join(Classes)),
+             bygroups(Keyword, Name.Class)),
+            (r'\b({})\b'.format('|'.join(Literals)), Name.Constant),
+            (r'\b({})\b'.format('|'.join(Commands)), Name.Builtin),
+            (r'\b({})\b'.format('|'.join(Control)), Keyword),
+            (r'\b({})\b'.format('|'.join(Declarations)), Keyword),
+            (r'\b({})\b'.format('|'.join(Reserved)), Name.Builtin),
+            (r'\b({})s?\b'.format('|'.join(BuiltIn)), Name.Builtin),
+            (r'\b({})\b'.format('|'.join(HandlerParams)), Name.Builtin),
+            (r'\b({})\b'.format('|'.join(StudioProperties)), Name.Attribute),
+            (r'\b({})s?\b'.format('|'.join(StudioClasses)), Name.Builtin),
+            (r'\b({})\b'.format('|'.join(StudioCommands)), Name.Builtin),
+            (r'\b({})\b'.format('|'.join(References)), Name.Builtin),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
+            (rf'\b({Identifiers})\b', Name.Variable),
+            (r'[-+]?(\d+\.\d*|\d*\.\d+)(E[-+][0-9]+)?', Number.Float),
+            (r'[-+]?\d+', Number.Integer),
+        ],
+        'comment': [
+            (r'\(\*', Comment.Multiline, '#push'),
+            (r'\*\)', Comment.Multiline, '#pop'),
+            ('[^*(]+', Comment.Multiline),
+            ('[*(]', Comment.Multiline),
+        ],
+    }
+
+
+class RexxLexer(RegexLexer):
+    """
+    Rexx is a scripting language available for
+    a wide range of different platforms with its roots found on mainframe
+    systems. It is popular for I/O- and data based tasks and can act as glue
+    language to bind different applications together.
+    """
+    name = 'Rexx'
+    url = 'http://www.rexxinfo.org/'
+    aliases = ['rexx', 'arexx']
+    filenames = ['*.rexx', '*.rex', '*.rx', '*.arexx']
+    mimetypes = ['text/x-rexx']
+    version_added = '2.0'
+    flags = re.IGNORECASE
+
+    tokens = {
+        'root': [
+            (r'\s+', Whitespace),
+            (r'/\*', Comment.Multiline, 'comment'),
+            (r'"', String, 'string_double'),
+            (r"'", String, 'string_single'),
+            (r'[0-9]+(\.[0-9]+)?(e[+-]?[0-9])?', Number),
+            (r'([a-z_]\w*)(\s*)(:)(\s*)(procedure)\b',
+             bygroups(Name.Function, Whitespace, Operator, Whitespace,
+                      Keyword.Declaration)),
+            (r'([a-z_]\w*)(\s*)(:)',
+             bygroups(Name.Label, Whitespace, Operator)),
+            include('function'),
+            include('keyword'),
+            include('operator'),
+            (r'[a-z_]\w*', Text),
+        ],
+        'function': [
+            (words((
+                'abbrev', 'abs', 'address', 'arg', 'b2x', 'bitand', 'bitor', 'bitxor',
+                'c2d', 'c2x', 'center', 'charin', 'charout', 'chars', 'compare',
+                'condition', 'copies', 'd2c', 'd2x', 'datatype', 'date', 'delstr',
+                'delword', 'digits', 'errortext', 'form', 'format', 'fuzz', 'insert',
+                'lastpos', 'left', 'length', 'linein', 'lineout', 'lines', 'max',
+                'min', 'overlay', 'pos', 'queued', 'random', 'reverse', 'right', 'sign',
+                'sourceline', 'space', 'stream', 'strip', 'substr', 'subword', 'symbol',
+                'time', 'trace', 'translate', 'trunc', 'value', 'verify', 'word',
+                'wordindex', 'wordlength', 'wordpos', 'words', 'x2b', 'x2c', 'x2d',
+                'xrange'), suffix=r'(\s*)(\()'),
+             bygroups(Name.Builtin, Whitespace, Operator)),
+        ],
+        'keyword': [
+            (r'(address|arg|by|call|do|drop|else|end|exit|for|forever|if|'
+             r'interpret|iterate|leave|nop|numeric|off|on|options|parse|'
+             r'pull|push|queue|return|say|select|signal|to|then|trace|until|'
+             r'while)\b', Keyword.Reserved),
+        ],
+        'operator': [
+            (r'(-|//|/|\(|\)|\*\*|\*|\\<<|\\<|\\==|\\=|\\>>|\\>|\\|\|\||\||'
+             r'&&|&|%|\+|<<=|<<|<=|<>|<|==|=|><|>=|>>=|>>|>|¬<<|¬<|¬==|¬=|'
+             r'¬>>|¬>|¬|\.|,)', Operator),
+        ],
+        'string_double': [
+            (r'[^"\n]+', String),
+            (r'""', String),
+            (r'"', String, '#pop'),
+            (r'\n', Text, '#pop'),  # Stray linefeed also terminates strings.
+        ],
+        'string_single': [
+            (r'[^\'\n]+', String),
+            (r'\'\'', String),
+            (r'\'', String, '#pop'),
+            (r'\n', Text, '#pop'),  # Stray linefeed also terminates strings.
+        ],
+        'comment': [
+            (r'[^*]+', Comment.Multiline),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'\*', Comment.Multiline),
+        ]
+    }
+
+    def _c(s):
+        return re.compile(s, re.MULTILINE)
+    _ADDRESS_COMMAND_PATTERN = _c(r'^\s*address\s+command\b')
+    _ADDRESS_PATTERN = _c(r'^\s*address\s+')
+    _DO_WHILE_PATTERN = _c(r'^\s*do\s+while\b')
+    _IF_THEN_DO_PATTERN = _c(r'^\s*if\b.+\bthen\s+do\s*$')
+    _PROCEDURE_PATTERN = _c(r'^\s*([a-z_]\w*)(\s*)(:)(\s*)(procedure)\b')
+    _ELSE_DO_PATTERN = _c(r'\belse\s+do\s*$')
+    _PARSE_ARG_PATTERN = _c(r'^\s*parse\s+(upper\s+)?(arg|value)\b')
+    PATTERNS_AND_WEIGHTS = (
+        (_ADDRESS_COMMAND_PATTERN, 0.2),
+        (_ADDRESS_PATTERN, 0.05),
+        (_DO_WHILE_PATTERN, 0.1),
+        (_ELSE_DO_PATTERN, 0.1),
+        (_IF_THEN_DO_PATTERN, 0.1),
+        (_PROCEDURE_PATTERN, 0.5),
+        (_PARSE_ARG_PATTERN, 0.2),
+    )
+
+    def analyse_text(text):
+        """
+        Check for initial comment and patterns that distinguish Rexx from other
+        C-like languages.
+        """
+        if re.search(r'/\*\**\s*rexx', text, re.IGNORECASE):
+            # Header matches MVS Rexx requirements, this is certainly a Rexx
+            # script.
+            return 1.0
+        elif text.startswith('/*'):
+            # Header matches general Rexx requirements; the source code might
+            # still be any language using C comments such as C++, C# or Java.
+            lowerText = text.lower()
+            result = sum(weight
+                         for (pattern, weight) in RexxLexer.PATTERNS_AND_WEIGHTS
+                         if pattern.search(lowerText)) + 0.01
+            return min(result, 1.0)
+
+
+class MOOCodeLexer(RegexLexer):
+    """
+    For MOOCode (the MOO scripting language).
+    """
+    name = 'MOOCode'
+    url = 'http://www.moo.mud.org/'
+    filenames = ['*.moo']
+    aliases = ['moocode', 'moo']
+    mimetypes = ['text/x-moocode']
+    version_added = '0.9'
+
+    tokens = {
+        'root': [
+            # Numbers
+            (r'(0|[1-9][0-9_]*)', Number.Integer),
+            # Strings
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+            # exceptions
+            (r'(E_PERM|E_DIV)', Name.Exception),
+            # db-refs
+            (r'((#[-0-9]+)|(\$\w+))', Name.Entity),
+            # Keywords
+            (r'\b(if|else|elseif|endif|for|endfor|fork|endfork|while'
+             r'|endwhile|break|continue|return|try'
+             r'|except|endtry|finally|in)\b', Keyword),
+            # builtins
+            (r'(random|length)', Name.Builtin),
+            # special variables
+            (r'(player|caller|this|args)', Name.Variable.Instance),
+            # skip whitespace
+            (r'\s+', Text),
+            (r'\n', Text),
+            # other operators
+            (r'([!;=,{}&|:.\[\]@()<>?]+)', Operator),
+            # function call
+            (r'(\w+)(\()', bygroups(Name.Function, Operator)),
+            # variables
+            (r'(\w+)', Text),
+        ]
+    }
+
+
+class HybrisLexer(RegexLexer):
+    """
+    For Hybris source code.
+    """
+
+    name = 'Hybris'
+    aliases = ['hybris']
+    filenames = ['*.hyb']
+    mimetypes = ['text/x-hybris', 'application/x-hybris']
+    url = 'https://github.com/evilsocket/hybris'
+    version_added = '1.4'
+
+    flags = re.MULTILINE | re.DOTALL
+
+    tokens = {
+        'root': [
+            # method names
+            (r'^(\s*(?:function|method|operator\s+)+?)'
+             r'([a-zA-Z_]\w*)'
+             r'(\s*)(\()', bygroups(Keyword, Name.Function, Text, Operator)),
+            (r'[^\S\n]+', Text),
+            (r'//.*?\n', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline),
+            (r'@[a-zA-Z_][\w.]*', Name.Decorator),
+            (r'(break|case|catch|next|default|do|else|finally|for|foreach|of|'
+             r'unless|if|new|return|switch|me|throw|try|while)\b', Keyword),
+            (r'(extends|private|protected|public|static|throws|function|method|'
+             r'operator)\b', Keyword.Declaration),
+            (r'(true|false|null|__FILE__|__LINE__|__VERSION__|__LIB_PATH__|'
+             r'__INC_PATH__)\b', Keyword.Constant),
+            (r'(class|struct)(\s+)',
+             bygroups(Keyword.Declaration, Text), 'class'),
+            (r'(import|include)(\s+)',
+             bygroups(Keyword.Namespace, Text), 'import'),
+            (words((
+                'gc_collect', 'gc_mm_items', 'gc_mm_usage', 'gc_collect_threshold',
+                'urlencode', 'urldecode', 'base64encode', 'base64decode', 'sha1', 'crc32',
+                'sha2', 'md5', 'md5_file', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos',
+                'cosh', 'exp', 'fabs', 'floor', 'fmod', 'log', 'log10', 'pow', 'sin',
+                'sinh', 'sqrt', 'tan', 'tanh', 'isint', 'isfloat', 'ischar', 'isstring',
+                'isarray', 'ismap', 'isalias', 'typeof', 'sizeof', 'toint', 'tostring',
+                'fromxml', 'toxml', 'binary', 'pack', 'load', 'eval', 'var_names',
+                'var_values', 'user_functions', 'dyn_functions', 'methods', 'call',
+                'call_method', 'mknod', 'mkfifo', 'mount', 'umount2', 'umount', 'ticks',
+                'usleep', 'sleep', 'time', 'strtime', 'strdate', 'dllopen', 'dlllink',
+                'dllcall', 'dllcall_argv', 'dllclose', 'env', 'exec', 'fork', 'getpid',
+                'wait', 'popen', 'pclose', 'exit', 'kill', 'pthread_create',
+                'pthread_create_argv', 'pthread_exit', 'pthread_join', 'pthread_kill',
+                'smtp_send', 'http_get', 'http_post', 'http_download', 'socket', 'bind',
+                'listen', 'accept', 'getsockname', 'getpeername', 'settimeout', 'connect',
+                'server', 'recv', 'send', 'close', 'print', 'println', 'printf', 'input',
+                'readline', 'serial_open', 'serial_fcntl', 'serial_get_attr',
+                'serial_get_ispeed', 'serial_get_ospeed', 'serial_set_attr',
+                'serial_set_ispeed', 'serial_set_ospeed', 'serial_write', 'serial_read',
+                'serial_close', 'xml_load', 'xml_parse', 'fopen', 'fseek', 'ftell',
+                'fsize', 'fread', 'fwrite', 'fgets', 'fclose', 'file', 'readdir',
+                'pcre_replace', 'size', 'pop', 'unmap', 'has', 'keys', 'values',
+                'length', 'find', 'substr', 'replace', 'split', 'trim', 'remove',
+                'contains', 'join'), suffix=r'\b'),
+             Name.Builtin),
+            (words((
+                'MethodReference', 'Runner', 'Dll', 'Thread', 'Pipe', 'Process',
+                'Runnable', 'CGI', 'ClientSocket', 'Socket', 'ServerSocket',
+                'File', 'Console', 'Directory', 'Exception'), suffix=r'\b'),
+             Keyword.Type),
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+            (r"'\\.'|'[^\\]'|'\\u[0-9a-f]{4}'", String.Char),
+            (r'(\.)([a-zA-Z_]\w*)',
+             bygroups(Operator, Name.Attribute)),
+            (r'[a-zA-Z_]\w*:', Name.Label),
+            (r'[a-zA-Z_$]\w*', Name),
+            (r'[~^*!%&\[\](){}<>|+=:;,./?\-@]+', Operator),
+            (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
+            (r'0x[0-9a-f]+', Number.Hex),
+            (r'[0-9]+L?', Number.Integer),
+            (r'\n', Text),
+        ],
+        'class': [
+            (r'[a-zA-Z_]\w*', Name.Class, '#pop')
+        ],
+        'import': [
+            (r'[\w.]+\*?', Name.Namespace, '#pop')
+        ],
+    }
+
+    def analyse_text(text):
+        """public method and private method don't seem to be quite common
+        elsewhere."""
+        result = 0
+        if re.search(r'\b(?:public|private)\s+method\b', text):
+            result += 0.01
+        return result
+
+
+
+class EasytrieveLexer(RegexLexer):
+    """
+    Easytrieve Plus is a programming language for extracting, filtering and
+    converting sequential data. Furthermore it can layout data for reports.
+    It is mainly used on mainframe platforms and can access several of the
+    mainframe's native file formats. It is somewhat comparable to awk.
+    """
+    name = 'Easytrieve'
+    aliases = ['easytrieve']
+    filenames = ['*.ezt', '*.mac']
+    mimetypes = ['text/x-easytrieve']
+    url = 'https://www.broadcom.com/products/mainframe/application-development/easytrieve-report-generator'
+    version_added = '2.1'
+    flags = 0
+
+    # Note: We cannot use r'\b' at the start and end of keywords because
+    # Easytrieve Plus delimiter characters are:
+    #
+    #   * space ( )
+    #   * apostrophe (')
+    #   * period (.)
+    #   * comma (,)
+    #   * parenthesis ( and )
+    #   * colon (:)
+    #
+    # Additionally words end once a '*' appears, indicatins a comment.
+    _DELIMITERS = r' \'.,():\n'
+    _DELIMITERS_OR_COMENT = _DELIMITERS + '*'
+    _DELIMITER_PATTERN = '[' + _DELIMITERS + ']'
+    _DELIMITER_PATTERN_CAPTURE = '(' + _DELIMITER_PATTERN + ')'
+    _NON_DELIMITER_OR_COMMENT_PATTERN = '[^' + _DELIMITERS_OR_COMENT + ']'
+    _OPERATORS_PATTERN = '[.+\\-/=\\[\\](){}<>;,&%¬]'
+    _KEYWORDS = [
+        'AFTER-BREAK', 'AFTER-LINE', 'AFTER-SCREEN', 'AIM', 'AND', 'ATTR',
+        'BEFORE', 'BEFORE-BREAK', 'BEFORE-LINE', 'BEFORE-SCREEN', 'BUSHU',
+        'BY', 'CALL', 'CASE', 'CHECKPOINT', 'CHKP', 'CHKP-STATUS', 'CLEAR',
+        'CLOSE', 'COL', 'COLOR', 'COMMIT', 'CONTROL', 'COPY', 'CURSOR', 'D',
+        'DECLARE', 'DEFAULT', 'DEFINE', 'DELETE', 'DENWA', 'DISPLAY', 'DLI',
+        'DO', 'DUPLICATE', 'E', 'ELSE', 'ELSE-IF', 'END', 'END-CASE',
+        'END-DO', 'END-IF', 'END-PROC', 'ENDPAGE', 'ENDTABLE', 'ENTER', 'EOF',
+        'EQ', 'ERROR', 'EXIT', 'EXTERNAL', 'EZLIB', 'F1', 'F10', 'F11', 'F12',
+        'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F2', 'F20', 'F21',
+        'F22', 'F23', 'F24', 'F25', 'F26', 'F27', 'F28', 'F29', 'F3', 'F30',
+        'F31', 'F32', 'F33', 'F34', 'F35', 'F36', 'F4', 'F5', 'F6', 'F7',
+        'F8', 'F9', 'FETCH', 'FILE-STATUS', 'FILL', 'FINAL', 'FIRST',
+        'FIRST-DUP', 'FOR', 'GE', 'GET', 'GO', 'GOTO', 'GQ', 'GR', 'GT',
+        'HEADING', 'HEX', 'HIGH-VALUES', 'IDD', 'IDMS', 'IF', 'IN', 'INSERT',
+        'JUSTIFY', 'KANJI-DATE', 'KANJI-DATE-LONG', 'KANJI-TIME', 'KEY',
+        'KEY-PRESSED', 'KOKUGO', 'KUN', 'LAST-DUP', 'LE', 'LEVEL', 'LIKE',
+        'LINE', 'LINE-COUNT', 'LINE-NUMBER', 'LINK', 'LIST', 'LOW-VALUES',
+        'LQ', 'LS', 'LT', 'MACRO', 'MASK', 'MATCHED', 'MEND', 'MESSAGE',
+        'MOVE', 'MSTART', 'NE', 'NEWPAGE', 'NOMASK', 'NOPRINT', 'NOT',
+        'NOTE', 'NOVERIFY', 'NQ', 'NULL', 'OF', 'OR', 'OTHERWISE', 'PA1',
+        'PA2', 'PA3', 'PAGE-COUNT', 'PAGE-NUMBER', 'PARM-REGISTER',
+        'PATH-ID', 'PATTERN', 'PERFORM', 'POINT', 'POS', 'PRIMARY', 'PRINT',
+        'PROCEDURE', 'PROGRAM', 'PUT', 'READ', 'RECORD', 'RECORD-COUNT',
+        'RECORD-LENGTH', 'REFRESH', 'RELEASE', 'RENUM', 'REPEAT', 'REPORT',
+        'REPORT-INPUT', 'RESHOW', 'RESTART', 'RETRIEVE', 'RETURN-CODE',
+        'ROLLBACK', 'ROW', 'S', 'SCREEN', 'SEARCH', 'SECONDARY', 'SELECT',
+        'SEQUENCE', 'SIZE', 'SKIP', 'SOKAKU', 'SORT', 'SQL', 'STOP', 'SUM',
+        'SYSDATE', 'SYSDATE-LONG', 'SYSIN', 'SYSIPT', 'SYSLST', 'SYSPRINT',
+        'SYSSNAP', 'SYSTIME', 'TALLY', 'TERM-COLUMNS', 'TERM-NAME',
+        'TERM-ROWS', 'TERMINATION', 'TITLE', 'TO', 'TRANSFER', 'TRC',
+        'UNIQUE', 'UNTIL', 'UPDATE', 'UPPERCASE', 'USER', 'USERID', 'VALUE',
+        'VERIFY', 'W', 'WHEN', 'WHILE', 'WORK', 'WRITE', 'X', 'XDM', 'XRST'
+    ]
+
+    tokens = {
+        'root': [
+            (r'\*.*\n', Comment.Single),
+            (r'\n+', Whitespace),
+            # Macro argument
+            (r'&' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+\.', Name.Variable,
+             'after_macro_argument'),
+            # Macro call
+            (r'%' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name.Variable),
+            (r'(FILE|MACRO|REPORT)(\s+)',
+             bygroups(Keyword.Declaration, Whitespace), 'after_declaration'),
+            (r'(JOB|PARM)' + r'(' + _DELIMITER_PATTERN + r')',
+             bygroups(Keyword.Declaration, Operator)),
+            (words(_KEYWORDS, suffix=_DELIMITER_PATTERN_CAPTURE),
+             bygroups(Keyword.Reserved, Operator)),
+            (_OPERATORS_PATTERN, Operator),
+            # Procedure declaration
+            (r'(' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+)(\s*)(\.?)(\s*)(PROC)(\s*\n)',
+             bygroups(Name.Function, Whitespace, Operator, Whitespace,
+                      Keyword.Declaration, Whitespace)),
+            (r'[0-9]+\.[0-9]*', Number.Float),
+            (r'[0-9]+', Number.Integer),
+            (r"'(''|[^'])*'", String),
+            (r'\s+', Whitespace),
+            # Everything else just belongs to a name
+            (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name),
+         ],
+        'after_declaration': [
+            (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name.Function),
+            default('#pop'),
+        ],
+        'after_macro_argument': [
+            (r'\*.*\n', Comment.Single, '#pop'),
+            (r'\s+', Whitespace, '#pop'),
+            (_OPERATORS_PATTERN, Operator, '#pop'),
+            (r"'(''|[^'])*'", String, '#pop'),
+            # Everything else just belongs to a name
+            (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name),
+        ],
+    }
+    _COMMENT_LINE_REGEX = re.compile(r'^\s*\*')
+    _MACRO_HEADER_REGEX = re.compile(r'^\s*MACRO')
+
+    def analyse_text(text):
+        """
+        Perform a structural analysis for basic Easytrieve constructs.
+        """
+        result = 0.0
+        lines = text.split('\n')
+        hasEndProc = False
+        hasHeaderComment = False
+        hasFile = False
+        hasJob = False
+        hasProc = False
+        hasParm = False
+        hasReport = False
+
+        def isCommentLine(line):
+            return EasytrieveLexer._COMMENT_LINE_REGEX.match(lines[0]) is not None
+
+        def isEmptyLine(line):
+            return not bool(line.strip())
+
+        # Remove possible empty lines and header comments.
+        while lines and (isEmptyLine(lines[0]) or isCommentLine(lines[0])):
+            if not isEmptyLine(lines[0]):
+                hasHeaderComment = True
+            del lines[0]
+
+        if EasytrieveLexer._MACRO_HEADER_REGEX.match(lines[0]):
+            # Looks like an Easytrieve macro.
+            result = 0.4
+            if hasHeaderComment:
+                result += 0.4
+        else:
+            # Scan the source for lines starting with indicators.
+            for line in lines:
+                words = line.split()
+                if (len(words) >= 2):
+                    firstWord = words[0]
+                    if not hasReport:
+                        if not hasJob:
+                            if not hasFile:
+                                if not hasParm:
+                                    if firstWord == 'PARM':
+                                        hasParm = True
+                                if firstWord == 'FILE':
+                                    hasFile = True
+                            if firstWord == 'JOB':
+                                hasJob = True
+                        elif firstWord == 'PROC':
+                            hasProc = True
+                        elif firstWord == 'END-PROC':
+                            hasEndProc = True
+                        elif firstWord == 'REPORT':
+                            hasReport = True
+
+            # Weight the findings.
+            if hasJob and (hasProc == hasEndProc):
+                if hasHeaderComment:
+                    result += 0.1
+                if hasParm:
+                    if hasProc:
+                        # Found PARM, JOB and PROC/END-PROC:
+                        # pretty sure this is Easytrieve.
+                        result += 0.8
+                    else:
+                        # Found PARAM and  JOB: probably this is Easytrieve
+                        result += 0.5
+                else:
+                    # Found JOB and possibly other keywords: might be Easytrieve
+                    result += 0.11
+                    if hasParm:
+                        # Note: PARAM is not a proper English word, so this is
+                        # regarded a much better indicator for Easytrieve than
+                        # the other words.
+                        result += 0.2
+                    if hasFile:
+                        result += 0.01
+                    if hasReport:
+                        result += 0.01
+        assert 0.0 <= result <= 1.0
+        return result
+
+
+class JclLexer(RegexLexer):
+    """
+    Job Control Language (JCL)
+    is a scripting language used on mainframe platforms to instruct the system
+    on how to run a batch job or start a subsystem. It is somewhat
+    comparable to MS DOS batch and Unix shell scripts.
+    """
+    name = 'JCL'
+    aliases = ['jcl']
+    filenames = ['*.jcl']
+    mimetypes = ['text/x-jcl']
+    url = 'https://en.wikipedia.org/wiki/Job_Control_Language'
+    version_added = '2.1'
+
+    flags = re.IGNORECASE
+
+    tokens = {
+        'root': [
+            (r'//\*.*\n', Comment.Single),
+            (r'//', Keyword.Pseudo, 'statement'),
+            (r'/\*', Keyword.Pseudo, 'jes2_statement'),
+            # TODO: JES3 statement
+            (r'.*\n', Other)  # Input text or inline code in any language.
+        ],
+        'statement': [
+            (r'\s*\n', Whitespace, '#pop'),
+            (r'([a-z]\w*)(\s+)(exec|job)(\s*)',
+             bygroups(Name.Label, Whitespace, Keyword.Reserved, Whitespace),
+             'option'),
+            (r'[a-z]\w*', Name.Variable, 'statement_command'),
+            (r'\s+', Whitespace, 'statement_command'),
+        ],
+        'statement_command': [
+            (r'\s+(command|cntl|dd|endctl|endif|else|include|jcllib|'
+             r'output|pend|proc|set|then|xmit)\s+', Keyword.Reserved, 'option'),
+            include('option')
+        ],
+        'jes2_statement': [
+            (r'\s*\n', Whitespace, '#pop'),
+            (r'\$', Keyword, 'option'),
+            (r'\b(jobparam|message|netacct|notify|output|priority|route|'
+             r'setup|signoff|xeq|xmit)\b', Keyword, 'option'),
+        ],
+        'option': [
+            # (r'\n', Text, 'root'),
+            (r'\*', Name.Builtin),
+            (r'[\[\](){}<>;,]', Punctuation),
+            (r'[-+*/=&%]', Operator),
+            (r'[a-z_]\w*', Name),
+            (r'\d+\.\d*', Number.Float),
+            (r'\.\d+', Number.Float),
+            (r'\d+', Number.Integer),
+            (r"'", String, 'option_string'),
+            (r'[ \t]+', Whitespace, 'option_comment'),
+            (r'\.', Punctuation),
+        ],
+        'option_string': [
+            (r"(\n)(//)", bygroups(Text, Keyword.Pseudo)),
+            (r"''", String),
+            (r"[^']", String),
+            (r"'", String, '#pop'),
+        ],
+        'option_comment': [
+            # (r'\n', Text, 'root'),
+            (r'.+', Comment.Single),
+        ]
+    }
+
+    _JOB_HEADER_PATTERN = re.compile(r'^//[a-z#$@][a-z0-9#$@]{0,7}\s+job(\s+.*)?$',
+                                     re.IGNORECASE)
+
+    def analyse_text(text):
+        """
+        Recognize JCL job by header.
+        """
+        result = 0.0
+        lines = text.split('\n')
+        if len(lines) > 0:
+            if JclLexer._JOB_HEADER_PATTERN.match(lines[0]):
+                result = 1.0
+        assert 0.0 <= result <= 1.0
+        return result
+
+
+class MiniScriptLexer(RegexLexer):
+    """
+    For MiniScript source code.
+    """
+
+    name = 'MiniScript'
+    url = 'https://miniscript.org'
+    aliases = ['miniscript', 'ms']
+    filenames = ['*.ms']
+    mimetypes = ['text/x-minicript', 'application/x-miniscript']
+    version_added = '2.6'
+
+    tokens = {
+        'root': [
+            (r'#!(.*?)$', Comment.Preproc),
+            default('base'),
+        ],
+        'base': [
+            ('//.*$', Comment.Single),
+            (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number),
+            (r'(?i)\d+e[+-]?\d+', Number),
+            (r'\d+', Number),
+            (r'\n', Text),
+            (r'[^\S\n]+', Text),
+            (r'"', String, 'string_double'),
+            (r'(==|!=|<=|>=|[=+\-*/%^<>.:])', Operator),
+            (r'[;,\[\]{}()]', Punctuation),
+            (words((
+                'break', 'continue', 'else', 'end', 'for', 'function', 'if',
+                'in', 'isa', 'then', 'repeat', 'return', 'while'), suffix=r'\b'),
+             Keyword),
+            (words((
+                'abs', 'acos', 'asin', 'atan', 'ceil', 'char', 'cos', 'floor',
+                'log', 'round', 'rnd', 'pi', 'sign', 'sin', 'sqrt', 'str', 'tan',
+                'hasIndex', 'indexOf', 'len', 'val', 'code', 'remove', 'lower',
+                'upper', 'replace', 'split', 'indexes', 'values', 'join', 'sum',
+                'sort', 'shuffle', 'push', 'pop', 'pull', 'range',
+                'print', 'input', 'time', 'wait', 'locals', 'globals', 'outer',
+                'yield'), suffix=r'\b'),
+             Name.Builtin),
+            (r'(true|false|null)\b', Keyword.Constant),
+            (r'(and|or|not|new)\b', Operator.Word),
+            (r'(self|super|__isa)\b', Name.Builtin.Pseudo),
+            (r'[a-zA-Z_]\w*', Name.Variable)
+        ],
+        'string_double': [
+            (r'[^"\n]+', String),
+            (r'""', String),
+            (r'"', String, '#pop'),
+            (r'\n', Text, '#pop'),  # Stray linefeed also terminates strings.
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/sgf.py b/.venv/lib/python3.12/site-packages/pygments/lexers/sgf.py
new file mode 100644
index 0000000..6d7c304
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/sgf.py
@@ -0,0 +1,59 @@
+"""
+    pygments.lexers.sgf
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Smart Game Format (sgf) file format.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Name, Literal, String, Punctuation, Whitespace
+
+__all__ = ["SmartGameFormatLexer"]
+
+
+class SmartGameFormatLexer(RegexLexer):
+    """
+    Lexer for Smart Game Format (sgf) file format.
+
+    The format is used to store game records of board games for two players
+    (mainly Go game).
+    """
+    name = 'SmartGameFormat'
+    url = 'https://www.red-bean.com/sgf/'
+    aliases = ['sgf']
+    filenames = ['*.sgf']
+    version_added = '2.4'
+
+    tokens = {
+        'root': [
+            (r'[():;]+', Punctuation),
+            # tokens:
+            (r'(A[BW]|AE|AN|AP|AR|AS|[BW]L|BM|[BW]R|[BW]S|[BW]T|CA|CH|CP|CR|'
+             r'DD|DM|DO|DT|EL|EV|EX|FF|FG|G[BW]|GC|GM|GN|HA|HO|ID|IP|IT|IY|KM|'
+             r'KO|LB|LN|LT|L|MA|MN|M|N|OB|OM|ON|OP|OT|OV|P[BW]|PC|PL|PM|RE|RG|'
+             r'RO|RU|SO|SC|SE|SI|SL|SO|SQ|ST|SU|SZ|T[BW]|TC|TE|TM|TR|UC|US|VW|'
+             r'V|[BW]|C)',
+             Name.Builtin),
+            # number:
+            (r'(\[)([0-9.]+)(\])',
+             bygroups(Punctuation, Literal.Number, Punctuation)),
+            # date:
+            (r'(\[)([0-9]{4}-[0-9]{2}-[0-9]{2})(\])',
+             bygroups(Punctuation, Literal.Date, Punctuation)),
+            # point:
+            (r'(\[)([a-z]{2})(\])',
+             bygroups(Punctuation, String, Punctuation)),
+            # double points:
+            (r'(\[)([a-z]{2})(:)([a-z]{2})(\])',
+             bygroups(Punctuation, String, Punctuation, String, Punctuation)),
+
+            (r'(\[)([\w\s#()+,\-.:?]+)(\])',
+             bygroups(Punctuation, String, Punctuation)),
+            (r'(\[)(\s.*)(\])',
+             bygroups(Punctuation, Whitespace, Punctuation)),
+            (r'\s+', Whitespace)
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/shell.py b/.venv/lib/python3.12/site-packages/pygments/lexers/shell.py
new file mode 100644
index 0000000..c1ce07f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/shell.py
@@ -0,0 +1,902 @@
+"""
+    pygments.lexers.shell
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for various shells.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, \
+    include, default, this, using, words, line_re
+from pygments.token import Punctuation, Whitespace, \
+    Text, Comment, Operator, Keyword, Name, String, Number, Generic
+from pygments.util import shebang_matches
+
+__all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer',
+           'SlurmBashLexer', 'MSDOSSessionLexer', 'PowerShellLexer',
+           'PowerShellSessionLexer', 'TcshSessionLexer', 'FishShellLexer',
+           'ExeclineLexer']
+
+
+class BashLexer(RegexLexer):
+    """
+    Lexer for (ba|k|z|)sh shell scripts.
+    """
+
+    name = 'Bash'
+    aliases = ['bash', 'sh', 'ksh', 'zsh', 'shell', 'openrc']
+    filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass',
+                 '*.exheres-0', '*.exlib', '*.zsh',
+                 '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc',
+                 '.kshrc', 'kshrc',
+                 'PKGBUILD']
+    mimetypes = ['application/x-sh', 'application/x-shellscript', 'text/x-shellscript']
+    url = 'https://en.wikipedia.org/wiki/Unix_shell'
+    version_added = '0.6'
+
+    tokens = {
+        'root': [
+            include('basic'),
+            (r'`', String.Backtick, 'backticks'),
+            include('data'),
+            include('interp'),
+        ],
+        'interp': [
+            (r'\$\(\(', Keyword, 'math'),
+            (r'\$\(', Keyword, 'paren'),
+            (r'\$\{#?', String.Interpol, 'curly'),
+            (r'\$[a-zA-Z_]\w*', Name.Variable),  # user variable
+            (r'\$(?:\d+|[#$?!_*@-])', Name.Variable),      # builtin
+            (r'\$', Text),
+        ],
+        'basic': [
+            (r'\b(if|fi|else|while|in|do|done|for|then|return|function|case|'
+             r'select|break|continue|until|esac|elif)(\s*)\b',
+             bygroups(Keyword, Whitespace)),
+            (r'\b(alias|bg|bind|builtin|caller|cd|command|compgen|'
+             r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|'
+             r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|'
+             r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|'
+             r'shopt|source|suspend|test|time|times|trap|true|type|typeset|'
+             r'ulimit|umask|unalias|unset|wait)(?=[\s)`])',
+             Name.Builtin),
+            (r'\A#!.+\n', Comment.Hashbang),
+            (r'#.*\n', Comment.Single),
+            (r'\\[\w\W]', String.Escape),
+            (r'(\b\w+)(\s*)(\+?=)', bygroups(Name.Variable, Whitespace, Operator)),
+            (r'[\[\]{}()=]', Operator),
+            (r'<<<', Operator),  # here-string
+            (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
+            (r'&&|\|\|', Operator),
+        ],
+        'data': [
+            (r'(?s)\$?"(\\.|[^"\\$])*"', String.Double),
+            (r'"', String.Double, 'string'),
+            (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
+            (r"(?s)'.*?'", String.Single),
+            (r';', Punctuation),
+            (r'&', Punctuation),
+            (r'\|', Punctuation),
+            (r'\s+', Whitespace),
+            (r'\d+\b', Number),
+            (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
+            (r'<', Text),
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
+            include('interp'),
+        ],
+        'curly': [
+            (r'\}', String.Interpol, '#pop'),
+            (r':-', Keyword),
+            (r'\w+', Name.Variable),
+            (r'[^}:"\'`$\\]+', Punctuation),
+            (r':', Punctuation),
+            include('root'),
+        ],
+        'paren': [
+            (r'\)', Keyword, '#pop'),
+            include('root'),
+        ],
+        'math': [
+            (r'\)\)', Keyword, '#pop'),
+            (r'\*\*|\|\||<<|>>|[-+*/%^|&<>]', Operator),
+            (r'\d+#[\da-zA-Z]+', Number),
+            (r'\d+#(?! )', Number),
+            (r'0[xX][\da-fA-F]+', Number),
+            (r'\d+', Number),
+            (r'[a-zA-Z_]\w*', Name.Variable),  # user variable
+            include('root'),
+        ],
+        'backticks': [
+            (r'`', String.Backtick, '#pop'),
+            include('root'),
+        ],
+    }
+
+    def analyse_text(text):
+        if shebang_matches(text, r'(ba|z|)sh'):
+            return 1
+        if text.startswith('$ '):
+            return 0.2
+
+
+class SlurmBashLexer(BashLexer):
+    """
+    Lexer for (ba|k|z|)sh Slurm scripts.
+    """
+
+    name = 'Slurm'
+    aliases = ['slurm', 'sbatch']
+    filenames = ['*.sl']
+    mimetypes = []
+    version_added = '2.4'
+    EXTRA_KEYWORDS = {'srun'}
+
+    def get_tokens_unprocessed(self, text):
+        for index, token, value in BashLexer.get_tokens_unprocessed(self, text):
+            if token is Text and value in self.EXTRA_KEYWORDS:
+                yield index, Name.Builtin, value
+            elif token is Comment.Single and 'SBATCH' in value:
+                yield index, Keyword.Pseudo, value
+            else:
+                yield index, token, value
+
+
+class ShellSessionBaseLexer(Lexer):
+    """
+    Base lexer for shell sessions.
+
+    .. versionadded:: 2.1
+    """
+
+    _bare_continuation = False
+    _venv = re.compile(r'^(\([^)]*\))(\s*)')
+
+    def get_tokens_unprocessed(self, text):
+        innerlexer = self._innerLexerCls(**self.options)
+
+        pos = 0
+        curcode = ''
+        insertions = []
+        backslash_continuation = False
+
+        for match in line_re.finditer(text):
+            line = match.group()
+
+            venv_match = self._venv.match(line)
+            if venv_match:
+                venv = venv_match.group(1)
+                venv_whitespace = venv_match.group(2)
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt.VirtualEnv, venv)]))
+                if venv_whitespace:
+                    insertions.append((len(curcode),
+                                       [(0, Text, venv_whitespace)]))
+                line = line[venv_match.end():]
+
+            m = self._ps1rgx.match(line)
+            if m:
+                # To support output lexers (say diff output), the output
+                # needs to be broken by prompts whenever the output lexer
+                # changes.
+                if not insertions:
+                    pos = match.start()
+
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, m.group(1))]))
+                curcode += m.group(2)
+                backslash_continuation = curcode.endswith('\\\n')
+            elif backslash_continuation:
+                if line.startswith(self._ps2):
+                    insertions.append((len(curcode),
+                                       [(0, Generic.Prompt,
+                                         line[:len(self._ps2)])]))
+                    curcode += line[len(self._ps2):]
+                else:
+                    curcode += line
+                backslash_continuation = curcode.endswith('\\\n')
+            elif self._bare_continuation and line.startswith(self._ps2):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt,
+                                     line[:len(self._ps2)])]))
+                curcode += line[len(self._ps2):]
+            else:
+                if insertions:
+                    toks = innerlexer.get_tokens_unprocessed(curcode)
+                    for i, t, v in do_insertions(insertions, toks):
+                        yield pos+i, t, v
+                yield match.start(), Generic.Output, line
+                insertions = []
+                curcode = ''
+        if insertions:
+            for i, t, v in do_insertions(insertions,
+                                         innerlexer.get_tokens_unprocessed(curcode)):
+                yield pos+i, t, v
+
+
+class BashSessionLexer(ShellSessionBaseLexer):
+    """
+    Lexer for Bash shell sessions, i.e. command lines, including a
+    prompt, interspersed with output.
+    """
+
+    name = 'Bash Session'
+    aliases = ['console', 'shell-session']
+    filenames = ['*.sh-session', '*.shell-session']
+    mimetypes = ['application/x-shell-session', 'application/x-sh-session']
+    url = 'https://en.wikipedia.org/wiki/Unix_shell'
+    version_added = '1.1'
+    _example = "console/example.sh-session"
+
+    _innerLexerCls = BashLexer
+    _ps1rgx = re.compile(
+        r'^((?:(?:\[.*?\])|(?:\(\S+\))?(?:| |sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)' \
+        r'?|\[\S+[@:][^\n]+\].+))\s*[$#%]\s*)(.*\n?)')
+    _ps2 = '> '
+
+
+class BatchLexer(RegexLexer):
+    """
+    Lexer for the DOS/Windows Batch file format.
+    """
+    name = 'Batchfile'
+    aliases = ['batch', 'bat', 'dosbatch', 'winbatch']
+    filenames = ['*.bat', '*.cmd']
+    mimetypes = ['application/x-dos-batch']
+    url = 'https://en.wikipedia.org/wiki/Batch_file'
+    version_added = '0.7'
+
+    flags = re.MULTILINE | re.IGNORECASE
+
+    _nl = r'\n\x1a'
+    _punct = r'&<>|'
+    _ws = r'\t\v\f\r ,;=\xa0'
+    _nlws = r'\s\x1a\xa0,;='
+    _space = rf'(?:(?:(?:\^[{_nl}])?[{_ws}])+)'
+    _keyword_terminator = (rf'(?=(?:\^[{_nl}]?)?[{_ws}+./:[\\\]]|[{_nl}{_punct}(])')
+    _token_terminator = rf'(?=\^?[{_ws}]|[{_punct}{_nl}])'
+    _start_label = rf'((?:(?<=^[^:])|^[^:]?)[{_ws}]*)(:)'
+    _label = rf'(?:(?:[^{_nlws}{_punct}+:^]|\^[{_nl}]?[\w\W])*)'
+    _label_compound = rf'(?:(?:[^{_nlws}{_punct}+:^)]|\^[{_nl}]?[^)])*)'
+    _number = rf'(?:-?(?:0[0-7]+|0x[\da-f]+|\d+){_token_terminator})'
+    _opword = r'(?:equ|geq|gtr|leq|lss|neq)'
+    _string = rf'(?:"[^{_nl}"]*(?:"|(?=[{_nl}])))'
+    _variable = (r'(?:(?:%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|'
+                 rf'[^%:{_nl}]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%{_nl}^]|'
+                 rf'\^[^%{_nl}])[^={_nl}]*=(?:[^%{_nl}^]|\^[^%{_nl}])*)?)?%))|'
+                 rf'(?:\^?![^!:{_nl}]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:'
+                 rf'[^!{_nl}^]|\^[^!{_nl}])[^={_nl}]*=(?:[^!{_nl}^]|\^[^!{_nl}])*)?)?\^?!))')
+    _core_token = rf'(?:(?:(?:\^[{_nl}]?)?[^"{_nlws}{_punct}])+)'
+    _core_token_compound = rf'(?:(?:(?:\^[{_nl}]?)?[^"{_nlws}{_punct})])+)'
+    _token = rf'(?:[{_punct}]+|{_core_token})'
+    _token_compound = rf'(?:[{_punct}]+|{_core_token_compound})'
+    _stoken = (rf'(?:[{_punct}]+|(?:{_string}|{_variable}|{_core_token})+)')
+
+    def _make_begin_state(compound, _core_token=_core_token,
+                          _core_token_compound=_core_token_compound,
+                          _keyword_terminator=_keyword_terminator,
+                          _nl=_nl, _punct=_punct, _string=_string,
+                          _space=_space, _start_label=_start_label,
+                          _stoken=_stoken, _token_terminator=_token_terminator,
+                          _variable=_variable, _ws=_ws):
+        rest = '(?:{}|{}|[^"%{}{}{}])*'.format(_string, _variable, _nl, _punct,
+                                            ')' if compound else '')
+        rest_of_line = rf'(?:(?:[^{_nl}^]|\^[{_nl}]?[\w\W])*)'
+        rest_of_line_compound = rf'(?:(?:[^{_nl}^)]|\^[{_nl}]?[^)])*)'
+        set_space = rf'((?:(?:\^[{_nl}]?)?[^\S\n])*)'
+        suffix = ''
+        if compound:
+            _keyword_terminator = rf'(?:(?=\))|{_keyword_terminator})'
+            _token_terminator = rf'(?:(?=\))|{_token_terminator})'
+            suffix = '/compound'
+        return [
+            ((r'\)', Punctuation, '#pop') if compound else
+             (rf'\)((?=\()|{_token_terminator}){rest_of_line}',
+              Comment.Single)),
+            (rf'(?={_start_label})', Text, f'follow{suffix}'),
+            (_space, using(this, state='text')),
+            include(f'redirect{suffix}'),
+            (rf'[{_nl}]+', Text),
+            (r'\(', Punctuation, 'root/compound'),
+            (r'@+', Punctuation),
+            (rf'((?:for|if|rem)(?:(?=(?:\^[{_nl}]?)?/)|(?:(?!\^)|'
+             rf'(?<=m))(?:(?=\()|{_token_terminator})))({_space}?{_core_token_compound if compound else _core_token}?(?:\^[{_nl}]?)?/(?:\^[{_nl}]?)?\?)',
+             bygroups(Keyword, using(this, state='text')),
+             f'follow{suffix}'),
+            (rf'(goto{_keyword_terminator})({rest}(?:\^[{_nl}]?)?/(?:\^[{_nl}]?)?\?{rest})',
+             bygroups(Keyword, using(this, state='text')),
+             f'follow{suffix}'),
+            (words(('assoc', 'break', 'cd', 'chdir', 'cls', 'color', 'copy',
+                    'date', 'del', 'dir', 'dpath', 'echo', 'endlocal', 'erase',
+                    'exit', 'ftype', 'keys', 'md', 'mkdir', 'mklink', 'move',
+                    'path', 'pause', 'popd', 'prompt', 'pushd', 'rd', 'ren',
+                    'rename', 'rmdir', 'setlocal', 'shift', 'start', 'time',
+                    'title', 'type', 'ver', 'verify', 'vol'),
+                   suffix=_keyword_terminator), Keyword, f'follow{suffix}'),
+            (rf'(call)({_space}?)(:)',
+             bygroups(Keyword, using(this, state='text'), Punctuation),
+             f'call{suffix}'),
+            (rf'call{_keyword_terminator}', Keyword),
+            (rf'(for{_token_terminator}(?!\^))({_space})(/f{_token_terminator})',
+             bygroups(Keyword, using(this, state='text'), Keyword),
+             ('for/f', 'for')),
+            (rf'(for{_token_terminator}(?!\^))({_space})(/l{_token_terminator})',
+             bygroups(Keyword, using(this, state='text'), Keyword),
+             ('for/l', 'for')),
+            (rf'for{_token_terminator}(?!\^)', Keyword, ('for2', 'for')),
+            (rf'(goto{_keyword_terminator})({_space}?)(:?)',
+             bygroups(Keyword, using(this, state='text'), Punctuation),
+             f'label{suffix}'),
+            (rf'(if(?:(?=\()|{_token_terminator})(?!\^))({_space}?)((?:/i{_token_terminator})?)({_space}?)((?:not{_token_terminator})?)({_space}?)',
+             bygroups(Keyword, using(this, state='text'), Keyword,
+                      using(this, state='text'), Keyword,
+                      using(this, state='text')), ('(?', 'if')),
+            (rf'rem(((?=\()|{_token_terminator}){_space}?{_stoken}?.*|{_keyword_terminator}{rest_of_line_compound if compound else rest_of_line})',
+             Comment.Single, f'follow{suffix}'),
+            (rf'(set{_keyword_terminator}){set_space}(/a)',
+             bygroups(Keyword, using(this, state='text'), Keyword),
+             f'arithmetic{suffix}'),
+            (r'(set{}){}((?:/p)?){}((?:(?:(?:\^[{}]?)?[^"{}{}^={}]|'
+             r'\^[{}]?[^"=])+)?)((?:(?:\^[{}]?)?=)?)'.format(_keyword_terminator, set_space, set_space, _nl, _nl, _punct,
+              ')' if compound else '', _nl, _nl),
+             bygroups(Keyword, using(this, state='text'), Keyword,
+                      using(this, state='text'), using(this, state='variable'),
+                      Punctuation),
+             f'follow{suffix}'),
+            default(f'follow{suffix}')
+        ]
+
+    def _make_follow_state(compound, _label=_label,
+                           _label_compound=_label_compound, _nl=_nl,
+                           _space=_space, _start_label=_start_label,
+                           _token=_token, _token_compound=_token_compound,
+                           _ws=_ws):
+        suffix = '/compound' if compound else ''
+        state = []
+        if compound:
+            state.append((r'(?=\))', Text, '#pop'))
+        state += [
+            (rf'{_start_label}([{_ws}]*)({_label_compound if compound else _label})(.*)',
+             bygroups(Text, Punctuation, Text, Name.Label, Comment.Single)),
+            include(f'redirect{suffix}'),
+            (rf'(?=[{_nl}])', Text, '#pop'),
+            (r'\|\|?|&&?', Punctuation, '#pop'),
+            include('text')
+        ]
+        return state
+
+    def _make_arithmetic_state(compound, _nl=_nl, _punct=_punct,
+                               _string=_string, _variable=_variable,
+                               _ws=_ws, _nlws=_nlws):
+        op = r'=+\-*/!~'
+        state = []
+        if compound:
+            state.append((r'(?=\))', Text, '#pop'))
+        state += [
+            (r'0[0-7]+', Number.Oct),
+            (r'0x[\da-f]+', Number.Hex),
+            (r'\d+', Number.Integer),
+            (r'[(),]+', Punctuation),
+            (rf'([{op}]|%|\^\^)+', Operator),
+            (r'({}|{}|(\^[{}]?)?[^(){}%\^"{}{}]|\^[{}]?{})+'.format(_string, _variable, _nl, op, _nlws, _punct, _nlws,
+              r'[^)]' if compound else r'[\w\W]'),
+             using(this, state='variable')),
+            (r'(?=[\x00|&])', Text, '#pop'),
+            include('follow')
+        ]
+        return state
+
+    def _make_call_state(compound, _label=_label,
+                         _label_compound=_label_compound):
+        state = []
+        if compound:
+            state.append((r'(?=\))', Text, '#pop'))
+        state.append((r'(:?)(%s)' % (_label_compound if compound else _label),
+                      bygroups(Punctuation, Name.Label), '#pop'))
+        return state
+
+    def _make_label_state(compound, _label=_label,
+                          _label_compound=_label_compound, _nl=_nl,
+                          _punct=_punct, _string=_string, _variable=_variable):
+        state = []
+        if compound:
+            state.append((r'(?=\))', Text, '#pop'))
+        state.append((r'({}?)((?:{}|{}|\^[{}]?{}|[^"%^{}{}{}])*)'.format(_label_compound if compound else _label, _string,
+                       _variable, _nl, r'[^)]' if compound else r'[\w\W]', _nl,
+                       _punct, r')' if compound else ''),
+                      bygroups(Name.Label, Comment.Single), '#pop'))
+        return state
+
+    def _make_redirect_state(compound,
+                             _core_token_compound=_core_token_compound,
+                             _nl=_nl, _punct=_punct, _stoken=_stoken,
+                             _string=_string, _space=_space,
+                             _variable=_variable, _nlws=_nlws):
+        stoken_compound = (rf'(?:[{_punct}]+|(?:{_string}|{_variable}|{_core_token_compound})+)')
+        return [
+            (rf'((?:(?<=[{_nlws}])\d)?)(>>?&|<&)([{_nlws}]*)(\d)',
+             bygroups(Number.Integer, Punctuation, Text, Number.Integer)),
+            (rf'((?:(?<=[{_nlws}])(?>?|<)({_space}?{stoken_compound if compound else _stoken})',
+             bygroups(Number.Integer, Punctuation, using(this, state='text')))
+        ]
+
+    tokens = {
+        'root': _make_begin_state(False),
+        'follow': _make_follow_state(False),
+        'arithmetic': _make_arithmetic_state(False),
+        'call': _make_call_state(False),
+        'label': _make_label_state(False),
+        'redirect': _make_redirect_state(False),
+        'root/compound': _make_begin_state(True),
+        'follow/compound': _make_follow_state(True),
+        'arithmetic/compound': _make_arithmetic_state(True),
+        'call/compound': _make_call_state(True),
+        'label/compound': _make_label_state(True),
+        'redirect/compound': _make_redirect_state(True),
+        'variable-or-escape': [
+            (_variable, Name.Variable),
+            (rf'%%|\^[{_nl}]?(\^!|[\w\W])', String.Escape)
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (_variable, Name.Variable),
+            (r'\^!|%%', String.Escape),
+            (rf'[^"%^{_nl}]+|[%^]', String.Double),
+            default('#pop')
+        ],
+        'sqstring': [
+            include('variable-or-escape'),
+            (r'[^%]+|%', String.Single)
+        ],
+        'bqstring': [
+            include('variable-or-escape'),
+            (r'[^%]+|%', String.Backtick)
+        ],
+        'text': [
+            (r'"', String.Double, 'string'),
+            include('variable-or-escape'),
+            (rf'[^"%^{_nlws}{_punct}\d)]+|.', Text)
+        ],
+        'variable': [
+            (r'"', String.Double, 'string'),
+            include('variable-or-escape'),
+            (rf'[^"%^{_nl}]+|.', Name.Variable)
+        ],
+        'for': [
+            (rf'({_space})(in)({_space})(\()',
+             bygroups(using(this, state='text'), Keyword,
+                      using(this, state='text'), Punctuation), '#pop'),
+            include('follow')
+        ],
+        'for2': [
+            (r'\)', Punctuation),
+            (rf'({_space})(do{_token_terminator})',
+             bygroups(using(this, state='text'), Keyword), '#pop'),
+            (rf'[{_nl}]+', Text),
+            include('follow')
+        ],
+        'for/f': [
+            (rf'(")((?:{_variable}|[^"])*?")([{_nlws}]*)(\))',
+             bygroups(String.Double, using(this, state='string'), Text,
+                      Punctuation)),
+            (r'"', String.Double, ('#pop', 'for2', 'string')),
+            (rf"('(?:%%|{_variable}|[\w\W])*?')([{_nlws}]*)(\))",
+             bygroups(using(this, state='sqstring'), Text, Punctuation)),
+            (rf'(`(?:%%|{_variable}|[\w\W])*?`)([{_nlws}]*)(\))',
+             bygroups(using(this, state='bqstring'), Text, Punctuation)),
+            include('for2')
+        ],
+        'for/l': [
+            (r'-?\d+', Number.Integer),
+            include('for2')
+        ],
+        'if': [
+            (rf'((?:cmdextversion|errorlevel){_token_terminator})({_space})(\d+)',
+             bygroups(Keyword, using(this, state='text'),
+                      Number.Integer), '#pop'),
+            (rf'(defined{_token_terminator})({_space})({_stoken})',
+             bygroups(Keyword, using(this, state='text'),
+                      using(this, state='variable')), '#pop'),
+            (rf'(exist{_token_terminator})({_space}{_stoken})',
+             bygroups(Keyword, using(this, state='text')), '#pop'),
+            (rf'({_number}{_space})({_opword})({_space}{_number})',
+             bygroups(using(this, state='arithmetic'), Operator.Word,
+                      using(this, state='arithmetic')), '#pop'),
+            (_stoken, using(this, state='text'), ('#pop', 'if2')),
+        ],
+        'if2': [
+            (rf'({_space}?)(==)({_space}?{_stoken})',
+             bygroups(using(this, state='text'), Operator,
+                      using(this, state='text')), '#pop'),
+            (rf'({_space})({_opword})({_space}{_stoken})',
+             bygroups(using(this, state='text'), Operator.Word,
+                      using(this, state='text')), '#pop')
+        ],
+        '(?': [
+            (_space, using(this, state='text')),
+            (r'\(', Punctuation, ('#pop', 'else?', 'root/compound')),
+            default('#pop')
+        ],
+        'else?': [
+            (_space, using(this, state='text')),
+            (rf'else{_token_terminator}', Keyword, '#pop'),
+            default('#pop')
+        ]
+    }
+
+
+class MSDOSSessionLexer(ShellSessionBaseLexer):
+    """
+    Lexer for MS DOS shell sessions, i.e. command lines, including a
+    prompt, interspersed with output.
+    """
+
+    name = 'MSDOS Session'
+    aliases = ['doscon']
+    filenames = []
+    mimetypes = []
+    url = 'https://en.wikipedia.org/wiki/MS-DOS'
+    version_added = '2.1'
+    _example = "doscon/session"
+
+    _innerLexerCls = BatchLexer
+    _ps1rgx = re.compile(r'^([^>]*>)(.*\n?)')
+    _ps2 = 'More? '
+
+
+class TcshLexer(RegexLexer):
+    """
+    Lexer for tcsh scripts.
+    """
+
+    name = 'Tcsh'
+    aliases = ['tcsh', 'csh']
+    filenames = ['*.tcsh', '*.csh']
+    mimetypes = ['application/x-csh']
+    url = 'https://www.tcsh.org'
+    version_added = '0.10'
+
+    tokens = {
+        'root': [
+            include('basic'),
+            (r'\$\(', Keyword, 'paren'),
+            (r'\$\{#?', Keyword, 'curly'),
+            (r'`', String.Backtick, 'backticks'),
+            include('data'),
+        ],
+        'basic': [
+            (r'\b(if|endif|else|while|then|foreach|case|default|'
+             r'break|continue|goto|breaksw|end|switch|endsw)\s*\b',
+             Keyword),
+            (r'\b(alias|alloc|bg|bindkey|builtins|bye|caller|cd|chdir|'
+             r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|'
+             r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|'
+             r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|'
+             r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|'
+             r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|'
+             r'source|stop|suspend|source|suspend|telltc|time|'
+             r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|'
+             r'ver|wait|warp|watchlog|where|which)\s*\b',
+             Name.Builtin),
+            (r'#.*', Comment),
+            (r'\\[\w\W]', String.Escape),
+            (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)),
+            (r'[\[\]{}()=]+', Operator),
+            (r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
+            (r';', Punctuation),
+        ],
+        'data': [
+            (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double),
+            (r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
+            (r'\s+', Text),
+            (r'[^=\s\[\]{}()$"\'`\\;#]+', Text),
+            (r'\d+(?= |\Z)', Number),
+            (r'\$#?(\w+|.)', Name.Variable),
+        ],
+        'curly': [
+            (r'\}', Keyword, '#pop'),
+            (r':-', Keyword),
+            (r'\w+', Name.Variable),
+            (r'[^}:"\'`$]+', Punctuation),
+            (r':', Punctuation),
+            include('root'),
+        ],
+        'paren': [
+            (r'\)', Keyword, '#pop'),
+            include('root'),
+        ],
+        'backticks': [
+            (r'`', String.Backtick, '#pop'),
+            include('root'),
+        ],
+    }
+
+
+class TcshSessionLexer(ShellSessionBaseLexer):
+    """
+    Lexer for Tcsh sessions, i.e. command lines, including a
+    prompt, interspersed with output.
+    """
+
+    name = 'Tcsh Session'
+    aliases = ['tcshcon']
+    filenames = []
+    mimetypes = []
+    url = 'https://www.tcsh.org'
+    version_added = '2.1'
+    _example = "tcshcon/session"
+
+    _innerLexerCls = TcshLexer
+    _ps1rgx = re.compile(r'^([^>]+>)(.*\n?)')
+    _ps2 = '? '
+
+
+class PowerShellLexer(RegexLexer):
+    """
+    For Windows PowerShell code.
+    """
+    name = 'PowerShell'
+    aliases = ['powershell', 'pwsh', 'posh', 'ps1', 'psm1']
+    filenames = ['*.ps1', '*.psm1']
+    mimetypes = ['text/x-powershell']
+    url = 'https://learn.microsoft.com/en-us/powershell'
+    version_added = '1.5'
+
+    flags = re.DOTALL | re.IGNORECASE | re.MULTILINE
+
+    keywords = (
+        'while validateset validaterange validatepattern validatelength '
+        'validatecount until trap switch return ref process param parameter in '
+        'if global: local: function foreach for finally filter end elseif else '
+        'dynamicparam do default continue cmdletbinding break begin alias \\? '
+        '% #script #private #local #global mandatory parametersetname position '
+        'valuefrompipeline valuefrompipelinebypropertyname '
+        'valuefromremainingarguments helpmessage try catch throw').split()
+
+    operators = (
+        'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle '
+        'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains '
+        'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt '
+        'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like '
+        'lt match ne not notcontains notlike notmatch or regex replace '
+        'wildcard').split()
+
+    verbs = (
+        'write where watch wait use update unregister unpublish unprotect '
+        'unlock uninstall undo unblock trace test tee take sync switch '
+        'suspend submit stop step start split sort skip show set send select '
+        'search scroll save revoke resume restore restart resolve resize '
+        'reset request repair rename remove register redo receive read push '
+        'publish protect pop ping out optimize open new move mount merge '
+        'measure lock limit join invoke install initialize import hide group '
+        'grant get format foreach find export expand exit enter enable edit '
+        'dismount disconnect disable deny debug cxnew copy convertto '
+        'convertfrom convert connect confirm compress complete compare close '
+        'clear checkpoint block backup assert approve aggregate add').split()
+
+    aliases_ = (
+        'ac asnp cat cd cfs chdir clc clear clhy cli clp cls clv cnsn '
+        'compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo epal '
+        'epcsv epsn erase etsn exsn fc fhx fl foreach ft fw gal gbp gc gci gcm '
+        'gcs gdr ghy gi gjb gl gm gmo gp gps gpv group gsn gsnp gsv gu gv gwmi '
+        'h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp '
+        'ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv '
+        'oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo '
+        'rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc select '
+        'set shcm si sl sleep sls sort sp spjb spps spsv start sujb sv swmi tee '
+        'trcm type wget where wjb write').split()
+
+    commenthelp = (
+        'component description example externalhelp forwardhelpcategory '
+        'forwardhelptargetname functionality inputs link '
+        'notes outputs parameter remotehelprunspace role synopsis').split()
+
+    tokens = {
+        'root': [
+            # we need to count pairs of parentheses for correct highlight
+            # of '$(...)' blocks in strings
+            (r'\(', Punctuation, 'child'),
+            (r'\s+', Text),
+            (r'^(\s*#[#\s]*)(\.(?:{}))([^\n]*$)'.format('|'.join(commenthelp)),
+             bygroups(Comment, String.Doc, Comment)),
+            (r'#[^\n]*?$', Comment),
+            (r'(<|<)#', Comment.Multiline, 'multline'),
+            (r'@"\n', String.Heredoc, 'heredoc-double'),
+            (r"@'\n.*?\n'@", String.Heredoc),
+            # escaped syntax
+            (r'`[\'"$@-]', Punctuation),
+            (r'"', String.Double, 'string'),
+            (r"'([^']|'')*'", String.Single),
+            (r'(\$|@@|@)((global|script|private|env):)?\w+',
+             Name.Variable),
+            (r'({})\b'.format('|'.join(keywords)), Keyword),
+            (r'-({})\b'.format('|'.join(operators)), Operator),
+            (r'({})-[a-z_]\w*\b'.format('|'.join(verbs)), Name.Builtin),
+            (r'({})\s'.format('|'.join(aliases_)), Name.Builtin),
+            (r'\[[a-z_\[][\w. `,\[\]]*\]', Name.Constant),  # .net [type]s
+            (r'-[a-z_]\w*', Name),
+            (r'\w+', Name),
+            (r'[.,;:@{}\[\]$()=+*/\\&%!~?^`|<>-]', Punctuation),
+        ],
+        'child': [
+            (r'\)', Punctuation, '#pop'),
+            include('root'),
+        ],
+        'multline': [
+            (r'[^#&.]+', Comment.Multiline),
+            (r'#(>|>)', Comment.Multiline, '#pop'),
+            (r'\.({})'.format('|'.join(commenthelp)), String.Doc),
+            (r'[#&.]', Comment.Multiline),
+        ],
+        'string': [
+            (r"`[0abfnrtv'\"$`]", String.Escape),
+            (r'[^$`"]+', String.Double),
+            (r'\$\(', Punctuation, 'child'),
+            (r'""', String.Double),
+            (r'[`$]', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'heredoc-double': [
+            (r'\n"@', String.Heredoc, '#pop'),
+            (r'\$\(', Punctuation, 'child'),
+            (r'[^@\n]+"]', String.Heredoc),
+            (r".", String.Heredoc),
+        ]
+    }
+
+
+class PowerShellSessionLexer(ShellSessionBaseLexer):
+    """
+    Lexer for PowerShell sessions, i.e. command lines, including a
+    prompt, interspersed with output.
+    """
+
+    name = 'PowerShell Session'
+    aliases = ['pwsh-session', 'ps1con']
+    filenames = []
+    mimetypes = []
+    url = 'https://learn.microsoft.com/en-us/powershell'
+    version_added = '2.1'
+    _example = "pwsh-session/session"
+
+    _innerLexerCls = PowerShellLexer
+    _bare_continuation = True
+    _ps1rgx = re.compile(r'^((?:\[[^]]+\]: )?PS[^>]*> ?)(.*\n?)')
+    _ps2 = '> '
+
+
+class FishShellLexer(RegexLexer):
+    """
+    Lexer for Fish shell scripts.
+    """
+
+    name = 'Fish'
+    aliases = ['fish', 'fishshell']
+    filenames = ['*.fish', '*.load']
+    mimetypes = ['application/x-fish']
+    url = 'https://fishshell.com'
+    version_added = '2.1'
+
+    tokens = {
+        'root': [
+            include('basic'),
+            include('data'),
+            include('interp'),
+        ],
+        'interp': [
+            (r'\$\(\(', Keyword, 'math'),
+            (r'\(', Keyword, 'paren'),
+            (r'\$#?(\w+|.)', Name.Variable),
+        ],
+        'basic': [
+            (r'\b(begin|end|if|else|while|break|for|in|return|function|block|'
+             r'case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|'
+             r'cd|count|test)(\s*)\b',
+             bygroups(Keyword, Text)),
+            (r'\b(alias|bg|bind|breakpoint|builtin|command|commandline|'
+             r'complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|'
+             r'fish_indent|fish_pager|fish_prompt|fish_right_prompt|'
+             r'fish_update_completions|fishd|funced|funcsave|functions|help|'
+             r'history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|'
+             r'pushd|random|read|set_color|source|status|trap|type|ulimit|'
+             r'umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)',
+             Name.Builtin),
+            (r'#.*\n', Comment),
+            (r'\\[\w\W]', String.Escape),
+            (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Whitespace, Operator)),
+            (r'[\[\]()=]', Operator),
+            (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String),
+        ],
+        'data': [
+            (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"', String.Double),
+            (r'"', String.Double, 'string'),
+            (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single),
+            (r"(?s)'.*?'", String.Single),
+            (r';', Punctuation),
+            (r'&|\||\^|<|>', Operator),
+            (r'\s+', Text),
+            (r'\d+(?= |\Z)', Number),
+            (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text),
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double),
+            include('interp'),
+        ],
+        'paren': [
+            (r'\)', Keyword, '#pop'),
+            include('root'),
+        ],
+        'math': [
+            (r'\)\)', Keyword, '#pop'),
+            (r'[-+*/%^|&]|\*\*|\|\|', Operator),
+            (r'\d+#\d+', Number),
+            (r'\d+#(?! )', Number),
+            (r'\d+', Number),
+            include('root'),
+        ],
+    }
+
+class ExeclineLexer(RegexLexer):
+    """
+    Lexer for Laurent Bercot's execline language.
+    """
+
+    name = 'execline'
+    aliases = ['execline']
+    filenames = ['*.exec']
+    url = 'https://skarnet.org/software/execline'
+    version_added = '2.7'
+
+    tokens = {
+        'root': [
+            include('basic'),
+            include('data'),
+            include('interp')
+        ],
+        'interp': [
+            (r'\$\{', String.Interpol, 'curly'),
+            (r'\$[\w@#]+', Name.Variable),  # user variable
+            (r'\$', Text),
+        ],
+        'basic': [
+            (r'\b(background|backtick|cd|define|dollarat|elgetopt|'
+             r'elgetpositionals|elglob|emptyenv|envfile|exec|execlineb|'
+             r'exit|export|fdblock|fdclose|fdmove|fdreserve|fdswap|'
+             r'forbacktickx|foreground|forstdin|forx|getcwd|getpid|heredoc|'
+             r'homeof|if|ifelse|ifte|ifthenelse|importas|loopwhilex|'
+             r'multidefine|multisubstitute|pipeline|piperw|posix-cd|'
+             r'redirfd|runblock|shift|trap|tryexec|umask|unexport|wait|'
+             r'withstdinas)\b', Name.Builtin),
+            (r'\A#!.+\n', Comment.Hashbang),
+            (r'#.*\n', Comment.Single),
+            (r'[{}]', Operator)
+        ],
+        'data': [
+            (r'(?s)"(\\.|[^"\\$])*"', String.Double),
+            (r'"', String.Double, 'string'),
+            (r'\s+', Text),
+            (r'[^\s{}$"\\]+', Text)
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'(?s)(\\\\|\\.|[^"\\$])+', String.Double),
+            include('interp'),
+        ],
+        'curly': [
+            (r'\}', String.Interpol, '#pop'),
+            (r'[\w#@]+', Name.Variable),
+            include('root')
+        ]
+
+    }
+
+    def analyse_text(text):
+        if shebang_matches(text, r'execlineb'):
+            return 1
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/sieve.py b/.venv/lib/python3.12/site-packages/pygments/lexers/sieve.py
new file mode 100644
index 0000000..4bda351
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/sieve.py
@@ -0,0 +1,78 @@
+"""
+    pygments.lexers.sieve
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Sieve file format.
+
+    https://tools.ietf.org/html/rfc5228
+    https://tools.ietf.org/html/rfc5173
+    https://tools.ietf.org/html/rfc5229
+    https://tools.ietf.org/html/rfc5230
+    https://tools.ietf.org/html/rfc5232
+    https://tools.ietf.org/html/rfc5235
+    https://tools.ietf.org/html/rfc5429
+    https://tools.ietf.org/html/rfc8580
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Comment, Name, Literal, String, Text, Punctuation, \
+    Keyword
+
+__all__ = ["SieveLexer"]
+
+
+class SieveLexer(RegexLexer):
+    """
+    Lexer for sieve format.
+    """
+    name = 'Sieve'
+    filenames = ['*.siv', '*.sieve']
+    aliases = ['sieve']
+    url = 'https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)'
+    version_added = '2.6'
+
+    tokens = {
+        'root': [
+            (r'\s+', Text),
+            (r'[();,{}\[\]]', Punctuation),
+            # import:
+            (r'(?i)require',
+             Keyword.Namespace),
+            # tags:
+            (r'(?i)(:)(addresses|all|contains|content|create|copy|comparator|'
+             r'count|days|detail|domain|fcc|flags|from|handle|importance|is|'
+             r'localpart|length|lowerfirst|lower|matches|message|mime|options|'
+             r'over|percent|quotewildcard|raw|regex|specialuse|subject|text|'
+             r'under|upperfirst|upper|value)',
+             bygroups(Name.Tag, Name.Tag)),
+            # tokens:
+            (r'(?i)(address|addflag|allof|anyof|body|discard|elsif|else|envelope|'
+             r'ereject|exists|false|fileinto|if|hasflag|header|keep|'
+             r'notify_method_capability|notify|not|redirect|reject|removeflag|'
+             r'setflag|size|spamtest|stop|string|true|vacation|virustest)',
+             Name.Builtin),
+            (r'(?i)set',
+             Keyword.Declaration),
+            # number:
+            (r'([0-9.]+)([kmgKMG])?',
+             bygroups(Literal.Number, Literal.Number)),
+            # comment:
+            (r'#.*$',
+             Comment.Single),
+            (r'/\*.*\*/',
+             Comment.Multiline),
+            # string:
+            (r'"[^"]*?"',
+             String),
+            # text block:
+            (r'text:',
+             Name.Tag, 'text'),
+        ],
+        'text': [
+            (r'[^.].*?\n', String),
+            (r'^\.', Punctuation, "#pop"),
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/slash.py b/.venv/lib/python3.12/site-packages/pygments/lexers/slash.py
new file mode 100644
index 0000000..2bcbdf8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/slash.py
@@ -0,0 +1,183 @@
+"""
+    pygments.lexers.slash
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for the Slash programming language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import ExtendedRegexLexer, bygroups, DelegatingLexer
+from pygments.token import Name, Number, String, Comment, Punctuation, \
+    Other, Keyword, Operator, Whitespace
+
+__all__ = ['SlashLexer']
+
+
+class SlashLanguageLexer(ExtendedRegexLexer):
+    _nkw = r'(?=[^a-zA-Z_0-9])'
+
+    def move_state(new_state):
+        return ("#pop", new_state)
+
+    def right_angle_bracket(lexer, match, ctx):
+        if len(ctx.stack) > 1 and ctx.stack[-2] == "string":
+            ctx.stack.pop()
+        yield match.start(), String.Interpol, '}'
+        ctx.pos = match.end()
+        pass
+
+    tokens = {
+        "root": [
+            (r"<%=",        Comment.Preproc,    move_state("slash")),
+            (r"<%!!",       Comment.Preproc,    move_state("slash")),
+            (r"<%#.*?%>",   Comment.Multiline),
+            (r"<%",         Comment.Preproc,    move_state("slash")),
+            (r".|\n",       Other),
+        ],
+        "string": [
+            (r"\\",         String.Escape,      move_state("string_e")),
+            (r"\"",         String,             move_state("slash")),
+            (r"#\{",        String.Interpol,    "slash"),
+            (r'.|\n',       String),
+        ],
+        "string_e": [
+            (r'n',                  String.Escape,      move_state("string")),
+            (r't',                  String.Escape,      move_state("string")),
+            (r'r',                  String.Escape,      move_state("string")),
+            (r'e',                  String.Escape,      move_state("string")),
+            (r'x[a-fA-F0-9]{2}',    String.Escape,      move_state("string")),
+            (r'.',                  String.Escape,      move_state("string")),
+        ],
+        "regexp": [
+            (r'}[a-z]*',            String.Regex,       move_state("slash")),
+            (r'\\(.|\n)',           String.Regex),
+            (r'{',                  String.Regex,       "regexp_r"),
+            (r'.|\n',               String.Regex),
+        ],
+        "regexp_r": [
+            (r'}[a-z]*',            String.Regex,       "#pop"),
+            (r'\\(.|\n)',           String.Regex),
+            (r'{',                  String.Regex,       "regexp_r"),
+        ],
+        "slash": [
+            (r"%>",                     Comment.Preproc,    move_state("root")),
+            (r"\"",                     String,             move_state("string")),
+            (r"'[a-zA-Z0-9_]+",         String),
+            (r'%r{',                    String.Regex,       move_state("regexp")),
+            (r'/\*.*?\*/',              Comment.Multiline),
+            (r"(#|//).*?\n",            Comment.Single),
+            (r'-?[0-9]+e[+-]?[0-9]+',   Number.Float),
+            (r'-?[0-9]+\.[0-9]+(e[+-]?[0-9]+)?', Number.Float),
+            (r'-?[0-9]+',               Number.Integer),
+            (r'nil'+_nkw,               Name.Builtin),
+            (r'true'+_nkw,              Name.Builtin),
+            (r'false'+_nkw,             Name.Builtin),
+            (r'self'+_nkw,              Name.Builtin),
+            (r'(class)(\s+)([A-Z][a-zA-Z0-9_\']*)',
+                bygroups(Keyword, Whitespace, Name.Class)),
+            (r'class'+_nkw,             Keyword),
+            (r'extends'+_nkw,           Keyword),
+            (r'(def)(\s+)(self)(\s*)(\.)(\s*)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|==|<=>|<=|<|>=|>|\+|-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)',
+                bygroups(Keyword, Whitespace, Name.Builtin, Whitespace, Punctuation, Whitespace, Name.Function)),
+            (r'(def)(\s+)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|==|<=>|<=|<|>=|>|\+|-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)',
+                bygroups(Keyword, Whitespace, Name.Function)),
+            (r'def'+_nkw,               Keyword),
+            (r'if'+_nkw,                Keyword),
+            (r'elsif'+_nkw,             Keyword),
+            (r'else'+_nkw,              Keyword),
+            (r'unless'+_nkw,            Keyword),
+            (r'for'+_nkw,               Keyword),
+            (r'in'+_nkw,                Keyword),
+            (r'while'+_nkw,             Keyword),
+            (r'until'+_nkw,             Keyword),
+            (r'and'+_nkw,               Keyword),
+            (r'or'+_nkw,                Keyword),
+            (r'not'+_nkw,               Keyword),
+            (r'lambda'+_nkw,            Keyword),
+            (r'try'+_nkw,               Keyword),
+            (r'catch'+_nkw,             Keyword),
+            (r'return'+_nkw,            Keyword),
+            (r'next'+_nkw,              Keyword),
+            (r'last'+_nkw,              Keyword),
+            (r'throw'+_nkw,             Keyword),
+            (r'use'+_nkw,               Keyword),
+            (r'switch'+_nkw,            Keyword),
+            (r'\\',                     Keyword),
+            (r'λ',                      Keyword),
+            (r'__FILE__'+_nkw,          Name.Builtin.Pseudo),
+            (r'__LINE__'+_nkw,          Name.Builtin.Pseudo),
+            (r'[A-Z][a-zA-Z0-9_\']*'+_nkw, Name.Constant),
+            (r'[a-z_][a-zA-Z0-9_\']*'+_nkw, Name),
+            (r'@[a-z_][a-zA-Z0-9_\']*'+_nkw, Name.Variable.Instance),
+            (r'@@[a-z_][a-zA-Z0-9_\']*'+_nkw, Name.Variable.Class),
+            (r'\(',                     Punctuation),
+            (r'\)',                     Punctuation),
+            (r'\[',                     Punctuation),
+            (r'\]',                     Punctuation),
+            (r'\{',                     Punctuation),
+            (r'\}',                     right_angle_bracket),
+            (r';',                      Punctuation),
+            (r',',                      Punctuation),
+            (r'<<=',                    Operator),
+            (r'>>=',                    Operator),
+            (r'<<',                     Operator),
+            (r'>>',                     Operator),
+            (r'==',                     Operator),
+            (r'!=',                     Operator),
+            (r'=>',                     Operator),
+            (r'=',                      Operator),
+            (r'<=>',                    Operator),
+            (r'<=',                     Operator),
+            (r'>=',                     Operator),
+            (r'<',                      Operator),
+            (r'>',                      Operator),
+            (r'\+\+',                   Operator),
+            (r'\+=',                    Operator),
+            (r'-=',                     Operator),
+            (r'\*\*=',                  Operator),
+            (r'\*=',                    Operator),
+            (r'\*\*',                   Operator),
+            (r'\*',                     Operator),
+            (r'/=',                     Operator),
+            (r'\+',                     Operator),
+            (r'-',                      Operator),
+            (r'/',                      Operator),
+            (r'%=',                     Operator),
+            (r'%',                      Operator),
+            (r'^=',                     Operator),
+            (r'&&=',                    Operator),
+            (r'&=',                     Operator),
+            (r'&&',                     Operator),
+            (r'&',                      Operator),
+            (r'\|\|=',                  Operator),
+            (r'\|=',                    Operator),
+            (r'\|\|',                   Operator),
+            (r'\|',                     Operator),
+            (r'!',                      Operator),
+            (r'\.\.\.',                 Operator),
+            (r'\.\.',                   Operator),
+            (r'\.',                     Operator),
+            (r'::',                     Operator),
+            (r':',                      Operator),
+            (r'(\s|\n)+',               Whitespace),
+            (r'[a-z_][a-zA-Z0-9_\']*',  Name.Variable),
+        ],
+    }
+
+
+class SlashLexer(DelegatingLexer):
+    """
+    Lexer for the Slash programming language.
+    """
+
+    name = 'Slash'
+    aliases = ['slash']
+    filenames = ['*.sla']
+    url = 'https://github.com/arturadib/Slash-A'
+    version_added = '2.4'
+
+    def __init__(self, **options):
+        from pygments.lexers.web import HtmlLexer
+        super().__init__(HtmlLexer, SlashLanguageLexer, **options)
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/smalltalk.py b/.venv/lib/python3.12/site-packages/pygments/lexers/smalltalk.py
new file mode 100644
index 0000000..69e0519
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/smalltalk.py
@@ -0,0 +1,194 @@
+"""
+    pygments.lexers.smalltalk
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Smalltalk and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, include, bygroups, default
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['SmalltalkLexer', 'NewspeakLexer']
+
+
+class SmalltalkLexer(RegexLexer):
+    """
+    For Smalltalk syntax.
+    Contributed by Stefan Matthias Aust.
+    Rewritten by Nils Winter.
+    """
+    name = 'Smalltalk'
+    url = 'http://www.smalltalk.org/'
+    filenames = ['*.st']
+    aliases = ['smalltalk', 'squeak', 'st']
+    mimetypes = ['text/x-smalltalk']
+    version_added = '0.10'
+
+    tokens = {
+        'root': [
+            (r'(<)(\w+:)(.*?)(>)', bygroups(Text, Keyword, Text, Text)),
+            include('squeak fileout'),
+            include('whitespaces'),
+            include('method definition'),
+            (r'(\|)([\w\s]*)(\|)', bygroups(Operator, Name.Variable, Operator)),
+            include('objects'),
+            (r'\^|\:=|\_', Operator),
+            # temporaries
+            (r'[\]({}.;!]', Text),
+        ],
+        'method definition': [
+            # Not perfect can't allow whitespaces at the beginning and the
+            # without breaking everything
+            (r'([a-zA-Z]+\w*:)(\s*)(\w+)',
+             bygroups(Name.Function, Text, Name.Variable)),
+            (r'^(\b[a-zA-Z]+\w*\b)(\s*)$', bygroups(Name.Function, Text)),
+            (r'^([-+*/\\~<>=|&!?,@%]+)(\s*)(\w+)(\s*)$',
+             bygroups(Name.Function, Text, Name.Variable, Text)),
+        ],
+        'blockvariables': [
+            include('whitespaces'),
+            (r'(:)(\s*)(\w+)',
+             bygroups(Operator, Text, Name.Variable)),
+            (r'\|', Operator, '#pop'),
+            default('#pop'),  # else pop
+        ],
+        'literals': [
+            (r"'(''|[^'])*'", String, 'afterobject'),
+            (r'\$.', String.Char, 'afterobject'),
+            (r'#\(', String.Symbol, 'parenth'),
+            (r'\)', Text, 'afterobject'),
+            (r'(\d+r)?-?\d+(\.\d+)?(e-?\d+)?', Number, 'afterobject'),
+        ],
+        '_parenth_helper': [
+            include('whitespaces'),
+            (r'(\d+r)?-?\d+(\.\d+)?(e-?\d+)?', Number),
+            (r'[-+*/\\~<>=|&#!?,@%\w:]+', String.Symbol),
+            # literals
+            (r"'(''|[^'])*'", String),
+            (r'\$.', String.Char),
+            (r'#*\(', String.Symbol, 'inner_parenth'),
+        ],
+        'parenth': [
+            # This state is a bit tricky since
+            # we can't just pop this state
+            (r'\)', String.Symbol, ('root', 'afterobject')),
+            include('_parenth_helper'),
+        ],
+        'inner_parenth': [
+            (r'\)', String.Symbol, '#pop'),
+            include('_parenth_helper'),
+        ],
+        'whitespaces': [
+            # skip whitespace and comments
+            (r'\s+', Text),
+            (r'"(""|[^"])*"', Comment),
+        ],
+        'objects': [
+            (r'\[', Text, 'blockvariables'),
+            (r'\]', Text, 'afterobject'),
+            (r'\b(self|super|true|false|nil|thisContext)\b',
+             Name.Builtin.Pseudo, 'afterobject'),
+            (r'\b[A-Z]\w*(?!:)\b', Name.Class, 'afterobject'),
+            (r'\b[a-z]\w*(?!:)\b', Name.Variable, 'afterobject'),
+            (r'#("(""|[^"])*"|[-+*/\\~<>=|&!?,@%]+|[\w:]+)',
+             String.Symbol, 'afterobject'),
+            include('literals'),
+        ],
+        'afterobject': [
+            (r'! !$', Keyword, '#pop'),  # squeak chunk delimiter
+            include('whitespaces'),
+            (r'\b(ifTrue:|ifFalse:|whileTrue:|whileFalse:|timesRepeat:)',
+             Name.Builtin, '#pop'),
+            (r'\b(new\b(?!:))', Name.Builtin),
+            (r'\:=|\_', Operator, '#pop'),
+            (r'\b[a-zA-Z]+\w*:', Name.Function, '#pop'),
+            (r'\b[a-zA-Z]+\w*', Name.Function),
+            (r'\w+:?|[-+*/\\~<>=|&!?,@%]+', Name.Function, '#pop'),
+            (r'\.', Punctuation, '#pop'),
+            (r';', Punctuation),
+            (r'[\])}]', Text),
+            (r'[\[({]', Text, '#pop'),
+        ],
+        'squeak fileout': [
+            # Squeak fileout format (optional)
+            (r'^"(""|[^"])*"!', Keyword),
+            (r"^'(''|[^'])*'!", Keyword),
+            (r'^(!)(\w+)( commentStamp: )(.*?)( prior: .*?!\n)(.*?)(!)',
+                bygroups(Keyword, Name.Class, Keyword, String, Keyword, Text, Keyword)),
+            (r"^(!)(\w+(?: class)?)( methodsFor: )('(?:''|[^'])*')(.*?!)",
+                bygroups(Keyword, Name.Class, Keyword, String, Keyword)),
+            (r'^(\w+)( subclass: )(#\w+)'
+             r'(\s+instanceVariableNames: )(.*?)'
+             r'(\s+classVariableNames: )(.*?)'
+             r'(\s+poolDictionaries: )(.*?)'
+             r'(\s+category: )(.*?)(!)',
+                bygroups(Name.Class, Keyword, String.Symbol, Keyword, String, Keyword,
+                         String, Keyword, String, Keyword, String, Keyword)),
+            (r'^(\w+(?: class)?)(\s+instanceVariableNames: )(.*?)(!)',
+                bygroups(Name.Class, Keyword, String, Keyword)),
+            (r'(!\n)(\].*)(! !)$', bygroups(Keyword, Text, Keyword)),
+            (r'! !$', Keyword),
+        ],
+    }
+
+
+class NewspeakLexer(RegexLexer):
+    """
+    For Newspeak syntax.
+    """
+    name = 'Newspeak'
+    url = 'http://newspeaklanguage.org/'
+    filenames = ['*.ns2']
+    aliases = ['newspeak', ]
+    mimetypes = ['text/x-newspeak']
+    version_added = '1.1'
+
+    tokens = {
+        'root': [
+            (r'\b(Newsqueak2)\b', Keyword.Declaration),
+            (r"'[^']*'", String),
+            (r'\b(class)(\s+)(\w+)(\s*)',
+             bygroups(Keyword.Declaration, Text, Name.Class, Text)),
+            (r'\b(mixin|self|super|private|public|protected|nil|true|false)\b',
+             Keyword),
+            (r'(\w+\:)(\s*)([a-zA-Z_]\w+)',
+             bygroups(Name.Function, Text, Name.Variable)),
+            (r'(\w+)(\s*)(=)',
+             bygroups(Name.Attribute, Text, Operator)),
+            (r'<\w+>', Comment.Special),
+            include('expressionstat'),
+            include('whitespace')
+        ],
+
+        'expressionstat': [
+            (r'(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float),
+            (r'\d+', Number.Integer),
+            (r':\w+', Name.Variable),
+            (r'(\w+)(::)', bygroups(Name.Variable, Operator)),
+            (r'\w+:', Name.Function),
+            (r'\w+', Name.Variable),
+            (r'\(|\)', Punctuation),
+            (r'\[|\]', Punctuation),
+            (r'\{|\}', Punctuation),
+
+            (r'(\^|\+|\/|~|\*|<|>|=|@|%|\||&|\?|!|,|-|:)', Operator),
+            (r'\.|;', Punctuation),
+            include('whitespace'),
+            include('literals'),
+        ],
+        'literals': [
+            (r'\$.', String),
+            (r"'[^']*'", String),
+            (r"#'[^']*'", String.Symbol),
+            (r"#\w+:?", String.Symbol),
+            (r"#(\+|\/|~|\*|<|>|=|@|%|\||&|\?|!|,|-)+", String.Symbol)
+        ],
+        'whitespace': [
+            (r'\s+', Text),
+            (r'"[^"]*"', Comment)
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/smithy.py b/.venv/lib/python3.12/site-packages/pygments/lexers/smithy.py
new file mode 100644
index 0000000..2642bb8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/smithy.py
@@ -0,0 +1,77 @@
+"""
+    pygments.lexers.smithy
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Smithy IDL.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, words
+from pygments.token import Text, Comment, Keyword, Name, String, \
+    Number, Whitespace, Punctuation
+
+__all__ = ['SmithyLexer']
+
+
+class SmithyLexer(RegexLexer):
+    """
+    For Smithy IDL
+    """
+    name = 'Smithy'
+    url = 'https://awslabs.github.io/smithy/'
+    filenames = ['*.smithy']
+    aliases = ['smithy']
+    version_added = '2.10'
+
+    unquoted = r'[A-Za-z0-9_\.#$-]+'
+    identifier = r"[A-Za-z0-9_\.#$-]+"
+
+    simple_shapes = (
+        'use', 'byte', 'short', 'integer', 'long', 'float', 'document',
+        'double', 'bigInteger', 'bigDecimal', 'boolean', 'blob', 'string',
+        'timestamp',
+    )
+
+    aggregate_shapes = (
+       'apply', 'list', 'map', 'set', 'structure', 'union', 'resource',
+       'operation', 'service', 'trait'
+    )
+
+    tokens = {
+        'root': [
+            (r'///.*$', Comment.Multiline),
+            (r'//.*$', Comment),
+            (r'@[0-9a-zA-Z\.#-]*', Name.Decorator),
+            (r'(=)', Name.Decorator),
+            (r'^(\$version)(:)(.+)',
+                bygroups(Keyword.Declaration, Name.Decorator, Name.Class)),
+            (r'^(namespace)(\s+' + identifier + r')\b',
+                bygroups(Keyword.Declaration, Name.Class)),
+            (words(simple_shapes,
+                   prefix=r'^', suffix=r'(\s+' + identifier + r')\b'),
+                bygroups(Keyword.Declaration, Name.Class)),
+            (words(aggregate_shapes,
+                   prefix=r'^', suffix=r'(\s+' + identifier + r')'),
+                bygroups(Keyword.Declaration, Name.Class)),
+            (r'^(metadata)(\s+)((?:\S+)|(?:\"[^"]+\"))(\s*)(=)',
+                bygroups(Keyword.Declaration, Whitespace, Name.Class,
+                         Whitespace, Name.Decorator)),
+            (r"(true|false|null)", Keyword.Constant),
+            (r"(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)", Number),
+            (identifier + ":", Name.Label),
+            (identifier, Name.Variable.Class),
+            (r'\[', Text, "#push"),
+            (r'\]', Text, "#pop"),
+            (r'\(', Text, "#push"),
+            (r'\)', Text, "#pop"),
+            (r'\{', Text, "#push"),
+            (r'\}', Text, "#pop"),
+            (r'"{3}(\\\\|\n|\\")*"{3}', String.Doc),
+            (r'"(\\\\|\n|\\"|[^"])*"', String.Double),
+            (r"'(\\\\|\n|\\'|[^'])*'", String.Single),
+            (r'[:,]+', Punctuation),
+            (r'\s+', Whitespace),
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/smv.py b/.venv/lib/python3.12/site-packages/pygments/lexers/smv.py
new file mode 100644
index 0000000..3d06f99
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/smv.py
@@ -0,0 +1,78 @@
+"""
+    pygments.lexers.smv
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the SMV languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words
+from pygments.token import Comment, Keyword, Name, Number, Operator, \
+    Punctuation, Text
+
+__all__ = ['NuSMVLexer']
+
+
+class NuSMVLexer(RegexLexer):
+    """
+    Lexer for the NuSMV language.
+    """
+
+    name = 'NuSMV'
+    aliases = ['nusmv']
+    filenames = ['*.smv']
+    mimetypes = []
+    url = 'https://nusmv.fbk.eu'
+    version_added = '2.2'
+
+    tokens = {
+        'root': [
+            # Comments
+            (r'(?s)\/\-\-.*?\-\-/', Comment),
+            (r'--.*\n', Comment),
+
+            # Reserved
+            (words(('MODULE', 'DEFINE', 'MDEFINE', 'CONSTANTS', 'VAR', 'IVAR',
+                    'FROZENVAR', 'INIT', 'TRANS', 'INVAR', 'SPEC', 'CTLSPEC',
+                    'LTLSPEC', 'PSLSPEC', 'COMPUTE', 'NAME', 'INVARSPEC',
+                    'FAIRNESS', 'JUSTICE', 'COMPASSION', 'ISA', 'ASSIGN',
+                    'CONSTRAINT', 'SIMPWFF', 'CTLWFF', 'LTLWFF', 'PSLWFF',
+                    'COMPWFF', 'IN', 'MIN', 'MAX', 'MIRROR', 'PRED',
+                    'PREDICATES'), suffix=r'(?![\w$#-])'),
+             Keyword.Declaration),
+            (r'process(?![\w$#-])', Keyword),
+            (words(('array', 'of', 'boolean', 'integer', 'real', 'word'),
+                   suffix=r'(?![\w$#-])'), Keyword.Type),
+            (words(('case', 'esac'), suffix=r'(?![\w$#-])'), Keyword),
+            (words(('word1', 'bool', 'signed', 'unsigned', 'extend', 'resize',
+                    'sizeof', 'uwconst', 'swconst', 'init', 'self', 'count',
+                    'abs', 'max', 'min'), suffix=r'(?![\w$#-])'),
+             Name.Builtin),
+            (words(('EX', 'AX', 'EF', 'AF', 'EG', 'AG', 'E', 'F', 'O', 'G',
+                    'H', 'X', 'Y', 'Z', 'A', 'U', 'S', 'V', 'T', 'BU', 'EBF',
+                    'ABF', 'EBG', 'ABG', 'next', 'mod', 'union', 'in', 'xor',
+                    'xnor'), suffix=r'(?![\w$#-])'),
+                Operator.Word),
+            (words(('TRUE', 'FALSE'), suffix=r'(?![\w$#-])'), Keyword.Constant),
+
+            # Names
+            (r'[a-zA-Z_][\w$#-]*', Name.Variable),
+
+            # Operators
+            (r':=', Operator),
+            (r'[-&|+*/<>!=]', Operator),
+
+            # Literals
+            (r'\-?\d+\b', Number.Integer),
+            (r'0[su][bB]\d*_[01_]+', Number.Bin),
+            (r'0[su][oO]\d*_[0-7_]+', Number.Oct),
+            (r'0[su][dD]\d*_[\d_]+', Number.Decimal),
+            (r'0[su][hH]\d*_[\da-fA-F_]+', Number.Hex),
+
+            # Whitespace, punctuation and the rest
+            (r'\s+', Text.Whitespace),
+            (r'[()\[\]{};?:.,]', Punctuation),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/snobol.py b/.venv/lib/python3.12/site-packages/pygments/lexers/snobol.py
new file mode 100644
index 0000000..7016c96
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/snobol.py
@@ -0,0 +1,82 @@
+"""
+    pygments.lexers.snobol
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the SNOBOL language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation
+
+__all__ = ['SnobolLexer']
+
+
+class SnobolLexer(RegexLexer):
+    """
+    Lexer for the SNOBOL4 programming language.
+
+    Recognizes the common ASCII equivalents of the original SNOBOL4 operators.
+    Does not require spaces around binary operators.
+    """
+
+    name = "Snobol"
+    aliases = ["snobol"]
+    filenames = ['*.snobol']
+    mimetypes = ['text/x-snobol']
+    url = 'https://www.regressive.org/snobol4'
+    version_added = '1.5'
+
+    tokens = {
+        # root state, start of line
+        # comments, continuation lines, and directives start in column 1
+        # as do labels
+        'root': [
+            (r'\*.*\n', Comment),
+            (r'[+.] ', Punctuation, 'statement'),
+            (r'-.*\n', Comment),
+            (r'END\s*\n', Name.Label, 'heredoc'),
+            (r'[A-Za-z$][\w$]*', Name.Label, 'statement'),
+            (r'\s+', Text, 'statement'),
+        ],
+        # statement state, line after continuation or label
+        'statement': [
+            (r'\s*\n', Text, '#pop'),
+            (r'\s+', Text),
+            (r'(?<=[^\w.])(LT|LE|EQ|NE|GE|GT|INTEGER|IDENT|DIFFER|LGT|SIZE|'
+             r'REPLACE|TRIM|DUPL|REMDR|DATE|TIME|EVAL|APPLY|OPSYN|LOAD|UNLOAD|'
+             r'LEN|SPAN|BREAK|ANY|NOTANY|TAB|RTAB|REM|POS|RPOS|FAIL|FENCE|'
+             r'ABORT|ARB|ARBNO|BAL|SUCCEED|INPUT|OUTPUT|TERMINAL)(?=[^\w.])',
+             Name.Builtin),
+            (r'[A-Za-z][\w.]*', Name),
+            # ASCII equivalents of original operators
+            # | for the EBCDIC equivalent, ! likewise
+            # \ for EBCDIC negation
+            (r'\*\*|[?$.!%*/#+\-@|&\\=]', Operator),
+            (r'"[^"]*"', String),
+            (r"'[^']*'", String),
+            # Accept SPITBOL syntax for real numbers
+            # as well as Macro SNOBOL4
+            (r'[0-9]+(?=[^.EeDd])', Number.Integer),
+            (r'[0-9]+(\.[0-9]*)?([EDed][-+]?[0-9]+)?', Number.Float),
+            # Goto
+            (r':', Punctuation, 'goto'),
+            (r'[()<>,;]', Punctuation),
+        ],
+        # Goto block
+        'goto': [
+            (r'\s*\n', Text, "#pop:2"),
+            (r'\s+', Text),
+            (r'F|S', Keyword),
+            (r'(\()([A-Za-z][\w.]*)(\))',
+             bygroups(Punctuation, Name.Label, Punctuation))
+        ],
+        # everything after the END statement is basically one
+        # big heredoc.
+        'heredoc': [
+            (r'.*\n', String.Heredoc)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/solidity.py b/.venv/lib/python3.12/site-packages/pygments/lexers/solidity.py
new file mode 100644
index 0000000..3f2d1dd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/solidity.py
@@ -0,0 +1,87 @@
+"""
+    pygments.lexers.solidity
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Solidity.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, include, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+
+__all__ = ['SolidityLexer']
+
+
+class SolidityLexer(RegexLexer):
+    """
+    For Solidity source code.
+    """
+
+    name = 'Solidity'
+    aliases = ['solidity']
+    filenames = ['*.sol']
+    mimetypes = []
+    url = 'https://soliditylang.org'
+    version_added = '2.5'
+
+    datatype = (
+        r'\b(address|bool|(?:(?:bytes|hash|int|string|uint)(?:8|16|24|32|40|48|56|64'
+        r'|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208'
+        r'|216|224|232|240|248|256)?))\b'
+    )
+
+    tokens = {
+        'root': [
+            include('whitespace'),
+            include('comments'),
+            (r'\bpragma\s+solidity\b', Keyword, 'pragma'),
+            (r'\b(contract)(\s+)([a-zA-Z_]\w*)',
+             bygroups(Keyword, Whitespace, Name.Entity)),
+            (datatype + r'(\s+)((?:external|public|internal|private)\s+)?' +
+             r'([a-zA-Z_]\w*)',
+             bygroups(Keyword.Type, Whitespace, Keyword, Name.Variable)),
+            (r'\b(enum|event|function|struct)(\s+)([a-zA-Z_]\w*)',
+             bygroups(Keyword.Type, Whitespace, Name.Variable)),
+            (r'\b(msg|block|tx)\.([A-Za-z_][a-zA-Z0-9_]*)\b', Keyword),
+            (words((
+                'block', 'break', 'constant', 'constructor', 'continue',
+                'contract', 'do', 'else', 'external', 'false', 'for',
+                'function', 'if', 'import', 'inherited', 'internal', 'is',
+                'library', 'mapping', 'memory', 'modifier', 'msg', 'new',
+                'payable', 'private', 'public', 'require', 'return',
+                'returns', 'struct', 'suicide', 'throw', 'this', 'true',
+                'tx', 'var', 'while'), prefix=r'\b', suffix=r'\b'),
+             Keyword.Type),
+            (words(('keccak256',), prefix=r'\b', suffix=r'\b'), Name.Builtin),
+            (datatype, Keyword.Type),
+            include('constants'),
+            (r'[a-zA-Z_]\w*', Text),
+            (r'[~!%^&*+=|?:<>/-]', Operator),
+            (r'[.;{}(),\[\]]', Punctuation)
+        ],
+        'comments': [
+            (r'//(\n|[\w\W]*?[^\\]\n)', Comment.Single),
+            (r'/(\\\n)?[*][\w\W]*?[*](\\\n)?/', Comment.Multiline),
+            (r'/(\\\n)?[*][\w\W]*', Comment.Multiline)
+        ],
+        'constants': [
+            (r'("(\\"|.)*?")', String.Double),
+            (r"('(\\'|.)*?')", String.Single),
+            (r'\b0[xX][0-9a-fA-F]+\b', Number.Hex),
+            (r'\b\d+\b', Number.Decimal),
+        ],
+        'pragma': [
+            include('whitespace'),
+            include('comments'),
+            (r'(\^|>=|<)(\s*)(\d+\.\d+\.\d+)',
+             bygroups(Operator, Whitespace, Keyword)),
+            (r';', Punctuation, '#pop')
+        ],
+        'whitespace': [
+            (r'\s+', Whitespace),
+            (r'\n', Whitespace)
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/soong.py b/.venv/lib/python3.12/site-packages/pygments/lexers/soong.py
new file mode 100644
index 0000000..0d7f12c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/soong.py
@@ -0,0 +1,78 @@
+"""
+    pygments.lexers.soong
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Soong (Android.bp Blueprint) files.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, include
+from pygments.token import Comment, Name, Number, Operator, Punctuation, \
+        String, Whitespace
+
+__all__ = ['SoongLexer']
+
+class SoongLexer(RegexLexer):
+    name = 'Soong'
+    version_added = '2.18'
+    url = 'https://source.android.com/docs/setup/reference/androidbp'
+    aliases = ['androidbp', 'bp', 'soong']
+    filenames = ['Android.bp']
+
+    tokens = {
+        'root': [
+            # A variable assignment
+            (r'(\w*)(\s*)(\+?=)(\s*)',
+             bygroups(Name.Variable, Whitespace, Operator, Whitespace),
+             'assign-rhs'),
+
+            # A top-level module
+            (r'(\w*)(\s*)(\{)',
+             bygroups(Name.Function, Whitespace, Punctuation),
+             'in-rule'),
+
+            # Everything else
+            include('comments'),
+            (r'\s+', Whitespace),  # newlines okay
+        ],
+        'assign-rhs': [
+            include('expr'),
+            (r'\n', Whitespace, '#pop'),
+        ],
+        'in-list': [
+            include('expr'),
+            include('comments'),
+            (r'\s+', Whitespace),  # newlines okay in a list
+            (r',', Punctuation),
+            (r'\]', Punctuation, '#pop'),
+        ],
+        'in-map': [
+            # A map key
+            (r'(\w+)(:)(\s*)', bygroups(Name, Punctuation, Whitespace)),
+
+            include('expr'),
+            include('comments'),
+            (r'\s+', Whitespace),  # newlines okay in a map
+            (r',', Punctuation),
+            (r'\}', Punctuation, '#pop'),
+        ],
+        'in-rule': [
+            # Just re-use map syntax
+            include('in-map'),
+        ],
+        'comments': [
+            (r'//.*', Comment.Single),
+            (r'/(\\\n)?[*](.|\n)*?[*](\\\n)?/', Comment.Multiline),
+        ],
+        'expr': [
+            (r'(true|false)\b', Name.Builtin),
+            (r'0x[0-9a-fA-F]+', Number.Hex),
+            (r'\d+', Number.Integer),
+            (r'".*?"', String),
+            (r'\{', Punctuation, 'in-map'),
+            (r'\[', Punctuation, 'in-list'),
+            (r'\w+', Name),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/sophia.py b/.venv/lib/python3.12/site-packages/pygments/lexers/sophia.py
new file mode 100644
index 0000000..86435f3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/sophia.py
@@ -0,0 +1,102 @@
+"""
+    pygments.lexers.sophia
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexer for Sophia.
+
+    Derived from pygments/lexers/reason.py.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, include, default, words
+from pygments.token import Comment, Keyword, Name, Number, Operator, \
+    Punctuation, String, Text
+
+__all__ = ['SophiaLexer']
+
+class SophiaLexer(RegexLexer):
+    """
+    A Sophia lexer.
+    """
+
+    name = 'Sophia'
+    aliases = ['sophia']
+    filenames = ['*.aes']
+    mimetypes = []
+    url = 'https://docs.aeternity.com/aesophia'
+    version_added = '2.11'
+
+    keywords = (
+        'contract', 'include', 'let', 'switch', 'type', 'record', 'datatype',
+        'if', 'elif', 'else', 'function', 'stateful', 'payable', 'public',
+        'entrypoint', 'private', 'indexed', 'namespace', 'interface', 'main',
+        'using', 'as', 'for', 'hiding',
+    )
+
+    builtins = ('state', 'put', 'abort', 'require')
+
+    word_operators = ('mod', 'band', 'bor', 'bxor', 'bnot')
+
+    primitive_types = ('int', 'address', 'bool', 'bits', 'bytes', 'string',
+                       'list', 'option', 'char', 'unit', 'map', 'event',
+                       'hash', 'signature', 'oracle', 'oracle_query')
+
+    tokens = {
+        'escape-sequence': [
+            (r'\\[\\"\'ntbr]', String.Escape),
+            (r'\\[0-9]{3}', String.Escape),
+            (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        ],
+        'root': [
+            (r'\s+', Text.Whitespace),
+            (r'(true|false)\b', Keyword.Constant),
+            (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Class, 'dotted'),
+            (r'\b([A-Z][\w\']*)', Name.Function),
+            (r'//.*?\n', Comment.Single),
+            (r'\/\*(?!/)', Comment.Multiline, 'comment'),
+
+            (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex),
+            (r'#[\da-fA-F][\da-fA-F_]*', Name.Label),
+            (r'\d[\d_]*', Number.Integer),
+
+            (words(keywords, suffix=r'\b'), Keyword),
+            (words(builtins, suffix=r'\b'), Name.Builtin),
+            (words(word_operators, prefix=r'\b', suffix=r'\b'), Operator.Word),
+            (words(primitive_types, prefix=r'\b', suffix=r'\b'), Keyword.Type),
+
+            (r'[=!<>+\\*/:&|?~@^-]', Operator.Word),
+            (r'[.;:{}(),\[\]]', Punctuation),
+
+            (r"(ak_|ok_|oq_|ct_)[\w']*", Name.Label),
+            (r"[^\W\d][\w']*", Name),
+
+            (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'",
+             String.Char),
+            (r"'.'", String.Char),
+            (r"'[a-z][\w]*", Name.Variable),
+
+            (r'"', String.Double, 'string')
+        ],
+        'comment': [
+            (r'[^/*]+', Comment.Multiline),
+            (r'\/\*', Comment.Multiline, '#push'),
+            (r'\*\/', Comment.Multiline, '#pop'),
+            (r'\*', Comment.Multiline),
+        ],
+        'string': [
+            (r'[^\\"]+', String.Double),
+            include('escape-sequence'),
+            (r'\\\n', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'dotted': [
+            (r'\s+', Text),
+            (r'\.', Punctuation),
+            (r'[A-Z][\w\']*(?=\s*\.)', Name.Function),
+            (r'[A-Z][\w\']*', Name.Function, '#pop'),
+            (r'[a-z_][\w\']*', Name, '#pop'),
+            default('#pop'),
+        ],
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/special.py b/.venv/lib/python3.12/site-packages/pygments/lexers/special.py
new file mode 100644
index 0000000..ea34522
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/special.py
@@ -0,0 +1,122 @@
+"""
+    pygments.lexers.special
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Special lexers.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import ast
+
+from pygments.lexer import Lexer, line_re
+from pygments.token import Token, Error, Text, Generic
+from pygments.util import get_choice_opt
+
+
+__all__ = ['TextLexer', 'OutputLexer', 'RawTokenLexer']
+
+
+class TextLexer(Lexer):
+    """
+    "Null" lexer, doesn't highlight anything.
+    """
+    name = 'Text only'
+    aliases = ['text']
+    filenames = ['*.txt']
+    mimetypes = ['text/plain']
+    url = ""
+    version_added = ''
+
+    priority = 0.01
+
+    def get_tokens_unprocessed(self, text):
+        yield 0, Text, text
+
+    def analyse_text(text):
+        return TextLexer.priority
+
+
+class OutputLexer(Lexer):
+    """
+    Simple lexer that highlights everything as ``Token.Generic.Output``.
+    """
+    name = 'Text output'
+    aliases = ['output']
+    url = ""
+    version_added = '2.10'
+    _example = "output/output"
+
+    def get_tokens_unprocessed(self, text):
+        yield 0, Generic.Output, text
+
+
+_ttype_cache = {}
+
+
+class RawTokenLexer(Lexer):
+    """
+    Recreate a token stream formatted with the `RawTokenFormatter`.
+
+    Additional options accepted:
+
+    `compress`
+        If set to ``"gz"`` or ``"bz2"``, decompress the token stream with
+        the given compression algorithm before lexing (default: ``""``).
+    """
+    name = 'Raw token data'
+    aliases = []
+    filenames = []
+    mimetypes = ['application/x-pygments-tokens']
+    url = 'https://pygments.org/docs/formatters/#RawTokenFormatter'
+    version_added = ''
+
+    def __init__(self, **options):
+        self.compress = get_choice_opt(options, 'compress',
+                                       ['', 'none', 'gz', 'bz2'], '')
+        Lexer.__init__(self, **options)
+
+    def get_tokens(self, text):
+        if self.compress:
+            if isinstance(text, str):
+                text = text.encode('latin1')
+            try:
+                if self.compress == 'gz':
+                    import gzip
+                    text = gzip.decompress(text)
+                elif self.compress == 'bz2':
+                    import bz2
+                    text = bz2.decompress(text)
+            except OSError:
+                yield Error, text.decode('latin1')
+        if isinstance(text, bytes):
+            text = text.decode('latin1')
+
+        # do not call Lexer.get_tokens() because stripping is not optional.
+        text = text.strip('\n') + '\n'
+        for i, t, v in self.get_tokens_unprocessed(text):
+            yield t, v
+
+    def get_tokens_unprocessed(self, text):
+        length = 0
+        for match in line_re.finditer(text):
+            try:
+                ttypestr, val = match.group().rstrip().split('\t', 1)
+                ttype = _ttype_cache.get(ttypestr)
+                if not ttype:
+                    ttype = Token
+                    ttypes = ttypestr.split('.')[1:]
+                    for ttype_ in ttypes:
+                        if not ttype_ or not ttype_[0].isupper():
+                            raise ValueError('malformed token name')
+                        ttype = getattr(ttype, ttype_)
+                    _ttype_cache[ttypestr] = ttype
+                val = ast.literal_eval(val)
+                if not isinstance(val, str):
+                    raise ValueError('expected str')
+            except (SyntaxError, ValueError):
+                val = match.group()
+                ttype = Error
+            yield length, ttype, val
+            length += len(val)
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/spice.py b/.venv/lib/python3.12/site-packages/pygments/lexers/spice.py
new file mode 100644
index 0000000..978f0a7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/spice.py
@@ -0,0 +1,70 @@
+"""
+    pygments.lexers.spice
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for the Spice programming language.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, bygroups, words
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+
+__all__ = ['SpiceLexer']
+
+
+class SpiceLexer(RegexLexer):
+    """
+    For Spice source.
+    """
+    name = 'Spice'
+    url = 'https://www.spicelang.com'
+    filenames = ['*.spice']
+    aliases = ['spice', 'spicelang']
+    mimetypes = ['text/x-spice']
+    version_added = '2.11'
+
+    tokens = {
+        'root': [
+            (r'\n', Whitespace),
+            (r'\s+', Whitespace),
+            (r'\\\n', Text),
+            # comments
+            (r'//(.*?)\n', Comment.Single),
+            (r'/(\\\n)?[*]{2}(.|\n)*?[*](\\\n)?/', String.Doc),
+            (r'/(\\\n)?[*](.|\n)*?[*](\\\n)?/', Comment.Multiline),
+            # keywords
+            (r'(import|as)\b', Keyword.Namespace),
+            (r'(f|p|type|struct|interface|enum|alias|operator)\b', Keyword.Declaration),
+            (words(('if', 'else', 'switch', 'case', 'default', 'for', 'foreach', 'do',
+                    'while', 'break', 'continue', 'fallthrough', 'return', 'assert',
+                    'unsafe', 'ext', 'cast'), suffix=r'\b'), Keyword),
+            (words(('const', 'signed', 'unsigned', 'inline', 'public', 'heap', 'compose'),
+                   suffix=r'\b'), Keyword.Pseudo),
+            (words(('new', 'yield', 'stash', 'pick', 'sync', 'class'), suffix=r'\b'),
+                   Keyword.Reserved),
+            (r'(true|false|nil)\b', Keyword.Constant),
+            (words(('double', 'int', 'short', 'long', 'byte', 'char', 'string',
+                    'bool', 'dyn'), suffix=r'\b'), Keyword.Type),
+            (words(('printf', 'sizeof', 'alignof', 'len', 'panic'), suffix=r'\b(\()'),
+             bygroups(Name.Builtin, Punctuation)),
+            # numeric literals
+            (r'[-]?[0-9]*[.][0-9]+([eE][+-]?[0-9]+)?', Number.Double),
+            (r'0[bB][01]+[slu]?', Number.Bin),
+            (r'0[oO][0-7]+[slu]?', Number.Oct),
+            (r'0[xXhH][0-9a-fA-F]+[slu]?', Number.Hex),
+            (r'(0[dD])?[0-9]+[slu]?', Number.Integer),
+            # string literal
+            (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
+            # char literal
+            (r'\'(\\\\|\\[^\\]|[^\'\\])\'', String.Char),
+            # tokens
+            (r'<<=|>>=|<<|>>|<=|>=|\+=|-=|\*=|/=|\%=|\|=|&=|\^=|&&|\|\||&|\||'
+             r'\+\+|--|\%|\^|\~|==|!=|->|::|[.]{3}|#!|#|[+\-*/&]', Operator),
+            (r'[|<>=!()\[\]{}.,;:\?]', Punctuation),
+            # identifiers
+            (r'[^\W\d]\w*', Name.Other),
+        ]
+    }
diff --git a/.venv/lib/python3.12/site-packages/pygments/lexers/sql.py b/.venv/lib/python3.12/site-packages/pygments/lexers/sql.py
new file mode 100644
index 0000000..1d3458e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pygments/lexers/sql.py
@@ -0,0 +1,1111 @@
+"""
+    pygments.lexers.sql
+    ~~~~~~~~~~~~~~~~~~~
+
+    Lexers for various SQL dialects and related interactive sessions.
+
+    Postgres specific lexers:
+
+    `PostgresLexer`
+        A SQL lexer for the PostgreSQL dialect. Differences w.r.t. the SQL
+        lexer are:
+
+        - keywords and data types list parsed from the PG docs (run the
+          `_postgres_builtins` module to update them);
+        - Content of $-strings parsed using a specific lexer, e.g. the content
+          of a PL/Python function is parsed using the Python lexer;
+        - parse PG specific constructs: E-strings, $-strings, U&-strings,
+          different operators and punctuation.
+
+    `PlPgsqlLexer`
+        A lexer for the PL/pgSQL language. Adds a few specific construct on
+        top of the PG SQL lexer (such as <{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: Union[str, PathLike[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 (Union[str, PathLike[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, "w", 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,
+        font_aspect_ratio: float = 0.61,
+        unique_id: Optional[str] = None,
+    ) -> str:
+        """
+        Generate an SVG from the console contents (requires record=True in Console constructor).
+
+        Args:
+            title (str, optional): 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, optional): 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.
+            font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``
+                string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).
+                If you aren't specifying a different font inside ``code_format``, you probably don't need this.
+            unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node
+                ids). If not set, this defaults to a computed value based on the recorded content.
+        """
+
+        import zlib
+        from html import escape
+
+        from 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 * font_aspect_ratio
+        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()
+
+        if unique_id is None:
+            unique_id = "terminal-" + str(
+                zlib.adler32(
+                    ("".join(repr(segment) 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: Union[str, PathLike[str]],
+        *,
+        title: str = "Rich",
+        theme: Optional[TerminalTheme] = None,
+        clear: bool = True,
+        code_format: str = CONSOLE_SVG_FORMAT,
+        font_aspect_ratio: float = 0.61,
+        unique_id: Optional[str] = None,
+    ) -> None:
+        """Generate an SVG file from the console contents (requires record=True in Console constructor).
+
+        Args:
+            path (Union[str, PathLike[str]]): The path to write the SVG to.
+            title (str, optional): 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, optional): 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.
+            font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``
+                string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).
+                If you aren't specifying a different font inside ``code_format``, you probably don't need this.
+            unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node
+                ids). If not set, this defaults to a computed value based on the recorded content.
+        """
+        svg = self.export_svg(
+            title=title,
+            theme=theme,
+            clear=clear,
+            code_format=code_format,
+            font_aspect_ratio=font_aspect_ratio,
+            unique_id=unique_id,
+        )
+        with open(path, "w", encoding="utf-8") as write_file:
+            write_file.write(svg)
+
+
+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/python3.12/site-packages/rich/constrain.py b/.venv/lib/python3.12/site-packages/rich/constrain.py
new file mode 100644
index 0000000..65fdf56
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/constrain.py
@@ -0,0 +1,37 @@
+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/python3.12/site-packages/rich/containers.py b/.venv/lib/python3.12/site-packages/rich/containers.py
new file mode 100644
index 0000000..901ff8b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/containers.py
@@ -0,0 +1,167 @@
+from itertools import zip_longest
+from typing import (
+    TYPE_CHECKING,
+    Iterable,
+    Iterator,
+    List,
+    Optional,
+    TypeVar,
+    Union,
+    overload,
+)
+
+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 cells available 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/python3.12/site-packages/rich/control.py b/.venv/lib/python3.12/site-packages/rich/control.py
new file mode 100644
index 0000000..248b0f5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/control.py
@@ -0,0 +1,219 @@
+import time
+from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union, Final
+
+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 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/python3.12/site-packages/rich/default_styles.py b/.venv/lib/python3.12/site-packages/rich/default_styles.py
new file mode 100644
index 0000000..f2e4cd1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/default_styles.py
@@ -0,0 +1,196 @@
+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="yellow"),
+    "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),
+    "traceback.error_range": Style(underline=True, bold=True),
+    "traceback.note": Style(color="green", bold=True),
+    "traceback.group.border": Style(color="magenta"),
+    "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.em": Style(italic=True),
+    "markdown.emph": Style(italic=True),  # For commonmark backwards compatibility
+    "markdown.strong": Style(bold=True),
+    "markdown.code": Style(bold=True, color="cyan", bgcolor="black"),
+    "markdown.code_block": Style(color="cyan", bgcolor="black"),
+    "markdown.block_quote": Style(color="magenta"),
+    "markdown.list": Style(color="cyan"),
+    "markdown.item": Style(),
+    "markdown.item.bullet": Style(bold=True),
+    "markdown.item.number": Style(color="cyan"),
+    "markdown.hr": Style(dim=True),
+    "markdown.h1.border": Style(),
+    "markdown.h1": Style(bold=True, underline=True),
+    "markdown.h2": Style(color="magenta", underline=True),
+    "markdown.h3": Style(color="magenta", bold=True),
+    "markdown.h4": Style(color="magenta", italic=True),
+    "markdown.h5": Style(italic=True),
+    "markdown.h6": Style(dim=True),
+    "markdown.h7": Style(italic=True, dim=True),
+    "markdown.link": Style(color="bright_blue"),
+    "markdown.link_url": Style(color="blue", underline=True),
+    "markdown.s": Style(strike=True),
+    "markdown.table.border": Style(color="cyan"),
+    "markdown.table.header": Style(color="cyan", bold=False),
+    "markdown.kbd": Style(bold=True, color="bright_yellow"),
+    "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 rich.console import Console
+    from rich.table import Table
+    from 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/python3.12/site-packages/rich/diagnose.py b/.venv/lib/python3.12/site-packages/rich/diagnose.py
new file mode 100644
index 0000000..9d5ff3e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/diagnose.py
@@ -0,0 +1,39 @@
+import os
+import platform
+
+from rich import inspect
+from rich.console import Console, get_windows_console_features
+from rich.panel import Panel
+from 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 = (
+        "CLICOLOR",
+        "COLORTERM",
+        "COLUMNS",
+        "JPY_PARENT_PID",
+        "JUPYTER_COLUMNS",
+        "JUPYTER_LINES",
+        "LINES",
+        "NO_COLOR",
+        "TERM_PROGRAM",
+        "TERM",
+        "TTY_COMPATIBLE",
+        "TTY_INTERACTIVE",
+        "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/python3.12/site-packages/rich/emoji.py b/.venv/lib/python3.12/site-packages/rich/emoji.py
new file mode 100644
index 0000000..067f93c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/emoji.py
@@ -0,0 +1,93 @@
+import sys
+from typing import TYPE_CHECKING, Literal, Optional, Union
+
+from ._emoji_replace import _emoji_replace
+from .jupyter import JupyterMixin
+from .segment import Segment
+from .style import Style
+
+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.
+        """
+        from ._emoji_codes import EMOJI
+
+        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 rich.columns import Columns
+    from rich.console import Console
+
+    console = Console(record=True)
+
+    from ._emoji_codes import EMOJI
+
+    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/python3.12/site-packages/rich/errors.py b/.venv/lib/python3.12/site-packages/rich/errors.py
new file mode 100644
index 0000000..0bcbe53
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/errors.py
@@ -0,0 +1,34 @@
+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/python3.12/site-packages/rich/file_proxy.py b/.venv/lib/python3.12/site-packages/rich/file_proxy.py
new file mode 100644
index 0000000..e32523b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/file_proxy.py
@@ -0,0 +1,60 @@
+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)
+                buffer.clear()
+            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[:]
+
+    def fileno(self) -> int:
+        return self.__file.fileno()
+
+    def isatty(self) -> bool:
+        return self.__file.isatty()
diff --git a/.venv/lib/python3.12/site-packages/rich/filesize.py b/.venv/lib/python3.12/site-packages/rich/filesize.py
new file mode 100644
index 0000000..83bc911
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/filesize.py
@@ -0,0 +1,88 @@
+"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2
+
+The functions declared in this module should cover the different
+use cases 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 f"{size:,} bytes"
+
+    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/python3.12/site-packages/rich/highlighter.py b/.venv/lib/python3.12/site-packages/rich/highlighter.py
new file mode 100644
index 0000000..df28048
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/highlighter.py
@@ -0,0 +1,232 @@
+import re
+from abc import ABC, abstractmethod
+from typing import ClassVar, Sequence, 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: ClassVar[Sequence[str]] = []
+    base_style: ClassVar[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: ClassVar[Sequence[str]] = [
+        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: ClassVar[str] = "iso8601."
+    highlights: ClassVar[Sequence[str]] = [
+        #
+        # 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{text}'
+        append_fragment(text)
+
+    code = "".join(fragments)
+    html = JUPYTER_HTML_FORMAT.format(code=code)
+
+    return html
+
+
+def display(segments: Iterable[Segment], text: str) -> None:
+    """Render segments to Jupyter."""
+    html = _render_segments(segments)
+    jupyter_renderable = JupyterRenderable(html, text)
+    try:
+        from IPython.display import display as ipython_display
+
+        ipython_display(jupyter_renderable)
+    except ModuleNotFoundError:
+        # Handle the case where the Console has force_jupyter=True,
+        # but IPython is not installed.
+        pass
+
+
+def print(*args: Any, **kwargs: Any) -> None:
+    """Proxy for Console print."""
+    console = get_console()
+    return console.print(*args, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/rich/layout.py b/.venv/lib/python3.12/site-packages/rich/layout.py
new file mode 100644
index 0000000..7fa2852
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/layout.py
@@ -0,0 +1,442 @@
+from abc import ABC, abstractmethod
+from itertools import islice
+from operator import itemgetter
+from threading import RLock
+from typing import (
+    TYPE_CHECKING,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Sequence,
+    Tuple,
+    Union,
+)
+
+from ._ratio import ratio_resolve
+from .align import Align
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .highlighter import ReprHighlighter
+from .panel import Panel
+from .pretty import Pretty
+from .region import Region
+from .repr import Result, rich_repr
+from .segment import Segment
+from .style import StyleType
+
+if TYPE_CHECKING:
+    from rich.tree import Tree
+
+
+class LayoutRender(NamedTuple):
+    """An individual layout render."""
+
+    region: Region
+    render: List[List[Segment]]
+
+
+RegionMap = Dict["Layout", Region]
+RenderMap = Dict["Layout", LayoutRender]
+
+
+class LayoutError(Exception):
+    """Layout related error."""
+
+
+class NoSplitter(LayoutError):
+    """Requested splitter does not exist."""
+
+
+class _Placeholder:
+    """An internal renderable used as a Layout placeholder."""
+
+    highlighter = ReprHighlighter()
+
+    def __init__(self, layout: "Layout", style: StyleType = "") -> None:
+        self.layout = layout
+        self.style = style
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        width = options.max_width
+        height = options.height or options.size.height
+        layout = self.layout
+        title = (
+            f"{layout.name!r} ({width} x {height})"
+            if layout.name
+            else f"({width} x {height})"
+        )
+        yield Panel(
+            Align.center(Pretty(layout), vertical="middle"),
+            style=self.style,
+            title=self.highlighter(title),
+            border_style="blue",
+            height=height,
+        )
+
+
+class Splitter(ABC):
+    """Base class for a splitter."""
+
+    name: str = ""
+
+    @abstractmethod
+    def get_tree_icon(self) -> str:
+        """Get the icon (emoji) used in layout.tree"""
+
+    @abstractmethod
+    def divide(
+        self, children: Sequence["Layout"], region: Region
+    ) -> Iterable[Tuple["Layout", Region]]:
+        """Divide a region amongst several child layouts.
+
+        Args:
+            children (Sequence(Layout)): A number of child layouts.
+            region (Region): A rectangular region to divide.
+        """
+
+
+class RowSplitter(Splitter):
+    """Split a layout region in to rows."""
+
+    name = "row"
+
+    def get_tree_icon(self) -> str:
+        return "[layout.tree.row]⬌"
+
+    def divide(
+        self, children: Sequence["Layout"], region: Region
+    ) -> Iterable[Tuple["Layout", Region]]:
+        x, y, width, height = region
+        render_widths = ratio_resolve(width, children)
+        offset = 0
+        _Region = Region
+        for child, child_width in zip(children, render_widths):
+            yield child, _Region(x + offset, y, child_width, height)
+            offset += child_width
+
+
+class ColumnSplitter(Splitter):
+    """Split a layout region in to columns."""
+
+    name = "column"
+
+    def get_tree_icon(self) -> str:
+        return "[layout.tree.column]⬍"
+
+    def divide(
+        self, children: Sequence["Layout"], region: Region
+    ) -> Iterable[Tuple["Layout", Region]]:
+        x, y, width, height = region
+        render_heights = ratio_resolve(height, children)
+        offset = 0
+        _Region = Region
+        for child, child_height in zip(children, render_heights):
+            yield child, _Region(x, y + offset, width, child_height)
+            offset += child_height
+
+
+@rich_repr
+class Layout:
+    """A renderable to divide a fixed height in to rows or columns.
+
+    Args:
+        renderable (RenderableType, optional): Renderable content, or None for placeholder. Defaults to None.
+        name (str, optional): Optional identifier for Layout. Defaults to None.
+        size (int, optional): Optional fixed size of layout. Defaults to None.
+        minimum_size (int, optional): Minimum size of layout. Defaults to 1.
+        ratio (int, optional): Optional ratio for flexible layout. Defaults to 1.
+        visible (bool, optional): Visibility of layout. Defaults to True.
+    """
+
+    splitters = {"row": RowSplitter, "column": ColumnSplitter}
+
+    def __init__(
+        self,
+        renderable: Optional[RenderableType] = None,
+        *,
+        name: Optional[str] = None,
+        size: Optional[int] = None,
+        minimum_size: int = 1,
+        ratio: int = 1,
+        visible: bool = True,
+    ) -> None:
+        self._renderable = renderable or _Placeholder(self)
+        self.size = size
+        self.minimum_size = minimum_size
+        self.ratio = ratio
+        self.name = name
+        self.visible = visible
+        self.splitter: Splitter = self.splitters["column"]()
+        self._children: List[Layout] = []
+        self._render_map: RenderMap = {}
+        self._lock = RLock()
+
+    def __rich_repr__(self) -> Result:
+        yield "name", self.name, None
+        yield "size", self.size, None
+        yield "minimum_size", self.minimum_size, 1
+        yield "ratio", self.ratio, 1
+
+    @property
+    def renderable(self) -> RenderableType:
+        """Layout renderable."""
+        return self if self._children else self._renderable
+
+    @property
+    def children(self) -> List["Layout"]:
+        """Gets (visible) layout children."""
+        return [child for child in self._children if child.visible]
+
+    @property
+    def map(self) -> RenderMap:
+        """Get a map of the last render."""
+        return self._render_map
+
+    def get(self, name: str) -> Optional["Layout"]:
+        """Get a named layout, or None if it doesn't exist.
+
+        Args:
+            name (str): Name of layout.
+
+        Returns:
+            Optional[Layout]: Layout instance or None if no layout was found.
+        """
+        if self.name == name:
+            return self
+        else:
+            for child in self._children:
+                named_layout = child.get(name)
+                if named_layout is not None:
+                    return named_layout
+        return None
+
+    def __getitem__(self, name: str) -> "Layout":
+        layout = self.get(name)
+        if layout is None:
+            raise KeyError(f"No layout with name {name!r}")
+        return layout
+
+    @property
+    def tree(self) -> "Tree":
+        """Get a tree renderable to show layout structure."""
+        from rich.styled import Styled
+        from rich.table import Table
+        from rich.tree import Tree
+
+        def summary(layout: "Layout") -> Table:
+            icon = layout.splitter.get_tree_icon()
+
+            table = Table.grid(padding=(0, 1, 0, 0))
+
+            text: RenderableType = (
+                Pretty(layout) if layout.visible else Styled(Pretty(layout), "dim")
+            )
+            table.add_row(icon, text)
+            _summary = table
+            return _summary
+
+        layout = self
+        tree = Tree(
+            summary(layout),
+            guide_style=f"layout.tree.{layout.splitter.name}",
+            highlight=True,
+        )
+
+        def recurse(tree: "Tree", layout: "Layout") -> None:
+            for child in layout._children:
+                recurse(
+                    tree.add(
+                        summary(child),
+                        guide_style=f"layout.tree.{child.splitter.name}",
+                    ),
+                    child,
+                )
+
+        recurse(tree, self)
+        return tree
+
+    def split(
+        self,
+        *layouts: Union["Layout", RenderableType],
+        splitter: Union[Splitter, str] = "column",
+    ) -> None:
+        """Split the layout in to multiple sub-layouts.
+
+        Args:
+            *layouts (Layout): Positional arguments should be (sub) Layout instances.
+            splitter (Union[Splitter, str]): Splitter instance or name of splitter.
+        """
+        _layouts = [
+            layout if isinstance(layout, Layout) else Layout(layout)
+            for layout in layouts
+        ]
+        try:
+            self.splitter = (
+                splitter
+                if isinstance(splitter, Splitter)
+                else self.splitters[splitter]()
+            )
+        except KeyError:
+            raise NoSplitter(f"No splitter called {splitter!r}")
+        self._children[:] = _layouts
+
+    def add_split(self, *layouts: Union["Layout", RenderableType]) -> None:
+        """Add a new layout(s) to existing split.
+
+        Args:
+            *layouts (Union[Layout, RenderableType]): Positional arguments should be renderables or (sub) Layout instances.
+
+        """
+        _layouts = (
+            layout if isinstance(layout, Layout) else Layout(layout)
+            for layout in layouts
+        )
+        self._children.extend(_layouts)
+
+    def split_row(self, *layouts: Union["Layout", RenderableType]) -> None:
+        """Split the layout in to a row (layouts side by side).
+
+        Args:
+            *layouts (Layout): Positional arguments should be (sub) Layout instances.
+        """
+        self.split(*layouts, splitter="row")
+
+    def split_column(self, *layouts: Union["Layout", RenderableType]) -> None:
+        """Split the layout in to a column (layouts stacked on top of each other).
+
+        Args:
+            *layouts (Layout): Positional arguments should be (sub) Layout instances.
+        """
+        self.split(*layouts, splitter="column")
+
+    def unsplit(self) -> None:
+        """Reset splits to initial state."""
+        del self._children[:]
+
+    def update(self, renderable: RenderableType) -> None:
+        """Update renderable.
+
+        Args:
+            renderable (RenderableType): New renderable object.
+        """
+        with self._lock:
+            self._renderable = renderable
+
+    def refresh_screen(self, console: "Console", layout_name: str) -> None:
+        """Refresh a sub-layout.
+
+        Args:
+            console (Console): Console instance where Layout is to be rendered.
+            layout_name (str): Name of layout.
+        """
+        with self._lock:
+            layout = self[layout_name]
+            region, _lines = self._render_map[layout]
+            (x, y, width, height) = region
+            lines = console.render_lines(
+                layout, console.options.update_dimensions(width, height)
+            )
+            self._render_map[layout] = LayoutRender(region, lines)
+            console.update_screen_lines(lines, x, y)
+
+    def _make_region_map(self, width: int, height: int) -> RegionMap:
+        """Create a dict that maps layout on to Region."""
+        stack: List[Tuple[Layout, Region]] = [(self, Region(0, 0, width, height))]
+        push = stack.append
+        pop = stack.pop
+        layout_regions: List[Tuple[Layout, Region]] = []
+        append_layout_region = layout_regions.append
+        while stack:
+            append_layout_region(pop())
+            layout, region = layout_regions[-1]
+            children = layout.children
+            if children:
+                for child_and_region in layout.splitter.divide(children, region):
+                    push(child_and_region)
+
+        region_map = {
+            layout: region
+            for layout, region in sorted(layout_regions, key=itemgetter(1))
+        }
+        return region_map
+
+    def render(self, console: Console, options: ConsoleOptions) -> RenderMap:
+        """Render the sub_layouts.
+
+        Args:
+            console (Console): Console instance.
+            options (ConsoleOptions): Console options.
+
+        Returns:
+            RenderMap: A dict that maps Layout on to a tuple of Region, lines
+        """
+        render_width = options.max_width
+        render_height = options.height or console.height
+        region_map = self._make_region_map(render_width, render_height)
+        layout_regions = [
+            (layout, region)
+            for layout, region in region_map.items()
+            if not layout.children
+        ]
+        render_map: Dict["Layout", "LayoutRender"] = {}
+        render_lines = console.render_lines
+        update_dimensions = options.update_dimensions
+
+        for layout, region in layout_regions:
+            lines = render_lines(
+                layout.renderable, update_dimensions(region.width, region.height)
+            )
+            render_map[layout] = LayoutRender(region, lines)
+        return render_map
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        with self._lock:
+            width = options.max_width or console.width
+            height = options.height or console.height
+            render_map = self.render(console, options.update_dimensions(width, height))
+            self._render_map = render_map
+            layout_lines: List[List[Segment]] = [[] for _ in range(height)]
+            _islice = islice
+            for region, lines in render_map.values():
+                _x, y, _layout_width, layout_height = region
+                for row, line in zip(
+                    _islice(layout_lines, y, y + layout_height), lines
+                ):
+                    row.extend(line)
+
+            new_line = Segment.line()
+            for layout_row in layout_lines:
+                yield from layout_row
+                yield new_line
+
+
+if __name__ == "__main__":
+    from rich.console import Console
+
+    console = Console()
+    layout = Layout()
+
+    layout.split_column(
+        Layout(name="header", size=3),
+        Layout(ratio=1, name="main"),
+        Layout(size=10, name="footer"),
+    )
+
+    layout["main"].split_row(Layout(name="side"), Layout(name="body", ratio=2))
+
+    layout["body"].split_row(Layout(name="content", ratio=2), Layout(name="s2"))
+
+    layout["s2"].split_column(
+        Layout(name="top"), Layout(name="middle"), Layout(name="bottom")
+    )
+
+    layout["side"].split_column(Layout(layout.tree, name="left1"), Layout(name="left2"))
+
+    layout["content"].update("foo")
+
+    console.print(layout)
diff --git a/.venv/lib/python3.12/site-packages/rich/live.py b/.venv/lib/python3.12/site-packages/rich/live.py
new file mode 100644
index 0000000..2fd893b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/live.py
@@ -0,0 +1,404 @@
+from __future__ import annotations
+
+import sys
+from threading import Event, RLock, Thread
+from types import TracebackType
+from typing import IO, TYPE_CHECKING, Any, Callable, List, Optional, TextIO, Type, cast
+
+from . import get_console
+from .console import Console, ConsoleRenderable, Group, RenderableType, RenderHook
+from .control import Control
+from .file_proxy import FileProxy
+from .jupyter import JupyterMixin
+from .live_render import LiveRender, VerticalOverflowMethod
+from .screen import Screen
+from .text import Text
+
+if TYPE_CHECKING:
+    # Can be replaced with `from typing import Self` in Python 3.11+
+    from typing_extensions import Self  # pragma: no cover
+
+
+class _RefreshThread(Thread):
+    """A thread that calls refresh() at regular intervals."""
+
+    def __init__(self, live: "Live", refresh_per_second: float) -> None:
+        self.live = live
+        self.refresh_per_second = refresh_per_second
+        self.done = Event()
+        super().__init__(daemon=True)
+
+    def stop(self) -> None:
+        self.done.set()
+
+    def run(self) -> None:
+        while not self.done.wait(1 / self.refresh_per_second):
+            with self.live._lock:
+                if not self.done.is_set():
+                    self.live.refresh()
+
+
+class Live(JupyterMixin, RenderHook):
+    """Renders an auto-updating live display of any given renderable.
+
+    Args:
+        renderable (RenderableType, optional): The renderable to live display. Defaults to displaying nothing.
+        console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
+        screen (bool, optional): Enable alternate screen mode. Defaults to False.
+        auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()` or `update()` with refresh flag. Defaults to True
+        refresh_per_second (float, optional): Number of times per second to refresh the live display. Defaults to 4.
+        transient (bool, optional): Clear the renderable on exit (has no effect when screen=True). Defaults to False.
+        redirect_stdout (bool, optional): Enable redirection of stdout, so ``print`` may be used. Defaults to True.
+        redirect_stderr (bool, optional): Enable redirection of stderr. Defaults to True.
+        vertical_overflow (VerticalOverflowMethod, optional): How to handle renderable when it is too tall for the console. Defaults to "ellipsis".
+        get_renderable (Callable[[], RenderableType], optional): Optional callable to get renderable. Defaults to None.
+    """
+
+    def __init__(
+        self,
+        renderable: Optional[RenderableType] = None,
+        *,
+        console: Optional[Console] = None,
+        screen: bool = False,
+        auto_refresh: bool = True,
+        refresh_per_second: float = 4,
+        transient: bool = False,
+        redirect_stdout: bool = True,
+        redirect_stderr: bool = True,
+        vertical_overflow: VerticalOverflowMethod = "ellipsis",
+        get_renderable: Optional[Callable[[], RenderableType]] = None,
+    ) -> None:
+        assert refresh_per_second > 0, "refresh_per_second must be > 0"
+        self._renderable = renderable
+        self.console = console if console is not None else get_console()
+        self._screen = screen
+        self._alt_screen = False
+
+        self._redirect_stdout = redirect_stdout
+        self._redirect_stderr = redirect_stderr
+        self._restore_stdout: Optional[IO[str]] = None
+        self._restore_stderr: Optional[IO[str]] = None
+
+        self._lock = RLock()
+        self.ipy_widget: Optional[Any] = None
+        self.auto_refresh = auto_refresh
+        self._started: bool = False
+        self.transient = True if screen else transient
+
+        self._refresh_thread: Optional[_RefreshThread] = None
+        self.refresh_per_second = refresh_per_second
+
+        self.vertical_overflow = vertical_overflow
+        self._get_renderable = get_renderable
+        self._live_render = LiveRender(
+            self.get_renderable(), vertical_overflow=vertical_overflow
+        )
+        self._nested = False
+
+    @property
+    def is_started(self) -> bool:
+        """Check if live display has been started."""
+        return self._started
+
+    def get_renderable(self) -> RenderableType:
+        renderable = (
+            self._get_renderable()
+            if self._get_renderable is not None
+            else self._renderable
+        )
+        return renderable or ""
+
+    def start(self, refresh: bool = False) -> None:
+        """Start live rendering display.
+
+        Args:
+            refresh (bool, optional): Also refresh. Defaults to False.
+        """
+        with self._lock:
+            if self._started:
+                return
+            self._started = True
+
+            if not self.console.set_live(self):
+                self._nested = True
+                return
+
+            if self._screen:
+                self._alt_screen = self.console.set_alt_screen(True)
+            self.console.show_cursor(False)
+            self._enable_redirect_io()
+            self.console.push_render_hook(self)
+            if refresh:
+                try:
+                    self.refresh()
+                except Exception:
+                    # If refresh fails, we want to stop the redirection of sys.stderr,
+                    # so the error stacktrace is properly displayed in the terminal.
+                    # (or, if the code that calls Rich captures the exception and wants to display something,
+                    # let this be displayed in the terminal).
+                    self.stop()
+                    raise
+            if self.auto_refresh:
+                self._refresh_thread = _RefreshThread(self, self.refresh_per_second)
+                self._refresh_thread.start()
+
+    def stop(self) -> None:
+        """Stop live rendering display."""
+        with self._lock:
+            if not self._started:
+                return
+            self._started = False
+            self.console.clear_live()
+            if self._nested:
+                if not self.transient:
+                    self.console.print(self.renderable)
+                return
+
+            if self.auto_refresh and self._refresh_thread is not None:
+                self._refresh_thread.stop()
+                self._refresh_thread = None
+            # allow it to fully render on the last even if overflow
+            self.vertical_overflow = "visible"
+            with self.console:
+                try:
+                    if not self._alt_screen and not self.console.is_jupyter:
+                        self.refresh()
+                finally:
+                    self._disable_redirect_io()
+                    self.console.pop_render_hook()
+                    if (
+                        not self._alt_screen
+                        and self.console.is_terminal
+                        and self._live_render.last_render_height
+                    ):
+                        self.console.line()
+                    self.console.show_cursor(True)
+                    if self._alt_screen:
+                        self.console.set_alt_screen(False)
+                    if self.transient and not self._alt_screen:
+                        self.console.control(self._live_render.restore_cursor())
+                    if self.ipy_widget is not None and self.transient:
+                        self.ipy_widget.close()  # pragma: no cover
+
+    def __enter__(self) -> Self:
+        self.start(refresh=self._renderable is not None)
+        return self
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.stop()
+
+    def _enable_redirect_io(self) -> None:
+        """Enable redirecting of stdout / stderr."""
+        if self.console.is_terminal or self.console.is_jupyter:
+            if self._redirect_stdout and not isinstance(sys.stdout, FileProxy):
+                self._restore_stdout = sys.stdout
+                sys.stdout = cast("TextIO", FileProxy(self.console, sys.stdout))
+            if self._redirect_stderr and not isinstance(sys.stderr, FileProxy):
+                self._restore_stderr = sys.stderr
+                sys.stderr = cast("TextIO", FileProxy(self.console, sys.stderr))
+
+    def _disable_redirect_io(self) -> None:
+        """Disable redirecting of stdout / stderr."""
+        if self._restore_stdout:
+            sys.stdout = cast("TextIO", self._restore_stdout)
+            self._restore_stdout = None
+        if self._restore_stderr:
+            sys.stderr = cast("TextIO", self._restore_stderr)
+            self._restore_stderr = None
+
+    @property
+    def renderable(self) -> RenderableType:
+        """Get the renderable that is being displayed
+
+        Returns:
+            RenderableType: Displayed renderable.
+        """
+        live_stack = self.console._live_stack
+        renderable: RenderableType
+        if live_stack and self is live_stack[0]:
+            # The first Live instance will render everything in the Live stack
+            renderable = Group(*[live.get_renderable() for live in live_stack])
+        else:
+            renderable = self.get_renderable()
+        return Screen(renderable) if self._alt_screen else renderable
+
+    def update(self, renderable: RenderableType, *, refresh: bool = False) -> None:
+        """Update the renderable that is being displayed
+
+        Args:
+            renderable (RenderableType): New renderable to use.
+            refresh (bool, optional): Refresh the display. Defaults to False.
+        """
+        if isinstance(renderable, str):
+            renderable = self.console.render_str(renderable)
+        with self._lock:
+            self._renderable = renderable
+            if refresh:
+                self.refresh()
+
+    def refresh(self) -> None:
+        """Update the display of the Live Render."""
+        with self._lock:
+            self._live_render.set_renderable(self.renderable)
+            if self._nested:
+                if self.console._live_stack:
+                    self.console._live_stack[0].refresh()
+                return
+
+            if self.console.is_jupyter:  # pragma: no cover
+                try:
+                    from IPython.display import display
+                    from ipywidgets import Output
+                except ImportError:
+                    import warnings
+
+                    warnings.warn('install "ipywidgets" for Jupyter support')
+                else:
+                    if self.ipy_widget is None:
+                        self.ipy_widget = Output()
+                        display(self.ipy_widget)
+
+                    with self.ipy_widget:
+                        self.ipy_widget.clear_output(wait=True)
+                        self.console.print(self._live_render.renderable)
+            elif self.console.is_terminal and not self.console.is_dumb_terminal:
+                with self.console:
+                    self.console.print(Control())
+            elif (
+                not self._started and not self.transient
+            ):  # if it is finished allow files or dumb-terminals to see final result
+                with self.console:
+                    self.console.print(Control())
+
+    def process_renderables(
+        self, renderables: List[ConsoleRenderable]
+    ) -> List[ConsoleRenderable]:
+        """Process renderables to restore cursor and display progress."""
+        self._live_render.vertical_overflow = self.vertical_overflow
+        if self.console.is_interactive:
+            # lock needs acquiring as user can modify live_render renderable at any time unlike in Progress.
+            with self._lock:
+                reset = (
+                    Control.home()
+                    if self._alt_screen
+                    else self._live_render.position_cursor()
+                )
+                renderables = [reset, *renderables, self._live_render]
+        elif (
+            not self._started and not self.transient
+        ):  # if it is finished render the final output for files or dumb_terminals
+            renderables = [*renderables, self._live_render]
+
+        return renderables
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import random
+    import time
+    from itertools import cycle
+    from typing import Dict, List, Tuple
+
+    from .align import Align
+    from .console import Console
+    from .live import Live as Live
+    from .panel import Panel
+    from .rule import Rule
+    from .syntax import Syntax
+    from .table import Table
+
+    console = Console()
+
+    syntax = Syntax(
+        '''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''',
+        "python",
+        line_numbers=True,
+    )
+
+    table = Table("foo", "bar", "baz")
+    table.add_row("1", "2", "3")
+
+    progress_renderables = [
+        "You can make the terminal shorter and taller to see the live table hide"
+        "Text may be printed while the progress bars are rendering.",
+        Panel("In fact, [i]any[/i] renderable will work"),
+        "Such as [magenta]tables[/]...",
+        table,
+        "Pretty printed structures...",
+        {"type": "example", "text": "Pretty printed"},
+        "Syntax...",
+        syntax,
+        Rule("Give it a try!"),
+    ]
+
+    examples = cycle(progress_renderables)
+
+    exchanges = [
+        "SGD",
+        "MYR",
+        "EUR",
+        "USD",
+        "AUD",
+        "JPY",
+        "CNH",
+        "HKD",
+        "CAD",
+        "INR",
+        "DKK",
+        "GBP",
+        "RUB",
+        "NZD",
+        "MXN",
+        "IDR",
+        "TWD",
+        "THB",
+        "VND",
+    ]
+    with Live(console=console) as live_table:
+        exchange_rate_dict: Dict[Tuple[str, str], float] = {}
+
+        for index in range(100):
+            select_exchange = exchanges[index % len(exchanges)]
+
+            for exchange in exchanges:
+                if exchange == select_exchange:
+                    continue
+                time.sleep(0.4)
+                if random.randint(0, 10) < 1:
+                    console.log(next(examples))
+                exchange_rate_dict[(select_exchange, exchange)] = 200 / (
+                    (random.random() * 320) + 1
+                )
+                if len(exchange_rate_dict) > len(exchanges) - 1:
+                    exchange_rate_dict.pop(list(exchange_rate_dict.keys())[0])
+                table = Table(title="Exchange Rates")
+
+                table.add_column("Source Currency")
+                table.add_column("Destination Currency")
+                table.add_column("Exchange Rate")
+
+                for (source, dest), exchange_rate in exchange_rate_dict.items():
+                    table.add_row(
+                        source,
+                        dest,
+                        Text(
+                            f"{exchange_rate:.4f}",
+                            style="red" if exchange_rate < 1.0 else "green",
+                        ),
+                    )
+
+                live_table.update(Align.center(table))
diff --git a/.venv/lib/python3.12/site-packages/rich/live_render.py b/.venv/lib/python3.12/site-packages/rich/live_render.py
new file mode 100644
index 0000000..e7ec970
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/live_render.py
@@ -0,0 +1,116 @@
+from typing import Literal, Optional, Tuple
+
+from ._loop import loop_last
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .control import Control
+from .segment import ControlType, Segment
+from .style import StyleType
+from .text import Text
+
+VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]
+
+
+class LiveRender:
+    """Creates a renderable that may be updated.
+
+    Args:
+        renderable (RenderableType): Any renderable object.
+        style (StyleType, optional): An optional style to apply to the renderable. Defaults to "".
+    """
+
+    def __init__(
+        self,
+        renderable: RenderableType,
+        style: StyleType = "",
+        vertical_overflow: VerticalOverflowMethod = "ellipsis",
+    ) -> None:
+        self.renderable = renderable
+        self.style = style
+        self.vertical_overflow = vertical_overflow
+        self._shape: Optional[Tuple[int, int]] = None
+
+    @property
+    def last_render_height(self) -> int:
+        """The number of lines in the last render (may be 0 if nothing was rendered).
+
+        Returns:
+            Height in lines
+        """
+        if self._shape is None:
+            return 0
+        return self._shape[1]
+
+    def set_renderable(self, renderable: RenderableType) -> None:
+        """Set a new renderable.
+
+        Args:
+            renderable (RenderableType): Any renderable object, including str.
+        """
+        self.renderable = renderable
+
+    def position_cursor(self) -> Control:
+        """Get control codes to move cursor to beginning of live render.
+
+        Returns:
+            Control: A control instance that may be printed.
+        """
+        if self._shape is not None:
+            _, height = self._shape
+            return Control(
+                ControlType.CARRIAGE_RETURN,
+                (ControlType.ERASE_IN_LINE, 2),
+                *(
+                    (
+                        (ControlType.CURSOR_UP, 1),
+                        (ControlType.ERASE_IN_LINE, 2),
+                    )
+                    * (height - 1)
+                )
+            )
+        return Control()
+
+    def restore_cursor(self) -> Control:
+        """Get control codes to clear the render and restore the cursor to its previous position.
+
+        Returns:
+            Control: A Control instance that may be printed.
+        """
+        if self._shape is not None:
+            _, height = self._shape
+            return Control(
+                ControlType.CARRIAGE_RETURN,
+                *((ControlType.CURSOR_UP, 1), (ControlType.ERASE_IN_LINE, 2)) * height
+            )
+        return Control()
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        renderable = self.renderable
+        style = console.get_style(self.style)
+        lines = console.render_lines(renderable, options, style=style, pad=False)
+        shape = Segment.get_shape(lines)
+
+        _, height = shape
+        if height > options.size.height:
+            if self.vertical_overflow == "crop":
+                lines = lines[: options.size.height]
+                shape = Segment.get_shape(lines)
+            elif self.vertical_overflow == "ellipsis":
+                lines = lines[: (options.size.height - 1)]
+                overflow_text = Text(
+                    "...",
+                    overflow="crop",
+                    justify="center",
+                    end="",
+                    style="live.ellipsis",
+                )
+                lines.append(list(console.render(overflow_text)))
+                shape = Segment.get_shape(lines)
+        self._shape = shape
+
+        new_line = Segment.line()
+        for last, line in loop_last(lines):
+            yield from line
+            if not last:
+                yield new_line
diff --git a/.venv/lib/python3.12/site-packages/rich/logging.py b/.venv/lib/python3.12/site-packages/rich/logging.py
new file mode 100644
index 0000000..e572f07
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/logging.py
@@ -0,0 +1,305 @@
+from __future__ import annotations
+
+import logging
+import os
+from datetime import datetime
+from logging import Handler, LogRecord
+from types import ModuleType
+from typing import TYPE_CHECKING, ClassVar, Iterable, List, Optional, Type, Union
+
+if TYPE_CHECKING:
+    from ._log_render import FormatTimeCallable
+    from .console import Console, ConsoleRenderable
+    from .highlighter import Highlighter
+    from .traceback import Traceback
+
+from rich._null_file import NullFile
+
+from . import get_console
+from ._log_render import LogRender
+from .highlighter import ReprHighlighter
+from .text import Text
+
+
+class RichHandler(Handler):
+    """A logging handler that renders output with Rich. The time / level / message and file are displayed in columns.
+    The level is color coded, and the message is syntax highlighted.
+
+    Note:
+        Be careful when enabling console markup in log messages if you have configured logging for libraries not
+        under your control. If a dependency writes messages containing square brackets, it may not produce the intended output.
+
+    Args:
+        level (Union[int, str], optional): Log level. Defaults to logging.NOTSET.
+        console (:class:`~rich.console.Console`, optional): Optional console instance to write logs.
+            Default will use a global console instance writing to stdout.
+        show_time (bool, optional): Show a column for the time. Defaults to True.
+        omit_repeated_times (bool, optional): Omit repetition of the same time. Defaults to True.
+        show_level (bool, optional): Show a column for the level. Defaults to True.
+        show_path (bool, optional): Show the path to the original log call. Defaults to True.
+        enable_link_path (bool, optional): Enable terminal link of path column to file. Defaults to True.
+        highlighter (Highlighter, optional): Highlighter to style log messages, or None to use ReprHighlighter. Defaults to None.
+        markup (bool, optional): Enable console markup in log messages. Defaults to False.
+        rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False.
+        tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None.
+        tracebacks_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88.
+        tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None.
+        tracebacks_theme (str, optional): Override pygments theme used in traceback.
+        tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True.
+        tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. Defaults to False.
+        tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
+        tracebacks_max_frames (int, optional): Optional maximum number of frames returned by traceback.
+        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to 10.
+        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+        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 %X] ".
+        keywords (List[str], optional): List of words to highlight instead of ``RichHandler.KEYWORDS``.
+    """
+
+    KEYWORDS: ClassVar[Optional[List[str]]] = [
+        "GET",
+        "POST",
+        "HEAD",
+        "PUT",
+        "DELETE",
+        "OPTIONS",
+        "TRACE",
+        "PATCH",
+    ]
+    HIGHLIGHTER_CLASS: ClassVar[Type[Highlighter]] = ReprHighlighter
+
+    def __init__(
+        self,
+        level: Union[int, str] = logging.NOTSET,
+        console: Optional[Console] = None,
+        *,
+        show_time: bool = True,
+        omit_repeated_times: bool = True,
+        show_level: bool = True,
+        show_path: bool = True,
+        enable_link_path: bool = True,
+        highlighter: Optional[Highlighter] = None,
+        markup: bool = False,
+        rich_tracebacks: bool = False,
+        tracebacks_width: Optional[int] = None,
+        tracebacks_code_width: Optional[int] = 88,
+        tracebacks_extra_lines: int = 3,
+        tracebacks_theme: Optional[str] = None,
+        tracebacks_word_wrap: bool = True,
+        tracebacks_show_locals: bool = False,
+        tracebacks_suppress: Iterable[Union[str, ModuleType]] = (),
+        tracebacks_max_frames: int = 100,
+        locals_max_length: int = 10,
+        locals_max_string: int = 80,
+        log_time_format: Union[str, FormatTimeCallable] = "[%x %X]",
+        keywords: Optional[List[str]] = None,
+    ) -> None:
+        super().__init__(level=level)
+        self.console = console or get_console()
+        self.highlighter = highlighter or self.HIGHLIGHTER_CLASS()
+        self._log_render = LogRender(
+            show_time=show_time,
+            show_level=show_level,
+            show_path=show_path,
+            time_format=log_time_format,
+            omit_repeated_times=omit_repeated_times,
+            level_width=None,
+        )
+        self.enable_link_path = enable_link_path
+        self.markup = markup
+        self.rich_tracebacks = rich_tracebacks
+        self.tracebacks_width = tracebacks_width
+        self.tracebacks_extra_lines = tracebacks_extra_lines
+        self.tracebacks_theme = tracebacks_theme
+        self.tracebacks_word_wrap = tracebacks_word_wrap
+        self.tracebacks_show_locals = tracebacks_show_locals
+        self.tracebacks_suppress = tracebacks_suppress
+        self.tracebacks_max_frames = tracebacks_max_frames
+        self.tracebacks_code_width = tracebacks_code_width
+        self.locals_max_length = locals_max_length
+        self.locals_max_string = locals_max_string
+        self.keywords = keywords
+
+    def get_level_text(self, record: LogRecord) -> Text:
+        """Get the level name from the record.
+
+        Args:
+            record (LogRecord): LogRecord instance.
+
+        Returns:
+            Text: A tuple of the style and level name.
+        """
+        level_name = record.levelname
+        level_text = Text.styled(
+            level_name.ljust(8), f"logging.level.{level_name.lower()}"
+        )
+        return level_text
+
+    def emit(self, record: LogRecord) -> None:
+        """Invoked by logging."""
+        message = self.format(record)
+        traceback = None
+        if (
+            self.rich_tracebacks
+            and record.exc_info
+            and record.exc_info != (None, None, None)
+        ):
+            exc_type, exc_value, exc_traceback = record.exc_info
+            assert exc_type is not None
+            assert exc_value is not None
+            from .traceback import Traceback
+
+            traceback = Traceback.from_exception(
+                exc_type,
+                exc_value,
+                exc_traceback,
+                width=self.tracebacks_width,
+                code_width=self.tracebacks_code_width,
+                extra_lines=self.tracebacks_extra_lines,
+                theme=self.tracebacks_theme,
+                word_wrap=self.tracebacks_word_wrap,
+                show_locals=self.tracebacks_show_locals,
+                locals_max_length=self.locals_max_length,
+                locals_max_string=self.locals_max_string,
+                suppress=self.tracebacks_suppress,
+                max_frames=self.tracebacks_max_frames,
+            )
+            message = record.getMessage()
+            if self.formatter:
+                record.message = record.getMessage()
+                formatter = self.formatter
+                if hasattr(formatter, "usesTime") and formatter.usesTime():
+                    record.asctime = formatter.formatTime(record, formatter.datefmt)
+                message = formatter.formatMessage(record)
+
+        message_renderable = self.render_message(record, message)
+        log_renderable = self.render(
+            record=record, traceback=traceback, message_renderable=message_renderable
+        )
+        if isinstance(self.console.file, NullFile):
+            # Handles pythonw, where stdout/stderr are null, and we return NullFile
+            # instance from Console.file. In this case, we still want to make a log record
+            # even though we won't be writing anything to a file.
+            self.handleError(record)
+        else:
+            try:
+                self.console.print(log_renderable)
+            except Exception:
+                self.handleError(record)
+
+    def render_message(self, record: LogRecord, message: str) -> ConsoleRenderable:
+        """Render message text in to Text.
+
+        Args:
+            record (LogRecord): logging Record.
+            message (str): String containing log message.
+
+        Returns:
+            ConsoleRenderable: Renderable to display log message.
+        """
+        use_markup = getattr(record, "markup", self.markup)
+        message_text = Text.from_markup(message) if use_markup else Text(message)
+
+        highlighter = getattr(record, "highlighter", self.highlighter)
+        if highlighter:
+            message_text = highlighter(message_text)
+
+        if self.keywords is None:
+            self.keywords = self.KEYWORDS
+
+        if self.keywords:
+            message_text.highlight_words(self.keywords, "logging.keyword")
+
+        return message_text
+
+    def render(
+        self,
+        *,
+        record: LogRecord,
+        traceback: Optional[Traceback],
+        message_renderable: ConsoleRenderable,
+    ) -> ConsoleRenderable:
+        """Render log for display.
+
+        Args:
+            record (LogRecord): logging Record.
+            traceback (Optional[Traceback]): Traceback instance or None for no Traceback.
+            message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents.
+
+        Returns:
+            ConsoleRenderable: Renderable to display log.
+        """
+        path = os.path.basename(record.pathname)
+        level = self.get_level_text(record)
+        time_format = None if self.formatter is None else self.formatter.datefmt
+        log_time = datetime.fromtimestamp(record.created)
+
+        log_renderable = self._log_render(
+            self.console,
+            [message_renderable] if not traceback else [message_renderable, traceback],
+            log_time=log_time,
+            time_format=time_format,
+            level=level,
+            path=path,
+            line_no=record.lineno,
+            link_path=record.pathname if self.enable_link_path else None,
+        )
+        return log_renderable
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from time import sleep
+
+    FORMAT = "%(message)s"
+    # FORMAT = "%(asctime)-15s - %(levelname)s - %(message)s"
+    logging.basicConfig(
+        level="NOTSET",
+        format=FORMAT,
+        datefmt="[%X]",
+        handlers=[RichHandler(rich_tracebacks=True, tracebacks_show_locals=True)],
+    )
+    log = logging.getLogger("rich")
+
+    log.info("Server starting...")
+    log.info("Listening on http://127.0.0.1:8080")
+    sleep(1)
+
+    log.info("GET /index.html 200 1298")
+    log.info("GET /imgs/backgrounds/back1.jpg 200 54386")
+    log.info("GET /css/styles.css 200 54386")
+    log.warning("GET /favicon.ico 404 242")
+    sleep(1)
+
+    log.debug(
+        "JSONRPC request\n--> %r\n<-- %r",
+        {
+            "version": "1.1",
+            "method": "confirmFruitPurchase",
+            "params": [["apple", "orange", "mangoes", "pomelo"], 1.123],
+            "id": "194521489",
+        },
+        {"version": "1.1", "result": True, "error": None, "id": "194521489"},
+    )
+    log.debug(
+        "Loading configuration file /adasd/asdasd/qeqwe/qwrqwrqwr/sdgsdgsdg/werwerwer/dfgerert/ertertert/ertetert/werwerwer"
+    )
+    log.error("Unable to find 'pomelo' in database!")
+    log.info("POST /jsonrpc/ 200 65532")
+    log.info("POST /admin/ 401 42234")
+    log.warning("password was rejected for admin site.")
+
+    def divide() -> None:
+        number = 1
+        divisor = 0
+        foos = ["foo"] * 100
+        log.debug("in divide")
+        try:
+            number / divisor
+        except:
+            log.exception("An error of some kind occurred!")
+
+    divide()
+    sleep(1)
+    log.critical("Out of memory!")
+    log.info("Server exited with code=-1")
+    log.info("[bold]EXITING...[/bold]", extra=dict(markup=True))
diff --git a/.venv/lib/python3.12/site-packages/rich/markdown.py b/.venv/lib/python3.12/site-packages/rich/markdown.py
new file mode 100644
index 0000000..abbbb07
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/markdown.py
@@ -0,0 +1,802 @@
+from __future__ import annotations
+
+import sys
+from dataclasses import dataclass
+from typing import ClassVar, Iterable, get_args
+
+from markdown_it import MarkdownIt
+from markdown_it.token import Token
+
+from rich.table import Table
+
+from . import box
+from ._loop import loop_first
+from ._stack import Stack
+from .console import Console, ConsoleOptions, JustifyMethod, RenderResult
+from .containers import Renderables
+from .jupyter import JupyterMixin
+from .rule import Rule
+from .segment import Segment
+from .style import Style, StyleStack
+from .syntax import Syntax
+from .text import Text, TextType
+
+
+class MarkdownElement:
+    new_line: ClassVar[bool] = True
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
+        """Factory to create markdown element,
+
+        Args:
+            markdown (Markdown): The parent Markdown object.
+            token (Token): A node from markdown-it.
+
+        Returns:
+            MarkdownElement: A new markdown element
+        """
+        return cls()
+
+    def on_enter(self, context: MarkdownContext) -> None:
+        """Called when the node is entered.
+
+        Args:
+            context (MarkdownContext): The markdown context.
+        """
+
+    def on_text(self, context: MarkdownContext, text: TextType) -> None:
+        """Called when text is parsed.
+
+        Args:
+            context (MarkdownContext): The markdown context.
+        """
+
+    def on_leave(self, context: MarkdownContext) -> None:
+        """Called when the parser leaves the element.
+
+        Args:
+            context (MarkdownContext): [description]
+        """
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        """Called when a child element is closed.
+
+        This method allows a parent element to take over rendering of its children.
+
+        Args:
+            context (MarkdownContext): The markdown context.
+            child (MarkdownElement): The child markdown element.
+
+        Returns:
+            bool: Return True to render the element, or False to not render the element.
+        """
+        return True
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        return ()
+
+
+class UnknownElement(MarkdownElement):
+    """An unknown element.
+
+    Hopefully there will be no unknown elements, and we will have a MarkdownElement for
+    everything in the document.
+
+    """
+
+
+class TextElement(MarkdownElement):
+    """Base class for elements that render text."""
+
+    style_name = "none"
+
+    def on_enter(self, context: MarkdownContext) -> None:
+        self.style = context.enter_style(self.style_name)
+        self.text = Text(justify="left")
+
+    def on_text(self, context: MarkdownContext, text: TextType) -> None:
+        self.text.append(text, context.current_style if isinstance(text, str) else None)
+
+    def on_leave(self, context: MarkdownContext) -> None:
+        context.leave_style()
+
+
+class Paragraph(TextElement):
+    """A Paragraph."""
+
+    style_name = "markdown.paragraph"
+    justify: JustifyMethod
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> Paragraph:
+        return cls(justify=markdown.justify or "left")
+
+    def __init__(self, justify: JustifyMethod) -> None:
+        self.justify = justify
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        self.text.justify = self.justify
+        yield self.text
+
+
+@dataclass
+class HeadingFormat:
+    justify: JustifyMethod = "left"
+    style: str = ""
+
+
+class Heading(TextElement):
+    """A heading."""
+
+    LEVEL_ALIGN: ClassVar[dict[str, JustifyMethod]] = {
+        "h1": "center",
+        "h2": "left",
+        "h3": "left",
+        "h4": "left",
+        "h5": "left",
+        "h6": "left",
+    }
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> Heading:
+        return cls(token.tag)
+
+    def on_enter(self, context: MarkdownContext) -> None:
+        self.text = Text()
+        context.enter_style(self.style_name)
+
+    def __init__(self, tag: str) -> None:
+        self.tag = tag
+        self.style_name = f"markdown.{tag}"
+        super().__init__()
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        text = self.text.copy()
+        heading_justify = self.LEVEL_ALIGN.get(self.tag, "left")
+        text.justify = heading_justify
+        yield text
+
+
+class CodeBlock(TextElement):
+    """A code block with syntax highlighting."""
+
+    style_name = "markdown.code_block"
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> CodeBlock:
+        node_info = token.info or ""
+        lexer_name = node_info.partition(" ")[0]
+        return cls(lexer_name or "text", markdown.code_theme)
+
+    def __init__(self, lexer_name: str, theme: str) -> None:
+        self.lexer_name = lexer_name
+        self.theme = theme
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        code = str(self.text).rstrip()
+        syntax = Syntax(
+            code, self.lexer_name, theme=self.theme, word_wrap=True, padding=1
+        )
+        yield syntax
+
+
+class BlockQuote(TextElement):
+    """A block quote."""
+
+    style_name = "markdown.block_quote"
+
+    def __init__(self) -> None:
+        self.elements: Renderables = Renderables()
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        self.elements.append(child)
+        return False
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        render_options = options.update(width=options.max_width - 4)
+        lines = console.render_lines(self.elements, render_options, style=self.style)
+        style = self.style
+        new_line = Segment("\n")
+        padding = Segment("▌ ", style)
+        for line in lines:
+            yield padding
+            yield from line
+            yield new_line
+
+
+class HorizontalRule(MarkdownElement):
+    """A horizontal rule to divide sections."""
+
+    new_line = False
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        style = console.get_style("markdown.hr", default="none")
+        yield Rule(style=style, characters="-")
+        yield Text()
+
+
+class TableElement(MarkdownElement):
+    """MarkdownElement corresponding to `table_open`."""
+
+    def __init__(self) -> None:
+        self.header: TableHeaderElement | None = None
+        self.body: TableBodyElement | None = None
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        if isinstance(child, TableHeaderElement):
+            self.header = child
+        elif isinstance(child, TableBodyElement):
+            self.body = child
+        else:
+            raise RuntimeError("Couldn't process markdown table.")
+        return False
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        table = Table(
+            box=box.SIMPLE,
+            pad_edge=False,
+            style="markdown.table.border",
+            show_edge=True,
+            collapse_padding=True,
+        )
+
+        if self.header is not None and self.header.row is not None:
+            for column in self.header.row.cells:
+                heading = column.content.copy()
+                heading.stylize("markdown.table.header")
+                table.add_column(heading)
+
+        if self.body is not None:
+            for row in self.body.rows:
+                row_content = [element.content for element in row.cells]
+                table.add_row(*row_content)
+
+        yield table
+
+
+class TableHeaderElement(MarkdownElement):
+    """MarkdownElement corresponding to `thead_open` and `thead_close`."""
+
+    def __init__(self) -> None:
+        self.row: TableRowElement | None = None
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        assert isinstance(child, TableRowElement)
+        self.row = child
+        return False
+
+
+class TableBodyElement(MarkdownElement):
+    """MarkdownElement corresponding to `tbody_open` and `tbody_close`."""
+
+    def __init__(self) -> None:
+        self.rows: list[TableRowElement] = []
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        assert isinstance(child, TableRowElement)
+        self.rows.append(child)
+        return False
+
+
+class TableRowElement(MarkdownElement):
+    """MarkdownElement corresponding to `tr_open` and `tr_close`."""
+
+    def __init__(self) -> None:
+        self.cells: list[TableDataElement] = []
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        assert isinstance(child, TableDataElement)
+        self.cells.append(child)
+        return False
+
+
+class TableDataElement(MarkdownElement):
+    """MarkdownElement corresponding to `td_open` and `td_close`
+    and `th_open` and `th_close`."""
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
+        style = str(token.attrs.get("style")) or ""
+
+        justify: JustifyMethod
+        if "text-align:right" in style:
+            justify = "right"
+        elif "text-align:center" in style:
+            justify = "center"
+        elif "text-align:left" in style:
+            justify = "left"
+        else:
+            justify = "default"
+
+        assert justify in get_args(JustifyMethod)
+        return cls(justify=justify)
+
+    def __init__(self, justify: JustifyMethod) -> None:
+        self.content: Text = Text("", justify=justify)
+        self.justify = justify
+
+    def on_text(self, context: MarkdownContext, text: TextType) -> None:
+        if isinstance(text, str):
+            self.content.append(text, context.current_style)
+        else:
+            self.content.append_text(text)
+
+
+class ListElement(MarkdownElement):
+    """A list element."""
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> ListElement:
+        return cls(token.type, int(token.attrs.get("start", 1)))
+
+    def __init__(self, list_type: str, list_start: int | None) -> None:
+        self.items: list[ListItem] = []
+        self.list_type = list_type
+        self.list_start = list_start
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        assert isinstance(child, ListItem)
+        self.items.append(child)
+        return False
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        if self.list_type == "bullet_list_open":
+            for item in self.items:
+                yield from item.render_bullet(console, options)
+        else:
+            number = 1 if self.list_start is None else self.list_start
+            last_number = number + len(self.items)
+            for index, item in enumerate(self.items):
+                yield from item.render_number(
+                    console, options, number + index, last_number
+                )
+
+
+class ListItem(TextElement):
+    """An item in a list."""
+
+    style_name = "markdown.item"
+
+    def __init__(self) -> None:
+        self.elements: Renderables = Renderables()
+
+    def on_child_close(self, context: MarkdownContext, child: MarkdownElement) -> bool:
+        self.elements.append(child)
+        return False
+
+    def render_bullet(self, console: Console, options: ConsoleOptions) -> RenderResult:
+        render_options = options.update(width=options.max_width - 3)
+        lines = console.render_lines(self.elements, render_options, style=self.style)
+        bullet_style = console.get_style("markdown.item.bullet", default="none")
+
+        bullet = Segment(" • ", bullet_style)
+        padding = Segment(" " * 3, bullet_style)
+        new_line = Segment("\n")
+        for first, line in loop_first(lines):
+            yield bullet if first else padding
+            yield from line
+            yield new_line
+
+    def render_number(
+        self, console: Console, options: ConsoleOptions, number: int, last_number: int
+    ) -> RenderResult:
+        number_width = len(str(last_number)) + 2
+        render_options = options.update(width=options.max_width - number_width)
+        lines = console.render_lines(self.elements, render_options, style=self.style)
+        number_style = console.get_style("markdown.item.number", default="none")
+
+        new_line = Segment("\n")
+        padding = Segment(" " * number_width, number_style)
+        numeral = Segment(f"{number}".rjust(number_width - 1) + " ", number_style)
+        for first, line in loop_first(lines):
+            yield numeral if first else padding
+            yield from line
+            yield new_line
+
+
+class Link(TextElement):
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
+        url = token.attrs.get("href", "#")
+        return cls(token.content, str(url))
+
+    def __init__(self, text: str, href: str):
+        self.text = Text(text)
+        self.href = href
+
+
+class ImageItem(TextElement):
+    """Renders a placeholder for an image."""
+
+    new_line = False
+
+    @classmethod
+    def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
+        """Factory to create markdown element,
+
+        Args:
+            markdown (Markdown): The parent Markdown object.
+            token (Any): A token from markdown-it.
+
+        Returns:
+            MarkdownElement: A new markdown element
+        """
+        return cls(str(token.attrs.get("src", "")), markdown.hyperlinks)
+
+    def __init__(self, destination: str, hyperlinks: bool) -> None:
+        self.destination = destination
+        self.hyperlinks = hyperlinks
+        self.link: str | None = None
+        super().__init__()
+
+    def on_enter(self, context: MarkdownContext) -> None:
+        self.link = context.current_style.link
+        self.text = Text(justify="left")
+        super().on_enter(context)
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        link_style = Style(link=self.link or self.destination or None)
+        title = self.text or Text(self.destination.strip("/").rsplit("/", 1)[-1])
+        if self.hyperlinks:
+            title.stylize(link_style)
+        text = Text.assemble("🌆 ", title, " ", end="")
+        yield text
+
+
+class MarkdownContext:
+    """Manages the console render state."""
+
+    def __init__(
+        self,
+        console: Console,
+        options: ConsoleOptions,
+        style: Style,
+        inline_code_lexer: str | None = None,
+        inline_code_theme: str = "monokai",
+    ) -> None:
+        self.console = console
+        self.options = options
+        self.style_stack: StyleStack = StyleStack(style)
+        self.stack: Stack[MarkdownElement] = Stack()
+
+        self._syntax: Syntax | None = None
+        if inline_code_lexer is not None:
+            self._syntax = Syntax("", inline_code_lexer, theme=inline_code_theme)
+
+    @property
+    def current_style(self) -> Style:
+        """Current style which is the product of all styles on the stack."""
+        return self.style_stack.current
+
+    def on_text(self, text: str, node_type: str) -> None:
+        """Called when the parser visits text."""
+        if node_type in {"fence", "code_inline"} and self._syntax is not None:
+            highlight_text = self._syntax.highlight(text)
+            highlight_text.rstrip()
+            self.stack.top.on_text(
+                self, Text.assemble(highlight_text, style=self.style_stack.current)
+            )
+        else:
+            self.stack.top.on_text(self, text)
+
+    def enter_style(self, style_name: str | Style) -> Style:
+        """Enter a style context."""
+        style = self.console.get_style(style_name, default="none")
+        self.style_stack.push(style)
+        return self.current_style
+
+    def leave_style(self) -> Style:
+        """Leave a style context."""
+        style = self.style_stack.pop()
+        return style
+
+
+class Markdown(JupyterMixin):
+    """A Markdown renderable.
+
+    Args:
+        markup (str): A string containing markdown.
+        code_theme (str, optional): Pygments theme for code blocks. Defaults to "monokai". See https://pygments.org/styles/ for code themes.
+        justify (JustifyMethod, optional): Justify value for paragraphs. Defaults to None.
+        style (Union[str, Style], optional): Optional style to apply to markdown.
+        hyperlinks (bool, optional): Enable hyperlinks. Defaults to ``True``.
+        inline_code_lexer: (str, optional): Lexer to use if inline code highlighting is
+            enabled. Defaults to None.
+        inline_code_theme: (Optional[str], optional): Pygments theme for inline code
+            highlighting, or None for no highlighting. Defaults to None.
+    """
+
+    elements: ClassVar[dict[str, type[MarkdownElement]]] = {
+        "paragraph_open": Paragraph,
+        "heading_open": Heading,
+        "fence": CodeBlock,
+        "code_block": CodeBlock,
+        "blockquote_open": BlockQuote,
+        "hr": HorizontalRule,
+        "bullet_list_open": ListElement,
+        "ordered_list_open": ListElement,
+        "list_item_open": ListItem,
+        "image": ImageItem,
+        "table_open": TableElement,
+        "tbody_open": TableBodyElement,
+        "thead_open": TableHeaderElement,
+        "tr_open": TableRowElement,
+        "td_open": TableDataElement,
+        "th_open": TableDataElement,
+    }
+
+    inlines = {"em", "strong", "code", "s"}
+
+    def __init__(
+        self,
+        markup: str,
+        code_theme: str = "monokai",
+        justify: JustifyMethod | None = None,
+        style: str | Style = "none",
+        hyperlinks: bool = True,
+        inline_code_lexer: str | None = None,
+        inline_code_theme: str | None = None,
+    ) -> None:
+        parser = MarkdownIt().enable("strikethrough").enable("table")
+        self.markup = markup
+        self.parsed = parser.parse(markup)
+        self.code_theme = code_theme
+        self.justify: JustifyMethod | None = justify
+        self.style = style
+        self.hyperlinks = hyperlinks
+        self.inline_code_lexer = inline_code_lexer
+        self.inline_code_theme = inline_code_theme or code_theme
+
+    def _flatten_tokens(self, tokens: Iterable[Token]) -> Iterable[Token]:
+        """Flattens the token stream."""
+        for token in tokens:
+            is_fence = token.type == "fence"
+            is_image = token.tag == "img"
+            if token.children and not (is_image or is_fence):
+                yield from self._flatten_tokens(token.children)
+            else:
+                yield token
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        """Render markdown to the console."""
+        style = console.get_style(self.style, default="none")
+        options = options.update(height=None)
+        context = MarkdownContext(
+            console,
+            options,
+            style,
+            inline_code_lexer=self.inline_code_lexer,
+            inline_code_theme=self.inline_code_theme,
+        )
+        tokens = self.parsed
+        inline_style_tags = self.inlines
+        new_line = False
+        _new_line_segment = Segment.line()
+
+        for token in self._flatten_tokens(tokens):
+            node_type = token.type
+            tag = token.tag
+
+            entering = token.nesting == 1
+            exiting = token.nesting == -1
+            self_closing = token.nesting == 0
+
+            if node_type == "text":
+                context.on_text(token.content, node_type)
+            elif node_type == "hardbreak":
+                context.on_text("\n", node_type)
+            elif node_type == "softbreak":
+                context.on_text(" ", node_type)
+            elif node_type == "link_open":
+                href = str(token.attrs.get("href", ""))
+                if self.hyperlinks:
+                    link_style = console.get_style("markdown.link_url", default="none")
+                    link_style += Style(link=href)
+                    context.enter_style(link_style)
+                else:
+                    context.stack.push(Link.create(self, token))
+            elif node_type == "html_inline":
+                if token.content == "":
+                    kbd_style = console.get_style("markdown.kbd", default="bold")
+                    context.enter_style(kbd_style)
+                elif token.content == "":
+                    context.leave_style()
+                else:
+                    continue
+            elif node_type == "link_close":
+                if self.hyperlinks:
+                    context.leave_style()
+                else:
+                    element = context.stack.pop()
+                    assert isinstance(element, Link)
+                    link_style = console.get_style("markdown.link", default="none")
+                    context.enter_style(link_style)
+                    context.on_text(element.text.plain, node_type)
+                    context.leave_style()
+                    context.on_text(" (", node_type)
+                    link_url_style = console.get_style(
+                        "markdown.link_url", default="none"
+                    )
+                    context.enter_style(link_url_style)
+                    context.on_text(element.href, node_type)
+                    context.leave_style()
+                    context.on_text(")", node_type)
+            elif (
+                tag in inline_style_tags
+                and node_type != "fence"
+                and node_type != "code_block"
+            ):
+                if entering:
+                    # If it's an opening inline token e.g. strong, em, etc.
+                    # Then we move into a style context i.e. push to stack.
+                    context.enter_style(f"markdown.{tag}")
+                elif exiting:
+                    # If it's a closing inline style, then we pop the style
+                    # off of the stack, to move out of the context of it...
+                    context.leave_style()
+                else:
+                    # If it's a self-closing inline style e.g. `code_inline`
+                    context.enter_style(f"markdown.{tag}")
+                    if token.content:
+                        context.on_text(token.content, node_type)
+                    context.leave_style()
+            else:
+                # Map the markdown tag -> MarkdownElement renderable
+                element_class = self.elements.get(token.type) or UnknownElement
+                element = element_class.create(self, token)
+
+                if entering or self_closing:
+                    context.stack.push(element)
+                    element.on_enter(context)
+
+                if exiting:  # CLOSING tag
+                    element = context.stack.pop()
+
+                    should_render = not context.stack or (
+                        context.stack
+                        and context.stack.top.on_child_close(context, element)
+                    )
+
+                    if should_render:
+                        if new_line:
+                            yield _new_line_segment
+
+                        yield from console.render(element, context.options)
+                elif self_closing:  # SELF-CLOSING tags (e.g. text, code, image)
+                    context.stack.pop()
+                    text = token.content
+                    if text is not None:
+                        element.on_text(context, text)
+
+                    should_render = (
+                        not context.stack
+                        or context.stack
+                        and context.stack.top.on_child_close(context, element)
+                    )
+                    if should_render:
+                        if new_line and node_type != "inline":
+                            yield _new_line_segment
+                        yield from console.render(element, context.options)
+
+                if exiting or self_closing:
+                    element.on_leave(context)
+                    new_line = element.new_line
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import argparse
+    import sys
+
+    parser = argparse.ArgumentParser(
+        description="Render Markdown to the console with Rich"
+    )
+    parser.add_argument(
+        "path",
+        metavar="PATH",
+        help="path to markdown file, or - for stdin",
+    )
+    parser.add_argument(
+        "-c",
+        "--force-color",
+        dest="force_color",
+        action="store_true",
+        default=None,
+        help="force color for non-terminals",
+    )
+    parser.add_argument(
+        "-t",
+        "--code-theme",
+        dest="code_theme",
+        default="monokai",
+        help="pygments code theme",
+    )
+    parser.add_argument(
+        "-i",
+        "--inline-code-lexer",
+        dest="inline_code_lexer",
+        default=None,
+        help="inline_code_lexer",
+    )
+    parser.add_argument(
+        "-y",
+        "--hyperlinks",
+        dest="hyperlinks",
+        action="store_true",
+        help="enable hyperlinks",
+    )
+    parser.add_argument(
+        "-w",
+        "--width",
+        type=int,
+        dest="width",
+        default=None,
+        help="width of output (default will auto-detect)",
+    )
+    parser.add_argument(
+        "-j",
+        "--justify",
+        dest="justify",
+        action="store_true",
+        help="enable full text justify",
+    )
+    parser.add_argument(
+        "-p",
+        "--page",
+        dest="page",
+        action="store_true",
+        help="use pager to scroll output",
+    )
+    args = parser.parse_args()
+
+    from rich.console import Console
+
+    if args.path == "-":
+        markdown_body = sys.stdin.read()
+    else:
+        with open(args.path, encoding="utf-8") as markdown_file:
+            markdown_body = markdown_file.read()
+
+    markdown = Markdown(
+        markdown_body,
+        justify="full" if args.justify else "left",
+        code_theme=args.code_theme,
+        hyperlinks=args.hyperlinks,
+        inline_code_lexer=args.inline_code_lexer,
+    )
+    if args.page:
+        import io
+        import pydoc
+
+        fileio = io.StringIO()
+        console = Console(
+            file=fileio, force_terminal=args.force_color, width=args.width
+        )
+        console.print(markdown)
+        pydoc.pager(fileio.getvalue())
+
+    else:
+        console = Console(
+            force_terminal=args.force_color, width=args.width, record=True
+        )
+        console.print(markdown)
diff --git a/.venv/lib/python3.12/site-packages/rich/markup.py b/.venv/lib/python3.12/site-packages/rich/markup.py
new file mode 100644
index 0000000..bd9c05a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/markup.py
@@ -0,0 +1,251 @@
+import re
+from ast import literal_eval
+from operator import attrgetter
+from typing import Callable, Iterable, List, Match, NamedTuple, Optional, Tuple, Union
+
+from ._emoji_replace import _emoji_replace
+from .emoji import EmojiVariant
+from .errors import MarkupError
+from .style import Style
+from .text import Span, Text
+
+RE_TAGS = re.compile(
+    r"""((\\*)\[([a-z#/@][^[]*?)])""",
+    re.VERBOSE,
+)
+
+RE_HANDLER = re.compile(r"^([\w.]*?)(\(.*?\))?$")
+
+
+class Tag(NamedTuple):
+    """A tag in console markup."""
+
+    name: str
+    """The tag name. e.g. 'bold'."""
+    parameters: Optional[str]
+    """Any additional parameters after the name."""
+
+    def __str__(self) -> str:
+        return (
+            self.name if self.parameters is None else f"{self.name} {self.parameters}"
+        )
+
+    @property
+    def markup(self) -> str:
+        """Get the string representation of this tag."""
+        return (
+            f"[{self.name}]"
+            if self.parameters is None
+            else f"[{self.name}={self.parameters}]"
+        )
+
+
+_ReStringMatch = Match[str]  # regex match object
+_ReSubCallable = Callable[[_ReStringMatch], str]  # Callable invoked by re.sub
+_EscapeSubMethod = Callable[[_ReSubCallable, str], str]  # Sub method of a compiled re
+
+
+def escape(
+    markup: str,
+    _escape: _EscapeSubMethod = re.compile(r"(\\*)(\[[a-z#/@][^[]*?])").sub,
+) -> str:
+    """Escapes text so that it won't be interpreted as markup.
+
+    Args:
+        markup (str): Content to be inserted in to markup.
+
+    Returns:
+        str: Markup with square brackets escaped.
+    """
+
+    def escape_backslashes(match: Match[str]) -> str:
+        """Called by re.sub replace matches."""
+        backslashes, text = match.groups()
+        return f"{backslashes}{backslashes}\\{text}"
+
+    markup = _escape(escape_backslashes, markup)
+    if markup.endswith("\\") and not markup.endswith("\\\\"):
+        return markup + "\\"
+
+    return markup
+
+
+def _parse(markup: str) -> Iterable[Tuple[int, Optional[str], Optional[Tag]]]:
+    """Parse markup in to an iterable of tuples of (position, text, tag).
+
+    Args:
+        markup (str): A string containing console markup
+
+    """
+    position = 0
+    _divmod = divmod
+    _Tag = Tag
+    for match in RE_TAGS.finditer(markup):
+        full_text, escapes, tag_text = match.groups()
+        start, end = match.span()
+        if start > position:
+            yield start, markup[position:start], None
+        if escapes:
+            backslashes, escaped = _divmod(len(escapes), 2)
+            if backslashes:
+                # Literal backslashes
+                yield start, "\\" * backslashes, None
+                start += backslashes * 2
+            if escaped:
+                # Escape of tag
+                yield start, full_text[len(escapes) :], None
+                position = end
+                continue
+        text, equals, parameters = tag_text.partition("=")
+        yield start, None, _Tag(text, parameters if equals else None)
+        position = end
+    if position < len(markup):
+        yield position, markup[position:], None
+
+
+def render(
+    markup: str,
+    style: Union[str, Style] = "",
+    emoji: bool = True,
+    emoji_variant: Optional[EmojiVariant] = None,
+) -> Text:
+    """Render console markup in to a Text instance.
+
+    Args:
+        markup (str): A string containing console markup.
+        style: (Union[str, Style]): The style to use.
+        emoji (bool, optional): Also render emoji code. Defaults to True.
+        emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
+
+
+    Raises:
+        MarkupError: If there is a syntax error in the markup.
+
+    Returns:
+        Text: A test instance.
+    """
+    emoji_replace = _emoji_replace
+    if "[" not in markup:
+        return Text(
+            emoji_replace(markup, default_variant=emoji_variant) if emoji else markup,
+            style=style,
+        )
+    text = Text(style=style)
+    append = text.append
+    normalize = Style.normalize
+
+    style_stack: List[Tuple[int, Tag]] = []
+    pop = style_stack.pop
+
+    spans: List[Span] = []
+    append_span = spans.append
+
+    _Span = Span
+    _Tag = Tag
+
+    def pop_style(style_name: str) -> Tuple[int, Tag]:
+        """Pop tag matching given style name."""
+        for index, (_, tag) in enumerate(reversed(style_stack), 1):
+            if tag.name == style_name:
+                return pop(-index)
+        raise KeyError(style_name)
+
+    for position, plain_text, tag in _parse(markup):
+        if plain_text is not None:
+            # Handle open brace escapes, where the brace is not part of a tag.
+            plain_text = plain_text.replace("\\[", "[")
+            append(emoji_replace(plain_text) if emoji else plain_text)
+        elif tag is not None:
+            if tag.name.startswith("/"):  # Closing tag
+                style_name = tag.name[1:].strip()
+
+                if style_name:  # explicit close
+                    style_name = normalize(style_name)
+                    try:
+                        start, open_tag = pop_style(style_name)
+                    except KeyError:
+                        raise MarkupError(
+                            f"closing tag '{tag.markup}' at position {position} doesn't match any open tag"
+                        ) from None
+                else:  # implicit close
+                    try:
+                        start, open_tag = pop()
+                    except IndexError:
+                        raise MarkupError(
+                            f"closing tag '[/]' at position {position} has nothing to close"
+                        ) from None
+
+                if open_tag.name.startswith("@"):
+                    if open_tag.parameters:
+                        handler_name = ""
+                        parameters = open_tag.parameters.strip()
+                        handler_match = RE_HANDLER.match(parameters)
+                        if handler_match is not None:
+                            handler_name, match_parameters = handler_match.groups()
+                            parameters = (
+                                "()" if match_parameters is None else match_parameters
+                            )
+
+                        try:
+                            meta_params = literal_eval(parameters)
+                        except SyntaxError as error:
+                            raise MarkupError(
+                                f"error parsing {parameters!r} in {open_tag.parameters!r}; {error.msg}"
+                            )
+                        except Exception as error:
+                            raise MarkupError(
+                                f"error parsing {open_tag.parameters!r}; {error}"
+                            ) from None
+
+                        if handler_name:
+                            meta_params = (
+                                handler_name,
+                                meta_params
+                                if isinstance(meta_params, tuple)
+                                else (meta_params,),
+                            )
+
+                    else:
+                        meta_params = ()
+
+                    append_span(
+                        _Span(
+                            start, len(text), Style(meta={open_tag.name: meta_params})
+                        )
+                    )
+                else:
+                    append_span(_Span(start, len(text), str(open_tag)))
+
+            else:  # Opening tag
+                normalized_tag = _Tag(normalize(tag.name), tag.parameters)
+                style_stack.append((len(text), normalized_tag))
+
+    text_length = len(text)
+    while style_stack:
+        start, tag = style_stack.pop()
+        style = str(tag)
+        if style:
+            append_span(_Span(start, text_length, style))
+
+    text.spans = sorted(spans[::-1], key=attrgetter("start"))
+    return text
+
+
+if __name__ == "__main__":  # pragma: no cover
+    MARKUP = [
+        "[red]Hello World[/red]",
+        "[magenta]Hello [b]World[/b]",
+        "[bold]Bold[italic] bold and italic [/bold]italic[/italic]",
+        "Click [link=https://www.willmcgugan.com]here[/link] to visit my Blog",
+        ":warning-emoji: [bold red blink] DANGER![/]",
+    ]
+
+    from rich import print
+    from rich.table import Table
+
+    grid = Table("Markup", "Result", padding=(0, 1))
+
+    for markup in MARKUP:
+        grid.add_row(Text(markup), markup)
+
+    print(grid)
diff --git a/.venv/lib/python3.12/site-packages/rich/measure.py b/.venv/lib/python3.12/site-packages/rich/measure.py
new file mode 100644
index 0000000..a508ffa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/measure.py
@@ -0,0 +1,151 @@
+from operator import itemgetter
+from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Sequence
+
+from . import errors
+from .protocol import is_renderable, rich_cast
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, RenderableType
+
+
+class Measurement(NamedTuple):
+    """Stores the minimum and maximum widths (in characters) required to render an object."""
+
+    minimum: int
+    """Minimum number of cells required to render."""
+    maximum: int
+    """Maximum number of cells required to render."""
+
+    @property
+    def span(self) -> int:
+        """Get difference between maximum and minimum."""
+        return self.maximum - self.minimum
+
+    def normalize(self) -> "Measurement":
+        """Get measurement that ensures that minimum <= maximum and minimum >= 0
+
+        Returns:
+            Measurement: A normalized measurement.
+        """
+        minimum, maximum = self
+        minimum = min(max(0, minimum), maximum)
+        return Measurement(max(0, minimum), max(0, max(minimum, maximum)))
+
+    def with_maximum(self, width: int) -> "Measurement":
+        """Get a RenderableWith where the widths are <= width.
+
+        Args:
+            width (int): Maximum desired width.
+
+        Returns:
+            Measurement: New Measurement object.
+        """
+        minimum, maximum = self
+        return Measurement(min(minimum, width), min(maximum, width))
+
+    def with_minimum(self, width: int) -> "Measurement":
+        """Get a RenderableWith where the widths are >= width.
+
+        Args:
+            width (int): Minimum desired width.
+
+        Returns:
+            Measurement: New Measurement object.
+        """
+        minimum, maximum = self
+        width = max(0, width)
+        return Measurement(max(minimum, width), max(maximum, width))
+
+    def clamp(
+        self, min_width: Optional[int] = None, max_width: Optional[int] = None
+    ) -> "Measurement":
+        """Clamp a measurement within the specified range.
+
+        Args:
+            min_width (int): Minimum desired width, or ``None`` for no minimum. Defaults to None.
+            max_width (int): Maximum desired width, or ``None`` for no maximum. Defaults to None.
+
+        Returns:
+            Measurement: New Measurement object.
+        """
+        measurement = self
+        if min_width is not None:
+            measurement = measurement.with_minimum(min_width)
+        if max_width is not None:
+            measurement = measurement.with_maximum(max_width)
+        return measurement
+
+    @classmethod
+    def get(
+        cls, console: "Console", options: "ConsoleOptions", renderable: "RenderableType"
+    ) -> "Measurement":
+        """Get a measurement for a renderable.
+
+        Args:
+            console (~rich.console.Console): Console instance.
+            options (~rich.console.ConsoleOptions): Console options.
+            renderable (RenderableType): An object that may be rendered with Rich.
+
+        Raises:
+            errors.NotRenderableError: If the object is not renderable.
+
+        Returns:
+            Measurement: Measurement object containing range of character widths required to render the object.
+        """
+        _max_width = options.max_width
+        if _max_width < 1:
+            return Measurement(0, 0)
+        if isinstance(renderable, str):
+            renderable = console.render_str(
+                renderable, markup=options.markup, highlight=False
+            )
+        renderable = rich_cast(renderable)
+        if is_renderable(renderable):
+            get_console_width: Optional[
+                Callable[["Console", "ConsoleOptions"], "Measurement"]
+            ] = getattr(renderable, "__rich_measure__", None)
+            if get_console_width is not None:
+                render_width = (
+                    get_console_width(console, options)
+                    .normalize()
+                    .with_maximum(_max_width)
+                )
+                if render_width.maximum < 1:
+                    return Measurement(0, 0)
+                return render_width.normalize()
+            else:
+                return Measurement(0, _max_width)
+        else:
+            raise errors.NotRenderableError(
+                f"Unable to get render width for {renderable!r}; "
+                "a str, Segment, or object with __rich_console__ method is required"
+            )
+
+
+def measure_renderables(
+    console: "Console",
+    options: "ConsoleOptions",
+    renderables: Sequence["RenderableType"],
+) -> "Measurement":
+    """Get a measurement that would fit a number of renderables.
+
+    Args:
+        console (~rich.console.Console): Console instance.
+        options (~rich.console.ConsoleOptions): Console options.
+        renderables (Iterable[RenderableType]): One or more renderable objects.
+
+    Returns:
+        Measurement: Measurement object containing range of character widths required to
+            contain all given renderables.
+    """
+    if not renderables:
+        return Measurement(0, 0)
+    get_measurement = Measurement.get
+    measurements = [
+        get_measurement(console, options, renderable) for renderable in renderables
+    ]
+    measured_width = Measurement(
+        max(measurements, key=itemgetter(0)).minimum,
+        max(measurements, key=itemgetter(1)).maximum,
+    )
+    return measured_width
diff --git a/.venv/lib/python3.12/site-packages/rich/padding.py b/.venv/lib/python3.12/site-packages/rich/padding.py
new file mode 100644
index 0000000..d1aa01b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/padding.py
@@ -0,0 +1,141 @@
+from typing import TYPE_CHECKING, List, Optional, Tuple, Union
+
+if TYPE_CHECKING:
+    from .console import (
+        Console,
+        ConsoleOptions,
+        RenderableType,
+        RenderResult,
+    )
+
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style
+
+PaddingDimensions = Union[int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]]
+
+
+class Padding(JupyterMixin):
+    """Draw space around content.
+
+    Example:
+        >>> print(Padding("Hello", (2, 4), style="on blue"))
+
+    Args:
+        renderable (RenderableType): String or other renderable.
+        pad (Union[int, Tuple[int]]): Padding for top, right, bottom, and left borders.
+            May be specified with 1, 2, or 4 integers (CSS style).
+        style (Union[str, Style], optional): Style for padding characters. Defaults to "none".
+        expand (bool, optional): Expand padding to fit available width. Defaults to True.
+    """
+
+    def __init__(
+        self,
+        renderable: "RenderableType",
+        pad: "PaddingDimensions" = (0, 0, 0, 0),
+        *,
+        style: Union[str, Style] = "none",
+        expand: bool = True,
+    ):
+        self.renderable = renderable
+        self.top, self.right, self.bottom, self.left = self.unpack(pad)
+        self.style = style
+        self.expand = expand
+
+    @classmethod
+    def indent(cls, renderable: "RenderableType", level: int) -> "Padding":
+        """Make padding instance to render an indent.
+
+        Args:
+            renderable (RenderableType): String or other renderable.
+            level (int): Number of characters to indent.
+
+        Returns:
+            Padding: A Padding instance.
+        """
+
+        return Padding(renderable, pad=(0, 0, 0, level), expand=False)
+
+    @staticmethod
+    def unpack(pad: "PaddingDimensions") -> Tuple[int, int, int, int]:
+        """Unpack padding specified in CSS style."""
+        if isinstance(pad, int):
+            return (pad, pad, pad, pad)
+        if len(pad) == 1:
+            _pad = pad[0]
+            return (_pad, _pad, _pad, _pad)
+        if len(pad) == 2:
+            pad_top, pad_right = pad
+            return (pad_top, pad_right, pad_top, pad_right)
+        if len(pad) == 4:
+            top, right, bottom, left = pad
+            return (top, right, bottom, left)
+        raise ValueError(f"1, 2 or 4 integers required for padding; {len(pad)} given")
+
+    def __repr__(self) -> str:
+        return f"Padding({self.renderable!r}, ({self.top},{self.right},{self.bottom},{self.left}))"
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        style = console.get_style(self.style)
+        if self.expand:
+            width = options.max_width
+        else:
+            width = min(
+                Measurement.get(console, options, self.renderable).maximum
+                + self.left
+                + self.right,
+                options.max_width,
+            )
+        render_options = options.update_width(width - self.left - self.right)
+        if render_options.height is not None:
+            render_options = render_options.update_height(
+                height=render_options.height - self.top - self.bottom
+            )
+        lines = console.render_lines(
+            self.renderable, render_options, style=style, pad=True
+        )
+        _Segment = Segment
+
+        left = _Segment(" " * self.left, style) if self.left else None
+        right = (
+            [_Segment(f'{" " * self.right}', style), _Segment.line()]
+            if self.right
+            else [_Segment.line()]
+        )
+        blank_line: Optional[List[Segment]] = None
+        if self.top:
+            blank_line = [_Segment(f'{" " * width}\n', style)]
+            yield from blank_line * self.top
+        if left:
+            for line in lines:
+                yield left
+                yield from line
+                yield from right
+        else:
+            for line in lines:
+                yield from line
+                yield from right
+        if self.bottom:
+            blank_line = blank_line or [_Segment(f'{" " * width}\n', style)]
+            yield from blank_line * self.bottom
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "Measurement":
+        max_width = options.max_width
+        extra_width = self.left + self.right
+        if max_width - extra_width < 1:
+            return Measurement(max_width, max_width)
+        measure_min, measure_max = Measurement.get(console, options, self.renderable)
+        measurement = Measurement(measure_min + extra_width, measure_max + extra_width)
+        measurement = measurement.with_maximum(max_width)
+        return measurement
+
+
+if __name__ == "__main__":  #  pragma: no cover
+    from rich import print
+
+    print(Padding("Hello, World", (2, 4), style="on blue"))
diff --git a/.venv/lib/python3.12/site-packages/rich/pager.py b/.venv/lib/python3.12/site-packages/rich/pager.py
new file mode 100644
index 0000000..a3f7aa6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/pager.py
@@ -0,0 +1,34 @@
+from abc import ABC, abstractmethod
+from typing import Any
+
+
+class Pager(ABC):
+    """Base class for a pager."""
+
+    @abstractmethod
+    def show(self, content: str) -> None:
+        """Show content in pager.
+
+        Args:
+            content (str): Content to be displayed.
+        """
+
+
+class SystemPager(Pager):
+    """Uses the pager installed on the system."""
+
+    def _pager(self, content: str) -> Any:  #  pragma: no cover
+        return __import__("pydoc").pager(content)
+
+    def show(self, content: str) -> None:
+        """Use the same pager used by pydoc."""
+        self._pager(content)
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from .__main__ import make_test_card
+    from .console import Console
+
+    console = Console()
+    with console.pager(styles=True):
+        console.print(make_test_card())
diff --git a/.venv/lib/python3.12/site-packages/rich/palette.py b/.venv/lib/python3.12/site-packages/rich/palette.py
new file mode 100644
index 0000000..f295879
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/palette.py
@@ -0,0 +1,100 @@
+from math import sqrt
+from functools import lru_cache
+from typing import Sequence, Tuple, TYPE_CHECKING
+
+from .color_triplet import ColorTriplet
+
+if TYPE_CHECKING:
+    from rich.table import Table
+
+
+class Palette:
+    """A palette of available colors."""
+
+    def __init__(self, colors: Sequence[Tuple[int, int, int]]):
+        self._colors = colors
+
+    def __getitem__(self, number: int) -> ColorTriplet:
+        return ColorTriplet(*self._colors[number])
+
+    def __rich__(self) -> "Table":
+        from rich.color import Color
+        from rich.style import Style
+        from rich.text import Text
+        from rich.table import Table
+
+        table = Table(
+            "index",
+            "RGB",
+            "Color",
+            title="Palette",
+            caption=f"{len(self._colors)} colors",
+            highlight=True,
+            caption_justify="right",
+        )
+        for index, color in enumerate(self._colors):
+            table.add_row(
+                str(index),
+                repr(color),
+                Text(" " * 16, style=Style(bgcolor=Color.from_rgb(*color))),
+            )
+        return table
+
+    # This is somewhat inefficient and needs caching
+    @lru_cache(maxsize=1024)
+    def match(self, color: Tuple[int, int, int]) -> int:
+        """Find a color from a palette that most closely matches a given color.
+
+        Args:
+            color (Tuple[int, int, int]): RGB components in range 0 > 255.
+
+        Returns:
+            int: Index of closes matching color.
+        """
+        red1, green1, blue1 = color
+        _sqrt = sqrt
+        get_color = self._colors.__getitem__
+
+        def get_color_distance(index: int) -> float:
+            """Get the distance to a color."""
+            red2, green2, blue2 = get_color(index)
+            red_mean = (red1 + red2) // 2
+            red = red1 - red2
+            green = green1 - green2
+            blue = blue1 - blue2
+            return _sqrt(
+                (((512 + red_mean) * red * red) >> 8)
+                + 4 * green * green
+                + (((767 - red_mean) * blue * blue) >> 8)
+            )
+
+        min_index = min(range(len(self._colors)), key=get_color_distance)
+        return min_index
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import colorsys
+    from typing import Iterable
+    from rich.color import Color
+    from rich.console import Console, ConsoleOptions
+    from rich.segment import Segment
+    from rich.style import Style
+
+    class ColorBox:
+        def __rich_console__(
+            self, console: Console, options: ConsoleOptions
+        ) -> Iterable[Segment]:
+            height = console.size.height - 3
+            for y in range(0, height):
+                for x in range(options.max_width):
+                    h = x / options.max_width
+                    l = y / (height + 1)
+                    r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0)
+                    r2, g2, b2 = colorsys.hls_to_rgb(h, l + (1 / height / 2), 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()
+
+    console = Console()
+    console.print(ColorBox())
diff --git a/.venv/lib/python3.12/site-packages/rich/panel.py b/.venv/lib/python3.12/site-packages/rich/panel.py
new file mode 100644
index 0000000..07587e3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/panel.py
@@ -0,0 +1,317 @@
+from typing import TYPE_CHECKING, Optional
+
+from .align import AlignMethod
+from .box import ROUNDED, Box
+from .cells import cell_len
+from .jupyter import JupyterMixin
+from .measure import Measurement, measure_renderables
+from .padding import Padding, PaddingDimensions
+from .segment import Segment
+from .style import Style, StyleType
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, RenderableType, RenderResult
+
+
+class Panel(JupyterMixin):
+    """A console renderable that draws a border around its contents.
+
+    Example:
+        >>> console.print(Panel("Hello, World!"))
+
+    Args:
+        renderable (RenderableType): A console renderable object.
+        box (Box): A Box instance that defines the look of the border (see :ref:`appendix_box`. Defaults to box.ROUNDED.
+        title (Optional[TextType], optional): Optional title displayed in panel header. Defaults to None.
+        title_align (AlignMethod, optional): Alignment of title. Defaults to "center".
+        subtitle (Optional[TextType], optional): Optional subtitle displayed in panel footer. Defaults to None.
+        subtitle_align (AlignMethod, optional): Alignment of subtitle. Defaults to "center".
+        safe_box (bool, optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
+        expand (bool, optional): If True the panel will stretch to fill the console width, otherwise it will be sized to fit the contents. Defaults to True.
+        style (str, optional): The style of the panel (border and contents). Defaults to "none".
+        border_style (str, optional): The style of the border. Defaults to "none".
+        width (Optional[int], optional): Optional width of panel. Defaults to None to auto-detect.
+        height (Optional[int], optional): Optional height of panel. Defaults to None to auto-detect.
+        padding (Optional[PaddingDimensions]): Optional padding around renderable. Defaults to 0.
+        highlight (bool, optional): Enable automatic highlighting of panel title (if str). Defaults to False.
+    """
+
+    def __init__(
+        self,
+        renderable: "RenderableType",
+        box: Box = ROUNDED,
+        *,
+        title: Optional[TextType] = None,
+        title_align: AlignMethod = "center",
+        subtitle: Optional[TextType] = None,
+        subtitle_align: AlignMethod = "center",
+        safe_box: Optional[bool] = None,
+        expand: bool = True,
+        style: StyleType = "none",
+        border_style: StyleType = "none",
+        width: Optional[int] = None,
+        height: Optional[int] = None,
+        padding: PaddingDimensions = (0, 1),
+        highlight: bool = False,
+    ) -> None:
+        self.renderable = renderable
+        self.box = box
+        self.title = title
+        self.title_align: AlignMethod = title_align
+        self.subtitle = subtitle
+        self.subtitle_align = subtitle_align
+        self.safe_box = safe_box
+        self.expand = expand
+        self.style = style
+        self.border_style = border_style
+        self.width = width
+        self.height = height
+        self.padding = padding
+        self.highlight = highlight
+
+    @classmethod
+    def fit(
+        cls,
+        renderable: "RenderableType",
+        box: Box = ROUNDED,
+        *,
+        title: Optional[TextType] = None,
+        title_align: AlignMethod = "center",
+        subtitle: Optional[TextType] = None,
+        subtitle_align: AlignMethod = "center",
+        safe_box: Optional[bool] = None,
+        style: StyleType = "none",
+        border_style: StyleType = "none",
+        width: Optional[int] = None,
+        height: Optional[int] = None,
+        padding: PaddingDimensions = (0, 1),
+        highlight: bool = False,
+    ) -> "Panel":
+        """An alternative constructor that sets expand=False."""
+        return cls(
+            renderable,
+            box,
+            title=title,
+            title_align=title_align,
+            subtitle=subtitle,
+            subtitle_align=subtitle_align,
+            safe_box=safe_box,
+            style=style,
+            border_style=border_style,
+            width=width,
+            height=height,
+            padding=padding,
+            highlight=highlight,
+            expand=False,
+        )
+
+    @property
+    def _title(self) -> Optional[Text]:
+        if self.title:
+            title_text = (
+                Text.from_markup(self.title)
+                if isinstance(self.title, str)
+                else self.title.copy()
+            )
+            title_text.end = ""
+            title_text.plain = title_text.plain.replace("\n", " ")
+            title_text.no_wrap = True
+            title_text.expand_tabs()
+            title_text.pad(1)
+            return title_text
+        return None
+
+    @property
+    def _subtitle(self) -> Optional[Text]:
+        if self.subtitle:
+            subtitle_text = (
+                Text.from_markup(self.subtitle)
+                if isinstance(self.subtitle, str)
+                else self.subtitle.copy()
+            )
+            subtitle_text.end = ""
+            subtitle_text.plain = subtitle_text.plain.replace("\n", " ")
+            subtitle_text.no_wrap = True
+            subtitle_text.expand_tabs()
+            subtitle_text.pad(1)
+            return subtitle_text
+        return None
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        _padding = Padding.unpack(self.padding)
+        renderable = (
+            Padding(self.renderable, _padding) if any(_padding) else self.renderable
+        )
+        style = console.get_style(self.style)
+        border_style = style + console.get_style(self.border_style)
+        width = (
+            options.max_width
+            if self.width is None
+            else min(options.max_width, self.width)
+        )
+
+        safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box
+        box = self.box.substitute(options, safe=safe_box)
+
+        def align_text(
+            text: Text, width: int, align: str, character: str, style: Style
+        ) -> Text:
+            """Gets new aligned text.
+
+            Args:
+                text (Text): Title or subtitle text.
+                width (int): Desired width.
+                align (str): Alignment.
+                character (str): Character for alignment.
+                style (Style): Border style
+
+            Returns:
+                Text: New text instance
+            """
+            text = text.copy()
+            text.truncate(width)
+            excess_space = width - cell_len(text.plain)
+            if text.style:
+                text.stylize(console.get_style(text.style))
+
+            if excess_space:
+                if align == "left":
+                    return Text.assemble(
+                        text,
+                        (character * excess_space, style),
+                        no_wrap=True,
+                        end="",
+                    )
+                elif align == "center":
+                    left = excess_space // 2
+                    return Text.assemble(
+                        (character * left, style),
+                        text,
+                        (character * (excess_space - left), style),
+                        no_wrap=True,
+                        end="",
+                    )
+                else:
+                    return Text.assemble(
+                        (character * excess_space, style),
+                        text,
+                        no_wrap=True,
+                        end="",
+                    )
+            return text
+
+        title_text = self._title
+        if title_text is not None:
+            title_text.stylize_before(border_style)
+
+        child_width = (
+            width - 2
+            if self.expand
+            else console.measure(
+                renderable, options=options.update_width(width - 2)
+            ).maximum
+        )
+        child_height = self.height or options.height or None
+        if child_height:
+            child_height -= 2
+        if title_text is not None:
+            child_width = min(
+                options.max_width - 2, max(child_width, title_text.cell_len + 2)
+            )
+
+        width = child_width + 2
+        child_options = options.update(
+            width=child_width, height=child_height, highlight=self.highlight
+        )
+        lines = console.render_lines(renderable, child_options, style=style)
+
+        line_start = Segment(box.mid_left, border_style)
+        line_end = Segment(f"{box.mid_right}", border_style)
+        new_line = Segment.line()
+        if title_text is None or width <= 4:
+            yield Segment(box.get_top([width - 2]), border_style)
+        else:
+            title_text = align_text(
+                title_text,
+                width - 4,
+                self.title_align,
+                box.top,
+                border_style,
+            )
+            yield Segment(box.top_left + box.top, border_style)
+            yield from console.render(title_text, child_options.update_width(width - 4))
+            yield Segment(box.top + box.top_right, border_style)
+
+        yield new_line
+        for line in lines:
+            yield line_start
+            yield from line
+            yield line_end
+            yield new_line
+
+        subtitle_text = self._subtitle
+        if subtitle_text is not None:
+            subtitle_text.stylize_before(border_style)
+
+        if subtitle_text is None or width <= 4:
+            yield Segment(box.get_bottom([width - 2]), border_style)
+        else:
+            subtitle_text = align_text(
+                subtitle_text,
+                width - 4,
+                self.subtitle_align,
+                box.bottom,
+                border_style,
+            )
+            yield Segment(box.bottom_left + box.bottom, border_style)
+            yield from console.render(
+                subtitle_text, child_options.update_width(width - 4)
+            )
+            yield Segment(box.bottom + box.bottom_right, border_style)
+
+        yield new_line
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "Measurement":
+        _title = self._title
+        _, right, _, left = Padding.unpack(self.padding)
+        padding = left + right
+        renderables = [self.renderable, _title] if _title else [self.renderable]
+
+        if self.width is None:
+            width = (
+                measure_renderables(
+                    console,
+                    options.update_width(options.max_width - padding - 2),
+                    renderables,
+                ).maximum
+                + padding
+                + 2
+            )
+        else:
+            width = self.width
+        return Measurement(width, width)
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from .console import Console
+
+    c = Console()
+
+    from .box import DOUBLE, ROUNDED
+    from .padding import Padding
+
+    p = Panel(
+        "Hello, World!",
+        title="rich.Panel",
+        style="white on blue",
+        box=DOUBLE,
+        padding=1,
+    )
+
+    c.print()
+    c.print(p)
diff --git a/.venv/lib/python3.12/site-packages/rich/pretty.py b/.venv/lib/python3.12/site-packages/rich/pretty.py
new file mode 100644
index 0000000..00abeca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/pretty.py
@@ -0,0 +1,1016 @@
+import builtins
+import collections
+import dataclasses
+import inspect
+import os
+import reprlib
+import sys
+from array import array
+from collections import Counter, UserDict, UserList, defaultdict, deque
+from dataclasses import dataclass, fields, is_dataclass
+from inspect import isclass
+from itertools import islice
+from types import MappingProxyType
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    DefaultDict,
+    Deque,
+    Dict,
+    Iterable,
+    List,
+    Optional,
+    Sequence,
+    Set,
+    Tuple,
+    Union,
+)
+
+from rich.repr import RichReprResult
+
+try:
+    import attr as _attr_module
+
+    _has_attrs = hasattr(_attr_module, "ib")
+except ImportError:  # pragma: no cover
+    _has_attrs = False
+
+from . import get_console
+from ._loop import loop_last
+from ._pick import pick_bool
+from .abc import RichRenderable
+from .cells import cell_len
+from .highlighter import ReprHighlighter
+from .jupyter import JupyterMixin, JupyterRenderable
+from .measure import Measurement
+from .text import Text
+
+if TYPE_CHECKING:
+    from .console import (
+        Console,
+        ConsoleOptions,
+        HighlighterType,
+        JustifyMethod,
+        OverflowMethod,
+        RenderResult,
+    )
+
+
+def _is_attr_object(obj: Any) -> bool:
+    """Check if an object was created with attrs module."""
+    return _has_attrs and _attr_module.has(type(obj))
+
+
+def _get_attr_fields(obj: Any) -> Sequence["_attr_module.Attribute[Any]"]:
+    """Get fields for an attrs object."""
+    return _attr_module.fields(type(obj)) if _has_attrs else []
+
+
+def _is_dataclass_repr(obj: object) -> bool:
+    """Check if an instance of a dataclass contains the default repr.
+
+    Args:
+        obj (object): A dataclass instance.
+
+    Returns:
+        bool: True if the default repr is used, False if there is a custom repr.
+    """
+    # Digging in to a lot of internals here
+    # Catching all exceptions in case something is missing on a non CPython implementation
+    try:
+        return obj.__repr__.__code__.co_filename in (
+            dataclasses.__file__,
+            reprlib.__file__,
+        )
+    except Exception:  # pragma: no coverage
+        return False
+
+
+_dummy_namedtuple = collections.namedtuple("_dummy_namedtuple", [])
+
+
+def _has_default_namedtuple_repr(obj: object) -> bool:
+    """Check if an instance of namedtuple contains the default repr
+
+    Args:
+        obj (object): A namedtuple
+
+    Returns:
+        bool: True if the default repr is used, False if there's a custom repr.
+    """
+    obj_file = None
+    try:
+        obj_file = inspect.getfile(obj.__repr__)
+    except (OSError, TypeError):
+        # OSError handles case where object is defined in __main__ scope, e.g. REPL - no filename available.
+        # TypeError trapped defensively, in case of object without filename slips through.
+        pass
+    default_repr_file = inspect.getfile(_dummy_namedtuple.__repr__)
+    return obj_file == default_repr_file
+
+
+def _ipy_display_hook(
+    value: Any,
+    console: Optional["Console"] = None,
+    overflow: "OverflowMethod" = "ignore",
+    crop: bool = False,
+    indent_guides: bool = False,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+    expand_all: bool = False,
+) -> Union[str, None]:
+    # needed here to prevent circular import:
+    from .console import ConsoleRenderable
+
+    # always skip rich generated jupyter renderables or None values
+    if _safe_isinstance(value, JupyterRenderable) or value is None:
+        return None
+
+    console = console or get_console()
+
+    with console.capture() as capture:
+        # certain renderables should start on a new line
+        if _safe_isinstance(value, ConsoleRenderable):
+            console.line()
+        console.print(
+            (
+                value
+                if _safe_isinstance(value, RichRenderable)
+                else Pretty(
+                    value,
+                    overflow=overflow,
+                    indent_guides=indent_guides,
+                    max_length=max_length,
+                    max_string=max_string,
+                    max_depth=max_depth,
+                    expand_all=expand_all,
+                    margin=12,
+                )
+            ),
+            crop=crop,
+            new_line_start=True,
+            end="",
+        )
+    # strip trailing newline, not usually part of a text repr
+    # I'm not sure if this should be prevented at a lower level
+    return capture.get().rstrip("\n")
+
+
+def _safe_isinstance(
+    obj: object, class_or_tuple: Union[type, Tuple[type, ...]]
+) -> bool:
+    """isinstance can fail in rare cases, for example types with no __class__"""
+    try:
+        return isinstance(obj, class_or_tuple)
+    except Exception:
+        return False
+
+
+def install(
+    console: Optional["Console"] = None,
+    overflow: "OverflowMethod" = "ignore",
+    crop: bool = False,
+    indent_guides: bool = False,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+    expand_all: bool = False,
+) -> None:
+    """Install automatic pretty printing in the Python REPL.
+
+    Args:
+        console (Console, optional): Console instance or ``None`` to use global console. Defaults to None.
+        overflow (Optional[OverflowMethod], optional): Overflow method. Defaults to "ignore".
+        crop (Optional[bool], optional): Enable cropping of long lines. Defaults to False.
+        indent_guides (bool, optional): Enable indentation guides. Defaults to False.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+        max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. Defaults to None.
+        expand_all (bool, optional): Expand all containers. Defaults to False.
+        max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
+    """
+    from rich import get_console
+
+    console = console or get_console()
+    assert console is not None
+
+    def display_hook(value: Any) -> None:
+        """Replacement sys.displayhook which prettifies objects with Rich."""
+        if value is not None:
+            assert console is not None
+            builtins._ = None  # type: ignore[attr-defined]
+            console.print(
+                (
+                    value
+                    if _safe_isinstance(value, RichRenderable)
+                    else Pretty(
+                        value,
+                        overflow=overflow,
+                        indent_guides=indent_guides,
+                        max_length=max_length,
+                        max_string=max_string,
+                        max_depth=max_depth,
+                        expand_all=expand_all,
+                    )
+                ),
+                crop=crop,
+            )
+            builtins._ = value  # type: ignore[attr-defined]
+
+    try:
+        ip = get_ipython()  # type: ignore[name-defined]
+    except NameError:
+        sys.displayhook = display_hook
+    else:
+        from IPython.core.formatters import BaseFormatter
+
+        class RichFormatter(BaseFormatter):  # type: ignore[misc]
+            pprint: bool = True
+
+            def __call__(self, value: Any) -> Any:
+                if self.pprint:
+                    return _ipy_display_hook(
+                        value,
+                        console=console,
+                        overflow=overflow,
+                        indent_guides=indent_guides,
+                        max_length=max_length,
+                        max_string=max_string,
+                        max_depth=max_depth,
+                        expand_all=expand_all,
+                    )
+                else:
+                    return repr(value)
+
+        # replace plain text formatter with rich formatter
+        rich_formatter = RichFormatter()
+        ip.display_formatter.formatters["text/plain"] = rich_formatter
+
+
+class Pretty(JupyterMixin):
+    """A rich renderable that pretty prints an object.
+
+    Args:
+        _object (Any): An object to pretty print.
+        highlighter (HighlighterType, optional): Highlighter object to apply to result, or None for ReprHighlighter. Defaults to None.
+        indent_size (int, optional): Number of spaces in indent. Defaults to 4.
+        justify (JustifyMethod, optional): Justify method, or None for default. Defaults to None.
+        overflow (OverflowMethod, optional): Overflow method, or None for default. Defaults to None.
+        no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to False.
+        indent_guides (bool, optional): Enable indentation guides. Defaults to False.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+        max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. Defaults to None.
+        expand_all (bool, optional): Expand all containers. Defaults to False.
+        margin (int, optional): Subtrace a margin from width to force containers to expand earlier. Defaults to 0.
+        insert_line (bool, optional): Insert a new line if the output has multiple new lines. Defaults to False.
+    """
+
+    def __init__(
+        self,
+        _object: Any,
+        highlighter: Optional["HighlighterType"] = None,
+        *,
+        indent_size: int = 4,
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        no_wrap: Optional[bool] = False,
+        indent_guides: bool = False,
+        max_length: Optional[int] = None,
+        max_string: Optional[int] = None,
+        max_depth: Optional[int] = None,
+        expand_all: bool = False,
+        margin: int = 0,
+        insert_line: bool = False,
+    ) -> None:
+        self._object = _object
+        self.highlighter = highlighter or ReprHighlighter()
+        self.indent_size = indent_size
+        self.justify: Optional["JustifyMethod"] = justify
+        self.overflow: Optional["OverflowMethod"] = overflow
+        self.no_wrap = no_wrap
+        self.indent_guides = indent_guides
+        self.max_length = max_length
+        self.max_string = max_string
+        self.max_depth = max_depth
+        self.expand_all = expand_all
+        self.margin = margin
+        self.insert_line = insert_line
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        pretty_str = pretty_repr(
+            self._object,
+            max_width=options.max_width - self.margin,
+            indent_size=self.indent_size,
+            max_length=self.max_length,
+            max_string=self.max_string,
+            max_depth=self.max_depth,
+            expand_all=self.expand_all,
+        )
+        pretty_text = Text.from_ansi(
+            pretty_str,
+            justify=self.justify or options.justify,
+            overflow=self.overflow or options.overflow,
+            no_wrap=pick_bool(self.no_wrap, options.no_wrap),
+            style="pretty",
+        )
+        pretty_text = (
+            self.highlighter(pretty_text)
+            if pretty_text
+            else Text(
+                f"{type(self._object)}.__repr__ returned empty string",
+                style="dim italic",
+            )
+        )
+        if self.indent_guides and not options.ascii_only:
+            pretty_text = pretty_text.with_indent_guides(
+                self.indent_size, style="repr.indent"
+            )
+        if self.insert_line and "\n" in pretty_text:
+            yield ""
+        yield pretty_text
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "Measurement":
+        pretty_str = pretty_repr(
+            self._object,
+            max_width=options.max_width,
+            indent_size=self.indent_size,
+            max_length=self.max_length,
+            max_string=self.max_string,
+            max_depth=self.max_depth,
+            expand_all=self.expand_all,
+        )
+        text_width = (
+            max(cell_len(line) for line in pretty_str.splitlines()) if pretty_str else 0
+        )
+        return Measurement(text_width, text_width)
+
+
+def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, str, str]:
+    return (
+        f"defaultdict({_object.default_factory!r}, {{",
+        "})",
+        f"defaultdict({_object.default_factory!r}, {{}})",
+    )
+
+
+def _get_braces_for_deque(_object: Deque[Any]) -> Tuple[str, str, str]:
+    if _object.maxlen is None:
+        return ("deque([", "])", "deque()")
+    return (
+        "deque([",
+        f"], maxlen={_object.maxlen})",
+        f"deque(maxlen={_object.maxlen})",
+    )
+
+
+def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]:
+    return (f"array({_object.typecode!r}, [", "])", f"array({_object.typecode!r})")
+
+
+_BRACES: Dict[type, Callable[[Any], Tuple[str, str, str]]] = {
+    os._Environ: lambda _object: ("environ({", "})", "environ({})"),
+    array: _get_braces_for_array,
+    defaultdict: _get_braces_for_defaultdict,
+    Counter: lambda _object: ("Counter({", "})", "Counter()"),
+    deque: _get_braces_for_deque,
+    dict: lambda _object: ("{", "}", "{}"),
+    UserDict: lambda _object: ("{", "}", "{}"),
+    frozenset: lambda _object: ("frozenset({", "})", "frozenset()"),
+    list: lambda _object: ("[", "]", "[]"),
+    UserList: lambda _object: ("[", "]", "[]"),
+    set: lambda _object: ("{", "}", "set()"),
+    tuple: lambda _object: ("(", ")", "()"),
+    MappingProxyType: lambda _object: ("mappingproxy({", "})", "mappingproxy({})"),
+}
+_CONTAINERS = tuple(_BRACES.keys())
+_MAPPING_CONTAINERS = (dict, os._Environ, MappingProxyType, UserDict)
+
+
+def is_expandable(obj: Any) -> bool:
+    """Check if an object may be expanded by pretty print."""
+    return (
+        _safe_isinstance(obj, _CONTAINERS)
+        or (is_dataclass(obj))
+        or (hasattr(obj, "__rich_repr__"))
+        or _is_attr_object(obj)
+    ) and not isclass(obj)
+
+
+@dataclass
+class Node:
+    """A node in a repr tree. May be atomic or a container."""
+
+    key_repr: str = ""
+    value_repr: str = ""
+    open_brace: str = ""
+    close_brace: str = ""
+    empty: str = ""
+    last: bool = False
+    is_tuple: bool = False
+    is_namedtuple: bool = False
+    children: Optional[List["Node"]] = None
+    key_separator: str = ": "
+    separator: str = ", "
+
+    def iter_tokens(self) -> Iterable[str]:
+        """Generate tokens for this node."""
+        if self.key_repr:
+            yield self.key_repr
+            yield self.key_separator
+        if self.value_repr:
+            yield self.value_repr
+        elif self.children is not None:
+            if self.children:
+                yield self.open_brace
+                if self.is_tuple and not self.is_namedtuple and len(self.children) == 1:
+                    yield from self.children[0].iter_tokens()
+                    yield ","
+                else:
+                    for child in self.children:
+                        yield from child.iter_tokens()
+                        if not child.last:
+                            yield self.separator
+                yield self.close_brace
+            else:
+                yield self.empty
+
+    def check_length(self, start_length: int, max_length: int) -> bool:
+        """Check the length fits within a limit.
+
+        Args:
+            start_length (int): Starting length of the line (indent, prefix, suffix).
+            max_length (int): Maximum length.
+
+        Returns:
+            bool: True if the node can be rendered within max length, otherwise False.
+        """
+        total_length = start_length
+        for token in self.iter_tokens():
+            total_length += cell_len(token)
+            if total_length > max_length:
+                return False
+        return True
+
+    def __str__(self) -> str:
+        repr_text = "".join(self.iter_tokens())
+        return repr_text
+
+    def render(
+        self, max_width: int = 80, indent_size: int = 4, expand_all: bool = False
+    ) -> str:
+        """Render the node to a pretty repr.
+
+        Args:
+            max_width (int, optional): Maximum width of the repr. Defaults to 80.
+            indent_size (int, optional): Size of indents. Defaults to 4.
+            expand_all (bool, optional): Expand all levels. Defaults to False.
+
+        Returns:
+            str: A repr string of the original object.
+        """
+        lines = [_Line(node=self, is_root=True)]
+        line_no = 0
+        while line_no < len(lines):
+            line = lines[line_no]
+            if line.expandable and not line.expanded:
+                if expand_all or not line.check_length(max_width):
+                    lines[line_no : line_no + 1] = line.expand(indent_size)
+            line_no += 1
+
+        repr_str = "\n".join(str(line) for line in lines)
+        return repr_str
+
+
+@dataclass
+class _Line:
+    """A line in repr output."""
+
+    parent: Optional["_Line"] = None
+    is_root: bool = False
+    node: Optional[Node] = None
+    text: str = ""
+    suffix: str = ""
+    whitespace: str = ""
+    expanded: bool = False
+    last: bool = False
+
+    @property
+    def expandable(self) -> bool:
+        """Check if the line may be expanded."""
+        return bool(self.node is not None and self.node.children)
+
+    def check_length(self, max_length: int) -> bool:
+        """Check this line fits within a given number of cells."""
+        start_length = (
+            len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)
+        )
+        assert self.node is not None
+        return self.node.check_length(start_length, max_length)
+
+    def expand(self, indent_size: int) -> Iterable["_Line"]:
+        """Expand this line by adding children on their own line."""
+        node = self.node
+        assert node is not None
+        whitespace = self.whitespace
+        assert node.children
+        if node.key_repr:
+            new_line = yield _Line(
+                text=f"{node.key_repr}{node.key_separator}{node.open_brace}",
+                whitespace=whitespace,
+            )
+        else:
+            new_line = yield _Line(text=node.open_brace, whitespace=whitespace)
+        child_whitespace = self.whitespace + " " * indent_size
+        tuple_of_one = node.is_tuple and len(node.children) == 1
+        for last, child in loop_last(node.children):
+            separator = "," if tuple_of_one else node.separator
+            line = _Line(
+                parent=new_line,
+                node=child,
+                whitespace=child_whitespace,
+                suffix=separator,
+                last=last and not tuple_of_one,
+            )
+            yield line
+
+        yield _Line(
+            text=node.close_brace,
+            whitespace=whitespace,
+            suffix=self.suffix,
+            last=self.last,
+        )
+
+    def __str__(self) -> str:
+        if self.last:
+            return f"{self.whitespace}{self.text}{self.node or ''}"
+        else:
+            return (
+                f"{self.whitespace}{self.text}{self.node or ''}{self.suffix.rstrip()}"
+            )
+
+
+def _is_namedtuple(obj: Any) -> bool:
+    """Checks if an object is most likely a namedtuple. It is possible
+    to craft an object that passes this check and isn't a namedtuple, but
+    there is only a minuscule chance of this happening unintentionally.
+
+    Args:
+        obj (Any): The object to test
+
+    Returns:
+        bool: True if the object is a namedtuple. False otherwise.
+    """
+    try:
+        fields = getattr(obj, "_fields", None)
+    except Exception:
+        # Being very defensive - if we cannot get the attr then its not a namedtuple
+        return False
+    return isinstance(obj, tuple) and isinstance(fields, tuple)
+
+
+def traverse(
+    _object: Any,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+) -> Node:
+    """Traverse object and generate a tree.
+
+    Args:
+        _object (Any): Object to be traversed.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
+            Defaults to None.
+        max_depth (int, optional): Maximum depth of data structures, or None for no maximum.
+            Defaults to None.
+
+    Returns:
+        Node: The root of a tree structure which can be used to render a pretty repr.
+    """
+
+    def to_repr(obj: Any) -> str:
+        """Get repr string for an object, but catch errors."""
+        if (
+            max_string is not None
+            and _safe_isinstance(obj, (bytes, str))
+            and len(obj) > max_string
+        ):
+            truncated = len(obj) - max_string
+            obj_repr = f"{obj[:max_string]!r}+{truncated}"
+        else:
+            try:
+                obj_repr = repr(obj)
+            except Exception as error:
+                obj_repr = f""
+        return obj_repr
+
+    visited_ids: Set[int] = set()
+    push_visited = visited_ids.add
+    pop_visited = visited_ids.remove
+
+    def _traverse(obj: Any, root: bool = False, depth: int = 0) -> Node:
+        """Walk the object depth first."""
+
+        obj_id = id(obj)
+        if obj_id in visited_ids:
+            # Recursion detected
+            return Node(value_repr="...")
+
+        obj_type = type(obj)
+        children: List[Node]
+        reached_max_depth = max_depth is not None and depth >= max_depth
+
+        def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
+            for arg in rich_args:
+                if _safe_isinstance(arg, tuple):
+                    if len(arg) == 3:
+                        key, child, default = arg
+                        if default == child:
+                            continue
+                        yield key, child
+                    elif len(arg) == 2:
+                        key, child = arg
+                        yield key, child
+                    elif len(arg) == 1:
+                        yield arg[0]
+                else:
+                    yield arg
+
+        try:
+            fake_attributes = hasattr(
+                obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492"
+            )
+        except Exception:
+            fake_attributes = False
+
+        rich_repr_result: Optional[RichReprResult] = None
+        if not fake_attributes:
+            try:
+                if hasattr(obj, "__rich_repr__") and not isclass(obj):
+                    rich_repr_result = obj.__rich_repr__()
+            except Exception:
+                pass
+
+        if rich_repr_result is not None:
+            push_visited(obj_id)
+            angular = getattr(obj.__rich_repr__, "angular", False)
+            args = list(iter_rich_args(rich_repr_result))
+            class_name = obj.__class__.__name__
+
+            if args:
+                children = []
+                append = children.append
+
+                if reached_max_depth:
+                    if angular:
+                        node = Node(value_repr=f"<{class_name}...>")
+                    else:
+                        node = Node(value_repr=f"{class_name}(...)")
+                else:
+                    if angular:
+                        node = Node(
+                            open_brace=f"<{class_name} ",
+                            close_brace=">",
+                            children=children,
+                            last=root,
+                            separator=" ",
+                        )
+                    else:
+                        node = Node(
+                            open_brace=f"{class_name}(",
+                            close_brace=")",
+                            children=children,
+                            last=root,
+                        )
+                    for last, arg in loop_last(args):
+                        if _safe_isinstance(arg, tuple):
+                            key, child = arg
+                            child_node = _traverse(child, depth=depth + 1)
+                            child_node.last = last
+                            child_node.key_repr = key
+                            child_node.key_separator = "="
+                            append(child_node)
+                        else:
+                            child_node = _traverse(arg, depth=depth + 1)
+                            child_node.last = last
+                            append(child_node)
+            else:
+                node = Node(
+                    value_repr=f"<{class_name}>" if angular else f"{class_name}()",
+                    children=[],
+                    last=root,
+                )
+            pop_visited(obj_id)
+        elif _is_attr_object(obj) and not fake_attributes:
+            push_visited(obj_id)
+            children = []
+            append = children.append
+
+            attr_fields = _get_attr_fields(obj)
+            if attr_fields:
+                if reached_max_depth:
+                    node = Node(value_repr=f"{obj.__class__.__name__}(...)")
+                else:
+                    node = Node(
+                        open_brace=f"{obj.__class__.__name__}(",
+                        close_brace=")",
+                        children=children,
+                        last=root,
+                    )
+
+                    def iter_attrs() -> (
+                        Iterable[Tuple[str, Any, Optional[Callable[[Any], str]]]]
+                    ):
+                        """Iterate over attr fields and values."""
+                        for attr in attr_fields:
+                            if attr.repr:
+                                try:
+                                    value = getattr(obj, attr.name)
+                                except Exception as error:
+                                    # Can happen, albeit rarely
+                                    yield (attr.name, error, None)
+                                else:
+                                    yield (
+                                        attr.name,
+                                        value,
+                                        attr.repr if callable(attr.repr) else None,
+                                    )
+
+                    for last, (name, value, repr_callable) in loop_last(iter_attrs()):
+                        if repr_callable:
+                            child_node = Node(value_repr=str(repr_callable(value)))
+                        else:
+                            child_node = _traverse(value, depth=depth + 1)
+                        child_node.last = last
+                        child_node.key_repr = name
+                        child_node.key_separator = "="
+                        append(child_node)
+            else:
+                node = Node(
+                    value_repr=f"{obj.__class__.__name__}()", children=[], last=root
+                )
+            pop_visited(obj_id)
+        elif (
+            is_dataclass(obj)
+            and not _safe_isinstance(obj, type)
+            and not fake_attributes
+            and _is_dataclass_repr(obj)
+        ):
+            push_visited(obj_id)
+            children = []
+            append = children.append
+            if reached_max_depth:
+                node = Node(value_repr=f"{obj.__class__.__name__}(...)")
+            else:
+                node = Node(
+                    open_brace=f"{obj.__class__.__name__}(",
+                    close_brace=")",
+                    children=children,
+                    last=root,
+                    empty=f"{obj.__class__.__name__}()",
+                )
+
+                for last, field in loop_last(
+                    field
+                    for field in fields(obj)
+                    if field.repr and hasattr(obj, field.name)
+                ):
+                    child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
+                    child_node.key_repr = field.name
+                    child_node.last = last
+                    child_node.key_separator = "="
+                    append(child_node)
+
+            pop_visited(obj_id)
+        elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):
+            push_visited(obj_id)
+            class_name = obj.__class__.__name__
+            if reached_max_depth:
+                # If we've reached the max depth, we still show the class name, but not its contents
+                node = Node(
+                    value_repr=f"{class_name}(...)",
+                )
+            else:
+                children = []
+                append = children.append
+                node = Node(
+                    open_brace=f"{class_name}(",
+                    close_brace=")",
+                    children=children,
+                    empty=f"{class_name}()",
+                )
+                for last, (key, value) in loop_last(obj._asdict().items()):
+                    child_node = _traverse(value, depth=depth + 1)
+                    child_node.key_repr = key
+                    child_node.last = last
+                    child_node.key_separator = "="
+                    append(child_node)
+            pop_visited(obj_id)
+        elif _safe_isinstance(obj, _CONTAINERS):
+            for container_type in _CONTAINERS:
+                if _safe_isinstance(obj, container_type):
+                    obj_type = container_type
+                    break
+
+            push_visited(obj_id)
+
+            open_brace, close_brace, empty = _BRACES[obj_type](obj)
+
+            if reached_max_depth:
+                node = Node(value_repr=f"{open_brace}...{close_brace}")
+            elif obj_type.__repr__ != type(obj).__repr__:
+                node = Node(value_repr=to_repr(obj), last=root)
+            elif obj:
+                children = []
+                node = Node(
+                    open_brace=open_brace,
+                    close_brace=close_brace,
+                    children=children,
+                    last=root,
+                )
+                append = children.append
+                num_items = len(obj)
+                last_item_index = num_items - 1
+
+                if _safe_isinstance(obj, _MAPPING_CONTAINERS):
+                    iter_items = iter(obj.items())
+                    if max_length is not None:
+                        iter_items = islice(iter_items, max_length)
+                    for index, (key, child) in enumerate(iter_items):
+                        child_node = _traverse(child, depth=depth + 1)
+                        child_node.key_repr = to_repr(key)
+                        child_node.last = index == last_item_index
+                        append(child_node)
+                else:
+                    iter_values = iter(obj)
+                    if max_length is not None:
+                        iter_values = islice(iter_values, max_length)
+                    for index, child in enumerate(iter_values):
+                        child_node = _traverse(child, depth=depth + 1)
+                        child_node.last = index == last_item_index
+                        append(child_node)
+                if max_length is not None and num_items > max_length:
+                    append(Node(value_repr=f"... +{num_items - max_length}", last=True))
+            else:
+                node = Node(empty=empty, children=[], last=root)
+
+            pop_visited(obj_id)
+        else:
+            node = Node(value_repr=to_repr(obj), last=root)
+        node.is_tuple = type(obj) == tuple
+        node.is_namedtuple = _is_namedtuple(obj)
+        return node
+
+    node = _traverse(_object, root=True)
+    return node
+
+
+def pretty_repr(
+    _object: Any,
+    *,
+    max_width: int = 80,
+    indent_size: int = 4,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+    expand_all: bool = False,
+) -> str:
+    """Prettify repr string by expanding on to new lines to fit within a given width.
+
+    Args:
+        _object (Any): Object to repr.
+        max_width (int, optional): Desired maximum width of repr string. Defaults to 80.
+        indent_size (int, optional): Number of spaces to indent. Defaults to 4.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
+            Defaults to None.
+        max_depth (int, optional): Maximum depth of nested data structure, or None for no depth.
+            Defaults to None.
+        expand_all (bool, optional): Expand all containers regardless of available width. Defaults to False.
+
+    Returns:
+        str: A possibly multi-line representation of the object.
+    """
+
+    if _safe_isinstance(_object, Node):
+        node = _object
+    else:
+        node = traverse(
+            _object, max_length=max_length, max_string=max_string, max_depth=max_depth
+        )
+    repr_str: str = node.render(
+        max_width=max_width, indent_size=indent_size, expand_all=expand_all
+    )
+    return repr_str
+
+
+def pprint(
+    _object: Any,
+    *,
+    console: Optional["Console"] = None,
+    indent_guides: bool = True,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+    expand_all: bool = False,
+) -> None:
+    """A convenience function for pretty printing.
+
+    Args:
+        _object (Any): Object to pretty print.
+        console (Console, optional): Console instance, or None to use default. Defaults to None.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of strings before truncating, or None to disable. Defaults to None.
+        max_depth (int, optional): Maximum depth for nested data structures, or None for unlimited depth. Defaults to None.
+        indent_guides (bool, optional): Enable indentation guides. Defaults to True.
+        expand_all (bool, optional): Expand all containers. Defaults to False.
+    """
+    _console = get_console() if console is None else console
+    _console.print(
+        Pretty(
+            _object,
+            max_length=max_length,
+            max_string=max_string,
+            max_depth=max_depth,
+            indent_guides=indent_guides,
+            expand_all=expand_all,
+            overflow="ignore",
+        ),
+        soft_wrap=True,
+    )
+
+
+if __name__ == "__main__":  # pragma: no cover
+
+    class BrokenRepr:
+        def __repr__(self) -> str:
+            1 / 0
+            return "this will fail"
+
+    from typing import NamedTuple
+
+    class StockKeepingUnit(NamedTuple):
+        name: str
+        description: str
+        price: float
+        category: str
+        reviews: List[str]
+
+    d = defaultdict(int)
+    d["foo"] = 5
+    data = {
+        "foo": [
+            1,
+            "Hello World!",
+            100.123,
+            323.232,
+            432324.0,
+            {5, 6, 7, (1, 2, 3, 4), 8},
+        ],
+        "bar": frozenset({1, 2, 3}),
+        "defaultdict": defaultdict(
+            list, {"crumble": ["apple", "rhubarb", "butter", "sugar", "flour"]}
+        ),
+        "counter": Counter(
+            [
+                "apple",
+                "orange",
+                "pear",
+                "kumquat",
+                "kumquat",
+                "durian" * 100,
+            ]
+        ),
+        "atomic": (False, True, None),
+        "namedtuple": StockKeepingUnit(
+            "Sparkling British Spring Water",
+            "Carbonated spring water",
+            0.9,
+            "water",
+            ["its amazing!", "its terrible!"],
+        ),
+        "Broken": BrokenRepr(),
+    }
+    data["foo"].append(data)  # type: ignore[attr-defined]
+
+    from rich import print
+
+    print(Pretty(data, indent_guides=True, max_string=20))
+
+    class Thing:
+        def __repr__(self) -> str:
+            return "Hello\x1b[38;5;239m World!"
+
+    print(Pretty(Thing()))
diff --git a/.venv/lib/python3.12/site-packages/rich/progress.py b/.venv/lib/python3.12/site-packages/rich/progress.py
new file mode 100644
index 0000000..c2de125
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/progress.py
@@ -0,0 +1,1716 @@
+from __future__ import annotations
+
+import io
+import typing
+import warnings
+from abc import ABC, abstractmethod
+from collections import deque
+from dataclasses import dataclass, field
+from datetime import timedelta
+from io import RawIOBase, UnsupportedOperation
+from math import ceil
+from mmap import mmap
+from operator import length_hint
+from os import PathLike, stat
+from threading import Event, RLock, Thread
+from types import TracebackType
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    BinaryIO,
+    Callable,
+    ContextManager,
+    Deque,
+    Dict,
+    Generic,
+    Iterable,
+    List,
+    Literal,
+    NamedTuple,
+    NewType,
+    Optional,
+    TextIO,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+
+if TYPE_CHECKING:
+    # Can be replaced with `from typing import Self` in Python 3.11+
+    from typing_extensions import Self  # pragma: no cover
+
+from . import filesize, get_console
+from .console import Console, Group, JustifyMethod, RenderableType
+from .highlighter import Highlighter
+from .jupyter import JupyterMixin
+from .live import Live
+from .progress_bar import ProgressBar
+from .spinner import Spinner
+from .style import StyleType
+from .table import Column, Table
+from .text import Text, TextType
+
+TaskID = NewType("TaskID", int)
+
+ProgressType = TypeVar("ProgressType")
+
+GetTimeCallable = Callable[[], float]
+
+
+_I = typing.TypeVar("_I", TextIO, BinaryIO)
+
+
+class _TrackThread(Thread):
+    """A thread to periodically update progress."""
+
+    def __init__(self, progress: "Progress", task_id: "TaskID", update_period: float):
+        self.progress = progress
+        self.task_id = task_id
+        self.update_period = update_period
+        self.done = Event()
+
+        self.completed = 0
+        super().__init__(daemon=True)
+
+    def run(self) -> None:
+        task_id = self.task_id
+        advance = self.progress.advance
+        update_period = self.update_period
+        last_completed = 0
+        wait = self.done.wait
+        while not wait(update_period) and self.progress.live.is_started:
+            completed = self.completed
+            if last_completed != completed:
+                advance(task_id, completed - last_completed)
+                last_completed = completed
+
+        self.progress.update(self.task_id, completed=self.completed, refresh=True)
+
+    def __enter__(self) -> "_TrackThread":
+        self.start()
+        return self
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.done.set()
+        self.join()
+
+
+def track(
+    sequence: Iterable[ProgressType],
+    description: str = "Working...",
+    total: Optional[float] = None,
+    completed: int = 0,
+    auto_refresh: bool = True,
+    console: Optional[Console] = None,
+    transient: bool = False,
+    get_time: Optional[Callable[[], float]] = None,
+    refresh_per_second: float = 10,
+    style: StyleType = "bar.back",
+    complete_style: StyleType = "bar.complete",
+    finished_style: StyleType = "bar.finished",
+    pulse_style: StyleType = "bar.pulse",
+    update_period: float = 0.1,
+    disable: bool = False,
+    show_speed: bool = True,
+) -> Iterable[ProgressType]:
+    """Track progress by iterating over a sequence.
+
+    You can also track progress of an iterable, which might require that you additionally specify ``total``.
+
+    Args:
+        sequence (Iterable[ProgressType]): Values you wish to iterate over and track progress.
+        description (str, optional): Description of task show next to progress bar. Defaults to "Working".
+        total: (float, optional): Total number of steps. Default is len(sequence).
+        completed (int, optional): Number of steps completed so far. Defaults to 0.
+        auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
+        transient: (bool, optional): Clear the progress on exit. Defaults to False.
+        console (Console, optional): Console to write to. Default creates internal Console instance.
+        refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10.
+        style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+        complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+        finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
+        pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+        update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
+        disable (bool, optional): Disable display of progress.
+        show_speed (bool, optional): Show speed if total isn't known. Defaults to True.
+    Returns:
+        Iterable[ProgressType]: An iterable of the values in the sequence.
+
+    """
+
+    columns: List["ProgressColumn"] = (
+        [TextColumn("[progress.description]{task.description}")] if description else []
+    )
+    columns.extend(
+        (
+            BarColumn(
+                style=style,
+                complete_style=complete_style,
+                finished_style=finished_style,
+                pulse_style=pulse_style,
+            ),
+            TaskProgressColumn(show_speed=show_speed),
+            TimeRemainingColumn(elapsed_when_finished=True),
+        )
+    )
+    progress = Progress(
+        *columns,
+        auto_refresh=auto_refresh,
+        console=console,
+        transient=transient,
+        get_time=get_time,
+        refresh_per_second=refresh_per_second or 10,
+        disable=disable,
+    )
+
+    with progress:
+        yield from progress.track(
+            sequence,
+            total=total,
+            completed=completed,
+            description=description,
+            update_period=update_period,
+        )
+
+
+class _Reader(RawIOBase, BinaryIO):
+    """A reader that tracks progress while it's being read from."""
+
+    def __init__(
+        self,
+        handle: BinaryIO,
+        progress: "Progress",
+        task: TaskID,
+        close_handle: bool = True,
+    ) -> None:
+        self.handle = handle
+        self.progress = progress
+        self.task = task
+        self.close_handle = close_handle
+        self._closed = False
+
+    def __enter__(self) -> "_Reader":
+        self.handle.__enter__()
+        return self
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.close()
+
+    def __iter__(self) -> BinaryIO:
+        return self
+
+    def __next__(self) -> bytes:
+        line = next(self.handle)
+        self.progress.advance(self.task, advance=len(line))
+        return line
+
+    @property
+    def closed(self) -> bool:
+        return self._closed
+
+    def fileno(self) -> int:
+        return self.handle.fileno()
+
+    def isatty(self) -> bool:
+        return self.handle.isatty()
+
+    @property
+    def mode(self) -> str:
+        return self.handle.mode
+
+    @property
+    def name(self) -> str:
+        return self.handle.name
+
+    def readable(self) -> bool:
+        return self.handle.readable()
+
+    def seekable(self) -> bool:
+        return self.handle.seekable()
+
+    def writable(self) -> bool:
+        return False
+
+    def read(self, size: int = -1) -> bytes:
+        block = self.handle.read(size)
+        self.progress.advance(self.task, advance=len(block))
+        return block
+
+    def readinto(self, b: Union[bytearray, memoryview, mmap]):  # type: ignore[no-untyped-def, override]
+        n = self.handle.readinto(b)  # type: ignore[attr-defined]
+        self.progress.advance(self.task, advance=n)
+        return n
+
+    def readline(self, size: int = -1) -> bytes:  # type: ignore[override]
+        line = self.handle.readline(size)
+        self.progress.advance(self.task, advance=len(line))
+        return line
+
+    def readlines(self, hint: int = -1) -> List[bytes]:
+        lines = self.handle.readlines(hint)
+        self.progress.advance(self.task, advance=sum(map(len, lines)))
+        return lines
+
+    def close(self) -> None:
+        if self.close_handle:
+            self.handle.close()
+        self._closed = True
+
+    def seek(self, offset: int, whence: int = 0) -> int:
+        pos = self.handle.seek(offset, whence)
+        self.progress.update(self.task, completed=pos)
+        return pos
+
+    def tell(self) -> int:
+        return self.handle.tell()
+
+    def write(self, s: Any) -> int:
+        raise UnsupportedOperation("write")
+
+    def writelines(self, lines: Iterable[Any]) -> None:
+        raise UnsupportedOperation("writelines")
+
+
+class _ReadContext(ContextManager[_I], Generic[_I]):
+    """A utility class to handle a context for both a reader and a progress."""
+
+    def __init__(self, progress: "Progress", reader: _I) -> None:
+        self.progress = progress
+        self.reader: _I = reader
+
+    def __enter__(self) -> _I:
+        self.progress.start()
+        return self.reader.__enter__()
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.progress.stop()
+        self.reader.__exit__(exc_type, exc_val, exc_tb)
+
+
+def wrap_file(
+    file: BinaryIO,
+    total: int,
+    *,
+    description: str = "Reading...",
+    auto_refresh: bool = True,
+    console: Optional[Console] = None,
+    transient: bool = False,
+    get_time: Optional[Callable[[], float]] = None,
+    refresh_per_second: float = 10,
+    style: StyleType = "bar.back",
+    complete_style: StyleType = "bar.complete",
+    finished_style: StyleType = "bar.finished",
+    pulse_style: StyleType = "bar.pulse",
+    disable: bool = False,
+) -> ContextManager[BinaryIO]:
+    """Read bytes from a file while tracking progress.
+
+    Args:
+        file (Union[str, PathLike[str], BinaryIO]): The path to the file to read, or a file-like object in binary mode.
+        total (int): Total number of bytes to read.
+        description (str, optional): Description of task show next to progress bar. Defaults to "Reading".
+        auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
+        transient: (bool, optional): Clear the progress on exit. Defaults to False.
+        console (Console, optional): Console to write to. Default creates internal Console instance.
+        refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10.
+        style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+        complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+        finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
+        pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+        disable (bool, optional): Disable display of progress.
+    Returns:
+        ContextManager[BinaryIO]: A context manager yielding a progress reader.
+
+    """
+
+    columns: List["ProgressColumn"] = (
+        [TextColumn("[progress.description]{task.description}")] if description else []
+    )
+    columns.extend(
+        (
+            BarColumn(
+                style=style,
+                complete_style=complete_style,
+                finished_style=finished_style,
+                pulse_style=pulse_style,
+            ),
+            DownloadColumn(),
+            TimeRemainingColumn(),
+        )
+    )
+    progress = Progress(
+        *columns,
+        auto_refresh=auto_refresh,
+        console=console,
+        transient=transient,
+        get_time=get_time,
+        refresh_per_second=refresh_per_second or 10,
+        disable=disable,
+    )
+
+    reader = progress.wrap_file(file, total=total, description=description)
+    return _ReadContext(progress, reader)
+
+
+@typing.overload
+def open(
+    file: Union[str, "PathLike[str]", bytes],
+    mode: Union[Literal["rt"], Literal["r"]],
+    buffering: int = -1,
+    encoding: Optional[str] = None,
+    errors: Optional[str] = None,
+    newline: Optional[str] = None,
+    *,
+    total: Optional[int] = None,
+    description: str = "Reading...",
+    auto_refresh: bool = True,
+    console: Optional[Console] = None,
+    transient: bool = False,
+    get_time: Optional[Callable[[], float]] = None,
+    refresh_per_second: float = 10,
+    style: StyleType = "bar.back",
+    complete_style: StyleType = "bar.complete",
+    finished_style: StyleType = "bar.finished",
+    pulse_style: StyleType = "bar.pulse",
+    disable: bool = False,
+) -> ContextManager[TextIO]:
+    pass
+
+
+@typing.overload
+def open(
+    file: Union[str, "PathLike[str]", bytes],
+    mode: Literal["rb"],
+    buffering: int = -1,
+    encoding: Optional[str] = None,
+    errors: Optional[str] = None,
+    newline: Optional[str] = None,
+    *,
+    total: Optional[int] = None,
+    description: str = "Reading...",
+    auto_refresh: bool = True,
+    console: Optional[Console] = None,
+    transient: bool = False,
+    get_time: Optional[Callable[[], float]] = None,
+    refresh_per_second: float = 10,
+    style: StyleType = "bar.back",
+    complete_style: StyleType = "bar.complete",
+    finished_style: StyleType = "bar.finished",
+    pulse_style: StyleType = "bar.pulse",
+    disable: bool = False,
+) -> ContextManager[BinaryIO]:
+    pass
+
+
+def open(
+    file: Union[str, "PathLike[str]", bytes],
+    mode: Union[Literal["rb"], Literal["rt"], Literal["r"]] = "r",
+    buffering: int = -1,
+    encoding: Optional[str] = None,
+    errors: Optional[str] = None,
+    newline: Optional[str] = None,
+    *,
+    total: Optional[int] = None,
+    description: str = "Reading...",
+    auto_refresh: bool = True,
+    console: Optional[Console] = None,
+    transient: bool = False,
+    get_time: Optional[Callable[[], float]] = None,
+    refresh_per_second: float = 10,
+    style: StyleType = "bar.back",
+    complete_style: StyleType = "bar.complete",
+    finished_style: StyleType = "bar.finished",
+    pulse_style: StyleType = "bar.pulse",
+    disable: bool = False,
+) -> Union[ContextManager[BinaryIO], ContextManager[TextIO]]:
+    """Read bytes from a file while tracking progress.
+
+    Args:
+        path (Union[str, PathLike[str], BinaryIO]): The path to the file to read, or a file-like object in binary mode.
+        mode (str): The mode to use to open the file. Only supports "r", "rb" or "rt".
+        buffering (int): The buffering strategy to use, see :func:`io.open`.
+        encoding (str, optional): The encoding to use when reading in text mode, see :func:`io.open`.
+        errors (str, optional): The error handling strategy for decoding errors, see :func:`io.open`.
+        newline (str, optional): The strategy for handling newlines in text mode, see :func:`io.open`
+        total: (int, optional): Total number of bytes to read. Must be provided if reading from a file handle. Default for a path is os.stat(file).st_size.
+        description (str, optional): Description of task show next to progress bar. Defaults to "Reading".
+        auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
+        transient: (bool, optional): Clear the progress on exit. Defaults to False.
+        console (Console, optional): Console to write to. Default creates internal Console instance.
+        refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10.
+        style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+        complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+        finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
+        pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+        disable (bool, optional): Disable display of progress.
+        encoding (str, optional): The encoding to use when reading in text mode.
+
+    Returns:
+        ContextManager[BinaryIO]: A context manager yielding a progress reader.
+
+    """
+
+    columns: List["ProgressColumn"] = (
+        [TextColumn("[progress.description]{task.description}")] if description else []
+    )
+    columns.extend(
+        (
+            BarColumn(
+                style=style,
+                complete_style=complete_style,
+                finished_style=finished_style,
+                pulse_style=pulse_style,
+            ),
+            DownloadColumn(),
+            TimeRemainingColumn(),
+        )
+    )
+    progress = Progress(
+        *columns,
+        auto_refresh=auto_refresh,
+        console=console,
+        transient=transient,
+        get_time=get_time,
+        refresh_per_second=refresh_per_second or 10,
+        disable=disable,
+    )
+
+    reader = progress.open(
+        file,
+        mode=mode,
+        buffering=buffering,
+        encoding=encoding,
+        errors=errors,
+        newline=newline,
+        total=total,
+        description=description,
+    )
+    return _ReadContext(progress, reader)  # type: ignore[return-value, type-var]
+
+
+class ProgressColumn(ABC):
+    """Base class for a widget to use in progress display."""
+
+    max_refresh: Optional[float] = None
+
+    def __init__(self, table_column: Optional[Column] = None) -> None:
+        self._table_column = table_column
+        self._renderable_cache: Dict[TaskID, Tuple[float, RenderableType]] = {}
+        self._update_time: Optional[float] = None
+
+    def get_table_column(self) -> Column:
+        """Get a table column, used to build tasks table."""
+        return self._table_column or Column()
+
+    def __call__(self, task: "Task") -> RenderableType:
+        """Called by the Progress object to return a renderable for the given task.
+
+        Args:
+            task (Task): An object containing information regarding the task.
+
+        Returns:
+            RenderableType: Anything renderable (including str).
+        """
+        current_time = task.get_time()
+        if self.max_refresh is not None and not task.completed:
+            try:
+                timestamp, renderable = self._renderable_cache[task.id]
+            except KeyError:
+                pass
+            else:
+                if timestamp + self.max_refresh > current_time:
+                    return renderable
+
+        renderable = self.render(task)
+        self._renderable_cache[task.id] = (current_time, renderable)
+        return renderable
+
+    @abstractmethod
+    def render(self, task: "Task") -> RenderableType:
+        """Should return a renderable object."""
+
+
+class RenderableColumn(ProgressColumn):
+    """A column to insert an arbitrary column.
+
+    Args:
+        renderable (RenderableType, optional): Any renderable. Defaults to empty string.
+    """
+
+    def __init__(
+        self, renderable: RenderableType = "", *, table_column: Optional[Column] = None
+    ):
+        self.renderable = renderable
+        super().__init__(table_column=table_column)
+
+    def render(self, task: "Task") -> RenderableType:
+        return self.renderable
+
+
+class SpinnerColumn(ProgressColumn):
+    """A column with a 'spinner' animation.
+
+    Args:
+        spinner_name (str, optional): Name of spinner animation. Defaults to "dots".
+        style (StyleType, optional): Style of spinner. Defaults to "progress.spinner".
+        speed (float, optional): Speed factor of spinner. Defaults to 1.0.
+        finished_text (TextType, optional): Text used when task is finished. Defaults to " ".
+    """
+
+    def __init__(
+        self,
+        spinner_name: str = "dots",
+        style: Optional[StyleType] = "progress.spinner",
+        speed: float = 1.0,
+        finished_text: TextType = " ",
+        table_column: Optional[Column] = None,
+    ):
+        self.spinner = Spinner(spinner_name, style=style, speed=speed)
+        self.finished_text = (
+            Text.from_markup(finished_text)
+            if isinstance(finished_text, str)
+            else finished_text
+        )
+        super().__init__(table_column=table_column)
+
+    def set_spinner(
+        self,
+        spinner_name: str,
+        spinner_style: Optional[StyleType] = "progress.spinner",
+        speed: float = 1.0,
+    ) -> None:
+        """Set a new spinner.
+
+        Args:
+            spinner_name (str): Spinner name, see python -m rich.spinner.
+            spinner_style (Optional[StyleType], optional): Spinner style. Defaults to "progress.spinner".
+            speed (float, optional): Speed factor of spinner. Defaults to 1.0.
+        """
+        self.spinner = Spinner(spinner_name, style=spinner_style, speed=speed)
+
+    def render(self, task: "Task") -> RenderableType:
+        text = (
+            self.finished_text
+            if task.finished
+            else self.spinner.render(task.get_time())
+        )
+        return text
+
+
+class TextColumn(ProgressColumn):
+    """A column containing text."""
+
+    def __init__(
+        self,
+        text_format: str,
+        style: StyleType = "none",
+        justify: JustifyMethod = "left",
+        markup: bool = True,
+        highlighter: Optional[Highlighter] = None,
+        table_column: Optional[Column] = None,
+    ) -> None:
+        self.text_format = text_format
+        self.justify: JustifyMethod = justify
+        self.style = style
+        self.markup = markup
+        self.highlighter = highlighter
+        super().__init__(table_column=table_column or Column(no_wrap=True))
+
+    def render(self, task: "Task") -> Text:
+        _text = self.text_format.format(task=task)
+        if self.markup:
+            text = Text.from_markup(_text, style=self.style, justify=self.justify)
+        else:
+            text = Text(_text, style=self.style, justify=self.justify)
+        if self.highlighter:
+            self.highlighter.highlight(text)
+        return text
+
+
+class BarColumn(ProgressColumn):
+    """Renders a visual progress bar.
+
+    Args:
+        bar_width (Optional[int], optional): Width of bar or None for full width. Defaults to 40.
+        style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+        complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+        finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
+        pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+    """
+
+    def __init__(
+        self,
+        bar_width: Optional[int] = 40,
+        style: StyleType = "bar.back",
+        complete_style: StyleType = "bar.complete",
+        finished_style: StyleType = "bar.finished",
+        pulse_style: StyleType = "bar.pulse",
+        table_column: Optional[Column] = None,
+    ) -> None:
+        self.bar_width = bar_width
+        self.style = style
+        self.complete_style = complete_style
+        self.finished_style = finished_style
+        self.pulse_style = pulse_style
+        super().__init__(table_column=table_column)
+
+    def render(self, task: "Task") -> ProgressBar:
+        """Gets a progress bar widget for a task."""
+        return ProgressBar(
+            total=max(0, task.total) if task.total is not None else None,
+            completed=max(0, task.completed),
+            width=None if self.bar_width is None else max(1, self.bar_width),
+            pulse=not task.started,
+            animation_time=task.get_time(),
+            style=self.style,
+            complete_style=self.complete_style,
+            finished_style=self.finished_style,
+            pulse_style=self.pulse_style,
+        )
+
+
+class TimeElapsedColumn(ProgressColumn):
+    """Renders time elapsed."""
+
+    def render(self, task: "Task") -> Text:
+        """Show time elapsed."""
+        elapsed = task.finished_time if task.finished else task.elapsed
+        if elapsed is None:
+            return Text("-:--:--", style="progress.elapsed")
+        delta = timedelta(seconds=max(0, int(elapsed)))
+        return Text(str(delta), style="progress.elapsed")
+
+
+class TaskProgressColumn(TextColumn):
+    """Show task progress as a percentage.
+
+    Args:
+        text_format (str, optional): Format for percentage display. Defaults to "[progress.percentage]{task.percentage:>3.0f}%".
+        text_format_no_percentage (str, optional): Format if percentage is unknown. Defaults to "".
+        style (StyleType, optional): Style of output. Defaults to "none".
+        justify (JustifyMethod, optional): Text justification. Defaults to "left".
+        markup (bool, optional): Enable markup. Defaults to True.
+        highlighter (Optional[Highlighter], optional): Highlighter to apply to output. Defaults to None.
+        table_column (Optional[Column], optional): Table Column to use. Defaults to None.
+        show_speed (bool, optional): Show speed if total is unknown. Defaults to False.
+    """
+
+    def __init__(
+        self,
+        text_format: str = "[progress.percentage]{task.percentage:>3.0f}%",
+        text_format_no_percentage: str = "",
+        style: StyleType = "none",
+        justify: JustifyMethod = "left",
+        markup: bool = True,
+        highlighter: Optional[Highlighter] = None,
+        table_column: Optional[Column] = None,
+        show_speed: bool = False,
+    ) -> None:
+        self.text_format_no_percentage = text_format_no_percentage
+        self.show_speed = show_speed
+        super().__init__(
+            text_format=text_format,
+            style=style,
+            justify=justify,
+            markup=markup,
+            highlighter=highlighter,
+            table_column=table_column,
+        )
+
+    @classmethod
+    def render_speed(cls, speed: Optional[float]) -> Text:
+        """Render the speed in iterations per second.
+
+        Args:
+            task (Task): A Task object.
+
+        Returns:
+            Text: Text object containing the task speed.
+        """
+        if speed is None:
+            return Text("", style="progress.percentage")
+        unit, suffix = filesize.pick_unit_and_suffix(
+            int(speed),
+            ["", "×10³", "×10⁶", "×10⁹", "×10¹²"],
+            1000,
+        )
+        data_speed = speed / unit
+        return Text(f"{data_speed:.1f}{suffix} it/s", style="progress.percentage")
+
+    def render(self, task: "Task") -> Text:
+        if task.total is None and self.show_speed:
+            return self.render_speed(task.finished_speed or task.speed)
+        text_format = (
+            self.text_format_no_percentage if task.total is None else self.text_format
+        )
+        _text = text_format.format(task=task)
+        if self.markup:
+            text = Text.from_markup(_text, style=self.style, justify=self.justify)
+        else:
+            text = Text(_text, style=self.style, justify=self.justify)
+        if self.highlighter:
+            self.highlighter.highlight(text)
+        return text
+
+
+class TimeRemainingColumn(ProgressColumn):
+    """Renders estimated time remaining.
+
+    Args:
+        compact (bool, optional): Render MM:SS when time remaining is less than an hour. Defaults to False.
+        elapsed_when_finished (bool, optional): Render time elapsed when the task is finished. Defaults to False.
+    """
+
+    # Only refresh twice a second to prevent jitter
+    max_refresh = 0.5
+
+    def __init__(
+        self,
+        compact: bool = False,
+        elapsed_when_finished: bool = False,
+        table_column: Optional[Column] = None,
+    ):
+        self.compact = compact
+        self.elapsed_when_finished = elapsed_when_finished
+        super().__init__(table_column=table_column)
+
+    def render(self, task: "Task") -> Text:
+        """Show time remaining."""
+        if self.elapsed_when_finished and task.finished:
+            task_time = task.finished_time
+            style = "progress.elapsed"
+        else:
+            task_time = task.time_remaining
+            style = "progress.remaining"
+
+        if task.total is None:
+            return Text("", style=style)
+
+        if task_time is None:
+            return Text("--:--" if self.compact else "-:--:--", style=style)
+
+        # Based on https://github.com/tqdm/tqdm/blob/master/tqdm/std.py
+        minutes, seconds = divmod(int(task_time), 60)
+        hours, minutes = divmod(minutes, 60)
+
+        if self.compact and not hours:
+            formatted = f"{minutes:02d}:{seconds:02d}"
+        else:
+            formatted = f"{hours:d}:{minutes:02d}:{seconds:02d}"
+
+        return Text(formatted, style=style)
+
+
+class FileSizeColumn(ProgressColumn):
+    """Renders completed filesize."""
+
+    def render(self, task: "Task") -> Text:
+        """Show data completed."""
+        data_size = filesize.decimal(int(task.completed))
+        return Text(data_size, style="progress.filesize")
+
+
+class TotalFileSizeColumn(ProgressColumn):
+    """Renders total filesize."""
+
+    def render(self, task: "Task") -> Text:
+        """Show data completed."""
+        data_size = filesize.decimal(int(task.total)) if task.total is not None else ""
+        return Text(data_size, style="progress.filesize.total")
+
+
+class MofNCompleteColumn(ProgressColumn):
+    """Renders completed count/total, e.g. '  10/1000'.
+
+    Best for bounded tasks with int quantities.
+
+    Space pads the completed count so that progress length does not change as task progresses
+    past powers of 10.
+
+    Args:
+        separator (str, optional): Text to separate completed and total values. Defaults to "/".
+    """
+
+    def __init__(self, separator: str = "/", table_column: Optional[Column] = None):
+        self.separator = separator
+        super().__init__(table_column=table_column)
+
+    def render(self, task: "Task") -> Text:
+        """Show completed/total."""
+        completed = int(task.completed)
+        total = int(task.total) if task.total is not None else "?"
+        total_width = len(str(total))
+        return Text(
+            f"{completed:{total_width}d}{self.separator}{total}",
+            style="progress.download",
+        )
+
+
+class DownloadColumn(ProgressColumn):
+    """Renders file size downloaded and total, e.g. '0.5/2.3 GB'.
+
+    Args:
+        binary_units (bool, optional): Use binary units, KiB, MiB etc. Defaults to False.
+    """
+
+    def __init__(
+        self, binary_units: bool = False, table_column: Optional[Column] = None
+    ) -> None:
+        self.binary_units = binary_units
+        super().__init__(table_column=table_column)
+
+    def render(self, task: "Task") -> Text:
+        """Calculate common unit for completed and total."""
+        completed = int(task.completed)
+
+        unit_and_suffix_calculation_base = (
+            int(task.total) if task.total is not None else completed
+        )
+        if self.binary_units:
+            unit, suffix = filesize.pick_unit_and_suffix(
+                unit_and_suffix_calculation_base,
+                ["bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"],
+                1024,
+            )
+        else:
+            unit, suffix = filesize.pick_unit_and_suffix(
+                unit_and_suffix_calculation_base,
+                ["bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
+                1000,
+            )
+        precision = 0 if unit == 1 else 1
+
+        completed_ratio = completed / unit
+        completed_str = f"{completed_ratio:,.{precision}f}"
+
+        if task.total is not None:
+            total = int(task.total)
+            total_ratio = total / unit
+            total_str = f"{total_ratio:,.{precision}f}"
+        else:
+            total_str = "?"
+
+        download_status = f"{completed_str}/{total_str} {suffix}"
+        download_text = Text(download_status, style="progress.download")
+        return download_text
+
+
+class TransferSpeedColumn(ProgressColumn):
+    """Renders human readable transfer speed."""
+
+    def render(self, task: "Task") -> Text:
+        """Show data transfer speed."""
+        speed = task.finished_speed or task.speed
+        if speed is None:
+            return Text("?", style="progress.data.speed")
+        data_speed = filesize.decimal(int(speed))
+        return Text(f"{data_speed}/s", style="progress.data.speed")
+
+
+class ProgressSample(NamedTuple):
+    """Sample of progress for a given time."""
+
+    timestamp: float
+    """Timestamp of sample."""
+    completed: float
+    """Number of steps completed."""
+
+
+@dataclass
+class Task:
+    """Information regarding a progress task.
+
+    This object should be considered read-only outside of the :class:`~Progress` class.
+
+    """
+
+    id: TaskID
+    """Task ID associated with this task (used in Progress methods)."""
+
+    description: str
+    """str: Description of the task."""
+
+    total: Optional[float]
+    """Optional[float]: Total number of steps in this task."""
+
+    completed: float
+    """float: Number of steps completed"""
+
+    _get_time: GetTimeCallable
+    """Callable to get the current time."""
+
+    finished_time: Optional[float] = None
+    """float: Time task was finished."""
+
+    visible: bool = True
+    """bool: Indicates if this task is visible in the progress display."""
+
+    fields: Dict[str, Any] = field(default_factory=dict)
+    """dict: Arbitrary fields passed in via Progress.update."""
+
+    start_time: Optional[float] = field(default=None, init=False, repr=False)
+    """Optional[float]: Time this task was started, or None if not started."""
+
+    stop_time: Optional[float] = field(default=None, init=False, repr=False)
+    """Optional[float]: Time this task was stopped, or None if not stopped."""
+
+    finished_speed: Optional[float] = None
+    """Optional[float]: The last speed for a finished task."""
+
+    _progress: Deque[ProgressSample] = field(
+        default_factory=lambda: deque(maxlen=1000), init=False, repr=False
+    )
+
+    _lock: RLock = field(repr=False, default_factory=RLock)
+    """Thread lock."""
+
+    def get_time(self) -> float:
+        """float: Get the current time, in seconds."""
+        return self._get_time()
+
+    @property
+    def started(self) -> bool:
+        """bool: Check if the task as started."""
+        return self.start_time is not None
+
+    @property
+    def remaining(self) -> Optional[float]:
+        """Optional[float]: Get the number of steps remaining, if a non-None total was set."""
+        if self.total is None:
+            return None
+        return self.total - self.completed
+
+    @property
+    def elapsed(self) -> Optional[float]:
+        """Optional[float]: Time elapsed since task was started, or ``None`` if the task hasn't started."""
+        if self.start_time is None:
+            return None
+        if self.stop_time is not None:
+            return self.stop_time - self.start_time
+        return self.get_time() - self.start_time
+
+    @property
+    def finished(self) -> bool:
+        """Check if the task has finished."""
+        return self.finished_time is not None
+
+    @property
+    def percentage(self) -> float:
+        """float: Get progress of task as a percentage. If a None total was set, returns 0"""
+        if not self.total:
+            return 0.0
+        completed = (self.completed / self.total) * 100.0
+        completed = min(100.0, max(0.0, completed))
+        return completed
+
+    @property
+    def speed(self) -> Optional[float]:
+        """Optional[float]: Get the estimated speed in steps per second."""
+        if self.start_time is None:
+            return None
+        with self._lock:
+            progress = self._progress
+            if not progress:
+                return None
+            total_time = progress[-1].timestamp - progress[0].timestamp
+            if total_time == 0:
+                return None
+            iter_progress = iter(progress)
+            next(iter_progress)
+            total_completed = sum(sample.completed for sample in iter_progress)
+            speed = total_completed / total_time
+            return speed
+
+    @property
+    def time_remaining(self) -> Optional[float]:
+        """Optional[float]: Get estimated time to completion, or ``None`` if no data."""
+        if self.finished:
+            return 0.0
+        speed = self.speed
+        if not speed:
+            return None
+        remaining = self.remaining
+        if remaining is None:
+            return None
+        estimate = ceil(remaining / speed)
+        return estimate
+
+    def _reset(self) -> None:
+        """Reset progress."""
+        self._progress.clear()
+        self.finished_time = None
+        self.finished_speed = None
+
+
+class Progress(JupyterMixin):
+    """Renders an auto-updating progress bar(s).
+
+    Args:
+        console (Console, optional): Optional Console instance. Defaults to an internal Console instance writing to stdout.
+        auto_refresh (bool, optional): Enable auto refresh. If disabled, you will need to call `refresh()`.
+        refresh_per_second (float, optional): Number of times per second to refresh the progress information. Defaults to 10.
+        speed_estimate_period: (float, optional): Period (in seconds) used to calculate the speed estimate. Defaults to 30.
+        transient: (bool, optional): Clear the progress on exit. Defaults to False.
+        redirect_stdout: (bool, optional): Enable redirection of stdout, so ``print`` may be used. Defaults to True.
+        redirect_stderr: (bool, optional): Enable redirection of stderr. Defaults to True.
+        get_time: (Callable, optional): A callable that gets the current time, or None to use Console.get_time. Defaults to None.
+        disable (bool, optional): Disable progress display. Defaults to False
+        expand (bool, optional): Expand tasks table to fit width. Defaults to False.
+    """
+
+    def __init__(
+        self,
+        *columns: Union[str, ProgressColumn],
+        console: Optional[Console] = None,
+        auto_refresh: bool = True,
+        refresh_per_second: float = 10,
+        speed_estimate_period: float = 30.0,
+        transient: bool = False,
+        redirect_stdout: bool = True,
+        redirect_stderr: bool = True,
+        get_time: Optional[GetTimeCallable] = None,
+        disable: bool = False,
+        expand: bool = False,
+    ) -> None:
+        assert refresh_per_second > 0, "refresh_per_second must be > 0"
+        self._lock = RLock()
+        self.columns = columns or self.get_default_columns()
+        self.speed_estimate_period = speed_estimate_period
+
+        self.disable = disable
+        self.expand = expand
+        self._tasks: Dict[TaskID, Task] = {}
+        self._task_index: TaskID = TaskID(0)
+        self.live = Live(
+            console=console or get_console(),
+            auto_refresh=auto_refresh,
+            refresh_per_second=refresh_per_second,
+            transient=transient,
+            redirect_stdout=redirect_stdout,
+            redirect_stderr=redirect_stderr,
+            get_renderable=self.get_renderable,
+        )
+        self.get_time = get_time or self.console.get_time
+        self.print = self.console.print
+        self.log = self.console.log
+
+    @classmethod
+    def get_default_columns(cls) -> Tuple[ProgressColumn, ...]:
+        """Get the default columns used for a new Progress instance:
+           - a text column for the description (TextColumn)
+           - the bar itself (BarColumn)
+           - a text column showing completion percentage (TextColumn)
+           - an estimated-time-remaining column (TimeRemainingColumn)
+        If the Progress instance is created without passing a columns argument,
+        the default columns defined here will be used.
+
+        You can also create a Progress instance using custom columns before
+        and/or after the defaults, as in this example:
+
+            progress = Progress(
+                SpinnerColumn(),
+                *Progress.get_default_columns(),
+                "Elapsed:",
+                TimeElapsedColumn(),
+            )
+
+        This code shows the creation of a Progress display, containing
+        a spinner to the left, the default columns, and a labeled elapsed
+        time column.
+        """
+        return (
+            TextColumn("[progress.description]{task.description}"),
+            BarColumn(),
+            TaskProgressColumn(),
+            TimeRemainingColumn(),
+        )
+
+    @property
+    def console(self) -> Console:
+        return self.live.console
+
+    @property
+    def tasks(self) -> List[Task]:
+        """Get a list of Task instances."""
+        with self._lock:
+            return list(self._tasks.values())
+
+    @property
+    def task_ids(self) -> List[TaskID]:
+        """A list of task IDs."""
+        with self._lock:
+            return list(self._tasks.keys())
+
+    @property
+    def finished(self) -> bool:
+        """Check if all tasks have been completed."""
+        with self._lock:
+            if not self._tasks:
+                return True
+            return all(task.finished for task in self._tasks.values())
+
+    def start(self) -> None:
+        """Start the progress display."""
+        if not self.disable:
+            self.live.start(refresh=True)
+
+    def stop(self) -> None:
+        """Stop the progress display."""
+        if not self.disable:
+            self.live.stop()
+            if not self.console.is_interactive and not self.console.is_jupyter:
+                self.console.print()
+
+    def __enter__(self) -> Self:
+        self.start()
+        return self
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.stop()
+
+    def track(
+        self,
+        sequence: Iterable[ProgressType],
+        total: Optional[float] = None,
+        completed: int = 0,
+        task_id: Optional[TaskID] = None,
+        description: str = "Working...",
+        update_period: float = 0.1,
+    ) -> Iterable[ProgressType]:
+        """Track progress by iterating over a sequence.
+
+        You can also track progress of an iterable, which might require that you additionally specify ``total``.
+
+        Args:
+            sequence (Iterable[ProgressType]): Values you want to iterate over and track progress.
+            total: (float, optional): Total number of steps. Default is len(sequence).
+            completed (int, optional): Number of steps completed so far. Defaults to 0.
+            task_id: (TaskID): Task to track. Default is new task.
+            description: (str, optional): Description of task, if new task is created.
+            update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1.
+
+        Returns:
+            Iterable[ProgressType]: An iterable of values taken from the provided sequence.
+        """
+        if total is None:
+            total = float(length_hint(sequence)) or None
+
+        if task_id is None:
+            task_id = self.add_task(description, total=total, completed=completed)
+        else:
+            self.update(task_id, total=total, completed=completed)
+
+        if self.live.auto_refresh:
+            with _TrackThread(self, task_id, update_period) as track_thread:
+                for value in sequence:
+                    yield value
+                    track_thread.completed += 1
+        else:
+            advance = self.advance
+            refresh = self.refresh
+            for value in sequence:
+                yield value
+                advance(task_id, 1)
+                refresh()
+
+    def wrap_file(
+        self,
+        file: BinaryIO,
+        total: Optional[int] = None,
+        *,
+        task_id: Optional[TaskID] = None,
+        description: str = "Reading...",
+    ) -> BinaryIO:
+        """Track progress file reading from a binary file.
+
+        Args:
+            file (BinaryIO): A file-like object opened in binary mode.
+            total (int, optional): Total number of bytes to read. This must be provided unless a task with a total is also given.
+            task_id (TaskID): Task to track. Default is new task.
+            description (str, optional): Description of task, if new task is created.
+
+        Returns:
+            BinaryIO: A readable file-like object in binary mode.
+
+        Raises:
+            ValueError: When no total value can be extracted from the arguments or the task.
+        """
+        # attempt to recover the total from the task
+        total_bytes: Optional[float] = None
+        if total is not None:
+            total_bytes = total
+        elif task_id is not None:
+            with self._lock:
+                total_bytes = self._tasks[task_id].total
+        if total_bytes is None:
+            raise ValueError(
+                f"unable to get the total number of bytes, please specify 'total'"
+            )
+
+        # update total of task or create new task
+        if task_id is None:
+            task_id = self.add_task(description, total=total_bytes)
+        else:
+            self.update(task_id, total=total_bytes)
+
+        return _Reader(file, self, task_id, close_handle=False)
+
+    @typing.overload
+    def open(
+        self,
+        file: Union[str, "PathLike[str]", bytes],
+        mode: Literal["rb"],
+        buffering: int = -1,
+        encoding: Optional[str] = None,
+        errors: Optional[str] = None,
+        newline: Optional[str] = None,
+        *,
+        total: Optional[int] = None,
+        task_id: Optional[TaskID] = None,
+        description: str = "Reading...",
+    ) -> BinaryIO:
+        pass
+
+    @typing.overload
+    def open(
+        self,
+        file: Union[str, "PathLike[str]", bytes],
+        mode: Union[Literal["r"], Literal["rt"]],
+        buffering: int = -1,
+        encoding: Optional[str] = None,
+        errors: Optional[str] = None,
+        newline: Optional[str] = None,
+        *,
+        total: Optional[int] = None,
+        task_id: Optional[TaskID] = None,
+        description: str = "Reading...",
+    ) -> TextIO:
+        pass
+
+    def open(
+        self,
+        file: Union[str, "PathLike[str]", bytes],
+        mode: Union[Literal["rb"], Literal["rt"], Literal["r"]] = "r",
+        buffering: int = -1,
+        encoding: Optional[str] = None,
+        errors: Optional[str] = None,
+        newline: Optional[str] = None,
+        *,
+        total: Optional[int] = None,
+        task_id: Optional[TaskID] = None,
+        description: str = "Reading...",
+    ) -> Union[BinaryIO, TextIO]:
+        """Track progress while reading from a binary file.
+
+        Args:
+            path (Union[str, PathLike[str]]): The path to the file to read.
+            mode (str): The mode to use to open the file. Only supports "r", "rb" or "rt".
+            buffering (int): The buffering strategy to use, see :func:`io.open`.
+            encoding (str, optional): The encoding to use when reading in text mode, see :func:`io.open`.
+            errors (str, optional): The error handling strategy for decoding errors, see :func:`io.open`.
+            newline (str, optional): The strategy for handling newlines in text mode, see :func:`io.open`.
+            total (int, optional): Total number of bytes to read. If none given, os.stat(path).st_size is used.
+            task_id (TaskID): Task to track. Default is new task.
+            description (str, optional): Description of task, if new task is created.
+
+        Returns:
+            BinaryIO: A readable file-like object in binary mode.
+
+        Raises:
+            ValueError: When an invalid mode is given.
+        """
+        # normalize the mode (always rb, rt)
+        _mode = "".join(sorted(mode, reverse=False))
+        if _mode not in ("br", "rt", "r"):
+            raise ValueError(f"invalid mode {mode!r}")
+
+        # patch buffering to provide the same behaviour as the builtin `open`
+        line_buffering = buffering == 1
+        if _mode == "br" and buffering == 1:
+            warnings.warn(
+                "line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used",
+                RuntimeWarning,
+            )
+            buffering = -1
+        elif _mode in ("rt", "r"):
+            if buffering == 0:
+                raise ValueError("can't have unbuffered text I/O")
+            elif buffering == 1:
+                buffering = -1
+
+        # attempt to get the total with `os.stat`
+        if total is None:
+            total = stat(file).st_size
+
+        # update total of task or create new task
+        if task_id is None:
+            task_id = self.add_task(description, total=total)
+        else:
+            self.update(task_id, total=total)
+
+        # open the file in binary mode,
+        handle = io.open(file, "rb", buffering=buffering)
+        reader = _Reader(handle, self, task_id, close_handle=True)
+
+        # wrap the reader in a `TextIOWrapper` if text mode
+        if mode in ("r", "rt"):
+            return io.TextIOWrapper(
+                reader,
+                encoding=encoding,
+                errors=errors,
+                newline=newline,
+                line_buffering=line_buffering,
+            )
+
+        return reader
+
+    def start_task(self, task_id: TaskID) -> None:
+        """Start a task.
+
+        Starts a task (used when calculating elapsed time). You may need to call this manually,
+        if you called ``add_task`` with ``start=False``.
+
+        Args:
+            task_id (TaskID): ID of task.
+        """
+        with self._lock:
+            task = self._tasks[task_id]
+            if task.start_time is None:
+                task.start_time = self.get_time()
+
+    def stop_task(self, task_id: TaskID) -> None:
+        """Stop a task.
+
+        This will freeze the elapsed time on the task.
+
+        Args:
+            task_id (TaskID): ID of task.
+        """
+        with self._lock:
+            task = self._tasks[task_id]
+            current_time = self.get_time()
+            if task.start_time is None:
+                task.start_time = current_time
+            task.stop_time = current_time
+
+    def update(
+        self,
+        task_id: TaskID,
+        *,
+        total: Optional[float] = None,
+        completed: Optional[float] = None,
+        advance: Optional[float] = None,
+        description: Optional[str] = None,
+        visible: Optional[bool] = None,
+        refresh: bool = False,
+        **fields: Any,
+    ) -> None:
+        """Update information associated with a task.
+
+        Args:
+            task_id (TaskID): Task id (returned by add_task).
+            total (float, optional): Updates task.total if not None.
+            completed (float, optional): Updates task.completed if not None.
+            advance (float, optional): Add a value to task.completed if not None.
+            description (str, optional): Change task description if not None.
+            visible (bool, optional): Set visible flag if not None.
+            refresh (bool): Force a refresh of progress information. Default is False.
+            **fields (Any): Additional data fields required for rendering.
+        """
+        with self._lock:
+            task = self._tasks[task_id]
+            completed_start = task.completed
+
+            if total is not None and total != task.total:
+                task.total = total
+                task._reset()
+            if advance is not None:
+                task.completed += advance
+            if completed is not None:
+                task.completed = completed
+            if description is not None:
+                task.description = description
+            if visible is not None:
+                task.visible = visible
+            task.fields.update(fields)
+            update_completed = task.completed - completed_start
+
+            current_time = self.get_time()
+            old_sample_time = current_time - self.speed_estimate_period
+            _progress = task._progress
+
+            popleft = _progress.popleft
+            while _progress and _progress[0].timestamp < old_sample_time:
+                popleft()
+            if update_completed > 0:
+                _progress.append(ProgressSample(current_time, update_completed))
+            if (
+                task.total is not None
+                and task.completed >= task.total
+                and task.finished_time is None
+            ):
+                task.finished_time = task.elapsed
+
+        if refresh:
+            self.refresh()
+
+    def reset(
+        self,
+        task_id: TaskID,
+        *,
+        start: bool = True,
+        total: Optional[float] = None,
+        completed: int = 0,
+        visible: Optional[bool] = None,
+        description: Optional[str] = None,
+        **fields: Any,
+    ) -> None:
+        """Reset a task so completed is 0 and the clock is reset.
+
+        Args:
+            task_id (TaskID): ID of task.
+            start (bool, optional): Start the task after reset. Defaults to True.
+            total (float, optional): New total steps in task, or None to use current total. Defaults to None.
+            completed (int, optional): Number of steps completed. Defaults to 0.
+            visible (bool, optional): Set visible flag if not None.
+            description (str, optional): Change task description if not None. Defaults to None.
+            **fields (str): Additional data fields required for rendering.
+        """
+        current_time = self.get_time()
+        with self._lock:
+            task = self._tasks[task_id]
+            task._reset()
+            task.start_time = current_time if start else None
+            if total is not None:
+                task.total = total
+            task.completed = completed
+            if visible is not None:
+                task.visible = visible
+            if fields:
+                task.fields = fields
+            if description is not None:
+                task.description = description
+            task.finished_time = None
+        self.refresh()
+
+    def advance(self, task_id: TaskID, advance: float = 1) -> None:
+        """Advance task by a number of steps.
+
+        Args:
+            task_id (TaskID): ID of task.
+            advance (float): Number of steps to advance. Default is 1.
+        """
+        current_time = self.get_time()
+        with self._lock:
+            task = self._tasks[task_id]
+            completed_start = task.completed
+            task.completed += advance
+            update_completed = task.completed - completed_start
+            old_sample_time = current_time - self.speed_estimate_period
+            _progress = task._progress
+
+            popleft = _progress.popleft
+            while _progress and _progress[0].timestamp < old_sample_time:
+                popleft()
+            while len(_progress) > 1000:
+                popleft()
+            _progress.append(ProgressSample(current_time, update_completed))
+            if (
+                task.total is not None
+                and task.completed >= task.total
+                and task.finished_time is None
+            ):
+                task.finished_time = task.elapsed
+                task.finished_speed = task.speed
+
+    def refresh(self) -> None:
+        """Refresh (render) the progress information."""
+        if not self.disable and self.live.is_started:
+            self.live.refresh()
+
+    def get_renderable(self) -> RenderableType:
+        """Get a renderable for the progress display."""
+        renderable = Group(*self.get_renderables())
+        return renderable
+
+    def get_renderables(self) -> Iterable[RenderableType]:
+        """Get a number of renderables for the progress display."""
+        table = self.make_tasks_table(self.tasks)
+        yield table
+
+    def make_tasks_table(self, tasks: Iterable[Task]) -> Table:
+        """Get a table to render the Progress display.
+
+        Args:
+            tasks (Iterable[Task]): An iterable of Task instances, one per row of the table.
+
+        Returns:
+            Table: A table instance.
+        """
+        table_columns = (
+            (
+                Column(no_wrap=True)
+                if isinstance(_column, str)
+                else _column.get_table_column().copy()
+            )
+            for _column in self.columns
+        )
+        table = Table.grid(*table_columns, padding=(0, 1), expand=self.expand)
+
+        for task in tasks:
+            if task.visible:
+                table.add_row(
+                    *(
+                        (
+                            column.format(task=task)
+                            if isinstance(column, str)
+                            else column(task)
+                        )
+                        for column in self.columns
+                    )
+                )
+        return table
+
+    def __rich__(self) -> RenderableType:
+        """Makes the Progress class itself renderable."""
+        with self._lock:
+            return self.get_renderable()
+
+    def add_task(
+        self,
+        description: str,
+        start: bool = True,
+        total: Optional[float] = 100.0,
+        completed: int = 0,
+        visible: bool = True,
+        **fields: Any,
+    ) -> TaskID:
+        """Add a new 'task' to the Progress display.
+
+        Args:
+            description (str): A description of the task.
+            start (bool, optional): Start the task immediately (to calculate elapsed time). If set to False,
+                you will need to call `start` manually. Defaults to True.
+            total (float, optional): Number of total steps in the progress if known.
+                Set to None to render a pulsing animation. Defaults to 100.
+            completed (int, optional): Number of steps completed so far. Defaults to 0.
+            visible (bool, optional): Enable display of the task. Defaults to True.
+            **fields (str): Additional data fields required for rendering.
+
+        Returns:
+            TaskID: An ID you can use when calling `update`.
+        """
+        with self._lock:
+            task = Task(
+                self._task_index,
+                description,
+                total,
+                completed,
+                visible=visible,
+                fields=fields,
+                _get_time=self.get_time,
+                _lock=self._lock,
+            )
+            self._tasks[self._task_index] = task
+            if start:
+                self.start_task(self._task_index)
+            new_task_index = self._task_index
+            self._task_index = TaskID(int(self._task_index) + 1)
+        self.refresh()
+        return new_task_index
+
+    def remove_task(self, task_id: TaskID) -> None:
+        """Delete a task if it exists.
+
+        Args:
+            task_id (TaskID): A task ID.
+
+        """
+        with self._lock:
+            del self._tasks[task_id]
+
+
+if __name__ == "__main__":  # pragma: no coverage
+    import random
+    import time
+
+    from .panel import Panel
+    from .rule import Rule
+    from .syntax import Syntax
+    from .table import Table
+
+    syntax = Syntax(
+        '''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''',
+        "python",
+        line_numbers=True,
+    )
+
+    table = Table("foo", "bar", "baz")
+    table.add_row("1", "2", "3")
+
+    progress_renderables = [
+        "Text may be printed while the progress bars are rendering.",
+        Panel("In fact, [i]any[/i] renderable will work"),
+        "Such as [magenta]tables[/]...",
+        table,
+        "Pretty printed structures...",
+        {"type": "example", "text": "Pretty printed"},
+        "Syntax...",
+        syntax,
+        Rule("Give it a try!"),
+    ]
+
+    from itertools import cycle
+
+    examples = cycle(progress_renderables)
+
+    console = Console(record=True)
+
+    with Progress(
+        SpinnerColumn(),
+        *Progress.get_default_columns(),
+        TimeElapsedColumn(),
+        console=console,
+        transient=False,
+    ) as progress:
+        task1 = progress.add_task("[red]Downloading", total=1000)
+        task2 = progress.add_task("[green]Processing", total=1000)
+        task3 = progress.add_task("[yellow]Thinking", total=None)
+
+        while not progress.finished:
+            progress.update(task1, advance=0.5)
+            progress.update(task2, advance=0.3)
+            time.sleep(0.01)
+            if random.randint(0, 100) < 1:
+                progress.log(next(examples))
diff --git a/.venv/lib/python3.12/site-packages/rich/progress_bar.py b/.venv/lib/python3.12/site-packages/rich/progress_bar.py
new file mode 100644
index 0000000..41794f7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/progress_bar.py
@@ -0,0 +1,223 @@
+import math
+from functools import lru_cache
+from time import monotonic
+from typing import Iterable, List, Optional
+
+from .color import Color, blend_rgb
+from .color_triplet import ColorTriplet
+from .console import Console, ConsoleOptions, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleType
+
+# Number of characters before 'pulse' animation repeats
+PULSE_SIZE = 20
+
+
+class ProgressBar(JupyterMixin):
+    """Renders a (progress) bar. Used by rich.progress.
+
+    Args:
+        total (float, optional): Number of steps in the bar. Defaults to 100. Set to None to render a pulsing animation.
+        completed (float, optional): Number of steps completed. Defaults to 0.
+        width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None.
+        pulse (bool, optional): Enable pulse effect. Defaults to False. Will pulse if a None total was passed.
+        style (StyleType, optional): Style for the bar background. Defaults to "bar.back".
+        complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete".
+        finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished".
+        pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse".
+        animation_time (Optional[float], optional): Time in seconds to use for animation, or None to use system time.
+    """
+
+    def __init__(
+        self,
+        total: Optional[float] = 100.0,
+        completed: float = 0,
+        width: Optional[int] = None,
+        pulse: bool = False,
+        style: StyleType = "bar.back",
+        complete_style: StyleType = "bar.complete",
+        finished_style: StyleType = "bar.finished",
+        pulse_style: StyleType = "bar.pulse",
+        animation_time: Optional[float] = None,
+    ):
+        self.total = total
+        self.completed = completed
+        self.width = width
+        self.pulse = pulse
+        self.style = style
+        self.complete_style = complete_style
+        self.finished_style = finished_style
+        self.pulse_style = pulse_style
+        self.animation_time = animation_time
+
+        self._pulse_segments: Optional[List[Segment]] = None
+
+    def __repr__(self) -> str:
+        return f""
+
+    @property
+    def percentage_completed(self) -> Optional[float]:
+        """Calculate percentage complete."""
+        if self.total is None:
+            return None
+        completed = (self.completed / self.total) * 100.0
+        completed = min(100, max(0.0, completed))
+        return completed
+
+    @lru_cache(maxsize=16)
+    def _get_pulse_segments(
+        self,
+        fore_style: Style,
+        back_style: Style,
+        color_system: str,
+        no_color: bool,
+        ascii: bool = False,
+    ) -> List[Segment]:
+        """Get a list of segments to render a pulse animation.
+
+        Returns:
+            List[Segment]: A list of segments, one segment per character.
+        """
+        bar = "-" if ascii else "━"
+        segments: List[Segment] = []
+        if color_system not in ("standard", "eight_bit", "truecolor") or no_color:
+            segments += [Segment(bar, fore_style)] * (PULSE_SIZE // 2)
+            segments += [Segment(" " if no_color else bar, back_style)] * (
+                PULSE_SIZE - (PULSE_SIZE // 2)
+            )
+            return segments
+
+        append = segments.append
+        fore_color = (
+            fore_style.color.get_truecolor()
+            if fore_style.color
+            else ColorTriplet(255, 0, 255)
+        )
+        back_color = (
+            back_style.color.get_truecolor()
+            if back_style.color
+            else ColorTriplet(0, 0, 0)
+        )
+        cos = math.cos
+        pi = math.pi
+        _Segment = Segment
+        _Style = Style
+        from_triplet = Color.from_triplet
+
+        for index in range(PULSE_SIZE):
+            position = index / PULSE_SIZE
+            fade = 0.5 + cos(position * pi * 2) / 2.0
+            color = blend_rgb(fore_color, back_color, cross_fade=fade)
+            append(_Segment(bar, _Style(color=from_triplet(color))))
+        return segments
+
+    def update(self, completed: float, total: Optional[float] = None) -> None:
+        """Update progress with new values.
+
+        Args:
+            completed (float): Number of steps completed.
+            total (float, optional): Total number of steps, or ``None`` to not change. Defaults to None.
+        """
+        self.completed = completed
+        self.total = total if total is not None else self.total
+
+    def _render_pulse(
+        self, console: Console, width: int, ascii: bool = False
+    ) -> Iterable[Segment]:
+        """Renders the pulse animation.
+
+        Args:
+            console (Console): Console instance.
+            width (int): Width in characters of pulse animation.
+
+        Returns:
+            RenderResult: [description]
+
+        Yields:
+            Iterator[Segment]: Segments to render pulse
+        """
+        fore_style = console.get_style(self.pulse_style, default="white")
+        back_style = console.get_style(self.style, default="black")
+
+        pulse_segments = self._get_pulse_segments(
+            fore_style, back_style, console.color_system, console.no_color, ascii=ascii
+        )
+        segment_count = len(pulse_segments)
+        current_time = (
+            monotonic() if self.animation_time is None else self.animation_time
+        )
+        segments = pulse_segments * (int(width / segment_count) + 2)
+        offset = int(-current_time * 15) % segment_count
+        segments = segments[offset : offset + width]
+        yield from segments
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        width = min(self.width or options.max_width, options.max_width)
+        ascii = options.legacy_windows or options.ascii_only
+        should_pulse = self.pulse or self.total is None
+        if should_pulse:
+            yield from self._render_pulse(console, width, ascii=ascii)
+            return
+
+        completed: Optional[float] = (
+            min(self.total, max(0, self.completed)) if self.total is not None else None
+        )
+
+        bar = "-" if ascii else "━"
+        half_bar_right = " " if ascii else "╸"
+        half_bar_left = " " if ascii else "╺"
+        complete_halves = (
+            int(width * 2 * completed / self.total)
+            if self.total and completed is not None
+            else width * 2
+        )
+        bar_count = complete_halves // 2
+        half_bar_count = complete_halves % 2
+        style = console.get_style(self.style)
+        is_finished = self.total is None or self.completed >= self.total
+        complete_style = console.get_style(
+            self.finished_style if is_finished else self.complete_style
+        )
+        _Segment = Segment
+        if bar_count:
+            yield _Segment(bar * bar_count, complete_style)
+        if half_bar_count:
+            yield _Segment(half_bar_right * half_bar_count, complete_style)
+
+        if not console.no_color:
+            remaining_bars = width - bar_count - half_bar_count
+            if remaining_bars and console.color_system is not None:
+                if not half_bar_count and bar_count:
+                    yield _Segment(half_bar_left, style)
+                    remaining_bars -= 1
+                if remaining_bars:
+                    yield _Segment(bar * remaining_bars, style)
+
+    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)
+        )
+
+
+if __name__ == "__main__":  # pragma: no cover
+    console = Console()
+    bar = ProgressBar(width=50, total=100)
+
+    import time
+
+    console.show_cursor(False)
+    for n in range(0, 101, 1):
+        bar.update(n)
+        console.print(bar)
+        console.file.write("\r")
+        time.sleep(0.05)
+    console.show_cursor(True)
+    console.print()
diff --git a/.venv/lib/python3.12/site-packages/rich/prompt.py b/.venv/lib/python3.12/site-packages/rich/prompt.py
new file mode 100644
index 0000000..ae94d9b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/prompt.py
@@ -0,0 +1,400 @@
+from typing import Any, Generic, List, Optional, TextIO, TypeVar, Union, overload
+
+from . import get_console
+from .console import Console
+from .text import Text, TextType
+
+PromptType = TypeVar("PromptType")
+DefaultType = TypeVar("DefaultType")
+
+
+class PromptError(Exception):
+    """Exception base class for prompt related errors."""
+
+
+class InvalidResponse(PromptError):
+    """Exception to indicate a response was invalid. Raise this within process_response() to indicate an error
+    and provide an error message.
+
+    Args:
+        message (Union[str, Text]): Error message.
+    """
+
+    def __init__(self, message: TextType) -> None:
+        self.message = message
+
+    def __rich__(self) -> TextType:
+        return self.message
+
+
+class PromptBase(Generic[PromptType]):
+    """Ask the user for input until a valid response is received. This is the base class, see one of
+    the concrete classes for examples.
+
+    Args:
+        prompt (TextType, optional): Prompt text. Defaults to "".
+        console (Console, optional): A Console instance or None to use global console. Defaults to None.
+        password (bool, optional): Enable password input. Defaults to False.
+        choices (List[str], optional): A list of valid choices. Defaults to None.
+        case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
+        show_default (bool, optional): Show default in prompt. Defaults to True.
+        show_choices (bool, optional): Show choices in prompt. Defaults to True.
+    """
+
+    response_type: type = str
+
+    validate_error_message = "[prompt.invalid]Please enter a valid value"
+    illegal_choice_message = (
+        "[prompt.invalid.choice]Please select one of the available options"
+    )
+    prompt_suffix = ": "
+
+    choices: Optional[List[str]] = None
+
+    def __init__(
+        self,
+        prompt: TextType = "",
+        *,
+        console: Optional[Console] = None,
+        password: bool = False,
+        choices: Optional[List[str]] = None,
+        case_sensitive: bool = True,
+        show_default: bool = True,
+        show_choices: bool = True,
+    ) -> None:
+        self.console = console or get_console()
+        self.prompt = (
+            Text.from_markup(prompt, style="prompt")
+            if isinstance(prompt, str)
+            else prompt
+        )
+        self.password = password
+        if choices is not None:
+            self.choices = choices
+        self.case_sensitive = case_sensitive
+        self.show_default = show_default
+        self.show_choices = show_choices
+
+    @classmethod
+    @overload
+    def ask(
+        cls,
+        prompt: TextType = "",
+        *,
+        console: Optional[Console] = None,
+        password: bool = False,
+        choices: Optional[List[str]] = None,
+        case_sensitive: bool = True,
+        show_default: bool = True,
+        show_choices: bool = True,
+        default: DefaultType,
+        stream: Optional[TextIO] = None,
+    ) -> Union[DefaultType, PromptType]:
+        ...
+
+    @classmethod
+    @overload
+    def ask(
+        cls,
+        prompt: TextType = "",
+        *,
+        console: Optional[Console] = None,
+        password: bool = False,
+        choices: Optional[List[str]] = None,
+        case_sensitive: bool = True,
+        show_default: bool = True,
+        show_choices: bool = True,
+        stream: Optional[TextIO] = None,
+    ) -> PromptType:
+        ...
+
+    @classmethod
+    def ask(
+        cls,
+        prompt: TextType = "",
+        *,
+        console: Optional[Console] = None,
+        password: bool = False,
+        choices: Optional[List[str]] = None,
+        case_sensitive: bool = True,
+        show_default: bool = True,
+        show_choices: bool = True,
+        default: Any = ...,
+        stream: Optional[TextIO] = None,
+    ) -> Any:
+        """Shortcut to construct and run a prompt loop and return the result.
+
+        Example:
+            >>> filename = Prompt.ask("Enter a filename")
+
+        Args:
+            prompt (TextType, optional): Prompt text. Defaults to "".
+            console (Console, optional): A Console instance or None to use global console. Defaults to None.
+            password (bool, optional): Enable password input. Defaults to False.
+            choices (List[str], optional): A list of valid choices. Defaults to None.
+            case_sensitive (bool, optional): Matching of choices should be case-sensitive. Defaults to True.
+            show_default (bool, optional): Show default in prompt. Defaults to True.
+            show_choices (bool, optional): Show choices in prompt. Defaults to True.
+            stream (TextIO, optional): Optional text file open for reading to get input. Defaults to None.
+        """
+        _prompt = cls(
+            prompt,
+            console=console,
+            password=password,
+            choices=choices,
+            case_sensitive=case_sensitive,
+            show_default=show_default,
+            show_choices=show_choices,
+        )
+        return _prompt(default=default, stream=stream)
+
+    def render_default(self, default: DefaultType) -> Text:
+        """Turn the supplied default in to a Text instance.
+
+        Args:
+            default (DefaultType): Default value.
+
+        Returns:
+            Text: Text containing rendering of default value.
+        """
+        return Text(f"({default})", "prompt.default")
+
+    def make_prompt(self, default: DefaultType) -> Text:
+        """Make prompt text.
+
+        Args:
+            default (DefaultType): Default value.
+
+        Returns:
+            Text: Text to display in prompt.
+        """
+        prompt = self.prompt.copy()
+        prompt.end = ""
+
+        if self.show_choices and self.choices:
+            _choices = "/".join(self.choices)
+            choices = f"[{_choices}]"
+            prompt.append(" ")
+            prompt.append(choices, "prompt.choices")
+
+        if (
+            default != ...
+            and self.show_default
+            and isinstance(default, (str, self.response_type))
+        ):
+            prompt.append(" ")
+            _default = self.render_default(default)
+            prompt.append(_default)
+
+        prompt.append(self.prompt_suffix)
+
+        return prompt
+
+    @classmethod
+    def get_input(
+        cls,
+        console: Console,
+        prompt: TextType,
+        password: bool,
+        stream: Optional[TextIO] = None,
+    ) -> str:
+        """Get input from user.
+
+        Args:
+            console (Console): Console instance.
+            prompt (TextType): Prompt text.
+            password (bool): Enable password entry.
+
+        Returns:
+            str: String from user.
+        """
+        return console.input(prompt, password=password, stream=stream)
+
+    def check_choice(self, value: str) -> bool:
+        """Check value is in the list of valid choices.
+
+        Args:
+            value (str): Value entered by user.
+
+        Returns:
+            bool: True if choice was valid, otherwise False.
+        """
+        assert self.choices is not None
+        if self.case_sensitive:
+            return value.strip() in self.choices
+        return value.strip().lower() in [choice.lower() for choice in self.choices]
+
+    def process_response(self, value: str) -> PromptType:
+        """Process response from user, convert to prompt type.
+
+        Args:
+            value (str): String typed by user.
+
+        Raises:
+            InvalidResponse: If ``value`` is invalid.
+
+        Returns:
+            PromptType: The value to be returned from ask method.
+        """
+        value = value.strip()
+        try:
+            return_value: PromptType = self.response_type(value)
+        except ValueError:
+            raise InvalidResponse(self.validate_error_message)
+
+        if self.choices is not None:
+            if not self.check_choice(value):
+                raise InvalidResponse(self.illegal_choice_message)
+
+            if not self.case_sensitive:
+                # return the original choice, not the lower case version
+                return_value = self.response_type(
+                    self.choices[
+                        [choice.lower() for choice in self.choices].index(value.lower())
+                    ]
+                )
+        return return_value
+
+    def on_validate_error(self, value: str, error: InvalidResponse) -> None:
+        """Called to handle validation error.
+
+        Args:
+            value (str): String entered by user.
+            error (InvalidResponse): Exception instance the initiated the error.
+        """
+        self.console.print(error, markup=True)
+
+    def pre_prompt(self) -> None:
+        """Hook to display something before the prompt."""
+
+    @overload
+    def __call__(self, *, stream: Optional[TextIO] = None) -> PromptType:
+        ...
+
+    @overload
+    def __call__(
+        self, *, default: DefaultType, stream: Optional[TextIO] = None
+    ) -> Union[PromptType, DefaultType]:
+        ...
+
+    def __call__(self, *, default: Any = ..., stream: Optional[TextIO] = None) -> Any:
+        """Run the prompt loop.
+
+        Args:
+            default (Any, optional): Optional default value.
+
+        Returns:
+            PromptType: Processed value.
+        """
+        while True:
+            self.pre_prompt()
+            prompt = self.make_prompt(default)
+            value = self.get_input(self.console, prompt, self.password, stream=stream)
+            if value == "" and default != ...:
+                return default
+            try:
+                return_value = self.process_response(value)
+            except InvalidResponse as error:
+                self.on_validate_error(value, error)
+                continue
+            else:
+                return return_value
+
+
+class Prompt(PromptBase[str]):
+    """A prompt that returns a str.
+
+    Example:
+        >>> name = Prompt.ask("Enter your name")
+
+
+    """
+
+    response_type = str
+
+
+class IntPrompt(PromptBase[int]):
+    """A prompt that returns an integer.
+
+    Example:
+        >>> burrito_count = IntPrompt.ask("How many burritos do you want to order")
+
+    """
+
+    response_type = int
+    validate_error_message = "[prompt.invalid]Please enter a valid integer number"
+
+
+class FloatPrompt(PromptBase[float]):
+    """A prompt that returns a float.
+
+    Example:
+        >>> temperature = FloatPrompt.ask("Enter desired temperature")
+
+    """
+
+    response_type = float
+    validate_error_message = "[prompt.invalid]Please enter a number"
+
+
+class Confirm(PromptBase[bool]):
+    """A yes / no confirmation prompt.
+
+    Example:
+        >>> if Confirm.ask("Continue"):
+                run_job()
+
+    """
+
+    response_type = bool
+    validate_error_message = "[prompt.invalid]Please enter Y or N"
+    choices: List[str] = ["y", "n"]
+
+    def render_default(self, default: DefaultType) -> Text:
+        """Render the default as (y) or (n) rather than True/False."""
+        yes, no = self.choices
+        return Text(f"({yes})" if default else f"({no})", style="prompt.default")
+
+    def process_response(self, value: str) -> bool:
+        """Convert choices to a bool."""
+        value = value.strip().lower()
+        if value not in self.choices:
+            raise InvalidResponse(self.validate_error_message)
+        return value == self.choices[0]
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich import print
+
+    if Confirm.ask("Run [i]prompt[/i] tests?", default=True):
+        while True:
+            result = IntPrompt.ask(
+                ":rocket: Enter a number between [b]1[/b] and [b]10[/b]", default=5
+            )
+            if result >= 1 and result <= 10:
+                break
+            print(":pile_of_poo: [prompt.invalid]Number must be between 1 and 10")
+        print(f"number={result}")
+
+        while True:
+            password = Prompt.ask(
+                "Please enter a password [cyan](must be at least 5 characters)",
+                password=True,
+            )
+            if len(password) >= 5:
+                break
+            print("[prompt.invalid]password too short")
+        print(f"password={password!r}")
+
+        fruit = Prompt.ask("Enter a fruit", choices=["apple", "orange", "pear"])
+        print(f"fruit={fruit!r}")
+
+        doggie = Prompt.ask(
+            "What's the best Dog? (Case INSENSITIVE)",
+            choices=["Border Terrier", "Collie", "Labradoodle"],
+            case_sensitive=False,
+        )
+        print(f"doggie={doggie!r}")
+
+    else:
+        print("[b]OK :loudly_crying_face:")
diff --git a/.venv/lib/python3.12/site-packages/rich/protocol.py b/.venv/lib/python3.12/site-packages/rich/protocol.py
new file mode 100644
index 0000000..ae13856
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/protocol.py
@@ -0,0 +1,41 @@
+from typing import Any, cast, Set, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from rich.console import RenderableType
+
+_GIBBERISH = """aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf"""
+
+
+def is_renderable(check_object: Any) -> bool:
+    """Check if an object may be rendered by Rich."""
+    return (
+        isinstance(check_object, str)
+        or hasattr(check_object, "__rich__")
+        or hasattr(check_object, "__rich_console__")
+    )
+
+
+def rich_cast(renderable: object) -> "RenderableType":
+    """Cast an object to a renderable by calling __rich__ if present.
+
+    Args:
+        renderable (object): A potentially renderable object
+
+    Returns:
+        object: The result of recursively calling __rich__.
+    """
+    from rich.console import RenderableType
+
+    rich_visited_set: Set[type] = set()  # Prevent potential infinite loop
+    while hasattr(renderable, "__rich__") and not isinstance(renderable, type):
+        # Detect object which claim to have all the attributes
+        if hasattr(renderable, _GIBBERISH):
+            return repr(renderable)
+        cast_method = getattr(renderable, "__rich__")
+        renderable = cast_method()
+        renderable_type = type(renderable)
+        if renderable_type in rich_visited_set:
+            break
+        rich_visited_set.add(renderable_type)
+
+    return cast(RenderableType, renderable)
diff --git a/.venv/lib/python3.12/site-packages/rich/py.typed b/.venv/lib/python3.12/site-packages/rich/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/rich/region.py b/.venv/lib/python3.12/site-packages/rich/region.py
new file mode 100644
index 0000000..75b3631
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/region.py
@@ -0,0 +1,10 @@
+from typing import NamedTuple
+
+
+class Region(NamedTuple):
+    """Defines a rectangular region of the screen."""
+
+    x: int
+    y: int
+    width: int
+    height: int
diff --git a/.venv/lib/python3.12/site-packages/rich/repr.py b/.venv/lib/python3.12/site-packages/rich/repr.py
new file mode 100644
index 0000000..0e9bc0e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/repr.py
@@ -0,0 +1,150 @@
+from functools import partial
+from typing import (
+    Any,
+    Callable,
+    Iterable,
+    List,
+    Optional,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    overload,
+)
+
+T = TypeVar("T")
+
+
+Result = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]]
+RichReprResult = Result
+
+
+class ReprError(Exception):
+    """An error occurred when attempting to build a repr."""
+
+
+@overload
+def auto(cls: Optional[Type[T]]) -> Type[T]:
+    ...
+
+
+@overload
+def auto(*, angular: bool = False) -> Callable[[Type[T]], Type[T]]:
+    ...
+
+
+def auto(
+    cls: Optional[Type[T]] = None, *, angular: Optional[bool] = None
+) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
+    """Class decorator to create __repr__ from __rich_repr__"""
+
+    def do_replace(cls: Type[T], angular: Optional[bool] = None) -> Type[T]:
+        def auto_repr(self: T) -> str:
+            """Create repr string from __rich_repr__"""
+            repr_str: List[str] = []
+            append = repr_str.append
+
+            angular: bool = getattr(self.__rich_repr__, "angular", False)  # type: ignore[attr-defined]
+            for arg in self.__rich_repr__():  # type: ignore[attr-defined]
+                if isinstance(arg, tuple):
+                    if len(arg) == 1:
+                        append(repr(arg[0]))
+                    else:
+                        key, value, *default = arg
+                        if key is None:
+                            append(repr(value))
+                        else:
+                            if default and default[0] == value:
+                                continue
+                            append(f"{key}={value!r}")
+                else:
+                    append(repr(arg))
+            if angular:
+                return f"<{self.__class__.__name__} {' '.join(repr_str)}>"
+            else:
+                return f"{self.__class__.__name__}({', '.join(repr_str)})"
+
+        def auto_rich_repr(self: Type[T]) -> Result:
+            """Auto generate __rich_rep__ from signature of __init__"""
+            try:
+                import inspect
+
+                signature = inspect.signature(self.__init__)
+                for name, param in signature.parameters.items():
+                    if param.kind == param.POSITIONAL_ONLY:
+                        yield getattr(self, name)
+                    elif param.kind in (
+                        param.POSITIONAL_OR_KEYWORD,
+                        param.KEYWORD_ONLY,
+                    ):
+                        if param.default is param.empty:
+                            yield getattr(self, param.name)
+                        else:
+                            yield param.name, getattr(self, param.name), param.default
+            except Exception as error:
+                raise ReprError(
+                    f"Failed to auto generate __rich_repr__; {error}"
+                ) from None
+
+        if not hasattr(cls, "__rich_repr__"):
+            auto_rich_repr.__doc__ = "Build a rich repr"
+            cls.__rich_repr__ = auto_rich_repr  # type: ignore[attr-defined]
+
+        auto_repr.__doc__ = "Return repr(self)"
+        cls.__repr__ = auto_repr  # type: ignore[assignment]
+        if angular is not None:
+            cls.__rich_repr__.angular = angular  # type: ignore[attr-defined]
+        return cls
+
+    if cls is None:
+        return partial(do_replace, angular=angular)
+    else:
+        return do_replace(cls, angular=angular)
+
+
+@overload
+def rich_repr(cls: Optional[Type[T]]) -> Type[T]:
+    ...
+
+
+@overload
+def rich_repr(*, angular: bool = False) -> Callable[[Type[T]], Type[T]]:
+    ...
+
+
+def rich_repr(
+    cls: Optional[Type[T]] = None, *, angular: bool = False
+) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
+    if cls is None:
+        return auto(angular=angular)
+    else:
+        return auto(cls)
+
+
+if __name__ == "__main__":
+
+    @auto
+    class Foo:
+        def __rich_repr__(self) -> Result:
+            yield "foo"
+            yield "bar", {"shopping": ["eggs", "ham", "pineapple"]}
+            yield "buy", "hand sanitizer"
+
+    foo = Foo()
+    from rich.console import Console
+
+    console = Console()
+
+    console.rule("Standard repr")
+    console.print(foo)
+
+    console.print(foo, width=60)
+    console.print(foo, width=30)
+
+    console.rule("Angular repr")
+    Foo.__rich_repr__.angular = True  # type: ignore[attr-defined]
+
+    console.print(foo)
+
+    console.print(foo, width=60)
+    console.print(foo, width=30)
diff --git a/.venv/lib/python3.12/site-packages/rich/rule.py b/.venv/lib/python3.12/site-packages/rich/rule.py
new file mode 100644
index 0000000..fb3d432
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/rule.py
@@ -0,0 +1,130 @@
+from typing import Union
+
+from .align import AlignMethod
+from .cells import cell_len, set_cell_size
+from .console import Console, ConsoleOptions, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .style import Style
+from .text import Text
+
+
+class Rule(JupyterMixin):
+    """A console renderable to draw a horizontal rule (line).
+
+    Args:
+        title (Union[str, Text], optional): Text to render in the rule. Defaults to "".
+        characters (str, optional): Character(s) used to draw the line. Defaults to "─".
+        style (StyleType, optional): Style of Rule. Defaults to "rule.line".
+        end (str, optional): Character at end of Rule. defaults to "\\\\n"
+        align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
+    """
+
+    def __init__(
+        self,
+        title: Union[str, Text] = "",
+        *,
+        characters: str = "─",
+        style: Union[str, Style] = "rule.line",
+        end: str = "\n",
+        align: AlignMethod = "center",
+    ) -> None:
+        if cell_len(characters) < 1:
+            raise ValueError(
+                "'characters' argument must have a cell width of at least 1"
+            )
+        if align not in ("left", "center", "right"):
+            raise ValueError(
+                f'invalid value for align, expected "left", "center", "right" (not {align!r})'
+            )
+        self.title = title
+        self.characters = characters
+        self.style = style
+        self.end = end
+        self.align = align
+
+    def __repr__(self) -> str:
+        return f"Rule({self.title!r}, {self.characters!r})"
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        width = options.max_width
+
+        characters = (
+            "-"
+            if (options.ascii_only and not self.characters.isascii())
+            else self.characters
+        )
+
+        chars_len = cell_len(characters)
+        if not self.title:
+            yield self._rule_line(chars_len, width)
+            return
+
+        if isinstance(self.title, Text):
+            title_text = self.title
+        else:
+            title_text = console.render_str(self.title, style="rule.text")
+
+        title_text.plain = title_text.plain.replace("\n", " ")
+        title_text.expand_tabs()
+
+        required_space = 4 if self.align == "center" else 2
+        truncate_width = max(0, width - required_space)
+        if not truncate_width:
+            yield self._rule_line(chars_len, width)
+            return
+
+        rule_text = Text(end=self.end)
+        if self.align == "center":
+            title_text.truncate(truncate_width, overflow="ellipsis")
+            side_width = (width - cell_len(title_text.plain)) // 2
+            left = Text(characters * (side_width // chars_len + 1))
+            left.truncate(side_width - 1)
+            right_length = width - cell_len(left.plain) - cell_len(title_text.plain)
+            right = Text(characters * (side_width // chars_len + 1))
+            right.truncate(right_length)
+            rule_text.append(left.plain + " ", self.style)
+            rule_text.append(title_text)
+            rule_text.append(" " + right.plain, self.style)
+        elif self.align == "left":
+            title_text.truncate(truncate_width, overflow="ellipsis")
+            rule_text.append(title_text)
+            rule_text.append(" ")
+            rule_text.append(characters * (width - rule_text.cell_len), self.style)
+        elif self.align == "right":
+            title_text.truncate(truncate_width, overflow="ellipsis")
+            rule_text.append(characters * (width - title_text.cell_len - 1), self.style)
+            rule_text.append(" ")
+            rule_text.append(title_text)
+
+        rule_text.plain = set_cell_size(rule_text.plain, width)
+        yield rule_text
+
+    def _rule_line(self, chars_len: int, width: int) -> Text:
+        rule_text = Text(self.characters * ((width // chars_len) + 1), self.style)
+        rule_text.truncate(width)
+        rule_text.plain = set_cell_size(rule_text.plain, width)
+        return rule_text
+
+    def __rich_measure__(
+        self, console: Console, options: ConsoleOptions
+    ) -> Measurement:
+        return Measurement(1, 1)
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import sys
+
+    from rich.console import Console
+
+    try:
+        text = sys.argv[1]
+    except IndexError:
+        text = "Hello, World"
+    console = Console()
+    console.print(Rule(title=text))
+
+    console = Console()
+    console.print(Rule("foo"), width=4)
diff --git a/.venv/lib/python3.12/site-packages/rich/scope.py b/.venv/lib/python3.12/site-packages/rich/scope.py
new file mode 100644
index 0000000..41d0299
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/scope.py
@@ -0,0 +1,92 @@
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any, Optional, Tuple
+
+from .highlighter import ReprHighlighter
+from .panel import Panel
+from .pretty import Pretty
+from .table import Table
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+    from .console import ConsoleRenderable, OverflowMethod
+
+
+def render_scope(
+    scope: "Mapping[str, Any]",
+    *,
+    title: Optional[TextType] = None,
+    sort_keys: bool = True,
+    indent_guides: bool = False,
+    max_length: Optional[int] = None,
+    max_string: Optional[int] = None,
+    max_depth: Optional[int] = None,
+    overflow: Optional["OverflowMethod"] = None,
+) -> "ConsoleRenderable":
+    """Render python variables in a given scope.
+
+    Args:
+        scope (Mapping): A mapping containing variable names and values.
+        title (str, optional): Optional title. Defaults to None.
+        sort_keys (bool, optional): Enable sorting of items. Defaults to True.
+        indent_guides (bool, optional): Enable indentation guides. Defaults to False.
+        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to None.
+        max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None.
+        max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
+        overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
+
+    Returns:
+        ConsoleRenderable: A renderable object.
+    """
+    highlighter = ReprHighlighter()
+    items_table = Table.grid(padding=(0, 1), expand=False)
+    items_table.add_column(justify="right")
+
+    def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]:
+        """Sort special variables first, then alphabetically."""
+        key, _ = item
+        return (not key.startswith("__"), key.lower())
+
+    items = sorted(scope.items(), key=sort_items) if sort_keys else scope.items()
+    for key, value in items:
+        key_text = Text.assemble(
+            (key, "scope.key.special" if key.startswith("__") else "scope.key"),
+            (" =", "scope.equals"),
+        )
+        items_table.add_row(
+            key_text,
+            Pretty(
+                value,
+                highlighter=highlighter,
+                indent_guides=indent_guides,
+                max_length=max_length,
+                max_string=max_string,
+                max_depth=max_depth,
+                overflow=overflow,
+            ),
+        )
+    return Panel.fit(
+        items_table,
+        title=title,
+        border_style="scope.border",
+        padding=(0, 1),
+    )
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich import print
+
+    print()
+
+    def test(foo: float, bar: float) -> None:
+        list_of_things = [1, 2, 3, None, 4, True, False, "Hello World"]
+        dict_of_things = {
+            "version": "1.1",
+            "method": "confirmFruitPurchase",
+            "params": [["apple", "orange", "mangoes", "pomelo"], 1.123],
+            "id": "194521489",
+        }
+        print(render_scope(locals(), title="[i]locals", sort_keys=False))
+
+    test(20.3423, 3.1427)
+    print()
diff --git a/.venv/lib/python3.12/site-packages/rich/screen.py b/.venv/lib/python3.12/site-packages/rich/screen.py
new file mode 100644
index 0000000..b4f7fd1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/screen.py
@@ -0,0 +1,54 @@
+from typing import Optional, TYPE_CHECKING
+
+from .segment import Segment
+from .style import StyleType
+from ._loop import loop_last
+
+
+if TYPE_CHECKING:
+    from .console import (
+        Console,
+        ConsoleOptions,
+        RenderResult,
+        RenderableType,
+        Group,
+    )
+
+
+class Screen:
+    """A renderable that fills the terminal screen and crops excess.
+
+    Args:
+        renderable (RenderableType): Child renderable.
+        style (StyleType, optional): Optional background style. Defaults to None.
+    """
+
+    renderable: "RenderableType"
+
+    def __init__(
+        self,
+        *renderables: "RenderableType",
+        style: Optional[StyleType] = None,
+        application_mode: bool = False,
+    ) -> None:
+        from rich.console import Group
+
+        self.renderable = Group(*renderables)
+        self.style = style
+        self.application_mode = application_mode
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        width, height = options.size
+        style = console.get_style(self.style) if self.style else None
+        render_options = options.update(width=width, height=height)
+        lines = console.render_lines(
+            self.renderable or "", render_options, style=style, pad=True
+        )
+        lines = Segment.set_shape(lines, width, height, style=style)
+        new_line = Segment("\n\r") if self.application_mode else Segment.line()
+        for last, line in loop_last(lines):
+            yield from line
+            if not last:
+                yield new_line
diff --git a/.venv/lib/python3.12/site-packages/rich/segment.py b/.venv/lib/python3.12/site-packages/rich/segment.py
new file mode 100644
index 0000000..dd1e8c2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/segment.py
@@ -0,0 +1,780 @@
+from enum import IntEnum
+from functools import lru_cache
+from itertools import filterfalse
+from operator import attrgetter
+from typing import (
+    TYPE_CHECKING,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Sequence,
+    Tuple,
+    Type,
+    Union,
+)
+
+from .cells import (
+    _is_single_cell_widths,
+    cached_cell_len,
+    cell_len,
+    get_character_cell_size,
+    set_cell_size,
+)
+from .repr import Result, rich_repr
+from .style import Style
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, RenderResult
+
+
+class ControlType(IntEnum):
+    """Non-printable control codes which typically translate to ANSI codes."""
+
+    BELL = 1
+    CARRIAGE_RETURN = 2
+    HOME = 3
+    CLEAR = 4
+    SHOW_CURSOR = 5
+    HIDE_CURSOR = 6
+    ENABLE_ALT_SCREEN = 7
+    DISABLE_ALT_SCREEN = 8
+    CURSOR_UP = 9
+    CURSOR_DOWN = 10
+    CURSOR_FORWARD = 11
+    CURSOR_BACKWARD = 12
+    CURSOR_MOVE_TO_COLUMN = 13
+    CURSOR_MOVE_TO = 14
+    ERASE_IN_LINE = 15
+    SET_WINDOW_TITLE = 16
+
+
+ControlCode = Union[
+    Tuple[ControlType],
+    Tuple[ControlType, Union[int, str]],
+    Tuple[ControlType, int, int],
+]
+
+
+@rich_repr()
+class Segment(NamedTuple):
+    """A piece of text with associated style. Segments are produced by the Console render process and
+    are ultimately converted in to strings to be written to the terminal.
+
+    Args:
+        text (str): A piece of text.
+        style (:class:`~rich.style.Style`, optional): An optional style to apply to the text.
+        control (Tuple[ControlCode], optional): Optional sequence of control codes.
+
+    Attributes:
+        cell_length (int): The cell length of this Segment.
+    """
+
+    text: str
+    style: Optional[Style] = None
+    control: Optional[Sequence[ControlCode]] = None
+
+    @property
+    def cell_length(self) -> int:
+        """The number of terminal cells required to display self.text.
+
+        Returns:
+            int: A number of cells.
+        """
+        text, _style, control = self
+        return 0 if control else cell_len(text)
+
+    def __rich_repr__(self) -> Result:
+        yield self.text
+        if self.control is None:
+            if self.style is not None:
+                yield self.style
+        else:
+            yield self.style
+            yield self.control
+
+    def __bool__(self) -> bool:
+        """Check if the segment contains text."""
+        return bool(self.text)
+
+    @property
+    def is_control(self) -> bool:
+        """Check if the segment contains control codes."""
+        return self.control is not None
+
+    @classmethod
+    @lru_cache(1024 * 16)
+    def _split_cells(cls, segment: "Segment", cut: int) -> Tuple["Segment", "Segment"]:
+        """Split a segment in to two at a given cell position.
+
+        Note that splitting a double-width character, may result in that character turning
+        into two spaces.
+
+        Args:
+            segment (Segment): A segment to split.
+            cut (int): A cell position to cut on.
+
+        Returns:
+            A tuple of two segments.
+        """
+        text, style, control = segment
+        _Segment = Segment
+        cell_length = segment.cell_length
+        if cut >= cell_length:
+            return segment, _Segment("", style, control)
+
+        cell_size = get_character_cell_size
+
+        pos = int((cut / cell_length) * len(text))
+
+        while True:
+            before = text[:pos]
+            cell_pos = cell_len(before)
+            out_by = cell_pos - cut
+            if not out_by:
+                return (
+                    _Segment(before, style, control),
+                    _Segment(text[pos:], style, control),
+                )
+            if out_by == -1 and cell_size(text[pos]) == 2:
+                return (
+                    _Segment(text[:pos] + " ", style, control),
+                    _Segment(" " + text[pos + 1 :], style, control),
+                )
+            if out_by == +1 and cell_size(text[pos - 1]) == 2:
+                return (
+                    _Segment(text[: pos - 1] + " ", style, control),
+                    _Segment(" " + text[pos:], style, control),
+                )
+            if cell_pos < cut:
+                pos += 1
+            else:
+                pos -= 1
+
+    def split_cells(self, cut: int) -> Tuple["Segment", "Segment"]:
+        """Split segment in to two segments at the specified column.
+
+        If the cut point falls in the middle of a 2-cell wide character then it is replaced
+        by two spaces, to preserve the display width of the parent segment.
+
+        Args:
+            cut (int): Offset within the segment to cut.
+
+        Returns:
+            Tuple[Segment, Segment]: Two segments.
+        """
+        text, style, control = self
+        assert cut >= 0
+
+        if _is_single_cell_widths(text):
+            # Fast path with all 1 cell characters
+            if cut >= len(text):
+                return self, Segment("", style, control)
+            return (
+                Segment(text[:cut], style, control),
+                Segment(text[cut:], style, control),
+            )
+
+        return self._split_cells(self, cut)
+
+    @classmethod
+    def line(cls) -> "Segment":
+        """Make a new line segment."""
+        return cls("\n")
+
+    @classmethod
+    def apply_style(
+        cls,
+        segments: Iterable["Segment"],
+        style: Optional[Style] = None,
+        post_style: Optional[Style] = None,
+    ) -> Iterable["Segment"]:
+        """Apply style(s) to an iterable of segments.
+
+        Returns an iterable of segments where the style is replaced by ``style + segment.style + post_style``.
+
+        Args:
+            segments (Iterable[Segment]): Segments to process.
+            style (Style, optional): Base style. Defaults to None.
+            post_style (Style, optional): Style to apply on top of segment style. Defaults to None.
+
+        Returns:
+            Iterable[Segments]: A new iterable of segments (possibly the same iterable).
+        """
+        result_segments = segments
+        if style:
+            apply = style.__add__
+            result_segments = (
+                cls(text, None if control else apply(_style), control)
+                for text, _style, control in result_segments
+            )
+        if post_style:
+            result_segments = (
+                cls(
+                    text,
+                    (
+                        None
+                        if control
+                        else (_style + post_style if _style else post_style)
+                    ),
+                    control,
+                )
+                for text, _style, control in result_segments
+            )
+        return result_segments
+
+    @classmethod
+    def filter_control(
+        cls, segments: Iterable["Segment"], is_control: bool = False
+    ) -> Iterable["Segment"]:
+        """Filter segments by ``is_control`` attribute.
+
+        Args:
+            segments (Iterable[Segment]): An iterable of Segment instances.
+            is_control (bool, optional): is_control flag to match in search.
+
+        Returns:
+            Iterable[Segment]: And iterable of Segment instances.
+
+        """
+        if is_control:
+            return filter(attrgetter("control"), segments)
+        else:
+            return filterfalse(attrgetter("control"), segments)
+
+    @classmethod
+    def split_lines(cls, segments: Iterable["Segment"]) -> Iterable[List["Segment"]]:
+        """Split a sequence of segments in to a list of lines.
+
+        Args:
+            segments (Iterable[Segment]): Segments potentially containing line feeds.
+
+        Yields:
+            Iterable[List[Segment]]: Iterable of segment lists, one per line.
+        """
+        line: List[Segment] = []
+        append = line.append
+
+        for segment in segments:
+            if "\n" in segment.text and not segment.control:
+                text, style, _ = segment
+                while text:
+                    _text, new_line, text = text.partition("\n")
+                    if _text:
+                        append(cls(_text, style))
+                    if new_line:
+                        yield line
+                        line = []
+                        append = line.append
+            else:
+                append(segment)
+        if line:
+            yield line
+
+    @classmethod
+    def split_lines_terminator(
+        cls, segments: Iterable["Segment"]
+    ) -> Iterable[Tuple[List["Segment"], bool]]:
+        """Split a sequence of segments in to a list of lines and a boolean to indicate if there was a new line.
+
+        Args:
+            segments (Iterable[Segment]): Segments potentially containing line feeds.
+
+        Yields:
+            Iterable[List[Segment]]: Iterable of segment lists, one per line.
+        """
+        line: List[Segment] = []
+        append = line.append
+
+        for segment in segments:
+            if "\n" in segment.text and not segment.control:
+                text, style, _ = segment
+                while text:
+                    _text, new_line, text = text.partition("\n")
+                    if _text:
+                        append(cls(_text, style))
+                    if new_line:
+                        yield (line, True)
+                        line = []
+                        append = line.append
+            else:
+                append(segment)
+        if line:
+            yield (line, False)
+
+    @classmethod
+    def split_and_crop_lines(
+        cls,
+        segments: Iterable["Segment"],
+        length: int,
+        style: Optional[Style] = None,
+        pad: bool = True,
+        include_new_lines: bool = True,
+    ) -> Iterable[List["Segment"]]:
+        """Split segments in to lines, and crop lines greater than a given length.
+
+        Args:
+            segments (Iterable[Segment]): An iterable of segments, probably
+                generated from console.render.
+            length (int): Desired line length.
+            style (Style, optional): Style to use for any padding.
+            pad (bool): Enable padding of lines that are less than `length`.
+
+        Returns:
+            Iterable[List[Segment]]: An iterable of lines of segments.
+        """
+        line: List[Segment] = []
+        append = line.append
+
+        adjust_line_length = cls.adjust_line_length
+        new_line_segment = cls("\n")
+
+        for segment in segments:
+            if "\n" in segment.text and not segment.control:
+                text, segment_style, _ = segment
+                while text:
+                    _text, new_line, text = text.partition("\n")
+                    if _text:
+                        append(cls(_text, segment_style))
+                    if new_line:
+                        cropped_line = adjust_line_length(
+                            line, length, style=style, pad=pad
+                        )
+                        if include_new_lines:
+                            cropped_line.append(new_line_segment)
+                        yield cropped_line
+                        line.clear()
+            else:
+                append(segment)
+        if line:
+            yield adjust_line_length(line, length, style=style, pad=pad)
+
+    @classmethod
+    def adjust_line_length(
+        cls,
+        line: List["Segment"],
+        length: int,
+        style: Optional[Style] = None,
+        pad: bool = True,
+    ) -> List["Segment"]:
+        """Adjust a line to a given width (cropping or padding as required).
+
+        Args:
+            segments (Iterable[Segment]): A list of segments in a single line.
+            length (int): The desired width of the line.
+            style (Style, optional): The style of padding if used (space on the end). Defaults to None.
+            pad (bool, optional): Pad lines with spaces if they are shorter than `length`. Defaults to True.
+
+        Returns:
+            List[Segment]: A line of segments with the desired length.
+        """
+        line_length = sum(segment.cell_length for segment in line)
+        new_line: List[Segment]
+
+        if line_length < length:
+            if pad:
+                new_line = line + [cls(" " * (length - line_length), style)]
+            else:
+                new_line = line[:]
+        elif line_length > length:
+            new_line = []
+            append = new_line.append
+            line_length = 0
+            for segment in line:
+                segment_length = segment.cell_length
+                if line_length + segment_length < length or segment.control:
+                    append(segment)
+                    line_length += segment_length
+                else:
+                    text, segment_style, _ = segment
+                    text = set_cell_size(text, length - line_length)
+                    append(cls(text, segment_style))
+                    break
+        else:
+            new_line = line[:]
+        return new_line
+
+    @classmethod
+    def get_line_length(cls, line: List["Segment"]) -> int:
+        """Get the length of list of segments.
+
+        Args:
+            line (List[Segment]): A line encoded as a list of Segments (assumes no '\\\\n' characters),
+
+        Returns:
+            int: The length of the line.
+        """
+        _cell_len = cell_len
+        return sum(_cell_len(text) for text, style, control in line if not control)
+
+    @classmethod
+    def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
+        """Get the shape (enclosing rectangle) of a list of lines.
+
+        Args:
+            lines (List[List[Segment]]): A list of lines (no '\\\\n' characters).
+
+        Returns:
+            Tuple[int, int]: Width and height in characters.
+        """
+        get_line_length = cls.get_line_length
+        max_width = max(get_line_length(line) for line in lines) if lines else 0
+        return (max_width, len(lines))
+
+    @classmethod
+    def set_shape(
+        cls,
+        lines: List[List["Segment"]],
+        width: int,
+        height: Optional[int] = None,
+        style: Optional[Style] = None,
+        new_lines: bool = False,
+    ) -> List[List["Segment"]]:
+        """Set the shape of a list of lines (enclosing rectangle).
+
+        Args:
+            lines (List[List[Segment]]): A list of lines.
+            width (int): Desired width.
+            height (int, optional): Desired height or None for no change.
+            style (Style, optional): Style of any padding added.
+            new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
+
+        Returns:
+            List[List[Segment]]: New list of lines.
+        """
+        _height = height or len(lines)
+
+        blank = (
+            [cls(" " * width + "\n", style)] if new_lines else [cls(" " * width, style)]
+        )
+
+        adjust_line_length = cls.adjust_line_length
+        shaped_lines = lines[:_height]
+        shaped_lines[:] = [
+            adjust_line_length(line, width, style=style) for line in lines
+        ]
+        if len(shaped_lines) < _height:
+            shaped_lines.extend([blank] * (_height - len(shaped_lines)))
+        return shaped_lines
+
+    @classmethod
+    def align_top(
+        cls: Type["Segment"],
+        lines: List[List["Segment"]],
+        width: int,
+        height: int,
+        style: Style,
+        new_lines: bool = False,
+    ) -> List[List["Segment"]]:
+        """Aligns lines to top (adds extra lines to bottom as required).
+
+        Args:
+            lines (List[List[Segment]]): A list of lines.
+            width (int): Desired width.
+            height (int, optional): Desired height or None for no change.
+            style (Style): Style of any padding added.
+            new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
+
+        Returns:
+            List[List[Segment]]: New list of lines.
+        """
+        extra_lines = height - len(lines)
+        if not extra_lines:
+            return lines[:]
+        lines = lines[:height]
+        blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
+        lines = lines + [[blank]] * extra_lines
+        return lines
+
+    @classmethod
+    def align_bottom(
+        cls: Type["Segment"],
+        lines: List[List["Segment"]],
+        width: int,
+        height: int,
+        style: Style,
+        new_lines: bool = False,
+    ) -> List[List["Segment"]]:
+        """Aligns render to bottom (adds extra lines above as required).
+
+        Args:
+            lines (List[List[Segment]]): A list of lines.
+            width (int): Desired width.
+            height (int, optional): Desired height or None for no change.
+            style (Style): Style of any padding added. Defaults to None.
+            new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
+
+        Returns:
+            List[List[Segment]]: New list of lines.
+        """
+        extra_lines = height - len(lines)
+        if not extra_lines:
+            return lines[:]
+        lines = lines[:height]
+        blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
+        lines = [[blank]] * extra_lines + lines
+        return lines
+
+    @classmethod
+    def align_middle(
+        cls: Type["Segment"],
+        lines: List[List["Segment"]],
+        width: int,
+        height: int,
+        style: Style,
+        new_lines: bool = False,
+    ) -> List[List["Segment"]]:
+        """Aligns lines to middle (adds extra lines to above and below as required).
+
+        Args:
+            lines (List[List[Segment]]): A list of lines.
+            width (int): Desired width.
+            height (int, optional): Desired height or None for no change.
+            style (Style): Style of any padding added.
+            new_lines (bool, optional): Padded lines should include "\n". Defaults to False.
+
+        Returns:
+            List[List[Segment]]: New list of lines.
+        """
+        extra_lines = height - len(lines)
+        if not extra_lines:
+            return lines[:]
+        lines = lines[:height]
+        blank = cls(" " * width + "\n", style) if new_lines else cls(" " * width, style)
+        top_lines = extra_lines // 2
+        bottom_lines = extra_lines - top_lines
+        lines = [[blank]] * top_lines + lines + [[blank]] * bottom_lines
+        return lines
+
+    @classmethod
+    def simplify(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+        """Simplify an iterable of segments by combining contiguous segments with the same style.
+
+        Args:
+            segments (Iterable[Segment]): An iterable of segments.
+
+        Returns:
+            Iterable[Segment]: A possibly smaller iterable of segments that will render the same way.
+        """
+        iter_segments = iter(segments)
+        try:
+            last_segment = next(iter_segments)
+        except StopIteration:
+            return
+
+        _Segment = Segment
+        for segment in iter_segments:
+            if last_segment.style == segment.style and not segment.control:
+                last_segment = _Segment(
+                    last_segment.text + segment.text, last_segment.style
+                )
+            else:
+                yield last_segment
+                last_segment = segment
+        yield last_segment
+
+    @classmethod
+    def strip_links(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+        """Remove all links from an iterable of styles.
+
+        Args:
+            segments (Iterable[Segment]): An iterable segments.
+
+        Yields:
+            Segment: Segments with link removed.
+        """
+        for segment in segments:
+            if segment.control or segment.style is None:
+                yield segment
+            else:
+                text, style, _control = segment
+                yield cls(text, style.update_link(None) if style else None)
+
+    @classmethod
+    def strip_styles(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+        """Remove all styles from an iterable of segments.
+
+        Args:
+            segments (Iterable[Segment]): An iterable segments.
+
+        Yields:
+            Segment: Segments with styles replace with None
+        """
+        for text, _style, control in segments:
+            yield cls(text, None, control)
+
+    @classmethod
+    def remove_color(cls, segments: Iterable["Segment"]) -> Iterable["Segment"]:
+        """Remove all color from an iterable of segments.
+
+        Args:
+            segments (Iterable[Segment]): An iterable segments.
+
+        Yields:
+            Segment: Segments with colorless style.
+        """
+
+        cache: Dict[Style, Style] = {}
+        for text, style, control in segments:
+            if style:
+                colorless_style = cache.get(style)
+                if colorless_style is None:
+                    colorless_style = style.without_color
+                    cache[style] = colorless_style
+                yield cls(text, colorless_style, control)
+            else:
+                yield cls(text, None, control)
+
+    @classmethod
+    def divide(
+        cls, segments: Iterable["Segment"], cuts: Iterable[int]
+    ) -> Iterable[List["Segment"]]:
+        """Divides an iterable of segments in to portions.
+
+        Args:
+            cuts (Iterable[int]): Cell positions where to divide.
+
+        Yields:
+            [Iterable[List[Segment]]]: An iterable of Segments in List.
+        """
+        split_segments: List["Segment"] = []
+        add_segment = split_segments.append
+
+        iter_cuts = iter(cuts)
+
+        while True:
+            cut = next(iter_cuts, -1)
+            if cut == -1:
+                return
+            if cut != 0:
+                break
+            yield []
+        pos = 0
+
+        segments_clear = split_segments.clear
+        segments_copy = split_segments.copy
+
+        _cell_len = cached_cell_len
+        for segment in segments:
+            text, _style, control = segment
+            while text:
+                end_pos = pos if control else pos + _cell_len(text)
+                if end_pos < cut:
+                    add_segment(segment)
+                    pos = end_pos
+                    break
+
+                if end_pos == cut:
+                    add_segment(segment)
+                    yield segments_copy()
+                    segments_clear()
+                    pos = end_pos
+
+                    cut = next(iter_cuts, -1)
+                    if cut == -1:
+                        if split_segments:
+                            yield segments_copy()
+                        return
+
+                    break
+
+                else:
+                    before, segment = segment.split_cells(cut - pos)
+                    text, _style, control = segment
+                    add_segment(before)
+                    yield segments_copy()
+                    segments_clear()
+                    pos = cut
+
+                cut = next(iter_cuts, -1)
+                if cut == -1:
+                    if split_segments:
+                        yield segments_copy()
+                    return
+
+        yield segments_copy()
+
+
+class Segments:
+    """A simple renderable to render an iterable of segments. This class may be useful if
+    you want to print segments outside of a __rich_console__ method.
+
+    Args:
+        segments (Iterable[Segment]): An iterable of segments.
+        new_lines (bool, optional): Add new lines between segments. Defaults to False.
+    """
+
+    def __init__(self, segments: Iterable[Segment], new_lines: bool = False) -> None:
+        self.segments = list(segments)
+        self.new_lines = new_lines
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        if self.new_lines:
+            line = Segment.line()
+            for segment in self.segments:
+                yield segment
+                yield line
+        else:
+            yield from self.segments
+
+
+class SegmentLines:
+    def __init__(self, lines: Iterable[List[Segment]], new_lines: bool = False) -> None:
+        """A simple renderable containing a number of lines of segments. May be used as an intermediate
+        in rendering process.
+
+        Args:
+            lines (Iterable[List[Segment]]): Lists of segments forming lines.
+            new_lines (bool, optional): Insert new lines after each line. Defaults to False.
+        """
+        self.lines = list(lines)
+        self.new_lines = new_lines
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        if self.new_lines:
+            new_line = Segment.line()
+            for line in self.lines:
+                yield from line
+                yield new_line
+        else:
+            for line in self.lines:
+                yield from line
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich.console import Console
+    from rich.syntax import Syntax
+    from rich.text import Text
+
+    code = """from rich.console import Console
+console = Console()
+text = Text.from_markup("Hello, [bold magenta]World[/]!")
+console.print(text)"""
+
+    text = Text.from_markup("Hello, [bold magenta]World[/]!")
+
+    console = Console()
+
+    console.rule("rich.Segment")
+    console.print(
+        "A Segment is the last step in the Rich render process before generating text with ANSI codes."
+    )
+    console.print("\nConsider the following code:\n")
+    console.print(Syntax(code, "python", line_numbers=True))
+    console.print()
+    console.print(
+        "When you call [b]print()[/b], Rich [i]renders[/i] the object in to the following:\n"
+    )
+    fragments = list(console.render(text))
+    console.print(fragments)
+    console.print()
+    console.print("The Segments are then processed to produce the following output:\n")
+    console.print(text)
+    console.print(
+        "\nYou will only need to know this if you are implementing your own Rich renderables."
+    )
diff --git a/.venv/lib/python3.12/site-packages/rich/spinner.py b/.venv/lib/python3.12/site-packages/rich/spinner.py
new file mode 100644
index 0000000..a3a3caf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/spinner.py
@@ -0,0 +1,132 @@
+from typing import TYPE_CHECKING, List, Optional, Union, cast
+
+from ._spinners import SPINNERS
+from .measure import Measurement
+from .table import Table
+from .text import Text
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, RenderableType, RenderResult
+    from .style import StyleType
+
+
+class Spinner:
+    """A spinner animation.
+
+    Args:
+        name (str): Name of spinner (run python -m rich.spinner).
+        text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
+        style (StyleType, optional): Style for spinner animation. Defaults to None.
+        speed (float, optional): Speed factor for animation. Defaults to 1.0.
+
+    Raises:
+        KeyError: If name isn't one of the supported spinner animations.
+    """
+
+    def __init__(
+        self,
+        name: str,
+        text: "RenderableType" = "",
+        *,
+        style: Optional["StyleType"] = None,
+        speed: float = 1.0,
+    ) -> None:
+        try:
+            spinner = SPINNERS[name]
+        except KeyError:
+            raise KeyError(f"no spinner called {name!r}")
+        self.text: "Union[RenderableType, Text]" = (
+            Text.from_markup(text) if isinstance(text, str) else text
+        )
+        self.name = name
+        self.frames = cast(List[str], spinner["frames"])[:]
+        self.interval = cast(float, spinner["interval"])
+        self.start_time: Optional[float] = None
+        self.style = style
+        self.speed = speed
+        self.frame_no_offset: float = 0.0
+        self._update_speed = 0.0
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        yield self.render(console.get_time())
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> Measurement:
+        text = self.render(0)
+        return Measurement.get(console, options, text)
+
+    def render(self, time: float) -> "RenderableType":
+        """Render the spinner for a given time.
+
+        Args:
+            time (float): Time in seconds.
+
+        Returns:
+            RenderableType: A renderable containing animation frame.
+        """
+        if self.start_time is None:
+            self.start_time = time
+
+        frame_no = ((time - self.start_time) * self.speed) / (
+            self.interval / 1000.0
+        ) + self.frame_no_offset
+        frame = Text(
+            self.frames[int(frame_no) % len(self.frames)], style=self.style or ""
+        )
+
+        if self._update_speed:
+            self.frame_no_offset = frame_no
+            self.start_time = time
+            self.speed = self._update_speed
+            self._update_speed = 0.0
+
+        if not self.text:
+            return frame
+        elif isinstance(self.text, (str, Text)):
+            return Text.assemble(frame, " ", self.text)
+        else:
+            table = Table.grid(padding=1)
+            table.add_row(frame, self.text)
+            return table
+
+    def update(
+        self,
+        *,
+        text: "RenderableType" = "",
+        style: Optional["StyleType"] = None,
+        speed: Optional[float] = None,
+    ) -> None:
+        """Updates attributes of a spinner after it has been started.
+
+        Args:
+            text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
+            style (StyleType, optional): Style for spinner animation. Defaults to None.
+            speed (float, optional): Speed factor for animation. Defaults to None.
+        """
+        if text:
+            self.text = Text.from_markup(text) if isinstance(text, str) else text
+        if style:
+            self.style = style
+        if speed:
+            self._update_speed = speed
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from time import sleep
+
+    from .console import Group
+    from .live import Live
+
+    all_spinners = Group(
+        *[
+            Spinner(spinner_name, text=Text(repr(spinner_name), style="green"))
+            for spinner_name in sorted(SPINNERS.keys())
+        ]
+    )
+
+    with Live(all_spinners, refresh_per_second=20) as live:
+        while True:
+            sleep(0.1)
diff --git a/.venv/lib/python3.12/site-packages/rich/status.py b/.venv/lib/python3.12/site-packages/rich/status.py
new file mode 100644
index 0000000..6574483
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/status.py
@@ -0,0 +1,131 @@
+from types import TracebackType
+from typing import Optional, Type
+
+from .console import Console, RenderableType
+from .jupyter import JupyterMixin
+from .live import Live
+from .spinner import Spinner
+from .style import StyleType
+
+
+class Status(JupyterMixin):
+    """Displays a status indicator with a 'spinner' animation.
+
+    Args:
+        status (RenderableType): A status renderable (str or Text typically).
+        console (Console, optional): Console instance to use, or None for global console. Defaults to None.
+        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.
+    """
+
+    def __init__(
+        self,
+        status: RenderableType,
+        *,
+        console: Optional[Console] = None,
+        spinner: str = "dots",
+        spinner_style: StyleType = "status.spinner",
+        speed: float = 1.0,
+        refresh_per_second: float = 12.5,
+    ):
+        self.status = status
+        self.spinner_style = spinner_style
+        self.speed = speed
+        self._spinner = Spinner(spinner, text=status, style=spinner_style, speed=speed)
+        self._live = Live(
+            self.renderable,
+            console=console,
+            refresh_per_second=refresh_per_second,
+            transient=True,
+        )
+
+    @property
+    def renderable(self) -> Spinner:
+        return self._spinner
+
+    @property
+    def console(self) -> "Console":
+        """Get the Console used by the Status objects."""
+        return self._live.console
+
+    def update(
+        self,
+        status: Optional[RenderableType] = None,
+        *,
+        spinner: Optional[str] = None,
+        spinner_style: Optional[StyleType] = None,
+        speed: Optional[float] = None,
+    ) -> None:
+        """Update status.
+
+        Args:
+            status (Optional[RenderableType], optional): New status renderable or None for no change. Defaults to None.
+            spinner (Optional[str], optional): New spinner or None for no change. Defaults to None.
+            spinner_style (Optional[StyleType], optional): New spinner style or None for no change. Defaults to None.
+            speed (Optional[float], optional): Speed factor for spinner animation or None for no change. Defaults to None.
+        """
+        if status is not None:
+            self.status = status
+        if spinner_style is not None:
+            self.spinner_style = spinner_style
+        if speed is not None:
+            self.speed = speed
+        if spinner is not None:
+            self._spinner = Spinner(
+                spinner, text=self.status, style=self.spinner_style, speed=self.speed
+            )
+            self._live.update(self.renderable, refresh=True)
+        else:
+            self._spinner.update(
+                text=self.status, style=self.spinner_style, speed=self.speed
+            )
+
+    def start(self) -> None:
+        """Start the status animation."""
+        self._live.start()
+
+    def stop(self) -> None:
+        """Stop the spinner animation."""
+        self._live.stop()
+
+    def __rich__(self) -> RenderableType:
+        return self.renderable
+
+    def __enter__(self) -> "Status":
+        self.start()
+        return self
+
+    def __exit__(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        self.stop()
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from time import sleep
+
+    from .console import Console
+
+    console = Console()
+    with console.status("[magenta]Covid detector booting up") as status:
+        sleep(3)
+        console.log("Importing advanced AI")
+        sleep(3)
+        console.log("Advanced Covid AI Ready")
+        sleep(3)
+        status.update(status="[bold blue] Scanning for Covid", spinner="earth")
+        sleep(3)
+        console.log("Found 10,000,000,000 copies of Covid32.exe")
+        sleep(3)
+        status.update(
+            status="[bold red]Moving Covid32.exe to Trash",
+            spinner="bouncingBall",
+            spinner_style="yellow",
+        )
+        sleep(5)
+    console.print("[bold green]Covid deleted successfully")
diff --git a/.venv/lib/python3.12/site-packages/rich/style.py b/.venv/lib/python3.12/site-packages/rich/style.py
new file mode 100644
index 0000000..3806a8c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/style.py
@@ -0,0 +1,796 @@
+import sys
+from functools import lru_cache
+from itertools import count
+from operator import attrgetter
+from pickle import dumps, loads
+from random import getrandbits
+from typing import Any, Dict, Iterable, List, Optional, Type, Union, cast
+
+from . import errors
+from .color import Color, ColorParseError, ColorSystem, blend_rgb
+from .repr import Result, rich_repr
+from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme
+
+_hash_getter = attrgetter(
+    "_color", "_bgcolor", "_attributes", "_set_attributes", "_link", "_meta"
+)
+
+# Style instances and style definitions are often interchangeable
+StyleType = Union[str, "Style"]
+
+
+_id_generator = count(getrandbits(24))
+
+
+class _Bit:
+    """A descriptor to get/set a style attribute bit."""
+
+    __slots__ = ["bit"]
+
+    def __init__(self, bit_no: int) -> None:
+        self.bit = 1 << bit_no
+
+    def __get__(self, obj: "Style", objtype: Type["Style"]) -> Optional[bool]:
+        if obj._set_attributes & self.bit:
+            return obj._attributes & self.bit != 0
+        return None
+
+
+@rich_repr
+class Style:
+    """A terminal style.
+
+    A terminal style consists of a color (`color`), a background color (`bgcolor`), and a number of attributes, such
+    as bold, italic etc. The attributes have 3 states: they can either be on
+    (``True``), off (``False``), or not set (``None``).
+
+    Args:
+        color (Union[Color, str], optional): Color of terminal text. Defaults to None.
+        bgcolor (Union[Color, str], optional): Color of terminal background. Defaults to None.
+        bold (bool, optional): Enable bold text. Defaults to None.
+        dim (bool, optional): Enable dim text. Defaults to None.
+        italic (bool, optional): Enable italic text. Defaults to None.
+        underline (bool, optional): Enable underlined text. Defaults to None.
+        blink (bool, optional): Enabled blinking text. Defaults to None.
+        blink2 (bool, optional): Enable fast blinking text. Defaults to None.
+        reverse (bool, optional): Enabled reverse text. Defaults to None.
+        conceal (bool, optional): Enable concealed text. Defaults to None.
+        strike (bool, optional): Enable strikethrough text. Defaults to None.
+        underline2 (bool, optional): Enable doubly underlined text. Defaults to None.
+        frame (bool, optional): Enable framed text. Defaults to None.
+        encircle (bool, optional): Enable encircled text. Defaults to None.
+        overline (bool, optional): Enable overlined text. Defaults to None.
+        link (str, link): Link URL. Defaults to None.
+
+    """
+
+    _color: Optional[Color]
+    _bgcolor: Optional[Color]
+    _attributes: int
+    _set_attributes: int
+    _hash: Optional[int]
+    _null: bool
+    _meta: Optional[bytes]
+
+    __slots__ = [
+        "_color",
+        "_bgcolor",
+        "_attributes",
+        "_set_attributes",
+        "_link",
+        "_link_id",
+        "_ansi",
+        "_style_definition",
+        "_hash",
+        "_null",
+        "_meta",
+    ]
+
+    # maps bits on to SGR parameter
+    _style_map = {
+        0: "1",
+        1: "2",
+        2: "3",
+        3: "4",
+        4: "5",
+        5: "6",
+        6: "7",
+        7: "8",
+        8: "9",
+        9: "21",
+        10: "51",
+        11: "52",
+        12: "53",
+    }
+
+    STYLE_ATTRIBUTES = {
+        "dim": "dim",
+        "d": "dim",
+        "bold": "bold",
+        "b": "bold",
+        "italic": "italic",
+        "i": "italic",
+        "underline": "underline",
+        "u": "underline",
+        "blink": "blink",
+        "blink2": "blink2",
+        "reverse": "reverse",
+        "r": "reverse",
+        "conceal": "conceal",
+        "c": "conceal",
+        "strike": "strike",
+        "s": "strike",
+        "underline2": "underline2",
+        "uu": "underline2",
+        "frame": "frame",
+        "encircle": "encircle",
+        "overline": "overline",
+        "o": "overline",
+    }
+
+    def __init__(
+        self,
+        *,
+        color: Optional[Union[Color, str]] = None,
+        bgcolor: Optional[Union[Color, str]] = None,
+        bold: Optional[bool] = None,
+        dim: Optional[bool] = None,
+        italic: Optional[bool] = None,
+        underline: Optional[bool] = None,
+        blink: Optional[bool] = None,
+        blink2: Optional[bool] = None,
+        reverse: Optional[bool] = None,
+        conceal: Optional[bool] = None,
+        strike: Optional[bool] = None,
+        underline2: Optional[bool] = None,
+        frame: Optional[bool] = None,
+        encircle: Optional[bool] = None,
+        overline: Optional[bool] = None,
+        link: Optional[str] = None,
+        meta: Optional[Dict[str, Any]] = None,
+    ):
+        self._ansi: Optional[str] = None
+        self._style_definition: Optional[str] = None
+
+        def _make_color(color: Union[Color, str]) -> Color:
+            return color if isinstance(color, Color) else Color.parse(color)
+
+        self._color = None if color is None else _make_color(color)
+        self._bgcolor = None if bgcolor is None else _make_color(bgcolor)
+        self._set_attributes = sum(
+            (
+                bold is not None,
+                dim is not None and 2,
+                italic is not None and 4,
+                underline is not None and 8,
+                blink is not None and 16,
+                blink2 is not None and 32,
+                reverse is not None and 64,
+                conceal is not None and 128,
+                strike is not None and 256,
+                underline2 is not None and 512,
+                frame is not None and 1024,
+                encircle is not None and 2048,
+                overline is not None and 4096,
+            )
+        )
+        self._attributes = (
+            sum(
+                (
+                    bold and 1 or 0,
+                    dim and 2 or 0,
+                    italic and 4 or 0,
+                    underline and 8 or 0,
+                    blink and 16 or 0,
+                    blink2 and 32 or 0,
+                    reverse and 64 or 0,
+                    conceal and 128 or 0,
+                    strike and 256 or 0,
+                    underline2 and 512 or 0,
+                    frame and 1024 or 0,
+                    encircle and 2048 or 0,
+                    overline and 4096 or 0,
+                )
+            )
+            if self._set_attributes
+            else 0
+        )
+
+        self._link = link
+        self._meta = None if meta is None else dumps(meta)
+        self._link_id = (
+            f"{next(_id_generator)}{hash(self._meta)}" if (link or meta) else ""
+        )
+        self._hash: Optional[int] = None
+        self._null = not (self._set_attributes or color or bgcolor or link or meta)
+
+    @classmethod
+    def null(cls) -> "Style":
+        """Create an 'null' style, equivalent to Style(), but more performant."""
+        return NULL_STYLE
+
+    @classmethod
+    def from_color(
+        cls, color: Optional[Color] = None, bgcolor: Optional[Color] = None
+    ) -> "Style":
+        """Create a new style with colors and no attributes.
+
+        Returns:
+            color (Optional[Color]): A (foreground) color, or None for no color. Defaults to None.
+            bgcolor (Optional[Color]): A (background) color, or None for no color. Defaults to None.
+        """
+        style: Style = cls.__new__(Style)
+        style._ansi = None
+        style._style_definition = None
+        style._color = color
+        style._bgcolor = bgcolor
+        style._set_attributes = 0
+        style._attributes = 0
+        style._link = None
+        style._link_id = ""
+        style._meta = None
+        style._null = not (color or bgcolor)
+        style._hash = None
+        return style
+
+    @classmethod
+    def from_meta(cls, meta: Optional[Dict[str, Any]]) -> "Style":
+        """Create a new style with meta data.
+
+        Returns:
+            meta (Optional[Dict[str, Any]]): A dictionary of meta data. Defaults to None.
+        """
+        style: Style = cls.__new__(Style)
+        style._ansi = None
+        style._style_definition = None
+        style._color = None
+        style._bgcolor = None
+        style._set_attributes = 0
+        style._attributes = 0
+        style._link = None
+        style._meta = dumps(meta)
+        style._link_id = f"{next(_id_generator)}{hash(style._meta)}"
+        style._hash = None
+        style._null = not (meta)
+        return style
+
+    @classmethod
+    def on(cls, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Style":
+        """Create a blank style with meta information.
+
+        Example:
+            style = Style.on(click=self.on_click)
+
+        Args:
+            meta (Optional[Dict[str, Any]], optional): An optional dict of meta information.
+            **handlers (Any): Keyword arguments are translated in to handlers.
+
+        Returns:
+            Style: A Style with meta information attached.
+        """
+        meta = {} if meta is None else meta
+        meta.update({f"@{key}": value for key, value in handlers.items()})
+        return cls.from_meta(meta)
+
+    bold = _Bit(0)
+    dim = _Bit(1)
+    italic = _Bit(2)
+    underline = _Bit(3)
+    blink = _Bit(4)
+    blink2 = _Bit(5)
+    reverse = _Bit(6)
+    conceal = _Bit(7)
+    strike = _Bit(8)
+    underline2 = _Bit(9)
+    frame = _Bit(10)
+    encircle = _Bit(11)
+    overline = _Bit(12)
+
+    @property
+    def link_id(self) -> str:
+        """Get a link id, used in ansi code for links."""
+        return self._link_id
+
+    def __str__(self) -> str:
+        """Re-generate style definition from attributes."""
+        if self._style_definition is None:
+            attributes: List[str] = []
+            append = attributes.append
+            bits = self._set_attributes
+            if bits & 0b0000000001111:
+                if bits & 1:
+                    append("bold" if self.bold else "not bold")
+                if bits & (1 << 1):
+                    append("dim" if self.dim else "not dim")
+                if bits & (1 << 2):
+                    append("italic" if self.italic else "not italic")
+                if bits & (1 << 3):
+                    append("underline" if self.underline else "not underline")
+            if bits & 0b0000111110000:
+                if bits & (1 << 4):
+                    append("blink" if self.blink else "not blink")
+                if bits & (1 << 5):
+                    append("blink2" if self.blink2 else "not blink2")
+                if bits & (1 << 6):
+                    append("reverse" if self.reverse else "not reverse")
+                if bits & (1 << 7):
+                    append("conceal" if self.conceal else "not conceal")
+                if bits & (1 << 8):
+                    append("strike" if self.strike else "not strike")
+            if bits & 0b1111000000000:
+                if bits & (1 << 9):
+                    append("underline2" if self.underline2 else "not underline2")
+                if bits & (1 << 10):
+                    append("frame" if self.frame else "not frame")
+                if bits & (1 << 11):
+                    append("encircle" if self.encircle else "not encircle")
+                if bits & (1 << 12):
+                    append("overline" if self.overline else "not overline")
+            if self._color is not None:
+                append(self._color.name)
+            if self._bgcolor is not None:
+                append("on")
+                append(self._bgcolor.name)
+            if self._link:
+                append("link")
+                append(self._link)
+            self._style_definition = " ".join(attributes) or "none"
+        return self._style_definition
+
+    def __bool__(self) -> bool:
+        """A Style is false if it has no attributes, colors, or links."""
+        return not self._null
+
+    def _make_ansi_codes(self, color_system: ColorSystem) -> str:
+        """Generate ANSI codes for this style.
+
+        Args:
+            color_system (ColorSystem): Color system.
+
+        Returns:
+            str: String containing codes.
+        """
+
+        if self._ansi is None:
+            sgr: List[str] = []
+            append = sgr.append
+            _style_map = self._style_map
+            attributes = self._attributes & self._set_attributes
+            if attributes:
+                if attributes & 1:
+                    append(_style_map[0])
+                if attributes & 2:
+                    append(_style_map[1])
+                if attributes & 4:
+                    append(_style_map[2])
+                if attributes & 8:
+                    append(_style_map[3])
+                if attributes & 0b0000111110000:
+                    for bit in range(4, 9):
+                        if attributes & (1 << bit):
+                            append(_style_map[bit])
+                if attributes & 0b1111000000000:
+                    for bit in range(9, 13):
+                        if attributes & (1 << bit):
+                            append(_style_map[bit])
+            if self._color is not None:
+                sgr.extend(self._color.downgrade(color_system).get_ansi_codes())
+            if self._bgcolor is not None:
+                sgr.extend(
+                    self._bgcolor.downgrade(color_system).get_ansi_codes(
+                        foreground=False
+                    )
+                )
+            self._ansi = ";".join(sgr)
+        return self._ansi
+
+    @classmethod
+    @lru_cache(maxsize=1024)
+    def normalize(cls, style: str) -> str:
+        """Normalize a style definition so that styles with the same effect have the same string
+        representation.
+
+        Args:
+            style (str): A style definition.
+
+        Returns:
+            str: Normal form of style definition.
+        """
+        try:
+            return str(cls.parse(style))
+        except errors.StyleSyntaxError:
+            return style.strip().lower()
+
+    @classmethod
+    def pick_first(cls, *values: Optional[StyleType]) -> StyleType:
+        """Pick first non-None style."""
+        for value in values:
+            if value is not None:
+                return value
+        raise ValueError("expected at least one non-None style")
+
+    def __rich_repr__(self) -> Result:
+        yield "color", self.color, None
+        yield "bgcolor", self.bgcolor, None
+        yield "bold", self.bold, None,
+        yield "dim", self.dim, None,
+        yield "italic", self.italic, None
+        yield "underline", self.underline, None,
+        yield "blink", self.blink, None
+        yield "blink2", self.blink2, None
+        yield "reverse", self.reverse, None
+        yield "conceal", self.conceal, None
+        yield "strike", self.strike, None
+        yield "underline2", self.underline2, None
+        yield "frame", self.frame, None
+        yield "encircle", self.encircle, None
+        yield "link", self.link, None
+        if self._meta:
+            yield "meta", self.meta
+
+    def __eq__(self, other: Any) -> bool:
+        if not isinstance(other, Style):
+            return NotImplemented
+        return self.__hash__() == other.__hash__()
+
+    def __ne__(self, other: Any) -> bool:
+        if not isinstance(other, Style):
+            return NotImplemented
+        return self.__hash__() != other.__hash__()
+
+    def __hash__(self) -> int:
+        if self._hash is not None:
+            return self._hash
+        self._hash = hash(_hash_getter(self))
+        return self._hash
+
+    @property
+    def color(self) -> Optional[Color]:
+        """The foreground color or None if it is not set."""
+        return self._color
+
+    @property
+    def bgcolor(self) -> Optional[Color]:
+        """The background color or None if it is not set."""
+        return self._bgcolor
+
+    @property
+    def link(self) -> Optional[str]:
+        """Link text, if set."""
+        return self._link
+
+    @property
+    def transparent_background(self) -> bool:
+        """Check if the style specified a transparent background."""
+        return self.bgcolor is None or self.bgcolor.is_default
+
+    @property
+    def background_style(self) -> "Style":
+        """A Style with background only."""
+        return Style(bgcolor=self.bgcolor)
+
+    @property
+    def meta(self) -> Dict[str, Any]:
+        """Get meta information (can not be changed after construction)."""
+        return {} if self._meta is None else cast(Dict[str, Any], loads(self._meta))
+
+    @property
+    def without_color(self) -> "Style":
+        """Get a copy of the style with color removed."""
+        if self._null:
+            return NULL_STYLE
+        style: Style = self.__new__(Style)
+        style._ansi = None
+        style._style_definition = None
+        style._color = None
+        style._bgcolor = None
+        style._attributes = self._attributes
+        style._set_attributes = self._set_attributes
+        style._link = self._link
+        style._link_id = f"{next(_id_generator)}" if self._link else ""
+        style._null = False
+        style._meta = None
+        style._hash = None
+        return style
+
+    @classmethod
+    @lru_cache(maxsize=4096)
+    def parse(cls, style_definition: str) -> "Style":
+        """Parse a style definition.
+
+        Args:
+            style_definition (str): A string containing a style.
+
+        Raises:
+            errors.StyleSyntaxError: If the style definition syntax is invalid.
+
+        Returns:
+            `Style`: A Style instance.
+        """
+        if style_definition.strip() == "none" or not style_definition:
+            return cls.null()
+
+        STYLE_ATTRIBUTES = cls.STYLE_ATTRIBUTES
+        color: Optional[str] = None
+        bgcolor: Optional[str] = None
+        attributes: Dict[str, Optional[Any]] = {}
+        link: Optional[str] = None
+
+        words = iter(style_definition.split())
+        for original_word in words:
+            word = original_word.lower()
+            if word == "on":
+                word = next(words, "")
+                if not word:
+                    raise errors.StyleSyntaxError("color expected after 'on'")
+                try:
+                    Color.parse(word)
+                except ColorParseError as error:
+                    raise errors.StyleSyntaxError(
+                        f"unable to parse {word!r} as background color; {error}"
+                    ) from None
+                bgcolor = word
+
+            elif word == "not":
+                word = next(words, "")
+                attribute = STYLE_ATTRIBUTES.get(word)
+                if attribute is None:
+                    raise errors.StyleSyntaxError(
+                        f"expected style attribute after 'not', found {word!r}"
+                    )
+                attributes[attribute] = False
+
+            elif word == "link":
+                word = next(words, "")
+                if not word:
+                    raise errors.StyleSyntaxError("URL expected after 'link'")
+                link = word
+
+            elif word in STYLE_ATTRIBUTES:
+                attributes[STYLE_ATTRIBUTES[word]] = True
+
+            else:
+                try:
+                    Color.parse(word)
+                except ColorParseError as error:
+                    raise errors.StyleSyntaxError(
+                        f"unable to parse {word!r} as color; {error}"
+                    ) from None
+                color = word
+        style = Style(color=color, bgcolor=bgcolor, link=link, **attributes)
+        return style
+
+    @lru_cache(maxsize=1024)
+    def get_html_style(self, theme: Optional[TerminalTheme] = None) -> str:
+        """Get a CSS style rule."""
+        theme = theme or DEFAULT_TERMINAL_THEME
+        css: List[str] = []
+        append = css.append
+
+        color = self.color
+        bgcolor = self.bgcolor
+        if self.reverse:
+            color, bgcolor = bgcolor, color
+        if self.dim:
+            foreground_color = (
+                theme.foreground_color if color is None else color.get_truecolor(theme)
+            )
+            color = Color.from_triplet(
+                blend_rgb(foreground_color, theme.background_color, 0.5)
+            )
+        if color is not None:
+            theme_color = color.get_truecolor(theme)
+            append(f"color: {theme_color.hex}")
+            append(f"text-decoration-color: {theme_color.hex}")
+        if bgcolor is not None:
+            theme_color = bgcolor.get_truecolor(theme, foreground=False)
+            append(f"background-color: {theme_color.hex}")
+        if self.bold:
+            append("font-weight: bold")
+        if self.italic:
+            append("font-style: italic")
+        if self.underline:
+            append("text-decoration: underline")
+        if self.strike:
+            append("text-decoration: line-through")
+        if self.overline:
+            append("text-decoration: overline")
+        return "; ".join(css)
+
+    @classmethod
+    def combine(cls, styles: Iterable["Style"]) -> "Style":
+        """Combine styles and get result.
+
+        Args:
+            styles (Iterable[Style]): Styles to combine.
+
+        Returns:
+            Style: A new style instance.
+        """
+        iter_styles = iter(styles)
+        return sum(iter_styles, next(iter_styles))
+
+    @classmethod
+    def chain(cls, *styles: "Style") -> "Style":
+        """Combine styles from positional argument in to a single style.
+
+        Args:
+            *styles (Iterable[Style]): Styles to combine.
+
+        Returns:
+            Style: A new style instance.
+        """
+        iter_styles = iter(styles)
+        return sum(iter_styles, next(iter_styles))
+
+    def copy(self) -> "Style":
+        """Get a copy of this style.
+
+        Returns:
+            Style: A new Style instance with identical attributes.
+        """
+        if self._null:
+            return NULL_STYLE
+        style: Style = self.__new__(Style)
+        style._ansi = self._ansi
+        style._style_definition = self._style_definition
+        style._color = self._color
+        style._bgcolor = self._bgcolor
+        style._attributes = self._attributes
+        style._set_attributes = self._set_attributes
+        style._link = self._link
+        style._link_id = f"{next(_id_generator)}" if self._link else ""
+        style._hash = self._hash
+        style._null = False
+        style._meta = self._meta
+        return style
+
+    @lru_cache(maxsize=128)
+    def clear_meta_and_links(self) -> "Style":
+        """Get a copy of this style with link and meta information removed.
+
+        Returns:
+            Style: New style object.
+        """
+        if self._null:
+            return NULL_STYLE
+        style: Style = self.__new__(Style)
+        style._ansi = self._ansi
+        style._style_definition = self._style_definition
+        style._color = self._color
+        style._bgcolor = self._bgcolor
+        style._attributes = self._attributes
+        style._set_attributes = self._set_attributes
+        style._link = None
+        style._link_id = ""
+        style._hash = None
+        style._null = False
+        style._meta = None
+        return style
+
+    def update_link(self, link: Optional[str] = None) -> "Style":
+        """Get a copy with a different value for link.
+
+        Args:
+            link (str, optional): New value for link. Defaults to None.
+
+        Returns:
+            Style: A new Style instance.
+        """
+        style: Style = self.__new__(Style)
+        style._ansi = self._ansi
+        style._style_definition = self._style_definition
+        style._color = self._color
+        style._bgcolor = self._bgcolor
+        style._attributes = self._attributes
+        style._set_attributes = self._set_attributes
+        style._link = link
+        style._link_id = f"{next(_id_generator)}" if link else ""
+        style._hash = None
+        style._null = False
+        style._meta = self._meta
+        return style
+
+    def render(
+        self,
+        text: str = "",
+        *,
+        color_system: Optional[ColorSystem] = ColorSystem.TRUECOLOR,
+        legacy_windows: bool = False,
+    ) -> str:
+        """Render the ANSI codes for the style.
+
+        Args:
+            text (str, optional): A string to style. Defaults to "".
+            color_system (Optional[ColorSystem], optional): Color system to render to. Defaults to ColorSystem.TRUECOLOR.
+
+        Returns:
+            str: A string containing ANSI style codes.
+        """
+        if not text or color_system is None:
+            return text
+        attrs = self._ansi or self._make_ansi_codes(color_system)
+        rendered = f"\x1b[{attrs}m{text}\x1b[0m" if attrs else text
+        if self._link and not legacy_windows:
+            rendered = (
+                f"\x1b]8;id={self._link_id};{self._link}\x1b\\{rendered}\x1b]8;;\x1b\\"
+            )
+        return rendered
+
+    def test(self, text: Optional[str] = None) -> None:
+        """Write text with style directly to terminal.
+
+        This method is for testing purposes only.
+
+        Args:
+            text (Optional[str], optional): Text to style or None for style name.
+
+        """
+        text = text or str(self)
+        sys.stdout.write(f"{self.render(text)}\n")
+
+    @lru_cache(maxsize=1024)
+    def _add(self, style: Optional["Style"]) -> "Style":
+        if style is None or style._null:
+            return self
+        if self._null:
+            return style
+        new_style: Style = self.__new__(Style)
+        new_style._ansi = None
+        new_style._style_definition = None
+        new_style._color = style._color or self._color
+        new_style._bgcolor = style._bgcolor or self._bgcolor
+        new_style._attributes = (self._attributes & ~style._set_attributes) | (
+            style._attributes & style._set_attributes
+        )
+        new_style._set_attributes = self._set_attributes | style._set_attributes
+        new_style._link = style._link or self._link
+        new_style._link_id = style._link_id or self._link_id
+        new_style._null = style._null
+        if self._meta and style._meta:
+            new_style._meta = dumps({**self.meta, **style.meta})
+        else:
+            new_style._meta = self._meta or style._meta
+        new_style._hash = None
+        return new_style
+
+    def __add__(self, style: Optional["Style"]) -> "Style":
+        combined_style = self._add(style)
+        return combined_style.copy() if combined_style.link else combined_style
+
+
+NULL_STYLE = Style()
+
+
+class StyleStack:
+    """A stack of styles."""
+
+    __slots__ = ["_stack"]
+
+    def __init__(self, default_style: "Style") -> None:
+        self._stack: List[Style] = [default_style]
+
+    def __repr__(self) -> str:
+        return f""
+
+    @property
+    def current(self) -> Style:
+        """Get the Style at the top of the stack."""
+        return self._stack[-1]
+
+    def push(self, style: Style) -> None:
+        """Push a new style on to the stack.
+
+        Args:
+            style (Style): New style to combine with current style.
+        """
+        self._stack.append(self._stack[-1] + style)
+
+    def pop(self) -> Style:
+        """Pop last style and discard.
+
+        Returns:
+            Style: New current style (also available as stack.current)
+        """
+        self._stack.pop()
+        return self._stack[-1]
diff --git a/.venv/lib/python3.12/site-packages/rich/styled.py b/.venv/lib/python3.12/site-packages/rich/styled.py
new file mode 100644
index 0000000..27243be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/styled.py
@@ -0,0 +1,42 @@
+from typing import TYPE_CHECKING
+
+from .measure import Measurement
+from .segment import Segment
+from .style import StyleType
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, RenderResult, RenderableType
+
+
+class Styled:
+    """Apply a style to a renderable.
+
+    Args:
+        renderable (RenderableType): Any renderable.
+        style (StyleType): A style to apply across the entire renderable.
+    """
+
+    def __init__(self, renderable: "RenderableType", style: "StyleType") -> None:
+        self.renderable = renderable
+        self.style = style
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        style = console.get_style(self.style)
+        rendered_segments = console.render(self.renderable, options)
+        segments = Segment.apply_style(rendered_segments, style)
+        return segments
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> Measurement:
+        return Measurement.get(console, options, self.renderable)
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich import print
+    from rich.panel import Panel
+
+    panel = Styled(Panel("hello"), "on blue")
+    print(panel)
diff --git a/.venv/lib/python3.12/site-packages/rich/syntax.py b/.venv/lib/python3.12/site-packages/rich/syntax.py
new file mode 100644
index 0000000..8c8f831
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/syntax.py
@@ -0,0 +1,988 @@
+from __future__ import annotations
+
+import os.path
+import re
+import sys
+import textwrap
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Sequence,
+    Set,
+    Tuple,
+    Type,
+    Union,
+)
+
+from pygments.lexer import Lexer
+from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
+from pygments.style import Style as PygmentsStyle
+from pygments.styles import get_style_by_name
+from pygments.token import (
+    Comment,
+    Error,
+    Generic,
+    Keyword,
+    Name,
+    Number,
+    Operator,
+    String,
+    Token,
+    Whitespace,
+)
+from pygments.util import ClassNotFound
+
+if TYPE_CHECKING:
+    from .console import Console, ConsoleOptions, JustifyMethod, RenderResult
+
+from rich.containers import Lines
+from rich.padding import Padding, PaddingDimensions
+
+from ._loop import loop_first
+from .cells import cell_len
+from .color import Color, blend_rgb
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment, Segments
+from .style import Style, StyleType
+from .text import Text
+
+TokenType = Tuple[str, ...]
+
+WINDOWS = sys.platform == "win32"
+DEFAULT_THEME = "monokai"
+
+# The following styles are based on https://github.com/pygments/pygments/blob/master/pygments/formatters/terminal.py
+# A few modifications were made
+
+ANSI_LIGHT: Dict[TokenType, Style] = {
+    Token: Style(),
+    Whitespace: Style(color="white"),
+    Comment: Style(dim=True),
+    Comment.Preproc: Style(color="cyan"),
+    Keyword: Style(color="blue"),
+    Keyword.Type: Style(color="cyan"),
+    Operator.Word: Style(color="magenta"),
+    Name.Builtin: Style(color="cyan"),
+    Name.Function: Style(color="green"),
+    Name.Namespace: Style(color="cyan", underline=True),
+    Name.Class: Style(color="green", underline=True),
+    Name.Exception: Style(color="cyan"),
+    Name.Decorator: Style(color="magenta", bold=True),
+    Name.Variable: Style(color="red"),
+    Name.Constant: Style(color="red"),
+    Name.Attribute: Style(color="cyan"),
+    Name.Tag: Style(color="bright_blue"),
+    String: Style(color="yellow"),
+    Number: Style(color="blue"),
+    Generic.Deleted: Style(color="bright_red"),
+    Generic.Inserted: Style(color="green"),
+    Generic.Heading: Style(bold=True),
+    Generic.Subheading: Style(color="magenta", bold=True),
+    Generic.Prompt: Style(bold=True),
+    Generic.Error: Style(color="bright_red"),
+    Error: Style(color="red", underline=True),
+}
+
+ANSI_DARK: Dict[TokenType, Style] = {
+    Token: Style(),
+    Whitespace: Style(color="bright_black"),
+    Comment: Style(dim=True),
+    Comment.Preproc: Style(color="bright_cyan"),
+    Keyword: Style(color="bright_blue"),
+    Keyword.Type: Style(color="bright_cyan"),
+    Operator.Word: Style(color="bright_magenta"),
+    Name.Builtin: Style(color="bright_cyan"),
+    Name.Function: Style(color="bright_green"),
+    Name.Namespace: Style(color="bright_cyan", underline=True),
+    Name.Class: Style(color="bright_green", underline=True),
+    Name.Exception: Style(color="bright_cyan"),
+    Name.Decorator: Style(color="bright_magenta", bold=True),
+    Name.Variable: Style(color="bright_red"),
+    Name.Constant: Style(color="bright_red"),
+    Name.Attribute: Style(color="bright_cyan"),
+    Name.Tag: Style(color="bright_blue"),
+    String: Style(color="yellow"),
+    Number: Style(color="bright_blue"),
+    Generic.Deleted: Style(color="bright_red"),
+    Generic.Inserted: Style(color="bright_green"),
+    Generic.Heading: Style(bold=True),
+    Generic.Subheading: Style(color="bright_magenta", bold=True),
+    Generic.Prompt: Style(bold=True),
+    Generic.Error: Style(color="bright_red"),
+    Error: Style(color="red", underline=True),
+}
+
+RICH_SYNTAX_THEMES = {"ansi_light": ANSI_LIGHT, "ansi_dark": ANSI_DARK}
+NUMBERS_COLUMN_DEFAULT_PADDING = 2
+
+
+class SyntaxTheme(ABC):
+    """Base class for a syntax theme."""
+
+    @abstractmethod
+    def get_style_for_token(self, token_type: TokenType) -> Style:
+        """Get a style for a given Pygments token."""
+        raise NotImplementedError  # pragma: no cover
+
+    @abstractmethod
+    def get_background_style(self) -> Style:
+        """Get the background color."""
+        raise NotImplementedError  # pragma: no cover
+
+
+class PygmentsSyntaxTheme(SyntaxTheme):
+    """Syntax theme that delegates to Pygments theme."""
+
+    def __init__(self, theme: Union[str, Type[PygmentsStyle]]) -> None:
+        self._style_cache: Dict[TokenType, Style] = {}
+        if isinstance(theme, str):
+            try:
+                self._pygments_style_class = get_style_by_name(theme)
+            except ClassNotFound:
+                self._pygments_style_class = get_style_by_name("default")
+        else:
+            self._pygments_style_class = theme
+
+        self._background_color = self._pygments_style_class.background_color
+        self._background_style = Style(bgcolor=self._background_color)
+
+    def get_style_for_token(self, token_type: TokenType) -> Style:
+        """Get a style from a Pygments class."""
+        try:
+            return self._style_cache[token_type]
+        except KeyError:
+            try:
+                pygments_style = self._pygments_style_class.style_for_token(token_type)
+            except KeyError:
+                style = Style.null()
+            else:
+                color = pygments_style["color"]
+                bgcolor = pygments_style["bgcolor"]
+                style = Style(
+                    color="#" + color if color else "#000000",
+                    bgcolor="#" + bgcolor if bgcolor else self._background_color,
+                    bold=pygments_style["bold"],
+                    italic=pygments_style["italic"],
+                    underline=pygments_style["underline"],
+                )
+            self._style_cache[token_type] = style
+        return style
+
+    def get_background_style(self) -> Style:
+        return self._background_style
+
+
+class ANSISyntaxTheme(SyntaxTheme):
+    """Syntax theme to use standard colors."""
+
+    def __init__(self, style_map: Dict[TokenType, Style]) -> None:
+        self.style_map = style_map
+        self._missing_style = Style.null()
+        self._background_style = Style.null()
+        self._style_cache: Dict[TokenType, Style] = {}
+
+    def get_style_for_token(self, token_type: TokenType) -> Style:
+        """Look up style in the style map."""
+        try:
+            return self._style_cache[token_type]
+        except KeyError:
+            # Styles form a hierarchy
+            # We need to go from most to least specific
+            # e.g. ("foo", "bar", "baz") to ("foo", "bar")  to ("foo",)
+            get_style = self.style_map.get
+            token = tuple(token_type)
+            style = self._missing_style
+            while token:
+                _style = get_style(token)
+                if _style is not None:
+                    style = _style
+                    break
+                token = token[:-1]
+            self._style_cache[token_type] = style
+            return style
+
+    def get_background_style(self) -> Style:
+        return self._background_style
+
+
+SyntaxPosition = Tuple[int, int]
+
+
+class _SyntaxHighlightRange(NamedTuple):
+    """
+    A range to highlight in a Syntax object.
+    `start` and `end` are 2-integers tuples, where the first integer is the line number
+    (starting from 1) and the second integer is the column index (starting from 0).
+    """
+
+    style: StyleType
+    start: SyntaxPosition
+    end: SyntaxPosition
+    style_before: bool = False
+
+
+class PaddingProperty:
+    """Descriptor to get and set padding."""
+
+    def __get__(self, obj: Syntax, objtype: Type[Syntax]) -> Tuple[int, int, int, int]:
+        """Space around the Syntax."""
+        return obj._padding
+
+    def __set__(self, obj: Syntax, padding: PaddingDimensions) -> None:
+        obj._padding = Padding.unpack(padding)
+
+
+class Syntax(JupyterMixin):
+    """Construct a Syntax object to render syntax highlighted code.
+
+    Args:
+        code (str): Code to highlight.
+        lexer (Lexer | str): Lexer to use (see https://pygments.org/docs/lexers/)
+        theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "monokai".
+        dedent (bool, optional): Enable stripping of initial whitespace. Defaults to False.
+        line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
+        start_line (int, optional): Starting number for line numbers. Defaults to 1.
+        line_range (Tuple[int | None, int | None], optional): If given should be a tuple of the start and end line to render.
+            A value of None in the tuple indicates the range is open in that direction.
+        highlight_lines (Set[int]): A set of line numbers to highlight.
+        code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
+        tab_size (int, optional): Size of tabs. Defaults to 4.
+        word_wrap (bool, optional): Enable word wrapping.
+        background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
+        indent_guides (bool, optional): Show indent guides. Defaults to False.
+        padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
+    """
+
+    _pygments_style_class: Type[PygmentsStyle]
+    _theme: SyntaxTheme
+
+    @classmethod
+    def get_theme(cls, name: Union[str, SyntaxTheme]) -> SyntaxTheme:
+        """Get a syntax theme instance."""
+        if isinstance(name, SyntaxTheme):
+            return name
+        theme: SyntaxTheme
+        if name in RICH_SYNTAX_THEMES:
+            theme = ANSISyntaxTheme(RICH_SYNTAX_THEMES[name])
+        else:
+            theme = PygmentsSyntaxTheme(name)
+        return theme
+
+    def __init__(
+        self,
+        code: str,
+        lexer: Union[Lexer, str],
+        *,
+        theme: Union[str, SyntaxTheme] = DEFAULT_THEME,
+        dedent: bool = False,
+        line_numbers: bool = False,
+        start_line: int = 1,
+        line_range: Optional[Tuple[Optional[int], Optional[int]]] = None,
+        highlight_lines: Optional[Set[int]] = None,
+        code_width: Optional[int] = None,
+        tab_size: int = 4,
+        word_wrap: bool = False,
+        background_color: Optional[str] = None,
+        indent_guides: bool = False,
+        padding: PaddingDimensions = 0,
+    ) -> None:
+        self.code = code
+        self._lexer = lexer
+        self.dedent = dedent
+        self.line_numbers = line_numbers
+        self.start_line = start_line
+        self.line_range = line_range
+        self.highlight_lines = highlight_lines or set()
+        self.code_width = code_width
+        self.tab_size = tab_size
+        self.word_wrap = word_wrap
+        self.background_color = background_color
+        self.background_style = (
+            Style(bgcolor=background_color) if background_color else Style()
+        )
+        self.indent_guides = indent_guides
+        self._padding = Padding.unpack(padding)
+
+        self._theme = self.get_theme(theme)
+        self._stylized_ranges: List[_SyntaxHighlightRange] = []
+
+    padding = PaddingProperty()
+
+    @classmethod
+    def from_path(
+        cls,
+        path: str,
+        encoding: str = "utf-8",
+        lexer: Optional[Union[Lexer, str]] = None,
+        theme: Union[str, SyntaxTheme] = DEFAULT_THEME,
+        dedent: bool = False,
+        line_numbers: bool = False,
+        line_range: Optional[Tuple[int, int]] = None,
+        start_line: int = 1,
+        highlight_lines: Optional[Set[int]] = None,
+        code_width: Optional[int] = None,
+        tab_size: int = 4,
+        word_wrap: bool = False,
+        background_color: Optional[str] = None,
+        indent_guides: bool = False,
+        padding: PaddingDimensions = 0,
+    ) -> "Syntax":
+        """Construct a Syntax object from a file.
+
+        Args:
+            path (str): Path to file to highlight.
+            encoding (str): Encoding of file.
+            lexer (str | Lexer, optional): Lexer to use. If None, lexer will be auto-detected from path/file content.
+            theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs".
+            dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
+            line_numbers (bool, optional): Enable rendering of line numbers. Defaults to False.
+            start_line (int, optional): Starting number for line numbers. Defaults to 1.
+            line_range (Tuple[int, int], optional): If given should be a tuple of the start and end line to render.
+            highlight_lines (Set[int]): A set of line numbers to highlight.
+            code_width: Width of code to render (not including line numbers), or ``None`` to use all available width.
+            tab_size (int, optional): Size of tabs. Defaults to 4.
+            word_wrap (bool, optional): Enable word wrapping of code.
+            background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
+            indent_guides (bool, optional): Show indent guides. Defaults to False.
+            padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
+
+        Returns:
+            [Syntax]: A Syntax object that may be printed to the console
+        """
+        code = Path(path).read_text(encoding=encoding)
+
+        if not lexer:
+            lexer = cls.guess_lexer(path, code=code)
+
+        return cls(
+            code,
+            lexer,
+            theme=theme,
+            dedent=dedent,
+            line_numbers=line_numbers,
+            line_range=line_range,
+            start_line=start_line,
+            highlight_lines=highlight_lines,
+            code_width=code_width,
+            tab_size=tab_size,
+            word_wrap=word_wrap,
+            background_color=background_color,
+            indent_guides=indent_guides,
+            padding=padding,
+        )
+
+    @classmethod
+    def guess_lexer(cls, path: str, code: Optional[str] = None) -> str:
+        """Guess the alias of the Pygments lexer to use based on a path and an optional string of code.
+        If code is supplied, it will use a combination of the code and the filename to determine the
+        best lexer to use. For example, if the file is ``index.html`` and the file contains Django
+        templating syntax, then "html+django" will be returned. If the file is ``index.html``, and no
+        templating language is used, the "html" lexer will be used. If no string of code
+        is supplied, the lexer will be chosen based on the file extension..
+
+        Args:
+            path (AnyStr): The path to the file containing the code you wish to know the lexer for.
+            code (str, optional): Optional string of code that will be used as a fallback if no lexer
+                is found for the supplied path.
+
+        Returns:
+            str: The name of the Pygments lexer that best matches the supplied path/code.
+        """
+        lexer: Optional[Lexer] = None
+        lexer_name = "default"
+        if code:
+            try:
+                lexer = guess_lexer_for_filename(path, code)
+            except ClassNotFound:
+                pass
+
+        if not lexer:
+            try:
+                _, ext = os.path.splitext(path)
+                if ext:
+                    extension = ext.lstrip(".").lower()
+                    lexer = get_lexer_by_name(extension)
+            except ClassNotFound:
+                pass
+
+        if lexer:
+            if lexer.aliases:
+                lexer_name = lexer.aliases[0]
+            else:
+                lexer_name = lexer.name
+
+        return lexer_name
+
+    def _get_base_style(self) -> Style:
+        """Get the base style."""
+        default_style = self._theme.get_background_style() + self.background_style
+        return default_style
+
+    def _get_token_color(self, token_type: TokenType) -> Optional[Color]:
+        """Get a color (if any) for the given token.
+
+        Args:
+            token_type (TokenType): A token type tuple from Pygments.
+
+        Returns:
+            Optional[Color]: Color from theme, or None for no color.
+        """
+        style = self._theme.get_style_for_token(token_type)
+        return style.color
+
+    @property
+    def lexer(self) -> Optional[Lexer]:
+        """The lexer for this syntax, or None if no lexer was found.
+
+        Tries to find the lexer by name if a string was passed to the constructor.
+        """
+
+        if isinstance(self._lexer, Lexer):
+            return self._lexer
+        try:
+            return get_lexer_by_name(
+                self._lexer,
+                stripnl=False,
+                ensurenl=True,
+                tabsize=self.tab_size,
+            )
+        except ClassNotFound:
+            return None
+
+    @property
+    def default_lexer(self) -> Lexer:
+        """A Pygments Lexer to use if one is not specified or invalid."""
+        return get_lexer_by_name(
+            "text",
+            stripnl=False,
+            ensurenl=True,
+            tabsize=self.tab_size,
+        )
+
+    def highlight(
+        self,
+        code: str,
+        line_range: Optional[Tuple[Optional[int], Optional[int]]] = None,
+    ) -> Text:
+        """Highlight code and return a Text instance.
+
+        Args:
+            code (str): Code to highlight.
+            line_range(Tuple[int, int], optional): Optional line range to highlight.
+
+        Returns:
+            Text: A text instance containing highlighted syntax.
+        """
+
+        base_style = self._get_base_style()
+        justify: JustifyMethod = (
+            "default" if base_style.transparent_background else "left"
+        )
+
+        text = Text(
+            justify=justify,
+            style=base_style,
+            tab_size=self.tab_size,
+            no_wrap=not self.word_wrap,
+        )
+        _get_theme_style = self._theme.get_style_for_token
+
+        lexer = self.lexer or self.default_lexer
+
+        if lexer is None:
+            text.append(code)
+        else:
+            if line_range:
+                # More complicated path to only stylize a portion of the code
+                # This speeds up further operations as there are less spans to process
+                line_start, line_end = line_range
+
+                def line_tokenize() -> Iterable[Tuple[Any, str]]:
+                    """Split tokens to one per line."""
+                    assert lexer  # required to make MyPy happy - we know lexer is not None at this point
+
+                    for token_type, token in lexer.get_tokens(code):
+                        while token:
+                            line_token, new_line, token = token.partition("\n")
+                            yield token_type, line_token + new_line
+
+                def tokens_to_spans() -> Iterable[Tuple[str, Optional[Style]]]:
+                    """Convert tokens to spans."""
+                    tokens = iter(line_tokenize())
+                    line_no = 0
+                    _line_start = line_start - 1 if line_start else 0
+
+                    # Skip over tokens until line start
+                    while line_no < _line_start:
+                        try:
+                            _token_type, token = next(tokens)
+                        except StopIteration:
+                            break
+                        yield (token, None)
+                        if token.endswith("\n"):
+                            line_no += 1
+                    # Generate spans until line end
+                    for token_type, token in tokens:
+                        yield (token, _get_theme_style(token_type))
+                        if token.endswith("\n"):
+                            line_no += 1
+                            if line_end and line_no >= line_end:
+                                break
+
+                text.append_tokens(tokens_to_spans())
+
+            else:
+                text.append_tokens(
+                    (token, _get_theme_style(token_type))
+                    for token_type, token in lexer.get_tokens(code)
+                )
+            if self.background_color is not None:
+                text.stylize(f"on {self.background_color}")
+
+        if self._stylized_ranges:
+            self._apply_stylized_ranges(text)
+
+        return text
+
+    def stylize_range(
+        self,
+        style: StyleType,
+        start: SyntaxPosition,
+        end: SyntaxPosition,
+        style_before: bool = False,
+    ) -> None:
+        """
+        Adds a custom style on a part of the code, that will be applied to the syntax display when it's rendered.
+        Line numbers are 1-based, while column indexes are 0-based.
+
+        Args:
+            style (StyleType): The style to apply.
+            start (Tuple[int, int]): The start of the range, in the form `[line number, column index]`.
+            end (Tuple[int, int]): The end of the range, in the form `[line number, column index]`.
+            style_before (bool): Apply the style before any existing styles.
+        """
+        self._stylized_ranges.append(
+            _SyntaxHighlightRange(style, start, end, style_before)
+        )
+
+    def _get_line_numbers_color(self, blend: float = 0.3) -> Color:
+        background_style = self._theme.get_background_style() + self.background_style
+        background_color = background_style.bgcolor
+        if background_color is None or background_color.is_system_defined:
+            return Color.default()
+        foreground_color = self._get_token_color(Token.Text)
+        if foreground_color is None or foreground_color.is_system_defined:
+            return foreground_color or Color.default()
+        new_color = blend_rgb(
+            background_color.get_truecolor(),
+            foreground_color.get_truecolor(),
+            cross_fade=blend,
+        )
+        return Color.from_triplet(new_color)
+
+    @property
+    def _numbers_column_width(self) -> int:
+        """Get the number of characters used to render the numbers column."""
+        column_width = 0
+        if self.line_numbers:
+            column_width = (
+                len(str(self.start_line + self.code.count("\n")))
+                + NUMBERS_COLUMN_DEFAULT_PADDING
+            )
+        return column_width
+
+    def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]:
+        """Get background, number, and highlight styles for line numbers."""
+        background_style = self._get_base_style()
+        if background_style.transparent_background:
+            return Style.null(), Style(dim=True), Style.null()
+        if console.color_system in ("256", "truecolor"):
+            number_style = Style.chain(
+                background_style,
+                self._theme.get_style_for_token(Token.Text),
+                Style(color=self._get_line_numbers_color()),
+                self.background_style,
+            )
+            highlight_number_style = Style.chain(
+                background_style,
+                self._theme.get_style_for_token(Token.Text),
+                Style(bold=True, color=self._get_line_numbers_color(0.9)),
+                self.background_style,
+            )
+        else:
+            number_style = background_style + Style(dim=True)
+            highlight_number_style = background_style + Style(dim=False)
+        return background_style, number_style, highlight_number_style
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "Measurement":
+        _, right, _, left = self.padding
+        padding = left + right
+        if self.code_width is not None:
+            width = self.code_width + self._numbers_column_width + padding + 1
+            return Measurement(self._numbers_column_width, width)
+        lines = self.code.splitlines()
+        width = (
+            self._numbers_column_width
+            + padding
+            + (max(cell_len(line) for line in lines) if lines else 0)
+        )
+        if self.line_numbers:
+            width += 1
+        return Measurement(self._numbers_column_width, width)
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        segments = Segments(self._get_syntax(console, options))
+        if any(self.padding):
+            yield Padding(segments, style=self._get_base_style(), pad=self.padding)
+        else:
+            yield segments
+
+    def _get_syntax(
+        self,
+        console: Console,
+        options: ConsoleOptions,
+    ) -> Iterable[Segment]:
+        """
+        Get the Segments for the Syntax object, excluding any vertical/horizontal padding
+        """
+        transparent_background = self._get_base_style().transparent_background
+        _pad_top, pad_right, _pad_bottom, pad_left = self.padding
+        horizontal_padding = pad_left + pad_right
+        code_width = (
+            (
+                (options.max_width - self._numbers_column_width - 1)
+                if self.line_numbers
+                else options.max_width
+            )
+            - horizontal_padding
+            if self.code_width is None
+            else self.code_width
+        )
+        code_width = max(0, code_width)
+
+        ends_on_nl, processed_code = self._process_code(self.code)
+        text = self.highlight(processed_code, self.line_range)
+
+        if not self.line_numbers and not self.word_wrap and not self.line_range:
+            if not ends_on_nl:
+                text.remove_suffix("\n")
+            # Simple case of just rendering text
+            style = (
+                self._get_base_style()
+                + self._theme.get_style_for_token(Comment)
+                + Style(dim=True)
+                + self.background_style
+            )
+            if self.indent_guides and not options.ascii_only:
+                text = text.with_indent_guides(self.tab_size, style=style)
+                text.overflow = "crop"
+            if style.transparent_background:
+                yield from console.render(
+                    text, options=options.update(width=code_width)
+                )
+            else:
+                syntax_lines = console.render_lines(
+                    text,
+                    options.update(width=code_width, height=None, justify="left"),
+                    style=self.background_style,
+                    pad=True,
+                    new_lines=True,
+                )
+                for syntax_line in syntax_lines:
+                    yield from syntax_line
+            return
+
+        start_line, end_line = self.line_range or (None, None)
+        line_offset = 0
+        if start_line:
+            line_offset = max(0, start_line - 1)
+        lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
+        if self.line_range:
+            if line_offset > len(lines):
+                return
+            lines = lines[line_offset:end_line]
+
+        if self.indent_guides and not options.ascii_only:
+            style = (
+                self._get_base_style()
+                + self._theme.get_style_for_token(Comment)
+                + Style(dim=True)
+                + self.background_style
+            )
+            lines = (
+                Text("\n")
+                .join(lines)
+                .with_indent_guides(self.tab_size, style=style + Style(italic=False))
+                .split("\n", allow_blank=True)
+            )
+
+        numbers_column_width = self._numbers_column_width
+        render_options = options.update(width=code_width)
+
+        highlight_line = self.highlight_lines.__contains__
+        _Segment = Segment
+        new_line = _Segment("\n")
+
+        line_pointer = "> " if options.legacy_windows else "❱ "
+
+        (
+            background_style,
+            number_style,
+            highlight_number_style,
+        ) = self._get_number_styles(console)
+
+        for line_no, line in enumerate(lines, self.start_line + line_offset):
+            if self.word_wrap:
+                wrapped_lines = console.render_lines(
+                    line,
+                    render_options.update(height=None, justify="left"),
+                    style=background_style,
+                    pad=not transparent_background,
+                )
+            else:
+                segments = list(line.render(console, end=""))
+                if options.no_wrap:
+                    wrapped_lines = [segments]
+                else:
+                    wrapped_lines = [
+                        _Segment.adjust_line_length(
+                            segments,
+                            render_options.max_width,
+                            style=background_style,
+                            pad=not transparent_background,
+                        )
+                    ]
+
+            if self.line_numbers:
+                wrapped_line_left_pad = _Segment(
+                    " " * numbers_column_width + " ", background_style
+                )
+                for first, wrapped_line in loop_first(wrapped_lines):
+                    if first:
+                        line_column = str(line_no).rjust(numbers_column_width - 2) + " "
+                        if highlight_line(line_no):
+                            yield _Segment(line_pointer, Style(color="red"))
+                            yield _Segment(line_column, highlight_number_style)
+                        else:
+                            yield _Segment("  ", highlight_number_style)
+                            yield _Segment(line_column, number_style)
+                    else:
+                        yield wrapped_line_left_pad
+                    yield from wrapped_line
+                    yield new_line
+            else:
+                for wrapped_line in wrapped_lines:
+                    yield from wrapped_line
+                    yield new_line
+
+    def _apply_stylized_ranges(self, text: Text) -> None:
+        """
+        Apply stylized ranges to a text instance,
+        using the given code to determine the right portion to apply the style to.
+
+        Args:
+            text (Text): Text instance to apply the style to.
+        """
+        code = text.plain
+        newlines_offsets = [
+            # Let's add outer boundaries at each side of the list:
+            0,
+            # N.B. using "\n" here is much faster than using metacharacters such as "^" or "\Z":
+            *[
+                match.start() + 1
+                for match in re.finditer("\n", code, flags=re.MULTILINE)
+            ],
+            len(code) + 1,
+        ]
+
+        for stylized_range in self._stylized_ranges:
+            start = _get_code_index_for_syntax_position(
+                newlines_offsets, stylized_range.start
+            )
+            end = _get_code_index_for_syntax_position(
+                newlines_offsets, stylized_range.end
+            )
+            if start is not None and end is not None:
+                if stylized_range.style_before:
+                    text.stylize_before(stylized_range.style, start, end)
+                else:
+                    text.stylize(stylized_range.style, start, end)
+
+    def _process_code(self, code: str) -> Tuple[bool, str]:
+        """
+        Applies various processing to a raw code string
+        (normalises it so it always ends with a line return, dedents it if necessary, etc.)
+
+        Args:
+            code (str): The raw code string to process
+
+        Returns:
+            Tuple[bool, str]: the boolean indicates whether the raw code ends with a line return,
+                while the string is the processed code.
+        """
+        ends_on_nl = code.endswith("\n")
+        processed_code = code if ends_on_nl else code + "\n"
+        processed_code = (
+            textwrap.dedent(processed_code) if self.dedent else processed_code
+        )
+        processed_code = processed_code.expandtabs(self.tab_size)
+        return ends_on_nl, processed_code
+
+
+def _get_code_index_for_syntax_position(
+    newlines_offsets: Sequence[int], position: SyntaxPosition
+) -> Optional[int]:
+    """
+    Returns the index of the code string for the given positions.
+
+    Args:
+        newlines_offsets (Sequence[int]): The offset of each newline character found in the code snippet.
+        position (SyntaxPosition): The position to search for.
+
+    Returns:
+        Optional[int]: The index of the code string for this position, or `None`
+            if the given position's line number is out of range (if it's the column that is out of range
+            we silently clamp its value so that it reaches the end of the line)
+    """
+    lines_count = len(newlines_offsets)
+
+    line_number, column_index = position
+    if line_number > lines_count or len(newlines_offsets) < (line_number + 1):
+        return None  # `line_number` is out of range
+    line_index = line_number - 1
+    line_length = newlines_offsets[line_index + 1] - newlines_offsets[line_index] - 1
+    # If `column_index` is out of range: let's silently clamp it:
+    column_index = min(line_length, column_index)
+    return newlines_offsets[line_index] + column_index
+
+
+if __name__ == "__main__":  # pragma: no cover
+    import argparse
+    import sys
+
+    parser = argparse.ArgumentParser(
+        description="Render syntax to the console with Rich"
+    )
+    parser.add_argument(
+        "path",
+        metavar="PATH",
+        help="path to file, or - for stdin",
+    )
+    parser.add_argument(
+        "-c",
+        "--force-color",
+        dest="force_color",
+        action="store_true",
+        default=None,
+        help="force color for non-terminals",
+    )
+    parser.add_argument(
+        "-i",
+        "--indent-guides",
+        dest="indent_guides",
+        action="store_true",
+        default=False,
+        help="display indent guides",
+    )
+    parser.add_argument(
+        "-l",
+        "--line-numbers",
+        dest="line_numbers",
+        action="store_true",
+        help="render line numbers",
+    )
+    parser.add_argument(
+        "-w",
+        "--width",
+        type=int,
+        dest="width",
+        default=None,
+        help="width of output (default will auto-detect)",
+    )
+    parser.add_argument(
+        "-r",
+        "--wrap",
+        dest="word_wrap",
+        action="store_true",
+        default=False,
+        help="word wrap long lines",
+    )
+    parser.add_argument(
+        "-s",
+        "--soft-wrap",
+        action="store_true",
+        dest="soft_wrap",
+        default=False,
+        help="enable soft wrapping mode",
+    )
+    parser.add_argument(
+        "-t", "--theme", dest="theme", default="monokai", help="pygments theme"
+    )
+    parser.add_argument(
+        "-b",
+        "--background-color",
+        dest="background_color",
+        default=None,
+        help="Override background color",
+    )
+    parser.add_argument(
+        "-x",
+        "--lexer",
+        default=None,
+        dest="lexer_name",
+        help="Lexer name",
+    )
+    parser.add_argument(
+        "-p", "--padding", type=int, default=0, dest="padding", help="Padding"
+    )
+    parser.add_argument(
+        "--highlight-line",
+        type=int,
+        default=None,
+        dest="highlight_line",
+        help="The line number (not index!) to highlight",
+    )
+    args = parser.parse_args()
+
+    from rich.console import Console
+
+    console = Console(force_terminal=args.force_color, width=args.width)
+
+    if args.path == "-":
+        code = sys.stdin.read()
+        syntax = Syntax(
+            code=code,
+            lexer=args.lexer_name,
+            line_numbers=args.line_numbers,
+            word_wrap=args.word_wrap,
+            theme=args.theme,
+            background_color=args.background_color,
+            indent_guides=args.indent_guides,
+            padding=args.padding,
+            highlight_lines={args.highlight_line},
+        )
+    else:
+        syntax = Syntax.from_path(
+            args.path,
+            lexer=args.lexer_name,
+            line_numbers=args.line_numbers,
+            word_wrap=args.word_wrap,
+            theme=args.theme,
+            background_color=args.background_color,
+            indent_guides=args.indent_guides,
+            padding=args.padding,
+            highlight_lines={args.highlight_line},
+        )
+    console.print(syntax, soft_wrap=args.soft_wrap)
diff --git a/.venv/lib/python3.12/site-packages/rich/table.py b/.venv/lib/python3.12/site-packages/rich/table.py
new file mode 100644
index 0000000..1e2eb2b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/table.py
@@ -0,0 +1,1015 @@
+from dataclasses import dataclass, field, replace
+from typing import (
+    TYPE_CHECKING,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Sequence,
+    Tuple,
+    Union,
+)
+
+from . import box, errors
+from ._loop import loop_first_last, loop_last
+from ._pick import pick_bool
+from ._ratio import ratio_distribute, ratio_reduce
+from .align import VerticalAlignMethod
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .padding import Padding, PaddingDimensions
+from .protocol import is_renderable
+from .segment import Segment
+from .style import Style, StyleType
+from .text import Text, TextType
+
+if TYPE_CHECKING:
+    from .console import (
+        Console,
+        ConsoleOptions,
+        JustifyMethod,
+        OverflowMethod,
+        RenderableType,
+        RenderResult,
+    )
+
+
+@dataclass
+class Column:
+    """Defines a column within a ~Table.
+
+    Args:
+        title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
+        caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
+        width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
+        min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
+        box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
+        safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
+        padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
+        collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
+        pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
+        show_header (bool, optional): Show a header row. Defaults to True.
+        show_footer (bool, optional): Show a footer row. Defaults to False.
+        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
+        show_lines (bool, optional): Draw lines between every row. Defaults to False.
+        leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
+        style (Union[str, Style], optional): Default style for the table. Defaults to "none".
+        row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
+        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
+        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
+        border_style (Union[str, Style], optional): Style of the border. Defaults to None.
+        title_style (Union[str, Style], optional): Style of the title. Defaults to None.
+        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.
+        title_justify (str, optional): Justify method for title. Defaults to "center".
+        caption_justify (str, optional): Justify method for caption. Defaults to "center".
+        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.
+    """
+
+    header: "RenderableType" = ""
+    """RenderableType: Renderable for the header (typically a string)"""
+
+    footer: "RenderableType" = ""
+    """RenderableType: Renderable for the footer (typically a string)"""
+
+    header_style: StyleType = ""
+    """StyleType: The style of the header."""
+
+    footer_style: StyleType = ""
+    """StyleType: The style of the footer."""
+
+    style: StyleType = ""
+    """StyleType: The style of the column."""
+
+    justify: "JustifyMethod" = "left"
+    """str: How to justify text within the column ("left", "center", "right", or "full")"""
+
+    vertical: "VerticalAlignMethod" = "top"
+    """str: How to vertically align content ("top", "middle", or "bottom")"""
+
+    overflow: "OverflowMethod" = "ellipsis"
+    """str: Overflow method."""
+
+    width: Optional[int] = None
+    """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""
+
+    min_width: Optional[int] = None
+    """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""
+
+    max_width: Optional[int] = None
+    """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""
+
+    ratio: Optional[int] = None
+    """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""
+
+    no_wrap: bool = False
+    """bool: Prevent wrapping of text within the column. Defaults to ``False``."""
+
+    highlight: bool = False
+    """bool: Apply highlighter to column. Defaults to ``False``."""
+
+    _index: int = 0
+    """Index of column."""
+
+    _cells: List["RenderableType"] = field(default_factory=list)
+
+    def copy(self) -> "Column":
+        """Return a copy of this Column."""
+        return replace(self, _cells=[])
+
+    @property
+    def cells(self) -> Iterable["RenderableType"]:
+        """Get all cells in the column, not including header."""
+        yield from self._cells
+
+    @property
+    def flexible(self) -> bool:
+        """Check if this column is flexible."""
+        return self.ratio is not None
+
+
+@dataclass
+class Row:
+    """Information regarding a row."""
+
+    style: Optional[StyleType] = None
+    """Style to apply to row."""
+
+    end_section: bool = False
+    """Indicated end of section, which will force a line beneath the row."""
+
+
+class _Cell(NamedTuple):
+    """A single cell in a table."""
+
+    style: StyleType
+    """Style to apply to cell."""
+    renderable: "RenderableType"
+    """Cell renderable."""
+    vertical: VerticalAlignMethod
+    """Cell vertical alignment."""
+
+
+class Table(JupyterMixin):
+    """A console renderable to draw a table.
+
+    Args:
+        *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
+        title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
+        caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
+        width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
+        min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
+        box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
+        safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
+        padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
+        collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
+        pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
+        expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
+        show_header (bool, optional): Show a header row. Defaults to True.
+        show_footer (bool, optional): Show a footer row. Defaults to False.
+        show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
+        show_lines (bool, optional): Draw lines between every row. Defaults to False.
+        leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
+        style (Union[str, Style], optional): Default style for the table. Defaults to "none".
+        row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
+        header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
+        footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
+        border_style (Union[str, Style], optional): Style of the border. Defaults to None.
+        title_style (Union[str, Style], optional): Style of the title. Defaults to None.
+        caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.
+        title_justify (str, optional): Justify method for title. Defaults to "center".
+        caption_justify (str, optional): Justify method for caption. Defaults to "center".
+        highlight (bool, optional): Highlight cell contents (if str). Defaults to False.
+    """
+
+    columns: List[Column]
+    rows: List[Row]
+
+    def __init__(
+        self,
+        *headers: Union[Column, str],
+        title: Optional[TextType] = None,
+        caption: Optional[TextType] = None,
+        width: Optional[int] = None,
+        min_width: Optional[int] = None,
+        box: Optional[box.Box] = box.HEAVY_HEAD,
+        safe_box: Optional[bool] = None,
+        padding: PaddingDimensions = (0, 1),
+        collapse_padding: bool = False,
+        pad_edge: bool = True,
+        expand: bool = False,
+        show_header: bool = True,
+        show_footer: bool = False,
+        show_edge: bool = True,
+        show_lines: bool = False,
+        leading: int = 0,
+        style: StyleType = "none",
+        row_styles: Optional[Iterable[StyleType]] = None,
+        header_style: Optional[StyleType] = "table.header",
+        footer_style: Optional[StyleType] = "table.footer",
+        border_style: Optional[StyleType] = None,
+        title_style: Optional[StyleType] = None,
+        caption_style: Optional[StyleType] = None,
+        title_justify: "JustifyMethod" = "center",
+        caption_justify: "JustifyMethod" = "center",
+        highlight: bool = False,
+    ) -> None:
+        self.columns: List[Column] = []
+        self.rows: List[Row] = []
+        self.title = title
+        self.caption = caption
+        self.width = width
+        self.min_width = min_width
+        self.box = box
+        self.safe_box = safe_box
+        self._padding = Padding.unpack(padding)
+        self.pad_edge = pad_edge
+        self._expand = expand
+        self.show_header = show_header
+        self.show_footer = show_footer
+        self.show_edge = show_edge
+        self.show_lines = show_lines
+        self.leading = leading
+        self.collapse_padding = collapse_padding
+        self.style = style
+        self.header_style = header_style or ""
+        self.footer_style = footer_style or ""
+        self.border_style = border_style
+        self.title_style = title_style
+        self.caption_style = caption_style
+        self.title_justify: "JustifyMethod" = title_justify
+        self.caption_justify: "JustifyMethod" = caption_justify
+        self.highlight = highlight
+        self.row_styles: Sequence[StyleType] = list(row_styles or [])
+        append_column = self.columns.append
+        for header in headers:
+            if isinstance(header, str):
+                self.add_column(header=header)
+            else:
+                header._index = len(self.columns)
+                append_column(header)
+
+    @classmethod
+    def grid(
+        cls,
+        *headers: Union[Column, str],
+        padding: PaddingDimensions = 0,
+        collapse_padding: bool = True,
+        pad_edge: bool = False,
+        expand: bool = False,
+    ) -> "Table":
+        """Get a table with no lines, headers, or footer.
+
+        Args:
+            *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
+            padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.
+            collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.
+            pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.
+            expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
+
+        Returns:
+            Table: A table instance.
+        """
+        return cls(
+            *headers,
+            box=None,
+            padding=padding,
+            collapse_padding=collapse_padding,
+            show_header=False,
+            show_footer=False,
+            show_edge=False,
+            pad_edge=pad_edge,
+            expand=expand,
+        )
+
+    @property
+    def expand(self) -> bool:
+        """Setting a non-None self.width implies expand."""
+        return self._expand or self.width is not None
+
+    @expand.setter
+    def expand(self, expand: bool) -> None:
+        """Set expand."""
+        self._expand = expand
+
+    @property
+    def _extra_width(self) -> int:
+        """Get extra width to add to cell content."""
+        width = 0
+        if self.box and self.show_edge:
+            width += 2
+        if self.box:
+            width += len(self.columns) - 1
+        return width
+
+    @property
+    def row_count(self) -> int:
+        """Get the current number of rows."""
+        return len(self.rows)
+
+    def get_row_style(self, console: "Console", index: int) -> StyleType:
+        """Get the current row style."""
+        style = Style.null()
+        if self.row_styles:
+            style += console.get_style(self.row_styles[index % len(self.row_styles)])
+        row_style = self.rows[index].style
+        if row_style is not None:
+            style += console.get_style(row_style)
+        return style
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> Measurement:
+        max_width = options.max_width
+        if self.width is not None:
+            max_width = self.width
+        if max_width < 0:
+            return Measurement(0, 0)
+
+        extra_width = self._extra_width
+        max_width = sum(
+            self._calculate_column_widths(
+                console, options.update_width(max_width - extra_width)
+            )
+        )
+        _measure_column = self._measure_column
+
+        measurements = [
+            _measure_column(console, options.update_width(max_width), column)
+            for column in self.columns
+        ]
+        minimum_width = (
+            sum(measurement.minimum for measurement in measurements) + extra_width
+        )
+        maximum_width = (
+            sum(measurement.maximum for measurement in measurements) + extra_width
+            if (self.width is None)
+            else self.width
+        )
+        measurement = Measurement(minimum_width, maximum_width)
+        measurement = measurement.clamp(self.min_width)
+        return measurement
+
+    @property
+    def padding(self) -> Tuple[int, int, int, int]:
+        """Get cell padding."""
+        return self._padding
+
+    @padding.setter
+    def padding(self, padding: PaddingDimensions) -> "Table":
+        """Set cell padding."""
+        self._padding = Padding.unpack(padding)
+        return self
+
+    def add_column(
+        self,
+        header: "RenderableType" = "",
+        footer: "RenderableType" = "",
+        *,
+        header_style: Optional[StyleType] = None,
+        highlight: Optional[bool] = None,
+        footer_style: Optional[StyleType] = None,
+        style: Optional[StyleType] = None,
+        justify: "JustifyMethod" = "left",
+        vertical: "VerticalAlignMethod" = "top",
+        overflow: "OverflowMethod" = "ellipsis",
+        width: Optional[int] = None,
+        min_width: Optional[int] = None,
+        max_width: Optional[int] = None,
+        ratio: Optional[int] = None,
+        no_wrap: bool = False,
+    ) -> None:
+        """Add a column to the table.
+
+        Args:
+            header (RenderableType, optional): Text or renderable for the header.
+                Defaults to "".
+            footer (RenderableType, optional): Text or renderable for the footer.
+                Defaults to "".
+            header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None.
+            highlight (bool, optional): Whether to highlight the text. The default of None uses the value of the table (self) object.
+            footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None.
+            style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None.
+            justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".
+            vertical (VerticalAlignMethod, optional): Vertical alignment, one of "top", "middle", or "bottom". Defaults to "top".
+            overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".
+            width (int, optional): Desired width of column in characters, or None to fit to contents. Defaults to None.
+            min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.
+            max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.
+            ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.
+            no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.
+        """
+
+        column = Column(
+            _index=len(self.columns),
+            header=header,
+            footer=footer,
+            header_style=header_style or "",
+            highlight=highlight if highlight is not None else self.highlight,
+            footer_style=footer_style or "",
+            style=style or "",
+            justify=justify,
+            vertical=vertical,
+            overflow=overflow,
+            width=width,
+            min_width=min_width,
+            max_width=max_width,
+            ratio=ratio,
+            no_wrap=no_wrap,
+        )
+        self.columns.append(column)
+
+    def add_row(
+        self,
+        *renderables: Optional["RenderableType"],
+        style: Optional[StyleType] = None,
+        end_section: bool = False,
+    ) -> None:
+        """Add a row of renderables.
+
+        Args:
+            *renderables (None or renderable): Each cell in a row must be a renderable object (including str),
+                or ``None`` for a blank cell.
+            style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.
+            end_section (bool, optional): End a section and draw a line. Defaults to False.
+
+        Raises:
+            errors.NotRenderableError: If you add something that can't be rendered.
+        """
+
+        def add_cell(column: Column, renderable: "RenderableType") -> None:
+            column._cells.append(renderable)
+
+        cell_renderables: List[Optional["RenderableType"]] = list(renderables)
+
+        columns = self.columns
+        if len(cell_renderables) < len(columns):
+            cell_renderables = [
+                *cell_renderables,
+                *[None] * (len(columns) - len(cell_renderables)),
+            ]
+        for index, renderable in enumerate(cell_renderables):
+            if index == len(columns):
+                column = Column(_index=index, highlight=self.highlight)
+                for _ in self.rows:
+                    add_cell(column, Text(""))
+                self.columns.append(column)
+            else:
+                column = columns[index]
+            if renderable is None:
+                add_cell(column, "")
+            elif is_renderable(renderable):
+                add_cell(column, renderable)
+            else:
+                raise errors.NotRenderableError(
+                    f"unable to render {type(renderable).__name__}; a string or other renderable object is required"
+                )
+        self.rows.append(Row(style=style, end_section=end_section))
+
+    def add_section(self) -> None:
+        """Add a new section (draw a line after current row)."""
+
+        if self.rows:
+            self.rows[-1].end_section = True
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        if not self.columns:
+            yield Segment("\n")
+            return
+
+        max_width = options.max_width
+        if self.width is not None:
+            max_width = self.width
+
+        extra_width = self._extra_width
+
+        widths = self._calculate_column_widths(
+            console, options.update_width(max_width - extra_width)
+        )
+        table_width = sum(widths) + extra_width
+
+        render_options = options.update(
+            width=table_width, highlight=self.highlight, height=None
+        )
+
+        def render_annotation(
+            text: TextType, style: StyleType, justify: "JustifyMethod" = "center"
+        ) -> "RenderResult":
+            render_text = (
+                console.render_str(text, style=style, highlight=False)
+                if isinstance(text, str)
+                else text
+            )
+            return console.render(
+                render_text, options=render_options.update(justify=justify)
+            )
+
+        if self.title:
+            yield from render_annotation(
+                self.title,
+                style=Style.pick_first(self.title_style, "table.title"),
+                justify=self.title_justify,
+            )
+        yield from self._render(console, render_options, widths)
+        if self.caption:
+            yield from render_annotation(
+                self.caption,
+                style=Style.pick_first(self.caption_style, "table.caption"),
+                justify=self.caption_justify,
+            )
+
+    def _calculate_column_widths(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> List[int]:
+        """Calculate the widths of each column, including padding, not including borders."""
+        max_width = options.max_width
+        columns = self.columns
+        width_ranges = [
+            self._measure_column(console, options, column) for column in columns
+        ]
+        widths = [_range.maximum or 1 for _range in width_ranges]
+
+        get_padding_width = self._get_padding_width
+        extra_width = self._extra_width
+        if self.expand:
+            ratios = [col.ratio or 0 for col in columns if col.flexible]
+            if any(ratios):
+                fixed_widths = [
+                    0 if column.flexible else _range.maximum
+                    for _range, column in zip(width_ranges, columns)
+                ]
+                flex_minimum = [
+                    (column.width or 1) + get_padding_width(column._index)
+                    for column in columns
+                    if column.flexible
+                ]
+                flexible_width = max_width - sum(fixed_widths)
+                flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)
+                iter_flex_widths = iter(flex_widths)
+                for index, column in enumerate(columns):
+                    if column.flexible:
+                        widths[index] = fixed_widths[index] + next(iter_flex_widths)
+        table_width = sum(widths)
+
+        if table_width > max_width:
+            widths = self._collapse_widths(
+                widths,
+                [(column.width is None and not column.no_wrap) for column in columns],
+                max_width,
+            )
+            table_width = sum(widths)
+            # last resort, reduce columns evenly
+            if table_width > max_width:
+                excess_width = table_width - max_width
+                widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)
+                table_width = sum(widths)
+
+            width_ranges = [
+                self._measure_column(console, options.update_width(width), column)
+                for width, column in zip(widths, columns)
+            ]
+            widths = [_range.maximum or 0 for _range in width_ranges]
+
+        if (table_width < max_width and self.expand) or (
+            self.min_width is not None and table_width < (self.min_width - extra_width)
+        ):
+            _max_width = (
+                max_width
+                if self.min_width is None
+                else min(self.min_width - extra_width, max_width)
+            )
+            pad_widths = ratio_distribute(_max_width - table_width, widths)
+            widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
+
+        return widths
+
+    @classmethod
+    def _collapse_widths(
+        cls, widths: List[int], wrapable: List[bool], max_width: int
+    ) -> List[int]:
+        """Reduce widths so that the total is under max_width.
+
+        Args:
+            widths (List[int]): List of widths.
+            wrapable (List[bool]): List of booleans that indicate if a column may shrink.
+            max_width (int): Maximum width to reduce to.
+
+        Returns:
+            List[int]: A new list of widths.
+        """
+        total_width = sum(widths)
+        excess_width = total_width - max_width
+        if any(wrapable):
+            while total_width and excess_width > 0:
+                max_column = max(
+                    width for width, allow_wrap in zip(widths, wrapable) if allow_wrap
+                )
+                second_max_column = max(
+                    width if allow_wrap and width != max_column else 0
+                    for width, allow_wrap in zip(widths, wrapable)
+                )
+                column_difference = max_column - second_max_column
+                ratios = [
+                    (1 if (width == max_column and allow_wrap) else 0)
+                    for width, allow_wrap in zip(widths, wrapable)
+                ]
+                if not any(ratios) or not column_difference:
+                    break
+                max_reduce = [min(excess_width, column_difference)] * len(widths)
+                widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
+
+                total_width = sum(widths)
+                excess_width = total_width - max_width
+        return widths
+
+    def _get_cells(
+        self, console: "Console", column_index: int, column: Column
+    ) -> Iterable[_Cell]:
+        """Get all the cells with padding and optional header."""
+
+        collapse_padding = self.collapse_padding
+        pad_edge = self.pad_edge
+        padding = self.padding
+        any_padding = any(padding)
+
+        first_column = column_index == 0
+        last_column = column_index == len(self.columns) - 1
+
+        _padding_cache: Dict[Tuple[bool, bool], Tuple[int, int, int, int]] = {}
+
+        def get_padding(first_row: bool, last_row: bool) -> Tuple[int, int, int, int]:
+            cached = _padding_cache.get((first_row, last_row))
+            if cached:
+                return cached
+            top, right, bottom, left = padding
+
+            if collapse_padding:
+                if not first_column:
+                    left = max(0, left - right)
+                if not last_row:
+                    bottom = max(0, top - bottom)
+
+            if not pad_edge:
+                if first_column:
+                    left = 0
+                if last_column:
+                    right = 0
+                if first_row:
+                    top = 0
+                if last_row:
+                    bottom = 0
+            _padding = (top, right, bottom, left)
+            _padding_cache[(first_row, last_row)] = _padding
+            return _padding
+
+        raw_cells: List[Tuple[StyleType, "RenderableType"]] = []
+        _append = raw_cells.append
+        get_style = console.get_style
+        if self.show_header:
+            header_style = get_style(self.header_style or "") + get_style(
+                column.header_style
+            )
+            _append((header_style, column.header))
+        cell_style = get_style(column.style or "")
+        for cell in column.cells:
+            _append((cell_style, cell))
+        if self.show_footer:
+            footer_style = get_style(self.footer_style or "") + get_style(
+                column.footer_style
+            )
+            _append((footer_style, column.footer))
+
+        if any_padding:
+            _Padding = Padding
+            for first, last, (style, renderable) in loop_first_last(raw_cells):
+                yield _Cell(
+                    style,
+                    _Padding(renderable, get_padding(first, last)),
+                    getattr(renderable, "vertical", None) or column.vertical,
+                )
+        else:
+            for style, renderable in raw_cells:
+                yield _Cell(
+                    style,
+                    renderable,
+                    getattr(renderable, "vertical", None) or column.vertical,
+                )
+
+    def _get_padding_width(self, column_index: int) -> int:
+        """Get extra width from padding."""
+        _, pad_right, _, pad_left = self.padding
+
+        if self.collapse_padding:
+            pad_left = 0
+            pad_right = abs(pad_left - pad_right)
+
+        if not self.pad_edge:
+            if column_index == 0:
+                pad_left = 0
+            if column_index == len(self.columns) - 1:
+                pad_right = 0
+
+        return pad_left + pad_right
+
+    def _measure_column(
+        self,
+        console: "Console",
+        options: "ConsoleOptions",
+        column: Column,
+    ) -> Measurement:
+        """Get the minimum and maximum width of the column."""
+
+        max_width = options.max_width
+        if max_width < 1:
+            return Measurement(0, 0)
+
+        padding_width = self._get_padding_width(column._index)
+        if column.width is not None:
+            # Fixed width column
+            return Measurement(
+                column.width + padding_width, column.width + padding_width
+            ).with_maximum(max_width)
+        # Flexible column, we need to measure contents
+        min_widths: List[int] = []
+        max_widths: List[int] = []
+        append_min = min_widths.append
+        append_max = max_widths.append
+        get_render_width = Measurement.get
+        for cell in self._get_cells(console, column._index, column):
+            _min, _max = get_render_width(console, options, cell.renderable)
+            append_min(_min)
+            append_max(_max)
+
+        measurement = Measurement(
+            max(min_widths) if min_widths else 1,
+            max(max_widths) if max_widths else max_width,
+        ).with_maximum(max_width)
+        measurement = measurement.clamp(
+            None if column.min_width is None else column.min_width + padding_width,
+            None if column.max_width is None else column.max_width + padding_width,
+        )
+        return measurement
+
+    def _render(
+        self, console: "Console", options: "ConsoleOptions", widths: List[int]
+    ) -> "RenderResult":
+        table_style = console.get_style(self.style or "")
+
+        border_style = table_style + console.get_style(self.border_style or "")
+        _column_cells = (
+            self._get_cells(console, column_index, column)
+            for column_index, column in enumerate(self.columns)
+        )
+
+        row_cells: List[Tuple[_Cell, ...]] = list(zip(*_column_cells))
+        _box = (
+            self.box.substitute(
+                options, safe=pick_bool(self.safe_box, console.safe_box)
+            )
+            if self.box
+            else None
+        )
+        _box = _box.get_plain_headed_box() if _box and not self.show_header else _box
+
+        new_line = Segment.line()
+
+        columns = self.columns
+        show_header = self.show_header
+        show_footer = self.show_footer
+        show_edge = self.show_edge
+        show_lines = self.show_lines
+        leading = self.leading
+
+        _Segment = Segment
+        if _box:
+            box_segments = [
+                (
+                    _Segment(_box.head_left, border_style),
+                    _Segment(_box.head_right, border_style),
+                    _Segment(_box.head_vertical, border_style),
+                ),
+                (
+                    _Segment(_box.mid_left, border_style),
+                    _Segment(_box.mid_right, border_style),
+                    _Segment(_box.mid_vertical, border_style),
+                ),
+                (
+                    _Segment(_box.foot_left, border_style),
+                    _Segment(_box.foot_right, border_style),
+                    _Segment(_box.foot_vertical, border_style),
+                ),
+            ]
+            if show_edge:
+                yield _Segment(_box.get_top(widths), border_style)
+                yield new_line
+        else:
+            box_segments = []
+
+        get_row_style = self.get_row_style
+        get_style = console.get_style
+
+        for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):
+            header_row = first and show_header
+            footer_row = last and show_footer
+            row = (
+                self.rows[index - show_header]
+                if (not header_row and not footer_row)
+                else None
+            )
+            max_height = 1
+            cells: List[List[List[Segment]]] = []
+            if header_row or footer_row:
+                row_style = Style.null()
+            else:
+                row_style = get_style(
+                    get_row_style(console, index - 1 if show_header else index)
+                )
+            for width, cell, column in zip(widths, row_cell, columns):
+                render_options = options.update(
+                    width=width,
+                    justify=column.justify,
+                    no_wrap=column.no_wrap,
+                    overflow=column.overflow,
+                    height=None,
+                    highlight=column.highlight,
+                )
+                lines = console.render_lines(
+                    cell.renderable,
+                    render_options,
+                    style=get_style(cell.style) + row_style,
+                )
+                max_height = max(max_height, len(lines))
+                cells.append(lines)
+
+            row_height = max(len(cell) for cell in cells)
+
+            def align_cell(
+                cell: List[List[Segment]],
+                vertical: "VerticalAlignMethod",
+                width: int,
+                style: Style,
+            ) -> List[List[Segment]]:
+                if header_row:
+                    vertical = "bottom"
+                elif footer_row:
+                    vertical = "top"
+
+                if vertical == "top":
+                    return _Segment.align_top(cell, width, row_height, style)
+                elif vertical == "middle":
+                    return _Segment.align_middle(cell, width, row_height, style)
+                return _Segment.align_bottom(cell, width, row_height, style)
+
+            cells[:] = [
+                _Segment.set_shape(
+                    align_cell(
+                        cell,
+                        _cell.vertical,
+                        width,
+                        get_style(_cell.style) + row_style,
+                    ),
+                    width,
+                    max_height,
+                )
+                for width, _cell, cell, column in zip(widths, row_cell, cells, columns)
+            ]
+
+            if _box:
+                if last and show_footer:
+                    yield _Segment(
+                        _box.get_row(widths, "foot", edge=show_edge), border_style
+                    )
+                    yield new_line
+                left, right, _divider = box_segments[0 if first else (2 if last else 1)]
+
+                # If the column divider is whitespace also style it with the row background
+                divider = (
+                    _divider
+                    if _divider.text.strip()
+                    else _Segment(
+                        _divider.text, row_style.background_style + _divider.style
+                    )
+                )
+                for line_no in range(max_height):
+                    if show_edge:
+                        yield left
+                    for last_cell, rendered_cell in loop_last(cells):
+                        yield from rendered_cell[line_no]
+                        if not last_cell:
+                            yield divider
+                    if show_edge:
+                        yield right
+                    yield new_line
+            else:
+                for line_no in range(max_height):
+                    for rendered_cell in cells:
+                        yield from rendered_cell[line_no]
+                    yield new_line
+            if _box and first and show_header:
+                yield _Segment(
+                    _box.get_row(widths, "head", edge=show_edge), border_style
+                )
+                yield new_line
+            end_section = row and row.end_section
+            if _box and (show_lines or leading or end_section):
+                if (
+                    not last
+                    and not (show_footer and index >= len(row_cells) - 2)
+                    and not (show_header and header_row)
+                ):
+                    if leading:
+                        yield _Segment(
+                            _box.get_row(widths, "mid", edge=show_edge) * leading,
+                            border_style,
+                        )
+                    else:
+                        yield _Segment(
+                            _box.get_row(widths, "row", edge=show_edge), border_style
+                        )
+                    yield new_line
+
+        if _box and show_edge:
+            yield _Segment(_box.get_bottom(widths), border_style)
+            yield new_line
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich.console import Console
+    from rich.highlighter import ReprHighlighter
+
+    from ._timer import timer
+
+    with timer("Table render"):
+        table = Table(
+            title="Star Wars Movies",
+            caption="Rich example table",
+            caption_justify="right",
+        )
+
+        table.add_column(
+            "Released", header_style="bright_cyan", style="cyan", no_wrap=True
+        )
+        table.add_column("Title", style="magenta")
+        table.add_column("Box Office", justify="right", style="green")
+
+        table.add_row(
+            "Dec 20, 2019",
+            "Star Wars: The Rise of Skywalker",
+            "$952,110,690",
+        )
+        table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
+        table.add_row(
+            "Dec 15, 2017",
+            "Star Wars Ep. V111: The Last Jedi",
+            "$1,332,539,889",
+            style="on black",
+            end_section=True,
+        )
+        table.add_row(
+            "Dec 16, 2016",
+            "Rogue One: A Star Wars Story",
+            "$1,332,439,889",
+        )
+
+        def header(text: str) -> None:
+            console.print()
+            console.rule(highlight(text))
+            console.print()
+
+        console = Console()
+        highlight = ReprHighlighter()
+        header("Example Table")
+        console.print(table, justify="center")
+
+        table.expand = True
+        header("expand=True")
+        console.print(table)
+
+        table.width = 50
+        header("width=50")
+
+        console.print(table, justify="center")
+
+        table.width = None
+        table.expand = False
+        table.row_styles = ["dim", "none"]
+        header("row_styles=['dim', 'none']")
+
+        console.print(table, justify="center")
+
+        table.width = None
+        table.expand = False
+        table.row_styles = ["dim", "none"]
+        table.leading = 1
+        header("leading=1, row_styles=['dim', 'none']")
+        console.print(table, justify="center")
+
+        table.width = None
+        table.expand = False
+        table.row_styles = ["dim", "none"]
+        table.show_lines = True
+        table.leading = 0
+        header("show_lines=True, row_styles=['dim', 'none']")
+        console.print(table, justify="center")
diff --git a/.venv/lib/python3.12/site-packages/rich/terminal_theme.py b/.venv/lib/python3.12/site-packages/rich/terminal_theme.py
new file mode 100644
index 0000000..565e9d9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/terminal_theme.py
@@ -0,0 +1,153 @@
+from typing import List, Optional, Tuple
+
+from .color_triplet import ColorTriplet
+from .palette import Palette
+
+_ColorTuple = Tuple[int, int, int]
+
+
+class TerminalTheme:
+    """A color theme used when exporting console content.
+
+    Args:
+        background (Tuple[int, int, int]): The background color.
+        foreground (Tuple[int, int, int]): The foreground (text) color.
+        normal (List[Tuple[int, int, int]]): A list of 8 normal intensity colors.
+        bright (List[Tuple[int, int, int]], optional): A list of 8 bright colors, or None
+            to repeat normal intensity. Defaults to None.
+    """
+
+    def __init__(
+        self,
+        background: _ColorTuple,
+        foreground: _ColorTuple,
+        normal: List[_ColorTuple],
+        bright: Optional[List[_ColorTuple]] = None,
+    ) -> None:
+        self.background_color = ColorTriplet(*background)
+        self.foreground_color = ColorTriplet(*foreground)
+        self.ansi_colors = Palette(normal + (bright or normal))
+
+
+DEFAULT_TERMINAL_THEME = TerminalTheme(
+    (255, 255, 255),
+    (0, 0, 0),
+    [
+        (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),
+    ],
+)
+
+MONOKAI = TerminalTheme(
+    (12, 12, 12),
+    (217, 217, 217),
+    [
+        (26, 26, 26),
+        (244, 0, 95),
+        (152, 224, 36),
+        (253, 151, 31),
+        (157, 101, 255),
+        (244, 0, 95),
+        (88, 209, 235),
+        (196, 197, 181),
+        (98, 94, 76),
+    ],
+    [
+        (244, 0, 95),
+        (152, 224, 36),
+        (224, 213, 97),
+        (157, 101, 255),
+        (244, 0, 95),
+        (88, 209, 235),
+        (246, 246, 239),
+    ],
+)
+DIMMED_MONOKAI = TerminalTheme(
+    (25, 25, 25),
+    (185, 188, 186),
+    [
+        (58, 61, 67),
+        (190, 63, 72),
+        (135, 154, 59),
+        (197, 166, 53),
+        (79, 118, 161),
+        (133, 92, 141),
+        (87, 143, 164),
+        (185, 188, 186),
+        (136, 137, 135),
+    ],
+    [
+        (251, 0, 31),
+        (15, 114, 47),
+        (196, 112, 51),
+        (24, 109, 227),
+        (251, 0, 103),
+        (46, 112, 109),
+        (253, 255, 185),
+    ],
+)
+NIGHT_OWLISH = TerminalTheme(
+    (255, 255, 255),
+    (64, 63, 83),
+    [
+        (1, 22, 39),
+        (211, 66, 62),
+        (42, 162, 152),
+        (218, 170, 1),
+        (72, 118, 214),
+        (64, 63, 83),
+        (8, 145, 106),
+        (122, 129, 129),
+        (122, 129, 129),
+    ],
+    [
+        (247, 110, 110),
+        (73, 208, 197),
+        (218, 194, 107),
+        (92, 167, 228),
+        (105, 112, 152),
+        (0, 201, 144),
+        (152, 159, 177),
+    ],
+)
+
+SVG_EXPORT_THEME = TerminalTheme(
+    (41, 41, 41),
+    (197, 200, 198),
+    [
+        (75, 78, 85),
+        (204, 85, 90),
+        (152, 168, 75),
+        (208, 179, 68),
+        (96, 138, 177),
+        (152, 114, 159),
+        (104, 160, 179),
+        (197, 200, 198),
+        (154, 155, 153),
+    ],
+    [
+        (255, 38, 39),
+        (0, 130, 61),
+        (208, 132, 66),
+        (25, 132, 233),
+        (255, 44, 122),
+        (57, 130, 128),
+        (253, 253, 197),
+    ],
+)
diff --git a/.venv/lib/python3.12/site-packages/rich/text.py b/.venv/lib/python3.12/site-packages/rich/text.py
new file mode 100644
index 0000000..7e087a4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/text.py
@@ -0,0 +1,1363 @@
+import re
+from functools import partial, reduce
+from math import gcd
+from operator import itemgetter
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Pattern,
+    Tuple,
+    Union,
+)
+
+from ._loop import loop_last
+from ._pick import pick_bool
+from ._wrap import divide_line
+from .align import AlignMethod
+from .cells import cell_len, set_cell_size
+from .containers import Lines
+from .control import strip_control_codes
+from .emoji import EmojiVariant
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleType
+
+if TYPE_CHECKING:  # pragma: no cover
+    from .console import Console, ConsoleOptions, JustifyMethod, OverflowMethod
+
+DEFAULT_JUSTIFY: "JustifyMethod" = "default"
+DEFAULT_OVERFLOW: "OverflowMethod" = "fold"
+
+
+_re_whitespace = re.compile(r"\s+$")
+
+TextType = Union[str, "Text"]
+"""A plain string or a :class:`Text` instance."""
+
+GetStyleCallable = Callable[[str], Optional[StyleType]]
+
+
+class Span(NamedTuple):
+    """A marked up region in some text."""
+
+    start: int
+    """Span start index."""
+    end: int
+    """Span end index."""
+    style: Union[str, Style]
+    """Style associated with the span."""
+
+    def __repr__(self) -> str:
+        return f"Span({self.start}, {self.end}, {self.style!r})"
+
+    def __bool__(self) -> bool:
+        return self.end > self.start
+
+    def split(self, offset: int) -> Tuple["Span", Optional["Span"]]:
+        """Split a span in to 2 from a given offset."""
+
+        if offset < self.start:
+            return self, None
+        if offset >= self.end:
+            return self, None
+
+        start, end, style = self
+        span1 = Span(start, min(end, offset), style)
+        span2 = Span(span1.end, end, style)
+        return span1, span2
+
+    def move(self, offset: int) -> "Span":
+        """Move start and end by a given offset.
+
+        Args:
+            offset (int): Number of characters to add to start and end.
+
+        Returns:
+            TextSpan: A new TextSpan with adjusted position.
+        """
+        start, end, style = self
+        return Span(start + offset, end + offset, style)
+
+    def right_crop(self, offset: int) -> "Span":
+        """Crop the span at the given offset.
+
+        Args:
+            offset (int): A value between start and end.
+
+        Returns:
+            Span: A new (possibly smaller) span.
+        """
+        start, end, style = self
+        if offset >= end:
+            return self
+        return Span(start, min(offset, end), style)
+
+    def extend(self, cells: int) -> "Span":
+        """Extend the span by the given number of cells.
+
+        Args:
+            cells (int): Additional space to add to end of span.
+
+        Returns:
+            Span: A span.
+        """
+        if cells:
+            start, end, style = self
+            return Span(start, end + cells, style)
+        else:
+            return self
+
+
+class Text(JupyterMixin):
+    """Text with color / style.
+
+    Args:
+        text (str, optional): Default unstyled text. Defaults to "".
+        style (Union[str, Style], optional): Base style for text. Defaults to "".
+        justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+        overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+        no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None.
+        end (str, optional): Character to end text with. Defaults to "\\\\n".
+        tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None.
+        spans (List[Span], optional). A list of predefined style spans. Defaults to None.
+    """
+
+    __slots__ = [
+        "_text",
+        "style",
+        "justify",
+        "overflow",
+        "no_wrap",
+        "end",
+        "tab_size",
+        "_spans",
+        "_length",
+    ]
+
+    def __init__(
+        self,
+        text: str = "",
+        style: Union[str, Style] = "",
+        *,
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        no_wrap: Optional[bool] = None,
+        end: str = "\n",
+        tab_size: Optional[int] = None,
+        spans: Optional[List[Span]] = None,
+    ) -> None:
+        sanitized_text = strip_control_codes(text)
+        self._text = [sanitized_text]
+        self.style = style
+        self.justify: Optional["JustifyMethod"] = justify
+        self.overflow: Optional["OverflowMethod"] = overflow
+        self.no_wrap = no_wrap
+        self.end = end
+        self.tab_size = tab_size
+        self._spans: List[Span] = spans or []
+        self._length: int = len(sanitized_text)
+
+    def __len__(self) -> int:
+        return self._length
+
+    def __bool__(self) -> bool:
+        return bool(self._length)
+
+    def __str__(self) -> str:
+        return self.plain
+
+    def __repr__(self) -> str:
+        return f""
+
+    def __add__(self, other: Any) -> "Text":
+        if isinstance(other, (str, Text)):
+            result = self.copy()
+            result.append(other)
+            return result
+        return NotImplemented
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Text):
+            return NotImplemented
+        return self.plain == other.plain and self._spans == other._spans
+
+    def __contains__(self, other: object) -> bool:
+        if isinstance(other, str):
+            return other in self.plain
+        elif isinstance(other, Text):
+            return other.plain in self.plain
+        return False
+
+    def __getitem__(self, slice: Union[int, slice]) -> "Text":
+        def get_text_at(offset: int) -> "Text":
+            _Span = Span
+            text = Text(
+                self.plain[offset],
+                spans=[
+                    _Span(0, 1, style)
+                    for start, end, style in self._spans
+                    if end > offset >= start
+                ],
+                end="",
+            )
+            return text
+
+        if isinstance(slice, int):
+            return get_text_at(slice)
+        else:
+            start, stop, step = slice.indices(len(self.plain))
+            if step == 1:
+                lines = self.divide([start, stop])
+                return lines[1]
+            else:
+                # This would be a bit of work to implement efficiently
+                # For now, its not required
+                raise TypeError("slices with step!=1 are not supported")
+
+    @property
+    def cell_len(self) -> int:
+        """Get the number of cells required to render this text."""
+        return cell_len(self.plain)
+
+    @property
+    def markup(self) -> str:
+        """Get console markup to render this Text.
+
+        Returns:
+            str: A string potentially creating markup tags.
+        """
+        from .markup import escape
+
+        output: List[str] = []
+
+        plain = self.plain
+        markup_spans = [
+            (0, False, self.style),
+            *((span.start, False, span.style) for span in self._spans),
+            *((span.end, True, span.style) for span in self._spans),
+            (len(plain), True, self.style),
+        ]
+        markup_spans.sort(key=itemgetter(0, 1))
+        position = 0
+        append = output.append
+        for offset, closing, style in markup_spans:
+            if offset > position:
+                append(escape(plain[position:offset]))
+                position = offset
+            if style:
+                append(f"[/{style}]" if closing else f"[{style}]")
+        markup = "".join(output)
+        return markup
+
+    @classmethod
+    def from_markup(
+        cls,
+        text: str,
+        *,
+        style: Union[str, Style] = "",
+        emoji: bool = True,
+        emoji_variant: Optional[EmojiVariant] = None,
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        end: str = "\n",
+    ) -> "Text":
+        """Create Text instance from markup.
+
+        Args:
+            text (str): A string containing console markup.
+            style (Union[str, Style], optional): Base style for text. Defaults to "".
+            emoji (bool, optional): Also render emoji code. Defaults to True.
+            emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
+            justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+            overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+            end (str, optional): Character to end text with. Defaults to "\\\\n".
+
+        Returns:
+            Text: A Text instance with markup rendered.
+        """
+        from .markup import render
+
+        rendered_text = render(text, style, emoji=emoji, emoji_variant=emoji_variant)
+        rendered_text.justify = justify
+        rendered_text.overflow = overflow
+        rendered_text.end = end
+        return rendered_text
+
+    @classmethod
+    def from_ansi(
+        cls,
+        text: str,
+        *,
+        style: Union[str, Style] = "",
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        no_wrap: Optional[bool] = None,
+        end: str = "\n",
+        tab_size: Optional[int] = 8,
+    ) -> "Text":
+        """Create a Text object from a string containing ANSI escape codes.
+
+        Args:
+            text (str): A string containing escape codes.
+            style (Union[str, Style], optional): Base style for text. Defaults to "".
+            justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+            overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+            no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None.
+            end (str, optional): Character to end text with. Defaults to "\\\\n".
+            tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None.
+        """
+        from .ansi import AnsiDecoder
+
+        joiner = Text(
+            "\n",
+            justify=justify,
+            overflow=overflow,
+            no_wrap=no_wrap,
+            end=end,
+            tab_size=tab_size,
+            style=style,
+        )
+        decoder = AnsiDecoder()
+        result = joiner.join(line for line in decoder.decode(text))
+        return result
+
+    @classmethod
+    def styled(
+        cls,
+        text: str,
+        style: StyleType = "",
+        *,
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+    ) -> "Text":
+        """Construct a Text instance with a pre-applied styled. A style applied in this way won't be used
+        to pad the text when it is justified.
+
+        Args:
+            text (str): A string containing console markup.
+            style (Union[str, Style]): Style to apply to the text. Defaults to "".
+            justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+            overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+
+        Returns:
+            Text: A text instance with a style applied to the entire string.
+        """
+        styled_text = cls(text, justify=justify, overflow=overflow)
+        styled_text.stylize(style)
+        return styled_text
+
+    @classmethod
+    def assemble(
+        cls,
+        *parts: Union[str, "Text", Tuple[str, StyleType]],
+        style: Union[str, Style] = "",
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        no_wrap: Optional[bool] = None,
+        end: str = "\n",
+        tab_size: int = 8,
+        meta: Optional[Dict[str, Any]] = None,
+    ) -> "Text":
+        """Construct a text instance by combining a sequence of strings with optional styles.
+        The positional arguments should be either strings, or a tuple of string + style.
+
+        Args:
+            style (Union[str, Style], optional): Base style for text. Defaults to "".
+            justify (str, optional): Justify method: "left", "center", "full", "right". Defaults to None.
+            overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None.
+            no_wrap (bool, optional): Disable text wrapping, or None for default. Defaults to None.
+            end (str, optional): Character to end text with. Defaults to "\\\\n".
+            tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to None.
+            meta (Dict[str, Any], optional). Meta data to apply to text, or None for no meta data. Default to None
+
+        Returns:
+            Text: A new text instance.
+        """
+        text = cls(
+            style=style,
+            justify=justify,
+            overflow=overflow,
+            no_wrap=no_wrap,
+            end=end,
+            tab_size=tab_size,
+        )
+        append = text.append
+        _Text = Text
+        for part in parts:
+            if isinstance(part, (_Text, str)):
+                append(part)
+            else:
+                append(*part)
+        if meta:
+            text.apply_meta(meta)
+        return text
+
+    @property
+    def plain(self) -> str:
+        """Get the text as a single string."""
+        if len(self._text) != 1:
+            self._text[:] = ["".join(self._text)]
+        return self._text[0]
+
+    @plain.setter
+    def plain(self, new_text: str) -> None:
+        """Set the text to a new value."""
+        if new_text != self.plain:
+            sanitized_text = strip_control_codes(new_text)
+            self._text[:] = [sanitized_text]
+            old_length = self._length
+            self._length = len(sanitized_text)
+            if old_length > self._length:
+                self._trim_spans()
+
+    @property
+    def spans(self) -> List[Span]:
+        """Get a reference to the internal list of spans."""
+        return self._spans
+
+    @spans.setter
+    def spans(self, spans: List[Span]) -> None:
+        """Set spans."""
+        self._spans = spans[:]
+
+    def blank_copy(self, plain: str = "") -> "Text":
+        """Return a new Text instance with copied metadata (but not the string or spans)."""
+        copy_self = Text(
+            plain,
+            style=self.style,
+            justify=self.justify,
+            overflow=self.overflow,
+            no_wrap=self.no_wrap,
+            end=self.end,
+            tab_size=self.tab_size,
+        )
+        return copy_self
+
+    def copy(self) -> "Text":
+        """Return a copy of this instance."""
+        copy_self = Text(
+            self.plain,
+            style=self.style,
+            justify=self.justify,
+            overflow=self.overflow,
+            no_wrap=self.no_wrap,
+            end=self.end,
+            tab_size=self.tab_size,
+        )
+        copy_self._spans[:] = self._spans
+        return copy_self
+
+    def stylize(
+        self,
+        style: Union[str, Style],
+        start: int = 0,
+        end: Optional[int] = None,
+    ) -> None:
+        """Apply a style to the text, or a portion of the text.
+
+        Args:
+            style (Union[str, Style]): Style instance or style definition to apply.
+            start (int): Start offset (negative indexing is supported). Defaults to 0.
+            end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None.
+        """
+        if style:
+            length = len(self)
+            if start < 0:
+                start = length + start
+            if end is None:
+                end = length
+            if end < 0:
+                end = length + end
+            if start >= length or end <= start:
+                # Span not in text or not valid
+                return
+            self._spans.append(Span(start, min(length, end), style))
+
+    def stylize_before(
+        self,
+        style: Union[str, Style],
+        start: int = 0,
+        end: Optional[int] = None,
+    ) -> None:
+        """Apply a style to the text, or a portion of the text. Styles will be applied before other styles already present.
+
+        Args:
+            style (Union[str, Style]): Style instance or style definition to apply.
+            start (int): Start offset (negative indexing is supported). Defaults to 0.
+            end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None.
+        """
+        if style:
+            length = len(self)
+            if start < 0:
+                start = length + start
+            if end is None:
+                end = length
+            if end < 0:
+                end = length + end
+            if start >= length or end <= start:
+                # Span not in text or not valid
+                return
+            self._spans.insert(0, Span(start, min(length, end), style))
+
+    def apply_meta(
+        self, meta: Dict[str, Any], start: int = 0, end: Optional[int] = None
+    ) -> None:
+        """Apply metadata to the text, or a portion of the text.
+
+        Args:
+            meta (Dict[str, Any]): A dict of meta information.
+            start (int): Start offset (negative indexing is supported). Defaults to 0.
+            end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None.
+
+        """
+        style = Style.from_meta(meta)
+        self.stylize(style, start=start, end=end)
+
+    def on(self, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Text":
+        """Apply event handlers (used by Textual project).
+
+        Example:
+            >>> from rich.text import Text
+            >>> text = Text("hello world")
+            >>> text.on(click="view.toggle('world')")
+
+        Args:
+            meta (Dict[str, Any]): Mapping of meta information.
+            **handlers: Keyword args are prefixed with "@" to defined handlers.
+
+        Returns:
+            Text: Self is returned to method may be chained.
+        """
+        meta = {} if meta is None else meta
+        meta.update({f"@{key}": value for key, value in handlers.items()})
+        self.stylize(Style.from_meta(meta))
+        return self
+
+    def remove_suffix(self, suffix: str) -> None:
+        """Remove a suffix if it exists.
+
+        Args:
+            suffix (str): Suffix to remove.
+        """
+        if self.plain.endswith(suffix):
+            self.right_crop(len(suffix))
+
+    def get_style_at_offset(self, console: "Console", offset: int) -> Style:
+        """Get the style of a character at give offset.
+
+        Args:
+            console (~Console): Console where text will be rendered.
+            offset (int): Offset in to text (negative indexing supported)
+
+        Returns:
+            Style: A Style instance.
+        """
+        # TODO: This is a little inefficient, it is only used by full justify
+        if offset < 0:
+            offset = len(self) + offset
+        get_style = console.get_style
+        style = get_style(self.style).copy()
+        for start, end, span_style in self._spans:
+            if end > offset >= start:
+                style += get_style(span_style, default="")
+        return style
+
+    def extend_style(self, spaces: int) -> None:
+        """Extend the Text given number of spaces where the spaces have the same style as the last character.
+
+        Args:
+            spaces (int): Number of spaces to add to the Text.
+        """
+        if spaces <= 0:
+            return
+        spans = self.spans
+        new_spaces = " " * spaces
+        if spans:
+            end_offset = len(self)
+            self._spans[:] = [
+                span.extend(spaces) if span.end >= end_offset else span
+                for span in spans
+            ]
+            self._text.append(new_spaces)
+            self._length += spaces
+        else:
+            self.plain += new_spaces
+
+    def highlight_regex(
+        self,
+        re_highlight: Union[Pattern[str], str],
+        style: Optional[Union[GetStyleCallable, StyleType]] = None,
+        *,
+        style_prefix: str = "",
+    ) -> int:
+        """Highlight text with a regular expression, where group names are
+        translated to styles.
+
+        Args:
+            re_highlight (Union[re.Pattern, str]): A regular expression object or string.
+            style (Union[GetStyleCallable, StyleType]): Optional style to apply to whole match, or a callable
+                which accepts the matched text and returns a style. Defaults to None.
+            style_prefix (str, optional): Optional prefix to add to style group names.
+
+        Returns:
+            int: Number of regex matches
+        """
+        count = 0
+        append_span = self._spans.append
+        _Span = Span
+        plain = self.plain
+        if isinstance(re_highlight, str):
+            re_highlight = re.compile(re_highlight)
+        for match in re_highlight.finditer(plain):
+            get_span = match.span
+            if style:
+                start, end = get_span()
+                match_style = style(plain[start:end]) if callable(style) else style
+                if match_style is not None and end > start:
+                    append_span(_Span(start, end, match_style))
+
+            count += 1
+            for name in match.groupdict().keys():
+                start, end = get_span(name)
+                if start != -1 and end > start:
+                    append_span(_Span(start, end, f"{style_prefix}{name}"))
+        return count
+
+    def highlight_words(
+        self,
+        words: Iterable[str],
+        style: Union[str, Style],
+        *,
+        case_sensitive: bool = True,
+    ) -> int:
+        """Highlight words with a style.
+
+        Args:
+            words (Iterable[str]): Words to highlight.
+            style (Union[str, Style]): Style to apply.
+            case_sensitive (bool, optional): Enable case sensitive matching. Defaults to True.
+
+        Returns:
+            int: Number of words highlighted.
+        """
+        re_words = "|".join(re.escape(word) for word in words)
+        add_span = self._spans.append
+        count = 0
+        _Span = Span
+        for match in re.finditer(
+            re_words, self.plain, flags=0 if case_sensitive else re.IGNORECASE
+        ):
+            start, end = match.span(0)
+            add_span(_Span(start, end, style))
+            count += 1
+        return count
+
+    def rstrip(self) -> None:
+        """Strip whitespace from end of text."""
+        self.plain = self.plain.rstrip()
+
+    def rstrip_end(self, size: int) -> None:
+        """Remove whitespace beyond a certain width at the end of the text.
+
+        Args:
+            size (int): The desired size of the text.
+        """
+        text_length = len(self)
+        if text_length > size:
+            excess = text_length - size
+            whitespace_match = _re_whitespace.search(self.plain)
+            if whitespace_match is not None:
+                whitespace_count = len(whitespace_match.group(0))
+                self.right_crop(min(whitespace_count, excess))
+
+    def set_length(self, new_length: int) -> None:
+        """Set new length of the text, clipping or padding is required."""
+        length = len(self)
+        if length != new_length:
+            if length < new_length:
+                self.pad_right(new_length - length)
+            else:
+                self.right_crop(length - new_length)
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> Iterable[Segment]:
+        tab_size: int = console.tab_size if self.tab_size is None else self.tab_size
+        justify = self.justify or options.justify or DEFAULT_JUSTIFY
+        overflow = self.overflow or options.overflow or DEFAULT_OVERFLOW
+
+        lines = self.wrap(
+            console,
+            options.max_width,
+            justify=justify,
+            overflow=overflow,
+            tab_size=tab_size or 8,
+            no_wrap=pick_bool(self.no_wrap, options.no_wrap, False),
+        )
+        all_lines = Text("\n").join(lines)
+        yield from all_lines.render(console, end=self.end)
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> Measurement:
+        text = self.plain
+        lines = text.splitlines()
+        max_text_width = max(cell_len(line) for line in lines) if lines else 0
+        words = text.split()
+        min_text_width = (
+            max(cell_len(word) for word in words) if words else max_text_width
+        )
+        return Measurement(min_text_width, max_text_width)
+
+    def render(self, console: "Console", end: str = "") -> Iterable["Segment"]:
+        """Render the text as Segments.
+
+        Args:
+            console (Console): Console instance.
+            end (Optional[str], optional): Optional end character.
+
+        Returns:
+            Iterable[Segment]: Result of render that may be written to the console.
+        """
+        _Segment = Segment
+        text = self.plain
+        if not self._spans:
+            yield Segment(text)
+            if end:
+                yield _Segment(end)
+            return
+        get_style = partial(console.get_style, default=Style.null())
+
+        enumerated_spans = list(enumerate(self._spans, 1))
+        style_map = {index: get_style(span.style) for index, span in enumerated_spans}
+        style_map[0] = get_style(self.style)
+
+        spans = [
+            (0, False, 0),
+            *((span.start, False, index) for index, span in enumerated_spans),
+            *((span.end, True, index) for index, span in enumerated_spans),
+            (len(text), True, 0),
+        ]
+        spans.sort(key=itemgetter(0, 1))
+
+        stack: List[int] = []
+        stack_append = stack.append
+        stack_pop = stack.remove
+
+        style_cache: Dict[Tuple[Style, ...], Style] = {}
+        style_cache_get = style_cache.get
+        combine = Style.combine
+
+        def get_current_style() -> Style:
+            """Construct current style from stack."""
+            styles = tuple(style_map[_style_id] for _style_id in sorted(stack))
+            cached_style = style_cache_get(styles)
+            if cached_style is not None:
+                return cached_style
+            current_style = combine(styles)
+            style_cache[styles] = current_style
+            return current_style
+
+        for (offset, leaving, style_id), (next_offset, _, _) in zip(spans, spans[1:]):
+            if leaving:
+                stack_pop(style_id)
+            else:
+                stack_append(style_id)
+            if next_offset > offset:
+                yield _Segment(text[offset:next_offset], get_current_style())
+        if end:
+            yield _Segment(end)
+
+    def join(self, lines: Iterable["Text"]) -> "Text":
+        """Join text together with this instance as the separator.
+
+        Args:
+            lines (Iterable[Text]): An iterable of Text instances to join.
+
+        Returns:
+            Text: A new text instance containing join text.
+        """
+
+        new_text = self.blank_copy()
+
+        def iter_text() -> Iterable["Text"]:
+            if self.plain:
+                for last, line in loop_last(lines):
+                    yield line
+                    if not last:
+                        yield self
+            else:
+                yield from lines
+
+        extend_text = new_text._text.extend
+        append_span = new_text._spans.append
+        extend_spans = new_text._spans.extend
+        offset = 0
+        _Span = Span
+
+        for text in iter_text():
+            extend_text(text._text)
+            if text.style:
+                append_span(_Span(offset, offset + len(text), text.style))
+            extend_spans(
+                _Span(offset + start, offset + end, style)
+                for start, end, style in text._spans
+            )
+            offset += len(text)
+        new_text._length = offset
+        return new_text
+
+    def expand_tabs(self, tab_size: Optional[int] = None) -> None:
+        """Converts tabs to spaces.
+
+        Args:
+            tab_size (int, optional): Size of tabs. Defaults to 8.
+
+        """
+        if "\t" not in self.plain:
+            return
+        if tab_size is None:
+            tab_size = self.tab_size
+        if tab_size is None:
+            tab_size = 8
+
+        new_text: List[Text] = []
+        append = new_text.append
+
+        for line in self.split("\n", include_separator=True):
+            if "\t" not in line.plain:
+                append(line)
+            else:
+                cell_position = 0
+                parts = line.split("\t", include_separator=True)
+                for part in parts:
+                    if part.plain.endswith("\t"):
+                        part._text[-1] = part._text[-1][:-1] + " "
+                        cell_position += part.cell_len
+                        tab_remainder = cell_position % tab_size
+                        if tab_remainder:
+                            spaces = tab_size - tab_remainder
+                            part.extend_style(spaces)
+                            cell_position += spaces
+                    else:
+                        cell_position += part.cell_len
+                    append(part)
+
+        result = Text("").join(new_text)
+
+        self._text = [result.plain]
+        self._length = len(self.plain)
+        self._spans[:] = result._spans
+
+    def truncate(
+        self,
+        max_width: int,
+        *,
+        overflow: Optional["OverflowMethod"] = None,
+        pad: bool = False,
+    ) -> None:
+        """Truncate text if it is longer that a given width.
+
+        Args:
+            max_width (int): Maximum number of characters in text.
+            overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None, to use self.overflow.
+            pad (bool, optional): Pad with spaces if the length is less than max_width. Defaults to False.
+        """
+        _overflow = overflow or self.overflow or DEFAULT_OVERFLOW
+        if _overflow != "ignore":
+            length = cell_len(self.plain)
+            if length > max_width:
+                if _overflow == "ellipsis":
+                    self.plain = set_cell_size(self.plain, max_width - 1) + "…"
+                else:
+                    self.plain = set_cell_size(self.plain, max_width)
+            if pad and length < max_width:
+                spaces = max_width - length
+                self._text = [f"{self.plain}{' ' * spaces}"]
+                self._length = len(self.plain)
+
+    def _trim_spans(self) -> None:
+        """Remove or modify any spans that are over the end of the text."""
+        max_offset = len(self.plain)
+        _Span = Span
+        self._spans[:] = [
+            (
+                span
+                if span.end < max_offset
+                else _Span(span.start, min(max_offset, span.end), span.style)
+            )
+            for span in self._spans
+            if span.start < max_offset
+        ]
+
+    def pad(self, count: int, character: str = " ") -> None:
+        """Pad left and right with a given number of characters.
+
+        Args:
+            count (int): Width of padding.
+            character (str): The character to pad with. Must be a string of length 1.
+        """
+        assert len(character) == 1, "Character must be a string of length 1"
+        if count:
+            pad_characters = character * count
+            self.plain = f"{pad_characters}{self.plain}{pad_characters}"
+            _Span = Span
+            self._spans[:] = [
+                _Span(start + count, end + count, style)
+                for start, end, style in self._spans
+            ]
+
+    def pad_left(self, count: int, character: str = " ") -> None:
+        """Pad the left with a given character.
+
+        Args:
+            count (int): Number of characters to pad.
+            character (str, optional): Character to pad with. Defaults to " ".
+        """
+        assert len(character) == 1, "Character must be a string of length 1"
+        if count:
+            self.plain = f"{character * count}{self.plain}"
+            _Span = Span
+            self._spans[:] = [
+                _Span(start + count, end + count, style)
+                for start, end, style in self._spans
+            ]
+
+    def pad_right(self, count: int, character: str = " ") -> None:
+        """Pad the right with a given character.
+
+        Args:
+            count (int): Number of characters to pad.
+            character (str, optional): Character to pad with. Defaults to " ".
+        """
+        assert len(character) == 1, "Character must be a string of length 1"
+        if count:
+            self.plain = f"{self.plain}{character * count}"
+
+    def align(self, align: AlignMethod, width: int, character: str = " ") -> None:
+        """Align text to a given width.
+
+        Args:
+            align (AlignMethod): One of "left", "center", or "right".
+            width (int): Desired width.
+            character (str, optional): Character to pad with. Defaults to " ".
+        """
+        self.truncate(width)
+        excess_space = width - cell_len(self.plain)
+        if excess_space:
+            if align == "left":
+                self.pad_right(excess_space, character)
+            elif align == "center":
+                left = excess_space // 2
+                self.pad_left(left, character)
+                self.pad_right(excess_space - left, character)
+            else:
+                self.pad_left(excess_space, character)
+
+    def append(
+        self, text: Union["Text", str], style: Optional[Union[str, "Style"]] = None
+    ) -> "Text":
+        """Add text with an optional style.
+
+        Args:
+            text (Union[Text, str]): A str or Text to append.
+            style (str, optional): A style name. Defaults to None.
+
+        Returns:
+            Text: Returns self for chaining.
+        """
+
+        if not isinstance(text, (str, Text)):
+            raise TypeError("Only str or Text can be appended to Text")
+
+        if len(text):
+            if isinstance(text, str):
+                sanitized_text = strip_control_codes(text)
+                self._text.append(sanitized_text)
+                offset = len(self)
+                text_length = len(sanitized_text)
+                if style:
+                    self._spans.append(Span(offset, offset + text_length, style))
+                self._length += text_length
+            elif isinstance(text, Text):
+                _Span = Span
+                if style is not None:
+                    raise ValueError(
+                        "style must not be set when appending Text instance"
+                    )
+                text_length = self._length
+                if text.style:
+                    self._spans.append(
+                        _Span(text_length, text_length + len(text), text.style)
+                    )
+                self._text.append(text.plain)
+                self._spans.extend(
+                    _Span(start + text_length, end + text_length, style)
+                    for start, end, style in text._spans.copy()
+                )
+                self._length += len(text)
+        return self
+
+    def append_text(self, text: "Text") -> "Text":
+        """Append another Text instance. This method is more performant than Text.append, but
+        only works for Text.
+
+        Args:
+            text (Text): The Text instance to append to this instance.
+
+        Returns:
+            Text: Returns self for chaining.
+        """
+        _Span = Span
+        text_length = self._length
+        if text.style:
+            self._spans.append(_Span(text_length, text_length + len(text), text.style))
+        self._text.append(text.plain)
+        self._spans.extend(
+            _Span(start + text_length, end + text_length, style)
+            for start, end, style in text._spans.copy()
+        )
+        self._length += len(text)
+        return self
+
+    def append_tokens(
+        self, tokens: Iterable[Tuple[str, Optional[StyleType]]]
+    ) -> "Text":
+        """Append iterable of str and style. Style may be a Style instance or a str style definition.
+
+        Args:
+            tokens (Iterable[Tuple[str, Optional[StyleType]]]): An iterable of tuples containing str content and style.
+
+        Returns:
+            Text: Returns self for chaining.
+        """
+        append_text = self._text.append
+        append_span = self._spans.append
+        _Span = Span
+        offset = len(self)
+        for content, style in tokens:
+            content = strip_control_codes(content)
+            append_text(content)
+            if style:
+                append_span(_Span(offset, offset + len(content), style))
+            offset += len(content)
+        self._length = offset
+        return self
+
+    def copy_styles(self, text: "Text") -> None:
+        """Copy styles from another Text instance.
+
+        Args:
+            text (Text): A Text instance to copy styles from, must be the same length.
+        """
+        self._spans.extend(text._spans)
+
+    def split(
+        self,
+        separator: str = "\n",
+        *,
+        include_separator: bool = False,
+        allow_blank: bool = False,
+    ) -> Lines:
+        """Split rich text in to lines, preserving styles.
+
+        Args:
+            separator (str, optional): String to split on. Defaults to "\\\\n".
+            include_separator (bool, optional): Include the separator in the lines. Defaults to False.
+            allow_blank (bool, optional): Return a blank line if the text ends with a separator. Defaults to False.
+
+        Returns:
+            List[RichText]: A list of rich text, one per line of the original.
+        """
+        assert separator, "separator must not be empty"
+
+        text = self.plain
+        if separator not in text:
+            return Lines([self.copy()])
+
+        if include_separator:
+            lines = self.divide(
+                match.end() for match in re.finditer(re.escape(separator), text)
+            )
+        else:
+
+            def flatten_spans() -> Iterable[int]:
+                for match in re.finditer(re.escape(separator), text):
+                    start, end = match.span()
+                    yield start
+                    yield end
+
+            lines = Lines(
+                line for line in self.divide(flatten_spans()) if line.plain != separator
+            )
+
+        if not allow_blank and text.endswith(separator):
+            lines.pop()
+
+        return lines
+
+    def divide(self, offsets: Iterable[int]) -> Lines:
+        """Divide text into a number of lines at given offsets.
+
+        Args:
+            offsets (Iterable[int]): Offsets used to divide text.
+
+        Returns:
+            Lines: New RichText instances between offsets.
+        """
+        _offsets = list(offsets)
+
+        if not _offsets:
+            return Lines([self.copy()])
+
+        text = self.plain
+        text_length = len(text)
+        divide_offsets = [0, *_offsets, text_length]
+        line_ranges = list(zip(divide_offsets, divide_offsets[1:]))
+
+        style = self.style
+        justify = self.justify
+        overflow = self.overflow
+        _Text = Text
+        new_lines = Lines(
+            _Text(
+                text[start:end],
+                style=style,
+                justify=justify,
+                overflow=overflow,
+            )
+            for start, end in line_ranges
+        )
+        if not self._spans:
+            return new_lines
+
+        _line_appends = [line._spans.append for line in new_lines._lines]
+        line_count = len(line_ranges)
+        _Span = Span
+
+        for span_start, span_end, style in self._spans:
+            lower_bound = 0
+            upper_bound = line_count
+            start_line_no = (lower_bound + upper_bound) // 2
+
+            while True:
+                line_start, line_end = line_ranges[start_line_no]
+                if span_start < line_start:
+                    upper_bound = start_line_no - 1
+                elif span_start > line_end:
+                    lower_bound = start_line_no + 1
+                else:
+                    break
+                start_line_no = (lower_bound + upper_bound) // 2
+
+            if span_end < line_end:
+                end_line_no = start_line_no
+            else:
+                end_line_no = lower_bound = start_line_no
+                upper_bound = line_count
+
+                while True:
+                    line_start, line_end = line_ranges[end_line_no]
+                    if span_end < line_start:
+                        upper_bound = end_line_no - 1
+                    elif span_end > line_end:
+                        lower_bound = end_line_no + 1
+                    else:
+                        break
+                    end_line_no = (lower_bound + upper_bound) // 2
+
+            for line_no in range(start_line_no, end_line_no + 1):
+                line_start, line_end = line_ranges[line_no]
+                new_start = max(0, span_start - line_start)
+                new_end = min(span_end - line_start, line_end - line_start)
+                if new_end > new_start:
+                    _line_appends[line_no](_Span(new_start, new_end, style))
+
+        return new_lines
+
+    def right_crop(self, amount: int = 1) -> None:
+        """Remove a number of characters from the end of the text."""
+        max_offset = len(self.plain) - amount
+        _Span = Span
+        self._spans[:] = [
+            (
+                span
+                if span.end < max_offset
+                else _Span(span.start, min(max_offset, span.end), span.style)
+            )
+            for span in self._spans
+            if span.start < max_offset
+        ]
+        self._text = [self.plain[:-amount]]
+        self._length -= amount
+
+    def wrap(
+        self,
+        console: "Console",
+        width: int,
+        *,
+        justify: Optional["JustifyMethod"] = None,
+        overflow: Optional["OverflowMethod"] = None,
+        tab_size: int = 8,
+        no_wrap: Optional[bool] = None,
+    ) -> Lines:
+        """Word wrap the text.
+
+        Args:
+            console (Console): Console instance.
+            width (int): Number of cells available per line.
+            justify (str, optional): Justify method: "default", "left", "center", "full", "right". Defaults to "default".
+            overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None.
+            tab_size (int, optional): Default tab size. Defaults to 8.
+            no_wrap (bool, optional): Disable wrapping, Defaults to False.
+
+        Returns:
+            Lines: Number of lines.
+        """
+        wrap_justify = justify or self.justify or DEFAULT_JUSTIFY
+        wrap_overflow = overflow or self.overflow or DEFAULT_OVERFLOW
+
+        no_wrap = pick_bool(no_wrap, self.no_wrap, False) or overflow == "ignore"
+
+        lines = Lines()
+        for line in self.split(allow_blank=True):
+            if "\t" in line:
+                line.expand_tabs(tab_size)
+            if no_wrap:
+                if overflow == "ignore":
+                    lines.append(line)
+                    continue
+                new_lines = Lines([line])
+            else:
+                offsets = divide_line(str(line), width, fold=wrap_overflow == "fold")
+                new_lines = line.divide(offsets)
+                for line in new_lines:
+                    line.rstrip_end(width)
+            if wrap_justify:
+                new_lines.justify(
+                    console, width, justify=wrap_justify, overflow=wrap_overflow
+                )
+            for line in new_lines:
+                line.truncate(width, overflow=wrap_overflow)
+            lines.extend(new_lines)
+        return lines
+
+    def fit(self, width: int) -> Lines:
+        """Fit the text in to given width by chopping in to lines.
+
+        Args:
+            width (int): Maximum characters in a line.
+
+        Returns:
+            Lines: Lines container.
+        """
+        lines: Lines = Lines()
+        append = lines.append
+        for line in self.split():
+            line.set_length(width)
+            append(line)
+        return lines
+
+    def detect_indentation(self) -> int:
+        """Auto-detect indentation of code.
+
+        Returns:
+            int: Number of spaces used to indent code.
+        """
+
+        _indentations = {
+            len(match.group(1))
+            for match in re.finditer(r"^( *)(.*)$", self.plain, flags=re.MULTILINE)
+        }
+
+        try:
+            indentation = (
+                reduce(gcd, [indent for indent in _indentations if not indent % 2]) or 1
+            )
+        except TypeError:
+            indentation = 1
+
+        return indentation
+
+    def with_indent_guides(
+        self,
+        indent_size: Optional[int] = None,
+        *,
+        character: str = "│",
+        style: StyleType = "dim green",
+    ) -> "Text":
+        """Adds indent guide lines to text.
+
+        Args:
+            indent_size (Optional[int]): Size of indentation, or None to auto detect. Defaults to None.
+            character (str, optional): Character to use for indentation. Defaults to "│".
+            style (Union[Style, str], optional): Style of indent guides.
+
+        Returns:
+            Text: New text with indentation guides.
+        """
+
+        _indent_size = self.detect_indentation() if indent_size is None else indent_size
+
+        text = self.copy()
+        text.expand_tabs()
+        indent_line = f"{character}{' ' * (_indent_size - 1)}"
+
+        re_indent = re.compile(r"^( *)(.*)$")
+        new_lines: List[Text] = []
+        add_line = new_lines.append
+        blank_lines = 0
+        for line in text.split(allow_blank=True):
+            match = re_indent.match(line.plain)
+            if not match or not match.group(2):
+                blank_lines += 1
+                continue
+            indent = match.group(1)
+            full_indents, remaining_space = divmod(len(indent), _indent_size)
+            new_indent = f"{indent_line * full_indents}{' ' * remaining_space}"
+            line.plain = new_indent + line.plain[len(new_indent) :]
+            line.stylize(style, 0, len(new_indent))
+            if blank_lines:
+                new_lines.extend([Text(new_indent, style=style)] * blank_lines)
+                blank_lines = 0
+            add_line(line)
+        if blank_lines:
+            new_lines.extend([Text("", style=style)] * blank_lines)
+
+        new_text = text.blank_copy("\n").join(new_lines)
+        return new_text
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich.console import Console
+
+    text = Text(
+        """\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n"""
+    )
+    text.highlight_words(["Lorem"], "bold")
+    text.highlight_words(["ipsum"], "italic")
+
+    console = Console()
+
+    console.rule("justify='left'")
+    console.print(text, style="red")
+    console.print()
+
+    console.rule("justify='center'")
+    console.print(text, style="green", justify="center")
+    console.print()
+
+    console.rule("justify='right'")
+    console.print(text, style="blue", justify="right")
+    console.print()
+
+    console.rule("justify='full'")
+    console.print(text, style="magenta", justify="full")
+    console.print()
diff --git a/.venv/lib/python3.12/site-packages/rich/theme.py b/.venv/lib/python3.12/site-packages/rich/theme.py
new file mode 100644
index 0000000..6b3c8c0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/theme.py
@@ -0,0 +1,116 @@
+from typing import IO, Dict, List, Mapping, Optional
+
+from .default_styles import DEFAULT_STYLES
+from .style import Style, StyleType
+
+
+class Theme:
+    """A container for style information, used by :class:`~rich.console.Console`.
+
+    Args:
+        styles (Dict[str, Style], optional): A mapping of style names on to styles. Defaults to None for a theme with no styles.
+        inherit (bool, optional): Inherit default styles. Defaults to True.
+    """
+
+    styles: Dict[str, Style]
+
+    def __init__(
+        self, styles: Optional[Mapping[str, StyleType]] = None, inherit: bool = True
+    ):
+        self.styles = DEFAULT_STYLES.copy() if inherit else {}
+        if styles is not None:
+            self.styles.update(
+                {
+                    name: style if isinstance(style, Style) else Style.parse(style)
+                    for name, style in styles.items()
+                }
+            )
+
+    @property
+    def config(self) -> str:
+        """Get contents of a config file for this theme."""
+        config = "[styles]\n" + "\n".join(
+            f"{name} = {style}" for name, style in sorted(self.styles.items())
+        )
+        return config
+
+    @classmethod
+    def from_file(
+        cls, config_file: IO[str], source: Optional[str] = None, inherit: bool = True
+    ) -> "Theme":
+        """Load a theme from a text mode file.
+
+        Args:
+            config_file (IO[str]): An open conf file.
+            source (str, optional): The filename of the open file. Defaults to None.
+            inherit (bool, optional): Inherit default styles. Defaults to True.
+
+        Returns:
+            Theme: A New theme instance.
+        """
+        import configparser
+
+        config = configparser.ConfigParser()
+        config.read_file(config_file, source=source)
+        styles = {name: Style.parse(value) for name, value in config.items("styles")}
+        theme = Theme(styles, inherit=inherit)
+        return theme
+
+    @classmethod
+    def read(
+        cls, path: str, inherit: bool = True, encoding: Optional[str] = None
+    ) -> "Theme":
+        """Read a theme from a path.
+
+        Args:
+            path (str): Path to a config file readable by Python configparser module.
+            inherit (bool, optional): Inherit default styles. Defaults to True.
+            encoding (str, optional): Encoding of the config file. Defaults to None.
+
+        Returns:
+            Theme: A new theme instance.
+        """
+        with open(path, encoding=encoding) as config_file:
+            return cls.from_file(config_file, source=path, inherit=inherit)
+
+
+class ThemeStackError(Exception):
+    """Base exception for errors related to the theme stack."""
+
+
+class ThemeStack:
+    """A stack of themes.
+
+    Args:
+        theme (Theme): A theme instance
+    """
+
+    def __init__(self, theme: Theme) -> None:
+        self._entries: List[Dict[str, Style]] = [theme.styles]
+        self.get = self._entries[-1].get
+
+    def push_theme(self, theme: Theme, inherit: bool = True) -> None:
+        """Push a theme on the top of the stack.
+
+        Args:
+            theme (Theme): A Theme instance.
+            inherit (boolean, optional): Inherit styles from current top of stack.
+        """
+        styles: Dict[str, Style]
+        styles = (
+            {**self._entries[-1], **theme.styles} if inherit else theme.styles.copy()
+        )
+        self._entries.append(styles)
+        self.get = self._entries[-1].get
+
+    def pop_theme(self) -> None:
+        """Pop (and discard) the top-most theme."""
+        if len(self._entries) == 1:
+            raise ThemeStackError("Unable to pop base theme")
+        self._entries.pop()
+        self.get = self._entries[-1].get
+
+
+if __name__ == "__main__":  # pragma: no cover
+    theme = Theme()
+    print(theme.config)
diff --git a/.venv/lib/python3.12/site-packages/rich/themes.py b/.venv/lib/python3.12/site-packages/rich/themes.py
new file mode 100644
index 0000000..bf6db10
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/themes.py
@@ -0,0 +1,5 @@
+from .default_styles import DEFAULT_STYLES
+from .theme import Theme
+
+
+DEFAULT = Theme(DEFAULT_STYLES)
diff --git a/.venv/lib/python3.12/site-packages/rich/traceback.py b/.venv/lib/python3.12/site-packages/rich/traceback.py
new file mode 100644
index 0000000..66eaeca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/traceback.py
@@ -0,0 +1,924 @@
+import inspect
+import linecache
+import os
+import sys
+from dataclasses import dataclass, field
+from itertools import islice
+from traceback import walk_tb
+from types import ModuleType, TracebackType
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    Iterable,
+    List,
+    Optional,
+    Sequence,
+    Set,
+    Tuple,
+    Type,
+    Union,
+)
+
+from pygments.lexers import guess_lexer_for_filename
+from pygments.token import Comment, Keyword, Name, Number, Operator, String
+from pygments.token import Text as TextToken
+from pygments.token import Token
+from pygments.util import ClassNotFound
+
+from . import pretty
+from ._loop import loop_first_last, loop_last
+from .columns import Columns
+from .console import (
+    Console,
+    ConsoleOptions,
+    ConsoleRenderable,
+    OverflowMethod,
+    Group,
+    RenderResult,
+    group,
+)
+from .constrain import Constrain
+from .highlighter import RegexHighlighter, ReprHighlighter
+from .panel import Panel
+from .scope import render_scope
+from .style import Style
+from .syntax import Syntax, SyntaxPosition
+from .text import Text
+from .theme import Theme
+
+WINDOWS = sys.platform == "win32"
+
+LOCALS_MAX_LENGTH = 10
+LOCALS_MAX_STRING = 80
+
+
+def _iter_syntax_lines(
+    start: SyntaxPosition, end: SyntaxPosition
+) -> Iterable[Tuple[int, int, int]]:
+    """Yield start and end positions per line.
+
+    Args:
+        start: Start position.
+        end: End position.
+
+    Returns:
+        Iterable of (LINE, COLUMN1, COLUMN2).
+    """
+
+    line1, column1 = start
+    line2, column2 = end
+
+    if line1 == line2:
+        yield line1, column1, column2
+    else:
+        for first, last, line_no in loop_first_last(range(line1, line2 + 1)):
+            if first:
+                yield line_no, column1, -1
+            elif last:
+                yield line_no, 0, column2
+            else:
+                yield line_no, 0, -1
+
+
+def install(
+    *,
+    console: Optional[Console] = None,
+    width: Optional[int] = 100,
+    code_width: Optional[int] = 88,
+    extra_lines: int = 3,
+    theme: Optional[str] = None,
+    word_wrap: bool = False,
+    show_locals: bool = False,
+    locals_max_length: int = LOCALS_MAX_LENGTH,
+    locals_max_string: int = LOCALS_MAX_STRING,
+    locals_max_depth: Optional[int] = None,
+    locals_hide_dunder: bool = True,
+    locals_hide_sunder: Optional[bool] = None,
+    locals_overflow: Optional[OverflowMethod] = None,
+    indent_guides: bool = True,
+    suppress: Iterable[Union[str, ModuleType]] = (),
+    max_frames: int = 100,
+) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]:
+    """Install a rich traceback handler.
+
+    Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.
+
+
+    Args:
+        console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance.
+        width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100.
+        code_width (Optional[int], optional): Code width (in characters) of traceback. Defaults to 88.
+        extra_lines (int, optional): Extra lines of code. Defaults to 3.
+        theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick
+            a theme appropriate for the platform.
+        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.
+        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to 10.
+        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+        locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
+        locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+        locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
+        locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
+        indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
+        suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
+
+    Returns:
+        Callable: The previous exception handler that was replaced.
+
+    """
+    traceback_console = Console(stderr=True) if console is None else console
+
+    locals_hide_sunder = (
+        True
+        if (traceback_console.is_jupyter and locals_hide_sunder is None)
+        else locals_hide_sunder
+    )
+
+    def excepthook(
+        type_: Type[BaseException],
+        value: BaseException,
+        traceback: Optional[TracebackType],
+    ) -> None:
+        exception_traceback = Traceback.from_exception(
+            type_,
+            value,
+            traceback,
+            width=width,
+            code_width=code_width,
+            extra_lines=extra_lines,
+            theme=theme,
+            word_wrap=word_wrap,
+            show_locals=show_locals,
+            locals_max_length=locals_max_length,
+            locals_max_string=locals_max_string,
+            locals_max_depth=locals_max_depth,
+            locals_hide_dunder=locals_hide_dunder,
+            locals_hide_sunder=bool(locals_hide_sunder),
+            locals_overflow=locals_overflow,
+            indent_guides=indent_guides,
+            suppress=suppress,
+            max_frames=max_frames,
+        )
+        traceback_console.print(exception_traceback)
+
+    def ipy_excepthook_closure(ip: Any) -> None:  # pragma: no cover
+        tb_data = {}  # store information about showtraceback call
+        default_showtraceback = ip.showtraceback  # keep reference of default traceback
+
+        def ipy_show_traceback(*args: Any, **kwargs: Any) -> None:
+            """wrap the default ip.showtraceback to store info for ip._showtraceback"""
+            nonlocal tb_data
+            tb_data = kwargs
+            default_showtraceback(*args, **kwargs)
+
+        def ipy_display_traceback(
+            *args: Any, is_syntax: bool = False, **kwargs: Any
+        ) -> None:
+            """Internally called traceback from ip._showtraceback"""
+            nonlocal tb_data
+            exc_tuple = ip._get_exc_info()
+
+            # do not display trace on syntax error
+            tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]
+
+            # determine correct tb_offset
+            compiled = tb_data.get("running_compiled_code", False)
+            tb_offset = tb_data.get("tb_offset")
+            if tb_offset is None:
+                tb_offset = 1 if compiled else 0
+            # remove ipython internal frames from trace with tb_offset
+            for _ in range(tb_offset):
+                if tb is None:
+                    break
+                tb = tb.tb_next
+
+            excepthook(exc_tuple[0], exc_tuple[1], tb)
+            tb_data = {}  # clear data upon usage
+
+        # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work
+        # this is also what the ipython docs recommends to modify when subclassing InteractiveShell
+        ip._showtraceback = ipy_display_traceback
+        # add wrapper to capture tb_data
+        ip.showtraceback = ipy_show_traceback
+        ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(
+            *args, is_syntax=True, **kwargs
+        )
+
+    try:  # pragma: no cover
+        # if within ipython, use customized traceback
+        ip = get_ipython()  # type: ignore[name-defined]
+        ipy_excepthook_closure(ip)
+        return sys.excepthook
+    except Exception:
+        # otherwise use default system hook
+        old_excepthook = sys.excepthook
+        sys.excepthook = excepthook
+        return old_excepthook
+
+
+@dataclass
+class Frame:
+    filename: str
+    lineno: int
+    name: str
+    line: str = ""
+    locals: Optional[Dict[str, pretty.Node]] = None
+    last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None
+
+
+@dataclass
+class _SyntaxError:
+    offset: int
+    filename: str
+    line: str
+    lineno: int
+    msg: str
+    notes: List[str] = field(default_factory=list)
+
+
+@dataclass
+class Stack:
+    exc_type: str
+    exc_value: str
+    syntax_error: Optional[_SyntaxError] = None
+    is_cause: bool = False
+    frames: List[Frame] = field(default_factory=list)
+    notes: List[str] = field(default_factory=list)
+    is_group: bool = False
+    exceptions: List["Trace"] = field(default_factory=list)
+
+
+@dataclass
+class Trace:
+    stacks: List[Stack]
+
+
+class PathHighlighter(RegexHighlighter):
+    highlights = [r"(?P.*/)(?P.+)"]
+
+
+class Traceback:
+    """A Console renderable that renders a traceback.
+
+    Args:
+        trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses
+            the last exception.
+        width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
+        code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
+        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.
+        indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
+        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+            Defaults to 10.
+        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+        locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
+        locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+        locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
+        locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
+        suppress (Sequence[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.
+
+    """
+
+    LEXERS = {
+        "": "text",
+        ".py": "python",
+        ".pxd": "cython",
+        ".pyx": "cython",
+        ".pxi": "pyrex",
+    }
+
+    def __init__(
+        self,
+        trace: Optional[Trace] = None,
+        *,
+        width: Optional[int] = 100,
+        code_width: Optional[int] = 88,
+        extra_lines: int = 3,
+        theme: Optional[str] = None,
+        word_wrap: bool = False,
+        show_locals: bool = False,
+        locals_max_length: int = LOCALS_MAX_LENGTH,
+        locals_max_string: int = LOCALS_MAX_STRING,
+        locals_max_depth: Optional[int] = None,
+        locals_hide_dunder: bool = True,
+        locals_hide_sunder: bool = False,
+        locals_overlow: Optional[OverflowMethod] = None,
+        indent_guides: bool = True,
+        suppress: Iterable[Union[str, ModuleType]] = (),
+        max_frames: int = 100,
+    ):
+        if trace is None:
+            exc_type, exc_value, traceback = sys.exc_info()
+            if exc_type is None or exc_value is None or traceback is None:
+                raise ValueError(
+                    "Value for 'trace' required if not called in except: block"
+                )
+            trace = self.extract(
+                exc_type, exc_value, traceback, show_locals=show_locals
+            )
+        self.trace = trace
+        self.width = width
+        self.code_width = code_width
+        self.extra_lines = extra_lines
+        self.theme = Syntax.get_theme(theme or "ansi_dark")
+        self.word_wrap = word_wrap
+        self.show_locals = show_locals
+        self.indent_guides = indent_guides
+        self.locals_max_length = locals_max_length
+        self.locals_max_string = locals_max_string
+        self.locals_max_depth = locals_max_depth
+        self.locals_hide_dunder = locals_hide_dunder
+        self.locals_hide_sunder = locals_hide_sunder
+        self.locals_overflow = locals_overlow
+
+        self.suppress: Sequence[str] = []
+        for suppress_entity in suppress:
+            if not isinstance(suppress_entity, str):
+                assert (
+                    suppress_entity.__file__ is not None
+                ), f"{suppress_entity!r} must be a module with '__file__' attribute"
+                path = os.path.dirname(suppress_entity.__file__)
+            else:
+                path = suppress_entity
+            path = os.path.normpath(os.path.abspath(path))
+            self.suppress.append(path)
+        self.max_frames = max(4, max_frames) if max_frames > 0 else 0
+
+    @classmethod
+    def from_exception(
+        cls,
+        exc_type: Type[Any],
+        exc_value: BaseException,
+        traceback: Optional[TracebackType],
+        *,
+        width: Optional[int] = 100,
+        code_width: Optional[int] = 88,
+        extra_lines: int = 3,
+        theme: Optional[str] = None,
+        word_wrap: bool = False,
+        show_locals: bool = False,
+        locals_max_length: int = LOCALS_MAX_LENGTH,
+        locals_max_string: int = LOCALS_MAX_STRING,
+        locals_max_depth: Optional[int] = None,
+        locals_hide_dunder: bool = True,
+        locals_hide_sunder: bool = False,
+        locals_overflow: Optional[OverflowMethod] = None,
+        indent_guides: bool = True,
+        suppress: Iterable[Union[str, ModuleType]] = (),
+        max_frames: int = 100,
+    ) -> "Traceback":
+        """Create a traceback from exception info
+
+        Args:
+            exc_type (Type[BaseException]): Exception type.
+            exc_value (BaseException): Exception value.
+            traceback (TracebackType): Python Traceback object.
+            width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
+            code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
+            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.
+            indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
+            locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+                Defaults to 10.
+            locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
+            locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+            locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+            locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
+            locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
+            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.
+
+        Returns:
+            Traceback: A Traceback instance that may be printed.
+        """
+        rich_traceback = cls.extract(
+            exc_type,
+            exc_value,
+            traceback,
+            show_locals=show_locals,
+            locals_max_length=locals_max_length,
+            locals_max_string=locals_max_string,
+            locals_max_depth=locals_max_depth,
+            locals_hide_dunder=locals_hide_dunder,
+            locals_hide_sunder=locals_hide_sunder,
+        )
+
+        return cls(
+            rich_traceback,
+            width=width,
+            code_width=code_width,
+            extra_lines=extra_lines,
+            theme=theme,
+            word_wrap=word_wrap,
+            show_locals=show_locals,
+            indent_guides=indent_guides,
+            locals_max_length=locals_max_length,
+            locals_max_string=locals_max_string,
+            locals_max_depth=locals_max_depth,
+            locals_hide_dunder=locals_hide_dunder,
+            locals_hide_sunder=locals_hide_sunder,
+            locals_overlow=locals_overflow,
+            suppress=suppress,
+            max_frames=max_frames,
+        )
+
+    @classmethod
+    def extract(
+        cls,
+        exc_type: Type[BaseException],
+        exc_value: BaseException,
+        traceback: Optional[TracebackType],
+        *,
+        show_locals: bool = False,
+        locals_max_length: int = LOCALS_MAX_LENGTH,
+        locals_max_string: int = LOCALS_MAX_STRING,
+        locals_max_depth: Optional[int] = None,
+        locals_hide_dunder: bool = True,
+        locals_hide_sunder: bool = False,
+        _visited_exceptions: Optional[Set[BaseException]] = None,
+    ) -> Trace:
+        """Extract traceback information.
+
+        Args:
+            exc_type (Type[BaseException]): Exception type.
+            exc_value (BaseException): Exception value.
+            traceback (TracebackType): Python Traceback object.
+            show_locals (bool, optional): Enable display of local variables. Defaults to False.
+            locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
+                Defaults to 10.
+            locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
+            locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
+            locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
+            locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
+
+        Returns:
+            Trace: A Trace instance which you can use to construct a `Traceback`.
+        """
+
+        stacks: List[Stack] = []
+        is_cause = False
+
+        from rich import _IMPORT_CWD
+
+        notes: List[str] = getattr(exc_value, "__notes__", None) or []
+
+        grouped_exceptions: Set[BaseException] = (
+            set() if _visited_exceptions is None else _visited_exceptions
+        )
+
+        def safe_str(_object: Any) -> str:
+            """Don't allow exceptions from __str__ to propagate."""
+            try:
+                return str(_object)
+            except Exception:
+                return ""
+
+        while True:
+            stack = Stack(
+                exc_type=safe_str(exc_type.__name__),
+                exc_value=safe_str(exc_value),
+                is_cause=is_cause,
+                notes=notes,
+            )
+
+            if sys.version_info >= (3, 11):
+                if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)):
+                    stack.is_group = True
+                    for exception in exc_value.exceptions:
+                        if exception in grouped_exceptions:
+                            continue
+                        grouped_exceptions.add(exception)
+                        stack.exceptions.append(
+                            Traceback.extract(
+                                type(exception),
+                                exception,
+                                exception.__traceback__,
+                                show_locals=show_locals,
+                                locals_max_length=locals_max_length,
+                                locals_hide_dunder=locals_hide_dunder,
+                                locals_hide_sunder=locals_hide_sunder,
+                                _visited_exceptions=grouped_exceptions,
+                            )
+                        )
+
+            if isinstance(exc_value, SyntaxError):
+                stack.syntax_error = _SyntaxError(
+                    offset=exc_value.offset or 0,
+                    filename=exc_value.filename or "?",
+                    lineno=exc_value.lineno or 0,
+                    line=exc_value.text or "",
+                    msg=exc_value.msg,
+                    notes=notes,
+                )
+
+            stacks.append(stack)
+            append = stack.frames.append
+
+            def get_locals(
+                iter_locals: Iterable[Tuple[str, object]],
+            ) -> Iterable[Tuple[str, object]]:
+                """Extract locals from an iterator of key pairs."""
+                if not (locals_hide_dunder or locals_hide_sunder):
+                    yield from iter_locals
+                    return
+                for key, value in iter_locals:
+                    if locals_hide_dunder and key.startswith("__"):
+                        continue
+                    if locals_hide_sunder and key.startswith("_"):
+                        continue
+                    yield key, value
+
+            for frame_summary, line_no in walk_tb(traceback):
+                filename = frame_summary.f_code.co_filename
+
+                last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]]
+                last_instruction = None
+                if sys.version_info >= (3, 11):
+                    instruction_index = frame_summary.f_lasti // 2
+                    instruction_position = next(
+                        islice(
+                            frame_summary.f_code.co_positions(),
+                            instruction_index,
+                            instruction_index + 1,
+                        )
+                    )
+                    (
+                        start_line,
+                        end_line,
+                        start_column,
+                        end_column,
+                    ) = instruction_position
+                    if (
+                        start_line is not None
+                        and end_line is not None
+                        and start_column is not None
+                        and end_column is not None
+                    ):
+                        last_instruction = (
+                            (start_line, start_column),
+                            (end_line, end_column),
+                        )
+
+                if filename and not filename.startswith("<"):
+                    if not os.path.isabs(filename):
+                        filename = os.path.join(_IMPORT_CWD, filename)
+                if frame_summary.f_locals.get("_rich_traceback_omit", False):
+                    continue
+
+                frame = Frame(
+                    filename=filename or "?",
+                    lineno=line_no,
+                    name=frame_summary.f_code.co_name,
+                    locals=(
+                        {
+                            key: pretty.traverse(
+                                value,
+                                max_length=locals_max_length,
+                                max_string=locals_max_string,
+                                max_depth=locals_max_depth,
+                            )
+                            for key, value in get_locals(frame_summary.f_locals.items())
+                            if not (inspect.isfunction(value) or inspect.isclass(value))
+                        }
+                        if show_locals
+                        else None
+                    ),
+                    last_instruction=last_instruction,
+                )
+                append(frame)
+                if frame_summary.f_locals.get("_rich_traceback_guard", False):
+                    del stack.frames[:]
+
+            if not grouped_exceptions:
+                cause = getattr(exc_value, "__cause__", None)
+                if cause is not None and cause is not exc_value:
+                    exc_type = cause.__class__
+                    exc_value = cause
+                    # __traceback__ can be None, e.g. for exceptions raised by the
+                    # 'multiprocessing' module
+                    traceback = cause.__traceback__
+                    is_cause = True
+                    continue
+
+                cause = exc_value.__context__
+                if cause is not None and not getattr(
+                    exc_value, "__suppress_context__", False
+                ):
+                    exc_type = cause.__class__
+                    exc_value = cause
+                    traceback = cause.__traceback__
+                    is_cause = False
+                    continue
+            # No cover, code is reached but coverage doesn't recognize it.
+            break  # pragma: no cover
+
+        trace = Trace(stacks=stacks)
+
+        return trace
+
+    def __rich_console__(
+        self, console: Console, options: ConsoleOptions
+    ) -> RenderResult:
+        theme = self.theme
+        background_style = theme.get_background_style()
+        token_style = theme.get_style_for_token
+
+        traceback_theme = Theme(
+            {
+                "pretty": token_style(TextToken),
+                "pygments.text": token_style(Token),
+                "pygments.string": token_style(String),
+                "pygments.function": token_style(Name.Function),
+                "pygments.number": token_style(Number),
+                "repr.indent": token_style(Comment) + Style(dim=True),
+                "repr.str": token_style(String),
+                "repr.brace": token_style(TextToken) + Style(bold=True),
+                "repr.number": token_style(Number),
+                "repr.bool_true": token_style(Keyword.Constant),
+                "repr.bool_false": token_style(Keyword.Constant),
+                "repr.none": token_style(Keyword.Constant),
+                "scope.border": token_style(String.Delimiter),
+                "scope.equals": token_style(Operator),
+                "scope.key": token_style(Name),
+                "scope.key.special": token_style(Name.Constant) + Style(dim=True),
+            },
+            inherit=False,
+        )
+
+        highlighter = ReprHighlighter()
+
+        @group()
+        def render_stack(stack: Stack, last: bool) -> RenderResult:
+            if stack.frames:
+                stack_renderable: ConsoleRenderable = Panel(
+                    self._render_stack(stack),
+                    title="[traceback.title]Traceback [dim](most recent call last)",
+                    style=background_style,
+                    border_style="traceback.border",
+                    expand=True,
+                    padding=(0, 1),
+                )
+                stack_renderable = Constrain(stack_renderable, self.width)
+                with console.use_theme(traceback_theme):
+                    yield stack_renderable
+
+            if stack.syntax_error is not None:
+                with console.use_theme(traceback_theme):
+                    yield Constrain(
+                        Panel(
+                            self._render_syntax_error(stack.syntax_error),
+                            style=background_style,
+                            border_style="traceback.border.syntax_error",
+                            expand=True,
+                            padding=(0, 1),
+                            width=self.width,
+                        ),
+                        self.width,
+                    )
+                yield Text.assemble(
+                    (f"{stack.exc_type}: ", "traceback.exc_type"),
+                    highlighter(stack.syntax_error.msg),
+                )
+            elif stack.exc_value:
+                yield Text.assemble(
+                    (f"{stack.exc_type}: ", "traceback.exc_type"),
+                    highlighter(stack.exc_value),
+                )
+            else:
+                yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type"))
+
+            for note in stack.notes:
+                yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note))
+
+            if stack.is_group:
+                for group_no, group_exception in enumerate(stack.exceptions, 1):
+                    grouped_exceptions: List[Group] = []
+                    for group_last, group_stack in loop_last(group_exception.stacks):
+                        grouped_exceptions.append(render_stack(group_stack, group_last))
+                    yield ""
+                    yield Constrain(
+                        Panel(
+                            Group(*grouped_exceptions),
+                            title=f"Sub-exception #{group_no}",
+                            border_style="traceback.group.border",
+                        ),
+                        self.width,
+                    )
+
+            if not last:
+                if stack.is_cause:
+                    yield Text.from_markup(
+                        "\n[i]The above exception was the direct cause of the following exception:\n",
+                    )
+                else:
+                    yield Text.from_markup(
+                        "\n[i]During handling of the above exception, another exception occurred:\n",
+                    )
+
+        for last, stack in loop_last(reversed(self.trace.stacks)):
+            yield render_stack(stack, last)
+
+    @group()
+    def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
+        highlighter = ReprHighlighter()
+        path_highlighter = PathHighlighter()
+        if syntax_error.filename != "":
+            if os.path.exists(syntax_error.filename):
+                text = Text.assemble(
+                    (f" {syntax_error.filename}", "pygments.string"),
+                    (":", "pygments.text"),
+                    (str(syntax_error.lineno), "pygments.number"),
+                    style="pygments.text",
+                )
+                yield path_highlighter(text)
+        syntax_error_text = highlighter(syntax_error.line.rstrip())
+        syntax_error_text.no_wrap = True
+        offset = min(syntax_error.offset - 1, len(syntax_error_text))
+        syntax_error_text.stylize("bold underline", offset, offset)
+        syntax_error_text += Text.from_markup(
+            "\n" + " " * offset + "[traceback.offset]▲[/]",
+            style="pygments.text",
+        )
+        yield syntax_error_text
+
+    @classmethod
+    def _guess_lexer(cls, filename: str, code: str) -> str:
+        ext = os.path.splitext(filename)[-1]
+        if not ext:
+            # No extension, look at first line to see if it is a hashbang
+            # Note, this is an educated guess and not a guarantee
+            # If it fails, the only downside is that the code is highlighted strangely
+            new_line_index = code.index("\n")
+            first_line = code[:new_line_index] if new_line_index != -1 else code
+            if first_line.startswith("#!") and "python" in first_line.lower():
+                return "python"
+        try:
+            return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name
+        except ClassNotFound:
+            return "text"
+
+    @group()
+    def _render_stack(self, stack: Stack) -> RenderResult:
+        path_highlighter = PathHighlighter()
+        theme = self.theme
+
+        def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
+            if frame.locals:
+                yield render_scope(
+                    frame.locals,
+                    title="locals",
+                    indent_guides=self.indent_guides,
+                    max_length=self.locals_max_length,
+                    max_string=self.locals_max_string,
+                    max_depth=self.locals_max_depth,
+                    overflow=self.locals_overflow,
+                )
+
+        exclude_frames: Optional[range] = None
+        if self.max_frames != 0:
+            exclude_frames = range(
+                self.max_frames // 2,
+                len(stack.frames) - self.max_frames // 2,
+            )
+
+        excluded = False
+        for frame_index, frame in enumerate(stack.frames):
+            if exclude_frames and frame_index in exclude_frames:
+                excluded = True
+                continue
+
+            if excluded:
+                assert exclude_frames is not None
+                yield Text(
+                    f"\n... {len(exclude_frames)} frames hidden ...",
+                    justify="center",
+                    style="traceback.error",
+                )
+                excluded = False
+
+            first = frame_index == 0
+            frame_filename = frame.filename
+            suppressed = any(frame_filename.startswith(path) for path in self.suppress)
+
+            if os.path.exists(frame.filename):
+                text = Text.assemble(
+                    path_highlighter(Text(frame.filename, style="pygments.string")),
+                    (":", "pygments.text"),
+                    (str(frame.lineno), "pygments.number"),
+                    " in ",
+                    (frame.name, "pygments.function"),
+                    style="pygments.text",
+                )
+            else:
+                text = Text.assemble(
+                    "in ",
+                    (frame.name, "pygments.function"),
+                    (":", "pygments.text"),
+                    (str(frame.lineno), "pygments.number"),
+                    style="pygments.text",
+                )
+            if not frame.filename.startswith("<") and not first:
+                yield ""
+            yield text
+            if frame.filename.startswith("<"):
+                yield from render_locals(frame)
+                continue
+            if not suppressed:
+                try:
+                    code_lines = linecache.getlines(frame.filename)
+                    code = "".join(code_lines)
+                    if not code:
+                        # code may be an empty string if the file doesn't exist, OR
+                        # if the traceback filename is generated dynamically
+                        continue
+                    lexer_name = self._guess_lexer(frame.filename, code)
+                    syntax = Syntax(
+                        code,
+                        lexer_name,
+                        theme=theme,
+                        line_numbers=True,
+                        line_range=(
+                            frame.lineno - self.extra_lines,
+                            frame.lineno + self.extra_lines,
+                        ),
+                        highlight_lines={frame.lineno},
+                        word_wrap=self.word_wrap,
+                        code_width=self.code_width,
+                        indent_guides=self.indent_guides,
+                        dedent=False,
+                    )
+                    yield ""
+                except Exception as error:
+                    yield Text.assemble(
+                        (f"\n{error}", "traceback.error"),
+                    )
+                else:
+                    if frame.last_instruction is not None:
+                        start, end = frame.last_instruction
+
+                        # Stylize a line at a time
+                        # So that indentation isn't underlined (which looks bad)
+                        for line1, column1, column2 in _iter_syntax_lines(start, end):
+                            try:
+                                if column1 == 0:
+                                    line = code_lines[line1 - 1]
+                                    column1 = len(line) - len(line.lstrip())
+                                if column2 == -1:
+                                    column2 = len(code_lines[line1 - 1])
+                            except IndexError:
+                                # Being defensive here
+                                # If last_instruction reports a line out-of-bounds, we don't want to crash
+                                continue
+
+                            syntax.stylize_range(
+                                style="traceback.error_range",
+                                start=(line1, column1),
+                                end=(line1, column2),
+                            )
+                    yield (
+                        Columns(
+                            [
+                                syntax,
+                                *render_locals(frame),
+                            ],
+                            padding=1,
+                        )
+                        if frame.locals
+                        else syntax
+                    )
+
+
+if __name__ == "__main__":  # pragma: no cover
+    install(show_locals=True)
+    import sys
+
+    def bar(
+        a: Any,
+    ) -> None:  # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
+        one = 1
+        print(one / a)
+
+    def foo(a: Any) -> None:
+        _rich_traceback_guard = True
+        zed = {
+            "characters": {
+                "Paul Atreides",
+                "Vladimir Harkonnen",
+                "Thufir Hawat",
+                "Duncan Idaho",
+            },
+            "atomic_types": (None, False, True),
+        }
+        bar(a)
+
+    def error() -> None:
+        foo(0)
+
+    error()
diff --git a/.venv/lib/python3.12/site-packages/rich/tree.py b/.venv/lib/python3.12/site-packages/rich/tree.py
new file mode 100644
index 0000000..9a87d60
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/rich/tree.py
@@ -0,0 +1,257 @@
+from typing import Iterator, List, Optional, Tuple
+
+from ._loop import loop_first, loop_last
+from .console import Console, ConsoleOptions, RenderableType, RenderResult
+from .jupyter import JupyterMixin
+from .measure import Measurement
+from .segment import Segment
+from .style import Style, StyleStack, StyleType
+from .styled import Styled
+
+GuideType = Tuple[str, str, str, str]
+
+
+class Tree(JupyterMixin):
+    """A renderable for a tree structure.
+
+    Attributes:
+        ASCII_GUIDES (GuideType): Guide lines used when Console.ascii_only is True.
+        TREE_GUIDES (List[GuideType, GuideType, GuideType]): Default guide lines.
+
+    Args:
+        label (RenderableType): The renderable or str for the tree label.
+        style (StyleType, optional): Style of this tree. Defaults to "tree".
+        guide_style (StyleType, optional): Style of the guide lines. Defaults to "tree.line".
+        expanded (bool, optional): Also display children. Defaults to True.
+        highlight (bool, optional): Highlight renderable (if str). Defaults to False.
+        hide_root (bool, optional): Hide the root node. Defaults to False.
+    """
+
+    ASCII_GUIDES = ("    ", "|   ", "+-- ", "`-- ")
+    TREE_GUIDES = [
+        ("    ", "│   ", "├── ", "└── "),
+        ("    ", "┃   ", "┣━━ ", "┗━━ "),
+        ("    ", "║   ", "╠══ ", "╚══ "),
+    ]
+
+    def __init__(
+        self,
+        label: RenderableType,
+        *,
+        style: StyleType = "tree",
+        guide_style: StyleType = "tree.line",
+        expanded: bool = True,
+        highlight: bool = False,
+        hide_root: bool = False,
+    ) -> None:
+        self.label = label
+        self.style = style
+        self.guide_style = guide_style
+        self.children: List[Tree] = []
+        self.expanded = expanded
+        self.highlight = highlight
+        self.hide_root = hide_root
+
+    def add(
+        self,
+        label: RenderableType,
+        *,
+        style: Optional[StyleType] = None,
+        guide_style: Optional[StyleType] = None,
+        expanded: bool = True,
+        highlight: Optional[bool] = False,
+    ) -> "Tree":
+        """Add a child tree.
+
+        Args:
+            label (RenderableType): The renderable or str for the tree label.
+            style (StyleType, optional): Style of this tree. Defaults to "tree".
+            guide_style (StyleType, optional): Style of the guide lines. Defaults to "tree.line".
+            expanded (bool, optional): Also display children. Defaults to True.
+            highlight (Optional[bool], optional): Highlight renderable (if str). Defaults to False.
+
+        Returns:
+            Tree: A new child Tree, which may be further modified.
+        """
+        node = Tree(
+            label,
+            style=self.style if style is None else style,
+            guide_style=self.guide_style if guide_style is None else guide_style,
+            expanded=expanded,
+            highlight=self.highlight if highlight is None else highlight,
+        )
+        self.children.append(node)
+        return node
+
+    def __rich_console__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "RenderResult":
+        stack: List[Iterator[Tuple[bool, Tree]]] = []
+        pop = stack.pop
+        push = stack.append
+        new_line = Segment.line()
+
+        get_style = console.get_style
+        null_style = Style.null()
+        guide_style = get_style(self.guide_style, default="") or null_style
+        SPACE, CONTINUE, FORK, END = range(4)
+
+        _Segment = Segment
+
+        def make_guide(index: int, style: Style) -> Segment:
+            """Make a Segment for a level of the guide lines."""
+            if options.ascii_only:
+                line = self.ASCII_GUIDES[index]
+            else:
+                guide = 1 if style.bold else (2 if style.underline2 else 0)
+                line = self.TREE_GUIDES[0 if options.legacy_windows else guide][index]
+            return _Segment(line, style)
+
+        levels: List[Segment] = [make_guide(CONTINUE, guide_style)]
+        push(iter(loop_last([self])))
+
+        guide_style_stack = StyleStack(get_style(self.guide_style))
+        style_stack = StyleStack(get_style(self.style))
+        remove_guide_styles = Style(bold=False, underline2=False)
+
+        depth = 0
+
+        while stack:
+            stack_node = pop()
+            try:
+                last, node = next(stack_node)
+            except StopIteration:
+                levels.pop()
+                if levels:
+                    guide_style = levels[-1].style or null_style
+                    levels[-1] = make_guide(FORK, guide_style)
+                    guide_style_stack.pop()
+                    style_stack.pop()
+                continue
+            push(stack_node)
+            if last:
+                levels[-1] = make_guide(END, levels[-1].style or null_style)
+
+            guide_style = guide_style_stack.current + get_style(node.guide_style)
+            style = style_stack.current + get_style(node.style)
+            prefix = levels[(2 if self.hide_root else 1) :]
+            renderable_lines = console.render_lines(
+                Styled(node.label, style),
+                options.update(
+                    width=options.max_width
+                    - sum(level.cell_length for level in prefix),
+                    highlight=self.highlight,
+                    height=None,
+                ),
+                pad=options.justify is not None,
+            )
+
+            if not (depth == 0 and self.hide_root):
+                for first, line in loop_first(renderable_lines):
+                    if prefix:
+                        yield from _Segment.apply_style(
+                            prefix,
+                            style.background_style,
+                            post_style=remove_guide_styles,
+                        )
+                    yield from line
+                    yield new_line
+                    if first and prefix:
+                        prefix[-1] = make_guide(
+                            SPACE if last else CONTINUE, prefix[-1].style or null_style
+                        )
+
+            if node.expanded and node.children:
+                levels[-1] = make_guide(
+                    SPACE if last else CONTINUE, levels[-1].style or null_style
+                )
+                levels.append(
+                    make_guide(END if len(node.children) == 1 else FORK, guide_style)
+                )
+                style_stack.push(get_style(node.style))
+                guide_style_stack.push(get_style(node.guide_style))
+                push(iter(loop_last(node.children)))
+                depth += 1
+
+    def __rich_measure__(
+        self, console: "Console", options: "ConsoleOptions"
+    ) -> "Measurement":
+        stack: List[Iterator[Tree]] = [iter([self])]
+        pop = stack.pop
+        push = stack.append
+        minimum = 0
+        maximum = 0
+        measure = Measurement.get
+        level = 0
+        while stack:
+            iter_tree = pop()
+            try:
+                tree = next(iter_tree)
+            except StopIteration:
+                level -= 1
+                continue
+            push(iter_tree)
+            min_measure, max_measure = measure(console, options, tree.label)
+            indent = level * 4
+            minimum = max(min_measure + indent, minimum)
+            maximum = max(max_measure + indent, maximum)
+            if tree.expanded and tree.children:
+                push(iter(tree.children))
+                level += 1
+        return Measurement(minimum, maximum)
+
+
+if __name__ == "__main__":  # pragma: no cover
+    from rich.console import Group
+    from rich.markdown import Markdown
+    from rich.panel import Panel
+    from rich.syntax import Syntax
+    from rich.table import Table
+
+    table = Table(row_styles=["", "dim"])
+
+    table.add_column("Released", style="cyan", no_wrap=True)
+    table.add_column("Title", style="magenta")
+    table.add_column("Box Office", justify="right", style="green")
+
+    table.add_row("Dec 20, 2019", "Star Wars: The Rise of Skywalker", "$952,110,690")
+    table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
+    table.add_row("Dec 15, 2017", "Star Wars Ep. V111: The Last Jedi", "$1,332,539,889")
+    table.add_row("Dec 16, 2016", "Rogue One: A Star Wars Story", "$1,332,439,889")
+
+    code = """\
+class Segment(NamedTuple):
+    text: str = ""
+    style: Optional[Style] = None
+    is_control: bool = False
+"""
+    syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
+
+    markdown = Markdown(
+        """\
+### example.md
+> Hello, World!
+>
+> Markdown _all_ the things
+"""
+    )
+
+    root = Tree("🌲 [b green]Rich Tree", highlight=True, hide_root=True)
+
+    node = root.add(":file_folder: Renderables", guide_style="red")
+    simple_node = node.add(":file_folder: [bold yellow]Atomic", guide_style="uu green")
+    simple_node.add(Group("📄 Syntax", syntax))
+    simple_node.add(Group("📄 Markdown", Panel(markdown, border_style="green")))
+
+    containers_node = node.add(
+        ":file_folder: [bold magenta]Containers", guide_style="bold magenta"
+    )
+    containers_node.expanded = True
+    panel = Panel.fit("Just a panel", border_style="red")
+    containers_node.add(Group("📄 Panels", panel))
+
+    containers_node.add(Group("📄 [b magenta]Table", table))
+
+    console = Console()
+
+    console.print(root)
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/METADATA
new file mode 100644
index 0000000..fa464a0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/METADATA
@@ -0,0 +1,278 @@
+Metadata-Version: 2.4
+Name: sanic
+Version: 25.12.0
+Summary: A web server and web framework that's written to go fast. Build fast. Run fast.
+Home-page: http://github.com/sanic-org/sanic/
+Author: Sanic Community
+Author-email: admhpkns@gmail.com
+License: MIT
+Platform: any
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: License :: OSI Approved :: MIT License
+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 :: 3.14
+Requires-Python: >=3.10
+License-File: LICENSE
+Requires-Dist: sanic-routing>=23.12.0
+Requires-Dist: httptools>=0.0.10
+Requires-Dist: uvloop>=0.15.0; sys_platform != "win32" and implementation_name == "cpython"
+Requires-Dist: ujson>=1.35; sys_platform != "win32" and implementation_name == "cpython"
+Requires-Dist: aiofiles>=0.6.0
+Requires-Dist: websockets>=10.0
+Requires-Dist: multidict<7.0,>=5.0
+Requires-Dist: html5tagger>=1.2.1
+Requires-Dist: tracerite>=2.2.0
+Requires-Dist: typing-extensions>=4.4.0
+Requires-Dist: setuptools>=70.1.0
+Provides-Extra: test
+Requires-Dist: sanic-testing>=23.6.0; extra == "test"
+Requires-Dist: pytest>=8.2.2; extra == "test"
+Requires-Dist: pytest-xdist>=3.5.0; extra == "test"
+Requires-Dist: pytest-cov>=4.0.0; extra == "test"
+Requires-Dist: coverage; extra == "test"
+Requires-Dist: beautifulsoup4; extra == "test"
+Requires-Dist: pytest-sanic; extra == "test"
+Requires-Dist: pytest-benchmark; extra == "test"
+Requires-Dist: chardet==3.*; extra == "test"
+Requires-Dist: ruff; extra == "test"
+Requires-Dist: bandit; extra == "test"
+Requires-Dist: mypy; extra == "test"
+Requires-Dist: docutils; extra == "test"
+Requires-Dist: pygments; extra == "test"
+Requires-Dist: uvicorn; extra == "test"
+Requires-Dist: slotscheck<1,>=0.8.0; extra == "test"
+Requires-Dist: types-ujson; (sys_platform != "win32" and implementation_name == "cpython") and extra == "test"
+Provides-Extra: dev
+Requires-Dist: sanic-testing>=23.6.0; extra == "dev"
+Requires-Dist: pytest>=8.2.2; extra == "dev"
+Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
+Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
+Requires-Dist: coverage; extra == "dev"
+Requires-Dist: beautifulsoup4; extra == "dev"
+Requires-Dist: pytest-sanic; extra == "dev"
+Requires-Dist: pytest-benchmark; extra == "dev"
+Requires-Dist: chardet==3.*; extra == "dev"
+Requires-Dist: ruff; extra == "dev"
+Requires-Dist: bandit; extra == "dev"
+Requires-Dist: mypy; extra == "dev"
+Requires-Dist: docutils; extra == "dev"
+Requires-Dist: pygments; extra == "dev"
+Requires-Dist: uvicorn; extra == "dev"
+Requires-Dist: slotscheck<1,>=0.8.0; extra == "dev"
+Requires-Dist: types-ujson; (sys_platform != "win32" and implementation_name == "cpython") and extra == "dev"
+Requires-Dist: cryptography; extra == "dev"
+Requires-Dist: tox; extra == "dev"
+Requires-Dist: towncrier; extra == "dev"
+Provides-Extra: docs
+Requires-Dist: sphinx>=2.1.2; extra == "docs"
+Requires-Dist: sphinx_rtd_theme>=0.4.3; extra == "docs"
+Requires-Dist: docutils; extra == "docs"
+Requires-Dist: pygments; extra == "docs"
+Requires-Dist: m2r2; extra == "docs"
+Requires-Dist: enum-tools[sphinx]; extra == "docs"
+Requires-Dist: mistune>=2.0.0; extra == "docs"
+Requires-Dist: autodocsumm>=0.2.11; extra == "docs"
+Requires-Dist: msgspec; extra == "docs"
+Requires-Dist: python-frontmatter; extra == "docs"
+Requires-Dist: docstring-parser; extra == "docs"
+Requires-Dist: libsass; extra == "docs"
+Provides-Extra: all
+Requires-Dist: mypy; extra == "all"
+Requires-Dist: msgspec; extra == "all"
+Requires-Dist: bandit; extra == "all"
+Requires-Dist: chardet==3.*; extra == "all"
+Requires-Dist: slotscheck<1,>=0.8.0; extra == "all"
+Requires-Dist: sphinx_rtd_theme>=0.4.3; extra == "all"
+Requires-Dist: docstring-parser; extra == "all"
+Requires-Dist: mistune>=2.0.0; extra == "all"
+Requires-Dist: tox; extra == "all"
+Requires-Dist: pytest-xdist>=3.5.0; extra == "all"
+Requires-Dist: beautifulsoup4; extra == "all"
+Requires-Dist: enum-tools[sphinx]; extra == "all"
+Requires-Dist: coverage; extra == "all"
+Requires-Dist: sphinx>=2.1.2; extra == "all"
+Requires-Dist: libsass; extra == "all"
+Requires-Dist: cryptography; extra == "all"
+Requires-Dist: uvicorn; extra == "all"
+Requires-Dist: m2r2; extra == "all"
+Requires-Dist: pytest>=8.2.2; extra == "all"
+Requires-Dist: autodocsumm>=0.2.11; extra == "all"
+Requires-Dist: ruff; extra == "all"
+Requires-Dist: types-ujson; (sys_platform != "win32" and implementation_name == "cpython") and extra == "all"
+Requires-Dist: sanic-testing>=23.6.0; extra == "all"
+Requires-Dist: python-frontmatter; extra == "all"
+Requires-Dist: pytest-benchmark; extra == "all"
+Requires-Dist: towncrier; extra == "all"
+Requires-Dist: pytest-cov>=4.0.0; extra == "all"
+Requires-Dist: pytest-sanic; extra == "all"
+Requires-Dist: docutils; extra == "all"
+Requires-Dist: pygments; extra == "all"
+Provides-Extra: ext
+Requires-Dist: sanic-ext; extra == "ext"
+Provides-Extra: http3
+Requires-Dist: aioquic; extra == "http3"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: platform
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
+
+.. image:: https://raw.githubusercontent.com/sanic-org/sanic-assets/master/png/sanic-framework-logo-400x97.png
+    :alt: Sanic | Build fast. Run fast.
+
+Sanic | Build fast. Run fast.
+=============================
+
+.. start-badges
+
+.. list-table::
+    :widths: 15 85
+    :stub-columns: 1
+
+    * - Build
+      - | |Tests|
+    * - Docs
+      - | |UserGuide| |Documentation|
+    * - Package
+      - | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style ruff|
+    * - Support
+      - | |Forums| |Discord| |Awesome|
+    * - Stats
+      - | |Monthly Downloads| |Weekly Downloads| |Conda downloads|
+
+.. |UserGuide| image:: https://img.shields.io/badge/user%20guide-sanic-ff0068
+   :target: https://sanic.dev/
+.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
+   :target: https://community.sanicframework.org/
+.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord&label=Discord&color=5865F2
+   :target: https://discord.gg/FARQzAEMAA
+.. |Tests| image:: https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main
+   :target: https://github.com/sanic-org/sanic/actions/workflows/tests.yml
+.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
+   :target: http://sanic.readthedocs.io/en/latest/?badge=latest
+.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
+   :target: https://pypi.python.org/pypi/sanic/
+.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
+   :target: https://pypi.python.org/pypi/sanic/
+.. |Code style ruff| image:: https://img.shields.io/badge/code%20style-ruff-000000.svg
+    :target: https://docs.astral.sh/ruff/
+.. |Wheel| image:: https://img.shields.io/pypi/wheel/sanic.svg
+    :alt: PyPI Wheel
+    :target: https://pypi.python.org/pypi/sanic
+.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic.svg
+    :alt: Supported implementations
+    :target: https://pypi.python.org/pypi/sanic
+.. |Awesome| image:: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
+    :alt: Awesome Sanic List
+    :target: https://github.com/mekicha/awesome-sanic
+.. |Monthly Downloads| image:: https://img.shields.io/pypi/dm/sanic.svg
+    :alt: Downloads
+    :target: https://pepy.tech/project/sanic
+.. |Weekly Downloads| image:: https://img.shields.io/pypi/dw/sanic.svg
+    :alt: Downloads
+    :target: https://pepy.tech/project/sanic
+.. |Conda downloads| image:: https://img.shields.io/conda/dn/conda-forge/sanic.svg
+    :alt: Downloads
+    :target: https://anaconda.org/conda-forge/sanic
+
+.. end-badges
+
+Sanic is a **Python 3.10+** web server and web framework that's written to go fast. It allows the usage of the ``async/await`` syntax added in Python 3.5, which makes your code non-blocking and speedy.
+
+Sanic is also ASGI compliant, so you can deploy it with an `alternative ASGI webserver `_.
+
+`Source code on GitHub `_ | `Help and discussion board `_ | `User Guide `_ | `Chat on Discord `_
+
+The project is maintained by the community, for the community. **Contributions are welcome!**
+
+The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.
+
+Sponsor
+-------
+
+Check out `open collective `_ to learn more about helping to fund Sanic.
+
+
+Installation
+------------
+
+``pip install sanic``
+
+    Sanic makes use of ``uvloop`` and ``ujson`` to help with performance. If you do not want to use those packages, simply add an environmental variable ``SANIC_NO_UVLOOP=true`` or ``SANIC_NO_UJSON=true`` at install time.
+
+    .. code:: shell
+
+       $ export SANIC_NO_UVLOOP=true
+       $ export SANIC_NO_UJSON=true
+       $ pip install --no-binary :all: sanic
+
+
+.. note::
+
+  If you are running on a clean install of Fedora 28 or above, please make sure you have the ``redhat-rpm-config`` package installed in case if you want to
+  use ``sanic`` with ``ujson`` dependency.
+
+
+Hello World Example
+-------------------
+
+.. code:: python
+
+    from sanic import Sanic
+    from sanic.response import json
+
+    app = Sanic("my-hello-world-app")
+
+    @app.route('/')
+    async def test(request):
+        return json({'hello': 'world'})
+
+Sanic can now be easily run from CLI using ``sanic hello.app``.
+
+.. code::
+
+    [2018-12-30 11:37:41 +0200] [13564] [INFO] Goin' Fast @ http://127.0.0.1:8000
+    [2018-12-30 11:37:41 +0200] [13564] [INFO] Starting worker [13564]
+
+And, we can verify it is working: ``curl localhost:8000 -i``
+
+.. code::
+
+    HTTP/1.1 200 OK
+    Connection: keep-alive
+    Keep-Alive: 5
+    Content-Length: 17
+    Content-Type: application/json
+
+    {"hello":"world"}
+
+**Now, let's go build something fast!**
+
+Minimum Python version is 3.10.
+
+Documentation
+-------------
+
+User Guide, Changelog, and API Documentation can be found at `sanic.dev `__.
+
+
+Questions and Discussion
+------------------------
+
+`Ask a question or join the conversation `__.
+
+Contribution
+------------
+
+We are always happy to have new contributions. We have `marked issues good for anyone looking to get started `_, and welcome `questions on the forums `_. Please take a look at our `Contribution guidelines `_.
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/RECORD
new file mode 100644
index 0000000..66cbbf0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/RECORD
@@ -0,0 +1,277 @@
+../../../bin/sanic,sha256=9qz-WNkho9GzGnTsmTbqjfK-oD-LverTS12Eip_NDqA,198
+sanic-25.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+sanic-25.12.0.dist-info/METADATA,sha256=n8m9aSo74BKyoTbGlrlpO9HYvXUGct5NKsclkuWcoTg,11066
+sanic-25.12.0.dist-info/RECORD,,
+sanic-25.12.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic-25.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+sanic-25.12.0.dist-info/entry_points.txt,sha256=0vIzcvJJECQnWr4YHvpLveDvBw5jLvB5pq9x9RCl-rE,46
+sanic-25.12.0.dist-info/licenses/LICENSE,sha256=zcKUxJWo2A8_tZbyV_t7YSFMfV5e4lSFK3U_-B6OE3w,1080
+sanic-25.12.0.dist-info/top_level.txt,sha256=ENtJKuBkCofUPUrIvzElGjOx1MUNBOm9GExhwSlZZQ8,6
+sanic/__init__.py,sha256=JnxQbtAwLI7vsTRJDo0MOKPnunEFiB3Al8RAeEsahzs,1752
+sanic/__main__.py,sha256=tPbFh2saH0TxJGAk9B9OHvZhP2s4qqIK66liRTMT4IM,675
+sanic/__pycache__/__init__.cpython-312.pyc,,
+sanic/__pycache__/__main__.cpython-312.pyc,,
+sanic/__pycache__/__version__.cpython-312.pyc,,
+sanic/__pycache__/app.cpython-312.pyc,,
+sanic/__pycache__/asgi.cpython-312.pyc,,
+sanic/__pycache__/blueprint_group.cpython-312.pyc,,
+sanic/__pycache__/blueprints.cpython-312.pyc,,
+sanic/__pycache__/compat.cpython-312.pyc,,
+sanic/__pycache__/config.cpython-312.pyc,,
+sanic/__pycache__/constants.cpython-312.pyc,,
+sanic/__pycache__/errorpages.cpython-312.pyc,,
+sanic/__pycache__/exceptions.cpython-312.pyc,,
+sanic/__pycache__/headers.cpython-312.pyc,,
+sanic/__pycache__/helpers.cpython-312.pyc,,
+sanic/__pycache__/log.cpython-312.pyc,,
+sanic/__pycache__/middleware.cpython-312.pyc,,
+sanic/__pycache__/router.cpython-312.pyc,,
+sanic/__pycache__/signals.cpython-312.pyc,,
+sanic/__pycache__/simple.cpython-312.pyc,,
+sanic/__pycache__/utils.cpython-312.pyc,,
+sanic/__pycache__/views.cpython-312.pyc,,
+sanic/__version__.py,sha256=j8iGa0_eusgomUBlpg53I6Uyvo1If0uNo9Y9RMqWjzI,24
+sanic/app.py,sha256=irn_Au_1hahnKOSpaYqo_lwRvSsP_KI0nnBUdaA9SuQ,92936
+sanic/application/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/application/__pycache__/__init__.cpython-312.pyc,,
+sanic/application/__pycache__/constants.cpython-312.pyc,,
+sanic/application/__pycache__/ext.cpython-312.pyc,,
+sanic/application/__pycache__/logo.cpython-312.pyc,,
+sanic/application/__pycache__/motd.cpython-312.pyc,,
+sanic/application/__pycache__/spinner.cpython-312.pyc,,
+sanic/application/__pycache__/state.cpython-312.pyc,,
+sanic/application/constants.py,sha256=2IL0wxTqVWLo0uguOj0cEvyq6f0hJrK6JAW8jP32jsU,711
+sanic/application/ext.py,sha256=SvB8lSru84QJAeXB7FigPX-VNODLjZjbkv8Pnh32w6g,1241
+sanic/application/logo.py,sha256=-LzPzPpmEQueVSXnFhpP5VH4rEqzsKljQGdSbdcpP1E,4030
+sanic/application/motd.py,sha256=VF2Wvy_rYUNN_BjhazHdD5HsWE4-uTiPPTkqwWauC2c,6026
+sanic/application/spinner.py,sha256=9EcXRY8eoXYo7Y4GNbctT-YfnTCburVBWsX93xEVrHE,2347
+sanic/application/state.py,sha256=_fm8FO5iWNLFfXjyZRL4O1Yk4VIao9mtXHjTKdXRiTw,3575
+sanic/asgi.py,sha256=OzI2irCA6rnBcLgn8-zbCloRwc-ufUeW42ktQRUwkE8,9383
+sanic/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/base/__pycache__/__init__.cpython-312.pyc,,
+sanic/base/__pycache__/meta.cpython-312.pyc,,
+sanic/base/__pycache__/root.cpython-312.pyc,,
+sanic/base/meta.py,sha256=o_H0oTtu4kr285uyN_YZSK6PmksbN20tmUWWexNAHoc,208
+sanic/base/root.py,sha256=jWeBC3aDjBFKPqS4B_zd5cmBmsUMl3wFUAVq5MAbvzU,2127
+sanic/blueprint_group.py,sha256=3jhb8Z4A9BcplfVeMpKdcuF1T79SaFXjSMbAqWCkoBw,84
+sanic/blueprints.py,sha256=jGaJRJaTAG1PiYCwrPA1GaIa02C4CvBi1gB_SxXlmQs,32509
+sanic/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/cli/__pycache__/__init__.cpython-312.pyc,,
+sanic/cli/__pycache__/app.cpython-312.pyc,,
+sanic/cli/__pycache__/arguments.cpython-312.pyc,,
+sanic/cli/__pycache__/base.cpython-312.pyc,,
+sanic/cli/__pycache__/console.cpython-312.pyc,,
+sanic/cli/__pycache__/daemon.cpython-312.pyc,,
+sanic/cli/__pycache__/executor.cpython-312.pyc,,
+sanic/cli/__pycache__/inspector.cpython-312.pyc,,
+sanic/cli/__pycache__/inspector_client.cpython-312.pyc,,
+sanic/cli/app.py,sha256=ZK4O1XHR-kj_-EUNNFr1xwx32H8ldRfZGUqfB4ier40,16814
+sanic/cli/arguments.py,sha256=nH9RBrpRTA684HzdtnHa4rjnVK8myUiNMIbOq0DTXXM,12052
+sanic/cli/base.py,sha256=rpn2WMXUTp_RvWkl1oojXxxx2O3rv4poE67xospRd_Q,1075
+sanic/cli/console.py,sha256=UBKHlkM0zJ3BqKEK9vHLiyydrbqpV9omlXukS6hYYmQ,9189
+sanic/cli/daemon.py,sha256=UUWbaRMHQxXuF-c2tD_Y5ch-A2VmryP-2aE7jVb5nYI,4308
+sanic/cli/executor.py,sha256=b2NtRDCDfRedUL6iipdR8eILaNYE1D2gAItjHqLjkwA,3296
+sanic/cli/inspector.py,sha256=ndvCVsN6GiTIeeHP54sdKJAsFz18A3vypbJjuPZs-FA,3266
+sanic/cli/inspector_client.py,sha256=CFiny588lAQVP5KvBWKpWpN2G1ljdTCcR9vSxs8cerY,3779
+sanic/compat.py,sha256=490_6FCNnKgc9l-ZI0HkpKe_Az_2ULzTnhUhkQw2W-A,5907
+sanic/config.py,sha256=8dfDSGqYm5hBvVblHfta8_QB4F9VDczWakaVU7GtIjU,16083
+sanic/constants.py,sha256=BPl-dSbJ6MXOsD6hIjy-M0f9Ec-Ll_EyhUACIEF4eis,862
+sanic/cookies/__init__.py,sha256=LhKqwzYs4p-8yFPsGXS81WvL279tFEqpbXNQx-xULX0,76
+sanic/cookies/__pycache__/__init__.cpython-312.pyc,,
+sanic/cookies/__pycache__/request.cpython-312.pyc,,
+sanic/cookies/__pycache__/response.cpython-312.pyc,,
+sanic/cookies/request.py,sha256=oAvWAZFOYWr5gY3z0n3xb2Qb_MHNjK8qRrVggZ-X-9M,4988
+sanic/cookies/response.py,sha256=wsuqyHOcf2SLTrvsaq-BDX2azaBUjTyYBVbXt--7cME,21455
+sanic/errorpages.py,sha256=lEUuc6XUxBNnjVo-ucRk9hRUL_Q67DUZU7gWZRzGbmE,12859
+sanic/exceptions.py,sha256=cSWFSlUZEPIlTTQAmH2V8sVaWmuRAr7tm7pjc7O8Opg,26463
+sanic/handlers/__init__.py,sha256=i0ajf0Bh0gLHLKexyuQ-2yyu4vVHc5re6UxV9vyD6a0,206
+sanic/handlers/__pycache__/__init__.cpython-312.pyc,,
+sanic/handlers/__pycache__/content_range.cpython-312.pyc,,
+sanic/handlers/__pycache__/directory.cpython-312.pyc,,
+sanic/handlers/__pycache__/error.cpython-312.pyc,,
+sanic/handlers/content_range.py,sha256=T44F9KDhwOOmi8JWEtiuRSunnceHDgm-dGqZ9KAHQjQ,2534
+sanic/handlers/directory.py,sha256=6nEBjsWSrjccHs4WsjbCDS7E36SPu11LClV9E_UEk6Q,5652
+sanic/handlers/error.py,sha256=1wdR31lZipA0fxQrqqOS3Y4BFjpPuSEFRhOZ9VNTb_A,7243
+sanic/headers.py,sha256=8aWCGUP5MlHsEeFNbSW_YeU5ZvpRxl7kd_J7d3_1Dks,18775
+sanic/helpers.py,sha256=fgBFbHBYtdYJhU6I6pv2gMdjFTz5xNqKEVtr6spiClw,4593
+sanic/http/__init__.py,sha256=qPK2fBZtnmDfYutL4QRqxbmDWn_10Edo2PV1bVJzpc8,117
+sanic/http/__pycache__/__init__.cpython-312.pyc,,
+sanic/http/__pycache__/constants.cpython-312.pyc,,
+sanic/http/__pycache__/http1.cpython-312.pyc,,
+sanic/http/__pycache__/http3.cpython-312.pyc,,
+sanic/http/__pycache__/stream.cpython-312.pyc,,
+sanic/http/constants.py,sha256=4znFzWXzBEy1beqELyJbZiaipoNqVnqpTRWUXSAfZZQ,890
+sanic/http/http1.py,sha256=rmGlWQmc3Wa_4JZfniFflteZJYrGpucwwoddRyWf_TQ,21441
+sanic/http/http3.py,sha256=XHxPryrZBtv6dpzevtqlF72a5puzt1VT7BdwjCW4_WY,13601
+sanic/http/stream.py,sha256=b-zJLvfxlSF5ktj3TEqnIo28WgViKXRfiQSMynlY-qQ,657
+sanic/http/tls/__init__.py,sha256=c34IH1GBUnLd1xZoO4q8yPifNPxwgIGgY9_03YBB7-4,132
+sanic/http/tls/__pycache__/__init__.cpython-312.pyc,,
+sanic/http/tls/__pycache__/context.cpython-312.pyc,,
+sanic/http/tls/__pycache__/creators.cpython-312.pyc,,
+sanic/http/tls/context.py,sha256=Rgp5Qof0Jh1E_Fdgd7H0ssRnxHH432BzRradkHV5_0U,7361
+sanic/http/tls/creators.py,sha256=PuMKA2xmyrFtLVKeFDPylX_tOYTytrCn5alb0P0UuI4,9501
+sanic/log.py,sha256=5T-WHfwyAJzOnR2maitiPruqRM3PkgwMIoOqM0JQyVY,529
+sanic/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/logging/__pycache__/__init__.cpython-312.pyc,,
+sanic/logging/__pycache__/color.cpython-312.pyc,,
+sanic/logging/__pycache__/default.cpython-312.pyc,,
+sanic/logging/__pycache__/deprecation.cpython-312.pyc,,
+sanic/logging/__pycache__/filter.cpython-312.pyc,,
+sanic/logging/__pycache__/formatter.cpython-312.pyc,,
+sanic/logging/__pycache__/loggers.cpython-312.pyc,,
+sanic/logging/__pycache__/setup.cpython-312.pyc,,
+sanic/logging/color.py,sha256=HEwkxKCxwpBdboT36IynaN6FPXdted4aDKF6n61OpNQ,1586
+sanic/logging/default.py,sha256=GZ5SQTYHUF2CBA6zUCBHUfPh9CmM0Phl_58tjwqCMp8,1637
+sanic/logging/deprecation.py,sha256=XCxb3RZEK49nXM8cm1ME9hrg3hi6sdfar5EadTtJvW8,934
+sanic/logging/filter.py,sha256=mmcHnPHoeFMdNM8VeBSFrTD3L7Rte9_U2qvCE5ynOAs,298
+sanic/logging/formatter.py,sha256=fjYg_A-sxEkNp6tipnBrKceOSOcgUsDkrwxhSoR6LA0,12112
+sanic/logging/loggers.py,sha256=RlEB2C_pOzUOet6QrQfay0JEOkGtrzRLD83uzeFew5U,951
+sanic/logging/setup.py,sha256=2ulciGU07NXIaIccUUorkyYugkgWn8jGhZJwGBTHDkQ,1453
+sanic/middleware.py,sha256=o17m6DkHpglyB_WWNi_nI200rt7N2Occx90qtIm9cDM,2989
+sanic/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/mixins/__pycache__/__init__.cpython-312.pyc,,
+sanic/mixins/__pycache__/base.cpython-312.pyc,,
+sanic/mixins/__pycache__/commands.cpython-312.pyc,,
+sanic/mixins/__pycache__/exceptions.cpython-312.pyc,,
+sanic/mixins/__pycache__/listeners.cpython-312.pyc,,
+sanic/mixins/__pycache__/middleware.cpython-312.pyc,,
+sanic/mixins/__pycache__/routes.cpython-312.pyc,,
+sanic/mixins/__pycache__/signals.cpython-312.pyc,,
+sanic/mixins/__pycache__/startup.cpython-312.pyc,,
+sanic/mixins/__pycache__/static.cpython-312.pyc,,
+sanic/mixins/base.py,sha256=fm6xXV5nnZLbHN_06tFX4oi7vWEygsusQjZksol4qL4,1071
+sanic/mixins/commands.py,sha256=ue9CNvhRh8ipirfN_xA4F7FV1H4IIke-oY_lmanbZkU,1000
+sanic/mixins/exceptions.py,sha256=Cnpz0mc8uY8Ou_EqVKkR807KrBuz9xWiNZoXC7vy0gM,3911
+sanic/mixins/listeners.py,sha256=TgBqCEBhNCqw3A4qMg9n9VttvvS-UQWFgY7dchGXFTY,16442
+sanic/mixins/middleware.py,sha256=KnZ5DHW-Lx_f03Y2gJCyYhVewsa--ocLQeVMhhHippg,8406
+sanic/mixins/routes.py,sha256=QUJr3D665j0OQJhaYrhSSRTCl9ntf4y23I_ohcTJksI,31298
+sanic/mixins/signals.py,sha256=hnViaO13ieg0BHwJDNp3aUBKZLHHGqADAMS2flNhd0I,5235
+sanic/mixins/startup.py,sha256=U6LCJKLISXuy71msxTlGesJ2gN_mmgoFNZ14qHSWOwU,53793
+sanic/mixins/static.py,sha256=SWYOmOioOp_EkmfmeF4zOCSGiWovxCG1r8mhCl6FxcM,16399
+sanic/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/models/__pycache__/__init__.cpython-312.pyc,,
+sanic/models/__pycache__/asgi.cpython-312.pyc,,
+sanic/models/__pycache__/ctx_types.cpython-312.pyc,,
+sanic/models/__pycache__/futures.cpython-312.pyc,,
+sanic/models/__pycache__/handler_types.cpython-312.pyc,,
+sanic/models/__pycache__/http_types.cpython-312.pyc,,
+sanic/models/__pycache__/protocol_types.cpython-312.pyc,,
+sanic/models/__pycache__/server_types.cpython-312.pyc,,
+sanic/models/asgi.py,sha256=lM9GkIYGrYLUwkFhgULtKqtcFqqzS2ymgX5vb6TBfPc,3046
+sanic/models/ctx_types.py,sha256=LFwkvc59vARZK40aB8hVTZuoNWeBBDWFxYl0DvZhtB4,1948
+sanic/models/futures.py,sha256=Vum3T1E60OD9yQ7K5WJ0wLH3aA9NhXwjDrdZj5KKXz4,1690
+sanic/models/handler_types.py,sha256=MeuQnTdKvEmmLLNh7FwLzJ3VEM3hgeMocp2JfbW6wq0,1013
+sanic/models/http_types.py,sha256=TbHjOPzobj8Jnks4423Yf7CgpcJcZHsgRVYu-Dn2eac,941
+sanic/models/protocol_types.py,sha256=OK7Tno_xmXlr1b7H_LAhv6_CGkiLMvjg50crV6PzDE4,570
+sanic/models/server_types.py,sha256=Rt9Cfbbh6tZ4tFYqchKEjC0C5hJV_HeIldiWCei6K1M,2390
+sanic/pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/pages/__pycache__/__init__.cpython-312.pyc,,
+sanic/pages/__pycache__/base.cpython-312.pyc,,
+sanic/pages/__pycache__/css.cpython-312.pyc,,
+sanic/pages/__pycache__/directory_page.cpython-312.pyc,,
+sanic/pages/__pycache__/error.cpython-312.pyc,,
+sanic/pages/base.py,sha256=oSfb9U80yMb5_wEkpIgGjw9zRNs-Z1-Rqf1xSruzwrY,2301
+sanic/pages/css.py,sha256=Oe8KpBPwgMT6sSMFWTkvbbd9xOiC_7s69WLgR-fihpw,1074
+sanic/pages/directory_page.py,sha256=XNCbZ1huxY0gfjQkUe7fgSvrSflLsiA_vV9KYAw12B8,1669
+sanic/pages/error.py,sha256=4Wk0JzRWDWk2_K5_nyWQlZXmGKsvBITA6eO2U9j5D3s,3943
+sanic/pages/styles/BasePage.css,sha256=XmqZR2AeLkMtbXOg_7UN0neK0rH_u5J_ns2Wu5rsauA,2852
+sanic/pages/styles/DirectoryPage.css,sha256=rVJkr2u3yqoXbx_iRGU1tf0ktyNyzVn0Yhjf2hG_BuE,909
+sanic/pages/styles/ErrorPage.css,sha256=QfFiKMBqw88yOC_dsm5Tzo0ucMtxxNC2WxJRMkfBRks,2126
+sanic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/request/__init__.py,sha256=t-tmbN2UK6e6sCBs8pipRZ88Z_pECj5Pc4C1eiuF2Iw,210
+sanic/request/__pycache__/__init__.cpython-312.pyc,,
+sanic/request/__pycache__/form.cpython-312.pyc,,
+sanic/request/__pycache__/parameters.cpython-312.pyc,,
+sanic/request/__pycache__/types.cpython-312.pyc,,
+sanic/request/form.py,sha256=mcvMks5ysO9Z4IRTboBpDYiK1IqfmKTM90QVYK2p8Bw,3830
+sanic/request/parameters.py,sha256=G-I_YEno48jWABDzsk7lwK8G06jc4spROJo8jWyn-e8,1102
+sanic/request/types.py,sha256=BA5FDrbRXSVMBEjYFJX-Z87zkSrnaGjuW0YJlzHHnaE,36965
+sanic/response/__init__.py,sha256=Pt4p_yILevgwftUBLJCzWlKoKBq-qvfOjBPRjUIMMNY,504
+sanic/response/__pycache__/__init__.cpython-312.pyc,,
+sanic/response/__pycache__/convenience.cpython-312.pyc,,
+sanic/response/__pycache__/types.cpython-312.pyc,,
+sanic/response/convenience.py,sha256=Q5TTT0ROnJ2ffrnds1_1t3wgtMRM-ceL0yFNDc5VeMQ,14202
+sanic/response/types.py,sha256=7WyYrL493rbPW0LBBVzjsoNEduJos57OqU9gtzYR9KM,18262
+sanic/router.py,sha256=DKcQiv9YDgJD9x6-EKpOen-A255pFouQXPzZzmRykhA,9283
+sanic/server/__init__.py,sha256=ifxPV3-LZXbgaGvDSFlEk8ziJxBX9oKpYpEDgK8uqoE,375
+sanic/server/__pycache__/__init__.cpython-312.pyc,,
+sanic/server/__pycache__/async_server.cpython-312.pyc,,
+sanic/server/__pycache__/events.cpython-312.pyc,,
+sanic/server/__pycache__/goodbye.cpython-312.pyc,,
+sanic/server/__pycache__/loop.cpython-312.pyc,,
+sanic/server/__pycache__/runners.cpython-312.pyc,,
+sanic/server/__pycache__/socket.cpython-312.pyc,,
+sanic/server/async_server.py,sha256=NWAozvMhVsdqa2bq6X2zgagchJLAs1cmiPdFaemaWuU,3443
+sanic/server/events.py,sha256=bP6K007yi2R_6o8PTIflrAIFrBSZU7Y0nDSJmYAol7s,1005
+sanic/server/goodbye.py,sha256=nc6Rg3AjEQaU_hss0dB_8WSe2ZhioRXwStBGDnjtVPQ,1096
+sanic/server/loop.py,sha256=5SANqxl0vxuxwJeRzM2UZ6RIUEM9ejNvi1EIajcFk6I,2515
+sanic/server/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/server/protocols/__pycache__/__init__.cpython-312.pyc,,
+sanic/server/protocols/__pycache__/base_protocol.cpython-312.pyc,,
+sanic/server/protocols/__pycache__/http_protocol.cpython-312.pyc,,
+sanic/server/protocols/__pycache__/websocket_protocol.cpython-312.pyc,,
+sanic/server/protocols/base_protocol.py,sha256=5vi51N8BA2xYUfB5Molyr4flXIRVGVzWeHA-RKLdiPQ,9304
+sanic/server/protocols/http_protocol.py,sha256=TVQ6E2h0CzZj6xPH5eqwEt--QFjE9qRKOHNOOhw61JY,11287
+sanic/server/protocols/websocket_protocol.py,sha256=3BJCpuMSIWexZ-_vjnOvV_tarCEV7y0u5wkfNrPVLV8,8306
+sanic/server/runners.py,sha256=j1oOa09ZLGSY0L0zA-bcLMqNj8DFvjSrRHbMx4C6GF0,13052
+sanic/server/socket.py,sha256=gCYBVdpAS8GcuB3NTtsE4IeuBiafGvQ77QbTkBiTJCs,3743
+sanic/server/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/server/websockets/__pycache__/__init__.cpython-312.pyc,,
+sanic/server/websockets/__pycache__/connection.cpython-312.pyc,,
+sanic/server/websockets/__pycache__/frame.cpython-312.pyc,,
+sanic/server/websockets/__pycache__/impl.cpython-312.pyc,,
+sanic/server/websockets/connection.py,sha256=z6mbE5zAWFDAmxWbvE--12evDST8qDWOkOve15P8VJA,2283
+sanic/server/websockets/frame.py,sha256=eN-BDFBiuphFRc7-0mD-YFdUkMBWZQ4VDai0cd8DCFk,11459
+sanic/server/websockets/impl.py,sha256=Ki5GcNNX7z7L6JzzrtyKvTO2f7tBWS-jlJmrd3E_NaM,36260
+sanic/signals.py,sha256=lYY6Hqao2z0U6I8jA2-qKbiBEoUhhMjpyqaxg79whGI,14735
+sanic/simple.py,sha256=BhEg0NxDoUnGmNYc1asrejd0sp-3lPwx4FhGY7HqnpU,435
+sanic/startup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/startup/__pycache__/__init__.cpython-312.pyc,,
+sanic/startup/__pycache__/errors.cpython-312.pyc,,
+sanic/startup/errors.py,sha256=FXuddH22bzPlz8xKqgTbHSDQtrob8w80cHlWm0crR9c,1446
+sanic/touchup/__init__.py,sha256=kBf5cCmZAwRD3buui0EeryaCc0lieMZQJ9l4Asms-jI,109
+sanic/touchup/__pycache__/__init__.cpython-312.pyc,,
+sanic/touchup/__pycache__/meta.cpython-312.pyc,,
+sanic/touchup/__pycache__/service.cpython-312.pyc,,
+sanic/touchup/meta.py,sha256=hLR72V4D4U4qV9Sd5uW9GtKpp7mgRDtu7L5AlcvLmok,702
+sanic/touchup/schemes/__init__.py,sha256=UVoQByp8fxnA0yVn79FHxXfqQayvdMLJBvpSzh1NR24,144
+sanic/touchup/schemes/__pycache__/__init__.cpython-312.pyc,,
+sanic/touchup/schemes/__pycache__/altsvc.cpython-312.pyc,,
+sanic/touchup/schemes/__pycache__/base.cpython-312.pyc,,
+sanic/touchup/schemes/__pycache__/ode.cpython-312.pyc,,
+sanic/touchup/schemes/altsvc.py,sha256=iW97wVKRCy17GBzLopqqNlI77DH0BDNNrsszzsAXix8,1584
+sanic/touchup/schemes/base.py,sha256=qa9jP3XX9pa6LEi8js95kyp179wSgaKW5FXySR1ixkA,992
+sanic/touchup/schemes/ode.py,sha256=jCATsiwa4jVKlY7orfo9ntXH7hlv2pymjBCHdsUDRIk,2857
+sanic/touchup/service.py,sha256=9X8cF0eZY-m90DvuWbma5pnIvKMGUwtKlFHTUwKbkiw,907
+sanic/types/__init__.py,sha256=nWL8T3hDPq1KBWF1086JgXh5uDctgZqr2koOxIL9axE,70
+sanic/types/__pycache__/__init__.cpython-312.pyc,,
+sanic/types/__pycache__/hashable_dict.cpython-312.pyc,,
+sanic/types/__pycache__/shared_ctx.cpython-312.pyc,,
+sanic/types/hashable_dict.py,sha256=RJe9BXg7Ej57KKzi-x7drY-uziAo2bApSTIzYvU67U4,99
+sanic/types/shared_ctx.py,sha256=1igVxCx9sDQ1-Hkw9-e_cl4_iVlzvjlHbcLMmhWuOkU,1893
+sanic/utils.py,sha256=SJzfIANP2N2lRzMqUfwSjjVpsejagn04xepxmNRe9s0,4484
+sanic/views.py,sha256=EttoFxb-Ro3jMmSklHI94BC493pNsf8_xB1bG3JtS4U,8507
+sanic/worker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic/worker/__pycache__/__init__.cpython-312.pyc,,
+sanic/worker/__pycache__/constants.cpython-312.pyc,,
+sanic/worker/__pycache__/daemon.cpython-312.pyc,,
+sanic/worker/__pycache__/inspector.cpython-312.pyc,,
+sanic/worker/__pycache__/loader.cpython-312.pyc,,
+sanic/worker/__pycache__/manager.cpython-312.pyc,,
+sanic/worker/__pycache__/multiplexer.cpython-312.pyc,,
+sanic/worker/__pycache__/process.cpython-312.pyc,,
+sanic/worker/__pycache__/reloader.cpython-312.pyc,,
+sanic/worker/__pycache__/restarter.cpython-312.pyc,,
+sanic/worker/__pycache__/serve.cpython-312.pyc,,
+sanic/worker/__pycache__/state.cpython-312.pyc,,
+sanic/worker/constants.py,sha256=pf9GgJcHqLtC2t9txPSK0CTUWuDBz-VQQbcQEUrsQR8,465
+sanic/worker/daemon.py,sha256=LdlIC8L0jboK--6xZwuM6c1m_yK5y7Pz7VnQTLT9Y6g,14773
+sanic/worker/inspector.py,sha256=5dFH9AQl2KfOLiHaabfxPqzIicWz0DKd5m0YhKf32rU,5176
+sanic/worker/loader.py,sha256=457318EvLOI4xCOC0Cw9G3bvGdPn7eTfqQb7duSMa6k,5958
+sanic/worker/manager.py,sha256=5KCv5d3e3dV_A-S49kmKQ_7e7RT2XTuTat-msKy4ZHY,19322
+sanic/worker/multiplexer.py,sha256=QBy-WJeOqNJg7e3LPtHK5WSO2owm1VSNvgOCYXWfMQY,5490
+sanic/worker/process.py,sha256=JesyIhVUMLT50LnP3mRUW2Z1K_3IDppJ87ebONftzFc,9103
+sanic/worker/reloader.py,sha256=BJWbtGzbIHFqfUioMG5-rYLufBMLFWvTdo1bMUM-Lug,3955
+sanic/worker/restarter.py,sha256=dPKgpfUI5Y4BGd0Iz_sX0fCjKPlHmF5_vH9uagTf_2s,3042
+sanic/worker/serve.py,sha256=oXEjM3cfEdi14CsHQxfE8MlExiwtLyzf5ZErdwoVw2Y,4776
+sanic/worker/state.py,sha256=lJfJ2D5xYl64zP-LWRIGQFJeN9h99X98LOGMlvfsm98,2375
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/WHEEL
new file mode 100644
index 0000000..e7fa31b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.9.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/entry_points.txt
new file mode 100644
index 0000000..4cae386
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[console_scripts]
+sanic = sanic.__main__:main
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..35740e3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016-present Sanic Community
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/top_level.txt
new file mode 100644
index 0000000..fe6e629
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic-25.12.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+sanic
diff --git a/.venv/lib/python3.12/site-packages/sanic/__init__.py b/.venv/lib/python3.12/site-packages/sanic/__init__.py
new file mode 100644
index 0000000..8438ded
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/__init__.py
@@ -0,0 +1,86 @@
+from types import SimpleNamespace
+
+from typing_extensions import TypeAlias
+
+from sanic.__version__ import __version__
+from sanic.app import Sanic
+from sanic.blueprints import Blueprint
+from sanic.config import Config
+from sanic.constants import HTTPMethod
+from sanic.exceptions import (
+    BadRequest,
+    ExpectationFailed,
+    FileNotFound,
+    Forbidden,
+    HeaderNotFound,
+    InternalServerError,
+    InvalidHeader,
+    MethodNotAllowed,
+    NotFound,
+    RangeNotSatisfiable,
+    SanicException,
+    ServerError,
+    ServiceUnavailable,
+    Unauthorized,
+)
+from sanic.request import Request
+from sanic.response import (
+    HTTPResponse,
+    empty,
+    file,
+    html,
+    json,
+    raw,
+    redirect,
+    text,
+)
+from sanic.server.websockets.impl import WebsocketImplProtocol as Websocket
+
+
+DefaultSanic: TypeAlias = "Sanic[Config, SimpleNamespace]"
+"""
+A type alias for a Sanic app with a default config and namespace.
+"""
+
+DefaultRequest: TypeAlias = Request[DefaultSanic, SimpleNamespace]
+"""
+A type alias for a request with a default Sanic app and namespace.
+"""
+
+__all__ = (
+    "__version__",
+    # Common objects
+    "Sanic",
+    "Config",
+    "Blueprint",
+    "HTTPMethod",
+    "HTTPResponse",
+    "Request",
+    "Websocket",
+    # Common types
+    "DefaultSanic",
+    "DefaultRequest",
+    # Common exceptions
+    "BadRequest",
+    "ExpectationFailed",
+    "FileNotFound",
+    "Forbidden",
+    "HeaderNotFound",
+    "InternalServerError",
+    "InvalidHeader",
+    "MethodNotAllowed",
+    "NotFound",
+    "RangeNotSatisfiable",
+    "SanicException",
+    "ServerError",
+    "ServiceUnavailable",
+    "Unauthorized",
+    # Common response methods
+    "empty",
+    "file",
+    "html",
+    "json",
+    "raw",
+    "redirect",
+    "text",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/__main__.py b/.venv/lib/python3.12/site-packages/sanic/__main__.py
new file mode 100644
index 0000000..b419ae9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/__main__.py
@@ -0,0 +1,29 @@
+import tracerite
+
+try:
+    tracerite.load()
+except Exception as exc:
+    raise RuntimeError(
+        "Failed to initialize tracerite. Please verify that tracerite is "
+        "correctly installed and compatible with this environment."
+    ) from exc
+
+from sanic.cli.app import SanicCLI
+from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
+from sanic.startup.errors import maybe_handle_startup_error
+
+if OS_IS_WINDOWS:
+    enable_windows_color_support()
+
+
+def main(args=None):
+    try:
+        cli = SanicCLI()
+        cli.attach()
+        cli.run(args)
+    except Exception as e:
+        maybe_handle_startup_error(e)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/.venv/lib/python3.12/site-packages/sanic/__version__.py b/.venv/lib/python3.12/site-packages/sanic/__version__.py
new file mode 100644
index 0000000..4074706
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/__version__.py
@@ -0,0 +1 @@
+__version__ = "25.12.0"
diff --git a/.venv/lib/python3.12/site-packages/sanic/app.py b/.venv/lib/python3.12/site-packages/sanic/app.py
new file mode 100644
index 0000000..e32f0b7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/app.py
@@ -0,0 +1,2552 @@
+from __future__ import annotations
+
+import asyncio
+import logging
+import logging.config
+import re
+import sys
+
+from asyncio import (
+    AbstractEventLoop,
+    CancelledError,
+    Task,
+    ensure_future,
+    get_running_loop,
+    wait_for,
+)
+from asyncio.futures import Future
+from collections import defaultdict, deque
+from collections.abc import Awaitable, Coroutine, Iterable, Iterator
+from contextlib import contextmanager, suppress
+from enum import Enum
+from functools import partial, wraps
+from inspect import isawaitable
+from os import environ
+from pathlib import Path
+from socket import socket
+from traceback import format_exc
+from types import SimpleNamespace
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    AnyStr,
+    Callable,
+    ClassVar,
+    Generic,
+    Literal,
+    TypeVar,
+    cast,
+    overload,
+)
+from urllib.parse import urlencode, urlunparse
+
+from sanic_routing.exceptions import FinalizationError, NotFound
+from sanic_routing.route import Route
+
+from sanic.application.ext import setup_ext
+from sanic.application.state import ApplicationState, ServerStage
+from sanic.asgi import ASGIApp, Lifespan
+from sanic.base.root import BaseSanic
+from sanic.blueprint_group import BlueprintGroup
+from sanic.blueprints import Blueprint
+from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
+from sanic.config import SANIC_PREFIX, Config
+from sanic.exceptions import (
+    BadRequest,
+    SanicException,
+    ServerError,
+    URLBuildError,
+)
+from sanic.handlers import ErrorHandler
+from sanic.helpers import Default, _default
+from sanic.http import Stage
+from sanic.log import LOGGING_CONFIG_DEFAULTS, error_logger, logger
+from sanic.logging.deprecation import deprecation
+from sanic.logging.setup import setup_logging
+from sanic.middleware import Middleware, MiddlewareLocation
+from sanic.mixins.commands import CommandMixin
+from sanic.mixins.listeners import ListenerEvent
+from sanic.mixins.startup import StartupMixin
+from sanic.mixins.static import StaticHandleMixin
+from sanic.models.ctx_types import REPLContext
+from sanic.models.futures import (
+    FutureException,
+    FutureListener,
+    FutureMiddleware,
+    FutureRegistry,
+    FutureRoute,
+    FutureSignal,
+)
+from sanic.models.handler_types import ListenerType, MiddlewareType
+from sanic.models.handler_types import Sanic as SanicVar
+from sanic.request import Request
+from sanic.response import BaseHTTPResponse, HTTPResponse, ResponseStream
+from sanic.router import Router
+from sanic.server.websockets.impl import ConnectionClosed
+from sanic.signals import Event, Signal, SignalRouter
+from sanic.touchup import TouchUp, TouchUpMeta
+from sanic.types.shared_ctx import SharedContext
+from sanic.worker.inspector import Inspector
+from sanic.worker.loader import CertLoader
+from sanic.worker.manager import WorkerManager
+
+
+if TYPE_CHECKING:
+    try:
+        from sanic_ext import Extend  # type: ignore
+        from sanic_ext.extensions.base import Extension  # type: ignore
+    except ImportError:
+        Extend = TypeVar("Extend", type)  # type: ignore
+
+
+if OS_IS_WINDOWS:  # no cov
+    enable_windows_color_support()
+
+ctx_type = TypeVar("ctx_type")
+config_type = TypeVar("config_type", bound=Config)
+
+
+class Sanic(
+    Generic[config_type, ctx_type],
+    StaticHandleMixin,
+    BaseSanic,
+    StartupMixin,
+    CommandMixin,
+    metaclass=TouchUpMeta,
+):
+    """The main application instance
+
+    You will create an instance of this class and use it to register
+    routes, listeners, middleware, blueprints, error handlers, etc.
+
+    By convention, it is often called `app`. It must be named using
+    the `name` parameter and is roughly constrained to the same
+    restrictions as a Python module name, however, it can contain
+    hyphens (`-`).
+
+    ```python
+    # will cause an error because it contains spaces
+    Sanic("This is not legal")
+    ```
+
+    ```python
+    # this is legal
+    Sanic("Hyphens-are-legal_or_also_underscores")
+    ```
+
+    Args:
+        name (str): The name of the application. Must be a valid
+            Python module name (including hyphens).
+        config (Optional[config_type]): The configuration to use for
+            the application. Defaults to `None`.
+        ctx (Optional[ctx_type]): The context to use for the
+            application. Defaults to `None`.
+        router (Optional[Router]): The router to use for the
+            application. Defaults to `None`.
+        signal_router (Optional[SignalRouter]): The signal router to
+            use for the application. Defaults to `None`.
+        error_handler (Optional[ErrorHandler]): The error handler to
+            use for the application. Defaults to `None`.
+        env_prefix (Optional[str]): The prefix to use for environment
+            variables. Defaults to `SANIC_`.
+        request_class (Optional[Type[Request]]): The request class to
+            use for the application. Defaults to `Request`.
+        strict_slashes (bool): Whether to enforce strict slashes.
+            Defaults to `False`.
+        log_config (Optional[Dict[str, Any]]): The logging configuration
+            to use for the application. Defaults to `None`.
+        configure_logging (bool): Whether to configure logging.
+            Defaults to `True`.
+        dumps (Optional[Callable[..., AnyStr]]): The function to use
+            for serializing JSON. Defaults to `None`.
+        loads (Optional[Callable[..., Any]]): The function to use
+            for deserializing JSON. Defaults to `None`.
+        inspector (bool): Whether to enable the inspector. Defaults
+            to `False`.
+        inspector_class (Optional[Type[Inspector]]): The inspector
+            class to use for the application. Defaults to `None`.
+        certloader_class (Optional[Type[CertLoader]]): The certloader
+            class to use for the application. Defaults to `None`.
+    """
+
+    __touchup__ = (
+        "handle_request",
+        "handle_exception",
+        "_run_response_middleware",
+        "_run_request_middleware",
+    )
+    __slots__ = (
+        "_asgi_app",
+        "_asgi_lifespan",
+        "_asgi_client",
+        "_blueprint_order",
+        "_delayed_tasks",
+        "_ext",
+        "_future_commands",
+        "_future_exceptions",
+        "_future_listeners",
+        "_future_middleware",
+        "_future_registry",
+        "_future_routes",
+        "_future_signals",
+        "_future_statics",
+        "_inspector",
+        "_manager",
+        "_state",
+        "_task_registry",
+        "_test_client",
+        "_test_manager",
+        "blueprints",
+        "certloader_class",
+        "config",
+        "configure_logging",
+        "ctx",
+        "error_handler",
+        "inspector_class",
+        "go_fast",
+        "listeners",
+        "multiplexer",
+        "named_request_middleware",
+        "named_response_middleware",
+        "repl_ctx",
+        "request_class",
+        "request_middleware",
+        "response_middleware",
+        "router",
+        "shared_ctx",
+        "signal_router",
+        "sock",
+        "strict_slashes",
+        "websocket_enabled",
+        "websocket_tasks",
+    )
+
+    _app_registry: ClassVar[dict[str, Sanic]] = {}
+    test_mode: ClassVar[bool] = False
+
+    @overload
+    def __init__(
+        self: Sanic[Config, SimpleNamespace],
+        name: str,
+        config: None = None,
+        ctx: None = None,
+        router: Router | None = None,
+        signal_router: SignalRouter | None = None,
+        error_handler: ErrorHandler | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        request_class: type[Request] | None = None,
+        strict_slashes: bool = False,
+        log_config: dict[str, Any] | None = None,
+        configure_logging: bool = True,
+        dumps: Callable[..., AnyStr] | None = None,
+        loads: Callable[..., Any] | None = None,
+        inspector: bool = False,
+        inspector_class: type[Inspector] | None = None,
+        certloader_class: type[CertLoader] | None = None,
+    ) -> None: ...
+
+    @overload
+    def __init__(
+        self: Sanic[config_type, SimpleNamespace],
+        name: str,
+        config: config_type | None = None,
+        ctx: None = None,
+        router: Router | None = None,
+        signal_router: SignalRouter | None = None,
+        error_handler: ErrorHandler | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        request_class: type[Request] | None = None,
+        strict_slashes: bool = False,
+        log_config: dict[str, Any] | None = None,
+        configure_logging: bool = True,
+        dumps: Callable[..., AnyStr] | None = None,
+        loads: Callable[..., Any] | None = None,
+        inspector: bool = False,
+        inspector_class: type[Inspector] | None = None,
+        certloader_class: type[CertLoader] | None = None,
+    ) -> None: ...
+
+    @overload
+    def __init__(
+        self: Sanic[Config, ctx_type],
+        name: str,
+        config: None = None,
+        ctx: ctx_type | None = None,
+        router: Router | None = None,
+        signal_router: SignalRouter | None = None,
+        error_handler: ErrorHandler | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        request_class: type[Request] | None = None,
+        strict_slashes: bool = False,
+        log_config: dict[str, Any] | None = None,
+        configure_logging: bool = True,
+        dumps: Callable[..., AnyStr] | None = None,
+        loads: Callable[..., Any] | None = None,
+        inspector: bool = False,
+        inspector_class: type[Inspector] | None = None,
+        certloader_class: type[CertLoader] | None = None,
+    ) -> None: ...
+
+    @overload
+    def __init__(
+        self: Sanic[config_type, ctx_type],
+        name: str,
+        config: config_type | None = None,
+        ctx: ctx_type | None = None,
+        router: Router | None = None,
+        signal_router: SignalRouter | None = None,
+        error_handler: ErrorHandler | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        request_class: type[Request] | None = None,
+        strict_slashes: bool = False,
+        log_config: dict[str, Any] | None = None,
+        configure_logging: bool = True,
+        dumps: Callable[..., AnyStr] | None = None,
+        loads: Callable[..., Any] | None = None,
+        inspector: bool = False,
+        inspector_class: type[Inspector] | None = None,
+        certloader_class: type[CertLoader] | None = None,
+    ) -> None: ...
+
+    def __init__(
+        self,
+        name: str,
+        config: config_type | None = None,
+        ctx: ctx_type | None = None,
+        router: Router | None = None,
+        signal_router: SignalRouter | None = None,
+        error_handler: ErrorHandler | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        request_class: type[Request] | None = None,
+        strict_slashes: bool = False,
+        log_config: dict[str, Any] | None = None,
+        configure_logging: bool = True,
+        dumps: Callable[..., AnyStr] | None = None,
+        loads: Callable[..., Any] | None = None,
+        inspector: bool = False,
+        inspector_class: type[Inspector] | None = None,
+        certloader_class: type[CertLoader] | None = None,
+    ) -> None:
+        super().__init__(name=name)
+        # logging
+        if configure_logging:
+            dict_config = log_config or LOGGING_CONFIG_DEFAULTS
+            logging.config.dictConfig(dict_config)  # type: ignore
+
+        if config and env_prefix != SANIC_PREFIX:
+            raise SanicException(
+                "When instantiating Sanic with config, you cannot also pass "
+                "env_prefix"
+            )
+
+        # First setup config
+        self.config: config_type = cast(
+            config_type, config or Config(env_prefix=env_prefix)
+        )
+        if inspector:
+            self.config.INSPECTOR = inspector
+
+        # Then we can do the rest
+        self._asgi_app: ASGIApp | None = None
+        self._asgi_lifespan: Lifespan | None = None
+        self._asgi_client: Any = None
+        self._blueprint_order: list[Blueprint] = []
+        self._delayed_tasks: list[str] = []
+        self._future_registry: FutureRegistry = FutureRegistry()
+        self._inspector: Inspector | None = None
+        self._manager: WorkerManager | None = None
+        self._state: ApplicationState = ApplicationState(app=self)
+        self._task_registry: dict[str, Task | None] = {}
+        self._test_client: Any = None
+        self._test_manager: Any = None
+        self.asgi = False
+        self.auto_reload = False
+        self.blueprints: dict[str, Blueprint] = {}
+        self.certloader_class: type[CertLoader] = (
+            certloader_class or CertLoader
+        )
+        self.configure_logging: bool = configure_logging
+        self.ctx: ctx_type = cast(ctx_type, ctx or SimpleNamespace())
+        self.error_handler: ErrorHandler = error_handler or ErrorHandler()
+        self.inspector_class: type[Inspector] = inspector_class or Inspector
+        self.listeners: dict[str, list[ListenerType[Any]]] = defaultdict(list)
+        self.named_request_middleware: dict[str, deque[Middleware]] = {}
+        self.named_response_middleware: dict[str, deque[Middleware]] = {}
+        self.repl_ctx: REPLContext = REPLContext()
+        self.request_class = request_class or Request
+        self.request_middleware: deque[Middleware] = deque()
+        self.response_middleware: deque[Middleware] = deque()
+        self.router: Router = router or Router()
+        self.shared_ctx: SharedContext = SharedContext()
+        self.signal_router: SignalRouter = signal_router or SignalRouter()
+        self.sock: socket | None = None
+        self.strict_slashes: bool = strict_slashes
+        self.websocket_enabled: bool = False
+        self.websocket_tasks: set[Future[Any]] = set()
+
+        # Register alternative method names
+        self.go_fast = self.run
+        self.router.ctx.app = self
+        self.signal_router.ctx.app = self
+        self.__class__.register_app(self)
+
+        if dumps:
+            BaseHTTPResponse._dumps = dumps  # type: ignore
+        if loads:
+            Request._loads = loads  # type: ignore
+
+    @property
+    def loop(self) -> AbstractEventLoop:
+        """Synonymous with asyncio.get_event_loop().
+
+        .. note::
+            Only supported when using the `app.run` method.
+
+        Returns:
+            AbstractEventLoop: The event loop for the application.
+
+        Raises:
+            SanicException: If the application is not running.
+        """
+        if self.state.stage is ServerStage.STOPPED and self.asgi is False:
+            raise SanicException(
+                "Loop can only be retrieved after the app has started "
+                "running. Not supported with `create_server` function"
+            )
+        try:
+            return get_running_loop()
+        except RuntimeError:  # no cov
+            return asyncio.get_event_loop_policy().get_event_loop()
+
+    # -------------------------------------------------------------------- #
+    # Registration
+    # -------------------------------------------------------------------- #
+
+    def register_listener(
+        self,
+        listener: ListenerType[SanicVar],
+        event: str,
+        *,
+        priority: int = 0,
+    ) -> ListenerType[SanicVar]:
+        """Register the listener for a given event.
+
+        Args:
+            listener (Callable): The listener to register.
+            event (str): The event to listen for.
+
+        Returns:
+            Callable: The listener that was registered.
+        """
+
+        try:
+            _event = ListenerEvent[event.upper()]
+        except (ValueError, AttributeError):
+            valid = ", ".join(
+                map(lambda x: x.lower(), ListenerEvent.__members__.keys())
+            )
+            raise BadRequest(f"Invalid event: {event}. Use one of: {valid}")
+
+        if "." in _event:
+            self.signal(_event.value, priority=priority)(
+                partial(self._listener, listener=listener)
+            )
+        else:
+            if priority:
+                error_logger.warning(
+                    f"Priority is not supported for {_event.value}"
+                )
+            self.listeners[_event.value].append(listener)
+
+        return listener
+
+    def register_middleware(
+        self,
+        middleware: MiddlewareType | Middleware,
+        attach_to: str = "request",
+        *,
+        priority: Default | int = _default,
+    ) -> MiddlewareType | Middleware:
+        """Register a middleware to be called before a request is handled.
+
+        Args:
+            middleware (Callable): A callable that takes in a request.
+            attach_to (str): Whether to attach to request or response.
+                Defaults to `'request'`.
+            priority (int): The priority level of the middleware.
+                Lower numbers are executed first. Defaults to `0`.
+
+        Returns:
+            Union[Callable, Callable[[Callable], Callable]]: The decorated
+                middleware function or a partial function depending on how
+                the method was called.
+        """
+        retval = middleware
+        location = MiddlewareLocation[attach_to.upper()]
+
+        if not isinstance(middleware, Middleware):
+            middleware = Middleware(
+                middleware,
+                location=location,
+                priority=priority if isinstance(priority, int) else 0,
+            )
+        elif middleware.priority != priority and isinstance(priority, int):
+            middleware = Middleware(
+                middleware.func,
+                location=middleware.location,
+                priority=priority,
+            )
+
+        if location is MiddlewareLocation.REQUEST:
+            if middleware not in self.request_middleware:
+                self.request_middleware.append(middleware)
+        if location is MiddlewareLocation.RESPONSE:
+            if middleware not in self.response_middleware:
+                self.response_middleware.appendleft(middleware)
+        return retval
+
+    def register_named_middleware(
+        self,
+        middleware: MiddlewareType,
+        route_names: Iterable[str],
+        attach_to: str = "request",
+        *,
+        priority: Default | int = _default,
+    ):
+        """Used to register named middleqare (middleware typically on blueprints)
+
+        Args:
+            middleware (Callable): A callable that takes in a request.
+            route_names (Iterable[str]): The route names to attach the
+                middleware to.
+            attach_to (str): Whether to attach to request or response.
+                Defaults to `'request'`.
+            priority (int): The priority level of the middleware.
+                Lower numbers are executed first. Defaults to `0`.
+
+        Returns:
+            Union[Callable, Callable[[Callable], Callable]]: The decorated
+                middleware function or a partial function depending on how
+                the method was called.
+        """  # noqa: E501
+        retval = middleware
+        location = MiddlewareLocation[attach_to.upper()]
+
+        if not isinstance(middleware, Middleware):
+            middleware = Middleware(
+                middleware,
+                location=location,
+                priority=priority if isinstance(priority, int) else 0,
+            )
+        elif middleware.priority != priority and isinstance(priority, int):
+            middleware = Middleware(
+                middleware.func,
+                location=middleware.location,
+                priority=priority,
+            )
+
+        if location is MiddlewareLocation.REQUEST:
+            for _rn in route_names:
+                if _rn not in self.named_request_middleware:
+                    self.named_request_middleware[_rn] = deque()
+                if middleware not in self.named_request_middleware[_rn]:
+                    self.named_request_middleware[_rn].append(middleware)
+        if location is MiddlewareLocation.RESPONSE:
+            for _rn in route_names:
+                if _rn not in self.named_response_middleware:
+                    self.named_response_middleware[_rn] = deque()
+                if middleware not in self.named_response_middleware[_rn]:
+                    self.named_response_middleware[_rn].appendleft(middleware)
+        return retval
+
+    def _apply_exception_handler(
+        self,
+        handler: FutureException,
+        route_names: list[str] | None = None,
+    ):
+        """Decorate a function to be registered as a handler for exceptions
+
+        :param exceptions: exceptions
+        :return: decorated function
+        """
+
+        for exception in handler.exceptions:
+            if isinstance(exception, (tuple, list)):
+                for e in exception:
+                    self.error_handler.add(e, handler.handler, route_names)
+            else:
+                self.error_handler.add(exception, handler.handler, route_names)
+        return handler.handler
+
+    def _apply_listener(self, listener: FutureListener):
+        return self.register_listener(
+            listener.listener, listener.event, priority=listener.priority
+        )
+
+    def _apply_route(
+        self, route: FutureRoute, overwrite: bool = False
+    ) -> list[Route]:
+        params = route._asdict()
+        params["overwrite"] = overwrite
+        websocket = params.pop("websocket", False)
+        subprotocols = params.pop("subprotocols", None)
+
+        if websocket:
+            self.enable_websocket()
+            websocket_handler = partial(
+                self._websocket_handler,
+                route.handler,
+                subprotocols=subprotocols,
+            )
+            websocket_handler.__name__ = route.handler.__name__  # type: ignore
+            websocket_handler.is_websocket = True  # type: ignore
+            params["handler"] = websocket_handler
+
+        ctx = params.pop("route_context")
+
+        with self.amend():
+            routes = self.router.add(**params)
+            if isinstance(routes, Route):
+                routes = [routes]
+
+            for r in routes:
+                r.extra.websocket = websocket
+                r.extra.static = params.get("static", False)
+                r.ctx.__dict__.update(ctx)
+
+        return routes
+
+    def _apply_middleware(
+        self,
+        middleware: FutureMiddleware,
+        route_names: list[str] | None = None,
+    ):
+        with self.amend():
+            if route_names:
+                return self.register_named_middleware(
+                    middleware.middleware, route_names, middleware.attach_to
+                )
+            else:
+                return self.register_middleware(
+                    middleware.middleware, middleware.attach_to
+                )
+
+    def _apply_signal(self, signal: FutureSignal) -> Signal:
+        with self.amend():
+            return self.signal_router.add(
+                handler=signal.handler,
+                event=signal.event,
+                condition=signal.condition,
+                exclusive=signal.exclusive,
+                priority=signal.priority,
+            )
+
+    @overload
+    def dispatch(
+        self,
+        event: str,
+        *,
+        condition: dict[str, str] | None = None,
+        context: dict[str, Any] | None = None,
+        fail_not_found: bool = True,
+        inline: Literal[True],
+        reverse: bool = False,
+    ) -> Coroutine[Any, Any, Awaitable[Any]]: ...
+
+    @overload
+    def dispatch(
+        self,
+        event: str,
+        *,
+        condition: dict[str, str] | None = None,
+        context: dict[str, Any] | None = None,
+        fail_not_found: bool = True,
+        inline: Literal[False] = False,
+        reverse: bool = False,
+    ) -> Coroutine[Any, Any, Awaitable[Task]]: ...
+
+    def dispatch(
+        self,
+        event: str,
+        *,
+        condition: dict[str, str] | None = None,
+        context: dict[str, Any] | None = None,
+        fail_not_found: bool = True,
+        inline: bool = False,
+        reverse: bool = False,
+    ) -> Coroutine[Any, Any, Awaitable[Task | Any]]:
+        """Dispatches an event to the signal router.
+
+        Args:
+            event (str): Name of the event to dispatch.
+            condition (Optional[Dict[str, str]]): Condition for the
+                event dispatch.
+            context (Optional[Dict[str, Any]]): Context for the event dispatch.
+            fail_not_found (bool): Whether to fail if the event is not found.
+                Default is `True`.
+            inline (bool): If `True`, returns the result directly. If `False`,
+                returns a `Task`. Default is `False`.
+            reverse (bool): Whether to reverse the dispatch order.
+                Default is `False`.
+
+        Returns:
+            Coroutine[Any, Any, Awaitable[Union[Task, Any]]]: An awaitable
+                that returns the result directly if `inline=True`, or a `Task`
+                if `inline=False`.
+
+        Examples:
+            ```python
+            @app.signal("user.registration.created")
+            async def send_registration_email(**context):
+                await send_email(context["email"], template="registration")
+
+            @app.post("/register")
+            async def handle_registration(request):
+                await do_registration(request)
+                await request.app.dispatch(
+                    "user.registration.created",
+                    context={"email": request.json.email}
+                })
+            ```
+        """
+        return self.signal_router.dispatch(
+            event,
+            context=context,
+            condition=condition,
+            inline=inline,
+            reverse=reverse,
+            fail_not_found=fail_not_found,
+        )
+
+    async def event(
+        self,
+        event: str | Enum,
+        timeout: int | float | None = None,
+        *,
+        condition: dict[str, Any] | None = None,
+        exclusive: bool = True,
+    ) -> None:
+        """Wait for a specific event to be triggered.
+
+        This method waits for a named event to be triggered and can be used
+        in conjunction with the signal system to wait for specific signals.
+        If the event is not found and auto-registration of events is enabled,
+        the event will be registered and then waited on. If the event is not
+        found and auto-registration is not enabled, a `NotFound` exception
+        is raised.
+
+        Auto-registration can be handled by setting the `EVENT_AUTOREGISTER`
+        config value to `True`.
+
+        ```python
+        app.config.EVENT_AUTOREGISTER = True
+        ```
+
+        Args:
+            event (str): The name of the event to wait for.
+            timeout (Optional[Union[int, float]]): An optional timeout value
+                in seconds. If provided, the wait will be terminated if the
+                timeout is reached. Defaults to `None`, meaning no timeout.
+            condition: If provided, method will only return when the signal
+                is dispatched with the given condition.
+            exclusive: When true (default), the signal can only be dispatched
+                when the condition has been met. When ``False``, the signal can
+                be dispatched either with or without it.
+
+        Raises:
+            NotFound: If the event is not found and auto-registration of
+                events is not enabled.
+
+        Returns:
+            The context dict of the dispatched signal.
+
+        Examples:
+            ```python
+            async def wait_for_event(app):
+                while True:
+                    print("> waiting")
+                    await app.event("foo.bar.baz")
+                    print("> event found")
+
+            @app.after_server_start
+            async def after_server_start(app, loop):
+                app.add_task(wait_for_event(app))
+            ```
+        """
+
+        waiter = self.signal_router.get_waiter(event, condition, exclusive)
+
+        if not waiter and self.config.EVENT_AUTOREGISTER:
+            self.signal_router.reset()
+            self.add_signal(None, event)
+            waiter = self.signal_router.get_waiter(event, condition, exclusive)
+            self.signal_router.finalize()
+
+        if not waiter:
+            raise NotFound(f"Could not find signal {event}")
+
+        return await wait_for(waiter.wait(), timeout=timeout)
+
+    def report_exception(
+        self, handler: Callable[[Sanic, Exception], Coroutine[Any, Any, None]]
+    ) -> Callable[[Exception], Coroutine[Any, Any, None]]:
+        """Register a handler to report exceptions.
+
+        A convenience method to register a handler for the signal that
+        is emitted when an exception occurs. It is typically used to
+        report exceptions to an external service.
+
+        It is equivalent to:
+
+        ```python
+        @app.signal(Event.SERVER_EXCEPTION_REPORT)
+        async def report(exception):
+            await do_something_with_error(exception)
+        ```
+
+        Args:
+            handler (Callable[[Sanic, Exception], Coroutine[Any, Any, None]]):
+                The handler to register.
+
+        Returns:
+            Callable[[Sanic, Exception], Coroutine[Any, Any, None]]: The
+                handler that was registered.
+        """
+
+        @wraps(handler)
+        async def report(exception: Exception) -> None:
+            await handler(self, exception)
+
+        self.add_signal(
+            handler=report, event=Event.SERVER_EXCEPTION_REPORT.value
+        )
+
+        return report
+
+    def enable_websocket(self, enable: bool = True) -> None:
+        """Enable or disable the support for websocket.
+
+        Websocket is enabled automatically if websocket routes are
+        added to the application. This typically will not need to be
+        called manually.
+
+        Args:
+            enable (bool, optional): If set to `True`, enables websocket
+                support. If set to `False`, disables websocket support.
+                Defaults to `True`.
+
+        Returns:
+            None
+        """
+
+        if not self.websocket_enabled:
+            # if the server is stopped, we want to cancel any ongoing
+            # websocket tasks, to allow the server to exit promptly
+            self.listener("before_server_stop")(self._cancel_websocket_tasks)
+
+        self.websocket_enabled = enable
+
+    def blueprint(
+        self,
+        blueprint: Blueprint | Iterable[Blueprint] | BlueprintGroup,
+        *,
+        url_prefix: str | None = None,
+        version: int | float | str | None = None,
+        strict_slashes: bool | None = None,
+        version_prefix: str | None = None,
+        name_prefix: str | None = None,
+    ) -> None:
+        """Register a blueprint on the application.
+
+        See [Blueprints](/en/guide/best-practices/blueprints) for more information.
+
+        Args:
+            blueprint (Union[Blueprint, Iterable[Blueprint], BlueprintGroup]): Blueprint object or (list, tuple) thereof.
+            url_prefix (Optional[str]): Prefix for all URLs bound to the blueprint. Defaults to `None`.
+            version (Optional[Union[int, float, str]]): Version prefix for URLs. Defaults to `None`.
+            strict_slashes (Optional[bool]): Enforce the trailing slashes. Defaults to `None`.
+            version_prefix (Optional[str]): Prefix for version. Defaults to `None`.
+            name_prefix (Optional[str]): Prefix for the blueprint name. Defaults to `None`.
+
+        Example:
+            ```python
+            app = Sanic("TestApp")
+            bp = Blueprint('TestBP')
+
+            @bp.route('/route')
+            def handler(request):
+                return text('Hello, Blueprint!')
+
+            app.blueprint(bp, url_prefix='/blueprint')
+            ```
+        """  # noqa: E501
+        options: dict[str, Any] = {}
+        if url_prefix is not None:
+            options["url_prefix"] = url_prefix
+        if version is not None:
+            options["version"] = version
+        if strict_slashes is not None:
+            options["strict_slashes"] = strict_slashes
+        if version_prefix is not None:
+            options["version_prefix"] = version_prefix
+        if name_prefix is not None:
+            options["name_prefix"] = name_prefix
+        if isinstance(blueprint, (Iterable, BlueprintGroup)):
+            for item in blueprint:
+                params: dict[str, Any] = {**options}
+                if isinstance(blueprint, BlueprintGroup):
+                    merge_from = [
+                        options.get("url_prefix", ""),
+                        blueprint.url_prefix or "",
+                    ]
+                    if not isinstance(item, BlueprintGroup):
+                        merge_from.append(item.url_prefix or "")
+                    merged_prefix = "/".join(
+                        str(u).strip("/") for u in merge_from if u
+                    ).rstrip("/")
+                    params["url_prefix"] = f"/{merged_prefix}"
+
+                    for _attr in ["version", "strict_slashes"]:
+                        if getattr(item, _attr) is None:
+                            params[_attr] = getattr(
+                                blueprint, _attr
+                            ) or options.get(_attr)
+                    if item.version_prefix == "/v":
+                        if blueprint.version_prefix == "/v":
+                            params["version_prefix"] = options.get(
+                                "version_prefix"
+                            )
+                        else:
+                            params["version_prefix"] = blueprint.version_prefix
+                    name_prefix = getattr(blueprint, "name_prefix", None)
+                    if name_prefix and "name_prefix" not in params:
+                        params["name_prefix"] = name_prefix
+                self.blueprint(item, **params)
+            return
+        if blueprint.name in self.blueprints:
+            assert self.blueprints[blueprint.name] is blueprint, (
+                'A blueprint with the name "%s" is already registered.  '
+                "Blueprint names must be unique." % (blueprint.name,)
+            )
+        else:
+            self.blueprints[blueprint.name] = blueprint
+            self._blueprint_order.append(blueprint)
+
+        if (
+            self.strict_slashes is not None
+            and blueprint.strict_slashes is None
+        ):
+            blueprint.strict_slashes = self.strict_slashes
+        blueprint.register(self, options)
+
+    def url_for(self, view_name: str, **kwargs):
+        """Build a URL based on a view name and the values provided.
+
+        This method constructs URLs for a given view name, taking into account
+        various special keyword arguments that can be used to modify the resulting
+        URL. It can handle internal routing as well as external URLs with different
+        schemes.
+
+        There are several special keyword arguments that can be used to modify
+        the URL that is built. They each begin with an underscore. They are:
+
+        - `_anchor`
+        - `_external`
+        - `_host`
+        - `_server`
+        - `_scheme`
+
+        Args:
+            view_name (str): String referencing the view name.
+            _anchor (str): Adds an "#anchor" to the end.
+            _scheme (str): Should be either "http" or "https", default is "http".
+            _external (bool): Whether to return the path or a full URL with scheme and host.
+            _host (str): Used when one or more hosts are defined for a route to tell Sanic which to use.
+            _server (str): If not using "_host", this will be used for defining the hostname of the URL.
+            **kwargs: Keys and values that are used to build request parameters and
+                    query string arguments.
+
+        Raises:
+            URLBuildError: If there are issues with constructing the URL.
+
+        Returns:
+            str: The built URL.
+
+        Examples:
+            Building a URL for a specific view with parameters:
+            ```python
+            url_for('view_name', param1='value1', param2='value2')
+            # /view-name?param1=value1¶m2=value2
+            ```
+
+            Creating an external URL with a specific scheme and anchor:
+            ```python
+            url_for('view_name', _scheme='https', _external=True, _anchor='section1')
+            # https://example.com/view-name#section1
+            ```
+
+            Creating a URL with a specific host:
+            ```python
+            url_for('view_name', _host='subdomain.example.com')
+            # http://subdomain.example.com/view-name
+        """  # noqa: E501
+        # find the route by the supplied view name
+        kw: dict[str, str] = {}
+        # special static files url_for
+
+        if "." not in view_name:
+            view_name = f"{self.name}.{view_name}"
+
+        if view_name.endswith(".static"):
+            name = kwargs.pop("name", None)
+            if name:
+                view_name = view_name.replace("static", name)
+            kw.update(name=view_name)
+
+        route = self.router.find_route_by_view_name(view_name, **kw)
+        if not route:
+            raise URLBuildError(
+                f"Endpoint with name `{view_name}` was not found"
+            )
+
+        uri = route.path
+
+        if getattr(route.extra, "static", None):
+            filename = kwargs.pop("filename", "")
+            # it's static folder
+            if "__file_uri__" in uri:
+                folder_ = uri.split("<__file_uri__:", 1)[0]
+                if folder_.endswith("/"):
+                    folder_ = folder_[:-1]
+
+                if filename.startswith("/"):
+                    filename = filename[1:]
+
+                kwargs["__file_uri__"] = filename
+
+        if (
+            uri != "/"
+            and uri.endswith("/")
+            and not route.strict
+            and not route.raw_path[:-1]
+        ):
+            uri = uri[:-1]
+
+        if not uri.startswith("/"):
+            uri = f"/{uri}"
+
+        out = uri
+
+        # _method is only a placeholder now, don't know how to support it
+        kwargs.pop("_method", None)
+        anchor = kwargs.pop("_anchor", "")
+        # _external need SERVER_NAME in config or pass _server arg
+        host = kwargs.pop("_host", None)
+        external = kwargs.pop("_external", False) or bool(host)
+        scheme = kwargs.pop("_scheme", "")
+        if route.extra.hosts and external:
+            if not host and len(route.extra.hosts) > 1:
+                raise ValueError(
+                    f"Host is ambiguous: {', '.join(route.extra.hosts)}"
+                )
+            elif host and host not in route.extra.hosts:
+                raise ValueError(
+                    f"Requested host ({host}) is not available for this "
+                    f"route: {route.extra.hosts}"
+                )
+            elif not host:
+                host = list(route.extra.hosts)[0]
+
+        if scheme and not external:
+            raise ValueError("When specifying _scheme, _external must be True")
+
+        netloc = kwargs.pop("_server", None)
+        if netloc is None and external:
+            netloc = host or self.config.get("SERVER_NAME", "")
+
+        if external:
+            if not scheme:
+                if ":" in netloc[:8]:
+                    scheme = netloc[:8].split(":", 1)[0]
+                else:
+                    scheme = "http"
+                # Replace http/https with ws/wss for WebSocket handlers
+                if route.extra.websocket:
+                    scheme = scheme.replace("http", "ws")
+
+            if "://" in netloc[:8]:
+                netloc = netloc.split("://", 1)[-1]
+
+        # find all the parameters we will need to build in the URL
+        # matched_params = re.findall(self.router.parameter_pattern, uri)
+        route.finalize()
+        for param_info in route.params.values():
+            # name, _type, pattern = self.router.parse_parameter_string(match)
+            # we only want to match against each individual parameter
+
+            try:
+                supplied_param = str(kwargs.pop(param_info.name))
+            except KeyError:
+                raise URLBuildError(
+                    f"Required parameter `{param_info.name}` was not "
+                    "passed to url_for"
+                )
+
+            # determine if the parameter supplied by the caller
+            # passes the test in the URL
+            if param_info.pattern:
+                pattern = (
+                    param_info.pattern[1]
+                    if isinstance(param_info.pattern, tuple)
+                    else param_info.pattern
+                )
+                passes_pattern = pattern.match(supplied_param)
+                if not passes_pattern:
+                    if param_info.cast is not str:
+                        msg = (
+                            f'Value "{supplied_param}" '
+                            f"for parameter `{param_info.name}` does "
+                            "not match pattern for type "
+                            f"`{param_info.cast.__name__}`: "
+                            f"{pattern.pattern}"
+                        )
+                    else:
+                        msg = (
+                            f'Value "{supplied_param}" for parameter '
+                            f"`{param_info.name}` does not satisfy "
+                            f"pattern {pattern.pattern}"
+                        )
+                    raise URLBuildError(msg)
+
+            # replace the parameter in the URL with the supplied value
+            replacement_regex = f"(<{param_info.name}.*?>)"
+            out = re.sub(replacement_regex, supplied_param, out)
+
+        # parse the remainder of the keyword arguments into a querystring
+        query_string = urlencode(kwargs, doseq=True) if kwargs else ""
+        # scheme://netloc/path;parameters?query#fragment
+        out = urlunparse((scheme, netloc, out, "", query_string, anchor))
+
+        return out
+
+    # -------------------------------------------------------------------- #
+    # Request Handling
+    # -------------------------------------------------------------------- #
+
+    async def handle_exception(
+        self,
+        request: Request,
+        exception: BaseException,
+        run_middleware: bool = True,
+    ) -> None:  # no cov
+        """A handler that catches specific exceptions and outputs a response.
+
+        .. note::
+            This method is typically used internally, and you should not need
+            to call it directly.
+
+        Args:
+            request (Request): The current request object.
+            exception (BaseException): The exception that was raised.
+            run_middleware (bool): Whether to run middleware. Defaults
+                to `True`.
+
+        Raises:
+            ServerError: response 500.
+        """
+        response = None
+        if not getattr(exception, "__dispatched__", False):
+            ...  # DO NOT REMOVE THIS LINE. IT IS NEEDED FOR TOUCHUP.
+            await self.dispatch(
+                "server.exception.report",
+                context={"exception": exception},
+            )
+        await self.dispatch(
+            "http.lifecycle.exception",
+            inline=True,
+            context={"request": request, "exception": exception},
+        )
+
+        if (
+            request.stream is not None
+            and request.stream.stage is not Stage.HANDLER
+        ):
+            error_logger.exception(exception, exc_info=True)
+            logger.error(
+                "The error response will not be sent to the client for "
+                f'the following exception:"{exception}". A previous response '
+                "has at least partially been sent."
+            )
+
+            handler = self.error_handler._lookup(
+                exception, request.name if request else None
+            )
+            if handler:
+                logger.warning(
+                    "An error occurred while handling the request after at "
+                    "least some part of the response was sent to the client. "
+                    "The response from your custom exception handler "
+                    f"{handler.__name__} will not be sent to the client."
+                    "Exception handlers should only be used to generate the "
+                    "exception responses. If you would like to perform any "
+                    "other action on a raised exception, consider using a "
+                    "signal handler like "
+                    '`@app.signal("http.lifecycle.exception")`\n'
+                    "For further information, please see the docs: "
+                    "https://sanicframework.org/en/guide/advanced/"
+                    "signals.html",
+                )
+            return
+
+        # -------------------------------------------- #
+        # Request Middleware
+        # -------------------------------------------- #
+        if run_middleware:
+            try:
+                middleware = (
+                    request.route and request.route.extra.request_middleware
+                ) or self.request_middleware
+                response = await self._run_request_middleware(
+                    request, middleware
+                )
+            except Exception as e:
+                return await self.handle_exception(request, e, False)
+        # No middleware results
+        if not response:
+            try:
+                response = self.error_handler.response(request, exception)
+                if isawaitable(response):
+                    response = await response
+            except Exception as e:
+                if isinstance(e, SanicException):
+                    response = self.error_handler.default(request, e)
+                elif self.debug:
+                    response = HTTPResponse(
+                        (
+                            f"Error while handling error: {e}\n"
+                            f"Stack: {format_exc()}"
+                        ),
+                        status=500,
+                    )
+                else:
+                    response = HTTPResponse(
+                        "An error occurred while handling an error", status=500
+                    )
+        if response is not None:
+            try:
+                request.reset_response()
+                response = await request.respond(response)
+            except BaseException:
+                # Skip response middleware
+                if request.stream:
+                    request.stream.respond(response)
+                await response.send(end_stream=True)
+                raise
+        else:
+            if request.stream:
+                response = request.stream.response
+
+        # Marked for cleanup and DRY with handle_request/handle_exception
+        # when ResponseStream is no longer supporder
+        if isinstance(response, BaseHTTPResponse):
+            await self.dispatch(
+                "http.lifecycle.response",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": response,
+                },
+            )
+            await response.send(end_stream=True)
+        elif isinstance(response, ResponseStream):
+            resp = await response(request)
+            await self.dispatch(
+                "http.lifecycle.response",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": resp,
+                },
+            )
+            await response.eof()
+        else:
+            raise ServerError(
+                f"Invalid response type {response!r} (need HTTPResponse)"
+            )
+
+    async def handle_request(self, request: Request) -> None:  # no cov
+        """Handles a request by dispatching it to the appropriate handler.
+
+        .. note::
+            This method is typically used internally, and you should not need
+            to call it directly.
+
+        Args:
+            request (Request): The current request object.
+
+        Raises:
+            ServerError: response 500.
+        """
+        __tracebackhide__ = True
+
+        await self.dispatch(
+            "http.lifecycle.handle",
+            inline=True,
+            context={"request": request},
+        )
+
+        # Define `response` var here to remove warnings about
+        # allocation before assignment below.
+        response: (
+            BaseHTTPResponse
+            | Coroutine[Any, Any, BaseHTTPResponse | None]
+            | ResponseStream
+            | None
+        ) = None
+        run_middleware = True
+        try:
+            await self.dispatch(
+                "http.routing.before",
+                inline=True,
+                context={"request": request},
+            )
+            # Fetch handler from router
+            route, handler, kwargs = self.router.get(
+                request.path,
+                request.method,
+                request.headers.getone("host", None),
+            )
+
+            request._match_info = {**kwargs}
+            request.route = route
+
+            await self.dispatch(
+                "http.routing.after",
+                inline=True,
+                context={
+                    "request": request,
+                    "route": route,
+                    "kwargs": kwargs,
+                    "handler": handler,
+                },
+            )
+
+            if (
+                request.stream
+                and request.stream.request_body
+                and not route.extra.ignore_body
+            ):
+                if hasattr(handler, "is_stream"):
+                    # Streaming handler: lift the size limit
+                    request.stream.request_max_size = float("inf")
+                else:
+                    # Non-streaming handler: preload body
+                    await request.receive_body()
+
+            # -------------------------------------------- #
+            # Request Middleware
+            # -------------------------------------------- #
+            run_middleware = False
+            if request.route.extra.request_middleware:
+                response = await self._run_request_middleware(
+                    request, request.route.extra.request_middleware
+                )
+
+            # No middleware results
+            if not response:
+                # -------------------------------------------- #
+                # Execute Handler
+                # -------------------------------------------- #
+
+                if handler is None:
+                    raise ServerError(
+                        "'None' was returned while requesting a "
+                        "handler from the router"
+                    )
+
+                # Run response handler
+                await self.dispatch(
+                    "http.handler.before",
+                    inline=True,
+                    context={"request": request},
+                )
+                response = handler(request, **request.match_info)
+                if isawaitable(response):
+                    response = await response
+                await self.dispatch(
+                    "http.handler.after",
+                    inline=True,
+                    context={"request": request},
+                )
+
+            if request.responded:
+                if response is not None:
+                    error_logger.error(
+                        "The response object returned by the route handler "
+                        "will not be sent to client. The request has already "
+                        "been responded to."
+                    )
+                if request.stream is not None:
+                    response = request.stream.response
+            elif response is not None:
+                response = await request.respond(response)  # type: ignore
+            elif not hasattr(handler, "is_websocket"):
+                response = request.stream.response  # type: ignore
+
+            # Marked for cleanup and DRY with handle_request/handle_exception
+            # when ResponseStream is no longer supporder
+            if isinstance(response, BaseHTTPResponse):
+                await self.dispatch(
+                    "http.lifecycle.response",
+                    inline=True,
+                    context={
+                        "request": request,
+                        "response": response,
+                    },
+                )
+                ...
+                await response.send(end_stream=True)
+            elif isinstance(response, ResponseStream):
+                resp = await response(request)
+                await self.dispatch(
+                    "http.lifecycle.response",
+                    inline=True,
+                    context={
+                        "request": request,
+                        "response": resp,
+                    },
+                )
+                await response.eof()
+            else:
+                if not hasattr(handler, "is_websocket"):
+                    raise ServerError(
+                        f"Invalid response type {response!r} "
+                        "(need HTTPResponse)"
+                    )
+
+        except CancelledError:  # type: ignore
+            raise
+        except Exception as e:
+            # Response Generation Failed
+            await self.handle_exception(
+                request, e, run_middleware=run_middleware
+            )
+
+    async def _websocket_handler(
+        self, handler, request, *args, subprotocols=None, **kwargs
+    ):
+        if self.asgi:
+            ws = request.transport.get_websocket_connection()
+            await ws.accept(subprotocols)
+        else:
+            protocol = request.transport.get_protocol()
+            ws = await protocol.websocket_handshake(request, subprotocols)
+
+        await self.dispatch(
+            "websocket.handler.before",
+            inline=True,
+            context={"request": request, "websocket": ws},
+            fail_not_found=False,
+        )
+        # schedule the application handler
+        # its future is kept in self.websocket_tasks in case it
+        # needs to be cancelled due to the server being stopped
+        fut = ensure_future(handler(request, ws, *args, **kwargs))
+        self.websocket_tasks.add(fut)
+        cancelled = False
+        try:
+            await fut
+            await self.dispatch(
+                "websocket.handler.after",
+                inline=True,
+                context={"request": request, "websocket": ws},
+                reverse=True,
+                fail_not_found=False,
+            )
+        except (CancelledError, ConnectionClosed):  # type: ignore
+            cancelled = True
+        except Exception as e:
+            self.error_handler.log(request, e)
+            await self.dispatch(
+                "websocket.handler.exception",
+                inline=True,
+                context={"request": request, "websocket": ws, "exception": e},
+                reverse=True,
+                fail_not_found=False,
+            )
+        finally:
+            self.websocket_tasks.remove(fut)
+            if cancelled:
+                ws.end_connection(1000)
+            else:
+                await ws.close()
+
+    # -------------------------------------------------------------------- #
+    # Testing
+    # -------------------------------------------------------------------- #
+
+    @property
+    def test_client(self) -> SanicTestClient:  # type: ignore # noqa
+        """A testing client that uses httpx and a live running server to reach into the application to execute handlers.
+
+        This property is available if the `sanic-testing` package is installed.
+
+        See [Test Clients](/en/plugins/sanic-testing/clients#wsgi-client-sanictestclient) for details.
+
+        Returns:
+            SanicTestClient: A testing client from the `sanic-testing` package.
+        """  # noqa: E501
+        if self._test_client:
+            return self._test_client
+        elif self._test_manager:
+            return self._test_manager.test_client
+        from sanic_testing.testing import SanicTestClient  # type: ignore
+
+        self._test_client = SanicTestClient(self)
+        return self._test_client
+
+    @property
+    def asgi_client(self) -> SanicASGITestClient:  # type: ignore # noqa
+        """A testing client that uses ASGI to reach into the application to execute handlers.
+
+        This property is available if the `sanic-testing` package is installed.
+
+        See [Test Clients](/en/plugins/sanic-testing/clients#asgi-async-client-sanicasgitestclient) for details.
+
+        Returns:
+            SanicASGITestClient: A testing client from the `sanic-testing` package.
+        """  # noqa: E501
+        if self._asgi_client:
+            return self._asgi_client
+        elif self._test_manager:
+            return self._test_manager.asgi_client
+        from sanic_testing.testing import SanicASGITestClient  # type: ignore
+
+        self._asgi_client = SanicASGITestClient(self)
+        return self._asgi_client
+
+    # -------------------------------------------------------------------- #
+    # Execution
+    # -------------------------------------------------------------------- #
+
+    async def _run_request_middleware(
+        self, request, middleware_collection
+    ):  # no cov
+        request._request_middleware_started = True
+
+        for middleware in middleware_collection:
+            await self.dispatch(
+                "http.middleware.before",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": None,
+                },
+                condition={"attach_to": "request"},
+            )
+
+            response = middleware(request)
+            if isawaitable(response):
+                response = await response
+
+            await self.dispatch(
+                "http.middleware.after",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": None,
+                },
+                condition={"attach_to": "request"},
+            )
+
+            if response:
+                return response
+        return None
+
+    async def _run_response_middleware(
+        self, request, response, middleware_collection
+    ):  # no cov
+        for middleware in middleware_collection:
+            await self.dispatch(
+                "http.middleware.before",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": response,
+                },
+                condition={"attach_to": "response"},
+            )
+
+            _response = middleware(request, response)
+            if isawaitable(_response):
+                _response = await _response
+
+            await self.dispatch(
+                "http.middleware.after",
+                inline=True,
+                context={
+                    "request": request,
+                    "response": _response if _response else response,
+                },
+                condition={"attach_to": "response"},
+            )
+
+            if _response:
+                response = _response
+                if isinstance(response, BaseHTTPResponse):
+                    response = request.stream.respond(response)
+                break
+        return response
+
+    def _build_endpoint_name(self, *parts):
+        parts = [self.name, *parts]
+        return ".".join(parts)
+
+    @classmethod
+    def _cancel_websocket_tasks(cls, app):
+        for task in app.websocket_tasks:
+            task.cancel()
+
+    @staticmethod
+    async def _listener(
+        app: Sanic, loop: AbstractEventLoop, listener: ListenerType
+    ):
+        try:
+            maybe_coro = listener(app)  # type: ignore
+        except TypeError:
+            name = getattr(
+                listener,
+                "__qualname__",
+                getattr(getattr(listener, "func", None), "__qualname__", None),
+            )
+            deprecation(
+                f"Passing the loop argument to listeners is deprecated. "
+                f"Your listener{f' {name!r}' if name else ''} should only "
+                "accept the app argument.",
+                26.6,
+            )
+            maybe_coro = listener(app, loop)  # type: ignore
+        if maybe_coro and isawaitable(maybe_coro):
+            await maybe_coro
+
+    # -------------------------------------------------------------------- #
+    # Task management
+    # -------------------------------------------------------------------- #
+
+    @classmethod
+    def _prep_task(
+        cls,
+        task,
+        app,
+        loop,
+    ):
+        async def do(task):
+            try:
+                if callable(task):
+                    try:
+                        task = task(app)
+                    except TypeError:
+                        task = task()
+                if isawaitable(task):
+                    return await task
+            except CancelledError:
+                error_logger.warning(
+                    f"Task {task} was cancelled before it completed."
+                )
+                raise
+            except Exception as e:
+                await app.dispatch(
+                    "server.exception.report",
+                    context={"exception": e},
+                )
+                raise
+
+        return do(task)
+
+    @classmethod
+    def _loop_add_task(
+        cls,
+        task,
+        app,
+        loop,
+        *,
+        name: str | None = None,
+        register: bool = True,
+    ) -> Task:
+        tsk: Task = task
+        if not isinstance(task, Future):
+            prepped = cls._prep_task(task, app, loop)
+            tsk = loop.create_task(prepped, name=name)
+
+        if name and register:
+            app._task_registry[name] = tsk
+
+        return tsk
+
+    @staticmethod
+    async def dispatch_delayed_tasks(app: Sanic) -> None:
+        """Signal handler for dispatching delayed tasks.
+
+        This is used to dispatch tasks that were added before the loop was
+        started, and will be called after the loop has started. It is
+        not typically used directly.
+
+        Args:
+            app (Sanic): The Sanic application instance.
+
+        Returns:
+            None
+        """
+        loop = asyncio.get_running_loop()
+        for name in app._delayed_tasks:
+            await app.dispatch(name, context={"app": app, "loop": loop})
+        app._delayed_tasks.clear()
+
+    @staticmethod
+    async def run_delayed_task(
+        app: Sanic,
+        loop: AbstractEventLoop,
+        task: Future[Any] | Task[Any] | Awaitable[Any],
+    ) -> None:
+        """Executes a delayed task within the context of a given app and loop.
+
+        This method prepares a given task by invoking the app's private
+        `_prep_task` method and then awaits the execution of the prepared task.
+
+        Args:
+            app (Any): The application instance on which the task will
+                be executed.
+            loop (AbstractEventLoop): The event loop where the task will
+                be scheduled.
+            task (Task[Any]): The task function that will be prepared
+                and executed.
+
+        Returns:
+            None
+        """
+        prepped = app._prep_task(task, app, loop)
+        await prepped
+
+    def add_task(
+        self,
+        task: Future[Any] | Coroutine[Any, Any, Any] | Awaitable[Any],
+        *,
+        name: str | None = None,
+        register: bool = True,
+    ) -> Task[Any] | None:
+        """Schedule a task to run later, after the loop has started.
+
+        While this is somewhat similar to `asyncio.create_task`, it can be
+        used before the loop has started (in which case it will run after the
+        loop has started in the `before_server_start` listener).
+
+        Naming tasks is a good practice as it allows you to cancel them later,
+        and allows Sanic to manage them when the server is stopped, if needed.
+
+        [See user guide re: background tasks](/en/guide/basics/tasks#background-tasks)
+
+        Args:
+            task (Union[Future[Any], Coroutine[Any, Any, Any], Awaitable[Any]]):
+                The future, coroutine, or awaitable to schedule.
+            name (Optional[str], optional): The name of the task, if needed for
+                later reference. Defaults to `None`.
+            register (bool, optional): Whether to register the task. Defaults
+                to `True`.
+
+        Returns:
+            Optional[Task[Any]]: The task that was scheduled, if applicable.
+        """  # noqa: E501
+        try:
+            loop = self.loop  # Will raise SanicError if loop is not started
+            return self._loop_add_task(
+                task, self, loop, name=name, register=register
+            )
+        except SanicException:
+            task_name = f"sanic.delayed_task.{hash(task)}"
+            if not self._delayed_tasks:
+                self.after_server_start(partial(self.dispatch_delayed_tasks))
+
+            if name:
+                raise RuntimeError(
+                    "Cannot name task outside of a running application"
+                )
+
+            self.signal(task_name)(partial(self.run_delayed_task, task=task))
+            self._delayed_tasks.append(task_name)
+            return None
+
+    @overload
+    def get_task(
+        self, name: str, *, raise_exception: Literal[True]
+    ) -> Task: ...
+
+    @overload
+    def get_task(
+        self, name: str, *, raise_exception: Literal[False]
+    ) -> Task | None: ...
+
+    @overload
+    def get_task(self, name: str, *, raise_exception: bool) -> Task | None: ...
+
+    def get_task(
+        self, name: str, *, raise_exception: bool = True
+    ) -> Task | None:
+        """Get a named task.
+
+        This method is used to get a task by its name. Optionally, you can
+        control whether an exception should be raised if the task is not found.
+
+        Args:
+            name (str): The name of the task to be retrieved.
+            raise_exception (bool): If `True`, an exception will be raised if
+                the task is not found. Defaults to `True`.
+
+        Returns:
+            Optional[Task]: The task, if found.
+        """
+        try:
+            return self._task_registry[name]
+        except KeyError:
+            if raise_exception:
+                raise SanicException(
+                    f'Registered task named "{name}" not found.'
+                )
+            return None
+
+    async def cancel_task(
+        self,
+        name: str,
+        msg: str | None = None,
+        *,
+        raise_exception: bool = True,
+    ) -> None:
+        """Cancel a named task.
+
+        This method is used to cancel a task by its name. Optionally, you can
+        provide a message that describes why the task was canceled, and control
+        whether an exception should be raised if the task is not found.
+
+        Args:
+            name (str): The name of the task to be canceled.
+            msg (Optional[str]): Optional message describing why the task was canceled. Defaults to None.
+            raise_exception (bool): If True, an exception will be raised if the task is not found. Defaults to True.
+
+        Example:
+            ```python
+            async def my_task():
+                try:
+                    await asyncio.sleep(10)
+                except asyncio.CancelledError as e:
+                    current_task = asyncio.current_task()
+                    print(f"Task {current_task.get_name()} was cancelled. {e}")
+                    # Task sleepy_task was cancelled. No more sleeping!
+
+
+            @app.before_server_start
+            async def before_start(app):
+                app.add_task(my_task, name="sleepy_task")
+                await asyncio.sleep(1)
+                await app.cancel_task("sleepy_task", msg="No more sleeping!")
+            ```
+        """  # noqa: E501
+        task = self.get_task(name, raise_exception=raise_exception)
+        if task and not task.cancelled():
+            if msg and sys.version_info < (3, 14):
+                task.cancel(msg)
+            else:
+                task.cancel()
+            try:
+                await task
+            except CancelledError:
+                ...
+
+    def purge_tasks(self) -> None:
+        """Purges completed and cancelled tasks from the task registry.
+
+        This method iterates through the task registry, identifying any tasks
+        that are either done or cancelled, and then removes those tasks,
+        leaving only the pending tasks in the registry.
+        """
+        for key, task in self._task_registry.items():
+            if task is None:
+                continue
+            if task.done() or task.cancelled():
+                self._task_registry[key] = None
+
+        self._task_registry = {
+            k: v for k, v in self._task_registry.items() if v is not None
+        }
+
+    def shutdown_tasks(
+        self, timeout: float | None = None, increment: float = 0.1
+    ) -> None:
+        """Cancel all tasks except the server task.
+
+        This method is used to cancel all tasks except the server task. It
+        iterates through the task registry, cancelling all tasks except the
+        server task, and then waits for the tasks to complete. Optionally, you
+        can provide a timeout and an increment to control how long the method
+        will wait for the tasks to complete.
+
+        Args:
+            timeout (Optional[float]): The amount of time to wait for the tasks
+                to complete. Defaults to `None`.
+            increment (float): The amount of time to wait between checks for
+                whether the tasks have completed. Defaults to `0.1`.
+        """
+        for task in self.tasks:
+            if task.get_name() != "RunServer":
+                task.cancel()
+
+        if timeout is None:
+            timeout = self.config.GRACEFUL_SHUTDOWN_TIMEOUT
+
+        while len(self._task_registry) and timeout:
+            with suppress(RuntimeError):
+                running_loop = get_running_loop()
+                running_loop.run_until_complete(asyncio.sleep(increment))
+            self.purge_tasks()
+            timeout -= increment
+
+    @property
+    def tasks(self) -> Iterable[Task[Any]]:
+        """The tasks that are currently registered with the application.
+
+        Returns:
+            Iterable[Task[Any]]: The tasks that are currently registered with
+                the application.
+        """
+        return (
+            task
+            for task in iter(self._task_registry.values())
+            if task is not None
+        )
+
+    # -------------------------------------------------------------------- #
+    # ASGI
+    # -------------------------------------------------------------------- #
+
+    async def __call__(self, scope, receive, send):
+        """
+        To be ASGI compliant, our instance must be a callable that accepts
+        three arguments: scope, receive, send. See the ASGI reference for more
+        details: https://asgi.readthedocs.io/en/latest
+        """
+        if scope["type"] == "lifespan":
+            setup_logging(
+                self.state.is_debug,
+                self.config.NO_COLOR,
+                self.config.LOG_EXTRA,
+            )
+            self.asgi = True
+            self.motd("")
+            self._asgi_lifespan = Lifespan(self, scope, receive, send)
+            await self._asgi_lifespan()
+        else:
+            self._asgi_app = await ASGIApp.create(self, scope, receive, send)
+            await self._asgi_app()
+
+    _asgi_single_callable = True  # We conform to ASGI 3.0 single-callable
+
+    # -------------------------------------------------------------------- #
+    # Configuration
+    # -------------------------------------------------------------------- #
+
+    def update_config(self, config: bytes | str | dict | Any) -> None:
+        """Update the application configuration.
+
+        This method is used to update the application configuration. It can
+        accept a configuration object, a dictionary, or a path to a file that
+        contains a configuration object or dictionary.
+
+        See [Configuration](/en/guide/deployment/configuration) for details.
+
+        Args:
+            config (Union[bytes, str, dict, Any]): The configuration object,
+                dictionary, or path to a configuration file.
+        """
+
+        self.config.update_config(config)
+
+    @property
+    def asgi(self) -> bool:
+        """Whether the app is running in ASGI mode."""
+        return self.state.asgi
+
+    @asgi.setter
+    def asgi(self, value: bool):
+        self.state.asgi = value
+
+    @property
+    def debug(self) -> bool:
+        """Whether the app is running in debug mode."""
+        return self.state.is_debug
+
+    @property
+    def auto_reload(self) -> bool:
+        """Whether the app is running in auto-reload mode."""
+        return self.config.AUTO_RELOAD
+
+    @auto_reload.setter
+    def auto_reload(self, value: bool):
+        self.config.AUTO_RELOAD = value
+        self.state.auto_reload = value
+
+    @property
+    def state(self) -> ApplicationState:  # type: ignore
+        """The application state.
+
+        Returns:
+            ApplicationState: The current state of the application.
+        """
+        return self._state
+
+    @property
+    def reload_dirs(self) -> set[Path]:
+        """The directories that are monitored for auto-reload.
+
+        Returns:
+            Set[str]: The set of directories that are monitored for
+                auto-reload.
+        """
+        return self.state.reload_dirs
+
+    # -------------------------------------------------------------------- #
+    # Sanic Extensions
+    # -------------------------------------------------------------------- #
+
+    @property
+    def ext(self) -> Extend:
+        """Convenience property for accessing Sanic Extensions.
+
+        This property is available if the `sanic-ext` package is installed.
+
+        See [Sanic Extensions](/en/plugins/sanic-ext/getting-started)
+            for details.
+
+        Returns:
+            Extend: The Sanic Extensions instance.
+
+        Examples:
+            A typical use case might be for registering a dependency injection.
+            ```python
+            app.ext.dependency(SomeObject())
+            ```
+        """
+        if not hasattr(self, "_ext"):
+            setup_ext(self, fail=True)
+
+        if not hasattr(self, "_ext"):
+            raise RuntimeError(
+                "Sanic Extensions is not installed. You can add it to your "
+                "environment using:\n$ pip install sanic[ext]\nor\n$ pip "
+                "install sanic-ext"
+            )
+        return self._ext  # type: ignore
+
+    def extend(
+        self,
+        *,
+        extensions: list[type[Extension]] | None = None,
+        built_in_extensions: bool = True,
+        config: Config | dict[str, Any] | None = None,
+        **kwargs,
+    ) -> Extend:
+        """Extend Sanic with additional functionality using Sanic Extensions.
+
+        This method enables you to add one or more Sanic Extensions to the
+        current Sanic instance. It allows for more control over the Extend
+        object, such as enabling or disabling built-in extensions or providing
+        custom configuration.
+
+        See [Sanic Extensions](/en/plugins/sanic-ext/getting-started)
+            for details.
+
+        Args:
+            extensions (Optional[List[Type[Extension]]], optional): A list of
+                extensions to add. Defaults to `None`, meaning only built-in
+                extensions are added.
+            built_in_extensions (bool, optional): Whether to enable built-in
+                extensions. Defaults to `True`.
+            config (Optional[Union[Config, Dict[str, Any]]], optional):
+                Optional custom configuration for the extensions. Defaults
+                to `None`.
+            **kwargs: Additional keyword arguments that might be needed by
+                specific extensions.
+
+        Returns:
+            Extend: The Sanic Extensions instance.
+
+        Raises:
+            RuntimeError: If an attempt is made to extend Sanic after Sanic
+                Extensions has already been set up.
+
+        Examples:
+            A typical use case might be to add a custom extension along with
+                built-in ones.
+            ```python
+            app.extend(
+                extensions=[MyCustomExtension],
+                built_in_extensions=True
+            )
+            ```
+        """
+        if hasattr(self, "_ext"):
+            raise RuntimeError(
+                "Cannot extend Sanic after Sanic Extensions has been setup."
+            )
+        setup_ext(
+            self,
+            extensions=extensions,
+            built_in_extensions=built_in_extensions,
+            config=config,
+            fail=True,
+            **kwargs,
+        )
+        return self.ext
+
+    # -------------------------------------------------------------------- #
+    # Class methods
+    # -------------------------------------------------------------------- #
+
+    @classmethod
+    def register_app(cls, app: Sanic) -> None:
+        """Register a Sanic instance with the class registry.
+
+        This method adds a Sanic application instance to the class registry,
+        which is used for tracking all instances of the application. It is
+        usually used internally, but can be used to register an application
+        that may have otherwise been created outside of the class registry.
+
+        Args:
+            app (Sanic): The Sanic instance to be registered.
+
+        Raises:
+            SanicException: If the app is not an instance of Sanic or if the
+                name of the app is already in use (unless in test mode).
+
+        Examples:
+            ```python
+            Sanic.register_app(my_app)
+            ```
+        """
+        if not isinstance(app, cls):
+            raise SanicException("Registered app must be an instance of Sanic")
+
+        name = app.name
+        if name in cls._app_registry and not cls.test_mode:
+            raise SanicException(f'Sanic app name "{name}" already in use.')
+
+        cls._app_registry[name] = app
+
+    @classmethod
+    def unregister_app(cls, app: Sanic) -> None:
+        """Unregister a Sanic instance from the class registry.
+
+        This method removes a previously registered Sanic application instance
+        from the class registry. This can be useful for cleanup purposes,
+        especially in testing or when an app instance is no longer needed. But,
+        it is typically used internally and should not be needed in most cases.
+
+        Args:
+            app (Sanic): The Sanic instance to be unregistered.
+
+        Raises:
+            SanicException: If the app is not an instance of Sanic.
+
+        Examples:
+            ```python
+            Sanic.unregister_app(my_app)
+            ```
+        """
+        if not isinstance(app, cls):
+            raise SanicException("Registered app must be an instance of Sanic")
+
+        name = app.name
+        if name in cls._app_registry:
+            del cls._app_registry[name]
+
+    @classmethod
+    def get_app(
+        cls, name: str | None = None, *, force_create: bool = False
+    ) -> Sanic:
+        """Retrieve an instantiated Sanic instance by name.
+
+        This method is best used when needing to get access to an already
+        defined application instance in another part of an app.
+
+        .. warning::
+            Be careful when using this method in the global scope as it is
+            possible that the import path running will cause it to error if
+            the imported global scope runs before the application instance
+            is created.
+
+            It is typically best used in a function or method that is called
+            after the application instance has been created.
+
+            ```python
+            def setup_routes():
+                app = Sanic.get_app()
+                app.add_route(handler_1, '/route1')
+                app.add_route(handler_2, '/route2')
+            ```
+
+        Args:
+            name (Optional[str], optional): Name of the application instance
+                to retrieve. When not specified, it will return the only
+                application instance if there is only one. If not specified
+                and there are multiple application instances, it will raise
+                an exception. Defaults to `None`.
+            force_create (bool, optional): If `True` and the named app does
+                not exist, a new instance will be created. Defaults to `False`.
+
+        Returns:
+            Sanic: The requested Sanic app instance.
+
+        Raises:
+            SanicException: If there are multiple or no Sanic apps found, or
+                if the specified name is not found.
+
+
+        Example:
+            ```python
+            app1 = Sanic("app1")
+            app2 = Sanic.get_app("app1")  # app2 is the same instance as app1
+            ```
+        """
+        if name is None:
+            if len(cls._app_registry) > 1:
+                raise SanicException(
+                    'Multiple Sanic apps found, use Sanic.get_app("app_name")'
+                )
+            elif len(cls._app_registry) == 0:
+                raise SanicException("No Sanic apps have been registered.")
+            else:
+                return list(cls._app_registry.values())[0]
+        try:
+            return cls._app_registry[name]
+        except KeyError:
+            if name == "__main__":
+                return cls.get_app("__mp_main__", force_create=force_create)
+            if force_create:
+                return cls(name)
+            raise SanicException(
+                f"Sanic app name '{name}' not found.\n"
+                "App instantiation must occur outside "
+                "if __name__ == '__main__' "
+                "block or by using an AppLoader.\nSee "
+                "https://sanic.dev/en/guide/deployment/app-loader.html"
+                " for more details."
+            )
+
+    @classmethod
+    def _check_uvloop_conflict(cls) -> None:
+        values = {app.config.USE_UVLOOP for app in cls._app_registry.values()}
+        if len(values) > 1:
+            error_logger.warning(
+                "It looks like you're running several apps with different "
+                "uvloop settings. This is not supported and may lead to "
+                "unintended behaviour."
+            )
+
+    # -------------------------------------------------------------------- #
+    # Lifecycle
+    # -------------------------------------------------------------------- #
+
+    @contextmanager
+    def amend(self) -> Iterator[None]:
+        """Context manager to allow changes to the app after it has started.
+
+        Typically, once an application has started and is running, you cannot
+        make certain changes, like adding routes, middleware, or signals. This
+        context manager allows you to make those changes, and then finalizes
+        the app again when the context manager exits.
+
+        Yields:
+            None
+
+        Example:
+            ```python
+            with app.amend():
+                app.add_route(handler, '/new_route')
+            ```
+        """
+        if not self.state.is_started:
+            yield
+        else:
+            do_router = self.router.finalized
+            do_signal_router = self.signal_router.finalized
+            if do_router:
+                self.router.reset()
+            if do_signal_router:
+                self.signal_router.reset()
+            yield
+            if do_signal_router:
+                self.signalize(cast(bool, self.config.TOUCHUP))
+            if do_router:
+                self.finalize()
+
+    def finalize(self) -> None:
+        """Finalize the routing configuration for the Sanic application.
+
+        This method completes the routing setup by calling the router's
+        finalize method, and it also finalizes any middleware that has been
+        added to the application. If the application is not in test mode,
+        any finalization errors will be raised.
+
+        Finalization consists of identifying defined routes and optimizing
+        Sanic's performance to meet the application's specific needs. If
+        you are manually adding routes, after Sanic has started, you will
+        typically want to use the  `amend` context manager rather than
+        calling this method directly.
+
+        .. note::
+            This method is usually called internally during the server setup
+            process and does not typically need to be invoked manually.
+
+        Raises:
+            FinalizationError: If there is an error during the finalization
+                process, and the application is not in test mode.
+
+        Example:
+            ```python
+            app.finalize()
+            ```
+        """
+        try:
+            self.router.finalize()
+        except FinalizationError as e:
+            if not Sanic.test_mode:
+                raise e
+        self.finalize_middleware()
+
+    def signalize(self, allow_fail_builtin: bool = True) -> None:
+        """Finalize the signal handling configuration for the Sanic application.
+
+        This method completes the signal handling setup by calling the signal
+        router's finalize method. If the application is not in test mode,
+        any finalization errors will be raised.
+
+        Finalization consists of identifying defined signaliz and optimizing
+        Sanic's performance to meet the application's specific needs. If
+        you are manually adding signals, after Sanic has started, you will
+        typically want to use the  `amend` context manager rather than
+        calling this method directly.
+
+        .. note::
+            This method is usually called internally during the server setup
+            process and does not typically need to be invoked manually.
+
+        Args:
+            allow_fail_builtin (bool, optional): If set to `True`, will allow
+                built-in signals to fail during the finalization process.
+                Defaults to `True`.
+
+        Raises:
+            FinalizationError: If there is an error during the signal
+                finalization process, and the application is not in test mode.
+
+        Example:
+            ```python
+            app.signalize(allow_fail_builtin=False)
+            ```
+        """  # noqa: E501
+        self.signal_router.allow_fail_builtin = allow_fail_builtin
+        try:
+            self.signal_router.finalize()
+        except FinalizationError as e:
+            if not Sanic.test_mode:
+                raise e
+
+    async def _startup(self):
+        self._future_registry.clear()
+
+        if not hasattr(self, "_ext"):
+            setup_ext(self)
+        if hasattr(self, "_ext"):
+            self.ext._display()
+
+        if self.state.is_debug and self.config.TOUCHUP is not True:
+            self.config.TOUCHUP = False
+        elif isinstance(self.config.TOUCHUP, Default):
+            self.config.TOUCHUP = True
+
+        # Setup routers
+        self.signalize(self.config.TOUCHUP)
+        self.finalize()
+
+        route_names = [route.extra.ident for route in self.router.routes]
+        duplicates = {
+            name for name in route_names if route_names.count(name) > 1
+        }
+        if duplicates:
+            names = ", ".join(duplicates)
+            message = (
+                f"Duplicate route names detected: {names}. You should rename "
+                "one or more of them explicitly by using the `name` param, "
+                "or changing the implicit name derived from the class and "
+                "function name. For more details, please see "
+                "https://sanic.dev/en/guide/release-notes/v23.3.html#duplicated-route-names-are-no-longer-allowed"  # noqa
+            )
+            raise ServerError(message)
+
+        Sanic._check_uvloop_conflict()
+
+        # Startup time optimizations
+        if self.state.primary:
+            # TODO:
+            # - Raise warning if secondary apps have error handler config
+            if self.config.TOUCHUP:
+                TouchUp.run(self)
+
+        self.state.is_started = True
+
+    def ack(self) -> None:
+        """Shorthand to send an ack message to the Server Manager.
+
+        In general, this should usually not need to be called manually.
+        It is used to tell the Manager that a process is operational and
+        ready to begin operation.
+        """
+        if hasattr(self, "multiplexer"):
+            self.multiplexer.ack()
+
+    def set_serving(self, serving: bool) -> None:
+        """Set the serving state of the application.
+
+        This method is used to set the serving state of the application.
+        It is used internally by Sanic and should not typically be called
+        manually.
+
+        Args:
+            serving (bool): Whether the application is serving.
+        """
+        self.state.is_running = serving
+        if hasattr(self, "multiplexer"):
+            self.multiplexer.set_serving(serving)
+
+    async def _server_event(
+        self,
+        concern: str,
+        action: str,
+        loop: AbstractEventLoop | None = None,
+    ) -> None:
+        event = f"server.{concern}.{action}"
+        if action not in ("before", "after") or concern not in (
+            "init",
+            "shutdown",
+        ):
+            raise SanicException(f"Invalid server event: {event}")
+        logger.debug(
+            f"Triggering server events: {event}", extra={"verbosity": 1}
+        )
+        reverse = concern == "shutdown"
+        if loop is None:
+            loop = self.loop
+        await self.dispatch(
+            event,
+            fail_not_found=False,
+            reverse=reverse,
+            inline=True,
+            context={
+                "app": self,
+                "loop": loop,
+            },
+        )
+
+    # -------------------------------------------------------------------- #
+    # Process Management
+    # -------------------------------------------------------------------- #
+
+    def refresh(
+        self,
+        passthru: dict[str, Any] | None = None,
+    ) -> Sanic:
+        """Refresh the application instance. **This is used internally by Sanic**.
+
+        .. warning::
+            This method is intended for internal use only and should not be
+            called directly.
+
+        Args:
+            passthru (Optional[Dict[str, Any]], optional): Optional dictionary
+                of attributes to pass through to the new instance. Defaults to
+                `None`.
+
+        Returns:
+            Sanic: The refreshed application instance.
+        """  # noqa: E501
+        registered = self.__class__.get_app(self.name)
+        if self is not registered:
+            if not registered.state.server_info:
+                registered.state.server_info = self.state.server_info
+            self = registered
+        if passthru:
+            for attr, info in passthru.items():
+                if isinstance(info, dict):
+                    for key, value in info.items():
+                        setattr(getattr(self, attr), key, value)
+                else:
+                    setattr(self, attr, info)
+        if hasattr(self, "multiplexer"):
+            self.shared_ctx.lock()
+        return self
+
+    @property
+    def inspector(self) -> Inspector:
+        """An instance of Inspector for accessing the application's state.
+
+        This can only be accessed from a worker process, and only if the
+        inspector has been enabled.
+
+        See [Inspector](/en/guide/deployment/inspector) for details.
+
+        Returns:
+            Inspector: An instance of Inspector.
+        """
+        if environ.get("SANIC_WORKER_PROCESS") or not self._inspector:
+            raise SanicException(
+                "Can only access the inspector from the main process "
+                "after main_process_start has run. For example, you most "
+                "likely want to use it inside the @app.main_process_ready "
+                "event listener."
+            )
+        return self._inspector
+
+    @property
+    def manager(self) -> WorkerManager:
+        """
+        Property to access the WorkerManager instance.
+
+        This property provides access to the WorkerManager object controlling
+        the worker processes. It can only be accessed from the main process.
+
+        .. note::
+            Make sure to only access this property from the main process,
+            as attempting to do so from a worker process will result
+            in an exception.
+
+        See [WorkerManager](/en/guide/deployment/manager) for details.
+
+        Returns:
+            WorkerManager: The manager responsible for managing
+                worker processes.
+
+        Raises:
+            SanicException: If an attempt is made to access the manager
+                from a worker process or if the manager is not initialized.
+
+        Example:
+            ```python
+            app.manager.manage(...)
+            ```
+        """
+
+        if environ.get("SANIC_WORKER_PROCESS") or not self._manager:
+            raise SanicException(
+                "Can only access the manager from the main process "
+                "after main_process_start has run. For example, you most "
+                "likely want to use it inside the @app.main_process_ready "
+                "event listener."
+            )
+        return self._manager
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/__init__.py b/.venv/lib/python3.12/site-packages/sanic/application/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/constants.py b/.venv/lib/python3.12/site-packages/sanic/application/constants.py
new file mode 100644
index 0000000..5b27486
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/constants.py
@@ -0,0 +1,38 @@
+from enum import Enum, IntEnum, auto
+
+
+class StrEnum(str, Enum):  # no cov
+    def _generate_next_value_(name: str, *args) -> str:  # type: ignore
+        return name.lower()
+
+    def __eq__(self, value: object) -> bool:
+        value = str(value).upper()
+        return super().__eq__(value)
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+    def __str__(self) -> str:
+        return self.value
+
+
+class Server(StrEnum):
+    """Server types."""
+
+    SANIC = auto()
+    ASGI = auto()
+
+
+class Mode(StrEnum):
+    """Server modes."""
+
+    PRODUCTION = auto()
+    DEBUG = auto()
+
+
+class ServerStage(IntEnum):
+    """Server stages."""
+
+    STOPPED = auto()
+    PARTIAL = auto()
+    SERVING = auto()
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/ext.py b/.venv/lib/python3.12/site-packages/sanic/application/ext.py
new file mode 100644
index 0000000..86b482a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/ext.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+from contextlib import suppress
+from importlib import import_module
+from typing import TYPE_CHECKING
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+def setup_ext(app: Sanic, *, fail: bool = False, **kwargs):
+    """Setup Sanic Extensions.
+
+    Requires Sanic Extensions to be installed.
+
+    Args:
+        app (Sanic): Sanic application.
+        fail (bool, optional): Raise an error if Sanic Extensions is not
+            installed. Defaults to `False`.
+        **kwargs: Keyword arguments to pass to `sanic_ext.Extend`.
+
+    Returns:
+        sanic_ext.Extend: Sanic Extensions instance.
+    """
+
+    if not app.config.AUTO_EXTEND:
+        return
+
+    sanic_ext = None
+    with suppress(ModuleNotFoundError):
+        sanic_ext = import_module("sanic_ext")
+
+    if not sanic_ext:  # no cov
+        if fail:
+            raise RuntimeError(
+                "Sanic Extensions is not installed. You can add it to your "
+                "environment using:\n$ pip install sanic[ext]\nor\n$ pip "
+                "install sanic-ext"
+            )
+
+        return
+
+    if not getattr(app, "_ext", None):
+        Ext = getattr(sanic_ext, "Extend")
+        app._ext = Ext(app, **kwargs)
+
+        return app.ext
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/logo.py b/.venv/lib/python3.12/site-packages/sanic/application/logo.py
new file mode 100644
index 0000000..0a2f56a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/logo.py
@@ -0,0 +1,72 @@
+import re
+import sys
+
+from os import environ
+
+from sanic.helpers import is_atty
+
+
+BASE_LOGO = """
+
+                 Sanic
+         Build Fast. Run Fast.
+
+"""
+COFFEE_LOGO = """\033[48;2;255;13;104m                     \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m     ▄████████▄      \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m    ██       ██▀▀▄   \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m    ███████████  █   \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m    ███████████▄▄▀   \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m     ▀███████▀       \033[0m
+\033[48;2;255;13;104m                     \033[0m
+Dark roast. No sugar."""
+
+COLOR_LOGO = """\033[48;2;255;13;104m                     \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m    ▄███ █████ ██    \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m   ██                \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m    ▀███████ ███▄    \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m                ██   \033[0m
+\033[38;2;255;255;255;48;2;255;13;104m   ████ ████████▀    \033[0m
+\033[48;2;255;13;104m                     \033[0m
+Build Fast. Run Fast."""
+
+FULL_COLOR_LOGO = """
+
+\033[38;2;255;13;104m  ▄███ █████ ██ \033[0m     ▄█▄      ██       █   █   ▄██████████
+\033[38;2;255;13;104m ██             \033[0m    █   █     █ ██     █   █  ██
+\033[38;2;255;13;104m  ▀███████ ███▄ \033[0m   ▀     █    █   ██   ▄   █  ██
+\033[38;2;255;13;104m              ██\033[0m  █████████   █     ██ █   █  ▄▄
+\033[38;2;255;13;104m ████ ████████▀ \033[0m █         █  █       ██   █   ▀██ ███████
+
+"""  # noqa
+
+SVG_LOGO_SIMPLE = """Sanic"""  # noqa
+
+ansi_pattern = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
+
+
+def get_logo(full: bool = False, coffee: bool = False) -> str:
+    """Get the Sanic logo.
+
+    Will return the full color logo if the terminal supports it.
+
+    Args:
+        full (bool, optional): Use the full color logo. Defaults to `False`.
+        coffee (bool, optional): Use the coffee logo. Defaults to `False`.
+
+    Returns:
+        str: Sanic logo.
+    """
+    logo = (
+        (FULL_COLOR_LOGO if full else (COFFEE_LOGO if coffee else COLOR_LOGO))
+        if is_atty()
+        else BASE_LOGO
+    )
+
+    if (
+        sys.platform == "darwin"
+        and environ.get("TERM_PROGRAM") == "Apple_Terminal"
+    ):
+        logo = ansi_pattern.sub("", logo)
+
+    return logo
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/motd.py b/.venv/lib/python3.12/site-packages/sanic/application/motd.py
new file mode 100644
index 0000000..bfe4f95
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/motd.py
@@ -0,0 +1,179 @@
+from abc import ABC, abstractmethod
+from shutil import get_terminal_size
+from textwrap import indent, wrap
+
+from sanic import __version__
+from sanic.helpers import is_atty
+from sanic.log import logger
+
+
+class MOTD(ABC):
+    """Base class for the Message of the Day (MOTD) display."""
+
+    def __init__(
+        self,
+        logo: str | None,
+        serve_location: str,
+        data: dict[str, str],
+        extra: dict[str, str],
+    ) -> None:
+        self.logo = logo
+        self.serve_location = serve_location
+        self.data = data
+        self.extra = extra
+        self.key_width = 0
+        self.value_width = 0
+
+    @abstractmethod
+    def display(self):
+        """Display the MOTD."""
+
+    @classmethod
+    def output(
+        cls,
+        logo: str | None,
+        serve_location: str,
+        data: dict[str, str],
+        extra: dict[str, str],
+    ) -> None:
+        """Output the MOTD.
+
+        Args:
+            logo (Optional[str]): Logo to display.
+            serve_location (str): Location to serve.
+            data (Dict[str, str]): Data to display.
+            extra (Dict[str, str]): Extra data to display.
+        """
+        motd_class = MOTDTTY if is_atty() else MOTDBasic
+        motd_class(logo, serve_location, data, extra).display()
+
+
+class MOTDBasic(MOTD):
+    """A basic MOTD display.
+
+    This is used when the terminal does not support ANSI escape codes.
+    """
+
+    def display(self):
+        if self.logo:
+            logger.debug(self.logo)
+        lines = [f"Sanic v{__version__}"]
+        if self.serve_location:
+            lines.append(f"Goin' Fast @ {self.serve_location}")
+        lines += [
+            *(f"{key}: {value}" for key, value in self.data.items()),
+            *(f"{key}: {value}" for key, value in self.extra.items()),
+        ]
+        for line in lines:
+            logger.info(line)
+
+
+class MOTDTTY(MOTD):
+    """A MOTD display for terminals that support ANSI escape codes."""
+
+    def __init__(self, *args, **kwargs) -> None:
+        super().__init__(*args, **kwargs)
+        self.set_variables()
+
+    def set_variables(self):  # no  cov
+        """Set the variables used for display."""
+        fallback = (108, 24)
+        terminal_width = max(
+            get_terminal_size(fallback=fallback).columns, fallback[0]
+        )
+        self.max_value_width = terminal_width - fallback[0] + 36
+
+        self.key_width = 4
+        self.value_width = self.max_value_width
+        if self.data:
+            self.key_width = max(map(len, self.data.keys()))
+            self.value_width = min(
+                max(map(len, self.data.values())), self.max_value_width
+            )
+        if self.extra:
+            self.key_width = max(
+                self.key_width, max(map(len, self.extra.keys()))
+            )
+            self.value_width = min(
+                max((*map(len, self.extra.values()), self.value_width)),
+                self.max_value_width,
+            )
+        self.logo_lines = self.logo.split("\n") if self.logo else []
+        self.logo_line_length = 24
+        self.centering_length = (
+            self.key_width + self.value_width + 2 + self.logo_line_length
+        )
+        self.display_length = self.key_width + self.value_width + 2
+
+    def display(self, version=True, action="Goin' Fast", out=None):
+        """Display the MOTD.
+
+        Args:
+            version (bool, optional): Display the version. Defaults to `True`.
+            action (str, optional): Action to display. Defaults to
+                `"Goin' Fast"`.
+            out (Optional[Callable], optional): Output function. Defaults to
+                `None`.
+        """
+        if not out:
+            out = logger.info
+        header = "Sanic"
+        if version:
+            header += f" v{__version__}"
+        header = header.center(self.centering_length)
+        running = (
+            f"{action} @ {self.serve_location}" if self.serve_location else ""
+        ).center(self.centering_length)
+        length = len(header) + 2 - self.logo_line_length
+        first_filler = "─" * (self.logo_line_length - 1)
+        second_filler = "─" * length
+        display_filler = "─" * (self.display_length + 2)
+        lines = [
+            f"\n┌{first_filler}─{second_filler}┐",
+            f"│ {header} │",
+            f"│ {running} │",
+            f"├{first_filler}┬{second_filler}┤",
+        ]
+
+        self._render_data(lines, self.data, 0)
+        if self.extra:
+            logo_part = self._get_logo_part(len(lines) - 4)
+            lines.append(f"│ {logo_part} ├{display_filler}┤")
+            self._render_data(lines, self.extra, len(lines) - 4)
+
+        self._render_fill(lines)
+
+        lines.append(f"└{first_filler}┴{second_filler}┘\n")
+        out(indent("\n".join(lines), "  "))
+
+    def _render_data(self, lines, data, start):
+        offset = 0
+        for idx, (key, value) in enumerate(data.items(), start=start):
+            key = key.rjust(self.key_width)
+
+            wrapped = wrap(value, self.max_value_width, break_on_hyphens=False)
+            for wrap_index, part in enumerate(wrapped):
+                part = part.ljust(self.value_width)
+                logo_part = self._get_logo_part(idx + offset + wrap_index)
+                display = (
+                    f"{key}: {part}"
+                    if wrap_index == 0
+                    else (" " * len(key) + f"  {part}")
+                )
+                lines.append(f"│ {logo_part} │ {display} │")
+                if wrap_index:
+                    offset += 1
+
+    def _render_fill(self, lines):
+        filler = " " * self.display_length
+        idx = len(lines) - 5
+        for i in range(1, len(self.logo_lines) - idx):
+            logo_part = self.logo_lines[idx + i]
+            lines.append(f"│ {logo_part} │ {filler} │")
+
+    def _get_logo_part(self, idx):
+        try:
+            logo_part = self.logo_lines[idx]
+        except IndexError:
+            logo_part = " " * (self.logo_line_length - 3)
+        return logo_part
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/spinner.py b/.venv/lib/python3.12/site-packages/sanic/application/spinner.py
new file mode 100644
index 0000000..ab59f92
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/spinner.py
@@ -0,0 +1,90 @@
+import os
+import sys
+import time
+
+from contextlib import contextmanager
+from queue import Queue
+from threading import Thread
+
+
+if os.name == "nt":  # noqa
+    import ctypes  # noqa
+
+    class _CursorInfo(ctypes.Structure):
+        _fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
+
+
+class Spinner:  # noqa
+    """Spinner class to show a loading spinner in the terminal.
+
+    Used internally by the `loading` context manager.
+    """
+
+    def __init__(self, message: str) -> None:
+        self.message = message
+        self.queue: Queue[int] = Queue()
+        self.spinner = self.cursor()
+        self.thread = Thread(target=self.run)
+
+    def start(self):
+        self.queue.put(1)
+        self.thread.start()
+        self.hide()
+
+    def run(self):
+        while self.queue.get():
+            output = f"\r{self.message} [{next(self.spinner)}]"
+            sys.stdout.write(output)
+            sys.stdout.flush()
+            time.sleep(0.1)
+            self.queue.put(1)
+
+    def stop(self):
+        self.queue.put(0)
+        self.thread.join()
+        self.show()
+
+    @staticmethod
+    def cursor():
+        while True:
+            yield from "|/-\\"
+
+    @staticmethod
+    def hide():
+        if os.name == "nt":
+            ci = _CursorInfo()
+            handle = ctypes.windll.kernel32.GetStdHandle(-11)
+            ctypes.windll.kernel32.GetConsoleCursorInfo(
+                handle, ctypes.byref(ci)
+            )
+            ci.visible = False
+            ctypes.windll.kernel32.SetConsoleCursorInfo(
+                handle, ctypes.byref(ci)
+            )
+        elif os.name == "posix":
+            sys.stdout.write("\033[?25l")
+            sys.stdout.flush()
+
+    @staticmethod
+    def show():
+        if os.name == "nt":
+            ci = _CursorInfo()
+            handle = ctypes.windll.kernel32.GetStdHandle(-11)
+            ctypes.windll.kernel32.GetConsoleCursorInfo(
+                handle, ctypes.byref(ci)
+            )
+            ci.visible = True
+            ctypes.windll.kernel32.SetConsoleCursorInfo(
+                handle, ctypes.byref(ci)
+            )
+        elif os.name == "posix":
+            sys.stdout.write("\033[?25h")
+            sys.stdout.flush()
+
+
+@contextmanager
+def loading(message: str = "Loading"):  # noqa
+    spinner = Spinner(message)
+    spinner.start()
+    yield
+    spinner.stop()
diff --git a/.venv/lib/python3.12/site-packages/sanic/application/state.py b/.venv/lib/python3.12/site-packages/sanic/application/state.py
new file mode 100644
index 0000000..627550e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/application/state.py
@@ -0,0 +1,115 @@
+from __future__ import annotations
+
+import logging
+
+from dataclasses import dataclass, field
+from pathlib import Path
+from socket import socket
+from ssl import SSLContext
+from typing import TYPE_CHECKING, Any
+
+from sanic.application.constants import Mode, Server, ServerStage
+from sanic.log import VerbosityFilter, logger
+from sanic.server.async_server import AsyncioServer
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+@dataclass
+class ApplicationServerInfo:
+    """Information about a server instance."""
+
+    settings: dict[str, Any]
+    stage: ServerStage = field(default=ServerStage.STOPPED)
+    server: AsyncioServer | None = field(default=None)
+
+
+@dataclass
+class ApplicationState:
+    """Application state.
+
+    This class is used to store the state of the application. It is
+    instantiated by the application and is available as `app.state`.
+    """
+
+    app: Sanic
+    asgi: bool = field(default=False)
+    coffee: bool = field(default=False)
+    fast: bool = field(default=False)
+    host: str = field(default="")
+    port: int = field(default=0)
+    ssl: SSLContext | None = field(default=None)
+    sock: socket | None = field(default=None)
+    unix: str | None = field(default=None)
+    mode: Mode = field(default=Mode.PRODUCTION)
+    reload_dirs: set[Path] = field(default_factory=set)
+    auto_reload: bool = field(default=False)
+    server: Server = field(default=Server.SANIC)
+    is_running: bool = field(default=False)
+    is_started: bool = field(default=False)
+    is_stopping: bool = field(default=False)
+    verbosity: int = field(default=0)
+    workers: int = field(default=0)
+    primary: bool = field(default=True)
+    server_info: list[ApplicationServerInfo] = field(default_factory=list)
+
+    # This property relates to the ApplicationState instance and should
+    # not be changed except in the __post_init__ method
+    _init: bool = field(default=False)
+
+    def __post_init__(self) -> None:
+        self._init = True
+
+    def __setattr__(self, name: str, value: Any) -> None:
+        if self._init and name == "_init":
+            raise RuntimeError(
+                "Cannot change the value of _init after instantiation"
+            )
+        super().__setattr__(name, value)
+        if self._init and hasattr(self, f"set_{name}"):
+            getattr(self, f"set_{name}")(value)
+
+    def set_mode(self, value: str | Mode):
+        if hasattr(self.app, "error_handler"):
+            self.app.error_handler.debug = self.app.debug
+        if getattr(self.app, "configure_logging", False) and self.app.debug:
+            logger.setLevel(logging.DEBUG)
+
+    def set_verbosity(self, value: int) -> None:
+        """Set the verbosity level.
+
+        Args:
+            value (int): Verbosity level.
+        """
+        VerbosityFilter.verbosity = value
+
+    @property
+    def is_debug(self) -> bool:
+        """Check if the application is in debug mode.
+
+        Returns:
+            bool: `True` if the application is in debug mode, `False`
+                otherwise.
+        """
+        return self.mode is Mode.DEBUG
+
+    @property
+    def stage(self) -> ServerStage:
+        """Get the server stage.
+
+        Returns:
+            ServerStage: Server stage.
+        """
+        if not self.server_info:
+            return ServerStage.STOPPED
+
+        if all(info.stage is ServerStage.SERVING for info in self.server_info):
+            return ServerStage.SERVING
+        elif any(
+            info.stage is ServerStage.SERVING for info in self.server_info
+        ):
+            return ServerStage.PARTIAL
+
+        return ServerStage.STOPPED
diff --git a/.venv/lib/python3.12/site-packages/sanic/asgi.py b/.venv/lib/python3.12/site-packages/sanic/asgi.py
new file mode 100644
index 0000000..4a3b589
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/asgi.py
@@ -0,0 +1,263 @@
+from __future__ import annotations
+
+import warnings
+
+from typing import TYPE_CHECKING
+
+from sanic.compat import Header
+from sanic.exceptions import BadRequest, ServerError
+from sanic.helpers import Default
+from sanic.http import Stage
+from sanic.log import error_logger, logger
+from sanic.models.asgi import ASGIReceive, ASGIScope, ASGISend, MockTransport
+from sanic.request import Request
+from sanic.response import BaseHTTPResponse
+from sanic.server import ConnInfo
+from sanic.server.websockets.connection import WebSocketConnection
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+class Lifespan:
+    def __init__(
+        self, sanic_app, scope: ASGIScope, receive: ASGIReceive, send: ASGISend
+    ) -> None:
+        self.sanic_app = sanic_app
+        self.scope = scope
+        self.receive = receive
+        self.send = send
+
+        if "server.init.before" in self.sanic_app.signal_router.name_index:
+            logger.debug(
+                'You have set a listener for "before_server_start" '
+                "in ASGI mode. "
+                "It will be executed as early as possible, but not before "
+                "the ASGI server is started.",
+                extra={"verbosity": 1},
+            )
+        if "server.shutdown.after" in self.sanic_app.signal_router.name_index:
+            logger.debug(
+                'You have set a listener for "after_server_stop" '
+                "in ASGI mode. "
+                "It will be executed as late as possible, but not after "
+                "the ASGI server is stopped.",
+                extra={"verbosity": 1},
+            )
+
+    async def startup(self) -> None:
+        """
+        Gather the listeners to fire on server start.
+        Because we are using a third-party server and not Sanic server, we do
+        not have access to fire anything BEFORE the server starts.
+        Therefore, we fire before_server_start and after_server_start
+        in sequence since the ASGI lifespan protocol only supports a single
+        startup event.
+        """
+        await self.sanic_app._startup()
+        await self.sanic_app._server_event("init", "before")
+        await self.sanic_app._server_event("init", "after")
+
+        if not isinstance(self.sanic_app.config.USE_UVLOOP, Default):
+            warnings.warn(
+                "You have set the USE_UVLOOP configuration option, but Sanic "
+                "cannot control the event loop when running in ASGI mode."
+                "This option will be ignored."
+            )
+
+    async def shutdown(self) -> None:
+        """
+        Gather the listeners to fire on server stop.
+        Because we are using a third-party server and not Sanic server, we do
+        not have access to fire anything AFTER the server stops.
+        Therefore, we fire before_server_stop and after_server_stop
+        in sequence since the ASGI lifespan protocol only supports a single
+        shutdown event.
+        """
+        await self.sanic_app._server_event("shutdown", "before")
+        await self.sanic_app._server_event("shutdown", "after")
+
+    async def __call__(self) -> None:
+        while True:
+            message = await self.receive()
+            if message["type"] == "lifespan.startup":
+                try:
+                    await self.startup()
+                except Exception as e:
+                    error_logger.exception(e)
+                    await self.send(
+                        {"type": "lifespan.startup.failed", "message": str(e)}
+                    )
+                else:
+                    await self.send({"type": "lifespan.startup.complete"})
+            elif message["type"] == "lifespan.shutdown":
+                try:
+                    await self.shutdown()
+                except Exception as e:
+                    error_logger.exception(e)
+                    await self.send(
+                        {"type": "lifespan.shutdown.failed", "message": str(e)}
+                    )
+                else:
+                    await self.send({"type": "lifespan.shutdown.complete"})
+                return
+
+
+class ASGIApp:
+    sanic_app: Sanic
+    request: Request
+    transport: MockTransport
+    lifespan: Lifespan
+    ws: WebSocketConnection | None
+    stage: Stage
+    response: BaseHTTPResponse | None
+
+    @classmethod
+    async def create(
+        cls,
+        sanic_app: Sanic,
+        scope: ASGIScope,
+        receive: ASGIReceive,
+        send: ASGISend,
+    ) -> ASGIApp:
+        instance = cls()
+        instance.ws = None
+        instance.sanic_app = sanic_app
+        instance.transport = MockTransport(scope, receive, send)
+        instance.transport.loop = sanic_app.loop
+        instance.stage = Stage.IDLE
+        instance.response = None
+        instance.sanic_app.state.is_started = True
+        setattr(instance.transport, "add_task", sanic_app.loop.create_task)
+
+        try:
+            headers = Header(
+                [
+                    (
+                        key.decode("ASCII"),
+                        value.decode(errors="surrogateescape"),
+                    )
+                    for key, value in scope.get("headers", [])
+                ]
+            )
+        except UnicodeDecodeError:
+            raise BadRequest(
+                "Header names can only contain US-ASCII characters"
+            )
+
+        if scope["type"] == "http":
+            version = scope["http_version"]
+            method = scope["method"]
+        elif scope["type"] == "websocket":
+            version = "1.1"
+            method = "GET"
+
+            instance.ws = instance.transport.create_websocket_connection(
+                send, receive
+            )
+        else:
+            raise ServerError("Received unknown ASGI scope")
+
+        url_bytes, query = scope["raw_path"], scope["query_string"]
+        if query:
+            # httpx ASGI client sends query string as part of raw_path
+            url_bytes = url_bytes.split(b"?", 1)[0]
+            # All servers send them separately
+            url_bytes = b"%b?%b" % (url_bytes, query)
+
+        request_class = sanic_app.request_class or Request  # type: ignore
+        instance.request = request_class(
+            url_bytes,
+            headers,
+            version,
+            method,
+            instance.transport,
+            sanic_app,
+        )
+        request_class._current.set(instance.request)
+        instance.request.stream = instance  # type: ignore
+        instance.request_body = True
+        instance.request.conn_info = ConnInfo(instance.transport)
+
+        await instance.sanic_app.dispatch(
+            "http.lifecycle.request",
+            inline=True,
+            context={"request": instance.request},
+            fail_not_found=False,
+        )
+
+        return instance
+
+    async def read(self) -> bytes | None:
+        """
+        Read and stream the body in chunks from an incoming ASGI message.
+        """
+        if self.stage is Stage.IDLE:
+            self.stage = Stage.REQUEST
+        message = await self.transport.receive()
+        body = message.get("body", b"")
+        if not message.get("more_body", False):
+            self.request_body = False
+            if not body:
+                return None
+        return body
+
+    async def __aiter__(self):
+        while self.request_body:
+            data = await self.read()
+            if data:
+                yield data
+
+    def respond(self, response: BaseHTTPResponse):
+        if self.stage is not Stage.HANDLER:
+            self.stage = Stage.FAILED
+            raise RuntimeError("Response already started")
+        if self.response is not None:
+            self.response.stream = None
+        response.stream, self.response = self, response
+        return response
+
+    async def send(self, data, end_stream):
+        if self.stage is Stage.IDLE:
+            if not end_stream or data:
+                raise RuntimeError(
+                    "There is no request to respond to, either the "
+                    "response has already been sent or the "
+                    "request has not been received yet."
+                )
+            return
+        if self.response and self.stage is Stage.HANDLER:
+            await self.transport.send(
+                {
+                    "type": "http.response.start",
+                    "status": self.response.status,
+                    "headers": self.response.processed_headers,
+                }
+            )
+            response_body = getattr(self.response, "body", None)
+            if response_body:
+                data = response_body + data if data else response_body
+        self.stage = Stage.IDLE if end_stream else Stage.RESPONSE
+        await self.transport.send(
+            {
+                "type": "http.response.body",
+                "body": data.encode() if hasattr(data, "encode") else data,
+                "more_body": not end_stream,
+            }
+        )
+
+    _asgi_single_callable = True  # We conform to ASGI 3.0 single-callable
+
+    async def __call__(self) -> None:
+        """
+        Handle the incoming request.
+        """
+        try:
+            self.stage = Stage.HANDLER
+            await self.sanic_app.handle_request(self.request)
+        except Exception as e:
+            try:
+                await self.sanic_app.handle_exception(self.request, e)
+            except Exception as exc:
+                await self.sanic_app.handle_exception(self.request, exc, False)
diff --git a/.venv/lib/python3.12/site-packages/sanic/base/__init__.py b/.venv/lib/python3.12/site-packages/sanic/base/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/base/meta.py b/.venv/lib/python3.12/site-packages/sanic/base/meta.py
new file mode 100644
index 0000000..2c6870c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/base/meta.py
@@ -0,0 +1,6 @@
+class SanicMeta(type):
+    @classmethod
+    def __prepare__(metaclass, name, bases, **kwds):
+        cls = super().__prepare__(metaclass, name, bases, **kwds)
+        cls["__slots__"] = ()
+        return cls
diff --git a/.venv/lib/python3.12/site-packages/sanic/base/root.py b/.venv/lib/python3.12/site-packages/sanic/base/root.py
new file mode 100644
index 0000000..3d6e33d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/base/root.py
@@ -0,0 +1,69 @@
+import re
+
+from typing import Any
+
+from sanic.base.meta import SanicMeta
+from sanic.exceptions import SanicException
+from sanic.mixins.commands import CommandMixin
+from sanic.mixins.exceptions import ExceptionMixin
+from sanic.mixins.listeners import ListenerMixin
+from sanic.mixins.middleware import MiddlewareMixin
+from sanic.mixins.routes import RouteMixin
+from sanic.mixins.signals import SignalMixin
+from sanic.mixins.static import StaticMixin
+
+
+VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$")
+
+
+class BaseSanic(
+    RouteMixin,
+    StaticMixin,
+    MiddlewareMixin,
+    ListenerMixin,
+    ExceptionMixin,
+    SignalMixin,
+    CommandMixin,
+    metaclass=SanicMeta,
+):
+    __slots__ = ("name",)
+
+    def __init__(
+        self, name: str | None = None, *args: Any, **kwargs: Any
+    ) -> None:
+        class_name = self.__class__.__name__
+
+        if name is None:
+            raise SanicException(
+                f"{class_name} instance cannot be unnamed. "
+                "Please use Sanic(name='your_application_name') instead.",
+            )
+
+        if not VALID_NAME.match(name):
+            raise SanicException(
+                f"{class_name} instance named '{name}' uses an invalid "
+                "format. Names must begin with a character and may only "
+                "contain alphanumeric characters, _, or -."
+            )
+
+        self.name = name
+
+        for base in BaseSanic.__bases__:
+            base.__init__(self, *args, **kwargs)  # type: ignore
+
+    def __str__(self) -> str:
+        return f"<{self.__class__.__name__} {self.name}>"
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(name="{self.name}")'
+
+    def __setattr__(self, name: str, value: Any) -> None:
+        try:
+            super().__setattr__(name, value)
+        except AttributeError as e:
+            raise AttributeError(
+                f"Setting variables on {self.__class__.__name__} instances is "
+                "not allowed. You should change your "
+                f"{self.__class__.__name__} instance to use "
+                f"instance.ctx.{name} instead.",
+            ) from e
diff --git a/.venv/lib/python3.12/site-packages/sanic/blueprint_group.py b/.venv/lib/python3.12/site-packages/sanic/blueprint_group.py
new file mode 100644
index 0000000..b95bc52
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/blueprint_group.py
@@ -0,0 +1,4 @@
+from .blueprints import BlueprintGroup
+
+
+__all__ = ["BlueprintGroup"]  # noqa: F405
diff --git a/.venv/lib/python3.12/site-packages/sanic/blueprints.py b/.venv/lib/python3.12/site-packages/sanic/blueprints.py
new file mode 100644
index 0000000..d572b45
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/blueprints.py
@@ -0,0 +1,983 @@
+from __future__ import annotations
+
+import asyncio
+
+from collections import defaultdict
+from collections.abc import Iterable, Iterator, MutableSequence, Sequence
+from copy import deepcopy
+from functools import partial, wraps
+from inspect import isfunction
+from itertools import chain
+from types import SimpleNamespace
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    overload,
+)
+
+from sanic_routing.exceptions import NotFound
+from sanic_routing.route import Route
+
+from sanic.base.root import BaseSanic
+from sanic.exceptions import SanicException
+from sanic.helpers import Default, _default
+from sanic.models.futures import FutureRoute, FutureSignal, FutureStatic
+from sanic.models.handler_types import (
+    ListenerType,
+    MiddlewareType,
+    RouteHandler,
+)
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+def lazy(func, as_decorator=True):
+    """Decorator to register a function to be called later.
+
+    Args:
+        func (Callable): Function to be called later.
+        as_decorator (bool): Whether the function should be called
+            immediately or not.
+    """
+
+    @wraps(func)
+    def decorator(bp, *args, **kwargs):
+        nonlocal as_decorator
+        kwargs["apply"] = False
+        pass_handler = None
+
+        if args and isfunction(args[0]):
+            as_decorator = False
+
+        def wrapper(handler):
+            future = func(bp, *args, **kwargs)
+            if as_decorator:
+                future = future(handler)
+
+            if bp.registered:
+                for app in bp.apps:
+                    bp.register(app, {})
+
+            return future
+
+        return wrapper if as_decorator else wrapper(pass_handler)
+
+    return decorator
+
+
+class Blueprint(BaseSanic):
+    """A logical collection of URLs that consist of a similar logical domain.
+
+    A Blueprint object is the main tool for grouping functionality and similar endpoints. It allows the developer to
+    organize routes, exception handlers, middleware, and other web functionalities into separate, modular groups.
+
+    See [Blueprints](/en/guide/best-practices/blueprints) for more information.
+
+    Args:
+        name (str): The name of the blueprint.
+        url_prefix (Optional[str]): The URL prefix for all routes defined on this blueprint.
+        host (Optional[Union[List[str], str]]): Host or list of hosts that this blueprint should respond to.
+        version (Optional[Union[int, str, float]]): Version number of the API implemented by this blueprint.
+        strict_slashes (Optional[bool]): Whether or not the URL should end with a slash.
+        version_prefix (str): Prefix for the version. Default is "/v".
+    """  # noqa: E501
+
+    __slots__ = (
+        "_apps",
+        "_future_commands",
+        "_future_routes",
+        "_future_statics",
+        "_future_middleware",
+        "_future_listeners",
+        "_future_exceptions",
+        "_future_signals",
+        "_allow_route_overwrite",
+        "copied_from",
+        "ctx",
+        "exceptions",
+        "host",
+        "listeners",
+        "middlewares",
+        "routes",
+        "statics",
+        "strict_slashes",
+        "url_prefix",
+        "version",
+        "version_prefix",
+        "websocket_routes",
+    )
+
+    def __init__(
+        self,
+        name: str,
+        url_prefix: str | None = None,
+        host: list[str] | str | None = None,
+        version: int | str | float | None = None,
+        strict_slashes: bool | None = None,
+        version_prefix: str = "/v",
+    ):
+        super().__init__(name=name)
+        self.reset()
+        self._allow_route_overwrite = False
+        self.copied_from = ""
+        self.ctx = SimpleNamespace()
+        self.host = host
+        self.strict_slashes = strict_slashes
+        self.url_prefix = (
+            url_prefix[:-1]
+            if url_prefix and url_prefix.endswith("/")
+            else url_prefix
+        )
+        self.version = version
+        self.version_prefix = version_prefix
+
+    def __repr__(self) -> str:
+        args = ", ".join(
+            [
+                f'{attr}="{getattr(self, attr)}"'
+                if isinstance(getattr(self, attr), str)
+                else f"{attr}={getattr(self, attr)}"
+                for attr in (
+                    "name",
+                    "url_prefix",
+                    "host",
+                    "version",
+                    "strict_slashes",
+                )
+            ]
+        )
+        return f"Blueprint({args})"
+
+    @property
+    def apps(self) -> set[Sanic]:
+        """Get the set of apps that this blueprint is registered to.
+
+        Returns:
+            Set[Sanic]: Set of apps that this blueprint is registered to.
+
+        Raises:
+            SanicException: If the blueprint has not yet been registered to
+                an app.
+        """
+        if not self._apps:
+            raise SanicException(
+                f"{self} has not yet been registered to an app"
+            )
+        return self._apps
+
+    @property
+    def registered(self) -> bool:
+        """Check if the blueprint has been registered to an app.
+
+        Returns:
+            bool: `True` if the blueprint has been registered to an app,
+                `False` otherwise.
+        """
+        return bool(self._apps)
+
+    exception = lazy(BaseSanic.exception)
+    listener = lazy(BaseSanic.listener)
+    middleware = lazy(BaseSanic.middleware)
+    route = lazy(BaseSanic.route)
+    signal = lazy(BaseSanic.signal)
+    static = lazy(BaseSanic.static, as_decorator=False)
+
+    def reset(self) -> None:
+        """Reset the blueprint to its initial state."""
+        self._apps: set[Sanic] = set()
+        self._allow_route_overwrite = False
+        self.exceptions: list[RouteHandler] = []
+        self.listeners: dict[str, list[ListenerType[Any]]] = {}
+        self.middlewares: list[MiddlewareType] = []
+        self.routes: list[Route] = []
+        self.statics: list[RouteHandler] = []
+        self.websocket_routes: list[Route] = []
+
+    def copy(
+        self,
+        name: str,
+        url_prefix: str | Default | None = _default,
+        version: int | str | float | Default | None = _default,
+        version_prefix: str | Default = _default,
+        allow_route_overwrite: bool | Default = _default,
+        strict_slashes: bool | Default | None = _default,
+        with_registration: bool = True,
+        with_ctx: bool = False,
+    ):
+        """Copy a blueprint instance with some optional parameters to override the values of attributes in the old instance.
+
+        Args:
+            name (str): Unique name of the blueprint.
+            url_prefix (Optional[Union[str, Default]]): URL to be prefixed before all route URLs.
+            version (Optional[Union[int, str, float, Default]]): Blueprint version.
+            version_prefix (Union[str, Default]): The prefix of the version number shown in the URL.
+            allow_route_overwrite (Union[bool, Default]): Whether to allow route overwrite or not.
+            strict_slashes (Optional[Union[bool, Default]]): Enforce the API URLs are requested with a trailing "/*".
+            with_registration (bool): Whether to register the new blueprint instance with Sanic apps that were registered with the old instance or not. Default is `True`.
+            with_ctx (bool): Whether the ``ctx`` will be copied or not. Default is `False`.
+
+        Returns:
+            Blueprint: A new Blueprint instance with the specified attributes.
+        """  # noqa: E501
+
+        attrs_backup = {
+            "_apps": self._apps,
+            "routes": self.routes,
+            "websocket_routes": self.websocket_routes,
+            "middlewares": self.middlewares,
+            "exceptions": self.exceptions,
+            "listeners": self.listeners,
+            "statics": self.statics,
+        }
+
+        self.reset()
+        new_bp = deepcopy(self)
+        new_bp.name = name
+        new_bp.copied_from = self.name
+
+        if not isinstance(url_prefix, Default):
+            new_bp.url_prefix = url_prefix
+        if not isinstance(version, Default):
+            new_bp.version = version
+        if not isinstance(strict_slashes, Default):
+            new_bp.strict_slashes = strict_slashes
+        if not isinstance(version_prefix, Default):
+            new_bp.version_prefix = version_prefix
+        if not isinstance(allow_route_overwrite, Default):
+            new_bp._allow_route_overwrite = allow_route_overwrite
+
+        for key, value in attrs_backup.items():
+            setattr(self, key, value)
+
+        if with_registration and self._apps:
+            if new_bp._future_statics:
+                raise SanicException(
+                    "Static routes registered with the old blueprint instance,"
+                    " cannot be registered again."
+                )
+            for app in self._apps:
+                app.blueprint(new_bp)
+
+        if not with_ctx:
+            new_bp.ctx = SimpleNamespace()
+
+        return new_bp
+
+    @staticmethod
+    def group(
+        *blueprints: Blueprint | BlueprintGroup,
+        url_prefix: str | None = None,
+        version: int | str | float | None = None,
+        strict_slashes: bool | None = None,
+        version_prefix: str = "/v",
+        name_prefix: str | None = "",
+    ) -> BlueprintGroup:
+        """Group multiple blueprints (or other blueprint groups) together.
+
+        Gropuping blueprings is a method for modularizing and organizing
+        your application's code. This can be a powerful tool for creating
+        reusable components, logically structuring your application code,
+        and easily maintaining route definitions in bulk.
+
+        This is the preferred way to group multiple blueprints together.
+
+        Args:
+            blueprints (Union[Blueprint, BlueprintGroup]): Blueprints to be
+                registered as a group.
+            url_prefix (Optional[str]): URL route to be prepended to all
+                sub-prefixes. Default is `None`.
+            version (Optional[Union[int, str, float]]): API Version to be
+                used for Blueprint group. Default is `None`.
+            strict_slashes (Optional[bool]): Indicate strict slash
+                termination behavior for URL. Default is `None`.
+            version_prefix (str): Prefix to be used for the version in the
+                URL. Default is "/v".
+            name_prefix (Optional[str]): Prefix to be used for the name of
+                the blueprints in the group. Default is an empty string.
+
+        Returns:
+            BlueprintGroup: A group of blueprints.
+
+        Example:
+            The resulting group will have the URL prefixes
+            `'/v2/bp1'` and `'/v2/bp2'` for bp1 and bp2, respectively.
+            ```python
+            bp1 = Blueprint('bp1', url_prefix='/bp1')
+            bp2 = Blueprint('bp2', url_prefix='/bp2')
+            group = group(bp1, bp2, version=2)
+            ```
+        """
+
+        def chain(nested) -> Iterable[Blueprint]:
+            """Iterate through nested blueprints"""
+            for i in nested:
+                if isinstance(i, (list, tuple)):
+                    yield from chain(i)
+                else:
+                    yield i
+
+        bps = BlueprintGroup(
+            url_prefix=url_prefix,
+            version=version,
+            strict_slashes=strict_slashes,
+            version_prefix=version_prefix,
+            name_prefix=name_prefix,
+        )
+        for bp in chain(blueprints):
+            bps.append(bp)
+        return bps
+
+    def register(self, app, options):
+        """Register the blueprint to the sanic app.
+
+        Args:
+            app (Sanic): Sanic app to register the blueprint to.
+            options (dict): Options to be passed to the blueprint.
+        """
+
+        self._apps.add(app)
+        url_prefix = options.get("url_prefix", self.url_prefix)
+        opt_version = options.get("version", None)
+        opt_strict_slashes = options.get("strict_slashes", None)
+        opt_version_prefix = options.get("version_prefix", self.version_prefix)
+        opt_name_prefix = options.get("name_prefix", None)
+        error_format = options.get(
+            "error_format", app.config.FALLBACK_ERROR_FORMAT
+        )
+
+        routes = []
+        middleware = []
+        exception_handlers = []
+        listeners = defaultdict(list)
+        registered = set()
+
+        # Routes
+        for future in self._future_routes:
+            # Prepend the blueprint URI prefix if available
+            uri = self._setup_uri(future.uri, url_prefix)
+
+            route_error_format = (
+                future.error_format if future.error_format else error_format
+            )
+
+            version_prefix = self.version_prefix
+            for prefix in (
+                future.version_prefix,
+                opt_version_prefix,
+            ):
+                if prefix and prefix != "/v":
+                    version_prefix = prefix
+                    break
+
+            version = self._extract_value(
+                future.version, opt_version, self.version
+            )
+            strict_slashes = self._extract_value(
+                future.strict_slashes, opt_strict_slashes, self.strict_slashes
+            )
+
+            name = future.name
+            if opt_name_prefix:
+                name = f"{opt_name_prefix}_{future.name}"
+            name = app.generate_name(name)
+            host = future.host or self.host
+            if isinstance(host, list):
+                host = tuple(host)
+
+            apply_route = FutureRoute(
+                future.handler,
+                uri,
+                future.methods,
+                host,
+                strict_slashes,
+                future.stream,
+                version,
+                name,
+                future.ignore_body,
+                future.websocket,
+                future.subprotocols,
+                future.unquote,
+                future.static,
+                version_prefix,
+                route_error_format,
+                future.route_context,
+            )
+
+            if (self, apply_route) in app._future_registry:
+                continue
+
+            registered.add(apply_route)
+            route = app._apply_route(
+                apply_route, overwrite=self._allow_route_overwrite
+            )
+
+            # If it is a copied BP, then make sure all of the names of routes
+            # matchup with the new BP name
+            if self.copied_from:
+                for r in route:
+                    r.name = r.name.replace(self.copied_from, self.name)
+                    r.extra.ident = r.extra.ident.replace(
+                        self.copied_from, self.name
+                    )
+
+            operation = (
+                routes.extend if isinstance(route, list) else routes.append
+            )
+            operation(route)
+
+        # Static Files
+        for future in self._future_statics:
+            # Prepend the blueprint URI prefix if available
+            uri = self._setup_uri(future.uri, url_prefix)
+            apply_route = FutureStatic(uri, *future[1:])
+
+            if (self, apply_route) in app._future_registry:
+                continue
+
+            registered.add(apply_route)
+            route = app._apply_static(apply_route)
+            routes.append(route)
+
+        route_names = [route.name for route in routes if route]
+
+        if route_names:
+            # Middleware
+            for future in self._future_middleware:
+                if (self, future) in app._future_registry:
+                    continue
+                middleware.append(app._apply_middleware(future, route_names))
+
+            # Exceptions
+            for future in self._future_exceptions:
+                if (self, future) in app._future_registry:
+                    continue
+                exception_handlers.append(
+                    app._apply_exception_handler(future, route_names)
+                )
+
+        # Event listeners
+        for future in self._future_listeners:
+            if (self, future) in app._future_registry:
+                continue
+            listeners[future.event].append(app._apply_listener(future))
+
+        # Signals
+        for future in self._future_signals:
+            if (self, future) in app._future_registry:
+                continue
+            future.condition.update({"__blueprint__": self.name})
+            # Force exclusive to be False
+            app._apply_signal(
+                FutureSignal(
+                    future.handler,
+                    future.event,
+                    future.condition,
+                    False,
+                    future.priority,
+                )
+            )
+
+        self.routes += [route for route in routes if isinstance(route, Route)]
+        self.websocket_routes += [
+            route for route in self.routes if route.extra.websocket
+        ]
+        self.middlewares += middleware
+        self.exceptions += exception_handlers
+        self.listeners.update(dict(listeners))
+
+        if self.registered:
+            self.register_futures(
+                self.apps,
+                self,
+                chain(
+                    registered,
+                    self._future_middleware,
+                    self._future_exceptions,
+                    self._future_listeners,
+                    self._future_signals,
+                ),
+            )
+
+        if self._future_commands:
+            raise SanicException(
+                "Registering commands with blueprints is not supported."
+            )
+
+    async def dispatch(self, *args, **kwargs):
+        """Dispatch a signal event
+
+        Args:
+            *args: Arguments to be passed to the signal event.
+            **kwargs: Keyword arguments to be passed to the signal event.
+        """
+        condition = kwargs.pop("condition", {})
+        condition.update({"__blueprint__": self.name})
+        kwargs["condition"] = condition
+        return await asyncio.gather(
+            *[app.dispatch(*args, **kwargs) for app in self.apps]
+        )
+
+    def event(
+        self,
+        event: str,
+        timeout: int | float | None = None,
+        *,
+        condition: dict[str, Any] | None = None,
+    ):
+        """Wait for a signal event to be dispatched.
+
+        Args:
+            event (str): Name of the signal event.
+            timeout (Optional[Union[int, float]]): Timeout for the event to be
+                dispatched.
+            condition: If provided, method will only return when the signal
+                is dispatched with the given condition.
+
+        Returns:
+            Awaitable: Awaitable for the event to be dispatched.
+        """
+        if condition is None:
+            condition = {}
+        condition.update({"__blueprint__": self.name})
+
+        waiters = []
+        for app in self.apps:
+            waiter = app.signal_router.get_waiter(
+                event, condition, exclusive=False
+            )
+            if not waiter:
+                raise NotFound("Could not find signal %s" % event)
+            waiters.append(waiter)
+
+        return self._event(waiters, timeout)
+
+    async def _event(self, waiters, timeout):
+        done, pending = await asyncio.wait(
+            [asyncio.create_task(waiter.wait()) for waiter in waiters],
+            return_when=asyncio.FIRST_COMPLETED,
+            timeout=timeout,
+        )
+        for task in pending:
+            task.cancel()
+        if not done:
+            raise TimeoutError()
+        (finished_task,) = done
+        return finished_task.result()
+
+    @staticmethod
+    def _extract_value(*values):
+        value = values[-1]
+        for v in values:
+            if v is not None:
+                value = v
+                break
+        return value
+
+    @staticmethod
+    def _setup_uri(base: str, prefix: str | None):
+        uri = base
+        if prefix:
+            uri = prefix
+            if base.startswith("/") and prefix.endswith("/"):
+                uri += base[1:]
+            else:
+                uri += base
+
+        return uri[1:] if uri.startswith("//") else uri
+
+    @staticmethod
+    def register_futures(
+        apps: set[Sanic], bp: Blueprint, futures: Sequence[tuple[Any, ...]]
+    ):
+        """Register futures to the apps.
+
+        Args:
+            apps (Set[Sanic]): Set of apps to register the futures to.
+            bp (Blueprint): Blueprint that the futures belong to.
+            futures (Sequence[Tuple[Any, ...]]): Sequence of futures to be
+                registered.
+        """
+
+        for app in apps:
+            app._future_registry.update({(bp, item) for item in futures})
+
+
+bpg_base = MutableSequence[Blueprint]
+
+
+class BlueprintGroup(bpg_base):
+    """This class provides a mechanism to implement a Blueprint Group.
+
+    The `BlueprintGroup` class allows grouping blueprints under a common
+    URL prefix, version, and other shared attributes. It integrates with
+    Sanic's Blueprint system, offering a custom iterator to treat an
+    object of this class as a list/tuple.
+
+    Although possible to instantiate a group directly, it is recommended
+    to use the `Blueprint.group` method to create a group of blueprints.
+
+    Args:
+        url_prefix (Optional[str]): URL to be prefixed before all the
+            Blueprint Prefixes. Default is `None`.
+        version (Optional[Union[int, str, float]]): API Version for the
+            blueprint group, inherited by each Blueprint. Default is `None`.
+        strict_slashes (Optional[bool]): URL Strict slash behavior
+            indicator. Default is `None`.
+        version_prefix (str): Prefix for the version in the URL.
+            Default is `"/v"`.
+        name_prefix (Optional[str]): Prefix for the name of the blueprints
+            in the group. Default is an empty string.
+
+    Examples:
+        ```python
+        bp1 = Blueprint("bp1", url_prefix="/bp1")
+        bp2 = Blueprint("bp2", url_prefix="/bp2")
+
+        bp3 = Blueprint("bp3", url_prefix="/bp4")
+        bp4 = Blueprint("bp3", url_prefix="/bp4")
+
+
+        group1 = Blueprint.group(bp1, bp2)
+        group2 = Blueprint.group(bp3, bp4, version_prefix="/api/v", version="1")
+
+
+        @bp1.on_request
+        async def bp1_only_middleware(request):
+            print("applied on Blueprint : bp1 Only")
+
+
+        @bp1.route("/")
+        async def bp1_route(request):
+            return text("bp1")
+
+
+        @bp2.route("/")
+        async def bp2_route(request, param):
+            return text(param)
+
+
+        @bp3.route("/")
+        async def bp3_route(request):
+            return text("bp3")
+
+
+        @bp4.route("/")
+        async def bp4_route(request, param):
+            return text(param)
+
+
+        @group1.on_request
+        async def group_middleware(request):
+            print("common middleware applied for both bp1 and bp2")
+
+
+        # Register Blueprint group under the app
+        app.blueprint(group1)
+        app.blueprint(group2)
+        ```
+    """  # noqa: E501
+
+    __slots__ = (
+        "_blueprints",
+        "_url_prefix",
+        "_version",
+        "_strict_slashes",
+        "_version_prefix",
+        "_name_prefix",
+    )
+
+    def __init__(
+        self,
+        url_prefix: str | None = None,
+        version: int | str | float | None = None,
+        strict_slashes: bool | None = None,
+        version_prefix: str = "/v",
+        name_prefix: str | None = "",
+    ):
+        self._blueprints: list[Blueprint] = []
+        self._url_prefix = url_prefix
+        self._version = version
+        self._version_prefix = version_prefix
+        self._strict_slashes = strict_slashes
+        self._name_prefix = name_prefix
+
+    @property
+    def url_prefix(self) -> int | str | float | None:
+        """The URL prefix for the Blueprint Group.
+
+        Returns:
+            Optional[Union[int, str, float]]: URL prefix for the Blueprint
+                Group.
+        """
+        return self._url_prefix
+
+    @property
+    def blueprints(self) -> list[Blueprint]:
+        """A list of all the available blueprints under this group.
+
+        Returns:
+            List[Blueprint]: List of all the available blueprints under
+                this group.
+        """
+        return self._blueprints
+
+    @property
+    def version(self) -> str | int | float | None:
+        """API Version for the Blueprint Group, if any.
+
+        Returns:
+            Optional[Union[str, int, float]]: API Version for the Blueprint
+        """
+        return self._version
+
+    @property
+    def strict_slashes(self) -> bool | None:
+        """Whether to enforce strict slashes for the Blueprint Group.
+
+        Returns:
+            Optional[bool]: Whether to enforce strict slashes for the
+        """
+        return self._strict_slashes
+
+    @property
+    def version_prefix(self) -> str:
+        """Version prefix for the Blueprint Group.
+
+        Returns:
+            str: Version prefix for the Blueprint Group.
+        """
+        return self._version_prefix
+
+    @property
+    def name_prefix(self) -> str | None:
+        """Name prefix for the Blueprint Group.
+
+        This is mainly needed when blueprints are copied in order to
+        avoid name conflicts.
+
+        Returns:
+            Optional[str]: Name prefix for the Blueprint Group.
+        """
+        return self._name_prefix
+
+    def __iter__(self) -> Iterator[Blueprint]:
+        """Iterate over the list of blueprints in the group.
+
+        Returns:
+            Iterator[Blueprint]: Iterator for the list of blueprints in
+        """
+        return iter(self._blueprints)
+
+    @overload
+    def __getitem__(self, item: int) -> Blueprint: ...
+
+    @overload
+    def __getitem__(self, item: slice) -> MutableSequence[Blueprint]: ...
+
+    def __getitem__(
+        self, item: int | slice
+    ) -> Blueprint | MutableSequence[Blueprint]:
+        """Get the Blueprint object at the specified index.
+
+        This method returns a blueprint inside the group specified by
+        an index value. This will enable indexing, splice and slicing
+        of the blueprint group like we can do with regular list/tuple.
+
+        This method is provided to ensure backward compatibility with
+        any of the pre-existing usage that might break.
+
+        Returns:
+            Blueprint: Blueprint object at the specified index.
+
+        Raises:
+            IndexError: If the index is out of range.
+        """
+        return self._blueprints[item]
+
+    @overload
+    def __setitem__(self, index: int, item: Blueprint) -> None: ...
+
+    @overload
+    def __setitem__(self, index: slice, item: Iterable[Blueprint]) -> None: ...
+
+    def __setitem__(
+        self,
+        index: int | slice,
+        item: Blueprint | Iterable[Blueprint],
+    ) -> None:
+        """Set the Blueprint object at the specified index.
+
+        Abstract method implemented to turn the `BlueprintGroup` class
+        into a list like object to support all the existing behavior.
+
+        This method is used to perform the list's indexed setter operation.
+
+        Args:
+            index (int): Index to use for removing a new Blueprint item
+            item (Blueprint): New `Blueprint` object.
+
+        Returns:
+            None
+
+        Raises:
+            IndexError: If the index is out of range.
+        """
+        if isinstance(index, int):
+            if not isinstance(item, Blueprint):
+                raise TypeError("Expected a Blueprint instance")
+            self._blueprints[index] = item
+        elif isinstance(index, slice):
+            if not isinstance(item, Iterable):
+                raise TypeError("Expected an iterable of Blueprint instances")
+            self._blueprints[index] = list(item)
+        else:
+            raise TypeError("Index must be int or slice")
+
+    @overload
+    def __delitem__(self, index: int) -> None: ...
+
+    @overload
+    def __delitem__(self, index: slice) -> None: ...
+
+    def __delitem__(self, index: int | slice) -> None:
+        """Delete the Blueprint object at the specified index.
+
+        Abstract method implemented to turn the `BlueprintGroup` class
+        into a list like object to support all the existing behavior.
+
+        This method is used to delete an item from the list of blueprint
+        groups like it can be done on a regular list with index.
+
+        Args:
+            index (int): Index to use for removing a new Blueprint item
+
+        Returns:
+            None
+
+        Raises:
+            IndexError: If the index is out of range.
+        """
+        del self._blueprints[index]
+
+    def __len__(self) -> int:
+        """Get the Length of the blueprint group object.
+
+        Returns:
+            int: Length of the blueprint group object.
+        """
+        return len(self._blueprints)
+
+    def append(self, value: Blueprint) -> None:
+        """Add a new Blueprint object to the group.
+
+        The Abstract class `MutableSequence` leverages this append method to
+        perform the `BlueprintGroup.append` operation.
+
+        Args:
+            value (Blueprint): New `Blueprint` object.
+
+        Returns:
+            None
+        """
+        self._blueprints.append(value)
+
+    def exception(self, *exceptions: Exception, **kwargs) -> Callable:
+        """Decorate a function to handle exceptions for all blueprints in the group.
+
+        In case of nested Blueprint Groups, the same handler is applied
+        across each of the Blueprints recursively.
+
+        Args:
+            *exceptions (Exception): Exceptions to handle
+            **kwargs (dict): Optional Keyword arg to use with Middleware
+
+        Returns:
+            Partial function to apply the middleware
+
+        Examples:
+            ```python
+            bp1 = Blueprint("bp1", url_prefix="/bp1")
+            bp2 = Blueprint("bp2", url_prefix="/bp2")
+            group1 = Blueprint.group(bp1, bp2)
+
+            @group1.exception(Exception)
+            def handler(request, exception):
+                return text("Exception caught")
+            ```
+        """  # noqa: E501
+
+        def register_exception_handler_for_blueprints(fn):
+            for blueprint in self.blueprints:
+                blueprint.exception(*exceptions, **kwargs)(fn)
+
+        return register_exception_handler_for_blueprints
+
+    def insert(self, index: int, item: Blueprint) -> None:
+        """Insert a new Blueprint object to the group at the specified index.
+
+        The Abstract class `MutableSequence` leverages this insert method to
+        perform the `BlueprintGroup.append` operation.
+
+        Args:
+            index (int): Index to use for removing a new Blueprint item
+            item (Blueprint): New `Blueprint` object.
+
+        Returns:
+            None
+        """
+        self._blueprints.insert(index, item)
+
+    def middleware(self, *args, **kwargs):
+        """A decorator that can be used to implement a Middleware for all blueprints in the group.
+
+        In case of nested Blueprint Groups, the same middleware is applied
+        across each of the Blueprints recursively.
+
+        Args:
+            *args (Optional): Optional positional Parameters to be use middleware
+            **kwargs (Optional): Optional Keyword arg to use with Middleware
+
+        Returns:
+            Partial function to apply the middleware
+        """  # noqa: E501
+
+        def register_middleware_for_blueprints(fn):
+            for blueprint in self.blueprints:
+                blueprint.middleware(fn, *args, **kwargs)
+
+        if args and callable(args[0]):
+            fn = args[0]
+            args = list(args)[1:]
+            return register_middleware_for_blueprints(fn)
+        return register_middleware_for_blueprints
+
+    def on_request(self, middleware=None):
+        """Convenience method to register a request middleware for all blueprints in the group.
+
+        Args:
+            middleware (Optional): Optional positional Parameters to be use middleware
+
+        Returns:
+            Partial function to apply the middleware
+        """  # noqa: E501
+        if callable(middleware):
+            return self.middleware(middleware, "request")
+        else:
+            return partial(self.middleware, attach_to="request")
+
+    def on_response(self, middleware=None):
+        """Convenience method to register a response middleware for all blueprints in the group.
+
+        Args:
+            middleware (Optional): Optional positional Parameters to be use middleware
+
+        Returns:
+            Partial function to apply the middleware
+        """  # noqa: E501
+        if callable(middleware):
+            return self.middleware(middleware, "response")
+        else:
+            return partial(self.middleware, attach_to="response")
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/__init__.py b/.venv/lib/python3.12/site-packages/sanic/cli/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/app.py b/.venv/lib/python3.12/site-packages/sanic/cli/app.py
new file mode 100644
index 0000000..929b5cc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/app.py
@@ -0,0 +1,499 @@
+from __future__ import annotations
+
+import os
+import shutil
+import sys
+
+from argparse import Namespace
+from functools import partial
+from textwrap import indent
+
+from sanic.app import Sanic
+from sanic.application.logo import get_logo
+from sanic.cli.arguments import Group
+from sanic.cli.base import SanicArgumentParser, SanicHelpFormatter
+from sanic.cli.console import SanicREPL
+from sanic.cli.daemon import (
+    kill_daemon,
+    make_kill_parser,
+    make_restart_parser,
+    make_status_parser,
+    resolve_target,
+    restart_daemon,
+    status_daemon,
+    stop_daemon,
+)
+from sanic.cli.executor import Executor, make_executor_parser
+from sanic.cli.inspector import make_inspector_parser
+from sanic.cli.inspector_client import InspectorClient
+from sanic.compat import OS_IS_WINDOWS
+from sanic.helpers import _default, is_atty
+from sanic.log import error_logger
+from sanic.worker.daemon import Daemon, DaemonError
+from sanic.worker.loader import AppLoader
+
+
+class SanicCLI:
+    DESCRIPTION_SHORT = indent(
+        f"""
+{get_logo(True)}
+
+Usage:
+    sanic  [options]       Run a Sanic application
+    sanic  exec       Execute a command in app context
+    sanic inspect [options]        Inspect a running instance
+    sanic help [--full]            Show help (--full for all options)
+
+Examples:
+    sanic path.to.server:app       Run app
+    sanic path.to.server --dev     Run in development mode
+    sanic ./static --simple        Serve static files
+""",
+        prefix=" ",
+    )
+
+    DESCRIPTION_SHORT_FOOTER = """
+(additional options available)
+
+For complete options and documentation:
+    sanic help --full
+"""
+
+    DESCRIPTION_FULL = indent(
+        f"""
+{get_logo(True)}
+
+To start running a Sanic application, provide a path to the module, where
+app is a Sanic() instance in the global scope:
+
+    $ sanic path.to.server:app
+
+If the Sanic instance variable is called 'app', you can leave off the last
+part, and only provide a path to the module where the instance is:
+
+    $ sanic path.to.server
+
+Or, a path to a callable that returns a Sanic() instance:
+
+    $ sanic path.to.factory:create_app
+
+Or, a path to a directory to run as a simple HTTP server:
+
+    $ sanic ./path/to/static
+
+Additional commands:
+
+    $ sanic inspect ...           Inspect a running Sanic instance
+    $ sanic path.to.app exec ...  Run app commands
+    $ sanic path.to.app status    Check if app daemon is running
+    $ sanic path.to.app restart   Restart app daemon (future use)
+    $ sanic path.to.app stop      Stop app daemon
+
+Advanced daemon management:
+
+    $ sanic kill (--pid PID | --pidfile PATH)      Force kill (SIGKILL)
+    $ sanic status (--pid PID | --pidfile PATH)    Check status
+    $ sanic restart (--pid PID | --pidfile PATH)   Restart (future use)
+""",
+        prefix=" ",
+    )
+
+    def __init__(self) -> None:
+        width = shutil.get_terminal_size().columns
+        self.parser = SanicArgumentParser(
+            prog="sanic",
+            description=self.DESCRIPTION_SHORT,
+            formatter_class=lambda prog: SanicHelpFormatter(
+                prog,
+                max_help_position=36 if width > 96 else 24,
+                indent_increment=4,
+                width=None,
+            ),
+        )
+        self.parser._positionals.title = "Required\n========\n  Positional"
+        self.parser._optionals.title = "Optional\n========\n  General"
+        self.main_process = (
+            os.environ.get("SANIC_RELOADER_PROCESS", "") != "true"
+        )
+        self.args: Namespace = Namespace()
+        self.groups: list[Group] = []
+        self.run_mode = "serve"
+
+    def attach(self):
+        if len(sys.argv) > 1 and sys.argv[1] == "help":
+            self.run_mode = "help"
+            return
+
+        if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"):
+            self.run_mode = "help"
+            return
+
+        if len(sys.argv) > 1 and sys.argv[1] == "inspect":
+            self.run_mode = "inspect"
+            self.parser.description = get_logo(True)
+            make_inspector_parser(self.parser)
+            return
+
+        if not OS_IS_WINDOWS and len(sys.argv) > 1 and sys.argv[1] == "kill":
+            self.run_mode = "kill"
+            self.parser.description = get_logo(True)
+            make_kill_parser(self.parser)
+            return
+
+        if not OS_IS_WINDOWS and len(sys.argv) > 1 and sys.argv[1] == "status":
+            self.run_mode = "status"
+            self.parser.description = get_logo(True)
+            make_status_parser(self.parser)
+            return
+
+        if (
+            not OS_IS_WINDOWS
+            and len(sys.argv) > 1
+            and sys.argv[1] == "restart"
+        ):
+            self.run_mode = "restart"
+            self.parser.description = get_logo(True)
+            make_restart_parser(self.parser)
+            return
+
+        # Check for app-based daemon commands: sanic  status|restart|stop
+        if (
+            not OS_IS_WINDOWS
+            and len(sys.argv) > 2
+            and sys.argv[2]
+            in (
+                "status",
+                "restart",
+                "stop",
+            )
+        ):
+            self.run_mode = f"app_{sys.argv[2]}"
+
+        for group in Group._registry:
+            instance = group.create(self.parser)
+            instance.attach()
+            self.groups.append(instance)
+
+        if len(sys.argv) > 2 and sys.argv[2] == "exec":
+            self.run_mode = "exec"
+            self.parser.description = get_logo(True)
+            make_executor_parser(self.parser)
+
+    def run(self, parse_args=None):
+        if self.run_mode == "inspect":
+            self._inspector()
+            return
+
+        if self.run_mode == "kill":
+            self._kill()
+            return
+
+        if self.run_mode == "status":
+            self._status()
+            return
+
+        if self.run_mode == "restart":
+            self._restart()
+            return
+
+        if self.run_mode == "help":
+            self._help()
+            return
+
+        if self.run_mode.startswith("app_"):
+            self._app_daemon_command()
+            return
+
+        legacy_version = False
+        if not parse_args:
+            # This is to provide backwards compat -v to display version
+            legacy_version = len(sys.argv) == 2 and sys.argv[-1] == "-v"
+            parse_args = ["--version"] if legacy_version else None
+        elif parse_args == ["-v"]:
+            parse_args = ["--version"]
+
+        if not legacy_version:
+            if self.run_mode == "exec":
+                parse_args = [
+                    a
+                    for a in (parse_args or sys.argv[1:])
+                    if a not in "-h --help".split()
+                ]
+            parsed, unknown = self.parser.parse_known_args(args=parse_args)
+            if unknown and parsed.factory:
+                for arg in unknown:
+                    if arg.startswith("--"):
+                        self.parser.add_argument(arg.split("=")[0])
+
+        if self.run_mode == "exec":
+            self.args, _ = self.parser.parse_known_args(args=parse_args)
+        else:
+            self.args = self.parser.parse_args(args=parse_args)
+        self._precheck()
+        app_loader = AppLoader(
+            self.args.target, self.args.factory, self.args.simple, self.args
+        )
+
+        try:
+            app = self._get_app(app_loader)
+            kwargs = self._build_run_kwargs()
+        except ValueError as e:
+            error_logger.exception(f"Failed to run app: {e}")
+        else:
+            if self.run_mode == "exec":
+                self._executor(app, kwargs)
+                return
+            elif self.run_mode != "serve":
+                raise ValueError(f"Unknown run mode: {self.run_mode}")
+
+            daemon = None
+            if getattr(self.args, "daemon", False):
+                daemon = self._setup_daemon(app.name)
+                if daemon:
+                    lines = ["Starting Sanic in daemon mode..."]
+                    if daemon.pidfile:
+                        lines.append(f"  PID file: {daemon.pidfile}")
+                    if daemon.logfile:
+                        lines.append(f"  Logs: {daemon.logfile}")
+                    print("\n".join(lines), flush=True)
+                    daemon.daemonize()
+
+            if self.args.repl:
+                self._repl(app)
+            for http_version in self.args.http:
+                app.prepare(**kwargs, version=http_version)
+
+            if daemon:
+                daemon.drop_privileges()
+
+            if self.args.single:
+                serve = Sanic.serve_single
+            else:
+                serve = partial(Sanic.serve, app_loader=app_loader)
+            serve(app)
+
+    def _inspector(self):
+        args = sys.argv[2:]
+        self.args, unknown = self.parser.parse_known_args(args=args)
+        if unknown:
+            for arg in unknown:
+                if arg.startswith("--"):
+                    try:
+                        key, value = arg.split("=")
+                        key = key.lstrip("-")
+                    except ValueError:
+                        value = False if arg.startswith("--no-") else True
+                        key = (
+                            arg.replace("--no-", "")
+                            .lstrip("-")
+                            .replace("-", "_")
+                        )
+                    setattr(self.args, key, value)
+
+        kwargs = {**self.args.__dict__}
+        host = kwargs.pop("host")
+        port = kwargs.pop("port")
+        secure = kwargs.pop("secure")
+        raw = kwargs.pop("raw")
+        action = kwargs.pop("action") or "info"
+        api_key = kwargs.pop("api_key")
+        positional = kwargs.pop("positional", None)
+        if action == "" and positional:
+            action = positional[0]
+            if len(positional) > 1:
+                kwargs["args"] = positional[1:]
+        InspectorClient(host, port, secure, raw, api_key).do(action, **kwargs)
+
+    def _kill(self):
+        self.args = self.parser.parse_args(args=sys.argv[2:])
+        pid, pidfile = resolve_target(self.args.pid, self.args.pidfile)
+        kill_daemon(pid, pidfile)
+
+    def _status(self):
+        self.args = self.parser.parse_args(args=sys.argv[2:])
+        pid, pidfile = resolve_target(self.args.pid, self.args.pidfile)
+        status_daemon(pid, pidfile)
+
+    def _restart(self):
+        self.args = self.parser.parse_args(args=sys.argv[2:])
+        pid, _ = resolve_target(self.args.pid, self.args.pidfile)
+        restart_daemon(pid)
+
+    def _help(self):
+        full = "--full" in sys.argv
+        if full:
+            self.parser.description = self.DESCRIPTION_FULL
+        for group in Group._registry:
+            instance = group.create(self.parser)
+            instance.attach(short=not full)
+            self.groups.append(instance)
+        self.parser.print_help()
+        if not full:
+            print(self.DESCRIPTION_SHORT_FOOTER)
+
+    def _app_daemon_command(self):
+        """Handle app-based daemon commands: sanic  status|restart|stop"""
+        command = self.run_mode.replace("app_", "")
+        # Parse just the app target (first arg)
+        self.args = self.parser.parse_args(args=[sys.argv[1]])
+
+        app_loader = AppLoader(
+            self.args.target, self.args.factory, self.args.simple, self.args
+        )
+
+        try:
+            app = self._get_app(app_loader)
+        except (ImportError, ValueError) as e:
+            error_logger.error(f"Failed to load app: {e}")
+            sys.exit(1)
+
+        pidfile = Daemon.get_pidfile_path(app.name)
+        pid, pidfile = resolve_target(None, str(pidfile))
+
+        if command == "status":
+            status_daemon(pid, pidfile)
+        elif command == "restart":
+            restart_daemon(pid)
+        elif command == "stop":
+            force = "-f" in sys.argv or "--force" in sys.argv
+            stop_daemon(pid, pidfile, force)
+
+    def _executor(self, app: Sanic, kwargs: dict):
+        args = sys.argv[3:]
+        Executor(app, kwargs).run(self.args.command, args)
+
+    def _repl(self, app: Sanic):
+        if is_atty():
+
+            @app.main_process_ready
+            async def start_repl(app):
+                SanicREPL(app, self.args.repl).run()
+                await app._startup()
+
+        elif self.args.repl is True:
+            error_logger.error(
+                "Can't start REPL in non-interactive mode. "
+                "You can only run with --repl in a TTY."
+            )
+
+    def _setup_daemon(self, app_name: str):
+        if OS_IS_WINDOWS:
+            error_logger.error(
+                "Daemon mode is not supported on Windows. "
+                "Consider using a Windows service or nssm instead."
+            )
+            sys.exit(1)
+
+        if getattr(self.args, "dev", False) or getattr(
+            self.args, "auto_reload", False
+        ):
+            error_logger.error(
+                "Daemon mode is not compatible with --dev or --auto-reload"
+            )
+            sys.exit(1)
+
+        if getattr(self.args, "repl", False):
+            error_logger.error("Daemon mode is not compatible with --repl")
+            sys.exit(1)
+
+        try:
+            daemon = Daemon(
+                pidfile=getattr(self.args, "pidfile", None) or "auto",
+                logfile=getattr(self.args, "logfile", None),
+                user=getattr(self.args, "daemon_user", None),
+                group=getattr(self.args, "daemon_group", None),
+                name=app_name,
+            )
+            return daemon
+        except DaemonError as e:
+            error_logger.error(f"Daemon configuration error: {e}")
+            sys.exit(1)
+
+    def _precheck(self):
+        # Custom TLS mismatch handling for better diagnostics
+        if self.main_process and (
+            # one of cert/key missing
+            bool(self.args.cert) != bool(self.args.key)
+            # new and old style self.args used together
+            or self.args.tls
+            and self.args.cert
+            # strict host checking without certs would always fail
+            or self.args.tlshost
+            and not self.args.tls
+            and not self.args.cert
+        ):
+            self.parser.print_usage(sys.stderr)
+            message = (
+                "TLS certificates must be specified by either of:\n"
+                "  --cert certdir/fullchain.pem --key certdir/privkey.pem\n"
+                "  --tls certdir  (equivalent to the above)"
+            )
+            error_logger.error(message)
+            sys.exit(1)
+
+    def _get_app(self, app_loader: AppLoader):
+        try:
+            app = app_loader.load()
+        except ImportError as e:
+            if app_loader.module_name.startswith(e.name):  # type: ignore
+                error_logger.error(
+                    f"No module named {e.name} found.\n"
+                    "  Example File: project/sanic_server.py -> app\n"
+                    "  Example Module: project.sanic_server.app"
+                )
+                error_logger.error(
+                    "\nThe error below might have caused the above one:\n"
+                    f"{e.msg}"
+                )
+                sys.exit(1)
+            else:
+                raise e
+        return app
+
+    def _build_run_kwargs(self):
+        for group in self.groups:
+            group.prepare(self.args)
+        ssl: None | dict | str | list = []
+        if self.args.tlshost:
+            ssl.append(None)
+        if self.args.cert is not None or self.args.key is not None:
+            ssl.append(dict(cert=self.args.cert, key=self.args.key))
+        if self.args.tls:
+            ssl += self.args.tls
+        if not ssl:
+            ssl = None
+        elif len(ssl) == 1 and ssl[0] is not None:
+            # Use only one cert, no TLSSelector.
+            ssl = ssl[0]
+
+        kwargs = {
+            "access_log": self.args.access_log,
+            "coffee": self.args.coffee,
+            "debug": self.args.debug,
+            "fast": self.args.fast,
+            "host": self.args.host,
+            "motd": self.args.motd,
+            "noisy_exceptions": self.args.noisy_exceptions,
+            "port": self.args.port,
+            "ssl": ssl,
+            "unix": self.args.unix,
+            "verbosity": self.args.verbosity or 0,
+            "workers": self.args.workers,
+            "auto_tls": self.args.auto_tls,
+            "single_process": self.args.single,
+        }
+
+        for maybe_arg in ("auto_reload", "dev"):
+            if getattr(self.args, maybe_arg, False):
+                kwargs[maybe_arg] = True
+
+        if self.args.dev and all(
+            arg not in sys.argv for arg in ("--repl", "--no-repl")
+        ):
+            self.args.repl = _default
+
+        if self.args.path:
+            kwargs["auto_reload"] = True
+            kwargs["reload_dir"] = self.args.path
+
+        return kwargs
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/arguments.py b/.venv/lib/python3.12/site-packages/sanic/cli/arguments.py
new file mode 100644
index 0000000..b3b84a1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/arguments.py
@@ -0,0 +1,431 @@
+from __future__ import annotations
+
+from argparse import ArgumentParser, _ArgumentGroup
+
+from sanic_routing import __version__ as __routing_version__
+
+from sanic import __version__
+from sanic.compat import OS_IS_WINDOWS
+from sanic.http.constants import HTTP
+
+
+class Group:
+    name: str | None
+    container: ArgumentParser | _ArgumentGroup
+    _registry: list[type[Group]] = []
+
+    def __init_subclass__(cls) -> None:
+        Group._registry.append(cls)
+
+    def __init__(self, parser: ArgumentParser, title: str | None):
+        self.parser = parser
+
+        if title:
+            self.container = self.parser.add_argument_group(title=f"  {title}")
+        else:
+            self.container = self.parser
+
+    @classmethod
+    def create(cls, parser: ArgumentParser):
+        instance = cls(parser, cls.name)
+        return instance
+
+    def add_bool_arguments(
+        self,
+        *args,
+        nullable=False,
+        help: str,
+        negative_help: str | None = None,
+        **kwargs,
+    ):
+        group = self.container.add_mutually_exclusive_group()
+
+        pos_help = help[0].upper() + help[1:]
+        neg_help = (
+            negative_help if negative_help else f"Disable {help.lower()}"
+        )
+
+        group.add_argument(
+            *args,
+            action="store_true",
+            help=pos_help,
+            **kwargs,
+        )
+
+        group.add_argument(
+            "--no-" + args[0][2:],
+            *args[1:],
+            action="store_false",
+            help=neg_help[0].upper() + neg_help[1:],
+            **kwargs,
+        )
+
+        if nullable:
+            group.set_defaults(**{args[0][2:].replace("-", "_"): None})
+
+    def prepare(self, args) -> None: ...
+
+    def attach(self, short: bool = False) -> None: ...
+
+
+class GeneralGroup(Group):
+    name = None
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        self.container.add_argument(
+            "--version",
+            action="version",
+            version=f"Sanic {__version__}; Routing {__routing_version__}",
+        )
+
+        self.container.add_argument(
+            "target",
+            help=(
+                "Path to your Sanic app instance.\n"
+                "\tExample: path.to.server:app\n"
+                "If running a Simple Server, path to directory to serve.\n"
+                "\tExample: ./\n"
+                "Additionally, this can be a path to a factory function\n"
+                "that returns a Sanic app instance.\n"
+                "\tExample: path.to.server:create_app\n"
+            ),
+        )
+
+        choices = ["serve", "exec"]
+        help_text = (
+            "Action to perform.\n"
+            "\tserve: Run the Sanic app [default]\n"
+            "\texec: Execute a command in the Sanic app context\n"
+        )
+        if not OS_IS_WINDOWS:
+            choices.extend(["status", "restart", "stop"])
+            help_text += (
+                "\tstatus: Check if daemon is running\n"
+                "\trestart: Restart daemon (future use)\n"
+                "\tstop: Stop daemon gracefully\n"
+            )
+        self.container.add_argument(
+            "action",
+            nargs="?",
+            default="serve",
+            choices=choices,
+            help=help_text,
+        )
+
+
+class ApplicationGroup(Group):
+    name = "Application"
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        group = self.container.add_mutually_exclusive_group()
+        group.add_argument(
+            "--factory",
+            action="store_true",
+            help=(
+                "Treat app as an application factory, "
+                "i.e. a () ->  callable"
+            ),
+        )
+        group.add_argument(
+            "-s",
+            "--simple",
+            dest="simple",
+            action="store_true",
+            help=(
+                "Run Sanic as a Simple Server, and serve the contents of "
+                "a directory\n(module arg should be a path)"
+            ),
+        )
+        self.add_bool_arguments(
+            "--repl",
+            help="Run with an interactive shell session",
+            negative_help="Disable interactive shell session",
+        )
+
+
+class SocketGroup(Group):
+    name = "Socket binding"
+
+    def attach(self, short: bool = False):
+        self.container.add_argument(
+            "-H",
+            "--host",
+            dest="host",
+            type=str,
+            help="Host address [default 127.0.0.1]",
+        )
+        self.container.add_argument(
+            "-p",
+            "--port",
+            dest="port",
+            type=int,
+            help="Port to serve on [default 8000]",
+        )
+        if not OS_IS_WINDOWS and not short:
+            self.container.add_argument(
+                "-u",
+                "--unix",
+                dest="unix",
+                type=str,
+                default="",
+                help="location of UNIX socket",
+            )
+
+
+class HTTPVersionGroup(Group):
+    name = "HTTP version"
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        http_values = [http.value for http in HTTP.__members__.values()]
+
+        self.container.add_argument(
+            "--http",
+            dest="http",
+            action="append",
+            choices=http_values,
+            type=int,
+            help=(
+                "Which HTTP version to use: HTTP/1.1 or HTTP/3. Value should\n"
+                "be either 1, or 3. [default 1]"
+            ),
+        )
+        self.container.add_argument(
+            "-1",
+            dest="http",
+            action="append_const",
+            const=1,
+            help=("Run Sanic server using HTTP/1.1"),
+        )
+        self.container.add_argument(
+            "-3",
+            dest="http",
+            action="append_const",
+            const=3,
+            help=("Run Sanic server using HTTP/3"),
+        )
+
+    def prepare(self, args):
+        if not args.http:
+            args.http = [1]
+        args.http = tuple(sorted(set(map(HTTP, args.http)), reverse=True))
+
+
+class TLSGroup(Group):
+    name = "TLS certificate"
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        self.container.add_argument(
+            "--cert",
+            dest="cert",
+            type=str,
+            help="Location of fullchain.pem, bundle.crt or equivalent",
+        )
+        self.container.add_argument(
+            "--key",
+            dest="key",
+            type=str,
+            help="Location of privkey.pem or equivalent .key file",
+        )
+        self.container.add_argument(
+            "--tls",
+            metavar="DIR",
+            type=str,
+            action="append",
+            help=(
+                "TLS certificate folder with fullchain.pem and privkey.pem\n"
+                "May be specified multiple times to choose multiple "
+                "certificates"
+            ),
+        )
+        self.container.add_argument(
+            "--tls-strict-host",
+            dest="tlshost",
+            action="store_true",
+            help="Only allow clients that send an SNI matching server certs",
+        )
+
+
+class DevelopmentGroup(Group):
+    name = "Development"
+
+    def attach(self, short: bool = False):
+        if not short:
+            self.container.add_argument(
+                "--debug",
+                dest="debug",
+                action="store_true",
+                help="Run the server in debug mode",
+            )
+            self.container.add_argument(
+                "-r",
+                "--reload",
+                "--auto-reload",
+                dest="auto_reload",
+                action="store_true",
+                help="Auto-reload on source changes",
+            )
+            self.container.add_argument(
+                "-R",
+                "--reload-dir",
+                dest="path",
+                action="append",
+                help="Additional directories to watch for changes",
+            )
+        self.container.add_argument(
+            "-d",
+            "--dev",
+            dest="dev",
+            action="store_true",
+            help="Run in development mode (debug + auto-reload)",
+        )
+        if not short:
+            self.container.add_argument(
+                "--auto-tls",
+                dest="auto_tls",
+                action="store_true",
+                help=(
+                    "Create a temporary TLS certificate for local development "
+                    "(requires mkcert or trustme)"
+                ),
+            )
+
+
+class WorkerGroup(Group):
+    name = "Worker"
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        group = self.container.add_mutually_exclusive_group()
+        group.add_argument(
+            "-w",
+            "--workers",
+            dest="workers",
+            type=int,
+            default=1,
+            help="Number of worker processes [default 1]",
+        )
+        group.add_argument(
+            "--fast",
+            dest="fast",
+            action="store_true",
+            help="Set the number of workers to max allowed",
+        )
+        group.add_argument(
+            "--single-process",
+            dest="single",
+            action="store_true",
+            help="Do not use multiprocessing, run server in a single process",
+        )
+        self.add_bool_arguments(
+            "--access-logs",
+            dest="access_log",
+            help="display access logs",
+            default=None,
+        )
+
+
+class OutputGroup(Group):
+    name = "Output"
+
+    def attach(self, short: bool = False):
+        if short:
+            return
+
+        self.add_bool_arguments(
+            "--coffee",
+            dest="coffee",
+            default=False,
+            help="Uhm, coffee?",
+            negative_help="No coffee? Is that a typo?",
+        )
+        self.add_bool_arguments(
+            "--motd",
+            dest="motd",
+            default=True,
+            help="Show the startup display",
+            negative_help="Disable the startup display",
+        )
+        self.container.add_argument(
+            "-v",
+            "--verbosity",
+            action="count",
+            help="Control logging noise, eg. -vv or --verbosity=2 [default 0]",
+        )
+        self.add_bool_arguments(
+            "--noisy-exceptions",
+            dest="noisy_exceptions",
+            help="Output stack traces for all exceptions",
+            default=None,
+        )
+
+
+class DaemonGroup(Group):
+    name = "Daemon"
+
+    def attach(self, short: bool = False):
+        if OS_IS_WINDOWS:
+            return
+
+        self.container.add_argument(
+            "-D",
+            "--daemon",
+            dest="daemon",
+            action="store_true",
+            help="Run server in background (auto-generated PID file)",
+        )
+
+        if short:
+            return
+
+        self.container.add_argument(
+            "--pidfile",
+            dest="pidfile",
+            type=str,
+            default=None,
+            help="Override auto-generated PID file path (requires --daemon)",
+        )
+        self.container.add_argument(
+            "--logfile",
+            dest="logfile",
+            type=str,
+            default=None,
+            help="Path to log file for daemon output (requires --daemon)",
+        )
+        self.container.add_argument(
+            "--user",
+            dest="daemon_user",
+            type=str,
+            default=None,
+            help="User to run daemon as (requires root)",
+        )
+        self.container.add_argument(
+            "--group",
+            dest="daemon_group",
+            type=str,
+            default=None,
+            help="Group to run daemon as (requires root)",
+        )
+
+    def prepare(self, args):
+        if OS_IS_WINDOWS:
+            return
+
+        has_daemon_opts = getattr(args, "pidfile", None) or getattr(
+            args, "logfile", None
+        )
+        if has_daemon_opts and not getattr(args, "daemon", False):
+            raise SystemExit("Error: --pidfile and --logfile require --daemon")
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/base.py b/.venv/lib/python3.12/site-packages/sanic/cli/base.py
new file mode 100644
index 0000000..c933547
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/base.py
@@ -0,0 +1,35 @@
+from argparse import (
+    SUPPRESS,
+    Action,
+    ArgumentParser,
+    RawTextHelpFormatter,
+    _SubParsersAction,
+)
+from typing import Any
+
+
+class SanicArgumentParser(ArgumentParser):
+    def _check_value(self, action: Action, value: Any) -> None:
+        if isinstance(action, SanicSubParsersAction):
+            return
+        super()._check_value(action, value)
+
+
+class SanicHelpFormatter(RawTextHelpFormatter):
+    def add_usage(self, usage, actions, groups, prefix=None):
+        if not usage:
+            usage = SUPPRESS
+            # Add one linebreak, but not two
+            self.add_text("\x1b[1A")
+        super().add_usage(usage, actions, groups, prefix)
+
+
+class SanicSubParsersAction(_SubParsersAction):
+    def __call__(self, parser, namespace, values, option_string=None):
+        self._name_parser_map
+        parser_name = values[0]
+        if parser_name not in self._name_parser_map:
+            self._name_parser_map[parser_name] = parser
+            values = ["", *values]
+
+        super().__call__(parser, namespace, values, option_string)
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/console.py b/.venv/lib/python3.12/site-packages/sanic/cli/console.py
new file mode 100644
index 0000000..f52fa15
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/console.py
@@ -0,0 +1,309 @@
+from __future__ import annotations
+
+import atexit
+import concurrent.futures
+import sys
+import threading
+import time
+import traceback
+
+
+try:
+    import termios
+
+    TERMIOS_AVAILABLE = True
+except ImportError:
+    TERMIOS_AVAILABLE = False
+    termios = None  # type: ignore
+
+from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT
+from asyncio import iscoroutine, new_event_loop
+from code import InteractiveConsole
+from collections.abc import Sequence
+from contextlib import suppress
+from types import FunctionType
+from typing import Any, NamedTuple
+
+import sanic
+
+from sanic import Request, Sanic
+from sanic.compat import Header
+from sanic.helpers import Default
+from sanic.http.constants import Stage
+from sanic.log import Colors
+from sanic.models.ctx_types import REPLContext
+from sanic.models.protocol_types import TransportProtocol
+from sanic.response.types import HTTPResponse
+
+
+try:
+    from httpx import Client
+
+    HTTPX_AVAILABLE = True
+
+    class SanicClient(Client):
+        def __init__(self, app: Sanic):
+            base_url = app.get_server_location(
+                app.state.server_info[0].settings
+            )
+            super().__init__(base_url=base_url)
+
+except ImportError:
+    HTTPX_AVAILABLE = False
+
+try:
+    import readline  # noqa
+except ImportError:
+    print(
+        "Module 'readline' not available. History navigation will be limited.",
+        file=sys.stderr,
+    )
+
+repl_app: Sanic | None = None
+repl_response: HTTPResponse | None = None
+
+
+class REPLProtocol(TransportProtocol):
+    def __init__(self):
+        self.stage = Stage.IDLE
+        self.request_body = True
+
+    def respond(self, response):
+        global repl_response
+        repl_response = response
+        response.stream = self
+        return response
+
+    async def send(self, data, end_stream): ...
+
+
+class Result(NamedTuple):
+    request: Request
+    response: HTTPResponse
+
+
+def make_request(
+    url: str = "/",
+    headers: dict[str, Any] | Sequence[tuple[str, str]] | None = None,
+    method: str = "GET",
+    body: str | None = None,
+):
+    assert repl_app, "No Sanic app has been registered."
+    headers = headers or {}
+    protocol = REPLProtocol()
+    request = Request(  # type: ignore
+        url.encode(),
+        Header(headers),
+        "1.1",
+        method,
+        protocol,
+        repl_app,
+    )
+    if body is not None:
+        request.body = body.encode()
+    request.stream = protocol  # type: ignore
+    request.conn_info = None
+    return request
+
+
+async def respond(request) -> HTTPResponse:
+    assert repl_app, "No Sanic app has been registered."
+    await repl_app.handle_request(request)
+    assert repl_response
+    return repl_response
+
+
+async def do(
+    url: str = "/",
+    headers: dict[str, Any] | Sequence[tuple[str, str]] | None = None,
+    method: str = "GET",
+    body: str | None = None,
+) -> Result:
+    request = make_request(url, headers, method, body)
+    response = await respond(request)
+    return Result(request, response)
+
+
+def _variable_description(name: str, desc: str, type_desc: str) -> str:
+    return (
+        f"  - {Colors.BOLD + Colors.SANIC}{name}{Colors.END}: {desc} - "
+        f"{Colors.BOLD + Colors.BLUE}{type_desc}{Colors.END}"
+    )
+
+
+class SanicREPL(InteractiveConsole):
+    def __init__(self, app: Sanic, start: Default | None = None):
+        global repl_app
+        repl_app = app
+        locals_available = {
+            "app": app,
+            "sanic": sanic,
+            "do": do,
+        }
+
+        user_locals = {
+            user_local.name: user_local.var for user_local in app.repl_ctx
+        }
+
+        client_availability = ""
+        variable_descriptions = [
+            _variable_description(
+                "app", REPLContext.BUILTINS["app"], str(app)
+            ),
+            _variable_description(
+                "sanic", REPLContext.BUILTINS["sanic"], "import sanic"
+            ),
+            _variable_description(
+                "do", REPLContext.BUILTINS["do"], "Result(request, response)"
+            ),
+        ]
+
+        user_locals_descriptions = [
+            _variable_description(
+                user_local.name, user_local.desc, str(type(user_local.var))
+            )
+            for user_local in app.repl_ctx
+        ]
+
+        if HTTPX_AVAILABLE:
+            locals_available["client"] = SanicClient(app)
+            variable_descriptions.append(
+                _variable_description(
+                    "client",
+                    REPLContext.BUILTINS["client"],
+                    "from httpx import Client",
+                ),
+            )
+        else:
+            client_availability = (
+                f"\n{Colors.YELLOW}The HTTP client has been disabled. "
+                "To enable it, install httpx:\n\t"
+                f"pip install httpx{Colors.END}\n"
+            )
+        super().__init__(locals={**locals_available, **user_locals})
+        self.compile.compiler.flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT
+        self.loop = new_event_loop()
+        self._start = start
+        self._pause_event = threading.Event()
+        self._started_event = threading.Event()
+        self._interact_thread = threading.Thread(
+            target=self._console,
+            daemon=True,
+        )
+        self._monitor_thread = threading.Thread(
+            target=self._monitor,
+            daemon=True,
+        )
+        self._async_thread = threading.Thread(
+            target=self.loop.run_forever,
+            daemon=True,
+        )
+        self.app = app
+        self.resume()
+        self.exit_message = "Closing the REPL."
+        self.banner_message = "\n".join(
+            [
+                f"\n{Colors.BOLD}Welcome to the Sanic interactive console{Colors.END}",  # noqa: E501
+                client_availability,
+                "The following objects are available for your convenience:",  # noqa: E501
+                *variable_descriptions,
+            ]
+            + (
+                [
+                    "\nREPL Context:",
+                    *user_locals_descriptions,
+                ]
+                if user_locals_descriptions
+                else []
+            )
+            + [
+                "\nThe async/await keywords are available for use here.",  # noqa: E501
+                f"To exit, press {Colors.BOLD}CTRL+C{Colors.END}, "
+                f"{Colors.BOLD}CTRL+D{Colors.END}, or type {Colors.BOLD}exit(){Colors.END}.\n",  # noqa: E501
+            ]
+        )
+
+    def pause(self):
+        if self.is_paused():
+            return
+        self._pause_event.clear()
+
+    def resume(self):
+        self._pause_event.set()
+
+    def runsource(self, source, filename="", symbol="single"):
+        if source.strip() == "exit()":
+            self._shutdown()
+            return False
+
+        if self.is_paused():
+            print("Console is paused. Please wait for it to be resumed.")
+            return False
+
+        return super().runsource(source, filename, symbol)
+
+    def runcode(self, code):
+        future = concurrent.futures.Future()
+
+        async def callback():
+            func = FunctionType(code, self.locals)
+            try:
+                result = func()
+                if iscoroutine(result):
+                    result = await result
+            except BaseException:
+                traceback.print_exc()
+                result = False
+            future.set_result(result)
+
+        self.loop.call_soon_threadsafe(self.loop.create_task, callback())
+        return future.result()
+
+    def is_paused(self):
+        return not self._pause_event.is_set()
+
+    def _console(self):
+        self._started_event.set()
+        self.interact(banner=self.banner_message, exitmsg=self.exit_message)
+        self._shutdown()
+
+    def _setup_terminal(self):
+        assert termios is not None
+        with suppress(termios.error, AttributeError):
+            fd = sys.stdin.fileno()
+            old_attrs = termios.tcgetattr(fd)
+            atexit.register(
+                termios.tcsetattr, fd, termios.TCSADRAIN, old_attrs
+            )
+
+    def _monitor(self):
+        if isinstance(self._start, Default):
+            if TERMIOS_AVAILABLE and sys.stdin.isatty():
+                self._setup_terminal()
+            enter = f"{Colors.BOLD + Colors.SANIC}ENTER{Colors.END}"
+            start = input(f"\nPress {enter} at anytime to start the REPL.\n\n")
+            if start:
+                return
+        try:
+            while True:
+                if not self._started_event.is_set():
+                    self.app.manager.wait_for_ack()
+                    self._interact_thread.start()
+                elif self.app.manager._all_workers_ack() and self.is_paused():
+                    self.resume()
+                    print(sys.ps1, end="", flush=True)
+                elif (
+                    not self.app.manager._all_workers_ack()
+                    and not self.is_paused()
+                ):
+                    self.pause()
+                time.sleep(0.1)
+        except (ConnectionResetError, BrokenPipeError):
+            pass
+
+    def _shutdown(self):
+        self.app.manager.monitor_publisher.send("__TERMINATE__")
+
+    def run(self):
+        self._monitor_thread.start()
+        self._async_thread.start()
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/daemon.py b/.venv/lib/python3.12/site-packages/sanic/cli/daemon.py
new file mode 100644
index 0000000..061de58
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/daemon.py
@@ -0,0 +1,164 @@
+from __future__ import annotations
+
+import os
+import signal
+import sys
+
+from argparse import ArgumentParser
+from contextlib import suppress
+from pathlib import Path
+
+from sanic.worker.daemon import Daemon
+
+
+def _add_target_args(parser: ArgumentParser) -> None:
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument(
+        "--pid",
+        type=int,
+        metavar="PID",
+        help="Process ID of the daemon",
+    )
+    group.add_argument(
+        "--pidfile",
+        type=str,
+        metavar="PATH",
+        help="Path to PID file of the daemon",
+    )
+
+
+def make_kill_parser(parser: ArgumentParser) -> None:
+    """Kill command always sends SIGKILL."""
+    _add_target_args(parser)
+
+
+def make_status_parser(parser: ArgumentParser) -> None:
+    _add_target_args(parser)
+
+
+def make_restart_parser(parser: ArgumentParser) -> None:
+    _add_target_args(parser)
+
+
+def resolve_target(
+    pid: int | None, pidfile: str | None
+) -> tuple[int, Path | None]:
+    """
+    Resolve a PID from either a direct PID or a pidfile path.
+
+    Returns:
+        Tuple of (pid, pidfile_path or None)
+
+    Raises:
+        SystemExit: If pidfile not found or invalid
+    """
+    if pid:
+        return pid, None
+
+    pidfile_path = Path(pidfile)  # type: ignore[arg-type]
+    if not pidfile_path.exists():
+        print(f"PID file not found: {pidfile_path}", file=sys.stderr)
+        sys.exit(1)
+
+    resolved_pid = Daemon.read_pidfile(pidfile_path)
+    if resolved_pid is None:
+        print(f"Invalid PID file: {pidfile_path}", file=sys.stderr)
+        sys.exit(1)
+
+    return resolved_pid, pidfile_path
+
+
+def _terminate_process(
+    pid: int, sig: signal.Signals, pidfile: Path | None = None
+) -> None:
+    """Send a signal to terminate a process and clean up pidfile."""
+    sig_name = sig.name
+
+    try:
+        os.kill(pid, sig)
+        print(f"Sent {sig_name} to process {pid}")
+    except ProcessLookupError:
+        print(f"Process {pid} not found", file=sys.stderr)
+        sys.exit(1)
+    except PermissionError:
+        print(
+            f"Permission denied to signal process {pid}. "
+            "Are you running as the correct user?",
+            file=sys.stderr,
+        )
+        sys.exit(1)
+    except OSError as e:
+        print(f"Failed to signal process {pid}: {e}", file=sys.stderr)
+        sys.exit(1)
+
+    if pidfile:
+        if pidfile.exists():
+            try:
+                pidfile.unlink()
+                print(f"Removed PID file: {pidfile}")
+            except OSError as e:
+                print(
+                    f"Warning: Could not remove PID file {pidfile}: {e}",
+                    file=sys.stderr,
+                )
+        lockfile = pidfile.with_suffix(".lock")
+        if lockfile.exists():
+            try:
+                lockfile.unlink()
+            except OSError:
+                pass
+
+
+def kill_daemon(pid: int, pidfile: Path | None = None) -> None:
+    """Force kill a daemon process with SIGKILL."""
+    _terminate_process(pid, signal.SIGKILL, pidfile)
+
+
+def stop_daemon(
+    pid: int, pidfile: Path | None = None, force: bool = False
+) -> None:
+    """Stop a daemon process gracefully (SIGTERM) or forcefully (SIGKILL)."""
+    sig = signal.SIGKILL if force else signal.SIGTERM
+    _terminate_process(pid, sig, pidfile)
+
+
+def status_daemon(pid: int, pidfile: Path | None = None) -> bool:
+    """
+    Check if a daemon process is running.
+
+    Args:
+        pid: Process ID to check
+        pidfile: Optional pidfile path to clean up if stale
+
+    Returns:
+        True if running, False otherwise (exits with code 1 if not)
+    """
+    try:
+        os.kill(pid, 0)
+        running = True
+    except ProcessLookupError:
+        running = False
+    except PermissionError:
+        running = True
+
+    if running:
+        print(f"Process {pid} is running")
+        return True
+
+    print(f"Process {pid} is NOT running")
+    if pidfile and pidfile.exists():
+        with suppress(OSError):
+            pidfile.unlink()
+            print(f"Removed stale PID file: {pidfile}")
+    sys.exit(1)
+
+
+def restart_daemon(pid: int) -> None:
+    """
+    Restart a daemon process.
+
+    Args:
+        pid: Process ID to restart (unused, for future use)
+    """
+    print("Restart is not yet implemented. Coming in a future release.")
+    sys.exit(0)
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/executor.py b/.venv/lib/python3.12/site-packages/sanic/cli/executor.py
new file mode 100644
index 0000000..1798355
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/executor.py
@@ -0,0 +1,100 @@
+import shutil
+
+from argparse import ArgumentParser
+from asyncio import run
+from inspect import signature
+from typing import Callable
+
+from sanic import Sanic
+from sanic.application.logo import get_logo
+from sanic.cli.base import (
+    SanicArgumentParser,
+    SanicHelpFormatter,
+)
+
+
+def make_executor_parser(parser: ArgumentParser) -> None:
+    parser.add_argument(
+        "command",
+        help="Command to execute",
+    )
+
+
+class ExecutorSubParser(ArgumentParser):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        if not self.description:
+            self.description = ""
+        self.description = get_logo(True) + self.description
+
+
+class Executor:
+    def __init__(self, app: Sanic, kwargs: dict) -> None:
+        self.app = app
+        self.kwargs = kwargs
+        self.commands = self._make_commands()
+        self.parser = self._make_parser()
+
+    def run(self, command: str, args: list[str]) -> None:
+        if command == "exec":
+            args = ["--help"]
+        parsed_args = self.parser.parse_args(args)
+        if command not in self.commands:
+            raise ValueError(f"Unknown command: {command}")
+        parsed_kwargs = vars(parsed_args)
+        parsed_kwargs.pop("command")
+        run(self.commands[command](**parsed_kwargs))
+
+    def _make_commands(self) -> dict[str, Callable]:
+        commands = {c.name: c.func for c in self.app._future_commands}
+        return commands
+
+    def _make_parser(self) -> SanicArgumentParser:
+        width = shutil.get_terminal_size().columns
+        parser = SanicArgumentParser(
+            prog="sanic",
+            description=get_logo(True),
+            formatter_class=lambda prog: SanicHelpFormatter(
+                prog,
+                max_help_position=36 if width > 96 else 24,
+                indent_increment=4,
+                width=None,
+            ),
+        )
+
+        subparsers = parser.add_subparsers(
+            dest="command",
+            title="  Commands",
+            parser_class=ExecutorSubParser,
+        )
+        for command in self.app._future_commands:
+            sub = subparsers.add_parser(
+                command.name,
+                help=command.func.__doc__ or f"Execute {command.name}",
+                formatter_class=SanicHelpFormatter,
+            )
+            self._add_arguments(sub, command.func)
+
+        return parser
+
+    def _add_arguments(self, parser: ArgumentParser, func: Callable) -> None:
+        sig = signature(func)
+        for param in sig.parameters.values():
+            kwargs = {}
+            if param.default is not param.empty:
+                kwargs["default"] = param.default
+            # In Python 3.14+, argparse validates help strings and rejects
+            # non-string types. Convert annotations to string representation.
+            help_text = None
+            if param.annotation is not param.empty:
+                if isinstance(param.annotation, str):
+                    help_text = param.annotation
+                else:
+                    help_text = getattr(
+                        param.annotation, "__name__", str(param.annotation)
+                    )
+            parser.add_argument(
+                f"--{param.name}",
+                help=help_text,
+                **kwargs,
+            )
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/inspector.py b/.venv/lib/python3.12/site-packages/sanic/cli/inspector.py
new file mode 100644
index 0000000..84c8a4e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/inspector.py
@@ -0,0 +1,105 @@
+from argparse import ArgumentParser
+
+from sanic.application.logo import get_logo
+from sanic.cli.base import SanicHelpFormatter, SanicSubParsersAction
+
+
+def _add_shared(parser: ArgumentParser) -> None:
+    parser.add_argument(
+        "--host",
+        "-H",
+        default="localhost",
+        help="Inspector host address [default 127.0.0.1]",
+    )
+    parser.add_argument(
+        "--port",
+        "-p",
+        default=6457,
+        type=int,
+        help="Inspector port [default 6457]",
+    )
+    parser.add_argument(
+        "--secure",
+        "-s",
+        action="store_true",
+        help="Whether to access the Inspector via TLS encryption",
+    )
+    parser.add_argument("--api-key", "-k", help="Inspector authentication key")
+    parser.add_argument(
+        "--raw",
+        action="store_true",
+        help="Whether to output the raw response information",
+    )
+
+
+class InspectorSubParser(ArgumentParser):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        _add_shared(self)
+        if not self.description:
+            self.description = ""
+        self.description = get_logo(True) + self.description
+
+
+def make_inspector_parser(parser: ArgumentParser) -> None:
+    _add_shared(parser)
+    subparsers = parser.add_subparsers(
+        action=SanicSubParsersAction,
+        dest="action",
+        description=(
+            "Run one or none of the below subcommands. Using inspect without "
+            "a subcommand will fetch general information about the state "
+            "of the application instance.\n\n"
+            "Or, you can optionally follow inspect with a subcommand. "
+            "If you have created a custom "
+            "Inspector instance, then you can run custom commands. See "
+            "https://sanic.dev/en/guide/deployment/inspector.html "
+            "for more details."
+        ),
+        title="  Subcommands",
+        parser_class=InspectorSubParser,
+    )
+    reloader = subparsers.add_parser(
+        "reload",
+        help="Trigger a reload of the server workers",
+        formatter_class=SanicHelpFormatter,
+    )
+    reloader.add_argument(
+        "--zero-downtime",
+        action="store_true",
+        help=(
+            "Whether to wait for the new process to be online before "
+            "terminating the old"
+        ),
+    )
+    subparsers.add_parser(
+        "shutdown",
+        help="Shutdown the application and all processes",
+        formatter_class=SanicHelpFormatter,
+    )
+    scale = subparsers.add_parser(
+        "scale",
+        help="Scale the number of workers",
+        formatter_class=SanicHelpFormatter,
+    )
+    scale.add_argument(
+        "replicas",
+        type=int,
+        help="Number of workers requested",
+    )
+
+    custom = subparsers.add_parser(
+        "",
+        help="Run a custom command",
+        description=(
+            "keyword arguments:\n  When running a custom command, you can "
+            "add keyword arguments by appending them to your command\n\n"
+            "\tsanic inspect foo --one=1 --two=2"
+        ),
+        formatter_class=SanicHelpFormatter,
+    )
+    custom.add_argument(
+        "positional",
+        nargs="*",
+        help="Add one or more non-keyword args to your custom command",
+    )
diff --git a/.venv/lib/python3.12/site-packages/sanic/cli/inspector_client.py b/.venv/lib/python3.12/site-packages/sanic/cli/inspector_client.py
new file mode 100644
index 0000000..01c6c14
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cli/inspector_client.py
@@ -0,0 +1,119 @@
+from __future__ import annotations
+
+import sys
+
+from http.client import RemoteDisconnected
+from textwrap import indent
+from typing import Any
+from urllib.error import URLError
+from urllib.request import Request as URequest
+from urllib.request import urlopen
+
+from sanic.application.logo import get_logo
+from sanic.application.motd import MOTDTTY
+from sanic.log import Colors
+
+
+try:  # no cov
+    from ujson import dumps, loads
+except ModuleNotFoundError:  # no cov
+    from json import dumps, loads  # type: ignore
+
+
+class InspectorClient:
+    def __init__(
+        self,
+        host: str,
+        port: int,
+        secure: bool,
+        raw: bool,
+        api_key: str | None,
+    ) -> None:
+        self.scheme = "https" if secure else "http"
+        self.host = host
+        self.port = port
+        self.raw = raw
+        self.api_key = api_key
+
+        for scheme in ("http", "https"):
+            full = f"{scheme}://"
+            if self.host.startswith(full):
+                self.scheme = scheme
+                self.host = self.host[len(full) :]  # noqa E203
+
+    def do(self, action: str, **kwargs: Any) -> None:
+        if action == "info":
+            self.info()
+            return
+        result = self.request(action, **kwargs).get("result")
+        if result:
+            out = (
+                dumps(result)
+                if isinstance(result, (list, dict))
+                else str(result)
+            )
+            sys.stdout.write(out + "\n")
+
+    def info(self) -> None:
+        out = sys.stdout.write
+        response = self.request("", "GET")
+        if self.raw or not response:
+            return
+        data = response["result"]
+        display = data.pop("info")
+        extra = display.pop("extra", {})
+        display["packages"] = ", ".join(display["packages"])
+        MOTDTTY(get_logo(), self.base_url, display, extra).display(
+            version=False,
+            action="Inspecting",
+            out=out,
+        )
+        for name, info in data["workers"].items():
+            info = "\n".join(
+                f"\t{key}: {Colors.BLUE}{value}{Colors.END}"
+                for key, value in info.items()
+            )
+            out(
+                "\n"
+                + indent(
+                    "\n".join(
+                        [
+                            f"{Colors.BOLD}{Colors.SANIC}{name}{Colors.END}",
+                            info,
+                        ]
+                    ),
+                    "  ",
+                )
+                + "\n"
+            )
+
+    def request(self, action: str, method: str = "POST", **kwargs: Any) -> Any:
+        url = f"{self.base_url}/{action}"
+        params: dict[str, Any] = {"method": method, "headers": {}}
+        if kwargs:
+            params["data"] = dumps(kwargs).encode()
+            params["headers"]["content-type"] = "application/json"
+        if self.api_key:
+            params["headers"]["authorization"] = f"Bearer {self.api_key}"
+        request = URequest(url, **params)
+
+        try:
+            with urlopen(request) as response:  # nosec B310
+                raw = response.read()
+                loaded = loads(raw)
+                if self.raw:
+                    sys.stdout.write(dumps(loaded.get("result")) + "\n")
+                    return {}
+                return loaded
+        except (URLError, RemoteDisconnected) as e:
+            sys.stderr.write(
+                f"{Colors.RED}Could not connect to inspector at: "
+                f"{Colors.YELLOW}{self.base_url}{Colors.END}\n"
+                "Either the application is not running, or it did not start "
+                f"an inspector instance.\n{e}\n"
+            )
+            sys.exit(1)
+
+    @property
+    def base_url(self):
+        return f"{self.scheme}://{self.host}:{self.port}"
diff --git a/.venv/lib/python3.12/site-packages/sanic/compat.py b/.venv/lib/python3.12/site-packages/sanic/compat.py
new file mode 100644
index 0000000..f608909
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/compat.py
@@ -0,0 +1,196 @@
+import asyncio
+import os
+import platform
+import signal
+import sys
+
+from collections.abc import Awaitable
+from contextlib import contextmanager
+from enum import Enum
+from typing import Literal
+
+from multidict import CIMultiDict  # type: ignore
+
+from sanic.helpers import Default
+from sanic.log import error_logger
+
+
+StartMethod = (
+    Default | Literal["fork"] | Literal["forkserver"] | Literal["spawn"]
+)
+
+OS_IS_WINDOWS = os.name == "nt"
+PYPY_IMPLEMENTATION = platform.python_implementation() == "PyPy"
+UVLOOP_INSTALLED = False
+PYTHON_314_OR_LATER = sys.version_info >= (3, 14)
+
+try:
+    import uvloop  # type: ignore # noqa
+
+    UVLOOP_INSTALLED = True
+except ImportError:
+    pass
+
+# Python 3.11 changed the way Enum formatting works for mixed-in types.
+if sys.version_info < (3, 11, 0):
+
+    class StrEnum(str, Enum):
+        pass
+
+else:
+    from enum import StrEnum  # type: ignore # noqa
+
+
+class UpperStrEnum(StrEnum):
+    """Base class for string enums that are case insensitive."""
+
+    def _generate_next_value_(name, start, count, last_values):
+        return name.upper()
+
+    def __eq__(self, value: object) -> bool:
+        value = str(value).upper()
+        return super().__eq__(value)
+
+    def __hash__(self) -> int:
+        return hash(self.value)
+
+    def __str__(self) -> str:
+        return self.value
+
+
+@contextmanager
+def use_context(method: StartMethod):
+    from sanic import Sanic
+
+    orig = Sanic.start_method
+    Sanic.start_method = method
+    yield
+    Sanic.start_method = orig
+
+
+def enable_windows_color_support():
+    import ctypes
+
+    kernel = ctypes.windll.kernel32
+    kernel.SetConsoleMode(kernel.GetStdHandle(-11), 7)
+
+
+def pypy_os_module_patch() -> None:
+    """
+    The PyPy os module is missing the 'readlink' function, which causes issues
+    withaiofiles. This workaround replaces the missing 'readlink' function
+    with 'os.path.realpath', which serves the same purpose.
+    """
+    if hasattr(os, "readlink"):
+        error_logger.debug(
+            "PyPy: Skipping patching of the os module as it appears the "
+            "'readlink' function has been added."
+        )
+        return
+
+    module = sys.modules["os"]
+    module.readlink = os.path.realpath  # type: ignore
+
+
+def pypy_windows_set_console_cp_patch() -> None:
+    """
+    A patch function for PyPy on Windows that sets the console code page to
+    UTF-8 encodingto allow for proper handling of non-ASCII characters. This
+    function uses ctypes to call the Windows API functions SetConsoleCP and
+    SetConsoleOutputCP to set the code page.
+    """
+    from ctypes import windll  # type: ignore
+
+    code: int = windll.kernel32.GetConsoleOutputCP()
+    if code != 65001:
+        windll.kernel32.SetConsoleCP(65001)
+        windll.kernel32.SetConsoleOutputCP(65001)
+
+
+class Header(CIMultiDict):
+    """Container used for both request and response headers.
+    It is a subclass of  [CIMultiDict](https://multidict.readthedocs.io/en/stable/multidict.html#cimultidictproxy)
+
+    It allows for multiple values for a single key in keeping with the HTTP
+    spec. Also, all keys are *case in-sensitive*.
+
+    Please checkout [the MultiDict documentation](https://multidict.readthedocs.io/en/stable/multidict.html#multidict)
+    for more details about how to use the object. In general, it should work
+    very similar to a regular dictionary.
+    """  # noqa: E501
+
+    def __getattr__(self, key: str) -> str:
+        if key.startswith("_"):
+            return self.__getattribute__(key)
+        key = key.rstrip("_").replace("_", "-")
+        return ",".join(self.getall(key, []))
+
+    def get_all(self, key: str):
+        """Convenience method mapped to ``getall()``."""
+        return self.getall(key, [])
+
+
+use_trio = sys.argv[0].endswith("hypercorn") and "trio" in sys.argv
+
+if use_trio:  # pragma: no cover
+    import trio  # type: ignore
+
+    def stat_async(path) -> Awaitable[os.stat_result]:
+        return trio.Path(path).stat()
+
+    open_async = trio.open_file
+    CancelledErrors = tuple([asyncio.CancelledError, trio.Cancelled])
+else:
+    if PYPY_IMPLEMENTATION:
+        pypy_os_module_patch()
+
+        if OS_IS_WINDOWS:
+            pypy_windows_set_console_cp_patch()
+
+    from aiofiles import open as aio_open  # type: ignore
+    from aiofiles.os import stat as stat_async  # type: ignore  # noqa: F401
+
+    async def open_async(file, mode="r", **kwargs):
+        return aio_open(file, mode, **kwargs)
+
+    CancelledErrors = tuple([asyncio.CancelledError])
+
+
+def ctrlc_workaround_for_windows(app):
+    async def stay_active(app):
+        """Asyncio wakeups to allow receiving SIGINT in Python"""
+        while not die:
+            # If someone else stopped the app, just exit
+            if app.state.is_stopping:
+                return
+            # Windows Python blocks signal handlers while the event loop is
+            # waiting for I/O. Frequent wakeups keep interrupts flowing.
+            await asyncio.sleep(0.1)
+        # Can't be called from signal handler, so call it from here
+        app.stop()
+
+    def ctrlc_handler(sig, frame):
+        nonlocal die
+        if die:
+            raise KeyboardInterrupt("Non-graceful Ctrl+C")
+        die = True
+
+    die = False
+    signal.signal(signal.SIGINT, ctrlc_handler)
+    app.add_task(stay_active)
+
+
+def clear_function_annotate(*funcs):
+    """Clear __annotate__ on functions for Python 3.14+ pickle compatibility.
+
+    In Python 3.14, PEP 649 adds __annotate__ to functions with annotations.
+    When methods are used in functools.partial and pickled, the __annotate__
+    function can cause PicklingError because pickle cannot locate it by name.
+
+    This function sets __annotate__ to None on the given functions to avoid
+    pickle issues.
+    """
+    if PYTHON_314_OR_LATER:
+        for func in funcs:
+            if hasattr(func, "__annotate__") and func.__annotate__ is not None:
+                func.__annotate__ = None
diff --git a/.venv/lib/python3.12/site-packages/sanic/config.py b/.venv/lib/python3.12/site-packages/sanic/config.py
new file mode 100644
index 0000000..270017b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/config.py
@@ -0,0 +1,495 @@
+from __future__ import annotations
+
+from abc import ABC, ABCMeta, abstractmethod
+from collections.abc import Sequence
+from inspect import getmembers, isclass, isdatadescriptor
+from os import environ
+from pathlib import Path
+from typing import Any, Callable, Literal
+from warnings import filterwarnings
+
+from sanic.constants import LocalCertCreator
+from sanic.errorpages import DEFAULT_FORMAT, check_error_format
+from sanic.helpers import Default, _default
+from sanic.http import Http
+from sanic.log import error_logger
+from sanic.utils import load_module_from_file_location, str_to_bool
+
+
+FilterWarningType = (
+    Literal["default"]
+    | Literal["error"]
+    | Literal["ignore"]
+    | Literal["always"]
+    | Literal["module"]
+    | Literal["once"]
+)
+
+SANIC_PREFIX = "SANIC_"
+
+
+DEFAULT_CONFIG = {
+    "_FALLBACK_ERROR_FORMAT": _default,
+    "ACCESS_LOG": False,
+    "AUTO_EXTEND": True,
+    "AUTO_RELOAD": False,
+    "EVENT_AUTOREGISTER": False,
+    "DEPRECATION_FILTER": "once",
+    "FORWARDED_FOR_HEADER": "X-Forwarded-For",
+    "FORWARDED_SECRET": None,
+    "GRACEFUL_SHUTDOWN_TIMEOUT": 15.0,
+    "GRACEFUL_TCP_CLOSE_TIMEOUT": 5.0,
+    "INSPECTOR": False,
+    "INSPECTOR_HOST": "localhost",
+    "INSPECTOR_PORT": 6457,
+    "INSPECTOR_TLS_KEY": _default,
+    "INSPECTOR_TLS_CERT": _default,
+    "INSPECTOR_API_KEY": "",
+    "KEEP_ALIVE_TIMEOUT": 120,
+    "KEEP_ALIVE": True,
+    "LOCAL_CERT_CREATOR": LocalCertCreator.AUTO,
+    "LOCAL_TLS_KEY": _default,
+    "LOCAL_TLS_CERT": _default,
+    "LOCALHOST": "localhost",
+    "LOG_EXTRA": _default,
+    "MOTD": True,
+    "MOTD_DISPLAY": {},
+    "NO_COLOR": False,
+    "NOISY_EXCEPTIONS": False,
+    "PROXIES_COUNT": None,
+    "REAL_IP_HEADER": None,
+    "REQUEST_BUFFER_SIZE": 65536,
+    "REQUEST_MAX_HEADER_SIZE": 8192,  # Cannot exceed 16384
+    "REQUEST_ID_HEADER": "X-Request-ID",
+    "REQUEST_MAX_SIZE": 100_000_000,
+    "REQUEST_TIMEOUT": 60,
+    "RESPONSE_TIMEOUT": 60,
+    "TLS_CERT_PASSWORD": "",
+    "TOUCHUP": _default,
+    "USE_UVLOOP": _default,
+    "WEBSOCKET_MAX_SIZE": 2**20,  # 1 MiB
+    "WEBSOCKET_PING_INTERVAL": 20,
+    "WEBSOCKET_PING_TIMEOUT": 20,
+}
+
+
+class DescriptorMeta(ABCMeta):
+    """Metaclass for Config."""
+
+    def __init__(cls, *_):
+        cls.__setters__ = {name for name, _ in getmembers(cls, cls._is_setter)}
+
+    @staticmethod
+    def _is_setter(member: object):
+        return isdatadescriptor(member) and hasattr(member, "setter")
+
+
+class DetailedConverter(ABC):
+    """Base class for detailed converters that need additional context.
+
+    DetailedConverter provides access to the full environment variable key,
+    the raw value, and the current config defaults. This allows for more
+    sophisticated conversion logic that can take into account the variable
+    name pattern, perform validation, or use default values for fallback.
+
+    Examples:
+        ```python
+        # Example of a converter that casts values to the type of the default
+        class DefaultsCastConverter(DetailedConverter):
+            def __call__(self, full_key: str, config_key: str, value: str,
+                                                        defaults: dict) -> Any:
+                try:
+                    if config_key in defaults:
+                        return type(defaults[config_key])(value)
+                except (ValueError, TypeError):
+                    raise ValueError
+        ```
+    """
+
+    @abstractmethod
+    def __call__(
+        self, full_key: str, config_key: str, value: str, defaults: dict
+    ) -> Any:
+        """Convert an environment variable to a Python value.
+
+        Args:
+            full_key: The full environment variable name (with prefix)
+                            (e.g., "SANIC_DATABASE_URL")
+            config_key: The environment variable name (without prefix)
+                            (e.g., "DATABASE_URL")
+            value: The raw string value from the environment
+            defaults: The current default configuration values
+
+        Returns:
+            The converted Python value
+
+        Raises:
+            ValueError: If the value cannot be converted by this converter
+        """
+
+
+class Config(dict, metaclass=DescriptorMeta):
+    """Configuration object for Sanic.
+
+    You can use this object to both: (1) configure how Sanic will operate, and
+    (2) manage your application's custom configuration values.
+    """
+
+    ACCESS_LOG: bool
+    AUTO_EXTEND: bool
+    AUTO_RELOAD: bool
+    EVENT_AUTOREGISTER: bool
+    DEPRECATION_FILTER: FilterWarningType
+    FORWARDED_FOR_HEADER: str
+    FORWARDED_SECRET: str | None
+    GRACEFUL_SHUTDOWN_TIMEOUT: float
+    GRACEFUL_TCP_CLOSE_TIMEOUT: float
+    INSPECTOR: bool
+    INSPECTOR_HOST: str
+    INSPECTOR_PORT: int
+    INSPECTOR_TLS_KEY: Path | str | Default
+    INSPECTOR_TLS_CERT: Path | str | Default
+    INSPECTOR_API_KEY: str
+    KEEP_ALIVE_TIMEOUT: int
+    KEEP_ALIVE: bool
+    LOCAL_CERT_CREATOR: str | LocalCertCreator
+    LOCAL_TLS_KEY: Path | str | Default
+    LOCAL_TLS_CERT: Path | str | Default
+    LOCALHOST: str
+    LOG_EXTRA: Default | bool
+    MOTD: bool
+    MOTD_DISPLAY: dict[str, str]
+    NO_COLOR: bool
+    NOISY_EXCEPTIONS: bool
+    PROXIES_COUNT: int | None
+    REAL_IP_HEADER: str | None
+    REQUEST_BUFFER_SIZE: int
+    REQUEST_MAX_HEADER_SIZE: int
+    REQUEST_ID_HEADER: str
+    REQUEST_MAX_SIZE: int
+    REQUEST_TIMEOUT: int
+    RESPONSE_TIMEOUT: int
+    SERVER_NAME: str
+    TLS_CERT_PASSWORD: str
+    TOUCHUP: Default | bool
+    USE_UVLOOP: Default | bool
+    WEBSOCKET_MAX_SIZE: int
+    WEBSOCKET_PING_INTERVAL: int
+    WEBSOCKET_PING_TIMEOUT: int
+
+    def __init__(
+        self,
+        defaults: dict[str, str | bool | int | float | None] | None = None,
+        env_prefix: str | None = SANIC_PREFIX,
+        keep_alive: bool | None = None,
+        *,
+        converters: Sequence[Callable[[str], Any]] | None = None,
+    ):
+        defaults = defaults or {}
+        self.defaults = {**DEFAULT_CONFIG, **defaults}
+        super().__init__(self.defaults)
+        self._configure_warnings()
+
+        self._converters = [str, str_to_bool, float, int]
+
+        if converters:
+            for converter in converters:
+                self.register_type(converter)
+
+        if keep_alive is not None:
+            self.KEEP_ALIVE = keep_alive
+
+        if env_prefix != SANIC_PREFIX:
+            if env_prefix:
+                self.load_environment_vars(env_prefix)
+        else:
+            self.load_environment_vars(SANIC_PREFIX)
+
+        self._configure_header_size()
+        self._check_error_format()
+        self._init = True
+
+    def __getattr__(self, attr: Any):
+        try:
+            return self[attr]
+        except KeyError as ke:
+            raise AttributeError(f"Config has no '{ke.args[0]}'")
+
+    def __setattr__(self, attr: str, value: Any) -> None:
+        self.update({attr: value})
+
+    def __setitem__(self, attr: str, value: Any) -> None:
+        self.update({attr: value})
+
+    def update(self, *other: Any, **kwargs: Any) -> None:
+        """Update the config with new values.
+
+        This method will update the config with the values from the provided
+        `other` objects, and then update the config with the provided
+        `kwargs`. The `other` objects can be any object that can be converted
+        to a dictionary, such as a `dict`, `Config` object, or `str` path to a
+        Python file. The `kwargs` must be a dictionary of key-value pairs.
+
+        .. note::
+            Only upper case settings are considered
+
+        Args:
+            *other: Any number of objects that can be converted to a
+                dictionary.
+            **kwargs: Any number of key-value pairs.
+
+        Raises:
+            AttributeError: If a key is not in the config.
+
+        Examples:
+            ```python
+            config.update(
+                {"A": 1, "B": 2},
+                {"C": 3, "D": 4},
+                E=5,
+                F=6,
+            )
+            ```
+        """
+        kwargs.update({k: v for item in other for k, v in dict(item).items()})
+        setters: dict[str, Any] = {
+            k: kwargs.pop(k)
+            for k in {**kwargs}.keys()
+            if k in self.__class__.__setters__
+        }
+
+        for key, value in setters.items():
+            try:
+                super().__setattr__(key, value)
+            except AttributeError:
+                ...
+
+        super().update(**kwargs)
+        for attr, value in {**setters, **kwargs}.items():
+            self._post_set(attr, value)
+
+    def _post_set(self, attr, value) -> None:
+        if self.get("_init"):
+            if attr in (
+                "REQUEST_MAX_HEADER_SIZE",
+                "REQUEST_BUFFER_SIZE",
+                "REQUEST_MAX_SIZE",
+            ):
+                self._configure_header_size()
+
+        if attr == "LOCAL_CERT_CREATOR" and not isinstance(
+            self.LOCAL_CERT_CREATOR, LocalCertCreator
+        ):
+            self.LOCAL_CERT_CREATOR = LocalCertCreator[
+                self.LOCAL_CERT_CREATOR.upper()
+            ]
+        elif attr == "DEPRECATION_FILTER":
+            self._configure_warnings()
+
+    @property
+    def FALLBACK_ERROR_FORMAT(self) -> str:
+        if isinstance(self._FALLBACK_ERROR_FORMAT, Default):
+            return DEFAULT_FORMAT
+        return self._FALLBACK_ERROR_FORMAT
+
+    @FALLBACK_ERROR_FORMAT.setter
+    def FALLBACK_ERROR_FORMAT(self, value):
+        self._check_error_format(value)
+        if (
+            not isinstance(self._FALLBACK_ERROR_FORMAT, Default)
+            and value != self._FALLBACK_ERROR_FORMAT
+        ):
+            error_logger.warning(
+                "Setting config.FALLBACK_ERROR_FORMAT on an already "
+                "configured value may have unintended consequences."
+            )
+        self._FALLBACK_ERROR_FORMAT = value
+
+    def _configure_header_size(self):
+        Http.set_header_max_size(
+            self.REQUEST_MAX_HEADER_SIZE,
+            self.REQUEST_BUFFER_SIZE - 4096,
+            self.REQUEST_MAX_SIZE,
+        )
+
+    def _configure_warnings(self):
+        filterwarnings(
+            self.DEPRECATION_FILTER,
+            category=DeprecationWarning,
+            module=r"sanic.*",
+        )
+
+    def _check_error_format(self, format: str | None = None):
+        check_error_format(format or self.FALLBACK_ERROR_FORMAT)
+
+    def load_environment_vars(self, prefix=SANIC_PREFIX):
+        """Load environment variables into the config.
+
+        Looks for prefixed environment variables and applies them to the
+        configuration if present. This is called automatically when Sanic
+        starts up to load environment variables into config. Environment
+        variables should start with the defined prefix and should only
+        contain uppercase letters.
+
+        It will automatically hydrate the following types:
+
+        - ``int``
+        - ``float``
+        - ``bool``
+
+        Anything else will be imported as a ``str``. If you would like to add
+        additional types to this list, you can use
+        :meth:`sanic.config.Config.register_type`. Just make sure that they
+        are registered before you instantiate your application.
+
+        You likely won't need to call this method directly.
+
+        See [Configuration](/en/guide/deployment/configuration) for more details.
+
+        Args:
+            prefix (str): The prefix to use when looking for environment
+                variables. Defaults to `SANIC_`.
+
+
+        Examples:
+            ```python
+            # Environment variables
+            # SANIC_SERVER_NAME=example.com
+            # SANIC_SERVER_PORT=9999
+            # SANIC_SERVER_AUTORELOAD=true
+
+            # Python
+            app.config.load_environment_vars()
+            ```
+        """  # noqa: E501
+        for key, value in environ.items():
+            if not key.startswith(prefix) or not key.isupper():
+                continue
+
+            _, config_key = key.split(prefix, 1)
+
+            for converter in reversed(self._converters):
+                try:
+                    if isinstance(converter, DetailedConverter):
+                        self[config_key] = converter(
+                            key, config_key, value, self.defaults
+                        )
+                    else:
+                        self[config_key] = converter(value)
+                    break
+                except ValueError:
+                    pass
+
+    def update_config(self, config: bytes | str | dict[str, Any] | Any):
+        """Update app.config.
+
+        .. note::
+
+            Only upper case settings are considered
+
+        See [Configuration](/en/guide/deployment/configuration) for more details.
+
+        Args:
+            config (Union[bytes, str, dict, Any]): Path to py file holding
+                settings, dict holding settings, or any object holding
+                settings.
+
+        Examples:
+            You can upload app config by providing path to py file
+            holding settings.
+
+            ```python
+            # /some/py/file
+            A = 1
+            B = 2
+            ```
+
+            ```python
+            config.update_config("${some}/py/file")
+            ```
+
+            Yes you can put environment variable here, but they must be provided
+            in format: ``${some_env_var}``, and mark that ``$some_env_var`` is
+            treated as plain string.
+
+            You can upload app config by providing dict holding settings.
+
+            ```python
+            d = {"A": 1, "B": 2}
+            config.update_config(d)
+            ```
+
+            You can upload app config by providing any object holding settings,
+            but in such case config.__dict__ will be used as dict holding settings.
+
+            ```python
+            class C:
+                A = 1
+                B = 2
+
+            config.update_config(C)
+            ```
+        """  # noqa: E501
+        if isinstance(config, (bytes, str, Path)):
+            config = load_module_from_file_location(location=config)
+
+        if not isinstance(config, dict):
+            cfg = {}
+            if not isclass(config):
+                cfg.update(
+                    {
+                        key: getattr(config, key)
+                        for key in config.__class__.__dict__.keys()
+                    }
+                )
+
+            config = dict(config.__dict__)
+            config.update(cfg)
+
+        config = dict(filter(lambda i: i[0].isupper(), config.items()))
+
+        self.update(config)
+
+    load = update_config
+
+    def register_type(self, converter: Callable[[str], Any]) -> None:
+        """Register a custom type converter.
+
+        Allows for adding custom function to cast from a string value to any
+        other type. The function should raise ValueError if it is not the
+        correct type.
+
+        Args:
+            converter (Callable[[str], Any]): A function that takes a string
+            and returns a value of any type, or a DetailedConverter instance.
+
+        Examples:
+            ```python
+            def my_converter(value: str) -> Any:
+                # Do something to convert the value
+                return value
+
+            config.register_type(my_converter)
+
+            # Or use a DetailedConverter for more context
+            # Example of a converter that casts values to
+            # the type of the default
+            class DefaultsCastConverter(DetailedConverter):
+                def __call__(self, full_key: str, config_key: str, value: str,
+                                                    defaults: dict) -> Any:
+                    try:
+                        if config_key in defaults:
+                            return type(defaults[config_key])(value)
+                    except (ValueError, TypeError):
+                        raise ValueError
+
+            config.register_type(DefaultsCastConverter())
+            ```
+        """
+        if converter in self._converters:
+            error_logger.warning(
+                f"Configuration value converter '{converter.__name__}' has "
+                "already been registered"
+            )
+            return
+        self._converters.append(converter)
diff --git a/.venv/lib/python3.12/site-packages/sanic/constants.py b/.venv/lib/python3.12/site-packages/sanic/constants.py
new file mode 100644
index 0000000..4c82144
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/constants.py
@@ -0,0 +1,38 @@
+from enum import auto
+
+from sanic.compat import UpperStrEnum
+
+
+class HTTPMethod(UpperStrEnum):
+    """HTTP methods that are commonly used."""
+
+    GET = auto()
+    POST = auto()
+    PUT = auto()
+    HEAD = auto()
+    OPTIONS = auto()
+    PATCH = auto()
+    DELETE = auto()
+
+
+class LocalCertCreator(UpperStrEnum):
+    """Local certificate creator."""
+
+    AUTO = auto()
+    TRUSTME = auto()
+    MKCERT = auto()
+
+
+HTTP_METHODS = tuple(HTTPMethod.__members__.values())
+SAFE_HTTP_METHODS = (HTTPMethod.GET, HTTPMethod.HEAD, HTTPMethod.OPTIONS)
+IDEMPOTENT_HTTP_METHODS = (
+    HTTPMethod.GET,
+    HTTPMethod.HEAD,
+    HTTPMethod.PUT,
+    HTTPMethod.DELETE,
+    HTTPMethod.OPTIONS,
+)
+CACHEABLE_HTTP_METHODS = (HTTPMethod.GET, HTTPMethod.HEAD)
+DEFAULT_HTTP_CONTENT_TYPE = "application/octet-stream"
+DEFAULT_LOCAL_TLS_KEY = "key.pem"
+DEFAULT_LOCAL_TLS_CERT = "cert.pem"
diff --git a/.venv/lib/python3.12/site-packages/sanic/cookies/__init__.py b/.venv/lib/python3.12/site-packages/sanic/cookies/__init__.py
new file mode 100644
index 0000000..a5cbe46
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cookies/__init__.py
@@ -0,0 +1,4 @@
+from .response import Cookie, CookieJar
+
+
+__all__ = ("Cookie", "CookieJar")
diff --git a/.venv/lib/python3.12/site-packages/sanic/cookies/request.py b/.venv/lib/python3.12/site-packages/sanic/cookies/request.py
new file mode 100644
index 0000000..f98eb45
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cookies/request.py
@@ -0,0 +1,159 @@
+import re
+
+from typing import Any
+
+from sanic.cookies.response import Cookie
+from sanic.request.parameters import RequestParameters
+
+
+COOKIE_NAME_RESERVED_CHARS = re.compile(
+    '[\x00-\x1f\x7f-\xff()<>@,;:\\\\"/[\\]?={} \x09]'
+)
+OCTAL_PATTERN = re.compile(r"\\[0-3][0-7][0-7]")
+QUOTE_PATTERN = re.compile(r"[\\].")
+
+
+def _unquote(str):  # no cov
+    if str is None or len(str) < 2:
+        return str
+    if str[0] != '"' or str[-1] != '"':
+        return str
+
+    str = str[1:-1]
+
+    i = 0
+    n = len(str)
+    res = []
+    while 0 <= i < n:
+        o_match = OCTAL_PATTERN.search(str, i)
+        q_match = QUOTE_PATTERN.search(str, i)
+        if not o_match and not q_match:
+            res.append(str[i:])
+            break
+        # else:
+        j = k = -1
+        if o_match:
+            j = o_match.start(0)
+        if q_match:
+            k = q_match.start(0)
+        if q_match and (not o_match or k < j):
+            res.append(str[i:k])
+            res.append(str[k + 1])
+            i = k + 2
+        else:
+            res.append(str[i:j])
+            res.append(chr(int(str[j + 1 : j + 4], 8)))  # noqa: E203
+            i = j + 4
+    return "".join(res)
+
+
+def parse_cookie(raw: str) -> dict[str, list[str]]:
+    """Parses a raw cookie string into a dictionary.
+
+    The function takes a raw cookie string (usually from HTTP headers) and
+    returns a dictionary where each key is a cookie name and the value is a
+    list of values for that cookie. The function handles quoted values and
+    skips invalid cookie names.
+
+    Args:
+        raw (str): The raw cookie string to be parsed.
+
+    Returns:
+        Dict[str, List[str]]: A dictionary containing the cookie names as keys
+        and a list of values for each cookie.
+
+    Example:
+        ```python
+        raw = 'name1=value1; name2="value2"; name3=value3'
+        cookies = parse_cookie(raw)
+        # cookies will be {'name1': ['value1'], 'name2': ['value2'], 'name3': ['value3']}
+        ```
+    """  # noqa: E501
+    cookies: dict[str, list[str]] = {}
+
+    for token in raw.split(";"):
+        name, sep, value = token.partition("=")
+        name = name.strip()
+        value = value.strip()
+
+        # Support cookies =value or plain value with no name
+        # https://github.com/httpwg/http-extensions/issues/159
+        if not sep:
+            if not name:
+                # Empty value like ;; or a cookie header with no value
+                continue
+            name, value = "", name
+
+        if COOKIE_NAME_RESERVED_CHARS.search(name):  # no cov
+            continue
+
+        if len(value) > 2 and value[0] == '"' and value[-1] == '"':  # no cov
+            value = _unquote(value)
+
+        if name in cookies:
+            cookies[name].append(value)
+        else:
+            cookies[name] = [value]
+
+    return cookies
+
+
+class CookieRequestParameters(RequestParameters):
+    """A container for accessing single and multiple cookie values.
+
+    Because the HTTP standard allows for multiple cookies with the same name,
+    a standard dictionary cannot be used to access cookie values. This class
+    provides a way to access cookie values in a way that is similar to a
+    dictionary, but also allows for accessing multiple values for a single
+    cookie name when necessary.
+
+    Args:
+        cookies (Dict[str, List[str]]): A dictionary containing the cookie
+            names as keys and a list of values for each cookie.
+
+    Example:
+        ```python
+        raw = 'name1=value1; name2="value2"; name3=value3'
+        cookies = parse_cookie(raw)
+        # cookies will be {'name1': ['value1'], 'name2': ['value2'], 'name3': ['value3']}
+
+        request_cookies = CookieRequestParameters(cookies)
+        request_cookies['name1']  # 'value1'
+        request_cookies.get('name1')  # 'value1'
+        request_cookies.getlist('name1')  # ['value1']
+        ```
+    """  # noqa: E501
+
+    def __getitem__(self, key: str) -> str | None:
+        try:
+            value = self._get_prefixed_cookie(key)
+        except KeyError:
+            value = super().__getitem__(key)
+        return value
+
+    def __getattr__(self, key: str) -> str:
+        if key.startswith("_"):
+            return self.__getattribute__(key)
+        key = key.rstrip("_").replace("_", "-")
+        return str(self.get(key, ""))
+
+    def get(self, name: str, default: Any | None = None) -> Any | None:
+        try:
+            return self._get_prefixed_cookie(name)[0]
+        except KeyError:
+            return super().get(name, default)
+
+    def getlist(
+        self, name: str, default: list[Any] | None = None
+    ) -> list[Any]:
+        try:
+            return self._get_prefixed_cookie(name)
+        except KeyError:
+            return super().getlist(name, default)
+
+    def _get_prefixed_cookie(self, name: str) -> Any:
+        getitem = super().__getitem__
+        try:
+            return getitem(f"{Cookie.HOST_PREFIX}{name}")
+        except KeyError:
+            return getitem(f"{Cookie.SECURE_PREFIX}{name}")
diff --git a/.venv/lib/python3.12/site-packages/sanic/cookies/response.py b/.venv/lib/python3.12/site-packages/sanic/cookies/response.py
new file mode 100644
index 0000000..38dba02
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/cookies/response.py
@@ -0,0 +1,607 @@
+from __future__ import annotations
+
+import re
+import string
+
+from datetime import datetime
+from typing import TYPE_CHECKING, Literal, cast
+
+from sanic.exceptions import ServerError
+
+
+if TYPE_CHECKING:
+    from sanic.compat import Header
+
+
+SameSite = (
+    Literal["Strict"]
+    | Literal["Lax"]
+    | Literal["None"]
+    | Literal["strict"]
+    | Literal["lax"]
+    | Literal["none"]
+)
+
+DEFAULT_MAX_AGE = 0
+SAMESITE_VALUES = ("strict", "lax", "none")
+
+LEGAL_CHARS = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:"
+UNESCAPED_CHARS = LEGAL_CHARS + " ()/<=>?@[]{}"
+TRANSLATOR = {ch: f"\\{ch:03o}" for ch in bytes(range(32)) + b'";\\\x7f'}
+
+
+def _quote(str):  # no cov
+    r"""Quote a string for use in a cookie header.
+    If the string does not need to be double-quoted, then just return the
+    string.  Otherwise, surround the string in doublequotes and quote
+    (with a \) special characters.
+    """
+    if str is None or _is_legal_key(str):
+        return str
+    else:
+        return f'"{str.translate(TRANSLATOR)}"'
+
+
+_is_legal_key = re.compile("[%s]+" % re.escape(LEGAL_CHARS)).fullmatch
+
+
+class CookieJar:
+    """A container to manipulate cookies.
+
+    CookieJar dynamically writes headers as cookies are added and removed
+    It gets around the limitation of one header per name by using the
+    MultiHeader class to provide a unique key that encodes to Set-Cookie.
+
+    Args:
+        headers (Header): The headers object to write cookies to.
+    """
+
+    HEADER_KEY = "Set-Cookie"
+
+    def __init__(self, headers: Header):
+        self.headers = headers
+
+    def __len__(self):  # no cov
+        return len(self.cookies)
+
+    @property
+    def cookies(self) -> list[Cookie]:
+        """A list of cookies in the CookieJar.
+
+        Returns:
+            List[Cookie]: A list of cookies in the CookieJar.
+        """
+        return self.headers.getall(self.HEADER_KEY, [])
+
+    def get_cookie(
+        self,
+        key: str,
+        path: str = "/",
+        domain: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> Cookie | None:
+        """Fetch a cookie from the CookieJar.
+
+        Args:
+            key (str): The key of the cookie to fetch.
+            path (str, optional): The path of the cookie. Defaults to `"/"`.
+            domain (Optional[str], optional): The domain of the cookie.
+                Defaults to `None`.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key.
+                This requires that path="/", domain=None, and secure=True.
+                Defaults to `False`.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key.
+                This requires that secure=True. Defaults to `False`.
+
+        Returns:
+            Optional[Cookie]: The cookie if it exists, otherwise `None`.
+        """  # noqa: E501
+        for cookie in self.cookies:
+            if (
+                cookie.key == Cookie.make_key(key, host_prefix, secure_prefix)
+                and cookie.path == path
+                and cookie.domain == domain
+            ):
+                return cookie
+        return None
+
+    def has_cookie(
+        self,
+        key: str,
+        path: str = "/",
+        domain: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> bool:
+        """Check if a cookie exists in the CookieJar.
+
+        Args:
+            key (str): The key of the cookie to check.
+            path (str, optional): The path of the cookie. Defaults to `"/"`.
+            domain (Optional[str], optional): The domain of the cookie.
+                Defaults to `None`.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key.
+                This requires that path="/", domain=None, and secure=True.
+                Defaults to `False`.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key.
+                This requires that secure=True. Defaults to `False`.
+
+        Returns:
+            bool: Whether the cookie exists.
+        """  # noqa: E501
+        for cookie in self.cookies:
+            if (
+                cookie.key == Cookie.make_key(key, host_prefix, secure_prefix)
+                and cookie.path == path
+                and cookie.domain == domain
+            ):
+                return True
+        return False
+
+    def add_cookie(
+        self,
+        key: str,
+        value: str,
+        *,
+        path: str = "/",
+        domain: str | None = None,
+        secure: bool = True,
+        max_age: int | None = None,
+        expires: datetime | None = None,
+        httponly: bool = False,
+        samesite: SameSite | None = "Lax",
+        partitioned: bool = False,
+        comment: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> Cookie:
+        """Add a cookie to the CookieJar.
+
+        Args:
+            key (str): Key of the cookie.
+            value (str): Value of the cookie.
+            path (str, optional): Path of the cookie. Defaults to "/".
+            domain (Optional[str], optional): Domain of the cookie. Defaults to None.
+            secure (bool, optional): Whether to set it as a secure cookie. Defaults to True.
+            max_age (Optional[int], optional): Max age of the cookie in seconds; if set to 0 a
+                browser should delete it. Defaults to None.
+            expires (Optional[datetime], optional): When the cookie expires; if set to None browsers
+                should set it as a session cookie. Defaults to None.
+            httponly (bool, optional): Whether to set it as HTTP only. Defaults to False.
+            samesite (Optional[SameSite], optional): How to set the samesite property, should be
+                strict, lax, or none (case insensitive). Defaults to "Lax".
+            partitioned (bool, optional): Whether to set it as partitioned. Defaults to False.
+            comment (Optional[str], optional): A cookie comment. Defaults to None.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key.
+                This requires that path="/", domain=None, and secure=True. Defaults to False.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key.
+                This requires that secure=True. Defaults to False.
+
+        Returns:
+            Cookie: The instance of the created cookie.
+
+        Raises:
+            ServerError: If host_prefix is set without secure=True.
+            ServerError: If host_prefix is set without path="/" and domain=None.
+            ServerError: If host_prefix is set with domain.
+            ServerError: If secure_prefix is set without secure=True.
+            ServerError: If partitioned is set without host_prefix=True.
+
+        Examples:
+            Basic usage
+            ```python
+            cookie = add_cookie('name', 'value')
+            ```
+
+            Adding a cookie with a custom path and domain
+            ```python
+            cookie = add_cookie('name', 'value', path='/custom', domain='example.com')
+            ```
+
+            Adding a secure, HTTP-only cookie with a comment
+            ```python
+            cookie = add_cookie('name', 'value', secure=True, httponly=True, comment='My Cookie')
+            ```
+
+            Adding a cookie with a max age of 60 seconds
+            ```python
+            cookie = add_cookie('name', 'value', max_age=60)
+            ```
+        """  # noqa: E501
+        cookie = Cookie(
+            key,
+            value,
+            path=path,
+            expires=expires,
+            comment=comment,
+            domain=domain,
+            max_age=max_age,
+            secure=secure,
+            httponly=httponly,
+            samesite=samesite,
+            partitioned=partitioned,
+            host_prefix=host_prefix,
+            secure_prefix=secure_prefix,
+        )
+        self.headers.add(self.HEADER_KEY, cookie)
+
+        return cookie
+
+    def delete_cookie(
+        self,
+        key: str,
+        *,
+        path: str = "/",
+        domain: str | None = None,
+        secure: bool = True,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> None:
+        """
+        Delete a cookie
+
+        This will effectively set it as Max-Age: 0, which a browser should
+        interpret it to mean: "delete the cookie".
+
+        Since it is a browser/client implementation, your results may vary
+        depending upon which client is being used.
+
+        :param key: The key to be deleted
+        :type key: str
+        :param path: Path of the cookie, defaults to None
+        :type path: Optional[str], optional
+        :param domain: Domain of the cookie, defaults to None
+        :type domain: Optional[str], optional
+        :param secure: Whether to delete a secure cookie. Defaults to True.
+        :param secure: bool
+        :param host_prefix: Whether to add __Host- as a prefix to the key.
+            This requires that path="/", domain=None, and secure=True,
+            defaults to False
+        :type host_prefix: bool
+        :param secure_prefix: Whether to add __Secure- as a prefix to the key.
+            This requires that secure=True, defaults to False
+        :type secure_prefix: bool
+        """
+        if host_prefix and not (secure and path == "/" and domain is None):
+            raise ServerError(
+                "Cannot set host_prefix on a cookie without "
+                "path='/', domain=None, and secure=True"
+            )
+        if secure_prefix and not secure:
+            raise ServerError(
+                "Cannot set secure_prefix on a cookie without secure=True"
+            )
+
+        cookies: list[Cookie] = self.headers.popall(self.HEADER_KEY, [])
+        existing_cookie = None
+        for cookie in cookies:
+            if (
+                cookie.key != Cookie.make_key(key, host_prefix, secure_prefix)
+                or cookie.path != path
+                or cookie.domain != domain
+            ):
+                self.headers.add(self.HEADER_KEY, cookie)
+            elif existing_cookie is None:
+                existing_cookie = cookie
+
+        if existing_cookie is not None:
+            # Use all the same values as the cookie to be deleted
+            # except value="" and max_age=0
+            self.add_cookie(
+                key=key,
+                value="",
+                path=existing_cookie.path,
+                domain=existing_cookie.domain,
+                secure=existing_cookie.secure,
+                max_age=0,
+                httponly=existing_cookie.httponly,
+                partitioned=existing_cookie.partitioned,
+                samesite=existing_cookie.samesite,
+                host_prefix=host_prefix,
+                secure_prefix=secure_prefix,
+            )
+        else:
+            self.add_cookie(
+                key=key,
+                value="",
+                path=path,
+                domain=domain,
+                secure=secure,
+                max_age=0,
+                samesite=None,
+                host_prefix=host_prefix,
+                secure_prefix=secure_prefix,
+            )
+
+
+class Cookie:
+    """A representation of a HTTP cookie, providing an interface to manipulate cookie attributes intended for a response.
+
+    This class is a simplified representation of a cookie, similar to the Morsel SimpleCookie in Python's standard library.
+    It allows the manipulation of various cookie attributes including path, domain, security settings, and others.
+
+    Several "smart defaults" are provided to make it easier to create cookies that are secure by default. These include:
+
+    - Setting the `secure` flag to `True` by default
+    - Setting the `samesite` flag to `Lax` by default
+
+    Args:
+        key (str): The key (name) of the cookie.
+        value (str): The value of the cookie.
+        path (str, optional): The path for the cookie. Defaults to "/".
+        domain (Optional[str], optional): The domain for the cookie.
+            Defaults to `None`.
+        secure (bool, optional): Whether the cookie is secure.
+            Defaults to `True`.
+        max_age (Optional[int], optional): The maximum age of the cookie
+            in seconds. Defaults to `None`.
+        expires (Optional[datetime], optional): The expiration date of the
+            cookie. Defaults to `None`.
+        httponly (bool, optional): HttpOnly flag for the cookie.
+            Defaults to `False`.
+        samesite (Optional[SameSite], optional): The SameSite attribute for
+            the cookie. Defaults to `"Lax"`.
+        partitioned (bool, optional): Whether the cookie is partitioned.
+            Defaults to `False`.
+        comment (Optional[str], optional): A comment for the cookie.
+            Defaults to `None`.
+        host_prefix (bool, optional): Whether to use the host prefix.
+            Defaults to `False`.
+        secure_prefix (bool, optional): Whether to use the secure prefix.
+            Defaults to `False`.
+    """  # noqa: E501
+
+    HOST_PREFIX = "__Host-"
+    SECURE_PREFIX = "__Secure-"
+
+    __slots__ = (
+        "key",
+        "value",
+        "_path",
+        "_comment",
+        "_domain",
+        "_secure",
+        "_httponly",
+        "_partitioned",
+        "_expires",
+        "_max_age",
+        "_samesite",
+    )
+
+    _keys = {
+        "path": "Path",
+        "comment": "Comment",
+        "domain": "Domain",
+        "max-age": "Max-Age",
+        "expires": "expires",
+        "samesite": "SameSite",
+        # "version": "Version",
+        "secure": "Secure",
+        "httponly": "HttpOnly",
+        "partitioned": "Partitioned",
+    }
+    _flags = {"secure", "httponly", "partitioned"}
+
+    def __init__(
+        self,
+        key: str,
+        value: str,
+        *,
+        path: str = "/",
+        domain: str | None = None,
+        secure: bool = True,
+        max_age: int | None = None,
+        expires: datetime | None = None,
+        httponly: bool = False,
+        samesite: SameSite | None = "Lax",
+        partitioned: bool = False,
+        comment: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ):
+        if key in self._keys:
+            raise KeyError("Cookie name is a reserved word")
+        if not _is_legal_key(key):
+            raise KeyError("Cookie key contains illegal characters")
+        if host_prefix:
+            if not secure:
+                raise ServerError(
+                    "Cannot set host_prefix on a cookie without secure=True"
+                )
+            if path != "/":
+                raise ServerError(
+                    "Cannot set host_prefix on a cookie unless path='/'"
+                )
+            if domain:
+                raise ServerError(
+                    "Cannot set host_prefix on a cookie with a defined domain"
+                )
+        elif secure_prefix and not secure:
+            raise ServerError(
+                "Cannot set secure_prefix on a cookie without secure=True"
+            )
+        if partitioned and not host_prefix:
+            # This is technically possible, but it is not advisable so we will
+            # take a stand and say "don't shoot yourself in the foot"
+            raise ServerError(
+                "Cannot create a partitioned cookie without "
+                "also setting host_prefix=True"
+            )
+
+        self.key = self.make_key(key, host_prefix, secure_prefix)
+        self.value = value
+
+        self._path = path
+        self._comment = comment
+        self._domain = domain
+        self._secure = secure
+        self._httponly = httponly
+        self._partitioned = partitioned
+        self._expires: datetime | None = None
+        self._max_age: int | None = None
+        self._samesite: SameSite | None = None
+
+        if expires is not None:
+            self.expires = expires
+        if max_age is not None:
+            self.max_age = max_age
+        if samesite is not None:
+            self.samesite = samesite
+
+    def __str__(self):
+        """Format as a Set-Cookie header value."""
+        output = ["{}={}".format(self.key, _quote(self.value))]
+        ordered_keys = list(self._keys.keys())
+        for key in sorted(
+            self._keys.keys(), key=lambda k: ordered_keys.index(k)
+        ):
+            value = getattr(self, key.replace("-", "_"))
+            if value is not None and value is not False:
+                if key == "max-age":
+                    try:
+                        output.append("%s=%d" % (self._keys[key], value))
+                    except TypeError:
+                        output.append("{}={}".format(self._keys[key], value))
+                elif key == "expires":
+                    output.append(
+                        "%s=%s"
+                        % (
+                            self._keys[key],
+                            value.strftime("%a, %d-%b-%Y %T GMT"),
+                        )
+                    )
+                elif key in self._flags:
+                    output.append(self._keys[key])
+                else:
+                    output.append("{}={}".format(self._keys[key], value))
+
+        return "; ".join(output)
+
+    @property
+    def path(self) -> str:  # no cov
+        """The path of the cookie. Defaults to `"/"`."""
+        return self._path
+
+    @path.setter
+    def path(self, value: str) -> None:  # no cov
+        self._path = value
+
+    @property
+    def expires(self) -> datetime | None:  # no cov
+        """The expiration date of the cookie. Defaults to `None`."""
+        return self._expires
+
+    @expires.setter
+    def expires(self, value: datetime) -> None:  # no cov
+        if not isinstance(value, datetime):
+            raise TypeError("Cookie 'expires' property must be a datetime")
+        self._expires = value
+
+    @property
+    def comment(self) -> str | None:  # no cov
+        """A comment for the cookie. Defaults to `None`."""
+        return self._comment
+
+    @comment.setter
+    def comment(self, value: str) -> None:  # no cov
+        self._comment = value
+
+    @property
+    def domain(self) -> str | None:  # no cov
+        """The domain of the cookie. Defaults to `None`."""
+        return self._domain
+
+    @domain.setter
+    def domain(self, value: str) -> None:  # no cov
+        self._domain = value
+
+    @property
+    def max_age(self) -> int | None:  # no cov
+        """The maximum age of the cookie in seconds. Defaults to `None`."""
+        return self._max_age
+
+    @max_age.setter
+    def max_age(self, value: int) -> None:  # no cov
+        if not str(value).isdigit():
+            raise ValueError("Cookie max-age must be an integer")
+        self._max_age = value
+
+    @property
+    def secure(self) -> bool:  # no cov
+        """Whether the cookie is secure. Defaults to `True`."""
+        return self._secure
+
+    @secure.setter
+    def secure(self, value: bool) -> None:  # no cov
+        self._secure = value
+
+    @property
+    def httponly(self) -> bool:  # no cov
+        """Whether the cookie is HTTP only. Defaults to `False`."""
+        return self._httponly
+
+    @httponly.setter
+    def httponly(self, value: bool) -> None:  # no cov
+        self._httponly = value
+
+    @property
+    def samesite(self) -> SameSite | None:  # no cov
+        """The SameSite attribute for the cookie. Defaults to `"Lax"`."""
+        return self._samesite
+
+    @samesite.setter
+    def samesite(self, value: SameSite) -> None:  # no cov
+        if value.lower() not in SAMESITE_VALUES:
+            raise TypeError(
+                "Cookie 'samesite' property must "
+                f"be one of: {','.join(SAMESITE_VALUES)}"
+            )
+        self._samesite = cast(SameSite, value.title())
+
+    @property
+    def partitioned(self) -> bool:  # no cov
+        """Whether the cookie is partitioned. Defaults to `False`."""
+        return self._partitioned
+
+    @partitioned.setter
+    def partitioned(self, value: bool) -> None:  # no cov
+        self._partitioned = value
+
+    @classmethod
+    def make_key(
+        cls, key: str, host_prefix: bool = False, secure_prefix: bool = False
+    ) -> str:
+        """Create a cookie key with the appropriate prefix.
+
+        Cookies can have one ow two prefixes. The first is `__Host-` which
+        requires that the cookie be set with `path="/", domain=None, and
+        secure=True`. The second is `__Secure-` which requires that
+        `secure=True`.
+
+        They cannot be combined.
+
+        Args:
+            key (str): The key (name) of the cookie.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key.
+                This requires that path="/", domain=None, and secure=True.
+                Defaults to `False`.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key.
+                This requires that secure=True. Defaults to `False`.
+
+        Raises:
+            ServerError: If both host_prefix and secure_prefix are set.
+
+        Returns:
+            str: The key with the appropriate prefix.
+        """  # noqa: E501
+        if host_prefix and secure_prefix:
+            raise ServerError(
+                "Both host_prefix and secure_prefix were requested. "
+                "A cookie should have only one prefix."
+            )
+        elif host_prefix:
+            key = cls.HOST_PREFIX + key
+        elif secure_prefix:
+            key = cls.SECURE_PREFIX + key
+        return key
diff --git a/.venv/lib/python3.12/site-packages/sanic/errorpages.py b/.venv/lib/python3.12/site-packages/sanic/errorpages.py
new file mode 100644
index 0000000..a613b63
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/errorpages.py
@@ -0,0 +1,405 @@
+"""
+Sanic `provides a pattern
+`_
+for providing a response when an exception occurs. However, if you do no handle
+an exception, it will provide a fallback. There are three fallback types:
+
+- HTML - *default*
+- Text
+- JSON
+
+Setting ``app.config.FALLBACK_ERROR_FORMAT = "auto"`` will enable a switch that
+will attempt to provide an appropriate response format based upon the
+request type.
+"""
+
+from __future__ import annotations
+
+import sys
+import typing as t
+
+from functools import partial
+from traceback import extract_tb
+
+from sanic.exceptions import BadRequest, SanicException
+from sanic.helpers import STATUS_CODES
+from sanic.log import deprecation, logger
+from sanic.pages.error import ErrorPage
+from sanic.response import html, json, text
+
+
+dumps: t.Callable[..., str]
+try:
+    from ujson import dumps
+
+    dumps = partial(dumps, escape_forward_slashes=False)
+except ImportError:  # noqa
+    from json import dumps
+
+if t.TYPE_CHECKING:
+    from sanic import HTTPResponse, Request
+
+DEFAULT_FORMAT = "auto"
+FALLBACK_TEXT = """\
+The application encountered an unexpected error and could not continue.\
+"""
+FALLBACK_STATUS = 500
+JSON = "application/json"
+
+
+class BaseRenderer:
+    """Base class that all renderers must inherit from.
+
+    This class defines the structure for rendering objects, handling the core functionality that specific renderers may extend.
+
+    Attributes:
+        request (Request): The incoming request object that needs rendering.
+        exception (Exception): Any exception that occurred and needs to be rendered.
+        debug (bool): Flag indicating whether to render with debugging information.
+
+    Methods:
+        dumps: A static method that must be overridden by subclasses to define the specific rendering.
+
+    Args:
+        request (Request): The incoming request object that needs rendering.
+        exception (Exception): Any exception that occurred and needs to be rendered.
+        debug (bool): Flag indicating whether to render with debugging information.
+    """  # noqa: E501
+
+    dumps = staticmethod(dumps)
+
+    def __init__(self, request: Request, exception: Exception, debug: bool):
+        self.request = request
+        self.exception = exception
+        self.debug = debug
+
+    @property
+    def headers(self) -> t.Dict[str, str]:
+        """The headers to be used for the response."""
+        if isinstance(self.exception, SanicException):
+            return getattr(self.exception, "headers", {})
+        return {}
+
+    @property
+    def status(self):
+        """The status code to be used for the response."""
+        if isinstance(self.exception, SanicException):
+            return getattr(self.exception, "status_code", FALLBACK_STATUS)
+        return FALLBACK_STATUS
+
+    @property
+    def text(self):
+        """The text to be used for the response."""
+        if self.debug or isinstance(self.exception, SanicException):
+            return str(self.exception)
+        return FALLBACK_TEXT
+
+    @property
+    def title(self):
+        """The title to be used for the response."""
+        status_text = STATUS_CODES.get(self.status, b"Error Occurred").decode()
+        return f"{self.status} — {status_text}"
+
+    def render(self) -> HTTPResponse:
+        """Outputs the exception as a response.
+
+        Returns:
+            HTTPResponse: The response object.
+        """
+        output = (
+            self.full
+            if self.debug and not getattr(self.exception, "quiet", False)
+            else self.minimal
+        )()
+        output.status = self.status
+        output.headers.update(self.headers)
+        return output
+
+    def minimal(self) -> HTTPResponse:  # noqa
+        """Provide a formatted message that is meant to not show any sensitive data or details.
+
+        This is the default fallback for production environments.
+
+        Returns:
+            HTTPResponse: The response object.
+        """  # noqa: E501
+        raise NotImplementedError
+
+    def full(self) -> HTTPResponse:  # noqa
+        """Provide a formatted message that has all details and is mean to be used primarily for debugging and non-production environments.
+
+        Returns:
+            HTTPResponse: The response object.
+        """  # noqa: E501
+        raise NotImplementedError
+
+
+class HTMLRenderer(BaseRenderer):
+    """Render an exception as HTML.
+
+    The default fallback type.
+    """
+
+    def full(self) -> HTTPResponse:
+        page = ErrorPage(
+            debug=self.debug,
+            title=super().title,
+            text=super().text,
+            request=self.request,
+            exc=self.exception,
+        )
+        return html(page.render())
+
+    def minimal(self) -> HTTPResponse:
+        return self.full()
+
+
+class TextRenderer(BaseRenderer):
+    """Render an exception as plain text."""
+
+    OUTPUT_TEXT = "{title}\n{bar}\n{text}\n\n{body}"
+    SPACER = "  "
+
+    def full(self) -> HTTPResponse:
+        return text(
+            self.OUTPUT_TEXT.format(
+                title=self.title,
+                text=self.text,
+                bar=("=" * len(self.title)),
+                body=self._generate_body(full=True),
+            )
+        )
+
+    def minimal(self) -> HTTPResponse:
+        return text(
+            self.OUTPUT_TEXT.format(
+                title=self.title,
+                text=self.text,
+                bar=("=" * len(self.title)),
+                body=self._generate_body(full=False),
+            )
+        )
+
+    @property
+    def title(self):
+        return f"⚠️ {super().title}"
+
+    def _generate_body(self, *, full):
+        lines = []
+        if full:
+            _, exc_value, __ = sys.exc_info()
+            exceptions = []
+
+            lines += [
+                f"{self.exception.__class__.__name__}: {self.exception} while "
+                f"handling path {self.request.path}",
+                f"Traceback of {self.request.app.name} "
+                "(most recent call last):\n",
+            ]
+
+            while exc_value:
+                exceptions.append(self._format_exc(exc_value))
+                exc_value = exc_value.__cause__
+
+            lines += exceptions[::-1]
+
+        for attr, display in (("context", True), ("extra", bool(full))):
+            info = getattr(self.exception, attr, None)
+            if info and display:
+                lines += self._generate_object_display_list(info, attr)
+
+        return "\n".join(lines)
+
+    def _format_exc(self, exc):
+        frames = "\n\n".join(
+            [
+                f"{self.SPACER * 2}File {frame.filename}, "
+                f"line {frame.lineno}, in "
+                f"{frame.name}\n{self.SPACER * 2}{frame.line}"
+                for frame in extract_tb(exc.__traceback__)
+            ]
+        )
+        return f"{self.SPACER}{exc.__class__.__name__}: {exc}\n{frames}"
+
+    def _generate_object_display_list(self, obj, descriptor):
+        lines = [f"\n{descriptor.title()}"]
+        for key, value in obj.items():
+            display = self.dumps(value)
+            lines.append(f"{self.SPACER * 2}{key}: {display}")
+        return lines
+
+
+class JSONRenderer(BaseRenderer):
+    """Render an exception as JSON."""
+
+    def full(self) -> HTTPResponse:
+        output = self._generate_output(full=True)
+        return json(output, dumps=self.dumps)
+
+    def minimal(self) -> HTTPResponse:
+        output = self._generate_output(full=False)
+        return json(output, dumps=self.dumps)
+
+    def _generate_output(self, *, full):
+        output = {
+            "description": self.title,
+            "status": self.status,
+            "message": self.text,
+        }
+
+        for attr, display in (("context", True), ("extra", bool(full))):
+            info = getattr(self.exception, attr, None)
+            if info and display:
+                output[attr] = info
+
+        if full:
+            _, exc_value, __ = sys.exc_info()
+            exceptions = []
+
+            while exc_value:
+                exceptions.append(
+                    {
+                        "type": exc_value.__class__.__name__,
+                        "exception": str(exc_value),
+                        "frames": [
+                            {
+                                "file": frame.filename,
+                                "line": frame.lineno,
+                                "name": frame.name,
+                                "src": frame.line,
+                            }
+                            for frame in extract_tb(exc_value.__traceback__)
+                        ],
+                    }
+                )
+                exc_value = exc_value.__cause__
+
+            output["path"] = self.request.path
+            output["args"] = self.request.args
+            output["exceptions"] = exceptions[::-1]
+
+        return output
+
+    @property
+    def title(self):
+        return STATUS_CODES.get(self.status, b"Error Occurred").decode()
+
+
+def escape(text):
+    """Minimal HTML escaping, not for attribute values (unlike html.escape)."""
+    return f"{text}".replace("&", "&").replace("<", "<")
+
+
+MIME_BY_CONFIG = {
+    "text": "text/plain",
+    "json": "application/json",
+    "html": "text/html",
+}
+CONFIG_BY_MIME = {v: k for k, v in MIME_BY_CONFIG.items()}
+RENDERERS_BY_CONTENT_TYPE = {
+    "text/plain": TextRenderer,
+    "application/json": JSONRenderer,
+    "multipart/form-data": HTMLRenderer,
+    "text/html": HTMLRenderer,
+}
+
+# Handler source code is checked for which response types it returns with the
+# route error_format="auto" (default) to determine which format to use.
+RESPONSE_MAPPING = {
+    "json": "json",
+    "text": "text",
+    "html": "html",
+    "JSONResponse": "json",
+    "text/plain": "text",
+    "text/html": "html",
+    "application/json": "json",
+}
+
+
+def check_error_format(format):
+    """Check that the format is known."""
+    if format not in MIME_BY_CONFIG and format != "auto":
+        raise SanicException(f"Unknown format: {format}")
+
+
+def exception_response(
+    request: Request,
+    exception: Exception,
+    debug: bool,
+    fallback: str,
+    base: t.Type[BaseRenderer],
+    renderer: t.Optional[t.Type[BaseRenderer]] = None,
+) -> HTTPResponse:
+    """Render a response for the default FALLBACK exception handler."""
+    if not renderer:
+        mt = guess_mime(request, fallback)
+        renderer = RENDERERS_BY_CONTENT_TYPE.get(mt, base)
+
+    renderer = t.cast(t.Type[BaseRenderer], renderer)
+    return renderer(request, exception, debug).render()
+
+
+def guess_mime(req: Request, fallback: str) -> str:
+    """Guess the MIME type for the response based upon the request."""
+    # Attempt to find a suitable MIME format for the response.
+    # Insertion-ordered map of formats["html"] = "source of that suggestion"
+    formats = {}
+    name = ""
+    # Route error_format (by magic from handler code if auto, the default)
+    if req.route:
+        name = req.route.name
+        f = req.route.extra.error_format
+        if f in MIME_BY_CONFIG:
+            formats[f] = name
+
+    if not formats and fallback in MIME_BY_CONFIG:
+        formats[fallback] = "FALLBACK_ERROR_FORMAT"
+
+    # If still not known, check for the request for clues of JSON
+    if not formats and fallback == "auto" and req.accept.match(JSON):
+        if JSON in req.accept:  # Literally, not wildcard
+            formats["json"] = "request.accept"
+        elif JSON in req.headers.getone("content-type", ""):
+            formats["json"] = "content-type"
+        # DEPRECATION: Remove this block in 24.3
+        else:
+            c = None
+            try:
+                c = req.json
+            except BadRequest:
+                pass
+            if c:
+                formats["json"] = "request.json"
+                deprecation(
+                    "Response type was determined by the JSON content of "
+                    "the request. This behavior is deprecated and will be "
+                    "removed in v24.3. Please specify the format either by\n"
+                    f'  error_format="json" on route {name}, by\n'
+                    '  FALLBACK_ERROR_FORMAT = "json", or by adding header\n'
+                    "  accept: application/json to your requests.",
+                    24.3,
+                )
+
+    # Any other supported formats
+    if fallback == "auto":
+        for k in MIME_BY_CONFIG:
+            if k not in formats:
+                formats[k] = "any"
+
+    mimes = [MIME_BY_CONFIG[k] for k in formats]
+    m = req.accept.match(*mimes)
+    if m:
+        format = CONFIG_BY_MIME[m.mime]
+        source = formats[format]
+        logger.debug(
+            "Error Page: The client accepts %s, using '%s' from %s",
+            m.header,
+            format,
+            source,
+        )
+    else:
+        logger.debug(
+            "Error Page: No format found, the client accepts %s",
+            repr(req.accept),
+        )
+    return m.mime
diff --git a/.venv/lib/python3.12/site-packages/sanic/exceptions.py b/.venv/lib/python3.12/site-packages/sanic/exceptions.py
new file mode 100644
index 0000000..e085f3c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/exceptions.py
@@ -0,0 +1,655 @@
+from asyncio import CancelledError
+from collections.abc import Sequence
+from os import PathLike
+from typing import Any
+
+from sanic.helpers import STATUS_CODES
+from sanic.models.protocol_types import Range
+
+
+class RequestCancelled(CancelledError):
+    quiet = True
+
+
+class ServerKilled(Exception):
+    """Exception Sanic server uses when killing a server process for something unexpected happening."""  # noqa: E501
+
+    quiet = True
+
+
+class SanicException(Exception):
+    """Generic exception that will generate an HTTP response when raised in the context of a request lifecycle.
+
+    Usually, it is best practice to use one of the more specific exceptions
+    than this generic one. Even when trying to raise a 500, it is generally
+    preferable to use `ServerError`.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`,
+            then the appropriate HTTP response status message will be used instead. Defaults to `None`.
+        status_code (Optional[int], optional): The HTTP response code to send, if applicable. If `None`,
+            then it will be 500. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed from the logs.
+            Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+
+    Examples:
+        ```python
+        raise SanicException(
+            "Something went wrong",
+            status_code=999,
+            context={
+                "info": "Some additional details to send to the client",
+            },
+            headers={
+                "X-Foo": "bar"
+            }
+        )
+        ```
+    """  # noqa: E501
+
+    status_code: int = 500
+    quiet: bool | None = False
+    headers: dict[str, str] = {}
+    message: str = ""
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        status_code: int | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, str] | None = None,
+    ) -> None:
+        self.context = context
+        self.extra = extra
+        status_code = status_code or getattr(
+            self.__class__, "status_code", None
+        )
+        quiet = (
+            quiet
+            if quiet is not None
+            else getattr(self.__class__, "quiet", None)
+        )
+        headers = headers or getattr(self.__class__, "headers", {})
+        if message is None:
+            message = self.message
+            if not message and status_code:
+                msg = STATUS_CODES.get(status_code, b"")
+                message = msg.decode()
+        elif isinstance(message, bytes):
+            message = message.decode()
+
+        super().__init__(message)
+
+        self.status_code = status_code or self.status_code
+        self.quiet = quiet
+        self.headers = headers
+        try:
+            self.message = message
+        except AttributeError:
+            ...
+
+
+class HTTPException(SanicException):
+    """A base class for other exceptions and should not be called directly."""
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+    ) -> None:
+        super().__init__(
+            message,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+
+
+class NotFound(HTTPException):
+    """A base class for other exceptions and should not be called directly.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`,
+            then the appropriate HTTP response status message will be used instead. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed from the logs.
+            Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 404
+    quiet = True
+
+
+class BadRequest(HTTPException):
+    """400 Bad Request
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 400
+    quiet = True
+
+
+InvalidUsage = BadRequest
+BadURL = BadRequest
+
+
+class MethodNotAllowed(HTTPException):
+    """405 Method Not Allowed
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Method Not Allowed' will be sent. Defaults to `None`.
+        method (Optional[str], optional): The HTTP method that was used. Defaults to an empty string.
+        allowed_methods (Optional[Sequence[str]], optional): The HTTP methods that can be used instead of the
+            one that was attempted.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 405
+    quiet = True
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        method: str = "",
+        allowed_methods: Sequence[str] | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+    ):
+        super().__init__(
+            message,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+        if allowed_methods:
+            self.headers = {
+                **self.headers,
+                "Allow": ", ".join(allowed_methods),
+            }
+        self.method = method
+        self.allowed_methods = allowed_methods
+
+
+MethodNotSupported = MethodNotAllowed
+
+
+class ServerError(HTTPException):
+    """500 Internal Server Error
+
+    A general server-side error has occurred. If no other HTTP exception is
+    appropriate, then this should be used
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 500
+
+
+InternalServerError = ServerError
+
+
+class ServiceUnavailable(HTTPException):
+    """503 Service Unavailable
+
+    The server is currently unavailable (because it is overloaded or
+    down for maintenance). Generally, this is a temporary state.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 503
+    quiet = True
+
+
+class URLBuildError(HTTPException):
+    """500 Internal Server Error
+
+    An exception used by Sanic internals when unable to build a URL.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 500
+
+
+class FileNotFound(NotFound):
+    """404 Not Found
+
+    A specific form of :class:`.NotFound` that is specifically when looking
+    for a file on the file system at a known path.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Not Found' will be sent. Defaults to `None`.
+        path (Optional[PathLike], optional): The path, if any, to the file that could not
+            be found. Defaults to `None`.
+        relative_url (Optional[str], optional): A relative URL of the file. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        path: PathLike | None = None,
+        relative_url: str | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+    ):
+        super().__init__(
+            message,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+        self.path = path
+        self.relative_url = relative_url
+
+
+class RequestTimeout(HTTPException):
+    """408 Request Timeout
+
+    The Web server (running the Web site) thinks that there has been too
+    long an interval of time between 1) the establishment of an IP
+    connection (socket) between the client and the server and
+    2) the receipt of any data on that socket, so the server has dropped
+    the connection. The socket connection has actually been lost - the Web
+    server has 'timed out' on that particular socket connection.
+
+    This is an internal exception thrown by Sanic and should not be used
+    directly.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 408
+    quiet = True
+
+
+class PayloadTooLarge(HTTPException):
+    """413 Payload Too Large
+
+    This is an internal exception thrown by Sanic and should not be used
+    directly.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 413
+    quiet = True
+
+
+class HeaderNotFound(BadRequest):
+    """400 Bad Request
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+
+class InvalidHeader(BadRequest):
+    """400 Bad Request
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+
+class RangeNotSatisfiable(HTTPException):
+    """416 Range Not Satisfiable
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Range Not Satisfiable' will be sent. Defaults to `None`.
+        content_range (Optional[ContentRange], optional): An object meeting the :class:`.ContentRange` protocol
+            that has a `total` property. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 416
+    quiet = True
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        content_range: Range | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+    ):
+        super().__init__(
+            message,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+        if content_range is not None:
+            self.headers = {
+                **self.headers,
+                "Content-Range": f"bytes */{content_range.total}",
+            }
+
+
+ContentRangeError = RangeNotSatisfiable
+
+
+class ExpectationFailed(HTTPException):
+    """417 Expectation Failed
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 417
+    quiet = True
+
+
+HeaderExpectationFailed = ExpectationFailed
+
+
+class Forbidden(HTTPException):
+    """403 Forbidden
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 403
+    quiet = True
+
+
+class InvalidRangeType(RangeNotSatisfiable):
+    """416 Range Not Satisfiable
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+    """  # noqa: E501
+
+    status_code = 416
+    quiet = True
+
+
+class PyFileError(SanicException):
+    def __init__(
+        self,
+        file,
+        status_code: int | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+    ):
+        super().__init__(
+            "could not execute config file %s" % file,
+            status_code=status_code,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+
+
+class Unauthorized(HTTPException):
+    """
+    **Status**: 401 Unauthorized
+
+    When present, additional keyword arguments may be used to complete
+    the WWW-Authentication header.
+
+    Args:
+        message (Optional[Union[str, bytes]], optional): The message to be sent to the client. If `None`
+            then the HTTP status 'Bad Request' will be sent. Defaults to `None`.
+        scheme (Optional[str], optional): Name of the authentication scheme to be used. Defaults to `None`.
+        quiet (Optional[bool], optional): When `True`, the error traceback will be suppressed
+            from the logs. Defaults to `None`.
+        context (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will be
+            sent to the client upon exception. Defaults to `None`.
+        extra (Optional[Dict[str, Any]], optional): Additional mapping of key/value data that will NOT be
+            sent to the client when in PRODUCTION mode. Defaults to `None`.
+        headers (Optional[Dict[str, Any]], optional): Additional headers that should be sent with the HTTP
+            response. Defaults to `None`.
+        **challenges (Dict[str, Any]): Additional keyword arguments that will be used to complete the
+            WWW-Authentication header. Defaults to `None`.
+
+    Examples:
+        With a Basic auth-scheme, realm MUST be present:
+        ```python
+        raise Unauthorized(
+            "Auth required.",
+            scheme="Basic",
+            realm="Restricted Area"
+        )
+        ```
+
+        With a Digest auth-scheme, things are a bit more complicated:
+        ```python
+        raise Unauthorized(
+            "Auth required.",
+            scheme="Digest",
+            realm="Restricted Area",
+            qop="auth, auth-int",
+            algorithm="MD5",
+            nonce="abcdef",
+            opaque="zyxwvu"
+        )
+        ```
+
+        With a Bearer auth-scheme, realm is optional so you can write:
+        ```python
+        raise Unauthorized("Auth required.", scheme="Bearer")
+        ```
+
+        or, if you want to specify the realm:
+        ```python
+        raise Unauthorized(
+            "Auth required.",
+            scheme="Bearer",
+            realm="Restricted Area"
+        )
+        ```
+    """  # noqa: E501
+
+    status_code = 401
+    quiet = True
+
+    def __init__(
+        self,
+        message: str | bytes | None = None,
+        scheme: str | None = None,
+        *,
+        quiet: bool | None = None,
+        context: dict[str, Any] | None = None,
+        extra: dict[str, Any] | None = None,
+        headers: dict[str, Any] | None = None,
+        **challenges,
+    ):
+        super().__init__(
+            message,
+            quiet=quiet,
+            context=context,
+            extra=extra,
+            headers=headers,
+        )
+
+        # if auth-scheme is specified, set "WWW-Authenticate" header
+        if scheme is not None:
+            values = [f'{k!s}="{v!s}"' for k, v in challenges.items()]
+            challenge = ", ".join(values)
+
+            self.headers = {
+                **self.headers,
+                "WWW-Authenticate": f"{scheme} {challenge}".rstrip(),
+            }
+
+
+class LoadFileException(SanicException):
+    """Exception raised when a file cannot be loaded."""
+
+
+class InvalidSignal(SanicException):
+    """Exception raised when an invalid signal is sent."""
+
+
+class WebsocketClosed(SanicException):
+    """Exception raised when a websocket is closed."""
+
+    quiet = True
+    message = "Client has closed the websocket connection"
diff --git a/.venv/lib/python3.12/site-packages/sanic/handlers/__init__.py b/.venv/lib/python3.12/site-packages/sanic/handlers/__init__.py
new file mode 100644
index 0000000..4777a8d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/handlers/__init__.py
@@ -0,0 +1,10 @@
+from .content_range import ContentRangeHandler
+from .directory import DirectoryHandler
+from .error import ErrorHandler
+
+
+__all__ = (
+    "ContentRangeHandler",
+    "DirectoryHandler",
+    "ErrorHandler",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/handlers/content_range.py b/.venv/lib/python3.12/site-packages/sanic/handlers/content_range.py
new file mode 100644
index 0000000..6adf042
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/handlers/content_range.py
@@ -0,0 +1,76 @@
+from __future__ import annotations
+
+import os
+
+from typing import TYPE_CHECKING
+
+from sanic.exceptions import (
+    HeaderNotFound,
+    InvalidRangeType,
+    RangeNotSatisfiable,
+)
+from sanic.models.protocol_types import Range
+
+
+if TYPE_CHECKING:
+    from sanic import Request
+
+
+class ContentRangeHandler(Range):
+    """Parse and process the incoming request headers to extract the content range information.
+
+    Args:
+        request (Request): The incoming request object.
+        stats (os.stat_result): The stats of the file being served.
+    """  # noqa: E501
+
+    __slots__ = ("start", "end", "size", "total", "headers")
+
+    def __init__(self, request: Request, stats: os.stat_result) -> None:
+        self.total = stats.st_size
+        _range = request.headers.getone("range", None)
+        if _range is None:
+            raise HeaderNotFound("Range Header Not Found")
+        unit, _, value = tuple(map(str.strip, _range.partition("=")))
+        if unit != "bytes":
+            raise InvalidRangeType(
+                "{} is not a valid Range Type".format(unit), self
+            )
+        start_b, _, end_b = tuple(map(str.strip, value.partition("-")))
+        try:
+            self.start = int(start_b) if start_b else None
+        except ValueError:
+            raise RangeNotSatisfiable(
+                "'{}' is invalid for Content Range".format(start_b), self
+            )
+        try:
+            self.end = int(end_b) if end_b else None
+        except ValueError:
+            raise RangeNotSatisfiable(
+                "'{}' is invalid for Content Range".format(end_b), self
+            )
+        if self.end is None:
+            if self.start is None:
+                raise RangeNotSatisfiable(
+                    "Invalid for Content Range parameters", self
+                )
+            else:
+                # this case represents `Content-Range: bytes 5-`
+                self.end = self.total - 1
+        else:
+            if self.start is None:
+                # this case represents `Content-Range: bytes -5`
+                self.start = self.total - self.end
+                self.end = self.total - 1
+        if self.start > self.end:
+            raise RangeNotSatisfiable(
+                "Invalid for Content Range parameters", self
+            )
+        self.size = self.end - self.start + 1
+        self.headers = {
+            "Content-Range": "bytes %s-%s/%s"
+            % (self.start, self.end, self.total)
+        }
+
+    def __bool__(self):
+        return hasattr(self, "size") and self.size > 0
diff --git a/.venv/lib/python3.12/site-packages/sanic/handlers/directory.py b/.venv/lib/python3.12/site-packages/sanic/handlers/directory.py
new file mode 100644
index 0000000..38555e4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/handlers/directory.py
@@ -0,0 +1,156 @@
+from __future__ import annotations
+
+from collections.abc import Iterable, Sequence
+from datetime import datetime
+from operator import itemgetter
+from pathlib import Path
+from stat import S_ISDIR
+from typing import cast
+from urllib.parse import unquote
+
+from sanic.exceptions import NotFound
+from sanic.pages.directory_page import DirectoryPage, FileInfo
+from sanic.request import Request
+from sanic.response import file, html, redirect
+
+
+def _is_path_within_root(path: Path, root: Path) -> bool:
+    """Check if a path (after resolution) is within the root directory.
+
+    Returns False for:
+    - Broken symlinks (cannot be resolved)
+    - Paths that resolve outside the root directory
+    - Any errors during resolution
+    """
+    try:
+        resolved = path.resolve()
+        resolved.relative_to(root.resolve())
+    except (ValueError, OSError, RuntimeError):
+        return False
+    else:
+        return True
+
+
+class DirectoryHandler:
+    """Serve files from a directory.
+
+    Args:
+        uri (str): The URI to serve the files at.
+        directory (Path): The directory to serve files from.
+        directory_view (bool): Whether to show a directory listing or not.
+        index (str | Sequence[str] | None): The index file(s) to
+            serve if the directory is requested. Defaults to None.
+        root_path (Optional[Path]): The root path for security checks.
+            Symlinks resolving outside this path will be hidden from
+            directory listings. Defaults to directory if not specified.
+        follow_external_symlink_files (bool): Whether to show file symlinks
+            pointing outside root in directory listings. Defaults to False.
+        follow_external_symlink_dirs (bool): Whether to show directory symlinks
+            pointing outside root in directory listings. Defaults to False.
+    """
+
+    def __init__(
+        self,
+        uri: str,
+        directory: Path,
+        directory_view: bool = False,
+        index: str | Sequence[str] | None = None,
+        root_path: Path | None = None,
+        follow_external_symlink_files: bool = False,
+        follow_external_symlink_dirs: bool = False,
+    ) -> None:
+        if isinstance(index, str):
+            index = [index]
+        elif index is None:
+            index = []
+        self.base = uri.strip("/")
+        self.directory = directory
+        self.directory_view = directory_view
+        self.index = tuple(index)
+        self.root_path = root_path if root_path is not None else directory
+        self.follow_external_symlink_files = follow_external_symlink_files
+        self.follow_external_symlink_dirs = follow_external_symlink_dirs
+
+    async def handle(self, request: Request, path: str):
+        """Handle the request.
+
+        Args:
+            request (Request): The incoming request object.
+            path (str): The path to the file to serve.
+
+        Raises:
+            NotFound: If the file is not found.
+            IsADirectoryError: If the path is a directory and directory_view is False.
+
+        Returns:
+            Response: The response object.
+        """  # noqa: E501
+        current = unquote(path).strip("/")[len(self.base) :].strip("/")  # noqa: E203
+        for file_name in self.index:
+            index_file = self.directory / current / file_name
+            if index_file.is_file():
+                return await file(index_file)
+
+        if self.directory_view:
+            return self._index(
+                self.directory / current, path, request.app.debug
+            )
+
+        if self.index:
+            raise NotFound("File not found")
+
+        raise IsADirectoryError(f"{self.directory.as_posix()} is a directory")
+
+    def _index(self, location: Path, path: str, debug: bool):
+        # Remove empty path elements, append slash
+        if "//" in path or not path.endswith("/"):
+            return redirect(
+                "/" + "".join([f"{p}/" for p in path.split("/") if p])
+            )
+
+        # Render file browser
+        page = DirectoryPage(self._iter_files(location), path, debug)
+        return html(page.render())
+
+    def _prepare_file(self, path: Path) -> dict[str, int | str] | None:
+        try:
+            stat = path.stat()
+        except OSError:
+            return None
+        modified = (
+            datetime.fromtimestamp(stat.st_mtime)
+            .isoformat()[:19]
+            .replace("T", " ")
+        )
+        is_dir = S_ISDIR(stat.st_mode)
+        icon = "📁" if is_dir else "📄"
+        file_name = path.name
+        if is_dir:
+            file_name += "/"
+        return {
+            "priority": is_dir * -1,
+            "file_name": file_name,
+            "icon": icon,
+            "file_access": modified,
+            "file_size": stat.st_size,
+        }
+
+    def _iter_files(self, location: Path) -> Iterable[FileInfo]:
+        prepared = []
+        for f in location.iterdir():
+            if f.is_symlink() and not _is_path_within_root(f, self.root_path):
+                # External symlink - check if allowed based on type
+                try:
+                    is_dir = f.resolve().is_dir()
+                except OSError:
+                    continue  # Broken symlink
+                if is_dir and not self.follow_external_symlink_dirs:
+                    continue
+                if not is_dir and not self.follow_external_symlink_files:
+                    continue
+            file_info = self._prepare_file(f)
+            if file_info is not None:
+                prepared.append(file_info)
+        for item in sorted(prepared, key=itemgetter("priority", "file_name")):
+            del item["priority"]
+            yield cast(FileInfo, item)
diff --git a/.venv/lib/python3.12/site-packages/sanic/handlers/error.py b/.venv/lib/python3.12/site-packages/sanic/handlers/error.py
new file mode 100644
index 0000000..bda367e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/handlers/error.py
@@ -0,0 +1,203 @@
+from __future__ import annotations
+
+from sanic.errorpages import BaseRenderer, TextRenderer, exception_response
+from sanic.exceptions import ServerError
+from sanic.log import error_logger
+from sanic.models.handler_types import RouteHandler
+from sanic.request.types import Request
+from sanic.response import text
+from sanic.response.types import HTTPResponse
+
+
+class ErrorHandler:
+    """Process and handle all uncaught exceptions.
+
+    This error handling framework is built into the core that can be extended
+    by the developers to perform a wide range of tasks from recording the error
+    stats to reporting them to an external service that can be used for
+    realtime alerting system.
+
+    Args:
+        base (BaseRenderer): The renderer to use for the error pages.
+    """  # noqa: E501
+
+    def __init__(
+        self,
+        base: type[BaseRenderer] = TextRenderer,
+    ):
+        self.cached_handlers: dict[
+            tuple[type[BaseException], str | None], RouteHandler | None
+        ] = {}
+        self.debug = False
+        self.base = base
+
+    def _full_lookup(self, exception, route_name: str | None = None):
+        return self.lookup(exception, route_name)
+
+    def _add(
+        self,
+        key: tuple[type[BaseException], str | None],
+        handler: RouteHandler,
+    ) -> None:
+        if key in self.cached_handlers:
+            exc, name = key
+            if name is None:
+                name = "__ALL_ROUTES__"
+
+            message = (
+                f"Duplicate exception handler definition on: route={name} "
+                f"and exception={exc}"
+            )
+            raise ServerError(message)
+        self.cached_handlers[key] = handler
+
+    def add(self, exception, handler, route_names: list[str] | None = None):
+        """Add a new exception handler to an already existing handler object.
+
+        Args:
+            exception (sanic.exceptions.SanicException or Exception): Type
+                of exception that needs to be handled.
+            handler (function): Reference to the function that will
+                handle the exception.
+
+        Returns:
+            None
+
+        """  # noqa: E501
+        if route_names:
+            for route in route_names:
+                self._add((exception, route), handler)
+        else:
+            self._add((exception, None), handler)
+
+    def lookup(self, exception, route_name: str | None = None):
+        """Lookup the existing instance of `ErrorHandler` and fetch the registered handler for a specific type of exception.
+
+        This method leverages a dict lookup to speedup the retrieval process.
+
+        Args:
+            exception (sanic.exceptions.SanicException or Exception): Type
+                of exception.
+
+        Returns:
+            Registered function if found, ``None`` otherwise.
+
+        """  # noqa: E501
+        exception_class = type(exception)
+
+        for name in (route_name, None):
+            exception_key = (exception_class, name)
+            handler = self.cached_handlers.get(exception_key)
+            if handler:
+                return handler
+
+        for name in (route_name, None):
+            for ancestor in type.mro(exception_class):
+                exception_key = (ancestor, name)
+                if exception_key in self.cached_handlers:
+                    handler = self.cached_handlers[exception_key]
+                    self.cached_handlers[(exception_class, route_name)] = (
+                        handler
+                    )
+                    return handler
+
+                if ancestor is BaseException:
+                    break
+        self.cached_handlers[(exception_class, route_name)] = None
+        handler = None
+        return handler
+
+    _lookup = _full_lookup
+
+    def response(self, request, exception):
+        """Fetch and executes an exception handler and returns a response object.
+
+        Args:
+            request (sanic.request.Request): Instance of the request.
+            exception (sanic.exceptions.SanicException or Exception): Exception to handle.
+
+        Returns:
+            Wrap the return value obtained from the `default` function or the registered handler for that type of exception.
+
+        """  # noqa: E501
+        route_name = request.name if request else None
+        handler = self._lookup(exception, route_name)
+        response = None
+        try:
+            if handler:
+                response = handler(request, exception)
+            if response is None:
+                response = self.default(request, exception)
+        except Exception:
+            try:
+                url = repr(request.url)
+            except AttributeError:  # no cov
+                url = "unknown"
+            response_message = (
+                'Exception raised in exception handler "%s" for uri: %s'
+            )
+            error_logger.exception(response_message, handler.__name__, url)
+
+            if self.debug:
+                return text(response_message % (handler.__name__, url), 500)
+            else:
+                return text("An error occurred while handling an error", 500)
+        return response
+
+    def default(self, request: Request, exception: Exception) -> HTTPResponse:
+        """Provide a default behavior for the objects of ErrorHandler.
+
+        If a developer chooses to extend the ErrorHandler, they can
+        provide a custom implementation for this method to behave in a way
+        they see fit.
+
+        Args:
+            request (sanic.request.Request): Incoming request.
+            exception (sanic.exceptions.SanicException or Exception): Exception object.
+
+        Returns:
+            HTTPResponse: The response object.
+
+        Examples:
+            ```python
+            class CustomErrorHandler(ErrorHandler):
+                def default(self, request: Request, exception: Exception) -> HTTPResponse:
+                    # Custom logic for handling the exception and creating a response
+                    custom_response = my_custom_logic(request, exception)
+                    return custom_response
+
+            app = Sanic("MyApp", error_handler=CustomErrorHandler())
+            ```
+        """  # noqa: E501
+        self.log(request, exception)
+        fallback = request.app.config.FALLBACK_ERROR_FORMAT
+        return exception_response(
+            request,
+            exception,
+            debug=self.debug,
+            base=self.base,
+            fallback=fallback,
+        )
+
+    @staticmethod
+    def log(request: Request, exception: Exception) -> None:
+        """Logs information about an incoming request and the associated exception.
+
+        Args:
+            request (Request): The incoming request to be logged.
+            exception (Exception): The exception that occurred during the handling of the request.
+
+        Returns:
+            None
+        """  # noqa: E501
+        quiet = getattr(exception, "quiet", False)
+        noisy = getattr(request.app.config, "NOISY_EXCEPTIONS", False)
+        if quiet is False or noisy is True:
+            try:
+                url = repr(request.url)
+            except AttributeError:  # no cov
+                url = "unknown"
+
+            error_logger.exception(
+                "Exception occurred while handling uri: %s", url
+            )
diff --git a/.venv/lib/python3.12/site-packages/sanic/headers.py b/.venv/lib/python3.12/site-packages/sanic/headers.py
new file mode 100644
index 0000000..e064d6d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/headers.py
@@ -0,0 +1,550 @@
+from __future__ import annotations
+
+import re
+
+from collections.abc import Iterable
+from typing import Any
+from urllib.parse import unquote
+
+from sanic.exceptions import InvalidHeader
+from sanic.helpers import STATUS_CODES
+
+
+# TODO:
+# - the Options object should be a typed object to allow for less casting
+#   across the application (in request.py for example)
+HeaderIterable = Iterable[tuple[str, Any]]  # Values convertible to str
+HeaderBytesIterable = Iterable[tuple[bytes, bytes]]
+Options = dict[str, int | str]  # key=value fields in various headers
+OptionsIterable = Iterable[tuple[str, str]]  # May contain duplicate keys
+
+_token, _quoted = r"([\w!#$%&'*+\-.^_`|~]+)", r'"([^"]*)"'
+_param = re.compile(rf";\s*{_token}=(?:{_token}|{_quoted})", re.ASCII)
+_ipv6 = "(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}"
+_ipv6_re = re.compile(_ipv6)
+_host_re = re.compile(
+    r"((?:\[" + _ipv6 + r"\])|[a-zA-Z0-9.\-]{1,253})(?::(\d{1,5}))?"
+)
+
+# RFC's quoted-pair escapes are mostly ignored by browsers. Chrome, Firefox and
+# curl all have different escaping, that we try to handle as well as possible,
+# even though no client escapes in a way that would allow perfect handling.
+
+# For more information, consult ../tests/test_requests.py
+
+
+class MediaType:
+    """A media type, as used in the Accept header.
+
+    This class is a representation of a media type, as used in the Accept
+    header. It encapsulates the type, subtype and any parameters, and
+    provides methods for matching against other media types.
+
+    Two separate methods are provided for searching the list:
+    - 'match' for finding the most preferred match (wildcards supported)
+    -  operator 'in' for checking explicit matches (wildcards as literals)
+
+    Args:
+        type_ (str): The type of the media type.
+        subtype (str): The subtype of the media type.
+        **params (str): Any parameters for the media type.
+    """
+
+    def __init__(
+        self,
+        type_: str,
+        subtype: str,
+        **params: str,
+    ):
+        self.type = type_
+        self.subtype = subtype
+        self.q = float(params.get("q", "1.0"))
+        self.params = params
+        self.mime = f"{type_}/{subtype}"
+        self.key = (
+            -1 * self.q,
+            -1 * len(self.params),
+            self.subtype == "*",
+            self.type == "*",
+        )
+
+    def __repr__(self):
+        return self.mime + "".join(f";{k}={v}" for k, v in self.params.items())
+
+    def __eq__(self, other):
+        """Check for mime (str or MediaType) identical type/subtype.
+        Parameters such as q are not considered."""
+        if isinstance(other, str):
+            # Give a friendly reminder if str contains parameters
+            if ";" in other:
+                raise ValueError("Use match() to compare with parameters")
+            return self.mime == other
+        if isinstance(other, MediaType):
+            # Ignore parameters silently with MediaType objects
+            return self.mime == other.mime
+        return NotImplemented
+
+    def match(
+        self,
+        mime_with_params: str | MediaType,
+    ) -> MediaType | None:
+        """Match this media type against another media type.
+
+        Check if this media type matches the given mime type/subtype.
+        Wildcards are supported both ways on both type and subtype.
+        If mime contains a semicolon, optionally followed by parameters,
+        the parameters of the two media types must match exactly.
+
+        .. note::
+            Use the `==` operator instead to check for literal matches
+            without expanding wildcards.
+
+
+        Args:
+            media_type (str): A type/subtype string to match.
+
+        Returns:
+            MediaType: Returns `self` if the media types are compatible.
+            None: Returns `None` if the media types are not compatible.
+        """
+        mt = (
+            MediaType._parse(mime_with_params)
+            if isinstance(mime_with_params, str)
+            else mime_with_params
+        )
+        return (
+            self
+            if (
+                mt
+                # All parameters given in the other media type must match
+                and all(self.params.get(k) == v for k, v in mt.params.items())
+                # Subtype match
+                and (
+                    self.subtype == mt.subtype
+                    or self.subtype == "*"
+                    or mt.subtype == "*"
+                )
+                # Type match
+                and (
+                    self.type == mt.type or self.type == "*" or mt.type == "*"
+                )
+            )
+            else None
+        )
+
+    @property
+    def has_wildcard(self) -> bool:
+        """Return True if this media type has a wildcard in it.
+
+        Returns:
+            bool: True if this media type has a wildcard in it.
+        """
+        return any(part == "*" for part in (self.subtype, self.type))
+
+    @classmethod
+    def _parse(cls, mime_with_params: str) -> MediaType | None:
+        mtype = mime_with_params.strip()
+        if "/" not in mime_with_params:
+            return None
+
+        mime, *raw_params = mtype.split(";")
+        type_, subtype = mime.split("/", 1)
+        if not type_ or not subtype:
+            raise ValueError(f"Invalid media type: {mtype}")
+
+        params = {
+            key.strip(): value.strip()
+            for key, value in (param.split("=", 1) for param in raw_params)
+        }
+
+        return cls(type_.lstrip(), subtype.rstrip(), **params)
+
+
+class Matched:
+    """A matching result of a MIME string against a header.
+
+    This class is a representation of a matching result of a MIME string
+    against a header. It encapsulates the MIME string, the header, and
+    provides methods for matching against other MIME strings.
+
+    Args:
+        mime (str): The MIME string to match.
+        header (MediaType): The header to match against, if any.
+    """
+
+    def __init__(self, mime: str, header: MediaType | None):
+        self.mime = mime
+        self.header = header
+
+    def __repr__(self):
+        return f"<{self} matched {self.header}>" if self else ""
+
+    def __str__(self):
+        return self.mime
+
+    def __bool__(self):
+        return self.header is not None
+
+    def __eq__(self, other: Any) -> bool:
+        try:
+            comp, other_accept = self._compare(other)
+        except TypeError:
+            return False
+
+        return bool(
+            comp
+            and (
+                (self.header and other_accept.header)
+                or (not self.header and not other_accept.header)
+            )
+        )
+
+    def _compare(self, other) -> tuple[bool, Matched]:
+        if isinstance(other, str):
+            parsed = Matched.parse(other)
+            if self.mime == other:
+                return True, parsed
+            other = parsed
+
+        if isinstance(other, Matched):
+            return self.header == other.header, other
+
+        raise TypeError(
+            "Comparison not supported between unequal "
+            f"mime types of '{self.mime}' and '{other}'"
+        )
+
+    def match(self, other: str | Matched) -> Matched | None:
+        """Match this MIME string against another MIME string.
+
+        Check if this MIME string matches the given MIME string. Wildcards are supported both ways on both type and subtype.
+
+        Args:
+            other (str): A MIME string to match.
+
+        Returns:
+            Matched: Returns `self` if the MIME strings are compatible.
+            None: Returns `None` if the MIME strings are not compatible.
+        """  # noqa: E501
+        accept = Matched.parse(other) if isinstance(other, str) else other
+        if not self.header or not accept.header:
+            return None
+        if self.header.match(accept.header):
+            return accept
+        return None
+
+    @classmethod
+    def parse(cls, raw: str) -> Matched:
+        media_type = MediaType._parse(raw)
+        return cls(raw, media_type)
+
+
+class AcceptList(list):
+    """A list of media types, as used in the Accept header.
+
+    The Accept header entries are listed in order of preference, starting
+    with the most preferred. This class is a list of `MediaType` objects,
+    that encapsulate also the q value or any other parameters.
+
+    Two separate methods are provided for searching the list:
+    - 'match' for finding the most preferred match (wildcards supported)
+    -  operator 'in' for checking explicit matches (wildcards as literals)
+
+    Args:
+        *args (MediaType): Any number of MediaType objects.
+    """
+
+    def match(self, *mimes: str, accept_wildcards=True) -> Matched:
+        """Find a media type accepted by the client.
+
+        This method can be used to find which of the media types requested by
+        the client is most preferred against the ones given as arguments.
+
+        The ordering of preference is set by:
+        1. The order set by RFC 7231, s. 5.3.2, giving a higher priority
+            to q values and more specific type definitions,
+        2. The order of the arguments (first is most preferred), and
+        3. The first matching entry on the Accept header.
+
+        Wildcards are matched both ways. A match is usually found, as the
+        Accept headers typically include `*/*`, in particular if the header
+        is missing, is not manually set, or if the client is a browser.
+
+        Note: the returned object behaves as a string of the mime argument
+        that matched, and is empty/falsy if no match was found. The matched
+        header entry `MediaType` or `None` is available as the `m` attribute.
+
+        Args:
+            mimes (List[str]): Any MIME types to search for in order of preference.
+            accept_wildcards (bool): Match Accept entries with wildcards in them.
+
+        Returns:
+            Match: A match object with the mime string and the MediaType object.
+        """  # noqa: E501
+        a = sorted(
+            (-acc.q, i, j, mime, acc)
+            for j, acc in enumerate(self)
+            if accept_wildcards or not acc.has_wildcard
+            for i, mime in enumerate(mimes)
+            if acc.match(mime)
+        )
+        return Matched(*(a[0][-2:] if a else ("", None)))
+
+    def __str__(self):
+        """Format as Accept header value (parsed, not original)."""
+        return ", ".join(str(m) for m in self)
+
+
+def parse_accept(accept: str | None) -> AcceptList:
+    """Parse an Accept header and order the acceptable media types according to RFC 7231, s. 5.3.2
+
+    https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
+
+    Args:
+        accept (str): The Accept header value to parse.
+
+    Returns:
+        AcceptList: A list of MediaType objects, ordered by preference.
+
+    Raises:
+        InvalidHeader: If the header value is invalid.
+    """  # noqa: E501
+    if not accept:
+        if accept == "":
+            return AcceptList()  # Empty header, accept nothing
+        accept = "*/*"  # No header means that all types are accepted
+    try:
+        a = [
+            mt
+            for mt in [MediaType._parse(mtype) for mtype in accept.split(",")]
+            if mt
+        ]
+        if not a:
+            raise ValueError
+        return AcceptList(sorted(a, key=lambda x: x.key))
+    except ValueError:
+        raise InvalidHeader(f"Invalid header value in Accept: {accept}")
+
+
+def parse_content_header(value: str) -> tuple[str, Options]:
+    """Parse content-type and content-disposition header values.
+
+    E.g. `form-data; name=upload; filename="file.txt"` to
+    ('form-data', {'name': 'upload', 'filename': 'file.txt'})
+
+    Mostly identical to cgi.parse_header and werkzeug.parse_options_header
+    but runs faster and handles special characters better.
+
+    Unescapes %22 to `"` and %0D%0A to `\n` in field values.
+
+    Args:
+        value (str): The header value to parse.
+
+    Returns:
+        Tuple[str, Options]: The header value and a dict of options.
+    """
+    pos = value.find(";")
+    if pos == -1:
+        options: dict[str, int | str] = {}
+    else:
+        options = {
+            m.group(1).lower(): (m.group(2) or m.group(3))
+            .replace("%22", '"')
+            .replace("%0D%0A", "\n")
+            for m in _param.finditer(value[pos:])
+        }
+        value = value[:pos]
+    return value.strip().lower(), options
+
+
+# https://tools.ietf.org/html/rfc7230#section-3.2.6 and
+# https://tools.ietf.org/html/rfc7239#section-4
+# This regex is for *reversed* strings because that works much faster for
+# right-to-left matching than the other way around. Be wary that all things are
+# a bit backwards! _rparam matches forwarded pairs alike ";key=value"
+_rparam = re.compile(f"(?:{_token}|{_quoted})={_token}\\s*($|[;,])", re.ASCII)
+
+
+def parse_forwarded(headers, config) -> Options | None:
+    """Parse RFC 7239 Forwarded headers.
+    The value of `by` or `secret` must match `config.FORWARDED_SECRET`
+    :return: dict with keys and values, or None if nothing matched
+    """
+    header = headers.getall("forwarded", None)
+    secret = config.FORWARDED_SECRET
+    if header is None or not secret:
+        return None
+    header = ",".join(header)  # Join multiple header lines
+    if secret not in header:
+        return None
+    # Loop over = elements from right to left
+    sep = pos = None
+    options_list: list[tuple[str, str]] = []
+    found = False
+    for m in _rparam.finditer(header[::-1]):
+        # Start of new element? (on parser skips and non-semicolon right sep)
+        if m.start() != pos or sep != ";":
+            # Was the previous element (from right) what we wanted?
+            if found:
+                break
+            # Clear values and parse as new element
+            del options_list[:]
+        pos = m.end()
+        val_token, val_quoted, key, sep = m.groups()
+        key = key.lower()[::-1]
+        val = (val_token or val_quoted.replace('"\\', '"'))[::-1]
+        options_list.append((key, val))
+        if key in ("secret", "by") and val == secret:
+            found = True
+        # Check if we would return on next round, to avoid useless parse
+        if found and sep != ";":
+            break
+    # If secret was found, return the matching options in left-to-right order
+    return fwd_normalize(reversed(options_list)) if found else None
+
+
+def parse_xforwarded(headers, config) -> Options | None:
+    """Parse traditional proxy headers."""
+    real_ip_header = config.REAL_IP_HEADER
+    proxies_count = config.PROXIES_COUNT
+    addr = real_ip_header and headers.getone(real_ip_header, None)
+    if not addr and proxies_count:
+        assert proxies_count > 0
+        try:
+            # Combine, split and filter multiple headers' entries
+            forwarded_for = headers.getall(config.FORWARDED_FOR_HEADER)
+            proxies = [
+                p
+                for p in (
+                    p.strip() for h in forwarded_for for p in h.split(",")
+                )
+                if p
+            ]
+            addr = proxies[-proxies_count]
+        except (KeyError, IndexError):
+            pass
+    # No processing of other headers if no address is found
+    if not addr:
+        return None
+
+    def options():
+        yield "for", addr
+        for key, header in (
+            ("proto", "x-scheme"),
+            ("proto", "x-forwarded-proto"),  # Overrides X-Scheme if present
+            ("host", "x-forwarded-host"),
+            ("port", "x-forwarded-port"),
+            ("path", "x-forwarded-path"),
+        ):
+            yield key, headers.getone(header, None)
+
+    return fwd_normalize(options())
+
+
+def fwd_normalize(fwd: OptionsIterable) -> Options:
+    """Normalize and convert values extracted from forwarded headers.
+
+    Args:
+        fwd (OptionsIterable): An iterable of key-value pairs.
+
+    Returns:
+        Options: A dict of normalized key-value pairs.
+    """
+    ret: dict[str, int | str] = {}
+    for key, val in fwd:
+        if val is not None:
+            try:
+                if key in ("by", "for"):
+                    ret[key] = fwd_normalize_address(val)
+                elif key in ("host", "proto"):
+                    ret[key] = val.lower()
+                elif key == "port":
+                    ret[key] = int(val)
+                elif key == "path":
+                    ret[key] = unquote(val)
+                else:
+                    ret[key] = val
+            except ValueError:
+                pass
+    return ret
+
+
+def fwd_normalize_address(addr: str) -> str:
+    """Normalize address fields of proxy headers.
+
+    Args:
+        addr (str): An address string.
+
+    Returns:
+        str: A normalized address string.
+    """
+    if addr == "unknown":
+        raise ValueError()  # omit unknown value identifiers
+    if addr.startswith("_"):
+        return addr  # do not lower-case obfuscated strings
+    if _ipv6_re.fullmatch(addr):
+        addr = f"[{addr}]"  # bracket IPv6
+    return addr.lower()
+
+
+def parse_host(host: str) -> tuple[str | None, int | None]:
+    """Split host:port into hostname and port.
+
+    Args:
+        host (str): A host string.
+
+    Returns:
+        Tuple[Optional[str], Optional[int]]: A tuple of hostname and port.
+    """
+    m = _host_re.fullmatch(host)
+    if not m:
+        return None, None
+    host, port = m.groups()
+    return host.lower(), int(port) if port is not None else None
+
+
+_HTTP1_STATUSLINES = [
+    b"HTTP/1.1 %d %b\r\n" % (status, STATUS_CODES.get(status, b"UNKNOWN"))
+    for status in range(1000)
+]
+
+
+def format_http1_response(status: int, headers: HeaderBytesIterable) -> bytes:
+    """Format a HTTP/1.1 response header.
+
+    Args:
+        status (int): The HTTP status code.
+        headers (HeaderBytesIterable): An iterable of header tuples.
+
+    Returns:
+        bytes: The formatted response header.
+    """
+    # Note: benchmarks show that here bytes concat is faster than bytearray,
+    # b"".join() or %-formatting. %timeit any changes you make.
+    ret = _HTTP1_STATUSLINES[status]
+    for h in headers:
+        ret += b"%b: %b\r\n" % h
+    ret += b"\r\n"
+    return ret
+
+
+def parse_credentials(
+    header: str | None,
+    prefixes: list | tuple | set | None = None,
+) -> tuple[str | None, str | None]:
+    """Parses any header with the aim to retrieve any credentials from it.
+
+    Args:
+        header (Optional[str]): The header to parse.
+        prefixes (Optional[Union[List, Tuple, Set]], optional): The prefixes to look for. Defaults to None.
+
+    Returns:
+        Tuple[Optional[str], Optional[str]]: The prefix and the credentials.
+    """  # noqa: E501
+    if not prefixes or not isinstance(prefixes, (list, tuple, set)):
+        prefixes = ("Basic", "Bearer", "Token")
+    if header is not None:
+        for prefix in prefixes:
+            if prefix in header:
+                return prefix, header.partition(prefix)[-1].strip()
+    return None, header
diff --git a/.venv/lib/python3.12/site-packages/sanic/helpers.py b/.venv/lib/python3.12/site-packages/sanic/helpers.py
new file mode 100644
index 0000000..330c8e2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/helpers.py
@@ -0,0 +1,173 @@
+"""Defines basics of HTTP standard."""
+
+import sys
+
+from functools import partial
+from importlib import import_module
+from inspect import ismodule
+
+
+try:
+    from ujson import dumps as ujson_dumps
+
+    json_dumps = partial(ujson_dumps, escape_forward_slashes=False)
+except ImportError:
+    # This is done in order to ensure that the JSON response is
+    # kept consistent across both ujson and inbuilt json usage.
+    from json import dumps
+
+    json_dumps = partial(dumps, separators=(",", ":"))
+
+STATUS_CODES: dict[int, bytes] = {
+    100: b"Continue",
+    101: b"Switching Protocols",
+    102: b"Processing",
+    103: b"Early Hints",
+    200: b"OK",
+    201: b"Created",
+    202: b"Accepted",
+    203: b"Non-Authoritative Information",
+    204: b"No Content",
+    205: b"Reset Content",
+    206: b"Partial Content",
+    207: b"Multi-Status",
+    208: b"Already Reported",
+    226: b"IM Used",
+    300: b"Multiple Choices",
+    301: b"Moved Permanently",
+    302: b"Found",
+    303: b"See Other",
+    304: b"Not Modified",
+    305: b"Use Proxy",
+    307: b"Temporary Redirect",
+    308: b"Permanent Redirect",
+    400: b"Bad Request",
+    401: b"Unauthorized",
+    402: b"Payment Required",
+    403: b"Forbidden",
+    404: b"Not Found",
+    405: b"Method Not Allowed",
+    406: b"Not Acceptable",
+    407: b"Proxy Authentication Required",
+    408: b"Request Timeout",
+    409: b"Conflict",
+    410: b"Gone",
+    411: b"Length Required",
+    412: b"Precondition Failed",
+    413: b"Request Entity Too Large",
+    414: b"Request-URI Too Long",
+    415: b"Unsupported Media Type",
+    416: b"Requested Range Not Satisfiable",
+    417: b"Expectation Failed",
+    418: b"I'm a teapot",
+    422: b"Unprocessable Entity",
+    423: b"Locked",
+    424: b"Failed Dependency",
+    426: b"Upgrade Required",
+    428: b"Precondition Required",
+    429: b"Too Many Requests",
+    431: b"Request Header Fields Too Large",
+    451: b"Unavailable For Legal Reasons",
+    500: b"Internal Server Error",
+    501: b"Not Implemented",
+    502: b"Bad Gateway",
+    503: b"Service Unavailable",
+    504: b"Gateway Timeout",
+    505: b"HTTP Version Not Supported",
+    506: b"Variant Also Negotiates",
+    507: b"Insufficient Storage",
+    508: b"Loop Detected",
+    510: b"Not Extended",
+    511: b"Network Authentication Required",
+}
+
+# According to https://tools.ietf.org/html/rfc2616#section-7.1
+_ENTITY_HEADERS = frozenset(
+    [
+        "allow",
+        "content-encoding",
+        "content-language",
+        "content-length",
+        "content-location",
+        "content-md5",
+        "content-range",
+        "content-type",
+        "expires",
+        "last-modified",
+        "extension-header",
+    ]
+)
+
+# According to https://tools.ietf.org/html/rfc2616#section-13.5.1
+_HOP_BY_HOP_HEADERS = frozenset(
+    [
+        "connection",
+        "keep-alive",
+        "proxy-authenticate",
+        "proxy-authorization",
+        "te",
+        "trailers",
+        "transfer-encoding",
+        "upgrade",
+    ]
+)
+
+
+def has_message_body(status):
+    """
+    According to the following RFC message body and length SHOULD NOT
+    be included in responses status 1XX, 204 and 304.
+    https://tools.ietf.org/html/rfc2616#section-4.4
+    https://tools.ietf.org/html/rfc2616#section-4.3
+    """
+    return status not in (204, 304) and not (100 <= status < 200)
+
+
+def is_entity_header(header):
+    """Checks if the given header is an Entity Header"""
+    return header.lower() in _ENTITY_HEADERS
+
+
+def is_hop_by_hop_header(header):
+    """Checks if the given header is a Hop By Hop header"""
+    return header.lower() in _HOP_BY_HOP_HEADERS
+
+
+def import_string(module_name, package=None):
+    """
+    import a module or class by string path.
+
+    :module_name: str with path of module or path to import and
+    instantiate a class
+    :returns: a module object or one instance from class if
+    module_name is a valid path to class
+
+    """
+    module, klass = module_name.rsplit(".", 1)
+    module = import_module(module, package=package)
+    obj = getattr(module, klass)
+    if ismodule(obj):
+        return obj
+    return obj()
+
+
+def is_atty() -> bool:
+    return bool(sys.stdout and sys.stdout.isatty())
+
+
+class Default:
+    """
+    It is used to replace `None` or `object()` as a sentinel
+    that represents a default value. Sometimes we want to set
+    a value to `None` so we cannot use `None` to represent the
+    default value, and `object()` is hard to be typed.
+    """
+
+    def __repr__(self):
+        return ""
+
+    def __str__(self) -> str:
+        return self.__repr__()
+
+
+_default = Default()
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/__init__.py b/.venv/lib/python3.12/site-packages/sanic/http/__init__.py
new file mode 100644
index 0000000..1045128
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/__init__.py
@@ -0,0 +1,6 @@
+from .constants import Stage
+from .http1 import Http
+from .http3 import Http3
+
+
+__all__ = ("Http", "Stage", "Http3")
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/constants.py b/.venv/lib/python3.12/site-packages/sanic/http/constants.py
new file mode 100644
index 0000000..f9886f9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/constants.py
@@ -0,0 +1,30 @@
+from enum import Enum, IntEnum
+
+
+class Stage(Enum):
+    """Enum for representing the stage of the request/response cycle
+
+    | ``IDLE``  Waiting for request
+    | ``REQUEST``  Request headers being received
+    | ``HANDLER``  Headers done, handler running
+    | ``RESPONSE``  Response headers sent, body in progress
+    | ``FAILED``  Unrecoverable state (error while sending response)
+    |
+    """
+
+    IDLE = 0  # Waiting for request
+    REQUEST = 1  # Request headers being received
+    HANDLER = 3  # Headers done, handler running
+    RESPONSE = 4  # Response headers sent, body in progress
+    FAILED = 100  # Unrecoverable state (error while sending response)
+
+
+class HTTP(IntEnum):
+    """Enum for representing HTTP versions"""
+
+    VERSION_1 = 1
+    VERSION_3 = 3
+
+    def display(self) -> str:
+        value = 1.1 if self.value == 1 else self.value
+        return f"HTTP/{value}"
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/http1.py b/.venv/lib/python3.12/site-packages/sanic/http/http1.py
new file mode 100644
index 0000000..ea0988e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/http1.py
@@ -0,0 +1,619 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+
+if TYPE_CHECKING:
+    from sanic.request import Request
+    from sanic.response import BaseHTTPResponse
+
+from asyncio import CancelledError, sleep
+from time import perf_counter
+
+from sanic.compat import Header
+from sanic.exceptions import (
+    BadRequest,
+    ExpectationFailed,
+    PayloadTooLarge,
+    RequestCancelled,
+    ServerError,
+    ServiceUnavailable,
+)
+from sanic.headers import format_http1_response
+from sanic.helpers import has_message_body
+from sanic.http.constants import Stage
+from sanic.http.stream import Stream
+from sanic.log import access_logger, error_logger, logger
+from sanic.touchup import TouchUpMeta
+
+
+HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n"
+
+
+class Http(Stream, metaclass=TouchUpMeta):
+    """ "Internal helper for managing the HTTP/1.1 request/response cycle.
+
+    Raises:
+        BadRequest: If the request body is malformed.
+        Exception: If the request is malformed.
+        ExpectationFailed: If the request is malformed.
+        PayloadTooLarge: If the request body exceeds the size limit.
+        RuntimeError: If the response status is invalid.
+        ServerError: If the handler does not produce a response.
+        ServerError: If the response is bigger than the content-length.
+    """
+
+    HEADER_CEILING = 16_384
+    HEADER_MAX_SIZE = 0
+    __touchup__ = (
+        "http1_request_header",
+        "http1_response_header",
+        "read",
+    )
+    __slots__ = [
+        "_send",
+        "_receive_more",
+        "dispatch",
+        "recv_buffer",
+        "protocol",
+        "expecting_continue",
+        "stage",
+        "keep_alive",
+        "head_only",
+        "request",
+        "exception",
+        "url",
+        "request_body",
+        "request_bytes",
+        "request_bytes_left",
+        "response",
+        "response_func",
+        "response_size",
+        "response_bytes_left",
+        "upgrade_websocket",
+        "perft0",
+    ]
+
+    def __init__(self, protocol):
+        self._send = protocol.send
+        self._receive_more = protocol.receive_more
+        self.recv_buffer = protocol.recv_buffer
+        self.protocol = protocol
+        self.keep_alive = True
+        self.stage: Stage = Stage.IDLE
+        self.dispatch = self.protocol.app.dispatch
+
+    def init_for_request(self):
+        """Init/reset all per-request variables."""
+        self.exception = None
+        self.expecting_continue: bool = False
+        self.head_only = None
+        self.request_body = None
+        self.request_bytes = None
+        self.request_bytes_left = None
+        self.request_max_size = self.protocol.request_max_size
+        self.request: Request = None
+        self.response: BaseHTTPResponse = None
+        self.upgrade_websocket = False
+        self.url = None
+        self.perft0 = None
+
+    def __bool__(self):
+        """Test if request handling is in progress"""
+        return self.stage in (Stage.HANDLER, Stage.RESPONSE)
+
+    async def http1(self):
+        """HTTP 1.1 connection handler"""
+        # Handle requests while the connection stays reusable
+        while self.keep_alive and self.stage is Stage.IDLE:
+            self.init_for_request()
+            # Wait for incoming bytes (in IDLE stage)
+            if not self.recv_buffer:
+                await self._receive_more()
+            self.stage = Stage.REQUEST
+            try:
+                # Receive and handle a request
+                self.response_func = self.http1_response_header
+
+                await self.http1_request_header()
+
+                self.stage = Stage.HANDLER
+                self.perft0 = perf_counter()
+                self.request.conn_info = self.protocol.conn_info
+                await self.protocol.request_handler(self.request)
+
+                # Handler finished, response should've been sent
+                if self.stage is Stage.HANDLER and not self.upgrade_websocket:
+                    raise ServerError("Handler produced no response")
+
+                if self.stage is Stage.RESPONSE:
+                    await self.response.send(end_stream=True)
+            except CancelledError as exc:
+                # Write an appropriate response before exiting
+                if not self.protocol.transport:
+                    logger.info(
+                        f"Request: {self.request.method} {self.request.url} "
+                        "stopped. Transport is closed."
+                    )
+                    return
+                e = (
+                    RequestCancelled()
+                    if self.protocol.conn_info.lost
+                    else (self.exception or exc)
+                )
+                self.exception = None
+                self.keep_alive = False
+                await self.error_response(e)
+            except Exception as e:
+                # Write an error response
+                await self.error_response(e)
+
+            # Try to consume any remaining request body
+            if self.request_body:
+                if self.response and 200 <= self.response.status < 300:
+                    error_logger.error(f"{self.request} body not consumed.")
+                # Limit the size because the handler may have set it infinite
+                self.request_max_size = min(
+                    self.request_max_size, self.protocol.request_max_size
+                )
+                try:
+                    async for _ in self:
+                        pass
+                except PayloadTooLarge:
+                    # We won't read the body and that may cause httpx and
+                    # tests to fail. This little delay allows clients to push
+                    # a small request into network buffers before we close the
+                    # socket, so that they are then able to read the response.
+                    await sleep(0.001)
+                    self.keep_alive = False
+
+            # Clean up to free memory and for the next request
+            if self.request:
+                self.request.stream = None
+                if self.response:
+                    self.response.stream = None
+
+    async def http1_request_header(self):  # no cov
+        """Receive and parse request header into self.request."""
+        # Receive until full header is in buffer
+        buf = self.recv_buffer
+        pos = 0
+
+        while True:
+            pos = buf.find(b"\r\n\r\n", pos)
+            if pos != -1:
+                break
+
+            pos = max(0, len(buf) - 3)
+            if pos >= self.HEADER_MAX_SIZE:
+                break
+
+            await self._receive_more()
+
+        if pos >= self.HEADER_MAX_SIZE:
+            raise PayloadTooLarge("Request header exceeds the size limit")
+
+        # Parse header content
+        try:
+            head = buf[:pos]
+            raw_headers = head.decode(errors="surrogateescape")
+            reqline, *split_headers = raw_headers.split("\r\n")
+            method, self.url, protocol = reqline.split(" ")
+
+            await self.dispatch(
+                "http.lifecycle.read_head",
+                inline=True,
+                context={"head": bytes(head)},
+            )
+
+            if protocol == "HTTP/1.1":
+                self.keep_alive = True
+            elif protocol == "HTTP/1.0":
+                self.keep_alive = False
+            else:
+                raise Exception  # Raise a Bad Request on try-except
+
+            self.head_only = method.upper() == "HEAD"
+            request_body = False
+            headers = []
+
+            for name, value in (h.split(":", 1) for h in split_headers):
+                name, value = h = name.lower(), value.lstrip()
+
+                if name in ("content-length", "transfer-encoding"):
+                    if request_body:
+                        raise ValueError(
+                            "Duplicate Content-Length or Transfer-Encoding"
+                        )
+                    request_body = True
+                elif name == "connection":
+                    self.keep_alive = value.lower() == "keep-alive"
+
+                headers.append(h)
+        except Exception:
+            raise BadRequest("Bad Request")
+
+        if not self.protocol.app.config.KEEP_ALIVE:
+            self.keep_alive = False
+
+        headers_instance = Header(headers)
+        self.upgrade_websocket = (
+            headers_instance.getone("upgrade", "").lower() == "websocket"
+        )
+
+        try:
+            url_bytes = self.url.encode("ASCII")
+        except UnicodeEncodeError:
+            raise BadRequest("URL may only contain US-ASCII characters.")
+
+        # Prepare a Request object
+        request = self.protocol.request_class(
+            url_bytes=url_bytes,
+            headers=headers_instance,
+            head=bytes(head),
+            version=protocol[5:],
+            method=method,
+            transport=self.protocol.transport,
+            app=self.protocol.app,
+        )
+        self.protocol.request_class._current.set(request)
+        await self.dispatch(
+            "http.lifecycle.request",
+            inline=True,
+            context={"request": request},
+        )
+
+        # Prepare for request body
+        self.request_bytes_left = self.request_bytes = 0
+        if request_body:
+            headers = request.headers
+            expect = headers.getone("expect", None)
+
+            if expect is not None:
+                if expect.lower() == "100-continue":
+                    self.expecting_continue = True
+                else:
+                    raise ExpectationFailed(f"Unknown Expect: {expect}")
+
+            if headers.getone("transfer-encoding", None) == "chunked":
+                self.request_body = "chunked"
+                pos -= 2  # One CRLF stays in buffer
+            else:
+                self.request_body = True
+                try:
+                    self.request_bytes_left = self.request_bytes = (
+                        self._safe_int(headers["content-length"])
+                    )
+                except Exception:
+                    raise BadRequest("Bad content-length")
+
+        # Remove header and its trailing CRLF
+        del buf[: pos + 4]
+        self.request, request.stream = request, self
+        self.protocol.state["requests_count"] += 1
+
+    async def http1_response_header(
+        self, data: bytes, end_stream: bool
+    ) -> None:  # no cov
+        """Format response header and send it."""
+        res = self.response
+
+        # Compatibility with simple response body
+        if not data and getattr(res, "body", None):
+            data, end_stream = res.body, True  # type: ignore
+
+        size = len(data)
+        headers = res.headers
+        status = res.status
+        self.response_size = size
+
+        if not isinstance(status, int) or status < 200:
+            raise RuntimeError(f"Invalid response status {status!r}")
+
+        if not has_message_body(status):
+            # Header-only response status
+            self.response_func = None
+            if (
+                data
+                or not end_stream
+                or "content-length" in headers
+                or "transfer-encoding" in headers
+            ):
+                data, size, end_stream = b"", 0, True
+                headers.pop("content-length", None)
+                headers.pop("transfer-encoding", None)
+                logger.warning(
+                    f"Message body set in response on {self.request.path}. "
+                    f"A {status} response may only have headers, no body."
+                )
+        elif self.head_only and "content-length" in headers:
+            self.response_func = None
+        elif end_stream:
+            # Non-streaming response (all in one block)
+            headers["content-length"] = size
+            self.response_func = None
+        elif "content-length" in headers:
+            # Streaming response with size known in advance
+            self.response_bytes_left = int(headers["content-length"]) - size
+            self.response_func = self.http1_response_normal
+        else:
+            # Length not known, use chunked encoding
+            headers["transfer-encoding"] = "chunked"
+            data = b"%x\r\n%b\r\n" % (size, data) if size else b""
+            self.response_func = self.http1_response_chunked
+
+        if self.head_only:
+            # Head request: don't send body
+            data = b""
+            self.response_func = self.head_response_ignored
+
+        headers["connection"] = "keep-alive" if self.keep_alive else "close"
+
+        # This header may be removed or modified by the AltSvcCheck Touchup
+        # service. At server start, we either remove this header from ever
+        # being assigned, or we change the value as required.
+        headers["alt-svc"] = ""
+
+        ret = format_http1_response(status, res.processed_headers)
+        if data:
+            ret += data
+
+        # Send a 100-continue if expected and not Expectation Failed
+        if self.expecting_continue:
+            self.expecting_continue = False
+            if status != 417:
+                ret = HTTP_CONTINUE + ret
+
+        # Send response
+        if self.protocol.access_log:
+            self.log_response()
+
+        await self._send(ret)
+        self.stage = Stage.IDLE if end_stream else Stage.RESPONSE
+
+    def head_response_ignored(self, data: bytes, end_stream: bool) -> None:
+        """HEAD response: body data silently ignored."""
+        if end_stream:
+            self.response_func = None
+            self.stage = Stage.IDLE
+
+    async def http1_response_chunked(
+        self, data: bytes, end_stream: bool
+    ) -> None:
+        """Format a part of response body in chunked encoding."""
+        # Chunked encoding
+        size = len(data)
+        if end_stream:
+            await self._send(
+                b"%x\r\n%b\r\n0\r\n\r\n" % (size, data)
+                if size
+                else b"0\r\n\r\n"
+            )
+            self.response_func = None
+            self.stage = Stage.IDLE
+        elif size:
+            await self._send(b"%x\r\n%b\r\n" % (size, data))
+
+    async def http1_response_normal(
+        self, data: bytes, end_stream: bool
+    ) -> None:
+        """Format / keep track of non-chunked response."""
+        bytes_left = self.response_bytes_left - len(data)
+        if bytes_left <= 0:
+            if bytes_left < 0:
+                raise ServerError("Response was bigger than content-length")
+
+            await self._send(data)
+            self.response_func = None
+            self.stage = Stage.IDLE
+        else:
+            if end_stream:
+                raise ServerError("Response was smaller than content-length")
+
+            await self._send(data)
+        self.response_bytes_left = bytes_left
+
+    async def error_response(self, exception: Exception) -> None:
+        """Handle response when exception encountered"""
+        # Disconnect after an error if in any other state than handler
+        if self.stage is not Stage.HANDLER:
+            self.keep_alive = False
+
+        # Request failure? Respond but then disconnect
+        if self.stage is Stage.REQUEST:
+            self.stage = Stage.HANDLER
+
+        # From request and handler states we can respond, otherwise be silent
+        if self.stage is Stage.HANDLER:
+            app = self.protocol.app
+
+            if self.request is None:
+                self.create_empty_request()
+
+            request_middleware = not isinstance(
+                exception, (ServiceUnavailable, RequestCancelled)
+            )
+            try:
+                await app.handle_exception(
+                    self.request, exception, request_middleware
+                )
+            except Exception as e:
+                await app.handle_exception(self.request, e, False)
+
+    def create_empty_request(self) -> None:
+        """Create an empty request object for error handling use.
+
+        Current error handling code needs a request object that won't exist
+        if an error occurred during before a request was received. Create a
+        bogus response for error handling use.
+        """
+
+        # Reformat any URL already received with \xHH escapes for better logs
+        url_bytes = (
+            self.url.encode(errors="surrogateescape")
+            .decode("ASCII", errors="backslashreplace")
+            .encode("ASCII")
+            if self.url
+            else b"*"
+        )
+
+        # FIXME: Avoid this by refactoring error handling and response code
+        self.request = self.protocol.request_class(
+            url_bytes=url_bytes,
+            headers=Header({}),
+            version="1.1",
+            method="NONE",
+            transport=self.protocol.transport,
+            app=self.protocol.app,
+        )
+        self.request.stream = self
+
+    def log_response(self) -> None:
+        """Helper method provided to enable the logging of responses in case if the `HttpProtocol.access_log` is enabled."""  # noqa: E501
+        req, res = self.request, self.response
+        extra = {
+            "status": getattr(res, "status", 0),
+            "byte": res.headers.get("content-length", 0)
+            if res.headers.get("transfer-encoding") != "chunked"
+            else "chunked",
+            "host": f"{id(self.protocol.transport):X}"[-5:-1] + "unx",
+            "request": "nil",
+            "duration": (
+                f" {1000 * (perf_counter() - self.perft0):.1f}ms"
+                if self.perft0 is not None
+                else ""
+            ),
+        }
+        if ip := req.client_ip:
+            extra["host"] = f"{ip}:{req.port}"
+        extra["request"] = f"{req.method} {req.url}"
+        access_logger.info("", extra=extra)
+
+    # Request methods
+
+    async def __aiter__(self):
+        """Async iterate over request body."""
+        while self.request_body:
+            data = await self.read()
+
+            if data:
+                yield data
+
+    async def read(self) -> bytes | None:  # no cov
+        """Read some bytes of request body."""
+
+        # Send a 100-continue if needed
+        if self.expecting_continue:
+            self.expecting_continue = False
+            await self._send(HTTP_CONTINUE)
+
+        # Receive request body chunk
+        buf = self.recv_buffer
+        if self.request_bytes_left == 0 and self.request_body == "chunked":
+            # Process a chunk header: \r\n[;]\r\n
+            while True:
+                pos = buf.find(b"\r\n", 3)
+
+                if pos != -1:
+                    break
+
+                if len(buf) > 64:
+                    self.keep_alive = False
+                    raise BadRequest("Bad chunked encoding")
+
+                await self._receive_more()
+
+            try:
+                raw = buf[2:pos].split(b";", 1)[0].decode()
+                size = self._safe_int(raw, 16)
+            except Exception:
+                self.keep_alive = False
+                raise BadRequest("Bad chunked encoding")
+
+            if size <= 0:
+                self.request_body = None
+
+                if size < 0:
+                    self.keep_alive = False
+                    raise BadRequest("Bad chunked encoding")
+
+                # Consume CRLF, chunk size 0 and the two CRLF that follow
+                pos += 4
+                # Might need to wait for the final CRLF
+                while len(buf) < pos:
+                    await self._receive_more()
+                del buf[:pos]
+                return None
+
+            # Remove CRLF, chunk size and the CRLF that follows
+            del buf[: pos + 2]
+
+            self.request_bytes_left = size
+            self.request_bytes += size
+
+        # Request size limit
+        if self.request_bytes > self.request_max_size:
+            self.keep_alive = False
+            raise PayloadTooLarge("Request body exceeds the size limit")
+
+        # End of request body?
+        if not self.request_bytes_left:
+            self.request_body = None
+            return None
+
+        # At this point we are good to read/return up to request_bytes_left
+        if not buf:
+            await self._receive_more()
+
+        data = bytes(buf[: self.request_bytes_left])
+        size = len(data)
+
+        del buf[:size]
+
+        self.request_bytes_left -= size
+
+        await self.dispatch(
+            "http.lifecycle.read_body",
+            inline=True,
+            context={"body": data},
+        )
+
+        return data
+
+    # Response methods
+
+    def respond(self, response: BaseHTTPResponse) -> BaseHTTPResponse:
+        """Initiate new streaming response.
+
+        Nothing is sent until the first send() call on the returned object, and
+        calling this function multiple times will just alter the response to be
+        given.
+        """
+        if self.stage is not Stage.HANDLER:
+            self.stage = Stage.FAILED
+            raise RuntimeError("Response already started")
+
+        # Disconnect any earlier but unused response object
+        if self.response is not None:
+            self.response.stream = None
+
+        # Connect and return the response
+        self.response, response.stream = response, self
+        return response
+
+    @property
+    def send(self):
+        return self.response_func
+
+    @classmethod
+    def set_header_max_size(cls, *sizes: int):
+        cls.HEADER_MAX_SIZE = min(
+            *sizes,
+            cls.HEADER_CEILING,
+        )
+
+    @staticmethod
+    def _safe_int(value: str, base: int = 10) -> int:
+        if "-" in value or "+" in value or "_" in value:
+            raise ValueError
+        return int(value, base)
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/http3.py b/.venv/lib/python3.12/site-packages/sanic/http/http3.py
new file mode 100644
index 0000000..2fcc987
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/http3.py
@@ -0,0 +1,423 @@
+from __future__ import annotations
+
+import asyncio
+
+from abc import ABC, abstractmethod
+from ssl import SSLContext
+from typing import TYPE_CHECKING, Any, Callable, cast
+
+from sanic.compat import Header
+from sanic.constants import LocalCertCreator
+from sanic.exceptions import (
+    BadRequest,
+    PayloadTooLarge,
+    SanicException,
+    ServerError,
+)
+from sanic.helpers import has_message_body
+from sanic.http.constants import Stage
+from sanic.http.stream import Stream
+from sanic.http.tls.context import CertSelector, SanicSSLContext
+from sanic.log import Colors, logger
+from sanic.models.protocol_types import TransportProtocol
+from sanic.models.server_types import ConnInfo
+
+
+try:
+    from aioquic.h0.connection import H0_ALPN, H0Connection
+    from aioquic.h3.connection import H3_ALPN, H3Connection
+    from aioquic.h3.events import (
+        DatagramReceived,
+        DataReceived,
+        H3Event,
+        HeadersReceived,
+        WebTransportStreamDataReceived,
+    )
+    from aioquic.quic.configuration import QuicConfiguration
+    from aioquic.tls import SessionTicket
+
+    HTTP3_AVAILABLE = True
+except ModuleNotFoundError:  # no cov
+    HTTP3_AVAILABLE = False
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+    from sanic.request import Request
+    from sanic.response import BaseHTTPResponse
+    from sanic.server.protocols.http_protocol import Http3Protocol
+
+    HttpConnection = H0Connection | H3Connection
+
+
+class HTTP3Transport(TransportProtocol):
+    """HTTP/3 transport implementation."""
+
+    __slots__ = ("_protocol",)
+
+    def __init__(self, protocol: Http3Protocol):
+        self._protocol = protocol
+
+    def get_protocol(self) -> Http3Protocol:
+        return self._protocol
+
+    def get_extra_info(self, info: str, default: Any = None) -> Any:
+        if (
+            info in ("socket", "sockname", "peername")
+            and self._protocol._transport
+        ):
+            return self._protocol._transport.get_extra_info(info, default)
+        elif info == "network_paths":
+            return self._protocol._quic._network_paths
+        elif info == "ssl_context":
+            return self._protocol.app.state.ssl
+        return default
+
+
+class Receiver(ABC):
+    """HTTP/3 receiver base class."""
+
+    future: asyncio.Future
+
+    def __init__(self, transmit, protocol, request: Request) -> None:
+        self.transmit = transmit
+        self.protocol = protocol
+        self.request = request
+
+    @abstractmethod
+    async def run(self):  # no cov
+        ...
+
+
+class HTTPReceiver(Receiver, Stream):
+    """HTTP/3 receiver implementation."""
+
+    stage: Stage
+    request: Request
+
+    def __init__(self, *args, **kwargs) -> None:
+        super().__init__(*args, **kwargs)
+        self.request_body = None
+        self.stage = Stage.IDLE
+        self.headers_sent = False
+        self.response: BaseHTTPResponse | None = None
+        self.request_max_size = self.protocol.request_max_size
+        self.request_bytes = 0
+
+    async def run(self, exception: Exception | None = None):
+        """Handle the request and response cycle."""
+        self.stage = Stage.HANDLER
+        self.head_only = self.request.method.upper() == "HEAD"
+
+        if exception:
+            logger.info(  # no cov
+                f"{Colors.BLUE}[exception]: "
+                f"{Colors.RED}{exception}{Colors.END}",
+                exc_info=True,
+                extra={"verbosity": 1},
+            )
+            await self.error_response(exception)
+        else:
+            try:
+                logger.info(  # no cov
+                    f"{Colors.BLUE}[request]:{Colors.END} {self.request}",
+                    extra={"verbosity": 1},
+                )
+                await self.protocol.request_handler(self.request)
+            except Exception as e:  # no cov
+                # This should largely be handled within the request handler.
+                # But, just in case...
+                await self.run(e)
+        self.stage = Stage.IDLE
+
+    async def error_response(self, exception: Exception) -> None:
+        """Handle response when exception encountered"""
+        # From request and handler states we can respond, otherwise be silent
+        app = self.protocol.app
+
+        await app.handle_exception(self.request, exception)
+
+    def _prepare_headers(
+        self, response: BaseHTTPResponse
+    ) -> list[tuple[bytes, bytes]]:
+        size = len(response.body) if response.body else 0
+        headers = response.headers
+        status = response.status
+
+        if not has_message_body(status) and (
+            size
+            or "content-length" in headers
+            or "transfer-encoding" in headers
+        ):
+            headers.pop("content-length", None)
+            headers.pop("transfer-encoding", None)
+            logger.warning(  # no cov
+                f"Message body set in response on {self.request.path}. "
+                f"A {status} response may only have headers, no body."
+            )
+        elif "content-length" not in headers:
+            if size:
+                headers["content-length"] = size
+            else:
+                headers["transfer-encoding"] = "chunked"
+
+        headers = [
+            (b":status", str(response.status).encode()),
+            *response.processed_headers,
+        ]
+        return headers
+
+    def send_headers(self) -> None:
+        """Send response headers to client"""
+        logger.debug(  # no cov
+            f"{Colors.BLUE}[send]: {Colors.GREEN}HEADERS{Colors.END}",
+            extra={"verbosity": 2},
+        )
+        if not self.response:
+            raise RuntimeError("no response")
+
+        response = self.response
+        headers = self._prepare_headers(response)
+
+        self.protocol.connection.send_headers(
+            stream_id=self.request.stream_id,
+            headers=headers,
+        )
+        self.headers_sent = True
+        self.stage = Stage.RESPONSE
+
+        if self.response.body and not self.head_only:
+            self._send(self.response.body, False)
+        elif self.head_only:
+            self.future.cancel()
+
+    def respond(self, response: BaseHTTPResponse) -> BaseHTTPResponse:
+        """Prepare response to client"""
+        logger.debug(  # no cov
+            f"{Colors.BLUE}[respond]:{Colors.END} {response}",
+            extra={"verbosity": 2},
+        )
+
+        if self.stage is not Stage.HANDLER:
+            self.stage = Stage.FAILED
+            raise RuntimeError("Response already started")
+
+        # Disconnect any earlier but unused response object
+        if self.response is not None:
+            self.response.stream = None
+
+        self.response, response.stream = response, self
+
+        return response
+
+    def receive_body(self, data: bytes) -> None:
+        """Receive request body from client"""
+        self.request_bytes += len(data)
+        if self.request_bytes > self.request_max_size:
+            raise PayloadTooLarge("Request body exceeds the size limit")
+
+        self.request.body += data
+
+    async def send(self, data: bytes, end_stream: bool) -> None:
+        """Send data to client"""
+        logger.debug(  # no cov
+            f"{Colors.BLUE}[send]: {Colors.GREEN}data={data.decode()} "
+            f"end_stream={end_stream}{Colors.END}",
+            extra={"verbosity": 2},
+        )
+        self._send(data, end_stream)
+
+    def _send(self, data: bytes, end_stream: bool) -> None:
+        if not self.headers_sent:
+            self.send_headers()
+        if self.stage is not Stage.RESPONSE:
+            raise ServerError(f"not ready to send: {self.stage}")
+
+        # Chunked
+        if (
+            self.response
+            and self.response.headers.get("transfer-encoding") == "chunked"
+        ):
+            size = len(data)
+            if end_stream:
+                data = (
+                    b"%x\r\n%b\r\n0\r\n\r\n" % (size, data)
+                    if size
+                    else b"0\r\n\r\n"
+                )
+            elif size:
+                data = b"%x\r\n%b\r\n" % (size, data)
+
+        logger.debug(  # no cov
+            f"{Colors.BLUE}[transmitting]{Colors.END}",
+            extra={"verbosity": 2},
+        )
+        self.protocol.connection.send_data(
+            stream_id=self.request.stream_id,
+            data=data,
+            end_stream=end_stream,
+        )
+        self.transmit()
+
+        if end_stream:
+            self.stage = Stage.IDLE
+
+
+class WebsocketReceiver(Receiver):  # noqa
+    """Websocket receiver implementation."""
+
+    async def run(self): ...
+
+
+class WebTransportReceiver(Receiver):  # noqa
+    """WebTransport receiver implementation."""
+
+    async def run(self): ...
+
+
+class Http3:
+    """Internal helper for managing the HTTP/3 request/response cycle"""
+
+    if HTTP3_AVAILABLE:
+        HANDLER_PROPERTY_MAPPING = {
+            DataReceived: "stream_id",
+            HeadersReceived: "stream_id",
+            DatagramReceived: "flow_id",
+            WebTransportStreamDataReceived: "session_id",
+        }
+
+    def __init__(
+        self,
+        protocol: Http3Protocol,
+        transmit: Callable[[], None],
+    ) -> None:
+        self.protocol = protocol
+        self.transmit = transmit
+        self.receivers: dict[int, Receiver] = {}
+
+    def http_event_received(self, event: H3Event) -> None:
+        logger.debug(  # no cov
+            f"{Colors.BLUE}[http_event_received]: "
+            f"{Colors.YELLOW}{event}{Colors.END}",
+            extra={"verbosity": 2},
+        )
+        receiver, created_new = self.get_or_make_receiver(event)
+        receiver = cast(HTTPReceiver, receiver)
+
+        if isinstance(event, HeadersReceived) and created_new:
+            receiver.future = asyncio.ensure_future(receiver.run())
+        elif isinstance(event, DataReceived):
+            try:
+                receiver.receive_body(event.data)
+            except Exception as e:
+                receiver.future.cancel()
+                receiver.future = asyncio.ensure_future(receiver.run(e))
+        else:
+            ...  # Intentionally here to help out Touchup
+            logger.debug(  # no cov
+                f"{Colors.RED}DOING NOTHING{Colors.END}",
+                extra={"verbosity": 2},
+            )
+
+    def get_or_make_receiver(self, event: H3Event) -> tuple[Receiver, bool]:
+        if (
+            isinstance(event, HeadersReceived)
+            and event.stream_id not in self.receivers
+        ):
+            request = self._make_request(event)
+            receiver = HTTPReceiver(self.transmit, self.protocol, request)
+            request.stream = receiver
+
+            self.receivers[event.stream_id] = receiver
+            return receiver, True
+        else:
+            ident = getattr(event, self.HANDLER_PROPERTY_MAPPING[type(event)])
+            return self.receivers[ident], False
+
+    def get_receiver_by_stream_id(self, stream_id: int) -> Receiver:
+        return self.receivers[stream_id]
+
+    def _make_request(self, event: HeadersReceived) -> Request:
+        try:
+            headers = Header(
+                (
+                    (k.decode("ASCII"), v.decode(errors="surrogateescape"))
+                    for k, v in event.headers
+                )
+            )
+        except UnicodeDecodeError:
+            raise BadRequest(
+                "Header names may only contain US-ASCII characters."
+            )
+        method = headers[":method"]
+        path = headers[":path"]
+        scheme = headers.pop(":scheme", "")
+        authority = headers.pop(":authority", "")
+
+        if authority:
+            headers["host"] = authority
+
+        try:
+            url_bytes = path.encode("ASCII")
+        except UnicodeEncodeError:
+            raise BadRequest("URL may only contain US-ASCII characters.")
+
+        transport = HTTP3Transport(self.protocol)
+        request = self.protocol.request_class(
+            url_bytes,
+            headers,
+            "3",
+            method,
+            transport,
+            self.protocol.app,
+            b"",
+        )
+        request.conn_info = ConnInfo(transport)
+        request._stream_id = event.stream_id
+        request._scheme = scheme
+
+        return request
+
+
+class SessionTicketStore:
+    """
+    Simple in-memory store for session tickets.
+    """
+
+    def __init__(self) -> None:
+        self.tickets: dict[bytes, SessionTicket] = {}
+
+    def add(self, ticket: SessionTicket) -> None:
+        self.tickets[ticket.ticket] = ticket
+
+    def pop(self, label: bytes) -> SessionTicket | None:
+        return self.tickets.pop(label, None)
+
+
+def get_config(app: Sanic, ssl: SanicSSLContext | CertSelector | SSLContext):
+    # TODO:
+    # - proper selection needed if service with multiple certs insted of
+    #   just taking the first
+    if isinstance(ssl, CertSelector):
+        ssl = cast(SanicSSLContext, ssl.sanic_select[0])
+    if app.config.LOCAL_CERT_CREATOR is LocalCertCreator.TRUSTME:
+        raise SanicException(
+            "Sorry, you cannot currently use trustme as a local certificate "
+            "generator for an HTTP/3 server. This is not yet supported. You "
+            "should be able to use mkcert instead. For more information, see: "
+            "https://github.com/aiortc/aioquic/issues/295."
+        )
+    if not isinstance(ssl, SanicSSLContext):
+        raise SanicException("SSLContext is not SanicSSLContext")
+
+    config = QuicConfiguration(
+        alpn_protocols=H3_ALPN + H0_ALPN + ["siduck"],
+        is_client=False,
+        max_datagram_frame_size=65536,
+    )
+    password = app.config.TLS_CERT_PASSWORD or None
+
+    config.load_cert_chain(
+        ssl.sanic["cert"], ssl.sanic["key"], password=password
+    )
+
+    return config
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/stream.py b/.venv/lib/python3.12/site-packages/sanic/http/stream.py
new file mode 100644
index 0000000..027b621
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/stream.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from sanic.http.constants import Stage
+
+
+if TYPE_CHECKING:
+    from sanic.response import BaseHTTPResponse
+    from sanic.server.protocols.http_protocol import HttpProtocol
+
+
+class Stream:
+    stage: Stage
+    response: BaseHTTPResponse | None
+    protocol: HttpProtocol
+    url: str | None
+    request_body: bytes | None
+    request_max_size: int | float
+
+    __touchup__: tuple[str, ...] = tuple()
+    __slots__ = ("request_max_size",)
+
+    def respond(
+        self, response: BaseHTTPResponse
+    ) -> BaseHTTPResponse:  # no cov
+        raise NotImplementedError("Not implemented")
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/tls/__init__.py b/.venv/lib/python3.12/site-packages/sanic/http/tls/__init__.py
new file mode 100644
index 0000000..b12fe52
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/tls/__init__.py
@@ -0,0 +1,5 @@
+from .context import process_to_context
+from .creators import get_ssl_context
+
+
+__all__ = ("get_ssl_context", "process_to_context")
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/tls/context.py b/.venv/lib/python3.12/site-packages/sanic/http/tls/context.py
new file mode 100644
index 0000000..3200f17
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/tls/context.py
@@ -0,0 +1,210 @@
+from __future__ import annotations
+
+import os
+import ssl
+
+from collections.abc import Iterable
+from typing import Any
+
+from sanic.log import logger
+
+
+# Only allow secure ciphers, notably leaving out AES-CBC mode
+# OpenSSL chooses ECDSA or RSA depending on the cert in use
+CIPHERS_TLS12 = [
+    "ECDHE-ECDSA-CHACHA20-POLY1305",
+    "ECDHE-ECDSA-AES256-GCM-SHA384",
+    "ECDHE-ECDSA-AES128-GCM-SHA256",
+    "ECDHE-RSA-CHACHA20-POLY1305",
+    "ECDHE-RSA-AES256-GCM-SHA384",
+    "ECDHE-RSA-AES128-GCM-SHA256",
+]
+
+
+def create_context(
+    certfile: str | None = None,
+    keyfile: str | None = None,
+    password: str | None = None,
+    purpose: ssl.Purpose = ssl.Purpose.CLIENT_AUTH,
+) -> ssl.SSLContext:
+    """Create a context with secure crypto and HTTP/1.1 in protocols."""
+    context = ssl.create_default_context(purpose=purpose)
+    context.minimum_version = ssl.TLSVersion.TLSv1_2
+    context.set_ciphers(":".join(CIPHERS_TLS12))
+    context.set_alpn_protocols(["http/1.1"])
+    if purpose is ssl.Purpose.CLIENT_AUTH:
+        context.sni_callback = server_name_callback
+    if certfile and keyfile:
+        context.load_cert_chain(certfile, keyfile, password)
+    return context
+
+
+def shorthand_to_ctx(
+    ctxdef: None | ssl.SSLContext | dict | str,
+) -> ssl.SSLContext | None:
+    """Convert an ssl argument shorthand to an SSLContext object."""
+    if ctxdef is None or isinstance(ctxdef, ssl.SSLContext):
+        return ctxdef
+    if isinstance(ctxdef, str):
+        return load_cert_dir(ctxdef)
+    if isinstance(ctxdef, dict):
+        return CertSimple(**ctxdef)
+    raise ValueError(
+        f"Invalid ssl argument {type(ctxdef)}."
+        " Expecting a list of certdirs, a dict or an SSLContext."
+    )
+
+
+def process_to_context(
+    ssldef: None | ssl.SSLContext | dict | str | list | tuple,
+) -> ssl.SSLContext | None:
+    """Process app.run ssl argument from easy formats to full SSLContext."""
+    return (
+        CertSelector(map(shorthand_to_ctx, ssldef))
+        if isinstance(ssldef, (list, tuple))
+        else shorthand_to_ctx(ssldef)
+    )
+
+
+def load_cert_dir(p: str) -> ssl.SSLContext:
+    if os.path.isfile(p):
+        raise ValueError(f"Certificate folder expected but {p} is a file.")
+    keyfile = os.path.join(p, "privkey.pem")
+    certfile = os.path.join(p, "fullchain.pem")
+    if not os.access(keyfile, os.R_OK):
+        raise ValueError(
+            f"Certificate not found or permission denied {keyfile}"
+        )
+    if not os.access(certfile, os.R_OK):
+        raise ValueError(
+            f"Certificate not found or permission denied {certfile}"
+        )
+    return CertSimple(certfile, keyfile)
+
+
+def find_cert(self: CertSelector, server_name: str):
+    """Find the first certificate that matches the given SNI.
+
+    :raises ssl.CertificateError: No matching certificate found.
+    :return: A matching ssl.SSLContext object if found."""
+    if not server_name:
+        if self.sanic_fallback:
+            return self.sanic_fallback
+        raise ValueError(
+            "The client provided no SNI to match for certificate."
+        )
+    for ctx in self.sanic_select:
+        if match_hostname(ctx, server_name):
+            return ctx
+    if self.sanic_fallback:
+        return self.sanic_fallback
+    raise ValueError(f"No certificate found matching hostname {server_name!r}")
+
+
+def match_hostname(ctx: ssl.SSLContext | CertSelector, hostname: str) -> bool:
+    """Match names from CertSelector against a received hostname."""
+    # Local certs are considered trusted, so this can be less pedantic
+    # and thus faster than the deprecated ssl.match_hostname function is.
+    names = dict(getattr(ctx, "sanic", {})).get("names", [])
+    hostname = hostname.lower()
+    for name in names:
+        if name.startswith("*."):
+            if hostname.split(".", 1)[-1] == name[2:]:
+                return True
+        elif name == hostname:
+            return True
+    return False
+
+
+def selector_sni_callback(
+    sslobj: ssl.SSLObject, server_name: str, ctx: CertSelector
+) -> int | None:
+    """Select a certificate matching the SNI."""
+    # Call server_name_callback to store the SNI on sslobj
+    server_name_callback(sslobj, server_name, ctx)
+    # Find a new context matching the hostname
+    try:
+        sslobj.context = find_cert(ctx, server_name)
+    except ValueError as e:
+        logger.warning(f"Rejecting TLS connection: {e}")
+        # This would show ERR_SSL_UNRECOGNIZED_NAME_ALERT on client side if
+        # asyncio/uvloop did proper SSL shutdown. They don't.
+        return ssl.ALERT_DESCRIPTION_UNRECOGNIZED_NAME
+    return None  # mypy complains without explicit return
+
+
+def server_name_callback(
+    sslobj: ssl.SSLObject, server_name: str, ctx: ssl.SSLContext
+) -> None:
+    """Store the received SNI as sslobj.sanic_server_name."""
+    sslobj.sanic_server_name = server_name  # type: ignore
+
+
+class SanicSSLContext(ssl.SSLContext):
+    sanic: dict[str, os.PathLike]
+
+    @classmethod
+    def create_from_ssl_context(cls, context: ssl.SSLContext):
+        context.__class__ = cls
+        return context
+
+
+class CertSimple(SanicSSLContext):
+    """A wrapper for creating SSLContext with a sanic attribute."""
+
+    sanic: dict[str, Any]
+
+    def __new__(cls, cert, key, **kw):
+        # try common aliases, rename to cert/key
+        certfile = kw["cert"] = kw.pop("certificate", None) or cert
+        keyfile = kw["key"] = kw.pop("keyfile", None) or key
+        password = kw.get("password", None)
+        if not certfile or not keyfile:
+            raise ValueError("SSL dict needs filenames for cert and key.")
+        subject = {}
+        if "names" not in kw:
+            cert = ssl._ssl._test_decode_cert(certfile)  # type: ignore
+            kw["names"] = [
+                name
+                for t, name in cert["subjectAltName"]
+                if t in ["DNS", "IP Address"]
+            ]
+            subject = {k: v for item in cert["subject"] for k, v in item}
+        self = create_context(certfile, keyfile, password)
+        self.__class__ = cls
+        self.sanic = {**subject, **kw}
+        return self
+
+    def __init__(self, cert, key, **kw):
+        pass  # Do not call super().__init__ because it is already initialized
+
+
+class CertSelector(ssl.SSLContext):
+    """Automatically select SSL certificate based on the hostname that the
+    client is trying to access, via SSL SNI. Paths to certificate folders
+    with privkey.pem and fullchain.pem in them should be provided, and
+    will be matched in the order given whenever there is a new connection.
+    """
+
+    def __new__(cls, ctxs):
+        return super().__new__(cls)
+
+    def __init__(self, ctxs: Iterable[ssl.SSLContext | None]):
+        super().__init__()
+        self.sni_callback = selector_sni_callback  # type: ignore
+        self.sanic_select = []
+        self.sanic_fallback = None
+        all_names = []
+        for i, ctx in enumerate(ctxs):
+            if not ctx:
+                continue
+            names = dict(getattr(ctx, "sanic", {})).get("names", [])
+            all_names += names
+            self.sanic_select.append(ctx)
+            if i == 0:
+                self.sanic_fallback = ctx
+        if not all_names:
+            raise ValueError(
+                "No certificates with SubjectAlternativeNames found."
+            )
+        logger.info(f"Certificate vhosts: {', '.join(all_names)}")
diff --git a/.venv/lib/python3.12/site-packages/sanic/http/tls/creators.py b/.venv/lib/python3.12/site-packages/sanic/http/tls/creators.py
new file mode 100644
index 0000000..8ece9c5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/http/tls/creators.py
@@ -0,0 +1,287 @@
+from __future__ import annotations
+
+import ssl
+import subprocess
+import sys
+
+from abc import ABC, abstractmethod
+from contextlib import suppress
+from pathlib import Path
+from tempfile import mkdtemp
+from types import ModuleType
+from typing import TYPE_CHECKING, cast
+
+from sanic.application.constants import Mode
+from sanic.application.spinner import loading
+from sanic.constants import (
+    DEFAULT_LOCAL_TLS_CERT,
+    DEFAULT_LOCAL_TLS_KEY,
+    LocalCertCreator,
+)
+from sanic.exceptions import SanicException
+from sanic.helpers import Default
+from sanic.http.tls.context import CertSimple, SanicSSLContext
+
+
+try:
+    import trustme
+
+    TRUSTME_INSTALLED = True
+except (ImportError, ModuleNotFoundError):
+    trustme = ModuleType("trustme")
+    TRUSTME_INSTALLED = False
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+# Only allow secure ciphers, notably leaving out AES-CBC mode
+# OpenSSL chooses ECDSA or RSA depending on the cert in use
+CIPHERS_TLS12 = [
+    "ECDHE-ECDSA-CHACHA20-POLY1305",
+    "ECDHE-ECDSA-AES256-GCM-SHA384",
+    "ECDHE-ECDSA-AES128-GCM-SHA256",
+    "ECDHE-RSA-CHACHA20-POLY1305",
+    "ECDHE-RSA-AES256-GCM-SHA384",
+    "ECDHE-RSA-AES128-GCM-SHA256",
+]
+
+
+def _make_path(maybe_path: Path | str, tmpdir: Path | None) -> Path:
+    if isinstance(maybe_path, Path):
+        return maybe_path
+    else:
+        path = Path(maybe_path)
+        if not path.exists():
+            if not tmpdir:
+                raise RuntimeError("Reached an unknown state. No tmpdir.")
+            return tmpdir / maybe_path
+
+    return path
+
+
+def get_ssl_context(app: Sanic, ssl: ssl.SSLContext | None) -> ssl.SSLContext:
+    if ssl:
+        return ssl
+
+    if app.state.mode is Mode.PRODUCTION:
+        raise SanicException(
+            "Cannot run Sanic as an HTTPS server in PRODUCTION mode "
+            "without passing a TLS certificate. If you are developing "
+            "locally, please enable DEVELOPMENT mode and Sanic will "
+            "generate a localhost TLS certificate. For more information "
+            "please see: https://sanic.dev/en/guide/deployment/development."
+            "html#automatic-tls-certificate."
+        )
+
+    creator = CertCreator.select(
+        app,
+        cast(LocalCertCreator, app.config.LOCAL_CERT_CREATOR),
+        app.config.LOCAL_TLS_KEY,
+        app.config.LOCAL_TLS_CERT,
+    )
+    context = creator.generate_cert(app.config.LOCALHOST)
+    return context
+
+
+class CertCreator(ABC):
+    def __init__(self, app, key, cert) -> None:
+        self.app = app
+        self.key = key
+        self.cert = cert
+        self.tmpdir = None
+
+        if isinstance(self.key, Default) or isinstance(self.cert, Default):
+            self.tmpdir = Path(mkdtemp())
+
+        key = (
+            DEFAULT_LOCAL_TLS_KEY
+            if isinstance(self.key, Default)
+            else self.key
+        )
+        cert = (
+            DEFAULT_LOCAL_TLS_CERT
+            if isinstance(self.cert, Default)
+            else self.cert
+        )
+
+        self.key_path = _make_path(key, self.tmpdir)
+        self.cert_path = _make_path(cert, self.tmpdir)
+
+    @abstractmethod
+    def check_supported(self) -> None:  # no cov
+        ...
+
+    @abstractmethod
+    def generate_cert(self, localhost: str) -> ssl.SSLContext:  # no cov
+        ...
+
+    @classmethod
+    def select(
+        cls,
+        app: Sanic,
+        cert_creator: LocalCertCreator,
+        local_tls_key,
+        local_tls_cert,
+    ) -> CertCreator:
+        creator: CertCreator | None = None
+
+        cert_creator_options: tuple[
+            tuple[type[CertCreator], LocalCertCreator], ...
+        ] = (
+            (MkcertCreator, LocalCertCreator.MKCERT),
+            (TrustmeCreator, LocalCertCreator.TRUSTME),
+        )
+        for creator_class, local_creator in cert_creator_options:
+            creator = cls._try_select(
+                app,
+                creator,
+                creator_class,
+                local_creator,
+                cert_creator,
+                local_tls_key,
+                local_tls_cert,
+            )
+            if creator:
+                break
+
+        if not creator:
+            raise SanicException(
+                "Sanic could not find package to create a TLS certificate. "
+                "You must have either mkcert or trustme installed. See "
+                "https://sanic.dev/en/guide/deployment/development.html"
+                "#automatic-tls-certificate for more details."
+            )
+
+        return creator
+
+    @staticmethod
+    def _try_select(
+        app: Sanic,
+        creator: CertCreator | None,
+        creator_class: type[CertCreator],
+        creator_requirement: LocalCertCreator,
+        creator_requested: LocalCertCreator,
+        local_tls_key,
+        local_tls_cert,
+    ):
+        if creator or (
+            creator_requested is not LocalCertCreator.AUTO
+            and creator_requested is not creator_requirement
+        ):
+            return creator
+
+        instance = creator_class(app, local_tls_key, local_tls_cert)
+        try:
+            instance.check_supported()
+        except SanicException:
+            if creator_requested is creator_requirement:
+                raise
+            else:
+                return None
+
+        return instance
+
+
+class MkcertCreator(CertCreator):
+    def check_supported(self) -> None:
+        try:
+            subprocess.run(  # nosec B603 B607
+                ["mkcert", "-help"],
+                check=True,
+                stderr=subprocess.DEVNULL,
+                stdout=subprocess.DEVNULL,
+            )
+        except Exception as e:
+            raise SanicException(
+                "Sanic is attempting to use mkcert to generate local TLS "
+                "certificates since you did not supply a certificate, but "
+                "one is required. Sanic cannot proceed since mkcert does not "
+                "appear to be installed. Alternatively, you can use trustme. "
+                "Please install mkcert, trustme, or supply TLS certificates "
+                "to proceed. Installation instructions can be found here: "
+                "https://github.com/FiloSottile/mkcert.\n"
+                "Find out more information about your options here: "
+                "https://sanic.dev/en/guide/deployment/development.html#"
+                "automatic-tls-certificate"
+            ) from e
+
+    def generate_cert(self, localhost: str) -> ssl.SSLContext:
+        try:
+            if not self.cert_path.exists():
+                message = "Generating TLS certificate"
+                # TODO: Validate input for security
+                with loading(message):
+                    cmd = [
+                        "mkcert",
+                        "-key-file",
+                        str(self.key_path),
+                        "-cert-file",
+                        str(self.cert_path),
+                        localhost,
+                    ]
+                    resp = subprocess.run(  # nosec B603
+                        cmd,
+                        check=True,
+                        stdout=subprocess.PIPE,
+                        stderr=subprocess.STDOUT,
+                        text=True,
+                    )
+                sys.stdout.write("\r" + " " * (len(message) + 4))
+                sys.stdout.flush()
+                sys.stdout.write(resp.stdout)
+        finally:
+
+            @self.app.main_process_stop
+            async def cleanup(*_):  # no cov
+                if self.tmpdir:
+                    with suppress(FileNotFoundError):
+                        self.key_path.unlink()
+                        self.cert_path.unlink()
+                    self.tmpdir.rmdir()
+
+        context = CertSimple(self.cert_path, self.key_path)
+        context.sanic["creator"] = "mkcert"
+        context.sanic["localhost"] = localhost
+        SanicSSLContext.create_from_ssl_context(context)
+
+        return context
+
+
+class TrustmeCreator(CertCreator):
+    def check_supported(self) -> None:
+        if not TRUSTME_INSTALLED:
+            raise SanicException(
+                "Sanic is attempting to use trustme to generate local TLS "
+                "certificates since you did not supply a certificate, but "
+                "one is required. Sanic cannot proceed since trustme does not "
+                "appear to be installed. Alternatively, you can use mkcert. "
+                "Please install mkcert, trustme, or supply TLS certificates "
+                "to proceed. Installation instructions can be found here: "
+                "https://github.com/python-trio/trustme.\n"
+                "Find out more information about your options here: "
+                "https://sanic.dev/en/guide/deployment/development.html#"
+                "automatic-tls-certificate"
+            )
+
+    def generate_cert(self, localhost: str) -> ssl.SSLContext:
+        context = SanicSSLContext.create_from_ssl_context(
+            ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+        )
+        context.sanic = {
+            "cert": self.cert_path.absolute(),
+            "key": self.key_path.absolute(),
+        }
+        ca = trustme.CA()
+        server_cert = ca.issue_cert(localhost)
+        server_cert.configure_cert(context)
+        ca.configure_trust(context)
+
+        ca.cert_pem.write_to_path(str(self.cert_path.absolute()))
+        server_cert.private_key_and_cert_chain_pem.write_to_path(
+            str(self.key_path.absolute())
+        )
+        context.sanic["creator"] = "trustme"
+        context.sanic["localhost"] = localhost
+
+        return context
diff --git a/.venv/lib/python3.12/site-packages/sanic/log.py b/.venv/lib/python3.12/site-packages/sanic/log.py
new file mode 100644
index 0000000..cdbc2e0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/log.py
@@ -0,0 +1,24 @@
+from sanic.logging.color import Colors
+from sanic.logging.default import LOGGING_CONFIG_DEFAULTS
+from sanic.logging.deprecation import deprecation
+from sanic.logging.filter import VerbosityFilter
+from sanic.logging.loggers import (
+    access_logger,
+    error_logger,
+    logger,
+    server_logger,
+    websockets_logger,
+)
+
+
+__all__ = (
+    "deprecation",
+    "logger",
+    "access_logger",
+    "error_logger",
+    "server_logger",
+    "websockets_logger",
+    "VerbosityFilter",
+    "Colors",
+    "LOGGING_CONFIG_DEFAULTS",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/__init__.py b/.venv/lib/python3.12/site-packages/sanic/logging/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/color.py b/.venv/lib/python3.12/site-packages/sanic/logging/color.py
new file mode 100644
index 0000000..bd1126a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/color.py
@@ -0,0 +1,68 @@
+import logging
+import os
+import sys
+
+from enum import Enum
+from typing import TYPE_CHECKING
+
+from sanic.helpers import is_atty
+
+
+# Python 3.11 changed the way Enum formatting works for mixed-in types.
+if sys.version_info < (3, 11, 0):
+
+    class StrEnum(str, Enum):
+        pass
+
+else:
+    if not TYPE_CHECKING:
+        from enum import StrEnum
+
+
+COLORIZE = is_atty() and not os.environ.get("SANIC_NO_COLOR")
+
+
+class Colors(StrEnum):  # no cov
+    """
+    Colors for log messages. If the output is not a TTY, the colors will be
+    disabled.
+
+    Can be used like this:
+
+    .. code-block:: python
+
+        from sanic.log import logger, Colors
+
+        logger.info(f"{Colors.GREEN}This is a green message{Colors.END}")
+
+
+    Attributes:
+        END: Reset the color
+        BOLD: Bold text
+        BLUE: Blue text
+        GREEN: Green text
+        PURPLE: Purple text
+        RED: Red text
+        SANIC: Sanic pink
+        YELLOW: Yellow text
+        GREY: Grey text
+    """
+
+    END = "\033[0m" if COLORIZE else ""
+    BOLD = "\033[1m" if COLORIZE else ""
+    BLUE = "\033[34m" if COLORIZE else ""
+    GREEN = "\033[32m" if COLORIZE else ""
+    PURPLE = "\033[35m" if COLORIZE else ""
+    CYAN = "\033[36m" if COLORIZE else ""
+    RED = "\033[31m" if COLORIZE else ""
+    YELLOW = "\033[33m" if COLORIZE else ""
+    GREY = "\033[38;5;240m" if COLORIZE else ""
+    SANIC = "\033[38;2;255;13;104m" if COLORIZE else ""
+
+
+LEVEL_COLORS = {
+    logging.DEBUG: Colors.BLUE,
+    logging.WARNING: Colors.YELLOW,
+    logging.ERROR: Colors.RED,
+    logging.CRITICAL: Colors.RED + Colors.BOLD,
+}
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/default.py b/.venv/lib/python3.12/site-packages/sanic/logging/default.py
new file mode 100644
index 0000000..4ad7d4f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/default.py
@@ -0,0 +1,60 @@
+import sys
+
+from typing import Any
+
+
+LOGGING_CONFIG_DEFAULTS: dict[str, Any] = dict(  # no cov
+    version=1,
+    disable_existing_loggers=False,
+    loggers={
+        "sanic.root": {"level": "INFO", "handlers": ["console"]},
+        "sanic.error": {
+            "level": "INFO",
+            "handlers": ["error_console"],
+            "propagate": True,
+            "qualname": "sanic.error",
+        },
+        "sanic.access": {
+            "level": "INFO",
+            "handlers": ["access_console"],
+            "propagate": True,
+            "qualname": "sanic.access",
+        },
+        "sanic.server": {
+            "level": "INFO",
+            "handlers": ["console"],
+            "propagate": True,
+            "qualname": "sanic.server",
+        },
+        "sanic.websockets": {
+            "level": "INFO",
+            "handlers": ["console"],
+            "propagate": True,
+            "qualname": "sanic.websockets",
+        },
+    },
+    handlers={
+        "console": {
+            "class": "logging.StreamHandler",
+            "formatter": "generic",
+            "stream": sys.stdout,
+        },
+        "error_console": {
+            "class": "logging.StreamHandler",
+            "formatter": "generic",
+            "stream": sys.stderr,
+        },
+        "access_console": {
+            "class": "logging.StreamHandler",
+            "formatter": "access",
+            "stream": sys.stdout,
+        },
+    },
+    formatters={
+        "generic": {"class": "sanic.logging.formatter.AutoFormatter"},
+        "access": {"class": "sanic.logging.formatter.AutoAccessFormatter"},
+    },
+)
+"""
+Default logging configuration
+"""
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/deprecation.py b/.venv/lib/python3.12/site-packages/sanic/logging/deprecation.py
new file mode 100644
index 0000000..e4efe24
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/deprecation.py
@@ -0,0 +1,33 @@
+from warnings import warn
+
+from sanic.helpers import is_atty
+from sanic.logging.color import Colors
+
+
+def deprecation(message: str, version: float):  # no cov
+    """
+    Add a deprecation notice
+
+    Example when a feature is being removed. In this case, version
+    should be AT LEAST next version + 2
+
+    .. code-block:: python
+
+        deprecation("Helpful message", 99.9)
+
+    Example when a feature is deprecated but not being removed:
+
+    .. code-block:: python
+
+        deprecation("Helpful message", 0)
+
+    Args:
+        message (str): Deprecation message
+        version (float): Version when the feature will be removed
+    """
+    version_display = f" v{version}" if version else ""
+    version_info = f"[DEPRECATION{version_display}] "
+    if is_atty():
+        version_info = f"{Colors.RED}{version_info}"
+        message = f"{Colors.YELLOW}{message}{Colors.END}"
+    warn(version_info + message, DeprecationWarning)
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/filter.py b/.venv/lib/python3.12/site-packages/sanic/logging/filter.py
new file mode 100644
index 0000000..69a064b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/filter.py
@@ -0,0 +1,13 @@
+import logging
+
+
+class VerbosityFilter(logging.Filter):
+    """
+    Filter log records based on verbosity level.
+    """
+
+    verbosity: int = 0
+
+    def filter(self, record: logging.LogRecord) -> bool:
+        verbosity = getattr(record, "verbosity", 0)
+        return verbosity <= self.verbosity
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/formatter.py b/.venv/lib/python3.12/site-packages/sanic/logging/formatter.py
new file mode 100644
index 0000000..f6003a5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/formatter.py
@@ -0,0 +1,387 @@
+from __future__ import annotations
+
+import logging
+import os
+import re
+
+from sanic.helpers import is_atty, json_dumps
+from sanic.logging.color import LEVEL_COLORS
+from sanic.logging.color import Colors as c
+
+
+CONTROL_RE = re.compile(r"\033\[[0-9;]*\w")
+CONTROL_LIMIT_IDENT = "\033[1000D\033[{limit}C"
+CONTROL_LIMIT_START = "\033[1000D\033[{start}C\033[K"
+CONTROL_LIMIT_END = "\033[1000C\033[{right}D\033[K"
+EXCEPTION_LINE_RE = re.compile(r"^(?P.*?): (?P.*)$")
+FILE_LINE_RE = re.compile(
+    r"File \"(?P.*?)\", line (?P\d+), in (?P.*)"
+)
+DEFAULT_FIELDS = set(
+    logging.LogRecord("", 0, "", 0, "", (), None).__dict__.keys()
+) | {
+    "ident",
+    "message",
+    "asctime",
+    "right",
+}
+
+
+class AutoFormatter(logging.Formatter):
+    """
+    Automatically sets up the formatter based on the environment.
+
+    It will switch between the Debug and Production formatters based upon
+    how the environment is set up. Additionally, it will automatically
+    detect if the output is a TTY and colorize the output accordingly.
+    """
+
+    SETUP = False
+    ATTY = is_atty()
+    NO_COLOR = os.environ.get("SANIC_NO_COLOR", "false").lower() == "true"
+    LOG_EXTRA = os.environ.get("SANIC_LOG_EXTRA", "true").lower() == "true"
+    IDENT = os.environ.get("SANIC_WORKER_IDENTIFIER", "Main ") or "Main "
+    DATE_FORMAT = "%Y-%m-%d %H:%M:%S %z"
+    IDENT_LIMIT = 5
+    MESSAGE_START = 42
+    PREFIX_FORMAT = (
+        f"{c.GREY}%(ident)s{{limit}} %(asctime)s {c.END}"
+        "%(levelname)s: {start}"
+    )
+    MESSAGE_FORMAT = "%(message)s"
+
+    def __init__(self, *args) -> None:
+        args_list = list(args)
+        if not args:
+            args_list.append(self._make_format())
+        elif args and not args[0]:
+            args_list[0] = self._make_format()
+        if len(args_list) < 2:
+            args_list.append(self.DATE_FORMAT)
+        elif not args[1]:
+            args_list[1] = self.DATE_FORMAT
+
+        super().__init__(*args_list)
+
+    def format(self, record: logging.LogRecord) -> str:
+        record.ident = self.IDENT
+        self._set_levelname(record)
+        output = super().format(record)
+        if self.LOG_EXTRA:
+            output += self._log_extra(record)
+        return output
+
+    def _set_levelname(self, record: logging.LogRecord) -> None:
+        if (
+            self.ATTY
+            and not self.NO_COLOR
+            and (color := LEVEL_COLORS.get(record.levelno))
+        ):
+            record.levelname = f"{color}{record.levelname}{c.END}"
+
+    def _make_format(self) -> str:
+        limit = CONTROL_LIMIT_IDENT.format(limit=self.IDENT_LIMIT)
+        start = CONTROL_LIMIT_START.format(start=self.MESSAGE_START)
+        base_format = self.PREFIX_FORMAT + self.MESSAGE_FORMAT
+        fmt = base_format.format(limit=limit, start=start)
+        if not self.ATTY or self.NO_COLOR:
+            return CONTROL_RE.sub("", fmt)
+        return fmt
+
+    def _log_extra(self, record: logging.LogRecord, indent: int = 0) -> str:
+        extra_lines = [""]
+
+        for key, value in record.__dict__.items():
+            if key not in DEFAULT_FIELDS:
+                extra_lines.append(self._format_key_value(key, value, indent))
+
+        return "\n".join(extra_lines)
+
+    def _format_key_value(self, key, value, indent):
+        indentation = " " * indent
+        template = (
+            f"{indentation}  {{c.YELLOW}}{{key}}{{c.END}}={{value}}"
+            if self.ATTY and not self.NO_COLOR
+            else f"{indentation}{{key}}={{value}}"
+        )
+        if isinstance(value, dict):
+            nested_lines = [template.format(c=c, key=key, value="")]
+            for nested_key, nested_value in value.items():
+                nested_lines.append(
+                    self._format_key_value(
+                        nested_key, nested_value, indent + 2
+                    )
+                )
+            return "\n".join(nested_lines)
+        else:
+            return template.format(c=c, key=key, value=value)
+
+
+class DebugFormatter(AutoFormatter):
+    """
+    The DebugFormatter is used for development and debugging purposes.
+
+    It can be used directly, or it will be automatically selected if the
+    environment is set up for development and is using the AutoFormatter.
+    """
+
+    IDENT_LIMIT = 5
+    MESSAGE_START = 23
+    DATE_FORMAT = "%H:%M:%S"
+
+    def _set_levelname(self, record: logging.LogRecord) -> None:
+        if len(record.levelname) > 5:
+            record.levelname = record.levelname[:4]
+        super()._set_levelname(record)
+
+    def formatException(self, ei):  # no cov
+        orig = super().formatException(ei)
+        if not self.ATTY or self.NO_COLOR:
+            return orig
+        colored_traceback = []
+        lines = orig.splitlines()
+        for idx, line in enumerate(lines):
+            if line.startswith("  File"):
+                line = self._color_file_line(line)
+            elif line.startswith("    "):
+                line = self._color_code_line(line)
+            elif (
+                "Error" in line or "Exception" in line or len(lines) - 1 == idx
+            ):
+                line = self._color_exception_line(line)
+            colored_traceback.append(line)
+        return "\n".join(colored_traceback)
+
+    def _color_exception_line(self, line: str) -> str:  # no cov
+        match = EXCEPTION_LINE_RE.match(line)
+        if not match:
+            return line
+        exc = match.group("exc")
+        message = match.group("message")
+        return f"{c.SANIC}{c.BOLD}{exc}{c.END}: {c.BOLD}{message}{c.END}"
+
+    def _color_file_line(self, line: str) -> str:  # no cov
+        match = FILE_LINE_RE.search(line)
+        if not match:
+            return line
+        path = match.group("path")
+        line_num = match.group("line_num")
+        location = match.group("location")
+        return (
+            f'  File "{path}", line {c.CYAN}{c.BOLD}{line_num}{c.END}, '
+            f"in {c.BLUE}{c.BOLD}{location}{c.END}"
+        )
+
+    def _color_code_line(self, line: str) -> str:  # no cov
+        return f"{c.YELLOW}{line}{c.END}"
+
+
+class ProdFormatter(AutoFormatter):
+    """
+    The ProdFormatter is used for production environments.
+
+    It can be used directly, or it will be automatically selected if the
+    environment is set up for production and is using the AutoFormatter.
+    """
+
+
+class LegacyFormatter(AutoFormatter):
+    """
+    The LegacyFormatter is used if you want to use the old style of logging.
+
+    You can use it as follows, typically in conjunction with the
+    LegacyAccessFormatter:
+
+    .. code-block:: python
+
+        from sanic.log import LOGGING_CONFIG_DEFAULTS
+
+        LOGGING_CONFIG_DEFAULTS["formatters"] = {
+            "generic": {
+                "class": "sanic.logging.formatter.LegacyFormatter"
+            },
+            "access": {
+                "class": "sanic.logging.formatter.LegacyAccessFormatter"
+            },
+        }
+    """
+
+    PREFIX_FORMAT = "%(asctime)s [%(process)s] [%(levelname)s] "
+    DATE_FORMAT = "[%Y-%m-%d %H:%M:%S %z]"
+
+
+class AutoAccessFormatter(AutoFormatter):
+    MESSAGE_FORMAT = (
+        f"{c.PURPLE}%(host)s "
+        f"{c.BLUE + c.BOLD}%(request)s{c.END} "
+        f"%(right)s%(status)s %(byte)s {c.GREY}%(duration)s{c.END}"
+    )
+
+    def format(self, record: logging.LogRecord) -> str:
+        status = len(str(getattr(record, "status", "")))
+        byte = len(str(getattr(record, "byte", "")))
+        duration = len(str(getattr(record, "duration", "")))
+        record.right = (
+            CONTROL_LIMIT_END.format(right=status + byte + duration + 1)
+            if self.ATTY
+            else ""
+        )
+        return super().format(record)
+
+    def _set_levelname(self, record: logging.LogRecord) -> None:
+        if self.ATTY and record.levelno == logging.INFO:
+            record.levelname = f"{c.SANIC}ACCESS{c.END}"
+
+
+class LegacyAccessFormatter(AutoAccessFormatter):
+    """
+    The LegacyFormatter is used if you want to use the old style of logging.
+
+    You can use it as follows, typically in conjunction with the
+    LegacyFormatter:
+
+    .. code-block:: python
+
+        from sanic.log import LOGGING_CONFIG_DEFAULTS
+
+        LOGGING_CONFIG_DEFAULTS["formatters"] = {
+            "generic": {
+                "class": "sanic.logging.formatter.LegacyFormatter"
+            },
+            "access": {
+                "class": "sanic.logging.formatter.LegacyAccessFormatter"
+            },
+        }
+    """
+
+    PREFIX_FORMAT = "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: "
+    MESSAGE_FORMAT = "%(request)s %(message)s %(status)s %(byte)s"
+
+
+class DebugAccessFormatter(AutoAccessFormatter):
+    IDENT_LIMIT = 5
+    MESSAGE_START = 23
+    DATE_FORMAT = "%H:%M:%S"
+    LOG_EXTRA = False
+
+
+class ProdAccessFormatter(AutoAccessFormatter):
+    IDENT_LIMIT = 5
+    MESSAGE_START = 42
+    PREFIX_FORMAT = (
+        f"{c.GREY}%(ident)s{{limit}}|%(asctime)s{c.END} "
+        f"%(levelname)s: {{start}}"
+    )
+    MESSAGE_FORMAT = (
+        f"{c.PURPLE}%(host)s {c.BLUE + c.BOLD}"
+        f"%(request)s{c.END} "
+        f"%(right)s%(status)s %(byte)s {c.GREY}%(duration)s{c.END}"
+    )
+    LOG_EXTRA = False
+
+
+class JSONFormatter(AutoFormatter):
+    """
+    The JSONFormatter is used to output logs in JSON format.
+
+    This is useful for logging to a file or to a log aggregator that
+    understands JSON. It will output all the fields from the LogRecord
+    as well as the extra fields that are passed in.
+
+    You can use it as follows:
+
+    .. code-block:: python
+
+        from sanic.log import LOGGING_CONFIG_DEFAULTS
+
+        LOGGING_CONFIG_DEFAULTS["formatters"] = {
+            "generic": {
+                "class": "sanic.logging.formatter.JSONFormatter"
+            },
+            "access": {
+                "class": "sanic.logging.formatter.JSONFormatter"
+            },
+        }
+    """
+
+    ATTY = False
+    NO_COLOR = True
+    FIELDS = [
+        "name",
+        "levelno",
+        "pathname",
+        "module",
+        "filename",
+        "lineno",
+    ]
+
+    dumps = json_dumps
+
+    def format(self, record: logging.LogRecord) -> str:
+        return self.format_dict(self.to_dict(record))
+
+    def to_dict(self, record: logging.LogRecord) -> dict:
+        base = {field: getattr(record, field, None) for field in self.FIELDS}
+        extra = {
+            key: value
+            for key, value in record.__dict__.items()
+            if key not in DEFAULT_FIELDS
+        }
+        info = {}
+        if record.exc_info:
+            info["exc_info"] = self.formatException(record.exc_info)
+        if record.stack_info:
+            info["stack_info"] = self.formatStack(record.stack_info)
+        return {
+            "timestamp": self.formatTime(record, self.datefmt),
+            "level": record.levelname,
+            "message": record.getMessage(),
+            **base,
+            **info,
+            **extra,
+        }
+
+    def format_dict(self, record: dict) -> str:
+        return self.dumps(record)
+
+
+class JSONAccessFormatter(JSONFormatter):
+    """
+    The JSONAccessFormatter is used to output access logs in JSON format.
+
+    This is useful for logging to a file or to a log aggregator that
+    understands JSON. It will output all the fields from the LogRecord
+    as well as the extra fields that are passed in.
+
+    You can use it as follows:
+
+    .. code-block:: python
+
+        from sanic.log import LOGGING_CONFIG_DEFAULTS
+
+        LOGGING_CONFIG_DEFAULTS["formatters"] = {
+            "generic": {
+                "class": "sanic.logging.formatter.JSONFormatter"
+            },
+            "access": {
+                "class": "sanic.logging.formatter.JSONAccessFormatter"
+            },
+        }
+    """
+
+    FIELDS = [
+        "host",
+        "request",
+        "status",
+        "byte",
+        "duration",
+    ]
+
+    def to_dict(self, record: logging.LogRecord) -> dict:
+        base = {field: getattr(record, field, None) for field in self.FIELDS}
+        return {
+            "timestamp": self.formatTime(record, self.datefmt),
+            "level": record.levelname,
+            "message": record.getMessage(),
+            **base,
+        }
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/loggers.py b/.venv/lib/python3.12/site-packages/sanic/logging/loggers.py
new file mode 100644
index 0000000..0957886
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/loggers.py
@@ -0,0 +1,37 @@
+import logging
+
+from sanic.logging.filter import VerbosityFilter
+
+
+_verbosity_filter = VerbosityFilter()
+
+logger = logging.getLogger("sanic.root")  # no cov
+"""
+General Sanic logger
+"""
+logger.addFilter(_verbosity_filter)
+
+error_logger = logging.getLogger("sanic.error")  # no cov
+"""
+Logger used by Sanic for error logging
+"""
+error_logger.addFilter(_verbosity_filter)
+
+access_logger = logging.getLogger("sanic.access")  # no cov
+"""
+Logger used by Sanic for access logging
+"""
+access_logger.addFilter(_verbosity_filter)
+
+server_logger = logging.getLogger("sanic.server")  # no cov
+"""
+Logger used by Sanic for server related messages
+"""
+server_logger.addFilter(_verbosity_filter)
+
+websockets_logger = logging.getLogger("sanic.websockets")  # no cov
+"""
+Logger used by Sanic for websockets module and protocol related messages
+"""
+websockets_logger.addFilter(_verbosity_filter)
+websockets_logger.setLevel(logging.WARNING)  # Too noisy on debug/info
diff --git a/.venv/lib/python3.12/site-packages/sanic/logging/setup.py b/.venv/lib/python3.12/site-packages/sanic/logging/setup.py
new file mode 100644
index 0000000..5552a7c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/logging/setup.py
@@ -0,0 +1,61 @@
+import logging
+import os
+
+from sanic.helpers import Default, _default
+from sanic.log import (
+    access_logger,
+    error_logger,
+    logger,
+    server_logger,
+    websockets_logger,
+)
+from sanic.logging.formatter import (
+    AutoAccessFormatter,
+    AutoFormatter,
+    DebugAccessFormatter,
+    DebugFormatter,
+    ProdAccessFormatter,
+    ProdFormatter,
+)
+
+
+def setup_logging(
+    debug: bool,
+    no_color: bool = False,
+    log_extra: bool | Default = _default,
+) -> None:
+    if AutoFormatter.SETUP:
+        return
+
+    if isinstance(log_extra, Default):
+        log_extra = debug
+        os.environ["SANIC_LOG_EXTRA"] = str(log_extra)
+    AutoFormatter.LOG_EXTRA = log_extra
+
+    if no_color:
+        os.environ["SANIC_NO_COLOR"] = str(no_color)
+        AutoFormatter.NO_COLOR = no_color
+
+    AutoFormatter.SETUP = True
+
+    for lggr in (logger, server_logger, error_logger, websockets_logger):
+        _auto_format(
+            lggr,
+            AutoFormatter,
+            DebugFormatter if debug else ProdFormatter,
+        )
+    _auto_format(
+        access_logger,
+        AutoAccessFormatter,
+        DebugAccessFormatter if debug else ProdAccessFormatter,
+    )
+
+
+def _auto_format(
+    logger: logging.Logger,
+    auto_class: type[AutoFormatter],
+    formatter_class: type[AutoFormatter],
+) -> None:
+    for handler in logger.handlers:
+        if type(handler.formatter) is auto_class:
+            handler.setFormatter(formatter_class())
diff --git a/.venv/lib/python3.12/site-packages/sanic/middleware.py b/.venv/lib/python3.12/site-packages/sanic/middleware.py
new file mode 100644
index 0000000..65098e1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/middleware.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+from collections import deque
+from collections.abc import Sequence
+from enum import IntEnum, auto
+from itertools import count
+
+from sanic.models.handler_types import MiddlewareType
+
+
+class MiddlewareLocation(IntEnum):
+    REQUEST = auto()
+    RESPONSE = auto()
+
+
+class Middleware:
+    """Middleware object that is used to encapsulate middleware functions.
+
+    This should generally not be instantiated directly, but rather through
+    the `sanic.Sanic.middleware` decorator and its variants.
+
+    Args:
+        func (MiddlewareType): The middleware function to be called.
+        location (MiddlewareLocation): The location of the middleware.
+        priority (int): The priority of the middleware.
+    """
+
+    _counter = count()
+    count: int
+
+    __slots__ = ("func", "priority", "location", "definition")
+
+    def __init__(
+        self,
+        func: MiddlewareType,
+        location: MiddlewareLocation,
+        priority: int = 0,
+    ) -> None:
+        self.func = func
+        self.priority = priority
+        self.location = location
+        self.definition = next(Middleware._counter)
+
+    def __call__(self, *args, **kwargs):
+        return self.func(*args, **kwargs)
+
+    def __hash__(self) -> int:
+        return hash(self.func)
+
+    def __repr__(self) -> str:
+        return (
+            f"{self.__class__.__name__}("
+            f"func=, "
+            f"priority={self.priority}, "
+            f"location={self.location.name})"
+        )
+
+    @property
+    def order(self) -> tuple[int, int]:
+        """Return a tuple of the priority and definition order.
+
+        This is used to sort the middleware.
+
+        Returns:
+            tuple[int, int]: The priority and definition order.
+        """
+        return (self.priority, -self.definition)
+
+    @classmethod
+    def convert(
+        cls,
+        *middleware_collections: Sequence[Middleware | MiddlewareType],
+        location: MiddlewareLocation,
+    ) -> deque[Middleware]:
+        """Convert middleware collections to a deque of Middleware objects.
+
+        Args:
+            *middleware_collections (Sequence[Union[Middleware, MiddlewareType]]):
+                The middleware collections to convert.
+            location (MiddlewareLocation): The location of the middleware.
+
+        Returns:
+            Deque[Middleware]: The converted middleware.
+        """  # noqa: E501
+        return deque(
+            [
+                middleware
+                if isinstance(middleware, Middleware)
+                else Middleware(middleware, location)
+                for collection in middleware_collections
+                for middleware in collection
+            ]
+        )
+
+    @classmethod
+    def reset_count(cls) -> None:
+        """Reset the counter for the middleware definition order.
+
+        This is used for testing.
+
+        Returns:
+            None
+        """
+        cls._counter = count()
+        cls.count = next(cls._counter)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/__init__.py b/.venv/lib/python3.12/site-packages/sanic/mixins/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/base.py b/.venv/lib/python3.12/site-packages/sanic/mixins/base.py
new file mode 100644
index 0000000..66b3cc8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/base.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+from typing import Protocol
+
+from sanic.base.meta import SanicMeta
+
+
+class NameProtocol(Protocol):
+    name: str
+
+
+class DunderNameProtocol(Protocol):
+    __name__: str
+
+
+class BaseMixin(metaclass=SanicMeta):
+    """Base class for various mixins."""
+
+    name: str
+    strict_slashes: bool | None
+
+    def _generate_name(
+        self, *objects: NameProtocol | DunderNameProtocol | str
+    ) -> str:
+        name: str | None = None
+        for obj in objects:
+            if not obj:
+                continue
+            if isinstance(obj, str):
+                name = obj
+            else:
+                name = getattr(obj, "name", getattr(obj, "__name__", None))
+
+            if name:
+                break
+        if not name or not isinstance(name, str):
+            raise ValueError("Could not generate a name for handler")
+
+        if not name.startswith(f"{self.name}."):
+            name = f"{self.name}.{name}"
+
+        return name
+
+    def generate_name(self, *objects) -> str:
+        return self._generate_name(*objects)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/commands.py b/.venv/lib/python3.12/site-packages/sanic/mixins/commands.py
new file mode 100644
index 0000000..02d12ed
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/commands.py
@@ -0,0 +1,31 @@
+from __future__ import annotations
+
+from functools import wraps
+from inspect import isawaitable
+from typing import Callable
+
+from sanic.base.meta import SanicMeta
+from sanic.models.futures import FutureCommand
+
+
+class CommandMixin(metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_commands: set[FutureCommand] = set()
+
+    def command(
+        self, maybe_func: Callable | None = None, *, name: str = ""
+    ) -> Callable | Callable[[Callable], Callable]:
+        def decorator(f):
+            @wraps(f)
+            async def decorated_function(*args, **kwargs):
+                response = f(*args, **kwargs)
+                if isawaitable(response):
+                    response = await response
+                return response
+
+            self._future_commands.add(
+                FutureCommand(name or f.__name__, decorated_function)
+            )
+            return decorated_function
+
+        return decorator(maybe_func) if maybe_func else decorator
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/exceptions.py b/.venv/lib/python3.12/site-packages/sanic/mixins/exceptions.py
new file mode 100644
index 0000000..c3ed52f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/exceptions.py
@@ -0,0 +1,110 @@
+from __future__ import annotations
+
+from typing import Any, Callable
+
+from sanic.base.meta import SanicMeta
+from sanic.models.futures import FutureException
+
+
+class ExceptionMixin(metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_exceptions: set[FutureException] = set()
+
+    def _apply_exception_handler(self, handler: FutureException):
+        raise NotImplementedError  # noqa
+
+    def exception(
+        self,
+        *exceptions: type[Exception] | list[type[Exception]],
+        apply: bool = True,
+    ) -> Callable:
+        """Decorator used to register an exception handler for the current application or blueprint instance.
+
+        This method allows you to define a handler for specific exceptions that
+        may be raised within the routes of this blueprint. You can specify one
+        or more exception types to catch, and the handler will be applied to
+        those exceptions.
+
+        When used on a Blueprint, the handler will only be applied to routes
+        registered under that blueprint. That means they only apply to
+        requests that have been matched, and the exception is raised within
+        the handler function (or middleware) for that route.
+
+        A general exception like `NotFound` should only be registered on the
+        application instance, not on a blueprint.
+
+        See [Exceptions](/en/guide/best-practices/exceptions.html) for more information.
+
+        Args:
+            exceptions (Union[Type[Exception], List[Type[Exception]]]): List of
+                Python exceptions to be caught by the handler.
+            apply (bool, optional): Whether the exception handler should be
+                applied. Defaults to True.
+
+        Returns:
+            Callable: A decorated method to handle global exceptions for any route
+                registered under this blueprint.
+
+        Example:
+            ```python
+            from sanic import Blueprint, text
+
+            bp = Blueprint('my_blueprint')
+
+            @bp.exception(Exception)
+            def handle_exception(request, exception):
+                return text("Oops, something went wrong!", status=500)
+            ```
+
+            ```python
+            from sanic import Sanic, NotFound, text
+
+            app = Sanic('MyApp')
+
+            @app.exception(NotFound)
+            def ignore_404s(request, exception):
+                return text(f"Yep, I totally found the page: {request.url}")
+        """  # noqa: E501
+
+        def decorator(handler):
+            nonlocal apply
+            nonlocal exceptions
+
+            if isinstance(exceptions[0], list):
+                exceptions = tuple(*exceptions)
+
+            future_exception = FutureException(handler, exceptions)
+            self._future_exceptions.add(future_exception)
+            if apply:
+                self._apply_exception_handler(future_exception)
+            return handler
+
+        return decorator
+
+    def all_exceptions(
+        self, handler: Callable[..., Any]
+    ) -> Callable[..., Any]:
+        """Enables the process of creating a global exception handler as a convenience.
+
+        This following two examples are equivalent:
+
+        ```python
+        @app.exception(Exception)
+        async def handler(request: Request, exception: Exception) -> HTTPResponse:
+            return text(f"Exception raised: {exception}")
+        ```
+
+        ```python
+        @app.all_exceptions
+        async def handler(request: Request, exception: Exception) -> HTTPResponse:
+            return text(f"Exception raised: {exception}")
+        ```
+
+        Args:
+            handler (Callable[..., Any]): A coroutine function to handle exceptions.
+
+        Returns:
+            Callable[..., Any]: A decorated method to handle global exceptions for
+                any route registered under this blueprint.
+        """  # noqa: E501
+        return self.exception(Exception)(handler)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/listeners.py b/.venv/lib/python3.12/site-packages/sanic/mixins/listeners.py
new file mode 100644
index 0000000..25e3d2f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/listeners.py
@@ -0,0 +1,433 @@
+from __future__ import annotations
+
+from enum import Enum, auto
+from functools import partial
+from typing import Callable, cast, overload
+
+from sanic.base.meta import SanicMeta
+from sanic.exceptions import BadRequest
+from sanic.models.futures import FutureListener
+from sanic.models.handler_types import ListenerType, Sanic
+
+
+class ListenerEvent(str, Enum):
+    def _generate_next_value_(name: str, *args) -> str:  # type: ignore
+        return name.lower()
+
+    BEFORE_SERVER_START = "server.init.before"
+    AFTER_SERVER_START = "server.init.after"
+    BEFORE_SERVER_STOP = "server.shutdown.before"
+    AFTER_SERVER_STOP = "server.shutdown.after"
+    MAIN_PROCESS_START = auto()
+    MAIN_PROCESS_READY = auto()
+    MAIN_PROCESS_STOP = auto()
+    RELOAD_PROCESS_START = auto()
+    RELOAD_PROCESS_STOP = auto()
+    BEFORE_RELOAD_TRIGGER = auto()
+    AFTER_RELOAD_TRIGGER = auto()
+
+
+class ListenerMixin(metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_listeners: list[FutureListener] = []
+
+    def _apply_listener(self, listener: FutureListener):
+        raise NotImplementedError  # noqa
+
+    @overload
+    def listener(
+        self,
+        listener_or_event: ListenerType[Sanic],
+        event_or_none: str,
+        apply: bool = ...,
+        *,
+        priority: int = 0,
+    ) -> ListenerType[Sanic]: ...
+
+    @overload
+    def listener(
+        self,
+        listener_or_event: str,
+        event_or_none: None = ...,
+        apply: bool = ...,
+        *,
+        priority: int = 0,
+    ) -> Callable[[ListenerType[Sanic]], ListenerType[Sanic]]: ...
+
+    def listener(
+        self,
+        listener_or_event: ListenerType[Sanic] | str,
+        event_or_none: str | None = None,
+        apply: bool = True,
+        *,
+        priority: int = 0,
+    ) -> (
+        ListenerType[Sanic]
+        | Callable[[ListenerType[Sanic]], ListenerType[Sanic]]
+    ):
+        """Create a listener for a specific event in the application's lifecycle.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        .. note::
+            Overloaded signatures allow for different ways of calling this method, depending on the types of the arguments.
+
+            Usually, it is prederred to use one of the convenience methods such as `before_server_start` or `after_server_stop` instead of calling this method directly.
+
+            ```python
+            @app.before_server_start
+            async def prefered_method(_):
+                ...
+
+            @app.listener("before_server_start")
+            async def not_prefered_method(_):
+                ...
+
+        Args:
+            listener_or_event (Union[ListenerType[Sanic], str]): A listener function or an event name.
+            event_or_none (Optional[str]): The event name to listen for if `listener_or_event` is a function. Defaults to `None`.
+            apply (bool): Whether to apply the listener immediately. Defaults to `True`.
+            priority (int): The priority of the listener. Defaults to `0`.
+
+        Returns:
+            Union[ListenerType[Sanic], Callable[[ListenerType[Sanic]], ListenerType[Sanic]]]: The listener or a callable that takes a listener.
+
+        Example:
+            The following code snippet shows how you can use this method as a decorator:
+
+            ```python
+            @bp.listener("before_server_start")
+            async def before_server_start(app, loop):
+                ...
+            ```
+        """  # noqa: E501
+
+        def register_listener(
+            listener: ListenerType[Sanic], event: str, priority: int = 0
+        ) -> ListenerType[Sanic]:
+            """A helper function to register a listener for an event.
+
+            Typically will not be called directly.
+
+            Args:
+                listener (ListenerType[Sanic]): The listener function to
+                    register.
+                event (str): The event name to listen for.
+
+            Returns:
+                ListenerType[Sanic]: The listener function that was registered.
+            """
+            nonlocal apply
+
+            future_listener = FutureListener(listener, event, priority)
+            self._future_listeners.append(future_listener)
+            if apply:
+                self._apply_listener(future_listener)
+            return listener
+
+        if callable(listener_or_event):
+            if event_or_none is None:
+                raise BadRequest(
+                    "Invalid event registration: Missing event name."
+                )
+            return register_listener(
+                listener_or_event, event_or_none, priority
+            )
+        else:
+            return partial(
+                register_listener, event=listener_or_event, priority=priority
+            )
+
+    def _setup_listener(
+        self,
+        listener: ListenerType[Sanic] | None,
+        event: str,
+        priority: int,
+    ) -> ListenerType[Sanic]:
+        if listener is not None:
+            return self.listener(listener, event, priority=priority)
+        return cast(
+            ListenerType[Sanic],
+            partial(self.listener, event_or_none=event, priority=priority),
+        )
+
+    def main_process_start(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the main_process_start event.
+
+        This event is fired only on the main process and **NOT** on any
+        worker processes. You should typically use this event to initialize
+        resources that are shared across workers, or to initialize resources
+        that are not safe to be initialized in a worker process.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.main_process_start
+            async def on_main_process_start(app: Sanic):
+                print("Main process started")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "main_process_start", priority)
+
+    def main_process_ready(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the main_process_ready event.
+
+        This event is fired only on the main process and **NOT** on any
+        worker processes. It is fired after the main process has started and
+        the Worker Manager has been initialized (ie, you will have access to
+        `app.manager` instance). The typical use case for this event is to
+        add a managed process to the Worker Manager.
+
+        See [Running custom processes](/en/guide/deployment/manager.html#running-custom-processes) and [Listeners](/en/guide/basics/listeners.html) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.main_process_ready
+            async def on_main_process_ready(app: Sanic):
+                print("Main process ready")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "main_process_ready", priority)
+
+    def main_process_stop(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the main_process_stop event.
+
+        This event is fired only on the main process and **NOT** on any
+        worker processes. You should typically use this event to clean up
+        resources that were initialized in the main_process_start event.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.main_process_stop
+            async def on_main_process_stop(app: Sanic):
+                print("Main process stopped")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "main_process_stop", priority)
+
+    def reload_process_start(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the reload_process_start event.
+
+        This event is fired only on the reload process and **NOT** on any
+        worker processes. This is similar to the main_process_start event,
+        except that it is fired only when the reload process is started.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.reload_process_start
+            async def on_reload_process_start(app: Sanic):
+                print("Reload process started")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "reload_process_start", priority)
+
+    def reload_process_stop(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the reload_process_stop event.
+
+        This event is fired only on the reload process and **NOT** on any
+        worker processes. This is similar to the main_process_stop event,
+        except that it is fired only when the reload process is stopped.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.reload_process_stop
+            async def on_reload_process_stop(app: Sanic):
+                print("Reload process stopped")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "reload_process_stop", priority)
+
+    def before_reload_trigger(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the before_reload_trigger event.
+
+        This event is fired only on the reload process and **NOT** on any
+        worker processes. This event is fired before the reload process
+        triggers the reload. A change event has been detected and the reload
+        process is about to be triggered.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.before_reload_trigger
+            async def on_before_reload_trigger(app: Sanic):
+                print("Before reload trigger")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(
+            listener, "before_reload_trigger", priority
+        )
+
+    def after_reload_trigger(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the after_reload_trigger event.
+
+        This event is fired only on the reload process and **NOT** on any
+        worker processes. This event is fired after the reload process
+        triggers the reload. A change event has been detected and the reload
+        process has been triggered.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.after_reload_trigger
+            async def on_after_reload_trigger(app: Sanic, changed: set[str]):
+                print("After reload trigger, changed files: ", changed)
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "after_reload_trigger", priority)
+
+    def before_server_start(
+        self,
+        listener: ListenerType[Sanic] | None = None,
+        *,
+        priority: int = 0,
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the before_server_start event.
+
+        This event is fired on all worker processes. You should typically
+        use this event to initialize resources that are global in nature, or
+        will be shared across requests and various parts of the application.
+
+        A common use case for this event is to initialize a database connection
+        pool, or to initialize a cache client.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.before_server_start
+            async def on_before_server_start(app: Sanic):
+                print("Before server start")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "before_server_start", priority)
+
+    def after_server_start(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the after_server_start event.
+
+        This event is fired on all worker processes. You should typically
+        use this event to run background tasks, or perform other actions that
+        are not directly related to handling requests. In theory, it is
+        possible that some requests may be handled before this event is fired,
+        so you should not use this event to initialize resources that are
+        required for handling requests.
+
+        A common use case for this event is to start a background task that
+        periodically performs some action, such as clearing a cache or
+        performing a health check.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.after_server_start
+            async def on_after_server_start(app: Sanic):
+                print("After server start")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "after_server_start", priority)
+
+    def before_server_stop(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the before_server_stop event.
+
+        This event is fired on all worker processes. This event is fired
+        before the server starts shutting down. You should not use this event
+        to perform any actions that are required for handling requests, as
+        some requests may continue to be handled after this event is fired.
+
+        A common use case for this event is to stop a background task that
+        was started in the after_server_start event.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.before_server_stop
+            async def on_before_server_stop(app: Sanic):
+                print("Before server stop")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "before_server_stop", priority)
+
+    def after_server_stop(
+        self, listener: ListenerType[Sanic] | None, *, priority: int = 0
+    ) -> ListenerType[Sanic]:
+        """Decorator for registering a listener for the after_server_stop event.
+
+        This event is fired on all worker processes. This event is fired
+        after the server has stopped shutting down, and all requests have
+        been handled. You should typically use this event to clean up
+        resources that were initialized in the before_server_start event.
+
+        A common use case for this event is to close a database connection
+        pool, or to close a cache client.
+
+        See [Listeners](/en/guide/basics/listeners) for more details.
+
+        Args:
+            listener (ListenerType[Sanic]): The listener handler to attach.
+
+        Examples:
+            ```python
+            @app.after_server_stop
+            async def on_after_server_stop(app: Sanic):
+                print("After server stop")
+            ```
+        """  # noqa: E501
+        return self._setup_listener(listener, "after_server_stop", priority)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/middleware.py b/.venv/lib/python3.12/site-packages/sanic/mixins/middleware.py
new file mode 100644
index 0000000..6e1b056
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/middleware.py
@@ -0,0 +1,232 @@
+from __future__ import annotations
+
+from collections import deque
+from functools import partial
+from operator import attrgetter
+from typing import Callable, overload
+
+from sanic.base.meta import SanicMeta
+from sanic.middleware import Middleware, MiddlewareLocation
+from sanic.models.futures import FutureMiddleware, MiddlewareType
+from sanic.router import Router
+
+
+class MiddlewareMixin(metaclass=SanicMeta):
+    router: Router
+
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_middleware: list[FutureMiddleware] = []
+
+    def _apply_middleware(self, middleware: FutureMiddleware):
+        raise NotImplementedError  # noqa
+
+    @overload
+    def middleware(
+        self,
+        middleware_or_request: MiddlewareType,
+        attach_to: str = "request",
+        apply: bool = True,
+        *,
+        priority: int = 0,
+    ) -> MiddlewareType: ...
+
+    @overload
+    def middleware(
+        self,
+        middleware_or_request: str,
+        attach_to: str = "request",
+        apply: bool = True,
+        *,
+        priority: int = 0,
+    ) -> Callable[[MiddlewareType], MiddlewareType]: ...
+
+    def middleware(
+        self,
+        middleware_or_request: MiddlewareType | str,
+        attach_to: str = "request",
+        apply: bool = True,
+        *,
+        priority: int = 0,
+    ) -> MiddlewareType | Callable[[MiddlewareType], MiddlewareType]:
+        """Decorator for registering middleware.
+
+        Decorate and register middleware to be called before a request is
+        handled or after a response is created. Can either be called as
+        *@app.middleware* or *@app.middleware('request')*. Although, it is
+        recommended to use *@app.on_request* or *@app.on_response* instead
+        for clarity and convenience.
+
+        See [Middleware](/guide/basics/middleware) for more information.
+
+        Args:
+            middleware_or_request (Union[Callable, str]): Middleware function
+                or the keyword 'request' or 'response'.
+            attach_to (str, optional): When to apply the middleware;
+                either 'request' (before the request is handled) or 'response'
+                (after the response is created). Defaults to `'request'`.
+            apply (bool, optional): Whether the middleware should be applied.
+                Defaults to `True`.
+            priority (int, optional): The priority level of the middleware.
+                Lower numbers are executed first. Defaults to `0`.
+
+        Returns:
+            Union[Callable, Callable[[Callable], Callable]]: The decorated
+                middleware function or a partial function depending on how
+                the method was called.
+
+        Example:
+            ```python
+            @app.middleware('request')
+            async def custom_middleware(request):
+                ...
+            ```
+        """
+
+        def register_middleware(middleware, attach_to="request"):
+            nonlocal apply
+
+            location = (
+                MiddlewareLocation.REQUEST
+                if attach_to == "request"
+                else MiddlewareLocation.RESPONSE
+            )
+            middleware = Middleware(middleware, location, priority=priority)
+            future_middleware = FutureMiddleware(middleware, attach_to)
+            self._future_middleware.append(future_middleware)
+            if apply:
+                self._apply_middleware(future_middleware)
+            return middleware
+
+        # Detect which way this was called, @middleware or @middleware('AT')
+        if callable(middleware_or_request):
+            return register_middleware(
+                middleware_or_request, attach_to=attach_to
+            )
+        else:
+            return partial(
+                register_middleware, attach_to=middleware_or_request
+            )
+
+    def on_request(self, middleware=None, *, priority=0) -> MiddlewareType:
+        """Register a middleware to be called before a request is handled.
+
+        This is the same as *@app.middleware('request')*.
+
+        Args:
+            middleware (Callable, optional): A callable that takes in a
+                request. Defaults to `None`.
+
+        Returns:
+            Callable: The decorated middleware function or a partial function
+                depending on how the method was called.
+
+        Examples:
+            ```python
+            @app.on_request
+            async def custom_middleware(request):
+                request.ctx.custom = 'value'
+            ```
+        """
+        if callable(middleware):
+            return self.middleware(middleware, "request", priority=priority)
+        else:
+            return partial(  # type: ignore
+                self.middleware, attach_to="request", priority=priority
+            )
+
+    def on_response(self, middleware=None, *, priority=0):
+        """Register a middleware to be called after a response is created.
+
+        This is the same as *@app.middleware('response')*.
+
+        Args:
+            middleware (Callable, optional): A callable that takes in a
+                request and response. Defaults to `None`.
+
+        Returns:
+            Callable: The decorated middleware function or a partial function
+                depending on how the method was called.
+
+        Examples:
+            ```python
+            @app.on_response
+            async def custom_middleware(request, response):
+                response.headers['X-Server'] = 'Sanic'
+            ```
+        """
+        if callable(middleware):
+            return self.middleware(middleware, "response", priority=priority)
+        else:
+            return partial(
+                self.middleware, attach_to="response", priority=priority
+            )
+
+    def finalize_middleware(self) -> None:
+        """Finalize the middleware configuration for the Sanic application.
+
+        This method completes the middleware setup for the application.
+        Middleware in Sanic is used to process requests globally before they
+        reach individual routes or after routes have been processed.
+
+        Finalization consists of identifying defined routes and optimizing
+        Sanic's performance to meet the application's specific needs. If
+        you are manually adding routes, after Sanic has started, you will
+        typically want to use the `amend` context manager rather than
+        calling this method directly.
+
+        .. note::
+            This method is usually called internally during the server setup
+            process and does not typically need to be invoked manually.
+
+        Example:
+            ```python
+            app.finalize_middleware()
+            ```
+        """
+        for route in self.router.routes:
+            request_middleware = Middleware.convert(
+                self.request_middleware,  # type: ignore
+                self.named_request_middleware.get(route.name, deque()),  # type: ignore  # noqa: E501
+                location=MiddlewareLocation.REQUEST,
+            )
+            response_middleware = Middleware.convert(
+                self.response_middleware,  # type: ignore
+                self.named_response_middleware.get(route.name, deque()),  # type: ignore  # noqa: E501
+                location=MiddlewareLocation.RESPONSE,
+            )
+            route.extra.request_middleware = deque(
+                sorted(
+                    request_middleware,
+                    key=attrgetter("order"),
+                    reverse=True,
+                )
+            )
+            route.extra.response_middleware = deque(
+                sorted(
+                    response_middleware,
+                    key=attrgetter("order"),
+                    reverse=True,
+                )[::-1]
+            )
+        request_middleware = Middleware.convert(
+            self.request_middleware,  # type: ignore
+            location=MiddlewareLocation.REQUEST,
+        )
+        response_middleware = Middleware.convert(
+            self.response_middleware,  # type: ignore
+            location=MiddlewareLocation.RESPONSE,
+        )
+        self.request_middleware = deque(
+            sorted(
+                request_middleware,
+                key=attrgetter("order"),
+                reverse=True,
+            )
+        )
+        self.response_middleware = deque(
+            sorted(
+                response_middleware,
+                key=attrgetter("order"),
+                reverse=True,
+            )[::-1]
+        )
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/routes.py b/.venv/lib/python3.12/site-packages/sanic/mixins/routes.py
new file mode 100644
index 0000000..9e97b89
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/routes.py
@@ -0,0 +1,817 @@
+from __future__ import annotations
+
+from ast import NodeVisitor, Return, parse
+from collections.abc import Iterable
+from contextlib import suppress
+from inspect import getsource, signature
+from textwrap import dedent
+from typing import (
+    Any,
+    Callable,
+    cast,
+)
+
+from sanic_routing.route import Route
+
+from sanic.base.meta import SanicMeta
+from sanic.constants import HTTP_METHODS
+from sanic.errorpages import RESPONSE_MAPPING
+from sanic.mixins.base import BaseMixin
+from sanic.models.futures import FutureRoute, FutureStatic
+from sanic.models.handler_types import RouteHandler
+from sanic.types import HashableDict
+
+
+RouteWrapper = Callable[
+    [RouteHandler], RouteHandler | tuple[Route, RouteHandler]
+]
+
+
+class RouteMixin(BaseMixin, metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_routes: set[FutureRoute] = set()
+        self._future_statics: set[FutureStatic] = set()
+
+    def _apply_route(self, route: FutureRoute) -> list[Route]:
+        raise NotImplementedError  # noqa
+
+    def route(
+        self,
+        uri: str,
+        methods: Iterable[str] | None = None,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        stream: bool = False,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        ignore_body: bool = False,
+        apply: bool = True,
+        subprotocols: list[str] | None = None,
+        websocket: bool = False,
+        unquote: bool = False,
+        static: bool = False,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteWrapper:
+        """Decorate a function to be registered as a route.
+
+        Args:
+            uri (str): Path of the URL.
+            methods (Optional[Iterable[str]]): List or tuple of
+                methods allowed.
+            host (Optional[Union[str, List[str]]]): The host, if required.
+            strict_slashes (Optional[bool]): Whether to apply strict slashes
+                to the route.
+            stream (bool): Whether to allow the request to stream its body.
+            version (Optional[Union[int, str, float]]): Route specific
+                versioning.
+            name (Optional[str]): User-defined route name for url_for.
+            ignore_body (bool): Whether the handler should ignore request
+                body (e.g. `GET` requests).
+            apply (bool): Apply middleware to the route.
+            subprotocols (Optional[List[str]]): List of subprotocols.
+            websocket (bool): Enable WebSocket support.
+            unquote (bool): Unquote special characters in the URL path.
+            static (bool): Enable static route.
+            version_prefix (str): URL path that should be before the version
+                 value; default: `"/v"`.
+            error_format (Optional[str]): Error format for the route.
+            ctx_kwargs (Any): Keyword arguments that begin with a `ctx_*`
+                prefix will be appended to the route context (`route.ctx`).
+
+        Returns:
+            RouteWrapper: Tuple of routes, decorated function.
+
+        Examples:
+            Using the method to define a GET endpoint:
+
+            ```python
+            @app.route("/hello")
+            async def hello(request: Request):
+                return text("Hello, World!")
+            ```
+
+            Adding context kwargs to the route:
+
+            ```python
+            @app.route("/greet", ctx_name="World")
+            async def greet(request: Request):
+                name = request.route.ctx.name
+                return text(f"Hello, {name}!")
+            ```
+        """
+
+        # Fix case where the user did not prefix the URL with a /
+        # and will probably get confused as to why it's not working
+        if not uri.startswith("/") and (uri or hasattr(self, "router")):
+            uri = "/" + uri
+
+        if strict_slashes is None:
+            strict_slashes = self.strict_slashes
+
+        if not methods and not websocket:
+            methods = frozenset({"GET"})
+
+        route_context = self._build_route_context(ctx_kwargs)
+
+        def decorator(handler):
+            nonlocal uri
+            nonlocal methods
+            nonlocal host
+            nonlocal strict_slashes
+            nonlocal stream
+            nonlocal version
+            nonlocal name
+            nonlocal ignore_body
+            nonlocal subprotocols
+            nonlocal websocket
+            nonlocal static
+            nonlocal version_prefix
+            nonlocal error_format
+
+            if isinstance(handler, tuple):
+                # if a handler fn is already wrapped in a route, the handler
+                # variable will be a tuple of (existing routes, handler fn)
+                _, handler = handler
+
+            name = self.generate_name(name, handler)
+
+            if isinstance(host, str):
+                host = frozenset([host])
+            elif host and not isinstance(host, frozenset):
+                try:
+                    host = frozenset(host)
+                except TypeError:
+                    raise ValueError(
+                        "Expected either string or Iterable of host strings, "
+                        "not %s" % host
+                    )
+            if isinstance(subprotocols, list):
+                # Ordered subprotocols, maintain order
+                subprotocols = tuple(subprotocols)
+            elif isinstance(subprotocols, set):
+                # subprotocol is unordered, keep it unordered
+                subprotocols = frozenset(subprotocols)
+
+            if not error_format or error_format == "auto":
+                error_format = self._determine_error_format(handler)
+
+            route = FutureRoute(
+                handler,
+                uri,
+                None if websocket else frozenset([x.upper() for x in methods]),
+                host,
+                strict_slashes,
+                stream,
+                version,
+                name,
+                ignore_body,
+                websocket,
+                subprotocols,
+                unquote,
+                static,
+                version_prefix,
+                error_format,
+                route_context,
+            )
+            overwrite = getattr(self, "_allow_route_overwrite", False)
+            if overwrite:
+                self._future_routes = set(
+                    filter(lambda x: x.uri != uri, self._future_routes)
+                )
+            self._future_routes.add(route)
+
+            args = list(signature(handler).parameters.keys())
+            if websocket and len(args) < 2:
+                handler_name = handler.__name__
+
+                raise ValueError(
+                    f"Required parameter `request` and/or `ws` missing "
+                    f"in the {handler_name}() route?"
+                )
+            elif not args:
+                handler_name = handler.__name__
+
+                raise ValueError(
+                    f"Required parameter `request` missing "
+                    f"in the {handler_name}() route?"
+                )
+
+            if not websocket and stream:
+                handler.is_stream = stream
+
+            if apply:
+                self._apply_route(route, overwrite=overwrite)
+
+            if static:
+                return route, handler
+            return handler
+
+        return decorator
+
+    def add_route(
+        self,
+        handler: RouteHandler,
+        uri: str,
+        methods: Iterable[str] = frozenset({"GET"}),
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        stream: bool = False,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        unquote: bool = False,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """A helper method to register class-based view or functions as a handler to the application url routes.
+
+        Args:
+            handler (RouteHandler): Function or class-based view used as a route handler.
+            uri (str): Path of the URL.
+            methods (Iterable[str]): List or tuple of methods allowed; these are overridden if using an HTTPMethodView.
+            host (Optional[Union[str, List[str]]]): Hostname or hostnames to match for this route.
+            strict_slashes (Optional[bool]): If set, a route's slashes will be strict. E.g. `/foo` will not match `/foo/`.
+            version (Optional[Union[int, str, float]]): Version of the API for this route.
+            name (Optional[str]): User-defined route name for `url_for`.
+            stream (bool): Boolean specifying if the handler is a stream handler.
+            version_prefix (str): URL path that should be before the version value; default: ``/v``.
+            error_format (Optional[str]): Custom error format string.
+            unquote (bool): Boolean specifying if the handler requires unquoting.
+            ctx_kwargs (Any): Keyword arguments that begin with a `ctx_*` prefix will be appended to the route context (``route.ctx``). See below for examples.
+
+        Returns:
+            RouteHandler: The route handler.
+
+        Examples:
+            ```python
+            from sanic import Sanic, text
+
+            app = Sanic("test")
+
+            async def handler(request):
+                return text("OK")
+
+            app.add_route(handler, "/test", methods=["GET", "POST"])
+            ```
+
+            You can use `ctx_kwargs` to add custom context to the route. This
+            can often be useful when wanting to add metadata to a route that
+            can be used by other parts of the application (like middleware).
+
+            ```python
+            from sanic import Sanic, text
+
+            app = Sanic("test")
+
+            async def handler(request):
+                return text("OK")
+
+            async def custom_middleware(request):
+                if request.route.ctx.monitor:
+                    do_some_monitoring()
+
+            app.add_route(handler, "/test", methods=["GET", "POST"], ctx_monitor=True)
+            app.register_middleware(custom_middleware)
+        """  # noqa: E501
+        # Handle HTTPMethodView differently
+        if hasattr(handler, "view_class"):
+            methods = set()
+
+            for method in HTTP_METHODS:
+                view_class = getattr(handler, "view_class")
+                _handler = getattr(view_class, method.lower(), None)
+                if _handler:
+                    methods.add(method)
+                    if hasattr(_handler, "is_stream"):
+                        stream = True
+
+        if strict_slashes is None:
+            strict_slashes = self.strict_slashes
+
+        self.route(
+            uri=uri,
+            methods=methods,
+            host=host,
+            strict_slashes=strict_slashes,
+            stream=stream,
+            version=version,
+            name=name,
+            version_prefix=version_prefix,
+            error_format=error_format,
+            unquote=unquote,
+            **ctx_kwargs,
+        )(handler)
+        return handler
+
+    # Shorthand method decorators
+    def get(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        ignore_body: bool = True,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **GET** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to GET method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            ignore_body (bool): Whether the handler should ignore request
+                body. This means the body of the request, if sent, will not
+                be consumed. In that instance, you will see a warning in
+                the logs. Defaults to `True`, meaning do not consume the body.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_* prefix` will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"GET"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                version=version,
+                name=name,
+                ignore_body=ignore_body,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def post(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        stream: bool = False,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **POST** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to POST method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            stream (bool): Whether or not to stream the request body.
+                Defaults to `False`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_*` prefix will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"POST"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                stream=stream,
+                version=version,
+                name=name,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def put(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        stream: bool = False,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **PUT** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to PUT method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            stream (bool): Whether or not to stream the request body.
+                Defaults to `False`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_*` prefix will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"PUT"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                stream=stream,
+                version=version,
+                name=name,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def head(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        ignore_body: bool = True,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **HEAD** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to HEAD method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            ignore_body (bool): Whether the handler should ignore request
+                body. This means the body of the request, if sent, will not
+                be consumed. In that instance, you will see a warning in
+                the logs. Defaults to `True`, meaning do not consume the body.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_*` prefix will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"HEAD"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                version=version,
+                name=name,
+                ignore_body=ignore_body,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def options(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        ignore_body: bool = True,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **OPTIONS** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to OPTIONS method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            ignore_body (bool): Whether the handler should ignore request
+                body. This means the body of the request, if sent, will not
+                be consumed. In that instance, you will see a warning in
+                the logs. Defaults to `True`, meaning do not consume the body.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_*` prefix will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"OPTIONS"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                version=version,
+                name=name,
+                ignore_body=ignore_body,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def patch(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        stream=False,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **PATCH** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to PATCH method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for
+                the service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a `/`.
+            stream (bool): Set to `True` if full request streaming is needed,
+                `False` otherwise. Defaults to `False`.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the route.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a
+                `ctx_*` prefix will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"PATCH"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                stream=stream,
+                version=version,
+                name=name,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def delete(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        ignore_body: bool = False,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ) -> RouteHandler:
+        """Decorate a function handler to create a route definition using the **DELETE** HTTP method.
+
+        Args:
+            uri (str): URL to be tagged to the DELETE method of HTTP.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN for the
+                service to use.
+            strict_slashes (Optional[bool]): Instruct Sanic to check if the
+                request URLs need to terminate with a */*.
+            version (Optional[Union[int, str, float]]): API Version.
+            name (Optional[str]): Unique name that can be used to identify
+                the Route.
+            ignore_body (bool): Whether or not to ignore the body in the
+                request. Defaults to `False`.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with a `ctx_*`
+                prefix will be appended to the route context (`route.ctx`).
+
+        Returns:
+            RouteHandler: Object decorated with route method.
+        """  # noqa: E501
+        return cast(
+            RouteHandler,
+            self.route(
+                uri,
+                methods=frozenset({"DELETE"}),
+                host=host,
+                strict_slashes=strict_slashes,
+                version=version,
+                name=name,
+                ignore_body=ignore_body,
+                version_prefix=version_prefix,
+                error_format=error_format,
+                **ctx_kwargs,
+            ),
+        )
+
+    def websocket(
+        self,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        subprotocols: list[str] | None = None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        apply: bool = True,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ):
+        """Decorate a function to be registered as a websocket route.
+
+        Args:
+            uri (str): Path of the URL.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN details.
+            strict_slashes (Optional[bool]): If the API endpoint needs to
+                terminate with a `"/"` or not.
+            subprotocols (Optional[List[str]]): Optional list of str with
+                supported subprotocols.
+            version (Optional[Union[int, str, float]]): WebSocket
+                protocol version.
+            name (Optional[str]): A unique name assigned to the URL so that
+                it can be used with url_for.
+            apply (bool): If set to False, it doesn't apply the route to the
+                app. Default is `True`.
+            version_prefix (str): URL path that should be before the version
+                value. Defaults to `"/v"`.
+            error_format (Optional[str]): Custom error format string.
+            **ctx_kwargs (Any): Keyword arguments that begin with
+                a `ctx_* prefix` will be appended to the route
+                context (`route.ctx`).
+
+        Returns:
+            tuple: Tuple of routes, decorated function.
+        """
+        return self.route(
+            uri=uri,
+            host=host,
+            methods=None,
+            strict_slashes=strict_slashes,
+            version=version,
+            name=name,
+            apply=apply,
+            subprotocols=subprotocols,
+            websocket=True,
+            version_prefix=version_prefix,
+            error_format=error_format,
+            **ctx_kwargs,
+        )
+
+    def add_websocket_route(
+        self,
+        handler,
+        uri: str,
+        host: str | list[str] | None = None,
+        strict_slashes: bool | None = None,
+        subprotocols=None,
+        version: int | str | float | None = None,
+        name: str | None = None,
+        version_prefix: str = "/v",
+        error_format: str | None = None,
+        **ctx_kwargs: Any,
+    ):
+        """A helper method to register a function as a websocket route.
+
+        Args:
+            handler (Callable): A callable function or instance of a class
+                that can handle the websocket request.
+            uri (str): URL path that will be mapped to the websocket handler.
+            host (Optional[Union[str, List[str]]]): Host IP or FQDN details.
+            strict_slashes (Optional[bool]): If the API endpoint needs to
+                terminate with a `"/"` or not.
+            subprotocols (Optional[List[str]]): Subprotocols to be used with
+                websocket handshake.
+            version (Optional[Union[int, str, float]]): Versioning information.
+            name (Optional[str]): A unique name assigned to the URL.
+            version_prefix (str): URL path before the version value.
+                Defaults to `"/v"`.
+            error_format (Optional[str]): Format for error handling.
+            **ctx_kwargs (Any): Keyword arguments beginning with `ctx_*`
+                prefix will be appended to the route context (`route.ctx`).
+
+        Returns:
+            Callable: Object passed as the handler.
+        """
+        return self.websocket(
+            uri=uri,
+            host=host,
+            strict_slashes=strict_slashes,
+            subprotocols=subprotocols,
+            version=version,
+            name=name,
+            version_prefix=version_prefix,
+            error_format=error_format,
+            **ctx_kwargs,
+        )(handler)
+
+    def _determine_error_format(self, handler) -> str:
+        with suppress(OSError, TypeError):
+            src = dedent(getsource(handler))
+            tree = parse(src)
+            http_response_types = self._get_response_types(tree)
+
+            if len(http_response_types) == 1:
+                return next(iter(http_response_types))
+
+        return ""
+
+    def _get_response_types(self, node):
+        types = set()
+
+        class HttpResponseVisitor(NodeVisitor):
+            def visit_Return(self, node: Return) -> Any:
+                nonlocal types
+
+                with suppress(AttributeError):
+                    checks = [node.value.func.id]  # type: ignore
+                    if node.value.keywords:  # type: ignore
+                        checks += [
+                            k.value
+                            for k in node.value.keywords  # type: ignore
+                            if k.arg == "content_type"
+                        ]
+
+                    for check in checks:
+                        if check in RESPONSE_MAPPING:
+                            types.add(RESPONSE_MAPPING[check])
+
+        HttpResponseVisitor().visit(node)
+
+        return types
+
+    def _build_route_context(self, raw: dict[str, Any]) -> HashableDict:
+        ctx_kwargs = {
+            key.replace("ctx_", ""): raw.pop(key)
+            for key in {**raw}.keys()
+            if key.startswith("ctx_")
+        }
+        if raw:
+            unexpected_arguments = ", ".join(raw.keys())
+            raise TypeError(
+                f"Unexpected keyword arguments: {unexpected_arguments}"
+            )
+        return HashableDict(ctx_kwargs)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/signals.py b/.venv/lib/python3.12/site-packages/sanic/mixins/signals.py
new file mode 100644
index 0000000..98d6821
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/signals.py
@@ -0,0 +1,144 @@
+from __future__ import annotations
+
+from collections.abc import Coroutine
+from enum import Enum
+from typing import Any, Callable
+
+from sanic.base.meta import SanicMeta
+from sanic.models.futures import FutureSignal
+from sanic.models.handler_types import SignalHandler
+from sanic.signals import Event, Signal
+from sanic.types import HashableDict
+
+
+class SignalMixin(metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_signals: set[FutureSignal] = set()
+
+    def _apply_signal(self, signal: FutureSignal) -> Signal:
+        raise NotImplementedError  # noqa
+
+    def signal(
+        self,
+        event: str | Enum,
+        *,
+        apply: bool = True,
+        condition: dict[str, Any] | None = None,
+        exclusive: bool = True,
+        priority: int = 0,
+    ) -> Callable[[SignalHandler], SignalHandler]:
+        """
+        For creating a signal handler, used similar to a route handler:
+
+        .. code-block:: python
+
+            @app.signal("foo.bar.")
+            async def signal_handler(thing, **kwargs):
+                print(f"[signal_handler] {thing=}", kwargs)
+
+        :param event: Representation of the event in ``one.two.three`` form
+        :type event: str
+        :param apply: For lazy evaluation, defaults to ``True``
+        :type apply: bool, optional
+        :param condition: For use with the ``condition`` argument in dispatch
+            filtering, defaults to ``None``
+        :param exclusive: When ``True``, the signal can only be dispatched
+            when the condition has been met. When ``False``, the signal can
+            be dispatched either with or without it. *THIS IS INAPPLICABLE TO
+            BLUEPRINT SIGNALS. THEY ARE ALWAYS NON-EXCLUSIVE*, defaults
+            to ``True``
+        :type condition: Dict[str, Any], optional
+        """
+        event_value = str(event.value) if isinstance(event, Enum) else event
+
+        def decorator(handler: SignalHandler):
+            future_signal = FutureSignal(
+                handler,
+                event_value,
+                HashableDict(condition or {}),
+                exclusive,
+                priority,
+            )
+            self._future_signals.add(future_signal)
+
+            if apply:
+                self._apply_signal(future_signal)
+
+            return handler
+
+        return decorator
+
+    def add_signal(
+        self,
+        handler: Callable[..., Any] | None,
+        event: str | Enum,
+        condition: dict[str, Any] | None = None,
+        exclusive: bool = True,
+    ) -> Callable[..., Any]:
+        """Registers a signal handler for a specific event.
+
+        Args:
+            handler (Optional[Callable[..., Any]]): The function to be called
+                when the event occurs. Defaults to a noop if not provided.
+            event (str): The name of the event to listen for.
+            condition (Optional[Dict[str, Any]]): Optional condition to filter
+                the event triggering. Defaults to `None`.
+            exclusive (bool): Whether or not the handler is exclusive. When
+                `True`, the signal can only be dispatched when the
+                `condition` has been met. *This is inapplicable to blueprint
+                signals, which are **ALWAYS** non-exclusive.* Defaults
+                to `True`.
+
+        Returns:
+            Callable[..., Any]: The handler that was registered.
+        """
+        if not handler:
+
+            async def noop(**context): ...
+
+            handler = noop
+        self.signal(event=event, condition=condition, exclusive=exclusive)(
+            handler
+        )
+        return handler
+
+    def event(self, event: str):
+        raise NotImplementedError
+
+    def catch_exception(
+        self,
+        handler: Callable[[SignalMixin, Exception], Coroutine[Any, Any, None]],
+    ) -> None:
+        """Register an exception handler for logging or processing.
+
+        This method allows the registration of a custom exception handler to
+        catch and process exceptions that occur in the application. Unlike a
+        typical exception handler that might modify the response to the client,
+        this is intended to capture exceptions for logging or other internal
+        processing, such as sending them to an error reporting utility.
+
+        Args:
+            handler (Callable): A coroutine function that takes the application
+                instance and the exception as arguments. It will be called when
+                an exception occurs within the application's lifecycle.
+
+        Example:
+            ```python
+            app = Sanic("TestApp")
+
+            @app.catch_exception
+            async def report_exception(app: Sanic, exception: Exception):
+                logging.error(f"An exception occurred: {exception}")
+
+                # Send to an error reporting service
+                await error_service.report(exception)
+
+            # Any unhandled exceptions within the application will now be
+            # logged and reported to the error service.
+            ```
+        """  # noqa: E501
+
+        async def signal_handler(exception: Exception):
+            await handler(self, exception)
+
+        self.signal(Event.SERVER_EXCEPTION_REPORT)(signal_handler)
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/startup.py b/.venv/lib/python3.12/site-packages/sanic/mixins/startup.py
new file mode 100644
index 0000000..a0eb198
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/startup.py
@@ -0,0 +1,1447 @@
+from __future__ import annotations
+
+import os
+import platform
+
+from asyncio import (
+    AbstractEventLoop,
+    CancelledError,
+    Protocol,
+    all_tasks,
+    get_event_loop,
+    get_running_loop,
+    new_event_loop,
+)
+from collections.abc import Mapping
+from contextlib import suppress
+from functools import partial
+from importlib import import_module
+from multiprocessing import (
+    Manager,
+    Pipe,
+    get_context,
+    get_start_method,
+    set_start_method,
+)
+from multiprocessing.context import BaseContext
+from pathlib import Path
+from socket import SHUT_RDWR, socket
+from ssl import SSLContext
+from time import sleep
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    ClassVar,
+    Literal,
+    cast,
+)
+
+from sanic.application.ext import setup_ext
+from sanic.application.logo import get_logo
+from sanic.application.motd import MOTD
+from sanic.application.state import ApplicationServerInfo, Mode, ServerStage
+from sanic.base.meta import SanicMeta
+from sanic.compat import OS_IS_WINDOWS, StartMethod
+from sanic.exceptions import ServerError, ServerKilled
+from sanic.helpers import Default, _default, is_atty
+from sanic.http.constants import HTTP
+from sanic.http.tls import get_ssl_context, process_to_context
+from sanic.http.tls.context import SanicSSLContext
+from sanic.log import Colors, deprecation, error_logger, logger
+from sanic.logging.setup import setup_logging
+from sanic.models.handler_types import ListenerType
+from sanic.server import Signal as ServerSignal
+from sanic.server import try_use_uvloop
+from sanic.server.async_server import AsyncioServer
+from sanic.server.events import trigger_events
+from sanic.server.goodbye import get_goodbye
+from sanic.server.loop import try_windows_loop
+from sanic.server.protocols.http_protocol import HttpProtocol
+from sanic.server.protocols.websocket_protocol import WebSocketProtocol
+from sanic.server.runners import serve
+from sanic.server.socket import configure_socket, remove_unix_socket
+from sanic.worker.loader import AppLoader
+from sanic.worker.manager import WorkerManager
+from sanic.worker.multiplexer import WorkerMultiplexer
+from sanic.worker.reloader import Reloader
+from sanic.worker.serve import worker_serve
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+    from sanic.application.state import ApplicationState
+    from sanic.config import Config
+
+SANIC_PACKAGES = ("sanic-routing", "sanic-testing", "sanic-ext")
+
+
+HTTPVersion = HTTP | Literal[1] | Literal[3]
+
+
+class StartupMixin(metaclass=SanicMeta):
+    _app_registry: ClassVar[dict[str, Sanic]]
+    name: str
+    asgi: bool
+    config: Config
+    listeners: dict[str, list[ListenerType[Any]]]
+    state: ApplicationState
+    websocket_enabled: bool
+    multiplexer: WorkerMultiplexer
+
+    test_mode: ClassVar[bool]
+    start_method: ClassVar[StartMethod] = _default
+    START_METHOD_SET: ClassVar[bool] = False
+
+    def setup_loop(self) -> None:
+        """Set up the event loop.
+
+        An internal method that sets up the event loop to uvloop if
+        possible, or a Windows selector loop if on Windows.
+
+        Returns:
+            None
+        """
+        if not self.asgi:
+            if self.config.USE_UVLOOP is True or (
+                isinstance(self.config.USE_UVLOOP, Default)
+                and not OS_IS_WINDOWS
+            ):
+                try_use_uvloop()
+            elif OS_IS_WINDOWS:
+                try_windows_loop()
+
+    @property
+    def m(self) -> WorkerMultiplexer:
+        """Interface for interacting with the worker processes
+
+        This is a shortcut for `app.multiplexer`. It is available only in a
+        worker process using the Sanic server. It allows you to interact with
+        the worker processes, such as sending messages and commands.
+
+        See [Access to the multiplexer](/en/guide/deployment/manager#access-to-the-multiplexer) for more information.
+
+        Returns:
+            WorkerMultiplexer: The worker multiplexer instance
+
+        Examples:
+            ```python
+            app.m.restart()    # restarts the worker
+            app.m.terminate()  # terminates the worker
+            app.m.scale(4)     # scales the number of workers to 4
+        ```
+        """  # noqa: E501
+        return self.multiplexer
+
+    def make_coffee(self, *args, **kwargs):
+        """
+        Try for yourself! `sanic server:app --coffee`
+
+         ```
+         ▄████████▄
+        ██       ██▀▀▄
+        ███████████  █
+        ███████████▄▄▀
+         ▀███████▀
+
+         ```
+        """
+        self.state.coffee = True
+        self.run(*args, **kwargs)
+
+    def run(
+        self,
+        host: str | None = None,
+        port: int | None = None,
+        *,
+        dev: bool = False,
+        debug: bool = False,
+        auto_reload: bool | None = None,
+        version: HTTPVersion = HTTP.VERSION_1,
+        ssl: None | SSLContext | dict | str | list | tuple = None,
+        sock: socket | None = None,
+        workers: int = 1,
+        protocol: type[Protocol] | None = None,
+        backlog: int = 100,
+        register_sys_signals: bool = True,
+        access_log: bool | None = None,
+        unix: str | None = None,
+        loop: AbstractEventLoop | None = None,
+        reload_dir: list[str] | str | None = None,
+        noisy_exceptions: bool | None = None,
+        motd: bool = True,
+        fast: bool = False,
+        verbosity: int = 0,
+        motd_display: dict[str, str] | None = None,
+        auto_tls: bool = False,
+        single_process: bool = False,
+    ) -> None:
+        """Run the HTTP Server and listen until keyboard interrupt or term signal. On termination, drain connections before closing.
+
+        .. note::
+            When you need control over running the Sanic instance, this is the method to use.
+            However, in most cases the preferred method is to use the CLI command:
+
+            ```sh
+            sanic server:app`
+            ```
+
+        If you are using this method to run Sanic, make sure you do the following:
+
+        1. Use `if __name__ == "__main__"` to guard the code.
+        2. Do **NOT** define the app instance inside the `if` block.
+
+        See [Dynamic Applications](/en/guide/deployment/app-loader) for more information about the second point.
+
+        Args:
+            host (Optional[str]): Address to host on.
+            port (Optional[int]): Port to host on.
+            dev (bool): Run the server in development mode.
+            debug (bool): Enables debug output (slows server).
+            auto_reload (Optional[bool]): Reload app whenever its source code is changed.
+                Enabled by default in debug mode.
+            version (HTTPVersion): HTTP Version.
+            ssl (Union[None, SSLContext, dict, str, list, tuple]): SSLContext, or location of certificate and key
+                for SSL encryption of worker(s).
+            sock (Optional[socket]): Socket for the server to accept connections from.
+            workers (int): Number of processes received before it is respected.
+            protocol (Optional[Type[Protocol]]): Subclass of asyncio Protocol class.
+            backlog (int): A number of unaccepted connections that the system will allow
+                before refusing new connections.
+            register_sys_signals (bool): Register SIG* events.
+            access_log (Optional[bool]): Enables writing access logs (slows server).
+            unix (Optional[str]): Unix socket to listen on instead of TCP port.
+            loop (Optional[AbstractEventLoop]): AsyncIO event loop.
+            reload_dir (Optional[Union[List[str], str]]): Directory to watch for code changes, if auto_reload is True.
+            noisy_exceptions (Optional[bool]): Log exceptions that are normally considered to be quiet/silent.
+            motd (bool): Display Message of the Day.
+            fast (bool): Enable fast mode.
+            verbosity (int): Verbosity level.
+            motd_display (Optional[Dict[str, str]]): Customize Message of the Day display.
+            auto_tls (bool): Enable automatic TLS certificate handling.
+            single_process (bool): Enable single process mode.
+
+        Returns:
+            None
+
+        Raises:
+            RuntimeError: Raised when attempting to serve HTTP/3 as a secondary server.
+            RuntimeError: Raised when attempting to use both `fast` and `workers`.
+            RuntimeError: Raised when attempting to use `single_process` with `fast`, `workers`, or `auto_reload`.
+            TypeError: Raised when attempting to use `loop` with `create_server`.
+            ValueError: Raised when `PROXIES_COUNT` is negative.
+
+        Examples:
+            ```python
+            from sanic import Sanic, Request, json
+
+            app = Sanic("TestApp")
+
+
+            @app.get("/")
+            async def handler(request: Request):
+                return json({"foo": "bar"})
+
+
+            if __name__ == "__main__":
+                app.run(port=9999, dev=True)
+            ```
+        """  # noqa: E501
+        self.prepare(
+            host=host,
+            port=port,
+            dev=dev,
+            debug=debug,
+            auto_reload=auto_reload,
+            version=version,
+            ssl=ssl,
+            sock=sock,
+            workers=workers,
+            protocol=protocol,
+            backlog=backlog,
+            register_sys_signals=register_sys_signals,
+            access_log=access_log,
+            unix=unix,
+            loop=loop,
+            reload_dir=reload_dir,
+            noisy_exceptions=noisy_exceptions,
+            motd=motd,
+            fast=fast,
+            verbosity=verbosity,
+            motd_display=motd_display,
+            auto_tls=auto_tls,
+            single_process=single_process,
+        )
+
+        if single_process:
+            serve = self.__class__.serve_single
+        else:
+            serve = self.__class__.serve
+        serve(primary=self)  # type: ignore
+
+    def prepare(
+        self,
+        host: str | None = None,
+        port: int | None = None,
+        *,
+        dev: bool = False,
+        debug: bool = False,
+        auto_reload: bool | None = None,
+        version: HTTPVersion = HTTP.VERSION_1,
+        ssl: None | SSLContext | dict | str | list | tuple = None,
+        sock: socket | None = None,
+        workers: int = 1,
+        protocol: type[Protocol] | None = None,
+        backlog: int = 100,
+        register_sys_signals: bool = True,
+        access_log: bool | None = None,
+        unix: str | None = None,
+        loop: AbstractEventLoop | None = None,
+        reload_dir: list[str] | str | None = None,
+        noisy_exceptions: bool | None = None,
+        motd: bool = True,
+        fast: bool = False,
+        verbosity: int = 0,
+        motd_display: dict[str, str] | None = None,
+        coffee: bool = False,
+        auto_tls: bool = False,
+        single_process: bool = False,
+    ) -> None:
+        """Prepares one or more Sanic applications to be served simultaneously.
+
+        This low-level API is typically used when you need to run multiple Sanic applications at the same time. Once prepared, `Sanic.serve()` should be called in the `if __name__ == "__main__"` block.
+
+        .. note::
+            "Preparing" and "serving" with this function is equivalent to using `app.run` for a single instance. This should only be used when running multiple applications at the same time.
+
+        Args:
+            host (Optional[str], optional): Hostname to listen on. Defaults to `None`.
+            port (Optional[int], optional): Port to listen on. Defaults to `None`.
+            dev (bool, optional): Development mode. Defaults to `False`.
+            debug (bool, optional): Debug mode. Defaults to `False`.
+            auto_reload (Optional[bool], optional): Auto reload feature. Defaults to `None`.
+            version (HTTPVersion, optional): HTTP version to use. Defaults to `HTTP.VERSION_1`.
+            ssl (Union[None, SSLContext, dict, str, list, tuple], optional): SSL configuration. Defaults to `None`.
+            sock (Optional[socket], optional): Socket to bind to. Defaults to `None`.
+            workers (int, optional): Number of worker processes. Defaults to `1`.
+            protocol (Optional[Type[Protocol]], optional): Custom protocol class. Defaults to `None`.
+            backlog (int, optional): Maximum number of pending connections. Defaults to `100`.
+            register_sys_signals (bool, optional): Register system signals. Defaults to `True`.
+            access_log (Optional[bool], optional): Access log. Defaults to `None`.
+            unix (Optional[str], optional): Unix socket. Defaults to `None`.
+            loop (Optional[AbstractEventLoop], optional): Event loop. Defaults to `None`.
+            reload_dir (Optional[Union[List[str], str]], optional): Reload directory. Defaults to `None`.
+            noisy_exceptions (Optional[bool], optional): Display exceptions. Defaults to `None`.
+            motd (bool, optional): Display message of the day. Defaults to `True`.
+            fast (bool, optional): Fast mode. Defaults to `False`.
+            verbosity (int, optional): Verbosity level. Defaults to `0`.
+            motd_display (Optional[Dict[str, str]], optional): Custom MOTD display. Defaults to `None`.
+            coffee (bool, optional): Coffee mode. Defaults to `False`.
+            auto_tls (bool, optional): Auto TLS. Defaults to `False`.
+            single_process (bool, optional): Single process mode. Defaults to `False`.
+
+        Raises:
+            RuntimeError: Raised when attempting to serve HTTP/3 as a secondary server.
+            RuntimeError: Raised when attempting to use both `fast` and `workers`.
+            RuntimeError: Raised when attempting to use `single_process` with `fast`, `workers`, or `auto_reload`.
+            TypeError: Raised when attempting to use `loop` with `create_server`.
+            ValueError: Raised when `PROXIES_COUNT` is negative.
+
+        Examples:
+            ```python
+            if __name__ == "__main__":
+                app.prepare()
+                app.serve()
+            ```
+        """  # noqa: E501
+        if version == 3 and self.state.server_info:
+            raise RuntimeError(
+                "Serving HTTP/3 instances as a secondary server is "
+                "not supported. There can only be a single HTTP/3 worker "
+                "and it must be the first instance prepared."
+            )
+
+        if dev:
+            debug = True
+            auto_reload = True
+
+        if debug and access_log is None:
+            access_log = True
+
+        self.state.verbosity = verbosity
+        if not self.state.auto_reload:
+            self.state.auto_reload = bool(auto_reload)
+
+        if fast and workers != 1:
+            raise RuntimeError("You cannot use both fast=True and workers=X")
+
+        if single_process and (fast or (workers > 1) or auto_reload):
+            raise RuntimeError(
+                "Single process cannot be run with multiple workers "
+                "or auto-reload"
+            )
+
+        if register_sys_signals is False and not single_process:
+            raise RuntimeError(
+                "Cannot run Sanic.serve with register_sys_signals=False. "
+                "Use Sanic.serve_single."
+            )
+
+        if motd_display:
+            self.config.MOTD_DISPLAY.update(motd_display)
+
+        if reload_dir:
+            if isinstance(reload_dir, str):
+                reload_dir = [reload_dir]
+
+            for directory in reload_dir:
+                direc = Path(directory)
+                if not direc.is_dir():
+                    logger.warning(
+                        f"Directory {directory} could not be located"
+                    )
+                self.state.reload_dirs.add(Path(directory))
+
+        if loop is not None:
+            raise TypeError(
+                "loop is not a valid argument. To use an existing loop, "
+                "change to create_server().\nSee more: "
+                "https://sanic.readthedocs.io/en/latest/sanic/deploying.html"
+                "#asynchronous-support"
+            )
+
+        if sock is None:
+            host, port = self.get_address(host, port, version, auto_tls)
+
+        if protocol is None:
+            protocol = (
+                WebSocketProtocol if self.websocket_enabled else HttpProtocol
+            )
+
+        # Set explicitly passed configuration values
+        for attribute, value in {
+            "ACCESS_LOG": access_log,
+            "AUTO_RELOAD": auto_reload,
+            "MOTD": motd,
+            "NOISY_EXCEPTIONS": noisy_exceptions,
+        }.items():
+            if value is not None:
+                setattr(self.config, attribute, value)
+
+        if fast:
+            self.state.fast = True
+            try:
+                workers = len(os.sched_getaffinity(0))
+            except AttributeError:  # no cov
+                workers = os.cpu_count() or 1
+
+        if coffee:
+            self.state.coffee = True
+
+        server_settings = self._helper(
+            host=host,
+            port=port,
+            debug=debug,
+            version=version,
+            ssl=ssl,
+            sock=sock,
+            unix=unix,
+            workers=workers,
+            protocol=protocol,
+            backlog=backlog,
+            register_sys_signals=register_sys_signals,
+            auto_tls=auto_tls,
+        )
+        self.state.server_info.append(
+            ApplicationServerInfo(settings=server_settings)
+        )
+
+        # if self.config.USE_UVLOOP is True or (
+        #     self.config.USE_UVLOOP is _default and not OS_IS_WINDOWS
+        # ):
+        #     try_use_uvloop()
+
+    async def create_server(
+        self,
+        host: str | None = None,
+        port: int | None = None,
+        *,
+        debug: bool = False,
+        ssl: None | SSLContext | dict | str | list | tuple = None,
+        sock: socket | None = None,
+        protocol: type[Protocol] | None = None,
+        backlog: int = 100,
+        access_log: bool | None = None,
+        unix: str | None = None,
+        return_asyncio_server: bool = True,
+        asyncio_server_kwargs: dict[str, Any] | None = None,
+        noisy_exceptions: bool | None = None,
+    ) -> AsyncioServer | None:
+        """
+        Low level API for creating a Sanic Server instance.
+
+        This method will create a Sanic Server instance, but will not start
+        it. This is useful for integrating Sanic into other systems. But, you
+        should take caution when using it as it is a low level API and does
+        not perform any of the lifecycle events.
+
+        .. note::
+            This does not support multiprocessing and is not the preferred
+            way to run a Sanic application. Proceed with caution.
+
+        You will need to start the server yourself as shown in the example
+        below. You are responsible for the lifecycle of the server, including
+        app startup using `await app.startup()`. No events will be triggered
+        for you, so you will need to trigger them yourself if wanted.
+
+        Args:
+            host (Optional[str]): Address to host on.
+            port (Optional[int]): Port to host on.
+            debug (bool): Enables debug output (slows server).
+            ssl (Union[None, SSLContext, dict, str, list, tuple]): SSLContext,
+                or location of certificate and key for SSL encryption
+                of worker(s).
+            sock (Optional[socket]): Socket for the server to accept
+                connections from.
+            protocol (Optional[Type[Protocol]]): Subclass of
+                `asyncio.Protocol` class.
+            backlog (int): Number of unaccepted connections that the system
+                will allow before refusing new connections.
+            access_log (Optional[bool]): Enables writing access logs
+                (slows server).
+            return_asyncio_server (bool): _DEPRECATED_
+            asyncio_server_kwargs (Optional[Dict[str, Any]]): Key-value
+                arguments for asyncio/uvloop `create_server` method.
+            noisy_exceptions (Optional[bool]): Log exceptions that are normally
+                considered to be quiet/silent.
+
+        Returns:
+            Optional[AsyncioServer]: AsyncioServer if `return_asyncio_server`
+                is `True` else `None`.
+
+        Examples:
+            ```python
+            import asyncio
+            import uvloop
+            from sanic import Sanic, response
+
+
+            app = Sanic("Example")
+
+
+            @app.route("/")
+            async def test(request):
+                return response.json({"answer": "42"})
+
+
+            async def main():
+                server = await app.create_server()
+                await server.startup()
+                await server.serve_forever()
+
+
+            if __name__ == "__main__":
+                asyncio.set_event_loop(uvloop.new_event_loop())
+                asyncio.run(main())
+            ```
+        """
+
+        if sock is None:
+            host, port = host, port = self.get_address(host, port)
+
+        if protocol is None:
+            protocol = (
+                WebSocketProtocol if self.websocket_enabled else HttpProtocol
+            )
+
+        # Set explicitly passed configuration values
+        for attribute, value in {
+            "ACCESS_LOG": access_log,
+            "NOISY_EXCEPTIONS": noisy_exceptions,
+        }.items():
+            if value is not None:
+                setattr(self.config, attribute, value)
+
+        if not return_asyncio_server:
+            return_asyncio_server = True
+            deprecation(
+                "The `return_asyncio_server` argument is deprecated and "
+                "ignored. It will be removed in v24.3.",
+                24.3,
+            )
+
+        server_settings = self._helper(
+            host=host,
+            port=port,
+            debug=debug,
+            ssl=ssl,
+            sock=sock,
+            unix=unix,
+            loop=get_event_loop(),
+            protocol=protocol,
+            backlog=backlog,
+            run_async=return_asyncio_server,
+        )
+
+        if not isinstance(self.config.USE_UVLOOP, Default):
+            error_logger.warning(
+                "You are trying to change the uvloop configuration, but "
+                "this is only effective when using the run(...) method. "
+                "When using the create_server(...) method Sanic will use "
+                "the already existing loop."
+            )
+
+        main_start = server_settings.pop("main_start", None)
+        main_stop = server_settings.pop("main_stop", None)
+        if main_start or main_stop:
+            logger.warning(
+                "Listener events for the main process are not available "
+                "with create_server()"
+            )
+
+        return await serve(
+            asyncio_server_kwargs=asyncio_server_kwargs, **server_settings
+        )
+
+    def stop(self, terminate: bool = True, unregister: bool = False) -> None:
+        """This kills the Sanic server, cleaning up after itself.
+
+        Args:
+            terminate (bool): Force kill all requests immediately without
+                allowing them to finish processing.
+            unregister (bool): Unregister the app from the global registry.
+
+        Returns:
+            None
+        """
+        if terminate and hasattr(self, "multiplexer"):
+            self.multiplexer.terminate()
+        if self.state.stage is not ServerStage.STOPPED:
+            self.shutdown_tasks(timeout=0)  # type: ignore
+            for task in all_tasks():
+                with suppress(AttributeError):
+                    if task.get_name() == "RunServer":
+                        task.cancel()
+            get_event_loop().stop()
+
+        if unregister:
+            self.__class__.unregister_app(self)  # type: ignore
+
+    def _helper(
+        self,
+        host: str | None = None,
+        port: int | None = None,
+        debug: bool = False,
+        version: HTTPVersion = HTTP.VERSION_1,
+        ssl: None | SSLContext | dict | str | list | tuple = None,
+        sock: socket | None = None,
+        unix: str | None = None,
+        workers: int = 1,
+        loop: AbstractEventLoop | None = None,
+        protocol: type[Protocol] = HttpProtocol,
+        backlog: int = 100,
+        register_sys_signals: bool = True,
+        run_async: bool = False,
+        auto_tls: bool = False,
+    ) -> dict[str, Any]:
+        """Helper function used by `run` and `create_server`."""
+        if self.config.PROXIES_COUNT and self.config.PROXIES_COUNT < 0:
+            raise ValueError(
+                "PROXIES_COUNT cannot be negative. "
+                "https://sanic.readthedocs.io/en/latest/sanic/config.html"
+                "#proxy-configuration"
+            )
+
+        if not self.state.is_debug:
+            self.state.mode = Mode.DEBUG if debug else Mode.PRODUCTION
+
+        setup_logging(
+            self.state.is_debug, self.config.NO_COLOR, self.config.LOG_EXTRA
+        )
+
+        if isinstance(version, int):
+            version = HTTP(version)
+
+        ssl = process_to_context(ssl)
+        if version is HTTP.VERSION_3 or auto_tls:
+            if TYPE_CHECKING:
+                self = cast(Sanic, self)
+            ssl = get_ssl_context(self, ssl)
+
+        self.state.host = host or ""
+        self.state.port = port or 0
+        self.state.workers = workers
+        self.state.ssl = ssl
+        self.state.unix = unix
+        self.state.sock = sock
+
+        server_settings = {
+            "protocol": protocol,
+            "host": host,
+            "port": port,
+            "version": version,
+            "sock": sock,
+            "unix": unix,
+            "ssl": ssl,
+            "app": self,
+            "signal": ServerSignal(),
+            "loop": loop,
+            "register_sys_signals": register_sys_signals,
+            "backlog": backlog,
+        }
+
+        self.motd(server_settings=server_settings)
+
+        if (
+            is_atty()
+            and not self.state.is_debug
+            and not os.environ.get("SANIC_IGNORE_PRODUCTION_WARNING")
+        ):
+            error_logger.warning(
+                f"{Colors.YELLOW}Sanic is running in PRODUCTION mode. "
+                "Consider using '--debug' or '--dev' while actively "
+                f"developing your application.{Colors.END}"
+            )
+
+        # Register start/stop events
+        for event_name, settings_name, reverse in (
+            ("main_process_start", "main_start", False),
+            ("main_process_stop", "main_stop", True),
+        ):
+            listeners = self.listeners[event_name].copy()
+            if reverse:
+                listeners.reverse()
+            # Prepend sanic to the arguments when listeners are triggered
+            listeners = [partial(listener, self) for listener in listeners]
+            server_settings[settings_name] = listeners  # type: ignore
+
+        if run_async:
+            server_settings["run_async"] = True
+
+        return server_settings
+
+    def motd(
+        self,
+        server_settings: dict[str, Any] | None = None,
+    ) -> None:
+        """Outputs the message of the day (MOTD).
+
+        It generally can only be called once per process, and is usually
+        called by the `run` method in the main process.
+
+        Args:
+            server_settings (Optional[Dict[str, Any]], optional): Settings for
+                the server. Defaults to `None`.
+
+        Returns:
+            None
+        """
+        if (
+            os.environ.get("SANIC_WORKER_NAME")
+            or os.environ.get("SANIC_MOTD_OUTPUT")
+            or os.environ.get("SANIC_WORKER_PROCESS")
+            or os.environ.get("SANIC_SERVER_RUNNING")
+        ):
+            return
+        serve_location = self.get_server_location(server_settings)
+        if self.config.MOTD:
+            logo = get_logo(coffee=self.state.coffee)
+            display, extra = self.get_motd_data(server_settings)
+
+            MOTD.output(logo, serve_location, display, extra)
+
+    def get_motd_data(
+        self, server_settings: dict[str, Any] | None = None
+    ) -> tuple[dict[str, Any], dict[str, Any]]:
+        """Retrieves the message of the day (MOTD) data.
+
+        Args:
+            server_settings (Optional[Dict[str, Any]], optional): Settings for
+                the server. Defaults to `None`.
+
+        Returns:
+            Tuple[Dict[str, Any], Dict[str, Any]]: A tuple containing two
+                dictionaries with the relevant MOTD data.
+        """
+
+        mode = [f"{self.state.mode},"]
+        if self.state.fast:
+            mode.append("goin' fast")
+        if self.state.asgi:
+            mode.append("ASGI")
+        else:
+            if self.state.workers == 1:
+                mode.append("single worker")
+            else:
+                mode.append(f"w/ {self.state.workers} workers")
+
+        if server_settings:
+            server = ", ".join(
+                (
+                    self.state.server,
+                    server_settings["version"].display(),  # type: ignore
+                )
+            )
+        else:
+            server = "ASGI" if self.asgi else "unknown"  # type: ignore
+
+        display = {
+            "app": self.name,
+            "mode": " ".join(mode),
+            "server": server,
+            "python": platform.python_version(),
+            "platform": platform.platform(),
+        }
+        extra = {}
+        if self.config.AUTO_RELOAD:
+            reload_display = "enabled"
+            if self.state.reload_dirs:
+                reload_display += ", ".join(
+                    [
+                        "",
+                        *(
+                            str(path.absolute())
+                            for path in self.state.reload_dirs
+                        ),
+                    ]
+                )
+            display["auto-reload"] = reload_display
+
+        packages = []
+        for package_name in SANIC_PACKAGES:
+            module_name = package_name.replace("-", "_")
+            try:
+                module = import_module(module_name)
+                packages.append(f"{package_name}=={module.__version__}")  # type: ignore
+            except ImportError:  # no cov
+                ...
+
+        if packages:
+            display["packages"] = ", ".join(packages)
+
+        if self.config.MOTD_DISPLAY:
+            extra.update(self.config.MOTD_DISPLAY)
+
+        return display, extra
+
+    @property
+    def serve_location(self) -> str:
+        """Retrieve the server location.
+
+        Returns:
+            str: The server location.
+        """
+        try:
+            server_settings = self.state.server_info[0].settings
+            return self.get_server_location(server_settings)
+        except IndexError:
+            location = "ASGI" if self.asgi else "unknown"  # type: ignore
+            return f"http://<{location}>"
+
+    @staticmethod
+    def get_server_location(
+        server_settings: dict[str, Any] | None = None,
+    ) -> str:
+        """Using the server settings, retrieve the server location.
+
+        Args:
+            server_settings (Optional[Dict[str, Any]], optional): Settings for
+                the server. Defaults to `None`.
+
+        Returns:
+            str: The server location.
+        """
+        serve_location = ""
+        proto = "http"
+        if not server_settings:
+            return serve_location
+
+        host = server_settings["host"]
+        port = server_settings["port"]
+
+        if server_settings.get("ssl") is not None:
+            proto = "https"
+        if server_settings.get("unix"):
+            serve_location = f"{server_settings['unix']} {proto}://..."
+        elif server_settings.get("sock"):
+            host, port, *_ = server_settings["sock"].getsockname()
+
+        if not serve_location and host and port:
+            # colon(:) is legal for a host only in an ipv6 address
+            display_host = f"[{host}]" if ":" in host else host
+            serve_location = f"{proto}://{display_host}:{port}"
+
+        return serve_location
+
+    @staticmethod
+    def get_address(
+        host: str | None,
+        port: int | None,
+        version: HTTPVersion = HTTP.VERSION_1,
+        auto_tls: bool = False,
+    ) -> tuple[str, int]:
+        """Retrieve the host address and port, with default values based on the given parameters.
+
+        Args:
+            host (Optional[str]): Host IP or FQDN for the service to use. Defaults to `"127.0.0.1"`.
+            port (Optional[int]): Port number. Defaults to `8443` if version is 3 or `auto_tls=True`, else `8000`
+            version (HTTPVersion, optional): HTTP Version. Defaults to `HTTP.VERSION_1` (HTTP/1.1).
+            auto_tls (bool, optional): Automatic TLS flag. Defaults to `False`.
+
+        Returns:
+            Tuple[str, int]: Tuple containing the host and port
+        """  # noqa: E501
+        host = host or "127.0.0.1"
+        port = port or (8443 if (version == 3 or auto_tls) else 8000)
+        return host, port
+
+    @classmethod
+    def should_auto_reload(cls) -> bool:
+        """Check if any applications have auto-reload enabled.
+
+        Returns:
+            bool: `True` if any applications have auto-reload enabled, else
+                `False`.
+        """
+        return any(app.state.auto_reload for app in cls._app_registry.values())
+
+    @classmethod
+    def _get_startup_method(cls) -> str:
+        return (
+            cls.start_method
+            if not isinstance(cls.start_method, Default)
+            else "spawn"
+        )
+
+    @classmethod
+    def _set_startup_method(cls) -> None:
+        if cls.START_METHOD_SET and not cls.test_mode:
+            return
+
+        method = cls._get_startup_method()
+        try:
+            set_start_method(method, force=cls.test_mode)
+        except RuntimeError:
+            ctx = get_context()
+            actual = ctx.get_start_method()
+            if actual != method:
+                raise RuntimeError(
+                    f"Start method '{method}' was requested, but '{actual}' "
+                    "was already set.\nFor more information, see: "
+                    "https://sanic.dev/en/guide/running/manager.html#overcoming-a-coderuntimeerrorcode"
+                ) from None
+            else:
+                raise
+        cls.START_METHOD_SET = True
+
+    @classmethod
+    def _get_context(cls) -> BaseContext:
+        method = cls._get_startup_method()
+        logger.debug("Creating multiprocessing context using '%s'", method)
+        actual = get_start_method()
+        if method != actual:
+            raise RuntimeError(
+                f"Start method '{method}' was requested, but '{actual}' "
+                "was already set.\nFor more information, see: "
+                "https://sanic.dev/en/guide/running/manager.html#overcoming-a-coderuntimeerrorcode"
+            ) from None
+        return get_context()
+
+    @classmethod
+    def serve(
+        cls,
+        primary: Sanic | None = None,
+        *,
+        app_loader: AppLoader | None = None,
+        factory: Callable[[], Sanic] | None = None,
+    ) -> None:
+        """Serve one or more Sanic applications.
+
+        This is the main entry point for running Sanic applications. It
+        should be called in the `if __name__ == "__main__"` block.
+
+        Args:
+            primary (Optional[Sanic], optional): The primary Sanic application
+                to serve. Defaults to `None`.
+            app_loader (Optional[AppLoader], optional): An AppLoader instance
+                to use for loading applications. Defaults to `None`.
+            factory (Optional[Callable[[], Sanic]], optional): A factory
+                function to use for loading applications. Defaults to `None`.
+
+        Raises:
+            RuntimeError: Raised when no applications are found.
+            RuntimeError: Raised when no server information is found for the
+                primary application.
+            RuntimeError: Raised when attempting to use `loop` with
+                `create_server`.
+            RuntimeError: Raised when attempting to use `single_process` with
+                `fast`, `workers`, or `auto_reload`.
+            RuntimeError: Raised when attempting to serve HTTP/3 as a
+                secondary server.
+            RuntimeError: Raised when attempting to use both `fast` and
+                `workers`.
+            TypeError: Raised when attempting to use `loop` with
+                `create_server`.
+            ValueError: Raised when `PROXIES_COUNT` is negative.
+
+        Examples:
+            ```python
+            if __name__ == "__main__":
+                app.prepare()
+                Sanic.serve()
+            ```
+        """
+        cls._set_startup_method()
+        os.environ["SANIC_MOTD_OUTPUT"] = "true"
+        apps = list(cls._app_registry.values())
+        if factory:
+            primary = factory()
+        else:
+            if not primary:
+                if app_loader:
+                    primary = app_loader.load()
+                if not primary:
+                    try:
+                        primary = apps[0]
+                    except IndexError:
+                        raise RuntimeError(
+                            "Did not find any applications."
+                        ) from None
+
+            # This exists primarily for unit testing
+            if not primary.state.server_info:  # no cov
+                for app in apps:
+                    app.state.server_info.clear()
+                return
+
+        try:
+            primary_server_info = primary.state.server_info[0]
+        except IndexError:
+            raise RuntimeError(
+                f"No server information found for {primary.name}. Perhaps you "
+                "need to run app.prepare(...)?"
+            ) from None
+
+        socks = []
+        try:
+            sync_manager = Manager()
+        except EOFError:
+            message = (
+                "Sanic server could not start: worker process failed.\n\n"
+                "This may have happened if you are running Sanic in the "
+                "global scope and not inside of a "
+                '`if __name__ == "__main__"` block.\n\nSee more information: '
+                "https://sanic.dev/en/guide/deployment/manager.html#"
+                "how-sanic-server-starts-processes\n"
+            )
+            if not OS_IS_WINDOWS:
+                message += (
+                    "\nAlternatively, you can set "
+                    '`Sanic.start_method = "fork"` at the start of your '
+                    "application to avoid this issue (Unix only).\n"
+                )
+            raise ServerError(message, quiet=True) from None
+        worker_state: Mapping[str, Any] = {"state": "NONE"}
+        setup_ext(primary)
+        exit_code = 0
+        workers_started = False
+        try:
+            primary_server_info.settings.pop("main_start", None)
+            primary_server_info.settings.pop("main_stop", None)
+            main_start = primary.listeners.get("main_process_start")
+            main_stop = primary.listeners.get("main_process_stop")
+            app = primary_server_info.settings.pop("app")
+            app.setup_loop()
+            loop = new_event_loop()
+            trigger_events(main_start, loop, primary)
+
+            socks = [
+                sock
+                for sock in [
+                    configure_socket(server_info.settings)
+                    for app in apps
+                    for server_info in app.state.server_info
+                ]
+                if sock
+            ]
+            primary_server_info.settings["run_multiple"] = True
+            monitor_sub, monitor_pub = Pipe(True)
+            worker_state = sync_manager.dict()
+            kwargs: dict[str, Any] = {
+                **primary_server_info.settings,
+                "monitor_publisher": monitor_pub,
+                "worker_state": worker_state,
+            }
+
+            if not app_loader:
+                if factory:
+                    app_loader = AppLoader(factory=factory)
+                else:
+                    app_loader = AppLoader(
+                        factory=partial(cls.get_app, app.name)  # type: ignore
+                    )
+            kwargs["app_name"] = app.name
+            kwargs["app_loader"] = app_loader
+            kwargs["server_info"] = {}
+            kwargs["passthru"] = {
+                "auto_reload": app.auto_reload,
+                "state": {
+                    "verbosity": app.state.verbosity,
+                    "mode": app.state.mode,
+                },
+                "config": {
+                    "ACCESS_LOG": app.config.ACCESS_LOG,
+                    "NOISY_EXCEPTIONS": app.config.NOISY_EXCEPTIONS,
+                },
+                "shared_ctx": app.shared_ctx.__dict__,
+            }
+            for app in apps:
+                kwargs["server_info"][app.name] = []
+                for server_info in app.state.server_info:
+                    server_info.settings = {
+                        k: v
+                        for k, v in server_info.settings.items()
+                        if k not in ("main_start", "main_stop", "app", "ssl")
+                    }
+                    kwargs["server_info"][app.name].append(server_info)
+
+            ssl = kwargs.get("ssl")
+
+            if isinstance(ssl, SanicSSLContext):
+                kwargs["ssl"] = ssl.sanic
+
+            manager = WorkerManager(
+                primary.state.workers,
+                worker_serve,
+                kwargs,
+                cls._get_context(),
+                (monitor_pub, monitor_sub),
+                worker_state,
+            )
+            if cls.should_auto_reload():
+                reload_dirs: set[Path] = primary.state.reload_dirs.union(
+                    *(app.state.reload_dirs for app in apps)
+                )
+                reloader = Reloader(monitor_pub, 0, reload_dirs, app_loader)
+                manager.manage("Reloader", reloader, {}, transient=False)
+
+            inspector = None
+            if primary.config.INSPECTOR:
+                display, extra = primary.get_motd_data()
+                packages = [
+                    pkg.strip() for pkg in display["packages"].split(",")
+                ]
+                module = import_module("sanic")
+                sanic_version = f"sanic=={module.__version__}"  # type: ignore
+                app_info = {
+                    **display,
+                    "packages": [sanic_version, *packages],
+                    "extra": extra,
+                }
+                inspector = primary.inspector_class(
+                    monitor_pub,
+                    app_info,
+                    worker_state,
+                    primary.config.INSPECTOR_HOST,
+                    primary.config.INSPECTOR_PORT,
+                    primary.config.INSPECTOR_API_KEY,
+                    primary.config.INSPECTOR_TLS_KEY,
+                    primary.config.INSPECTOR_TLS_CERT,
+                )
+                manager.manage("Inspector", inspector, {}, transient=False)
+
+            primary._inspector = inspector
+            primary._manager = manager
+
+            ready = primary.listeners["main_process_ready"]
+            trigger_events(ready, loop, primary)
+
+            workers_started = True
+            manager.run()
+        except ServerKilled:
+            exit_code = 1
+        except BaseException:
+            kwargs = primary_server_info.settings
+            error_logger.error("Experienced exception while trying to serve")
+            raise
+        finally:
+            logger.info("Server Stopped")
+            for app in apps:
+                app.state.server_info.clear()
+                app.router.reset()
+                app.signal_router.reset()
+
+            for sock in socks:
+                try:
+                    sock.shutdown(SHUT_RDWR)
+                except OSError:
+                    ...
+                sock.close()
+            socks = []
+
+            trigger_events(main_stop, loop, primary)
+
+            loop.close()
+            cls._cleanup_env_vars()
+            cls._cleanup_apps()
+
+            if workers_started:
+                limit = 100
+                while cls._get_process_states(worker_state):
+                    sleep(0.1)
+                    limit -= 1
+                    if limit <= 0:
+                        error_logger.warning(
+                            "Worker shutdown timed out. "
+                            "Some processes may still be running."
+                        )
+                        break
+            sync_manager.shutdown()
+            unix = kwargs.get("unix")
+            if unix:
+                remove_unix_socket(unix)
+            logger.debug(get_goodbye())
+        if exit_code:
+            os._exit(exit_code)
+
+    @staticmethod
+    def _get_process_states(worker_state) -> list[str]:
+        try:
+            return [
+                state
+                for s in worker_state.values()
+                if (
+                    (state := s.get("state"))
+                    and state
+                    not in ("TERMINATED", "FAILED", "COMPLETED", "NONE")
+                )
+            ]
+        except (BrokenPipeError, ConnectionResetError, EOFError):
+            return []
+
+    @classmethod
+    def serve_single(cls, primary: Sanic | None = None) -> None:
+        """Serve a single process of a Sanic application.
+
+        Similar to `serve`, but only serves a single process. When used,
+        certain features are disabled, such as `fast`, `workers`,
+        `multiplexer`, `auto_reload`, and the Inspector. It is almost
+        never needed to use this method directly. Instead, you should
+        use the CLI:
+
+        ```sh
+        sanic app.sanic:app --single-process
+        ```
+
+        Or, if you need to do it programmatically, you should use the
+        `single_process` argument of `run`:
+
+        ```python
+        app.run(single_process=True)
+        ```
+
+        Args:
+            primary (Optional[Sanic], optional): The primary Sanic application
+                to serve. Defaults to `None`.
+
+        Raises:
+            RuntimeError: Raised when no applications are found.
+            RuntimeError: Raised when no server information is found for the
+                primary application.
+            RuntimeError: Raised when attempting to serve HTTP/3 as a
+                secondary server.
+            RuntimeError: Raised when attempting to use both `fast` and
+                `workers`.
+            ValueError: Raised when `PROXIES_COUNT` is negative.
+        """
+        os.environ["SANIC_MOTD_OUTPUT"] = "true"
+        apps = list(cls._app_registry.values())
+
+        if not primary:
+            try:
+                primary = apps[0]
+            except IndexError:
+                raise RuntimeError("Did not find any applications.")
+
+        # This exists primarily for unit testing
+        if not primary.state.server_info:  # no cov
+            for app in apps:
+                app.state.server_info.clear()
+            return
+
+        primary_server_info = primary.state.server_info[0]
+        primary.before_server_start(partial(primary._start_servers, apps=apps))
+        kwargs = {
+            k: v
+            for k, v in primary_server_info.settings.items()
+            if k
+            not in (
+                "main_start",
+                "main_stop",
+                "app",
+            )
+        }
+        kwargs["app_name"] = primary.name
+        kwargs["app_loader"] = None
+        sock = configure_socket(kwargs)
+
+        kwargs["server_info"] = {}
+        kwargs["server_info"][primary.name] = []
+        for server_info in primary.state.server_info:
+            server_info.settings = {
+                k: v
+                for k, v in server_info.settings.items()
+                if k not in ("main_start", "main_stop", "app")
+            }
+            kwargs["server_info"][primary.name].append(server_info)
+
+        try:
+            worker_serve(monitor_publisher=None, **kwargs)
+        except BaseException:
+            error_logger.exception(
+                "Experienced exception while trying to serve"
+            )
+            raise
+        finally:
+            logger.info("Server Stopped")
+            for app in apps:
+                app.state.server_info.clear()
+                app.router.reset()
+                app.signal_router.reset()
+
+            if sock:
+                sock.close()
+
+            cls._cleanup_env_vars()
+            cls._cleanup_apps()
+
+    async def _start_servers(
+        self,
+        primary: Sanic,
+        apps: list[Sanic],
+    ) -> None:
+        for app in apps:
+            if (
+                app.name is not primary.name
+                and app.state.workers != primary.state.workers
+                and app.state.server_info
+            ):
+                message = (
+                    f"The primary application {repr(primary)} is running "
+                    f"with {primary.state.workers} worker(s). All "
+                    "application instances will run with the same number. "
+                    f"You requested {repr(app)} to run with "
+                    f"{app.state.workers} worker(s), which will be ignored "
+                    "in favor of the primary application."
+                )
+                if is_atty():
+                    message = "".join(
+                        [
+                            Colors.YELLOW,
+                            message,
+                            Colors.END,
+                        ]
+                    )
+                error_logger.warning(message, exc_info=True)
+            for server_info in app.state.server_info:
+                if server_info.stage is not ServerStage.SERVING:
+                    app.state.primary = False
+                    handlers = [
+                        *server_info.settings.pop("main_start", []),
+                        *server_info.settings.pop("main_stop", []),
+                    ]
+                    if handlers:  # no cov
+                        error_logger.warning(
+                            f"Sanic found {len(handlers)} listener(s) on "
+                            "secondary applications attached to the main "
+                            "process. These will be ignored since main "
+                            "process listeners can only be attached to your "
+                            "primary application: "
+                            f"{repr(primary)}"
+                        )
+
+                    if not server_info.settings["loop"]:
+                        server_info.settings["loop"] = get_running_loop()
+
+                    serve_args: dict[str, Any] = {
+                        **server_info.settings,
+                        "run_async": True,
+                        "reuse_port": bool(primary.state.workers - 1),
+                    }
+                    if "app" not in serve_args:
+                        serve_args["app"] = app
+                    try:
+                        server_info.server = await serve(**serve_args)
+                    except OSError as e:  # no cov
+                        first_message = (
+                            "An OSError was detected on startup. "
+                            "The encountered error was: "
+                        )
+                        second_message = str(e)
+                        if is_atty():
+                            message_parts = [
+                                Colors.YELLOW,
+                                first_message,
+                                Colors.RED,
+                                second_message,
+                                Colors.END,
+                            ]
+                        else:
+                            message_parts = [first_message, second_message]
+                        message = "".join(message_parts)
+                        error_logger.warning(message, exc_info=True)
+                        continue
+                    primary.add_task(
+                        self._run_server(app, server_info), name="RunServer"
+                    )
+
+    async def _run_server(
+        self,
+        app: StartupMixin,
+        server_info: ApplicationServerInfo,
+    ) -> None:  # no cov
+        try:
+            # We should never get to this point without a server
+            # This is primarily to keep mypy happy
+            if not server_info.server:  # no cov
+                raise RuntimeError("Could not locate AsyncioServer")
+            if app.state.stage is ServerStage.STOPPED:
+                server_info.stage = ServerStage.SERVING
+                await server_info.server.startup()
+                await server_info.server.before_start()
+                await server_info.server.after_start()
+            await server_info.server.serve_forever()
+        except CancelledError:
+            # We should never get to this point without a server
+            # This is primarily to keep mypy happy
+            if not server_info.server:  # no cov
+                raise RuntimeError("Could not locate AsyncioServer")
+            await server_info.server.before_stop()
+            await server_info.server.close()
+            await server_info.server.after_stop()
+        finally:
+            server_info.stage = ServerStage.STOPPED
+            server_info.server = None
+
+    @staticmethod
+    def _cleanup_env_vars():
+        variables = (
+            "SANIC_RELOADER_PROCESS",
+            "SANIC_IGNORE_PRODUCTION_WARNING",
+            "SANIC_WORKER_NAME",
+            "SANIC_MOTD_OUTPUT",
+            "SANIC_WORKER_PROCESS",
+            "SANIC_SERVER_RUNNING",
+        )
+        for var in variables:
+            try:
+                del os.environ[var]
+            except KeyError:
+                ...
+
+    @classmethod
+    def _cleanup_apps(cls):
+        for app in cls._app_registry.values():
+            app.state.server_info.clear()
+            app.router.reset()
+            app.signal_router.reset()
diff --git a/.venv/lib/python3.12/site-packages/sanic/mixins/static.py b/.venv/lib/python3.12/site-packages/sanic/mixins/static.py
new file mode 100644
index 0000000..be1e18b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/mixins/static.py
@@ -0,0 +1,425 @@
+from __future__ import annotations
+
+from collections.abc import Sequence
+from email.utils import formatdate
+from functools import partial, wraps
+from os import PathLike, path
+from pathlib import Path, PurePath
+from urllib.parse import unquote
+
+from sanic_routing.route import Route
+
+from sanic.base.meta import SanicMeta
+from sanic.compat import clear_function_annotate, stat_async
+from sanic.exceptions import FileNotFound, HeaderNotFound, RangeNotSatisfiable
+from sanic.handlers import ContentRangeHandler
+from sanic.handlers.directory import DirectoryHandler
+from sanic.log import error_logger
+from sanic.mixins.base import BaseMixin
+from sanic.models.futures import FutureStatic
+from sanic.request import Request
+from sanic.response import HTTPResponse, file, file_stream, validate_file
+from sanic.response.convenience import guess_content_type
+
+
+class StaticMixin(BaseMixin, metaclass=SanicMeta):
+    def __init__(self, *args, **kwargs) -> None:
+        self._future_statics: set[FutureStatic] = set()
+
+    def _apply_static(self, static: FutureStatic) -> Route:
+        raise NotImplementedError  # noqa
+
+    def static(
+        self,
+        uri: str,
+        file_or_directory: PathLike | str,
+        pattern: str = r"/?.+",
+        use_modified_since: bool = True,
+        use_content_range: bool = False,
+        stream_large_files: bool | int = False,
+        name: str = "static",
+        host: str | None = None,
+        strict_slashes: bool | None = None,
+        content_type: str | None = None,
+        apply: bool = True,
+        resource_type: str | None = None,
+        index: str | Sequence[str] | None = None,
+        directory_view: bool = False,
+        directory_handler: DirectoryHandler | None = None,
+        follow_external_symlink_files: bool = False,
+        follow_external_symlink_dirs: bool = False,
+    ):
+        """Register a root to serve files from. The input can either be a file or a directory.
+
+        This method provides an easy and simple way to set up the route necessary to serve static files.
+
+        Args:
+            uri (str): URL path to be used for serving static content.
+            file_or_directory (Union[PathLike, str]): Path to the static file
+                or directory with static files.
+            pattern (str, optional): Regex pattern identifying the valid
+                static files. Defaults to `r"/?.+"`.
+            use_modified_since (bool, optional): If true, send file modified
+                time, and return not modified if the browser's matches the
+                server's. Defaults to `True`.
+            use_content_range (bool, optional): If true, process header for
+                range requests and sends  the file part that is requested.
+                Defaults to `False`.
+            stream_large_files (Union[bool, int], optional): If `True`, use
+                the `StreamingHTTPResponse.file_stream` handler rather than
+                the `HTTPResponse.file handler` to send the file. If this
+                is an integer, it represents the threshold size to switch
+                to `StreamingHTTPResponse.file_stream`. Defaults to `False`,
+                which means that the response will not be streamed.
+            name (str, optional): User-defined name used for url_for.
+                Defaults to `"static"`.
+            host (Optional[str], optional): Host IP or FQDN for the
+                service to use.
+            strict_slashes (Optional[bool], optional): Instruct Sanic to
+                check if the request URLs need to terminate with a slash.
+            content_type (Optional[str], optional): User-defined content type
+                for header.
+            apply (bool, optional): If true, will register the route
+                immediately. Defaults to `True`.
+            resource_type (Optional[str], optional): Explicitly declare a
+                resource to be a `"file"` or a `"dir"`.
+            index (Optional[Union[str, Sequence[str]]], optional): When
+                exposing against a directory, index is  the name that will
+                be served as the default file. When multiple file names are
+                passed, then they will be tried in order.
+            directory_view (bool, optional): Whether to fallback to showing
+                the directory viewer when exposing a directory. Defaults
+                to `False`.
+            directory_handler (Optional[DirectoryHandler], optional): An
+                instance of DirectoryHandler that can be used for explicitly
+                controlling and subclassing the behavior of the default
+                directory handler.
+            follow_external_symlink_files (bool, optional): Whether to serve
+                files that are symlinks pointing outside the static root.
+                Defaults to `False` for security.
+            follow_external_symlink_dirs (bool, optional): Whether to serve
+                files from directories that are symlinks pointing outside
+                the static root. Defaults to `False` for security.
+
+        Returns:
+            List[sanic.router.Route]: Routes registered on the router.
+
+        Examples:
+            Serving a single file:
+            ```python
+            app.static('/foo', 'path/to/static/file.txt')
+            ```
+
+            Serving all files from a directory:
+            ```python
+            app.static('/static', 'path/to/static/directory')
+            ```
+
+            Serving large files with a specific threshold:
+            ```python
+            app.static('/static', 'path/to/large/files', stream_large_files=1000000)
+            ```
+        """  # noqa: E501
+
+        name = self.generate_name(name)
+
+        if strict_slashes is None and self.strict_slashes is not None:
+            strict_slashes = self.strict_slashes
+
+        if not isinstance(file_or_directory, (str, bytes, PurePath)):
+            raise ValueError(
+                f"Static route must be a valid path, not {file_or_directory}"
+            )
+
+        try:
+            file_or_directory = Path(file_or_directory).resolve()
+        except TypeError:
+            raise TypeError(
+                "Static file or directory must be a path-like object or string"
+            )
+
+        if directory_handler and (directory_view or index):
+            raise ValueError(
+                "When explicitly setting directory_handler, you cannot "
+                "set either directory_view or index. Instead, pass "
+                "these arguments to your DirectoryHandler instance."
+            )
+
+        if not directory_handler:
+            directory_handler = DirectoryHandler(
+                uri=uri,
+                directory=file_or_directory,
+                directory_view=directory_view,
+                index=index,
+                root_path=file_or_directory,
+                follow_external_symlink_files=follow_external_symlink_files,
+                follow_external_symlink_dirs=follow_external_symlink_dirs,
+            )
+
+        static = FutureStatic(
+            uri,
+            file_or_directory,
+            pattern,
+            use_modified_since,
+            use_content_range,
+            stream_large_files,
+            name,
+            host,
+            strict_slashes,
+            content_type,
+            resource_type,
+            directory_handler,
+            follow_external_symlink_files,
+            follow_external_symlink_dirs,
+        )
+        self._future_statics.add(static)
+
+        if apply:
+            self._apply_static(static)
+
+
+class StaticHandleMixin(metaclass=SanicMeta):
+    def _apply_static(self, static: FutureStatic) -> Route:
+        return self._register_static(static)
+
+    def _register_static(
+        self,
+        static: FutureStatic,
+    ):
+        # TODO: Though sanic is not a file server, I feel like we should
+        # at least make a good effort here.  Modified-since is nice, but
+        # we could also look into etags, expires, and caching
+        """
+        Register a static directory handler with Sanic by adding a route to the
+        router and registering a handler.
+        """
+        file_or_directory: PathLike
+
+        if isinstance(static.file_or_directory, bytes):
+            file_or_directory = Path(static.file_or_directory.decode("utf-8"))
+        elif isinstance(static.file_or_directory, PurePath):
+            file_or_directory = static.file_or_directory
+        elif isinstance(static.file_or_directory, str):
+            file_or_directory = Path(static.file_or_directory)
+        else:
+            raise ValueError("Invalid file path string.")
+
+        uri = static.uri
+        name = static.name
+        # If we're not trying to match a file directly,
+        # serve from the folder
+        if not static.resource_type:
+            if not path.isfile(file_or_directory):
+                uri = uri.rstrip("/")
+                uri += "/<__file_uri__:path>"
+        elif static.resource_type == "dir":
+            if path.isfile(file_or_directory):
+                raise TypeError(
+                    "Resource type improperly identified as directory. "
+                    f"'{file_or_directory}'"
+                )
+            uri = uri.rstrip("/")
+            uri += "/<__file_uri__:path>"
+        elif static.resource_type == "file" and not path.isfile(
+            file_or_directory
+        ):
+            raise TypeError(
+                "Resource type improperly identified as file. "
+                f"'{file_or_directory}'"
+            )
+        elif static.resource_type != "file":
+            raise ValueError(
+                "The resource_type should be set to 'file' or 'dir'"
+            )
+
+        # special prefix for static files
+        # if not static.name.startswith("_static_"):
+        #     name = f"_static_{static.name}"
+
+        _handler = wraps(self._static_request_handler)(
+            partial(
+                self._static_request_handler,
+                file_or_directory=str(file_or_directory),
+                use_modified_since=static.use_modified_since,
+                use_content_range=static.use_content_range,
+                stream_large_files=static.stream_large_files,
+                content_type=static.content_type,
+                directory_handler=static.directory_handler,
+                follow_external_symlink_files=static.follow_external_symlink_files,
+                follow_external_symlink_dirs=static.follow_external_symlink_dirs,
+            )
+        )
+
+        route, _ = self.route(  # type: ignore
+            uri=uri,
+            methods=["GET", "HEAD"],
+            name=name,
+            host=static.host,
+            strict_slashes=static.strict_slashes,
+            static=True,
+        )(_handler)
+
+        return route
+
+    async def _static_request_handler(
+        self,
+        request: Request,
+        *,
+        file_or_directory: str,
+        use_modified_since: bool,
+        use_content_range: bool,
+        stream_large_files: bool | int,
+        directory_handler: DirectoryHandler,
+        follow_external_symlink_files: bool,
+        follow_external_symlink_dirs: bool,
+        content_type: str | None = None,
+        __file_uri__: str | None = None,
+    ):
+        not_found = FileNotFound(
+            "File not found",
+            path=Path(file_or_directory),
+            relative_url=__file_uri__,
+        )
+
+        # Merge served directory and requested file if provided
+        file_path = await self._get_file_path(
+            file_or_directory,
+            __file_uri__,
+            not_found,
+            follow_external_symlink_files,
+            follow_external_symlink_dirs,
+        )
+
+        try:
+            headers = {}
+            # Check if the client has been sent this file before
+            # and it has not been modified since
+            stats = None
+            if use_modified_since:
+                stats = await stat_async(file_path)
+                modified_since = stats.st_mtime
+                response = await validate_file(request.headers, modified_since)
+                if response:
+                    return response
+                headers["Last-Modified"] = formatdate(
+                    modified_since, usegmt=True
+                )
+            _range = None
+            if use_content_range:
+                _range = None
+                if not stats:
+                    stats = await stat_async(file_path)
+                headers["Accept-Ranges"] = "bytes"
+                headers["Content-Length"] = str(stats.st_size)
+                if request.method != "HEAD":
+                    try:
+                        _range = ContentRangeHandler(request, stats)
+                    except HeaderNotFound:
+                        pass
+                    else:
+                        del headers["Content-Length"]
+                        headers.update(_range.headers)
+
+            if "content-type" not in headers:
+                content_type = content_type or guess_content_type(file_path)
+
+                if "charset=" not in content_type and (
+                    content_type.startswith("text/")
+                    or content_type == "application/javascript"
+                ):
+                    content_type += "; charset=utf-8"
+
+                headers["Content-Type"] = content_type
+
+            if request.method == "HEAD":
+                return HTTPResponse(headers=headers)
+            else:
+                if stream_large_files:
+                    if isinstance(stream_large_files, bool):
+                        threshold = 1024 * 1024
+                    else:
+                        threshold = stream_large_files
+
+                    if not stats:
+                        stats = await stat_async(file_path)
+                    if stats.st_size >= threshold:
+                        return await file_stream(
+                            file_path, headers=headers, _range=_range
+                        )
+                return await file(file_path, headers=headers, _range=_range)
+        except (IsADirectoryError, PermissionError):
+            return await directory_handler.handle(request, request.path)
+        except RangeNotSatisfiable:
+            raise
+        except FileNotFoundError:
+            raise not_found
+        except Exception:
+            error_logger.exception(
+                "Exception in static request handler: "
+                f"path={file_or_directory}, "
+                f"relative_url={__file_uri__}"
+            )
+            raise
+
+    async def _get_file_path(
+        self,
+        file_or_directory,
+        __file_uri__,
+        not_found,
+        follow_external_symlink_files: bool,
+        follow_external_symlink_dirs: bool,
+    ):
+        """
+        Resolve a filesystem path safely.
+
+        Security goals:
+        - Prevent path traversal via `..`
+        - Prevent escaping the root via symlinks unless explicitly allowed
+        - Treat file URIs as relative paths even if they look absolute
+        """
+
+        def reject():
+            error_logger.exception(
+                f"File not found: path={file_or_directory}, "
+                f"relative_url={__file_uri__}"
+            )
+            raise not_found
+
+        root_raw = Path(unquote(file_or_directory))
+        root_path = root_raw.resolve()
+        file_path_raw = root_raw
+
+        if __file_uri__:
+            # URLs may start with `/`, Path() interprets as absolute
+            rel_uri = unquote(__file_uri__).lstrip("/")
+            file_path_raw = Path(root_raw, rel_uri)
+
+            if ".." in file_path_raw.parts:
+                reject()
+
+        file_path = file_path_raw.resolve()
+
+        try:
+            file_path.relative_to(root_path)
+        except ValueError:
+            # Check if it's a symlink and determine its type
+            is_file_symlink = (
+                file_path_raw.is_symlink() and not file_path.is_dir()
+            )
+            if is_file_symlink:
+                allowed = follow_external_symlink_files
+            else:
+                allowed = follow_external_symlink_dirs
+            if not allowed:
+                reject()
+
+        return file_path
+
+
+# Clear __annotate__ on methods that may be pickled via functools.partial
+# to avoid PicklingError in Python 3.14+ (PEP 649)
+clear_function_annotate(
+    StaticHandleMixin._static_request_handler,
+    StaticHandleMixin._get_file_path,
+    StaticHandleMixin._register_static,
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/__init__.py b/.venv/lib/python3.12/site-packages/sanic/models/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/asgi.py b/.venv/lib/python3.12/site-packages/sanic/models/asgi.py
new file mode 100644
index 0000000..2a2f19e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/asgi.py
@@ -0,0 +1,97 @@
+import asyncio
+
+from collections.abc import Awaitable, MutableMapping
+from typing import Any, Callable
+
+from sanic.exceptions import BadRequest
+from sanic.models.protocol_types import TransportProtocol
+from sanic.server.websockets.connection import WebSocketConnection
+
+
+ASGIScope = MutableMapping[str, Any]
+ASGIMessage = MutableMapping[str, Any]
+ASGISend = Callable[[ASGIMessage], Awaitable[None]]
+ASGIReceive = Callable[[], Awaitable[ASGIMessage]]
+
+
+class MockProtocol:  # no cov
+    def __init__(self, transport: "MockTransport", loop):
+        self.transport = transport
+        self._not_paused = asyncio.Event()
+        self._not_paused.set()
+        self._complete = asyncio.Event()
+
+    def pause_writing(self) -> None:
+        self._not_paused.clear()
+
+    def resume_writing(self) -> None:
+        self._not_paused.set()
+
+    async def complete(self) -> None:
+        self._not_paused.set()
+        await self.transport.send(
+            {"type": "http.response.body", "body": b"", "more_body": False}
+        )
+
+    @property
+    def is_complete(self) -> bool:
+        return self._complete.is_set()
+
+    async def push_data(self, data: bytes) -> None:
+        if not self.is_complete:
+            await self.transport.send(
+                {"type": "http.response.body", "body": data, "more_body": True}
+            )
+
+    async def drain(self) -> None:
+        await self._not_paused.wait()
+
+
+class MockTransport(TransportProtocol):  # no cov
+    _protocol: MockProtocol | None
+
+    def __init__(
+        self, scope: ASGIScope, receive: ASGIReceive, send: ASGISend
+    ) -> None:
+        self.scope = scope
+        self._receive = receive
+        self._send = send
+        self._protocol = None
+        self.loop: asyncio.AbstractEventLoop | None = None
+
+    def get_protocol(self) -> MockProtocol:  # type: ignore
+        if not self._protocol:
+            self._protocol = MockProtocol(self, self.loop)
+        return self._protocol
+
+    def get_extra_info(self, info: str, default=None) -> str | bool | None:
+        if info == "peername":
+            return self.scope.get("client")
+        elif info == "sslcontext":
+            return self.scope.get("scheme") in ["https", "wss"]
+        return default
+
+    def get_websocket_connection(self) -> WebSocketConnection:
+        try:
+            return self._websocket_connection
+        except AttributeError:
+            raise BadRequest("Improper websocket connection.")
+
+    def create_websocket_connection(
+        self, send: ASGISend, receive: ASGIReceive
+    ) -> WebSocketConnection:
+        self._websocket_connection = WebSocketConnection(
+            send, receive, self.scope.get("subprotocols", [])
+        )
+        return self._websocket_connection
+
+    def add_task(self) -> None:
+        raise NotImplementedError
+
+    async def send(self, data) -> None:
+        # TODO:
+        # - Validation on data and that it is formatted properly and is valid
+        await self._send(data)
+
+    async def receive(self) -> ASGIMessage:
+        return await self._receive()
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/ctx_types.py b/.venv/lib/python3.12/site-packages/sanic/models/ctx_types.py
new file mode 100644
index 0000000..7f5d2e4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/ctx_types.py
@@ -0,0 +1,69 @@
+from typing import Any, NamedTuple
+
+
+class REPLLocal(NamedTuple):
+    var: Any
+    name: str
+    desc: str
+
+
+class REPLContext:
+    BUILTINS = {
+        "app": "The Sanic application instance",
+        "sanic": "The Sanic module",
+        "do": "An async function to fake a request to the application",
+        "client": "A client to access the Sanic app instance using httpx",
+    }
+
+    def __init__(self):
+        self._locals: set[REPLLocal] = set()
+
+    def add(
+        self,
+        var: Any,
+        name: str | None = None,
+        desc: str | None = None,
+    ):
+        """Add a local variable to be available in REPL context.
+
+        Args:
+            var (Any): A module, class, object or a class.
+            name (Optional[str], optional): An alias for the local. Defaults to None.
+            desc (Optional[str], optional): A brief description for the local. Defaults to None.
+        """  # noqa: E501
+        if name is None:
+            try:
+                name = var.__name__
+            except AttributeError:
+                name = var.__class__.__name__
+
+        if desc is None:
+            try:
+                desc = var.__doc__ or ""
+            except AttributeError:
+                desc = str(type(var))
+
+        assert isinstance(desc, str) and isinstance(
+            name, str
+        )  # Just to make mypy happy
+
+        if name in self.BUILTINS:
+            raise ValueError(f"Cannot override built-in variable: {name}")
+
+        desc = self._truncate(desc)
+
+        self._locals.add(REPLLocal(var, name, desc))
+
+    def __setattr__(self, name: str, value: Any):
+        if name.startswith("_"):
+            super().__setattr__(name, value)
+        else:
+            self.add(value, name)
+
+    def __iter__(self):
+        return iter(self._locals)
+
+    @staticmethod
+    def _truncate(s: str, limit: int = 40) -> str:
+        s = s.replace("\n", " ")
+        return s[:limit] + "..." if len(s) > limit else s
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/futures.py b/.venv/lib/python3.12/site-packages/sanic/models/futures.py
new file mode 100644
index 0000000..6fd7a6f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/futures.py
@@ -0,0 +1,80 @@
+from collections.abc import Iterable
+from pathlib import Path
+from typing import Callable, NamedTuple
+
+from sanic.handlers.directory import DirectoryHandler
+from sanic.models.handler_types import (
+    ErrorMiddlewareType,
+    ListenerType,
+    MiddlewareType,
+    SignalHandler,
+)
+from sanic.types import HashableDict
+
+
+class FutureRoute(NamedTuple):
+    handler: str
+    uri: str
+    methods: Iterable[str] | None
+    host: str | list[str]
+    strict_slashes: bool
+    stream: bool
+    version: int | None
+    name: str
+    ignore_body: bool
+    websocket: bool
+    subprotocols: list[str] | None
+    unquote: bool
+    static: bool
+    version_prefix: str
+    error_format: str | None
+    route_context: HashableDict
+
+
+class FutureListener(NamedTuple):
+    listener: ListenerType
+    event: str
+    priority: int
+
+
+class FutureMiddleware(NamedTuple):
+    middleware: MiddlewareType
+    attach_to: str
+
+
+class FutureException(NamedTuple):
+    handler: ErrorMiddlewareType
+    exceptions: list[BaseException]
+
+
+class FutureStatic(NamedTuple):
+    uri: str
+    file_or_directory: Path
+    pattern: str
+    use_modified_since: bool
+    use_content_range: bool
+    stream_large_files: bool | int
+    name: str
+    host: str | None
+    strict_slashes: bool | None
+    content_type: str | None
+    resource_type: str | None
+    directory_handler: DirectoryHandler
+    follow_external_symlink_files: bool
+    follow_external_symlink_dirs: bool
+
+
+class FutureSignal(NamedTuple):
+    handler: SignalHandler
+    event: str
+    condition: dict[str, str] | None
+    exclusive: bool
+    priority: int
+
+
+class FutureRegistry(set): ...
+
+
+class FutureCommand(NamedTuple):
+    name: str
+    func: Callable
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/handler_types.py b/.venv/lib/python3.12/site-packages/sanic/models/handler_types.py
new file mode 100644
index 0000000..2b7eade
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/handler_types.py
@@ -0,0 +1,30 @@
+from asyncio.events import AbstractEventLoop
+from collections.abc import Coroutine
+from typing import Any, Callable, TypeVar
+
+import sanic
+
+from sanic import request
+from sanic.response import BaseHTTPResponse, HTTPResponse
+
+
+Sanic = TypeVar("Sanic", bound="sanic.Sanic")
+Request = TypeVar("Request", bound="request.Request")
+
+MiddlewareResponse = (
+    HTTPResponse | None | Coroutine[Any, Any, HTTPResponse | None]
+)
+RequestMiddlewareType = Callable[[Request], MiddlewareResponse]
+ResponseMiddlewareType = Callable[
+    [Request, BaseHTTPResponse], MiddlewareResponse
+]
+ErrorMiddlewareType = Callable[
+    [Request, BaseException], Coroutine[Any, Any, None] | None
+]
+MiddlewareType = RequestMiddlewareType | ResponseMiddlewareType
+ListenerType = (
+    Callable[[Sanic], Coroutine[Any, Any, None] | None]
+    | Callable[[Sanic, AbstractEventLoop], Coroutine[Any, Any, None] | None]
+)
+RouteHandler = Callable[..., Coroutine[Any, Any, HTTPResponse | None]]
+SignalHandler = Callable[..., Coroutine[Any, Any, None]]
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/http_types.py b/.venv/lib/python3.12/site-packages/sanic/models/http_types.py
new file mode 100644
index 0000000..fc86b6c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/http_types.py
@@ -0,0 +1,34 @@
+from __future__ import annotations
+
+from base64 import b64decode
+from dataclasses import dataclass, field
+
+
+@dataclass()
+class Credentials:
+    auth_type: str | None
+    token: str | None
+    _username: str | None = field(default=None)
+    _password: str | None = field(default=None)
+
+    def __post_init__(self):
+        if self._auth_is_basic:
+            self._username, self._password = (
+                b64decode(self.token.encode("utf-8")).decode().split(":")
+            )
+
+    @property
+    def username(self):
+        if not self._auth_is_basic:
+            raise AttributeError("Username is available for Basic Auth only")
+        return self._username
+
+    @property
+    def password(self):
+        if not self._auth_is_basic:
+            raise AttributeError("Password is available for Basic Auth only")
+        return self._password
+
+    @property
+    def _auth_is_basic(self) -> bool:
+        return self.auth_type == "Basic"
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/protocol_types.py b/.venv/lib/python3.12/site-packages/sanic/models/protocol_types.py
new file mode 100644
index 0000000..7ff220e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/protocol_types.py
@@ -0,0 +1,29 @@
+from __future__ import annotations
+
+from asyncio import BaseTransport
+from typing import TYPE_CHECKING, Protocol
+
+
+if TYPE_CHECKING:
+    from sanic.http.constants import HTTP
+    from sanic.models.asgi import ASGIScope
+
+
+class HTMLProtocol(Protocol):
+    def __html__(self) -> str | bytes: ...
+
+    def _repr_html_(self) -> str | bytes: ...
+
+
+class Range(Protocol):
+    start: int | None
+    end: int | None
+    size: int | None
+    total: int | None
+    __slots__ = ()
+
+
+class TransportProtocol(BaseTransport):
+    scope: ASGIScope
+    version: HTTP
+    __slots__ = ()
diff --git a/.venv/lib/python3.12/site-packages/sanic/models/server_types.py b/.venv/lib/python3.12/site-packages/sanic/models/server_types.py
new file mode 100644
index 0000000..88b9d5b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/models/server_types.py
@@ -0,0 +1,71 @@
+from __future__ import annotations
+
+from ssl import SSLContext, SSLObject
+from types import SimpleNamespace
+from typing import Any
+
+from sanic.models.protocol_types import TransportProtocol
+
+
+class Signal:
+    stopped = False
+
+
+class ConnInfo:
+    """
+    Local and remote addresses and SSL status info.
+    """
+
+    __slots__ = (
+        "client_port",
+        "client",
+        "client_ip",
+        "ctx",
+        "lost",
+        "peername",
+        "server_port",
+        "server",
+        "server_name",
+        "sockname",
+        "ssl",
+        "cert",
+        "network_paths",
+    )
+
+    def __init__(self, transport: TransportProtocol, unix=None):
+        self.ctx = SimpleNamespace()
+        self.lost = False
+        self.peername: tuple[str, int] | None = None
+        self.server = self.client = ""
+        self.server_port = self.client_port = 0
+        self.client_ip = ""
+        self.sockname = addr = transport.get_extra_info("sockname")
+        self.ssl = False
+        self.server_name = ""
+        self.cert: dict[str, Any] = {}
+        self.network_paths: list[Any] = []
+        sslobj: SSLObject | None = transport.get_extra_info("ssl_object")  # type: ignore
+        sslctx: SSLContext | None = transport.get_extra_info("ssl_context")  # type: ignore
+        if sslobj:
+            self.ssl = True
+            self.server_name = getattr(sslobj, "sanic_server_name", None) or ""
+            self.cert = dict(getattr(sslobj.context, "sanic", {}))
+        if sslctx and not self.cert:
+            self.cert = dict(getattr(sslctx, "sanic", {}))
+        if isinstance(addr, str):  # UNIX socket
+            self.server = unix or addr
+            return
+        # IPv4 (ip, port) or IPv6 (ip, port, flowinfo, scopeid)
+        if isinstance(addr, tuple):
+            self.server = addr[0] if len(addr) == 2 else f"[{addr[0]}]"
+            self.server_port = addr[1]
+            # self.server gets non-standard port appended
+            if addr[1] != (443 if self.ssl else 80):
+                self.server = f"{self.server}:{addr[1]}"
+        self.peername = addr = transport.get_extra_info("peername")
+        self.network_paths = transport.get_extra_info("network_paths")  # type: ignore
+
+        if isinstance(addr, tuple):
+            self.client = addr[0] if len(addr) == 2 else f"[{addr[0]}]"
+            self.client_ip = addr[0]
+            self.client_port = addr[1]
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/__init__.py b/.venv/lib/python3.12/site-packages/sanic/pages/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/base.py b/.venv/lib/python3.12/site-packages/sanic/pages/base.py
new file mode 100644
index 0000000..cd89e7a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/base.py
@@ -0,0 +1,81 @@
+from abc import ABC, abstractmethod
+
+from html5tagger import HTML, Builder, Document
+
+from sanic import __version__ as VERSION
+from sanic.application.logo import SVG_LOGO_SIMPLE
+from sanic.pages.css import CSS
+
+
+class BasePage(ABC, metaclass=CSS):  # no cov
+    """Base page for Sanic pages."""
+
+    TITLE = "Sanic"
+    HEADING = None
+    CSS: str
+    doc: Builder
+
+    def __init__(self, debug: bool = True) -> None:
+        self.debug = debug
+
+    @property
+    def style(self) -> str:
+        """Returns the CSS for the page.
+
+        Returns:
+            str: The CSS for the page.
+        """
+        return self.CSS
+
+    def render(self) -> str:
+        """Renders the page.
+
+        Returns:
+            str: The rendered page.
+        """
+        self.doc = Document(self.TITLE, lang="en", id="sanic")
+        self._head()
+        self._body()
+        self._foot()
+        return str(self.doc)
+
+    def _head(self) -> None:
+        self.doc.style(HTML(self.style))
+        with self.doc.header:
+            self.doc.div(self.HEADING or self.TITLE)
+
+    def _foot(self) -> None:
+        with self.doc.footer:
+            self.doc.div("powered by")
+            with self.doc.div:
+                self._sanic_logo()
+            if self.debug:
+                self.doc.div(f"Version {VERSION}")
+                with self.doc.div:
+                    for idx, (title, href) in enumerate(
+                        (
+                            ("Docs", "https://sanic.dev"),
+                            ("Help", "https://sanic.dev/en/help.html"),
+                            ("GitHub", "https://github.com/sanic-org/sanic"),
+                        )
+                    ):
+                        if idx > 0:
+                            self.doc(" | ")
+                        self.doc.a(
+                            title,
+                            href=href,
+                            target="_blank",
+                            referrerpolicy="no-referrer",
+                        )
+                self.doc.div("DEBUG mode")
+
+    @abstractmethod
+    def _body(self) -> None: ...
+
+    def _sanic_logo(self) -> None:
+        self.doc.a(
+            HTML(SVG_LOGO_SIMPLE),
+            href="https://sanic.dev",
+            target="_blank",
+            referrerpolicy="no-referrer",
+        )
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/css.py b/.venv/lib/python3.12/site-packages/sanic/pages/css.py
new file mode 100644
index 0000000..519c0bb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/css.py
@@ -0,0 +1,34 @@
+from abc import ABCMeta
+from pathlib import Path
+
+
+CURRENT_DIR = Path(__file__).parent
+
+
+def _extract_style(maybe_style: str | None, name: str) -> str:
+    if maybe_style is not None:
+        maybe_path = Path(maybe_style)
+        if maybe_path.exists():
+            return maybe_path.read_text(encoding="UTF-8")
+        return maybe_style
+    maybe_path = CURRENT_DIR / "styles" / f"{name}.css"
+    if maybe_path.exists():
+        return maybe_path.read_text(encoding="UTF-8")
+    return ""
+
+
+class CSS(ABCMeta):
+    """Cascade stylesheets, i.e. combine all ancestor styles"""
+
+    def __new__(cls, name, bases, attrs):
+        Page = super().__new__(cls, name, bases, attrs)
+        # Use a locally defined STYLE or the one from styles directory
+        Page.STYLE = _extract_style(attrs.get("STYLE_FILE"), name)
+        Page.STYLE += attrs.get("STYLE_APPEND", "")
+        # Combine with all ancestor styles
+        Page.CSS = "".join(
+            Class.STYLE
+            for Class in reversed(Page.__mro__)
+            if type(Class) is CSS
+        )
+        return Page
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/directory_page.py b/.venv/lib/python3.12/site-packages/sanic/pages/directory_page.py
new file mode 100644
index 0000000..5328919
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/directory_page.py
@@ -0,0 +1,63 @@
+from collections.abc import Iterable
+from typing import TypedDict
+
+from html5tagger import E
+
+from .base import BasePage
+
+
+class FileInfo(TypedDict):
+    """Type for file info."""
+
+    icon: str
+    file_name: str
+    file_access: str
+    file_size: str
+
+
+class DirectoryPage(BasePage):  # no cov
+    """Page for viewing a directory."""
+
+    TITLE = "Directory Viewer"
+
+    def __init__(
+        self, files: Iterable[FileInfo], url: str, debug: bool
+    ) -> None:
+        super().__init__(debug)
+        self.files = files
+        self.url = url
+
+    def _body(self) -> None:
+        with self.doc.main:
+            self._headline()
+            files = list(self.files)
+            if files:
+                self._file_table(files)
+            else:
+                self.doc.p("The folder is empty.")
+
+    def _headline(self):
+        """Implement a heading with the current path, combined with
+        breadcrumb links"""
+        with self.doc.h1(id="breadcrumbs"):
+            p = self.url.split("/")[:-1]
+
+            for i, part in enumerate(p):
+                path = "/".join(p[: i + 1]) + "/"
+                with self.doc.a(href=path):
+                    self.doc.span(part, class_="dir").span("/", class_="sep")
+
+    def _file_table(self, files: Iterable[FileInfo]):
+        with self.doc.table(class_="autoindex container"):
+            for f in files:
+                self._file_row(**f)
+
+    def _file_row(
+        self,
+        icon: str,
+        file_name: str,
+        file_access: str,
+        file_size: str,
+    ):
+        first = E.span(icon, class_="icon").a(file_name, href=file_name)
+        self.doc.tr.td(first).td(file_size).td(file_access)
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/error.py b/.venv/lib/python3.12/site-packages/sanic/pages/error.py
new file mode 100644
index 0000000..2220b3f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/error.py
@@ -0,0 +1,112 @@
+from collections.abc import Mapping
+from typing import Any
+
+import tracerite.html
+
+from html5tagger import E
+from tracerite import html_traceback, inspector
+
+from sanic.request import Request
+
+from .base import BasePage
+
+
+# Avoid showing the request in the traceback variable inspectors
+inspector.blacklist_types += (Request,)
+
+ENDUSER_TEXT = """\
+We're sorry, but it looks like something went wrong. Please try refreshing \
+the page or navigating back to the homepage. If the issue persists, our \
+technical team is working to resolve it as soon as possible. We apologize \
+for the inconvenience and appreciate your patience.\
+"""
+
+
+class ErrorPage(BasePage):
+    """Page for displaying an error."""
+
+    STYLE_APPEND = tracerite.html.style
+
+    def __init__(
+        self,
+        debug: bool,
+        title: str,
+        text: str,
+        request: Request,
+        exc: Exception,
+    ) -> None:
+        super().__init__(debug)
+        name = request.app.name.replace("_", " ").strip()
+        if name.islower():
+            name = name.title()
+        self.TITLE = f"Application {name} cannot handle your request"
+        self.HEADING = E("Application ").strong(name)(
+            " cannot handle your request"
+        )
+        self.title = title
+        self.text = text
+        self.request = request
+        self.exc = exc
+        self.details_open = not getattr(exc, "quiet", False)
+
+    def _head(self) -> None:
+        self.doc._script(tracerite.html.javascript)
+        super()._head()
+
+    def _body(self) -> None:
+        debug = self.request.app.debug
+        route_name = self.request.name or "[route not found]"
+        with self.doc.main:
+            self.doc.h1(f"⚠️ {self.title}").p(self.text)
+            # Show context details if available on the exception
+            context = getattr(self.exc, "context", None)
+            if context:
+                self._key_value_table(
+                    "Issue context", "exception-context", context
+                )
+
+            if not debug:
+                with self.doc.div(id="enduser"):
+                    self.doc.p(ENDUSER_TEXT).p.a("Front Page", href="/")
+                return
+            # Show additional details in debug mode,
+            # open by default for 500 errors
+            with self.doc.details(open=self.details_open, class_="smalltext"):
+                # Show extra details if available on the exception
+                extra = getattr(self.exc, "extra", None)
+                if extra:
+                    self._key_value_table(
+                        "Issue extra data", "exception-extra", extra
+                    )
+
+                self.doc.summary(
+                    "Details for developers (Sanic debug mode only)"
+                )
+                if self.exc:
+                    with self.doc.div(class_="exception-wrapper"):
+                        self.doc.h2(f"Exception in {route_name}:")
+                        self.doc(
+                            html_traceback(self.exc, include_js_css=False)
+                        )
+
+                self._key_value_table(
+                    f"{self.request.method} {self.request.path}",
+                    "request-headers",
+                    self.request.headers,
+                )
+
+    def _key_value_table(
+        self, title: str, table_id: str, data: Mapping[str, Any]
+    ) -> None:
+        with self.doc.div(class_="key-value-display"):
+            self.doc.h2(title)
+            with self.doc.dl(id=table_id, class_="key-value-table smalltext"):
+                for key, value in data.items():
+                    # Reading values may cause a new exception, so suppress it
+                    try:
+                        value = str(value)
+                    except Exception:
+                        value = E.em("Unable to display value")
+                    self.doc.dt.span(key, class_="nobr key").span(": ").dd(
+                        value
+                    )
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/styles/BasePage.css b/.venv/lib/python3.12/site-packages/sanic/pages/styles/BasePage.css
new file mode 100644
index 0000000..d914bf2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/styles/BasePage.css
@@ -0,0 +1,146 @@
+/** BasePage **/
+
+:root {
+    --sanic: #ff0d68;
+    --sanic-yellow: #FFE900;
+    --sanic-background: #efeced;
+    --sanic-text: #121010;
+    --sanic-text-lighter: #756169;
+    --sanic-link: #ff0d68;
+    --sanic-block-background: #f7f4f6;
+    --sanic-block-text: #000;
+    --sanic-block-alt-text: #6b6468;
+    --sanic-header-background: #272325;
+    --sanic-header-border: #fff;
+    --sanic-header-text: #fff;
+    --sanic-highlight-background: var(--sanic-yellow);
+    --sanic-highlight-text: var(--sanic-text);
+    --sanic-tab-background: #f7f4f6;
+    --sanic-tab-shadow: #f7f6f6;
+    --sanic-tab-text: #222021;
+    --sanic-tracerite-var: var(--sanic-text);
+    --sanic-tracerite-val: #ff0d68;
+    --sanic-tracerite-type: #6d6a6b;
+}
+
+
+@media (prefers-color-scheme: dark) {
+    :root {
+        --sanic-text: #f7f4f6;
+        --sanic-background: #121010;
+        --sanic-block-background: #0f0d0e;
+        --sanic-block-text: #f7f4f6;
+        --sanic-header-background: #030203;
+        --sanic-header-border: #000;
+        --sanic-highlight-text: var(--sanic-background);
+        --sanic-tab-background: #292728;
+        --sanic-tab-shadow: #0f0d0e;
+        --sanic-tab-text: #aea7ab;
+    }
+}
+
+html {
+    font: 16px sans-serif;
+    background: var(--sanic-background);
+    color: var(--sanic-text);
+    scrollbar-gutter: stable;
+    overflow: hidden auto;
+}
+
+body {
+    margin: 0;
+    font-size: 1.25rem;
+    line-height: 125%;
+}
+
+body>* {
+    padding: 1rem 2vw;
+}
+
+@media (max-width: 1000px) {
+    body>* {
+        padding: 0.5rem 1.5vw;
+    }
+
+    html {
+        /* Scale everything by rem of 6px-16px by viewport width */
+        font-size: calc(6px + 10 * 100vw / 1000);
+    }
+}
+
+main {
+    /* Make sure the footer is closer to bottom */
+    min-height: 70vh;
+    /* Generous padding for readability */
+    padding: 1rem 2.5rem;
+}
+
+.smalltext {
+    font-size: 1.0rem;
+}
+
+.container {
+    min-width: 600px;
+    max-width: 1600px;
+}
+
+header {
+    background: var(--sanic-header-background);
+    color: var(--sanic-header-text);
+    border-bottom: 1px solid var(--sanic-header-border);
+    text-align: center;
+}
+
+footer {
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    font-size: 0.8rem;
+    margin: 2rem;
+    line-height: 1.5em;
+}
+
+h1 {
+    text-align: left;
+}
+
+a {
+    text-decoration: none;
+    color: var(--sanic-link);
+}
+
+a:hover,
+a:focus {
+    text-decoration: underline;
+    outline: none;
+}
+
+
+span.icon {
+    margin-right: 1rem;
+}
+
+#logo-simple {
+    height: 1.75rem;
+    padding: 0 0.25rem;
+}
+
+
+@media (prefers-color-scheme: dark) {
+    #logo-simple path:last-child {
+        fill: #e1e1e1;
+    }
+}
+
+#sanic pre,
+#sanic code {
+    font-family: "Fira Code",
+        "Source Code Pro",
+        Menlo,
+        Meslo,
+        Monaco,
+        Consolas,
+        Lucida Console,
+        monospace;
+    font-size: 0.8rem;
+}
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/styles/DirectoryPage.css b/.venv/lib/python3.12/site-packages/sanic/pages/styles/DirectoryPage.css
new file mode 100644
index 0000000..1f052af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/styles/DirectoryPage.css
@@ -0,0 +1,63 @@
+/** DirectoryPage **/
+#breadcrumbs>a:hover {
+    text-decoration: none;
+}
+
+#breadcrumbs>a .dir {
+    padding: 0 0.25em;
+}
+
+#breadcrumbs>a:first-child:hover::before,
+#breadcrumbs>a .dir:hover {
+    text-decoration: underline;
+}
+
+#breadcrumbs>a:first-child::before {
+    content: "🏠";
+}
+
+#breadcrumbs>a:last-child {
+    color: #ff0d68;
+}
+
+main a {
+    color: inherit;
+    font-weight: bold;
+}
+
+table.autoindex {
+    width: 100%;
+    font-family: monospace;
+    font-size: 1.25rem;
+}
+
+table.autoindex tr {
+    display: flex;
+}
+
+table.autoindex tr:hover {
+    background-color: #ddd;
+}
+
+table.autoindex td {
+    margin: 0 0.5rem;
+}
+
+table.autoindex td:first-child {
+    flex: 1;
+}
+
+table.autoindex td:nth-child(2) {
+    text-align: right;
+}
+
+table.autoindex td:last-child {
+    text-align: right;
+}
+
+
+@media (prefers-color-scheme: dark) {
+    table.autoindex tr:hover {
+        background-color: #222;
+    }
+}
diff --git a/.venv/lib/python3.12/site-packages/sanic/pages/styles/ErrorPage.css b/.venv/lib/python3.12/site-packages/sanic/pages/styles/ErrorPage.css
new file mode 100644
index 0000000..1fd27d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/pages/styles/ErrorPage.css
@@ -0,0 +1,108 @@
+/** ErrorPage **/
+#enduser {
+    max-width: 30em;
+    margin: 5em auto 5em auto;
+    text-align: justify;
+    /*text-justify: both;*/
+}
+
+#enduser a {
+    color: var(--sanic-blue);
+}
+
+#enduser p:last-child {
+    text-align: right;
+}
+
+summary {
+    margin-top: 3em;
+    color: var(--sanic-text-lighter);
+    cursor: pointer;
+}
+
+.tracerite {
+    --tracerite-var: var(--sanic-tracerite-var);
+    --tracerite-val: var(--sanic-tracerite-val);
+    --tracerite-type: var(--sanic-tracerite-type);
+    --tracerite-exception: var(--sanic);
+    --tracerite-highlight: var(--sanic-yellow);
+    --tracerite-tab: var(--sanic-tab-background);
+    --tracerite-tab-text: var(--sanic-tab-text);
+}
+
+.tracerite>h3 {
+    margin: 0.5rem 0 !important;
+}
+
+#sanic .tracerite .traceback-labels button {
+    font-size: 0.8rem;
+    line-height: 120%;
+    background: var(--tracerite-tab);
+    color: var(--tracerite-tab-text);
+    transition: 0.3s;
+    cursor: pointer;
+}
+
+.tracerite .traceback-labels {
+    padding-top: 5px;
+}
+
+.tracerite .traceback-labels button:hover {
+    filter: contrast(150%) brightness(120%) drop-shadow(0 -0 2px var(--sanic-tab-shadow));
+}
+
+#sanic .tracerite .tracerite-tooltip::before {
+    bottom: 1.75em;
+}
+
+#sanic .tracerite .traceback-details mark span {
+    background: var(--sanic-highlight-background);
+    color: var(--sanic-highlight-text);
+}
+
+header {
+    background: var(--sanic-header-background);
+}
+
+h2 {
+    font-size: 1.3rem;
+    color: var(--sanic-text);
+}
+
+.key-value-display,
+.exception-wrapper {
+    padding: 0.5rem;
+    margin-top: 1rem;
+}
+
+.key-value-display {
+    background-color: var(--sanic-block-background);
+    color: var(--sanic-block-text);
+}
+
+.key-value-display h2 {
+    margin-bottom: 0.2em;
+}
+
+dl.key-value-table {
+    width: 100%;
+    margin: 0;
+    display: grid;
+    grid-template-columns: 1fr 5fr;
+    grid-gap: .3em;
+    white-space: pre-wrap;
+}
+
+dl.key-value-table * {
+    margin: 0;
+}
+
+dl.key-value-table dt {
+    color: var(--sanic-block-alt-text);
+    word-break: break-word;
+}
+
+dl.key-value-table dd {
+    /* Better breaking for cookies header and such */
+    word-break: break-all;
+}
diff --git a/.venv/lib/python3.12/site-packages/sanic/py.typed b/.venv/lib/python3.12/site-packages/sanic/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/request/__init__.py b/.venv/lib/python3.12/site-packages/sanic/request/__init__.py
new file mode 100644
index 0000000..3321f23
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/request/__init__.py
@@ -0,0 +1,11 @@
+from .form import File, parse_multipart_form
+from .parameters import RequestParameters
+from .types import Request
+
+
+__all__ = (
+    "File",
+    "parse_multipart_form",
+    "Request",
+    "RequestParameters",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/request/form.py b/.venv/lib/python3.12/site-packages/sanic/request/form.py
new file mode 100644
index 0000000..192f71e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/request/form.py
@@ -0,0 +1,114 @@
+from __future__ import annotations
+
+import email.utils
+import unicodedata
+
+from typing import NamedTuple
+from urllib.parse import unquote
+
+from sanic.headers import parse_content_header
+from sanic.log import logger
+
+from .parameters import RequestParameters
+
+
+class File(NamedTuple):
+    """Model for defining a file.
+
+    It is a `namedtuple`, therefore you can iterate over the object, or
+    access the parameters by name.
+
+    Args:
+        type (str, optional): The mimetype, defaults to "text/plain".
+        body (bytes): Bytes of the file.
+        name (str): The filename.
+    """
+
+    type: str
+    body: bytes
+    name: str
+
+
+def parse_multipart_form(body, boundary):
+    """Parse a request body and returns fields and files
+
+    Args:
+        body (bytes): Bytes request body.
+        boundary (bytes): Bytes multipart boundary.
+
+    Returns:
+        tuple[RequestParameters, RequestParameters]: A tuple containing fields and files as `RequestParameters`.
+    """  # noqa: E501
+    files = {}
+    fields = {}
+
+    form_parts = body.split(boundary)
+    for form_part in form_parts[1:-1]:
+        file_name = None
+        content_type = "text/plain"
+        content_charset = "utf-8"
+        field_name = None
+        line_index = 2
+        line_end_index = 0
+        while not line_end_index == -1:
+            line_end_index = form_part.find(b"\r\n", line_index)
+            form_line = form_part[line_index:line_end_index].decode("utf-8")
+            line_index = line_end_index + 2
+
+            if not form_line:
+                break
+
+            colon_index = form_line.index(":")
+            idx = colon_index + 2
+            form_header_field = form_line[0:colon_index].lower()
+            form_header_value, form_parameters = parse_content_header(
+                form_line[idx:]
+            )
+
+            if form_header_field == "content-disposition":
+                field_name = form_parameters.get("name")
+                file_name = form_parameters.get("filename")
+
+                # non-ASCII filenames in RFC2231, "filename*" format
+                if file_name is None and form_parameters.get("filename*"):
+                    encoding, _, value = email.utils.decode_rfc2231(
+                        form_parameters["filename*"]
+                    )
+                    file_name = unquote(value, encoding=encoding)
+
+                # Normalize to NFC (Apple MacOS/iOS send NFD)
+                # Notes:
+                # - No effect for Windows, Linux or Android clients which
+                #   already send NFC
+                # - Python open() is tricky (creates files in NFC no matter
+                #   which form you use)
+                if file_name is not None:
+                    file_name = unicodedata.normalize("NFC", file_name)
+
+            elif form_header_field == "content-type":
+                content_type = form_header_value
+                content_charset = form_parameters.get("charset", "utf-8")
+
+        if field_name:
+            post_data = form_part[line_index:-4]
+            if file_name is None:
+                value = post_data.decode(content_charset)
+                if field_name in fields:
+                    fields[field_name].append(value)
+                else:
+                    fields[field_name] = [value]
+            else:
+                form_file = File(
+                    type=content_type, name=file_name, body=post_data
+                )
+                if field_name in files:
+                    files[field_name].append(form_file)
+                else:
+                    files[field_name] = [form_file]
+        else:
+            logger.debug(
+                "Form-data field does not have a 'name' parameter "
+                "in the Content-Disposition header"
+            )
+
+    return RequestParameters(fields), RequestParameters(files)
diff --git a/.venv/lib/python3.12/site-packages/sanic/request/parameters.py b/.venv/lib/python3.12/site-packages/sanic/request/parameters.py
new file mode 100644
index 0000000..5d5b9e9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/request/parameters.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from typing import Any
+
+
+class RequestParameters(dict):
+    """Hosts a dict with lists as values where get returns the first value of the list and getlist returns the whole shebang"""  # noqa: E501
+
+    def get(self, name: str, default: Any | None = None) -> Any | None:
+        """Return the first value, either the default or actual
+
+        Args:
+            name (str): The name of the parameter
+            default (Any | None, optional): The default value. Defaults to None.
+
+        Returns:
+            Any | None: The first value of the list
+        """  # noqa: E501
+        return super().get(name, [default])[0]
+
+    def getlist(
+        self, name: str, default: list[Any] | None = None
+    ) -> list[Any]:
+        """Return the entire list
+
+        Args:
+            name (str): The name of the parameter
+            default (list[Any] | None, optional): The default value. Defaults to None.
+
+        Returns:
+            list[Any]: The entire list of values or [] if not found
+        """  # noqa: E501
+        return super().get(name, default) or []
diff --git a/.venv/lib/python3.12/site-packages/sanic/request/types.py b/.venv/lib/python3.12/site-packages/sanic/request/types.py
new file mode 100644
index 0000000..187978f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/request/types.py
@@ -0,0 +1,1145 @@
+from __future__ import annotations
+
+from asyncio import BaseProtocol
+from collections import defaultdict
+from contextvars import ContextVar
+from inspect import isawaitable
+from types import SimpleNamespace
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Generic,
+    cast,
+)
+
+from sanic_routing.route import Route
+from typing_extensions import TypeVar
+
+from sanic.http.constants import HTTP  # type: ignore
+from sanic.http.stream import Stream
+from sanic.models.asgi import ASGIScope
+from sanic.models.http_types import Credentials
+
+
+if TYPE_CHECKING:
+    from sanic.app import Sanic
+    from sanic.config import Config
+    from sanic.server import ConnInfo
+
+import uuid
+
+from urllib.parse import parse_qs, parse_qsl, urlunparse
+
+from httptools import parse_url
+from httptools.parser.errors import HttpParserInvalidURLError
+
+from sanic.compat import CancelledErrors, Header
+from sanic.constants import (
+    CACHEABLE_HTTP_METHODS,
+    DEFAULT_HTTP_CONTENT_TYPE,
+    IDEMPOTENT_HTTP_METHODS,
+    SAFE_HTTP_METHODS,
+)
+from sanic.cookies.request import CookieRequestParameters, parse_cookie
+from sanic.exceptions import BadRequest, BadURL, ServerError
+from sanic.headers import (
+    AcceptList,
+    Options,
+    parse_accept,
+    parse_content_header,
+    parse_credentials,
+    parse_forwarded,
+    parse_host,
+    parse_xforwarded,
+)
+from sanic.http import Stage
+from sanic.log import error_logger
+from sanic.models.protocol_types import TransportProtocol
+from sanic.response import BaseHTTPResponse, HTTPResponse
+
+from .form import parse_multipart_form
+from .parameters import RequestParameters
+
+
+try:
+    from ujson import loads as json_loads  # type: ignore
+except ImportError:
+    from json import loads as json_loads  # type: ignore
+
+if TYPE_CHECKING:
+    # The default argument of TypeVar is proposed to be added in Python 3.13
+    # by PEP 696 (https://www.python.org/dev/peps/pep-0696/).
+    # Therefore, we use typing_extensions.TypeVar for compatibility.
+    # For more information, see:
+    # https://discuss.python.org/t/pep-696-type-defaults-for-typevarlikes
+    sanic_type = TypeVar(
+        "sanic_type", bound=Sanic, default=Sanic[Config, SimpleNamespace]
+    )
+    ctx_type = TypeVar(
+        "ctx_type", bound=SimpleNamespace, default=SimpleNamespace
+    )
+else:
+    sanic_type = TypeVar("sanic_type")
+    ctx_type = TypeVar("ctx_type")
+
+
+class Request(Generic[sanic_type, ctx_type]):
+    """State of HTTP request.
+
+    Args:
+        url_bytes (bytes): Raw URL bytes.
+        headers (Header): Request headers.
+        version (str): HTTP version.
+        method (str): HTTP method.
+        transport (TransportProtocol): Transport protocol.
+        app (Sanic): Sanic instance.
+        head (bytes, optional): Request head. Defaults to `b""`.
+        stream_id (int, optional): HTTP/3 stream ID. Defaults to `0`.
+    """
+
+    _current: ContextVar[Request] = ContextVar("request")
+    _loads = json_loads
+
+    __slots__ = (
+        "__weakref__",
+        "_cookies",
+        "_ctx",
+        "_id",
+        "_ip",
+        "_parsed_url",
+        "_port",
+        "_protocol",
+        "_remote_addr",
+        "_request_middleware_started",
+        "_response_middleware_started",
+        "_scheme",
+        "_socket",
+        "_stream_id",
+        "_match_info",
+        "_name",
+        "app",
+        "body",
+        "conn_info",
+        "head",
+        "headers",
+        "method",
+        "parsed_accept",
+        "parsed_args",
+        "parsed_cookies",
+        "parsed_credentials",
+        "parsed_files",
+        "parsed_form",
+        "parsed_forwarded",
+        "parsed_json",
+        "parsed_not_grouped_args",
+        "parsed_token",
+        "raw_url",
+        "responded",
+        "route",
+        "stream",
+        "transport",
+        "version",
+    )
+
+    def __init__(
+        self,
+        url_bytes: bytes,
+        headers: Header,
+        version: str,
+        method: str,
+        transport: TransportProtocol,
+        app: sanic_type,
+        head: bytes = b"",
+        stream_id: int = 0,
+    ):
+        self.raw_url = url_bytes
+        try:
+            self._parsed_url = parse_url(url_bytes)
+        except HttpParserInvalidURLError:
+            url = url_bytes.decode(errors="backslashreplace")
+            raise BadURL(f"Bad URL: {url}")
+        self._id: uuid.UUID | str | int | None = None
+        self._name: str | None = None
+        self._stream_id = stream_id
+        self.app = app
+
+        self.headers = Header(headers)
+        self.version = version
+        self.method = method
+        self.transport = transport
+        self.head = head
+
+        # Init but do not inhale
+        self.body = b""
+        self.conn_info: ConnInfo | None = None
+        self._ctx: ctx_type | None = None
+        self.parsed_accept: AcceptList | None = None
+        self.parsed_args: defaultdict[
+            tuple[bool, bool, str, str], RequestParameters
+        ] = defaultdict(RequestParameters)
+        self.parsed_cookies: RequestParameters | None = None
+        self.parsed_credentials: Credentials | None = None
+        self.parsed_files: RequestParameters | None = None
+        self.parsed_form: RequestParameters | None = None
+        self.parsed_forwarded: Options | None = None
+        self.parsed_json = None
+        self.parsed_not_grouped_args: defaultdict[
+            tuple[bool, bool, str, str], list[tuple[str, str]]
+        ] = defaultdict(list)
+        self.parsed_token: str | None = None
+        self._request_middleware_started = False
+        self._response_middleware_started = False
+        self.responded: bool = False
+        self.route: Route | None = None
+        self.stream: Stream | None = None
+        self._match_info: dict[str, Any] = {}
+        self._protocol: BaseProtocol | None = None
+
+    def __repr__(self):
+        class_name = self.__class__.__name__
+        return f"<{class_name}: {self.method} {self.path}>"
+
+    @staticmethod
+    def make_context() -> ctx_type:
+        """Create a new context object.
+
+        This method is called when a new request context is pushed. It is
+        a great candidate for overriding in a subclass if you want to
+        control the type of context object that is created.
+
+        By default, it returns a `types.SimpleNamespace` instance.
+
+        Returns:
+            ctx_type: A new context object.
+        """
+        return cast(ctx_type, SimpleNamespace())
+
+    @classmethod
+    def get_current(cls) -> Request:
+        """Retrieve the current request object
+
+        This implements [Context Variables](https://docs.python.org/3/library/contextvars.html)
+        to allow for accessing the current request from anywhere.
+
+        A typical usecase is when you want to access the current request
+        from a function that is not a handler, such as a logging function:
+
+        ```python
+        import logging
+
+        class LoggingFormater(logging.Formatter):
+            def format(self, record):
+                request = Request.get_current()
+                record.url = request.url
+                record.ip = request.ip
+                return super().format(record)
+        ```
+
+        Returns:
+            Request: The current request object
+
+        Raises:
+            sanic.exceptions.ServerError: If it is outside of a request
+                lifecycle.
+        """  # noqa: E501
+        request = cls._current.get(None)
+        if not request:
+            raise ServerError("No current request")
+        return request
+
+    @classmethod
+    def generate_id(*_) -> uuid.UUID | str | int:
+        """Generate a unique ID for the request.
+
+        This method is called to generate a unique ID for each request.
+        By default, it returns a `uuid.UUID` instance.
+
+        Returns:
+            uuid.UUID | str | int: A unique ID for the request.
+        """
+        return uuid.uuid4()
+
+    @property
+    def ctx(self) -> ctx_type:
+        """The current request context.
+
+        This is a context object for the current request. It is created
+        by `Request.make_context` and is a great place to store data
+        that you want to be accessible during the request lifecycle.
+
+        Returns:
+            ctx_type: The current request context.
+        """
+        if not self._ctx:
+            self._ctx = self.make_context()
+        return self._ctx
+
+    @property
+    def stream_id(self) -> int:
+        """Access the HTTP/3 stream ID.
+
+        Raises:
+            sanic.exceptions.ServerError: If the request is not HTTP/3.
+
+        Returns:
+            int: The HTTP/3 stream ID.
+        """
+        if self.protocol.version is not HTTP.VERSION_3:
+            raise ServerError(
+                "Stream ID is only a property of a HTTP/3 request"
+            )
+        return self._stream_id
+
+    def reset_response(self) -> None:
+        """Reset the response object.
+
+        This clears much of the state of the object. It should
+        generally not be called directly, but is called automatically as
+        part of the request lifecycle.
+
+        Raises:
+            sanic.exceptions.ServerError: If the response has already been
+                sent.
+        """
+        try:
+            if (
+                self.stream is not None
+                and self.stream.stage is not Stage.HANDLER
+            ):
+                raise ServerError(
+                    "Cannot reset response because previous response was sent."
+                )
+            self.stream.response.stream = None  # type: ignore
+            self.stream.response = None  # type: ignore
+            self.responded = False
+        except AttributeError:
+            pass
+
+    async def respond(
+        self,
+        response: BaseHTTPResponse | None = None,
+        *,
+        status: int = 200,
+        headers: Header | dict[str, str] | None = None,
+        content_type: str | None = None,
+    ):
+        """Respond to the request without returning.
+
+        This method can only be called once, as you can only respond once.
+        If no ``response`` argument is passed, one will be created from the
+        ``status``, ``headers`` and ``content_type`` arguments.
+
+        **The first typical usecase** is if you wish to respond to the
+        request without returning from the handler:
+
+        ```python
+        @app.get("/")
+        async def handler(request: Request):
+            data = ...  # Process something
+
+            json_response = json({"data": data})
+            await request.respond(json_response)
+
+        @app.on_response
+        async def add_header(_, response: HTTPResponse):
+            # Middlewares still get executed as expected
+            response.headers["one"] = "two"
+        ```
+
+        **The second possible usecase** is for when you want to directly
+        respond to the request:
+
+        ```python
+        response = await request.respond(content_type="text/csv")
+        await response.send("foo,")
+        await response.send("bar")
+
+        # You can control the completion of the response by calling
+        # the 'eof()' method:
+        await response.eof()
+        ```
+
+        Args:
+            response (ResponseType): Response instance to send.
+            status (int): Status code to return in the response.
+            headers (dict[str, str] | None): Headers to return in the response, defaults to None.
+            content_type (str | None): Content-Type header of the response, defaults to None.
+
+        Returns:
+            FinalResponseType: Final response being sent (may be different from the
+                "response" parameter because of middlewares), which can be
+                used to manually send data.
+        """  # noqa: E501
+        try:
+            if self.stream is not None and self.stream.response:
+                raise ServerError("Second respond call is not allowed.")
+        except AttributeError:
+            pass
+        # This logic of determining which response to use is subject to change
+        if response is None:
+            response = HTTPResponse(
+                status=status,
+                headers=headers,
+                content_type=content_type,
+            )
+
+        # Connect the response
+        if isinstance(response, BaseHTTPResponse) and self.stream:
+            response = self.stream.respond(response)
+
+            if isawaitable(response):
+                response = await response  # type: ignore
+        # Run response middleware
+        try:
+            middleware = (
+                self.route and self.route.extra.response_middleware
+            ) or self.app.response_middleware
+            if middleware and not self._response_middleware_started:
+                self._response_middleware_started = True
+                response = await self.app._run_response_middleware(
+                    self, response, middleware
+                )
+        except CancelledErrors:
+            raise
+        except Exception:
+            error_logger.exception(
+                "Exception occurred in one of response middleware handlers"
+            )
+        self.responded = True
+        return response
+
+    async def receive_body(self):
+        """Receive request.body, if not already received.
+
+        Streaming handlers may call this to receive the full body. Sanic calls
+        this function before running any handlers of non-streaming routes.
+
+        Custom request classes can override this for custom handling of both
+        streaming and non-streaming routes.
+        """
+        if not self.body:
+            self.body = b"".join([data async for data in self.stream])
+
+    @property
+    def name(self) -> str | None:
+        """The route name
+
+        In the following pattern:
+
+        ```
+        .[.]
+        ```
+
+        Returns:
+            str | None: The route name
+        """
+        if self._name:
+            return self._name
+        elif self.route:
+            return self.route.name
+        return None
+
+    @property
+    def endpoint(self) -> str | None:
+        """Alias of `sanic.request.Request.name`
+
+        Returns:
+            str | None: The route name
+        """
+        return self.name
+
+    @property
+    def uri_template(self) -> str | None:
+        """The defined URI template
+
+        Returns:
+            str | None: The defined URI template
+        """
+        if self.route:
+            return f"/{self.route.path}"
+        return None
+
+    @property
+    def protocol(self) -> TransportProtocol:
+        """The HTTP protocol instance
+
+        Returns:
+            Protocol: The HTTP protocol instance
+        """
+        if not self._protocol:
+            self._protocol = self.transport.get_protocol()
+        return self._protocol  # type: ignore
+
+    @property
+    def raw_headers(self) -> bytes:
+        """The unparsed HTTP headers
+
+        Returns:
+            bytes: The unparsed HTTP headers
+        """
+        _, headers = self.head.split(b"\r\n", 1)
+        return bytes(headers)
+
+    @property
+    def request_line(self) -> bytes:
+        """The first line of a HTTP request
+
+        Returns:
+            bytes: The first line of a HTTP request
+        """
+        reqline, _ = self.head.split(b"\r\n", 1)
+        return bytes(reqline)
+
+    @property
+    def id(self) -> uuid.UUID | str | int | None:
+        """A request ID passed from the client, or generated from the backend.
+
+        By default, this will look in a request header defined at:
+        `self.app.config.REQUEST_ID_HEADER`. It defaults to
+        `X-Request-ID`. Sanic will try to cast the ID into a `UUID` or an
+        `int`.
+
+        If there is not a UUID from the client, then Sanic will try
+        to generate an ID by calling `Request.generate_id()`. The default
+        behavior is to generate a `UUID`. You can customize this behavior
+        by subclassing `Request` and overwriting that method.
+
+        ```python
+        from sanic import Request, Sanic
+        from itertools import count
+
+        class IntRequest(Request):
+            counter = count()
+
+            def generate_id(self):
+                return next(self.counter)
+
+        app = Sanic("MyApp", request_class=IntRequest)
+        ```
+
+        Returns:
+            uuid.UUID | str | int | None: A request ID passed from the
+                client, or generated from the backend.
+        """
+        if not self._id:
+            self._id = self.headers.getone(
+                self.app.config.REQUEST_ID_HEADER,
+                self.__class__.generate_id(self),  # type: ignore
+            )
+
+            # Try casting to a UUID or an integer
+            if isinstance(self._id, str):
+                try:
+                    self._id = uuid.UUID(self._id)
+                except ValueError:
+                    try:
+                        self._id = int(self._id)  # type: ignore
+                    except ValueError:
+                        ...
+
+        return self._id  # type: ignore
+
+    @property
+    def json(self) -> Any:
+        """The request body parsed as JSON
+
+        Returns:
+            Any: The request body parsed as JSON
+        """
+        if self.parsed_json is None:
+            self.load_json()
+
+        return self.parsed_json
+
+    def load_json(self, loads=None) -> Any:
+        """Load the request body as JSON
+
+        Args:
+            loads (Callable, optional): A custom JSON loader. Defaults to None.
+
+        Raises:
+            BadRequest: If the request body cannot be parsed as JSON
+
+        Returns:
+            Any: The request body parsed as JSON
+        """
+        try:
+            if not loads:
+                loads = self.__class__._loads
+
+            self.parsed_json = loads(self.body)
+        except Exception:
+            if not self.body:
+                return None
+            raise BadRequest("Failed when parsing body as json")
+
+        return self.parsed_json
+
+    @property
+    def accept(self) -> AcceptList:
+        """Accepted response content types.
+
+        A convenience handler for easier RFC-compliant matching of MIME types,
+        parsed as a list that can match wildcards and includes */* by default.
+
+        Returns:
+            AcceptList: Accepted response content types
+        """
+        if self.parsed_accept is None:
+            self.parsed_accept = parse_accept(self.headers.get("accept"))
+        return self.parsed_accept
+
+    @property
+    def token(self) -> str | None:
+        """Attempt to return the auth header token.
+
+        Returns:
+            str | None: The auth header token
+        """
+        if self.parsed_token is None:
+            prefixes = ("Bearer", "Token")
+            _, token = parse_credentials(
+                self.headers.getone("authorization", None), prefixes
+            )
+            self.parsed_token = token
+        return self.parsed_token
+
+    @property
+    def credentials(self) -> Credentials | None:
+        """Attempt to return the auth header value.
+
+        Covers NoAuth, Basic Auth, Bearer Token, Api Token authentication
+        schemas.
+
+        Returns:
+            Credentials | None: A Credentials object with token, or username
+                and password related to the request
+        """
+        if self.parsed_credentials is None:
+            try:
+                prefix, credentials = parse_credentials(
+                    self.headers.getone("authorization", None)
+                )
+                if credentials:
+                    self.parsed_credentials = Credentials(
+                        auth_type=prefix, token=credentials
+                    )
+            except ValueError:
+                pass
+        return self.parsed_credentials
+
+    def get_form(
+        self, keep_blank_values: bool = False
+    ) -> RequestParameters | None:
+        """Method to extract and parse the form data from a request.
+
+        Args:
+            keep_blank_values (bool): Whether to discard blank values from the form data.
+
+        Returns:
+            RequestParameters | None: The parsed form data.
+        """  # noqa: E501
+        self.parsed_form = RequestParameters()
+        self.parsed_files = RequestParameters()
+        content_type = self.headers.getone(
+            "content-type", DEFAULT_HTTP_CONTENT_TYPE
+        )
+        content_type, parameters = parse_content_header(content_type)
+        try:
+            if content_type == "application/x-www-form-urlencoded":
+                self.parsed_form = RequestParameters(
+                    parse_qs(
+                        self.body.decode("utf-8"),
+                        keep_blank_values=keep_blank_values,
+                    )
+                )
+            elif content_type == "multipart/form-data":
+                # TODO: Stream this instead of reading to/from memory
+                boundary = parameters["boundary"].encode(  # type: ignore
+                    "utf-8"
+                )  # type: ignore
+                self.parsed_form, self.parsed_files = parse_multipart_form(
+                    self.body, boundary
+                )
+        except Exception:
+            error_logger.exception("Failed when parsing form")
+
+        return self.parsed_form
+
+    @property
+    def form(self) -> RequestParameters | None:
+        """The request body parsed as form data
+
+        Returns:
+            RequestParameters | None: The request body parsed as form data
+        """
+        if self.parsed_form is None:
+            self.get_form()
+
+        return self.parsed_form
+
+    @property
+    def files(self) -> RequestParameters | None:
+        """The request body parsed as uploaded files
+
+        Returns:
+            RequestParameters | None: The request body parsed as uploaded files
+        """  # noqa: E501
+        if self.parsed_files is None:
+            self.form  # compute form to get files
+
+        return self.parsed_files
+
+    def get_args(
+        self,
+        keep_blank_values: bool = False,
+        strict_parsing: bool = False,
+        encoding: str = "utf-8",
+        errors: str = "replace",
+    ) -> RequestParameters:
+        """Parse `query_string` using `urllib.parse.parse_qs`.
+
+        This methods is used by the `args` property, but it also
+        can be used directly if you need to change default parameters.
+
+        Args:
+            keep_blank_values (bool): Flag indicating whether blank values in
+                percent-encoded queries should be treated as blank strings.
+                A `True` value indicates that blanks should be retained as
+                blank strings. The default `False` value indicates that
+                blank values are to be ignored and treated as if they were
+                not included.
+            strict_parsing (bool): Flag indicating what to do with parsing
+                errors. If `False` (the default), errors are silently ignored.
+                If `True`, errors raise a `ValueError` exception.
+            encoding (str): Specify how to decode percent-encoded sequences
+                into Unicode characters, as accepted by the
+                `bytes.decode()` method.
+            errors (str): Specify how to decode percent-encoded sequences
+                into Unicode characters, as accepted by the
+                `bytes.decode()` method.
+
+        Returns:
+            RequestParameters: A dictionary containing the parsed arguments.
+        """
+        if (
+            keep_blank_values,
+            strict_parsing,
+            encoding,
+            errors,
+        ) not in self.parsed_args:
+            if self.query_string:
+                self.parsed_args[
+                    (keep_blank_values, strict_parsing, encoding, errors)
+                ] = RequestParameters(
+                    parse_qs(
+                        qs=self.query_string,
+                        keep_blank_values=keep_blank_values,
+                        strict_parsing=strict_parsing,
+                        encoding=encoding,
+                        errors=errors,
+                    )
+                )
+
+        return self.parsed_args[
+            (keep_blank_values, strict_parsing, encoding, errors)
+        ]
+
+    args = property(get_args)
+    """Convenience property to access `Request.get_args` with default values.
+    """
+
+    def get_query_args(
+        self,
+        keep_blank_values: bool = False,
+        strict_parsing: bool = False,
+        encoding: str = "utf-8",
+        errors: str = "replace",
+    ) -> list:
+        """Parse `query_string` using `urllib.parse.parse_qsl`.
+
+        This methods is used by `query_args` propertyn but can be used
+        directly if you need to change default parameters.
+
+        Args:
+            keep_blank_values (bool): Flag indicating whether blank values in
+                percent-encoded queries should be treated as blank strings.
+                A `True` value indicates that blanks should be retained as
+                blank strings. The default `False` value indicates that
+                blank values are to be ignored and treated as if they were
+                not included.
+            strict_parsing (bool): Flag indicating what to do with
+                parsing errors. If `False` (the default), errors are
+                silently ignored. If `True`, errors raise a
+                `ValueError` exception.
+            encoding (str): Specify how to decode percent-encoded sequences
+                into Unicode characters, as accepted by the
+                `bytes.decode()` method.
+            errors (str): Specify how to decode percent-encoded sequences
+                into Unicode characters, as accepted by the
+                `bytes.decode()` method.
+
+        Returns:
+            list: A list of tuples containing the parsed arguments.
+        """
+        if (
+            keep_blank_values,
+            strict_parsing,
+            encoding,
+            errors,
+        ) not in self.parsed_not_grouped_args:
+            if self.query_string:
+                self.parsed_not_grouped_args[
+                    (keep_blank_values, strict_parsing, encoding, errors)
+                ] = parse_qsl(
+                    qs=self.query_string,
+                    keep_blank_values=keep_blank_values,
+                    strict_parsing=strict_parsing,
+                    encoding=encoding,
+                    errors=errors,
+                )
+        return self.parsed_not_grouped_args[
+            (keep_blank_values, strict_parsing, encoding, errors)
+        ]
+
+    query_args = property(get_query_args)
+    """Convenience property to access `Request.get_query_args` with default values.
+    """  # noqa: E501
+
+    def get_cookies(self) -> RequestParameters:
+        cookie = self.headers.getone("cookie", "")
+        self.parsed_cookies = CookieRequestParameters(parse_cookie(cookie))
+        return self.parsed_cookies
+
+    @property
+    def cookies(self) -> RequestParameters:
+        """Incoming cookies on the request
+
+        Returns:
+            RequestParameters: Incoming cookies on the request
+        """
+
+        if self.parsed_cookies is None:
+            self.get_cookies()
+        return cast(CookieRequestParameters, self.parsed_cookies)
+
+    @property
+    def content_type(self) -> str:
+        """Content-Type header form the request
+
+        Returns:
+            str: Content-Type header form the request
+        """
+        return self.headers.getone("content-type", DEFAULT_HTTP_CONTENT_TYPE)
+
+    @property
+    def match_info(self) -> dict[str, Any]:
+        """Matched path parameters after resolving route
+
+        Returns:
+            dict[str, Any]: Matched path parameters after resolving route
+        """
+        return self._match_info
+
+    @match_info.setter
+    def match_info(self, value):
+        self._match_info = value
+
+    @property
+    def ip(self) -> str:
+        """Peer ip of the socket
+
+        Returns:
+            str: Peer ip of the socket
+        """
+        return self.conn_info.client_ip if self.conn_info else ""
+
+    @property
+    def port(self) -> int:
+        """Peer port of the socket
+
+        Returns:
+            int: Peer port of the socket
+        """
+        return self.conn_info.client_port if self.conn_info else 0
+
+    @property
+    def socket(self) -> tuple[str, int] | tuple[None, None]:
+        """Information about the connected socket if available
+
+        Returns:
+            tuple[str, int] | tuple[None, None]: Information about the
+                connected socket if available, in the form of a tuple of
+                (ip, port)
+        """
+        return (
+            self.conn_info.peername
+            if self.conn_info and self.conn_info.peername
+            else (None, None)
+        )
+
+    @property
+    def path(self) -> str:
+        """Path of the local HTTP request
+
+        Returns:
+            str: Path of the local HTTP request
+        """
+        return self._parsed_url.path.decode("utf-8")
+
+    @property
+    def network_paths(self) -> list[Any] | None:
+        """Access the network paths if available
+
+        Returns:
+            list[Any] | None: Access the network paths if available
+        """
+        if self.conn_info is None:
+            return None
+        return self.conn_info.network_paths
+
+    # Proxy properties (using SERVER_NAME/forwarded/request/transport info)
+
+    @property
+    def forwarded(self) -> Options:
+        """Active proxy information obtained from request headers, as specified in Sanic configuration.
+
+        Field names by, for, proto, host, port and path are normalized.
+        - for and by IPv6 addresses are bracketed
+        - port (int) is only set by port headers, not from host.
+        - path is url-unencoded
+
+        Additional values may be available from new style Forwarded headers.
+
+        Returns:
+            Options: proxy information from request headers
+        """  # noqa: E501
+        if self.parsed_forwarded is None:
+            self.parsed_forwarded = (
+                parse_forwarded(self.headers, self.app.config)
+                or parse_xforwarded(self.headers, self.app.config)
+                or {}
+            )
+        return self.parsed_forwarded
+
+    @property
+    def remote_addr(self) -> str:
+        """Client IP address, if available from proxy.
+
+        Returns:
+            str: IPv4, bracketed IPv6, UNIX socket name or arbitrary string
+        """
+        if not hasattr(self, "_remote_addr"):
+            self._remote_addr = str(self.forwarded.get("for", ""))
+        return self._remote_addr
+
+    @property
+    def client_ip(self) -> str:
+        """
+        Client IP address.
+        1. proxied remote address `self.forwarded['for']`
+        2. local peer address `self.ip`
+
+        New in Sanic 23.6. Prefer this over `remote_addr` for determining the
+        client address regardless of whether the service runs behind a proxy
+        or not (proxy deployment needs separate configuration).
+
+        Returns:
+            str: IPv4, bracketed IPv6, UNIX socket name or arbitrary string
+        """
+        return self.remote_addr or self.ip
+
+    @property
+    def scheme(self) -> str:
+        """Determine request scheme.
+
+        1. `config.SERVER_NAME` if in full URL format
+        2. proxied proto/scheme
+        3. local connection protocol
+
+        Returns:
+            str: http|https|ws|wss or arbitrary value given by the headers.
+        """
+        if not hasattr(self, "_scheme"):
+            if (
+                self.app.websocket_enabled
+                and self.headers.upgrade.lower() == "websocket"
+            ):
+                scheme = "ws"
+            else:
+                scheme = "http"
+            proto = None
+            sp = self.app.config.get("SERVER_NAME", "").split("://", 1)
+            if len(sp) == 2:
+                proto = sp[0]
+            elif "proto" in self.forwarded:
+                proto = str(self.forwarded["proto"])
+            if proto:
+                # Give ws/wss if websocket, otherwise keep the same
+                scheme = proto.replace("http", scheme)
+            elif self.conn_info and self.conn_info.ssl:
+                scheme += "s"
+            self._scheme = scheme
+
+        return self._scheme
+
+    @property
+    def host(self) -> str:
+        """The currently effective server 'host' (hostname or hostname:port).
+
+        1. `config.SERVER_NAME` overrides any client headers
+        2. proxied host of original request
+        3. request host header
+        hostname and port may be separated by
+        `sanic.headers.parse_host(request.host)`.
+
+        Returns:
+            str: the first matching host found, or empty string
+        """
+        server_name = self.app.config.get("SERVER_NAME")
+        if server_name:
+            return server_name.split("//", 1)[-1].split("/", 1)[0]
+        return str(
+            self.forwarded.get("host") or self.headers.getone("host", "")
+        )
+
+    @property
+    def server_name(self) -> str:
+        """hostname the client connected to, by `request.host`
+
+        Returns:
+            str: hostname the client connected to, by `request.host`
+        """
+        return parse_host(self.host)[0] or ""
+
+    @property
+    def server_port(self) -> int:
+        """The port the client connected to, by forwarded `port` or `request.host`.
+
+        Default port is returned as 80 and 443 based on `request.scheme`.
+
+        Returns:
+            int: The port the client connected to, by forwarded `port` or `request.host`.
+        """  # noqa: E501
+        port = self.forwarded.get("port") or parse_host(self.host)[1]
+        return int(port or (80 if self.scheme in ("http", "ws") else 443))
+
+    @property
+    def server_path(self) -> str:
+        """Full path of current URL; uses proxied or local path
+
+        Returns:
+            str: Full path of current URL; uses proxied or local path
+        """
+        return str(self.forwarded.get("path") or self.path)
+
+    @property
+    def query_string(self) -> str:
+        """Representation of the requested query
+
+        Returns:
+            str: Representation of the requested query
+        """
+        if self._parsed_url.query:
+            return self._parsed_url.query.decode("utf-8")
+        else:
+            return ""
+
+    @property
+    def url(self) -> str:
+        """The URL
+
+        Returns:
+            str: The URL
+        """
+        return urlunparse(
+            (self.scheme, self.host, self.path, None, self.query_string, None)
+        )
+
+    def url_for(self, view_name: str, **kwargs) -> str:
+        """Retrieve a URL for a given view name.
+
+        Same as `sanic.Sanic.url_for`, but automatically determine `scheme`
+        and `netloc` base on the request. Since this method is aiming
+        to generate correct schema & netloc, `_external` is implied.
+
+        Args:
+            view_name (str): The view name to generate URL for.
+            **kwargs: Arbitrary keyword arguments to build URL query string.
+
+        Returns:
+            str: The generated URL.
+        """
+        # Full URL SERVER_NAME can only be handled in app.url_for
+        try:
+            sp = self.app.config.get("SERVER_NAME", "").split("://", 1)
+            if len(sp) == 2:
+                return self.app.url_for(view_name, _external=True, **kwargs)
+        except AttributeError:
+            pass
+
+        scheme = self.scheme
+        host = self.server_name
+        port = self.server_port
+
+        if (scheme.lower() in ("http", "ws") and port == 80) or (
+            scheme.lower() in ("https", "wss") and port == 443
+        ):
+            netloc = host
+        else:
+            netloc = f"{host}:{port}"
+
+        return self.app.url_for(
+            view_name, _external=True, _scheme=scheme, _server=netloc, **kwargs
+        )
+
+    @property
+    def scope(self) -> ASGIScope:
+        """The ASGI scope of the request.
+
+        Returns:
+            ASGIScope: The ASGI scope of the request.
+
+        Raises:
+            NotImplementedError: If the app isn't an ASGI app.
+        """
+        if not self.app.asgi:
+            raise NotImplementedError(
+                "App isn't running in ASGI mode. "
+                "Scope is only available for ASGI apps."
+            )
+
+        return self.transport.scope
+
+    @property
+    def is_safe(self) -> bool:
+        """Whether the HTTP method is safe.
+
+        See https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
+
+        Returns:
+            bool: Whether the HTTP method is safe.
+        """
+        return self.method in SAFE_HTTP_METHODS
+
+    @property
+    def is_idempotent(self) -> bool:
+        """Whether the HTTP method is iempotent.
+
+        See https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2
+
+        Returns:
+            bool: Whether the HTTP method is iempotent.
+        """
+        return self.method in IDEMPOTENT_HTTP_METHODS
+
+    @property
+    def is_cacheable(self) -> bool:
+        """Whether the HTTP method is cacheable.
+
+        See https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.3
+
+        Returns:
+            bool: Whether the HTTP method is cacheable.
+        """
+        return self.method in CACHEABLE_HTTP_METHODS
diff --git a/.venv/lib/python3.12/site-packages/sanic/response/__init__.py b/.venv/lib/python3.12/site-packages/sanic/response/__init__.py
new file mode 100644
index 0000000..99b9307
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/response/__init__.py
@@ -0,0 +1,36 @@
+from .convenience import (
+    empty,
+    file,
+    file_stream,
+    html,
+    json,
+    raw,
+    redirect,
+    text,
+    validate_file,
+)
+from .types import (
+    BaseHTTPResponse,
+    HTTPResponse,
+    JSONResponse,
+    ResponseStream,
+    json_dumps,
+)
+
+
+__all__ = (
+    "BaseHTTPResponse",
+    "HTTPResponse",
+    "JSONResponse",
+    "ResponseStream",
+    "empty",
+    "json",
+    "text",
+    "raw",
+    "html",
+    "validate_file",
+    "file",
+    "redirect",
+    "file_stream",
+    "json_dumps",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/response/convenience.py b/.venv/lib/python3.12/site-packages/sanic/response/convenience.py
new file mode 100644
index 0000000..da82db9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/response/convenience.py
@@ -0,0 +1,412 @@
+from __future__ import annotations
+
+from datetime import datetime, timezone
+from email.utils import formatdate, parsedate_to_datetime
+from mimetypes import guess_type
+from os import path
+from pathlib import PurePath
+from time import time
+from typing import Any, AnyStr, Callable
+from urllib.parse import quote_plus
+
+from sanic.compat import Header, open_async, stat_async
+from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
+from sanic.helpers import Default, _default
+from sanic.log import logger
+from sanic.models.protocol_types import HTMLProtocol, Range
+
+from .types import HTTPResponse, JSONResponse, ResponseStream
+
+
+def empty(
+    status: int = 204, headers: dict[str, str] | None = None
+) -> HTTPResponse:
+    """Returns an empty response to the client.
+
+    Args:
+        status (int, optional): HTTP response code. Defaults to `204`.
+        headers ([type], optional): Custom HTTP headers. Defaults to `None`.
+
+    Returns:
+        HTTPResponse: An empty response to the client.
+    """
+    return HTTPResponse(body=b"", status=status, headers=headers)
+
+
+def json(
+    body: Any,
+    status: int = 200,
+    headers: dict[str, str] | None = None,
+    content_type: str = "application/json",
+    dumps: Callable[..., AnyStr] | None = None,
+    **kwargs: Any,
+) -> JSONResponse:
+    """Returns response object with body in json format.
+
+    Args:
+        body (Any): Response data to be serialized.
+        status (int, optional): HTTP response code. Defaults to `200`.
+        headers (Dict[str, str], optional): Custom HTTP headers. Defaults to `None`.
+        content_type (str, optional): The content type (string) of the response. Defaults to `"application/json"`.
+        dumps (Callable[..., AnyStr], optional): A custom json dumps function. Defaults to `None`.
+        **kwargs (Any): Remaining arguments that are passed to the json encoder.
+
+    Returns:
+        JSONResponse: A response object with body in json format.
+    """  # noqa: E501
+    return JSONResponse(
+        body,
+        status=status,
+        headers=headers,
+        content_type=content_type,
+        dumps=dumps,
+        **kwargs,
+    )
+
+
+def text(
+    body: str,
+    status: int = 200,
+    headers: dict[str, str] | None = None,
+    content_type: str = "text/plain; charset=utf-8",
+) -> HTTPResponse:
+    """Returns response object with body in text format.
+
+    Args:
+        body (str): Response data.
+        status (int, optional): HTTP response code. Defaults to `200`.
+        headers (Dict[str, str], optional): Custom HTTP headers. Defaults to `None`.
+        content_type (str, optional): The content type (string) of the response. Defaults to `"text/plain; charset=utf-8"`.
+
+    Returns:
+        HTTPResponse: A response object with body in text format.
+
+    Raises:
+        TypeError: If the body is not a string.
+    """  # noqa: E501
+    if not isinstance(body, str):
+        raise TypeError(
+            f"Bad body type. Expected str, got {type(body).__name__})"
+        )
+
+    return HTTPResponse(
+        body, status=status, headers=headers, content_type=content_type
+    )
+
+
+def raw(
+    body: AnyStr | None,
+    status: int = 200,
+    headers: dict[str, str] | None = None,
+    content_type: str = DEFAULT_HTTP_CONTENT_TYPE,
+) -> HTTPResponse:
+    """Returns response object without encoding the body.
+
+    Args:
+        body (Optional[AnyStr]): Response data.
+        status (int, optional): HTTP response code. Defaults to `200`.
+        headers (Dict[str, str], optional): Custom HTTP headers. Defaults to `None`.
+        content_type (str, optional): The content type (string) of the response. Defaults to `"application/octet-stream"`.
+
+    Returns:
+        HTTPResponse: A response object without encoding the body.
+    """  # noqa: E501
+    return HTTPResponse(
+        body=body,
+        status=status,
+        headers=headers,
+        content_type=content_type,
+    )
+
+
+def html(
+    body: str | bytes | HTMLProtocol,
+    status: int = 200,
+    headers: dict[str, str] | None = None,
+) -> HTTPResponse:
+    """Returns response object with body in html format.
+
+    Body should be a `str` or `bytes` like object, or an object with `__html__` or `_repr_html_`.
+
+    Args:
+        body (Union[str, bytes, HTMLProtocol]): Response data.
+        status (int, optional): HTTP response code. Defaults to `200`.
+        headers (Dict[str, str], optional): Custom HTTP headers. Defaults to `None`.
+
+    Returns:
+        HTTPResponse: A response object with body in html format.
+    """  # noqa: E501
+    if not isinstance(body, (str, bytes)):
+        if hasattr(body, "__html__"):
+            body = body.__html__()
+        elif hasattr(body, "_repr_html_"):
+            body = body._repr_html_()
+
+    return HTTPResponse(
+        body,
+        status=status,
+        headers=headers,
+        content_type="text/html; charset=utf-8",
+    )
+
+
+async def validate_file(
+    request_headers: Header, last_modified: datetime | float | int
+) -> HTTPResponse | None:
+    """Validate file based on request headers.
+
+    Args:
+        request_headers (Header): The request headers.
+        last_modified (Union[datetime, float, int]): The last modified date and time of the file.
+
+    Returns:
+        Optional[HTTPResponse]: A response object with status 304 if the file is not modified.
+    """  # noqa: E501
+    try:
+        if_modified_since = request_headers.getone("If-Modified-Since")
+    except KeyError:
+        return None
+    try:
+        if_modified_since = parsedate_to_datetime(if_modified_since)
+    except (TypeError, ValueError):
+        logger.warning(
+            "Ignorning invalid If-Modified-Since header received: '%s'",
+            if_modified_since,
+        )
+        return None
+    if not isinstance(last_modified, datetime):
+        last_modified = datetime.fromtimestamp(
+            float(last_modified), tz=timezone.utc
+        ).replace(microsecond=0)
+
+    if (
+        last_modified.utcoffset() is None
+        and if_modified_since.utcoffset() is not None
+    ):
+        logger.warning(
+            "Cannot compare tz-aware and tz-naive datetimes. To avoid "
+            "this conflict Sanic is converting last_modified to UTC."
+        )
+        last_modified.replace(tzinfo=timezone.utc)
+    elif (
+        last_modified.utcoffset() is not None
+        and if_modified_since.utcoffset() is None
+    ):
+        logger.warning(
+            "Cannot compare tz-aware and tz-naive datetimes. To avoid "
+            "this conflict Sanic is converting if_modified_since to UTC."
+        )
+        if_modified_since.replace(tzinfo=timezone.utc)
+    if last_modified.timestamp() <= if_modified_since.timestamp():
+        return HTTPResponse(status=304)
+
+    return None
+
+
+async def file(
+    location: str | PurePath,
+    status: int = 200,
+    request_headers: Header | None = None,
+    validate_when_requested: bool = True,
+    mime_type: str | None = None,
+    headers: dict[str, str] | None = None,
+    filename: str | None = None,
+    last_modified: datetime | float | int | Default | None = _default,
+    max_age: float | int | None = None,
+    no_store: bool | None = None,
+    _range: Range | None = None,
+) -> HTTPResponse:
+    """Return a response object with file data.
+
+    Args:
+        location (Union[str, PurePath]): Location of file on system.
+        status (int, optional): HTTP response code. Won't enforce the passed in status if only a part of the content will be sent (206) or file is being validated (304). Defaults to 200.
+        request_headers (Optional[Header], optional): The request headers.
+        validate_when_requested (bool, optional): If `True`, will validate the file when requested. Defaults to True.
+        mime_type (Optional[str], optional): Specific mime_type.
+        headers (Optional[Dict[str, str]], optional): Custom Headers.
+        filename (Optional[str], optional): Override filename.
+        last_modified (Optional[Union[datetime, float, int, Default]], optional): The last modified date and time of the file.
+        max_age (Optional[Union[float, int]], optional): Max age for cache control.
+        no_store (Optional[bool], optional): Any cache should not store this response. Defaults to None.
+        _range (Optional[Range], optional):
+
+    Returns:
+        HTTPResponse: The response object with the file data.
+    """  # noqa: E501
+
+    if isinstance(last_modified, datetime):
+        last_modified = last_modified.replace(microsecond=0).timestamp()
+    elif isinstance(last_modified, Default):
+        stat = await stat_async(location)
+        last_modified = stat.st_mtime
+
+    if (
+        validate_when_requested
+        and request_headers is not None
+        and last_modified
+    ):
+        response = await validate_file(request_headers, last_modified)
+        if response:
+            return response
+
+    headers = headers or {}
+    if last_modified:
+        headers.setdefault(
+            "Last-Modified", formatdate(last_modified, usegmt=True)
+        )
+
+    if filename:
+        headers.setdefault(
+            "Content-Disposition", f'attachment; filename="{filename}"'
+        )
+
+    if no_store:
+        cache_control = "no-store"
+    elif max_age:
+        cache_control = f"public, max-age={max_age}"
+        headers.setdefault(
+            "expires",
+            formatdate(
+                time() + max_age,
+                usegmt=True,
+            ),
+        )
+    else:
+        cache_control = "no-cache"
+
+    headers.setdefault("cache-control", cache_control)
+
+    filename = filename or path.split(location)[-1]
+
+    async with await open_async(location, mode="rb") as f:
+        if _range:
+            await f.seek(_range.start)
+            out_stream = await f.read(_range.size)
+            headers["Content-Range"] = (
+                f"bytes {_range.start}-{_range.end}/{_range.total}"
+            )
+            status = 206
+        else:
+            out_stream = await f.read()
+
+    content_type = mime_type or guess_content_type(
+        filename, fallback="text/plain; charset=utf-8"
+    )
+    return HTTPResponse(
+        body=out_stream,
+        status=status,
+        headers=headers,
+        content_type=content_type,
+    )
+
+
+def redirect(
+    to: str,
+    headers: dict[str, str] | None = None,
+    status: int = 302,
+    content_type: str = "text/html; charset=utf-8",
+) -> HTTPResponse:
+    """Cause a HTTP redirect (302 by default) by setting a Location header.
+
+    Args:
+        to (str): path or fully qualified URL to redirect to
+        headers (Optional[Dict[str, str]], optional): optional dict of headers to include in the new request. Defaults to None.
+        status (int, optional): status code (int) of the new request, defaults to 302. Defaults to 302.
+        content_type (str, optional): the content type (string) of the response. Defaults to "text/html; charset=utf-8".
+
+    Returns:
+        HTTPResponse: A response object with the redirect.
+    """  # noqa: E501
+    headers = headers or {}
+
+    # URL Quote the URL before redirecting
+    safe_to = quote_plus(to, safe=":/%#?&=@[]!$&'()*+,;")
+
+    # According to RFC 7231, a relative URI is now permitted.
+    headers["Location"] = safe_to
+
+    return HTTPResponse(
+        status=status, headers=headers, content_type=content_type
+    )
+
+
+async def file_stream(
+    location: str | PurePath,
+    status: int = 200,
+    chunk_size: int = 4096,
+    mime_type: str | None = None,
+    headers: dict[str, str] | None = None,
+    filename: str | None = None,
+    _range: Range | None = None,
+) -> ResponseStream:
+    """Return a streaming response object with file data.
+
+    :param location: Location of file on system.
+    :param chunk_size: The size of each chunk in the stream (in bytes)
+    :param mime_type: Specific mime_type.
+    :param headers: Custom Headers.
+    :param filename: Override filename.
+    :param _range:
+
+    Args:
+        location (Union[str, PurePath]): Location of file on system.
+        status (int, optional): HTTP response code. Won't enforce the passed in status if only a part of the content will be sent (206) or file is being validated (304). Defaults to `200`.
+        chunk_size (int, optional): The size of each chunk in the stream (in bytes). Defaults to `4096`.
+        mime_type (Optional[str], optional): Specific mime_type.
+        headers (Optional[Dict[str, str]], optional): Custom HTTP headers.
+        filename (Optional[str], optional): Override filename.
+        _range (Optional[Range], optional): The range of bytes to send.
+    """  # noqa: E501
+    headers = headers or {}
+    if filename:
+        headers.setdefault(
+            "Content-Disposition", f'attachment; filename="{filename}"'
+        )
+    filename = filename or path.split(location)[-1]
+    mime_type = mime_type or guess_type(filename)[0] or "text/plain"
+    if _range:
+        start = _range.start
+        end = _range.end
+        total = _range.total
+
+        headers["Content-Range"] = f"bytes {start}-{end}/{total}"
+        status = 206
+
+    async def _streaming_fn(response):
+        async with await open_async(location, mode="rb") as f:
+            if _range:
+                await f.seek(_range.start)
+                to_send = _range.size
+                while to_send > 0:
+                    content = await f.read(min((_range.size, chunk_size)))
+                    if len(content) < 1:
+                        break
+                    to_send -= len(content)
+                    await response.write(content)
+            else:
+                while True:
+                    content = await f.read(chunk_size)
+                    if len(content) < 1:
+                        break
+                    await response.write(content)
+
+    return ResponseStream(
+        streaming_fn=_streaming_fn,
+        status=status,
+        headers=headers,
+        content_type=mime_type,
+    )
+
+
+def guess_content_type(
+    file_path: str | PurePath,
+    fallback: str = DEFAULT_HTTP_CONTENT_TYPE,
+) -> str:
+    """Guess the content type (rather than MIME only) by the file extension."""
+    mediatype = guess_type(file_path)[0]
+    if mediatype is None:
+        return fallback
+    if mediatype.startswith("text/"):
+        return f"{mediatype}; charset=utf-8"
+    return mediatype
diff --git a/.venv/lib/python3.12/site-packages/sanic/response/types.py b/.venv/lib/python3.12/site-packages/sanic/response/types.py
new file mode 100644
index 0000000..e9b6fd5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/response/types.py
@@ -0,0 +1,553 @@
+from __future__ import annotations
+
+from collections.abc import Coroutine, Iterator
+from datetime import datetime
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    AnyStr,
+    Callable,
+    TypeVar,
+)
+
+from sanic.compat import Header
+from sanic.cookies import CookieJar
+from sanic.cookies.response import Cookie, SameSite
+from sanic.exceptions import SanicException, ServerError
+from sanic.helpers import Default, _default, has_message_body, json_dumps
+from sanic.http import Http
+
+
+if TYPE_CHECKING:
+    from sanic.asgi import ASGIApp
+    from sanic.http.http3 import HTTPReceiver
+    from sanic.request import Request
+else:
+    Request = TypeVar("Request")
+
+
+class BaseHTTPResponse:
+    """The base class for all HTTP Responses"""
+
+    __slots__ = (
+        "asgi",
+        "body",
+        "content_type",
+        "stream",
+        "status",
+        "headers",
+        "_cookies",
+    )
+
+    _dumps = json_dumps
+
+    def __init__(self):
+        self.asgi: bool = False
+        self.body: bytes | None = None
+        self.content_type: str | None = None
+        self.stream: Http | ASGIApp | HTTPReceiver | None = None
+        self.status: int = None
+        self.headers = Header({})
+        self._cookies: CookieJar | None = None
+
+    def __repr__(self):
+        class_name = self.__class__.__name__
+        return f"<{class_name}: {self.status} {self.content_type}>"
+
+    def _encode_body(self, data: str | bytes | None):
+        if data is None:
+            return b""
+        return data.encode() if hasattr(data, "encode") else data  # type: ignore
+
+    @property
+    def cookies(self) -> CookieJar:
+        """The response cookies.
+
+        See [Cookies](/en/guide/basics/cookies.html)
+
+        Returns:
+            CookieJar: The response cookies
+        """
+        if self._cookies is None:
+            self._cookies = CookieJar(self.headers)
+        return self._cookies
+
+    @property
+    def processed_headers(self) -> Iterator[tuple[bytes, bytes]]:
+        """Obtain a list of header tuples encoded in bytes for sending.
+
+        Add and remove headers based on status and content_type.
+
+        Returns:
+            Iterator[Tuple[bytes, bytes]]: A list of header tuples encoded in bytes for sending
+        """  # noqa: E501
+        if has_message_body(self.status):
+            self.headers.setdefault("content-type", self.content_type)
+        # Encode headers into bytes
+        return (
+            (name.encode("ascii"), f"{value}".encode(errors="surrogateescape"))
+            for name, value in self.headers.items()
+        )
+
+    async def send(
+        self,
+        data: AnyStr | None = None,
+        end_stream: bool | None = None,
+    ) -> None:
+        """Send any pending response headers and the given data as body.
+
+        Args:
+            data (Optional[AnyStr], optional): str or bytes to be written. Defaults to `None`.
+            end_stream (Optional[bool], optional): whether to close the stream after this block. Defaults to `None`.
+        """  # noqa: E501
+        if data is None and end_stream is None:
+            end_stream = True
+        if self.stream is None:
+            raise SanicException(
+                "No stream is connected to the response object instance."
+            )
+        if self.stream.send is None:
+            if end_stream and not data:
+                return
+            raise ServerError(
+                "Response stream was ended, no more response data is "
+                "allowed to be sent."
+            )
+        data = data.encode() if hasattr(data, "encode") else data or b""  # type: ignore
+        await self.stream.send(
+            data,  # type: ignore
+            end_stream=end_stream or False,
+        )
+
+    def add_cookie(
+        self,
+        key: str,
+        value: str,
+        *,
+        path: str = "/",
+        domain: str | None = None,
+        secure: bool = True,
+        max_age: int | None = None,
+        expires: datetime | None = None,
+        httponly: bool = False,
+        samesite: SameSite | None = "Lax",
+        partitioned: bool = False,
+        comment: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> Cookie:
+        """Add a cookie to the CookieJar
+
+        See [Cookies](/en/guide/basics/cookies.html)
+
+        Args:
+            key (str): The key to be added
+            value (str): The value to be added
+            path (str, optional): Path of the cookie. Defaults to `"/"`.
+            domain (Optional[str], optional): Domain of the cookie. Defaults to `None`.
+            secure (bool, optional): Whether the cookie is secure. Defaults to `True`.
+            max_age (Optional[int], optional): Max age of the cookie. Defaults to `None`.
+            expires (Optional[datetime], optional): Expiry date of the cookie. Defaults to `None`.
+            httponly (bool, optional): Whether the cookie is http only. Defaults to `False`.
+            samesite (Optional[SameSite], optional): SameSite policy of the cookie. Defaults to `"Lax"`.
+            partitioned (bool, optional): Whether the cookie is partitioned. Defaults to `False`.
+            comment (Optional[str], optional): Comment of the cookie. Defaults to `None`.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key. This requires that path="/", domain=None, and secure=True. Defaults to `False`.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key. This requires that secure=True. Defaults to `False`.
+
+        Returns:
+            Cookie: The cookie that was added
+        """  # noqa: E501
+        return self.cookies.add_cookie(
+            key=key,
+            value=value,
+            path=path,
+            domain=domain,
+            secure=secure,
+            max_age=max_age,
+            expires=expires,
+            httponly=httponly,
+            samesite=samesite,
+            partitioned=partitioned,
+            comment=comment,
+            host_prefix=host_prefix,
+            secure_prefix=secure_prefix,
+        )
+
+    def delete_cookie(
+        self,
+        key: str,
+        *,
+        path: str = "/",
+        domain: str | None = None,
+        host_prefix: bool = False,
+        secure_prefix: bool = False,
+    ) -> None:
+        """Delete a cookie
+
+        This will effectively set it as Max-Age: 0, which a browser should
+        interpret it to mean: "delete the cookie".
+
+        Since it is a browser/client implementation, your results may vary
+        depending upon which client is being used.
+
+        See [Cookies](/en/guide/basics/cookies.html)
+
+        Args:
+            key (str): The key to be deleted
+            path (str, optional): Path of the cookie. Defaults to `"/"`.
+            domain (Optional[str], optional): Domain of the cookie. Defaults to `None`.
+            host_prefix (bool, optional): Whether to add __Host- as a prefix to the key. This requires that path="/", domain=None, and secure=True. Defaults to `False`.
+            secure_prefix (bool, optional): Whether to add __Secure- as a prefix to the key. This requires that secure=True. Defaults to `False`.
+        """  # noqa: E501
+        self.cookies.delete_cookie(
+            key=key,
+            path=path,
+            domain=domain,
+            host_prefix=host_prefix,
+            secure_prefix=secure_prefix,
+        )
+
+
+class HTTPResponse(BaseHTTPResponse):
+    """HTTP response to be sent back to the client.
+
+    Args:
+        body (Optional[Any], optional): The body content to be returned. Defaults to `None`.
+        status (int, optional): HTTP response number. Defaults to `200`.
+        headers (Optional[Union[Header, Dict[str, str]]], optional): Headers to be returned. Defaults to `None`.
+        content_type (Optional[str], optional): Content type to be returned (as a header). Defaults to `None`.
+    """  # noqa: E501
+
+    __slots__ = ()
+
+    def __init__(
+        self,
+        body: Any = None,
+        status: int = 200,
+        headers: Header | dict[str, str] | None = None,
+        content_type: str | None = None,
+    ):
+        super().__init__()
+
+        self.content_type: str | None = content_type
+        self.body = self._encode_body(body)
+        self.status = status
+        self.headers = Header(headers or {})
+        self._cookies = None
+
+    async def eof(self):
+        """Send a EOF (End of File) message to the client."""
+        await self.send("", True)
+
+    async def __aenter__(self):
+        return self.send
+
+    async def __aexit__(self, *_):
+        await self.eof()
+
+
+class JSONResponse(HTTPResponse):
+    """Convenience class for JSON responses
+
+    HTTP response to be sent back to the client, when the response
+    is of json type. Offers several utilities to manipulate common
+    json data types.
+
+    Args:
+        body (Optional[Any], optional): The body content to be returned. Defaults to `None`.
+        status (int, optional): HTTP response number. Defaults to `200`.
+        headers (Optional[Union[Header, Dict[str, str]]], optional): Headers to be returned. Defaults to `None`.
+        content_type (str, optional): Content type to be returned (as a header). Defaults to `"application/json"`.
+        dumps (Optional[Callable[..., AnyStr]], optional): The function to use for json encoding. Defaults to `None`.
+        **kwargs (Any, optional): The kwargs to pass to the json encoding function. Defaults to `{}`.
+    """  # noqa: E501
+
+    __slots__ = (
+        "_body",
+        "_body_manually_set",
+        "_initialized",
+        "_raw_body",
+        "_use_dumps",
+        "_use_dumps_kwargs",
+    )
+
+    def __init__(
+        self,
+        body: Any = None,
+        status: int = 200,
+        headers: Header | dict[str, str] | None = None,
+        content_type: str = "application/json",
+        dumps: Callable[..., AnyStr] | None = None,
+        **kwargs: Any,
+    ):
+        self._initialized = False
+        self._body_manually_set = False
+
+        self._use_dumps: Callable[..., str | bytes] = (
+            dumps or BaseHTTPResponse._dumps
+        )
+        self._use_dumps_kwargs = kwargs
+
+        self._raw_body = body
+
+        super().__init__(
+            self._encode_body(self._use_dumps(body, **self._use_dumps_kwargs)),
+            headers=headers,
+            status=status,
+            content_type=content_type,
+        )
+
+        self._initialized = True
+
+    def _check_body_not_manually_set(self):
+        if self._body_manually_set:
+            raise SanicException(
+                "Cannot use raw_body after body has been manually set."
+            )
+
+    @property
+    def raw_body(self) -> Any:
+        """Returns the raw body, as long as body has not been manually set previously.
+
+        NOTE: This object should not be mutated, as it will not be
+        reflected in the response body. If you need to mutate the
+        response body, consider using one of the provided methods in
+        this class or alternatively call set_body() with the mutated
+        object afterwards or set the raw_body property to it.
+
+        Returns:
+            Optional[Any]: The raw body
+        """  # noqa: E501
+        self._check_body_not_manually_set()
+        return self._raw_body
+
+    @raw_body.setter
+    def raw_body(self, value: Any):
+        self._body_manually_set = False
+        self._body = self._encode_body(
+            self._use_dumps(value, **self._use_dumps_kwargs)
+        )
+        self._raw_body = value
+
+    @property  # type: ignore
+    def body(self) -> bytes | None:  # type: ignore
+        """Returns the response body.
+
+        Returns:
+            Optional[bytes]: The response body
+        """
+        return self._body
+
+    @body.setter
+    def body(self, value: bytes | None):
+        self._body = value
+        if not self._initialized:
+            return
+        self._body_manually_set = True
+
+    def set_body(
+        self,
+        body: Any,
+        dumps: Callable[..., AnyStr] | None = None,
+        **dumps_kwargs: Any,
+    ) -> None:
+        """Set the response body to the given value, using the given dumps function
+
+        Sets a new response body using the given dumps function
+        and kwargs, or falling back to the defaults given when
+        creating the object if none are specified.
+
+        Args:
+            body (Any): The body to set
+            dumps (Optional[Callable[..., AnyStr]], optional): The function to use for json encoding. Defaults to `None`.
+            **dumps_kwargs (Any, optional): The kwargs to pass to the json encoding function. Defaults to `{}`.
+
+        Examples:
+            ```python
+            response = JSONResponse({"foo": "bar"})
+            response.set_body({"bar": "baz"})
+            assert response.body == b'{"bar": "baz"}'
+            ```
+        """  # noqa: E501
+        self._body_manually_set = False
+        self._raw_body = body
+
+        use_dumps = dumps or self._use_dumps
+        use_dumps_kwargs = dumps_kwargs if dumps else self._use_dumps_kwargs
+
+        self._body = self._encode_body(use_dumps(body, **use_dumps_kwargs))
+
+    def append(self, value: Any) -> None:
+        """Appends a value to the response raw_body, ensuring that body is kept up to date.
+
+        This can only be used if raw_body is a list.
+
+        Args:
+            value (Any): The value to append
+
+        Raises:
+            SanicException: If the body is not a list
+        """  # noqa: E501
+
+        self._check_body_not_manually_set()
+
+        if not isinstance(self._raw_body, list):
+            raise SanicException("Cannot append to a non-list object.")
+
+        self._raw_body.append(value)
+        self.raw_body = self._raw_body
+
+    def extend(self, value: Any) -> None:
+        """Extends the response's raw_body with the given values, ensuring that body is kept up to date.
+
+        This can only be used if raw_body is a list.
+
+        Args:
+            value (Any): The values to extend with
+
+        Raises:
+            SanicException: If the body is not a list
+        """  # noqa: E501
+
+        self._check_body_not_manually_set()
+
+        if not isinstance(self._raw_body, list):
+            raise SanicException("Cannot extend a non-list object.")
+
+        self._raw_body.extend(value)
+        self.raw_body = self._raw_body
+
+    def update(self, *args, **kwargs) -> None:
+        """Updates the response's raw_body with the given values, ensuring that body is kept up to date.
+
+        This can only be used if raw_body is a dict.
+
+        Args:
+            *args: The args to update with
+            **kwargs: The kwargs to update with
+
+        Raises:
+            SanicException: If the body is not a dict
+        """  # noqa: E501
+
+        self._check_body_not_manually_set()
+
+        if not isinstance(self._raw_body, dict):
+            raise SanicException("Cannot update a non-dict object.")
+
+        self._raw_body.update(*args, **kwargs)
+        self.raw_body = self._raw_body
+
+    def pop(self, key: Any, default: Any = _default) -> Any:
+        """Pops a key from the response's raw_body, ensuring that body is kept up to date.
+
+        This can only be used if raw_body is a dict or a list.
+
+        Args:
+            key (Any): The key to pop
+            default (Any, optional): The default value to return if the key is not found. Defaults to `_default`.
+
+        Raises:
+            SanicException: If the body is not a dict or a list
+            TypeError: If the body is a list and a default value is provided
+
+        Returns:
+            Any: The value that was popped
+        """  # noqa: E501
+
+        self._check_body_not_manually_set()
+
+        if not isinstance(self._raw_body, (list, dict)):
+            raise SanicException(
+                "Cannot pop from a non-list and non-dict object."
+            )
+
+        if isinstance(default, Default):
+            value = self._raw_body.pop(key)
+        elif isinstance(self._raw_body, list):
+            raise TypeError("pop doesn't accept a default argument for lists")
+        else:
+            value = self._raw_body.pop(key, default)
+
+        self.raw_body = self._raw_body
+
+        return value
+
+
+class ResponseStream:
+    """A compat layer to bridge the gap after the deprecation of StreamingHTTPResponse.
+
+    It will be removed when:
+    - file_stream is moved to new style streaming
+    - file and file_stream are combined into a single API
+    """  # noqa: E501
+
+    __slots__ = (
+        "_cookies",
+        "content_type",
+        "headers",
+        "request",
+        "response",
+        "status",
+        "streaming_fn",
+    )
+
+    def __init__(
+        self,
+        streaming_fn: Callable[
+            [BaseHTTPResponse | ResponseStream],
+            Coroutine[Any, Any, None],
+        ],
+        status: int = 200,
+        headers: Header | dict[str, str] | None = None,
+        content_type: str | None = None,
+    ):
+        if headers is None:
+            headers = Header()
+        elif not isinstance(headers, Header):
+            headers = Header(headers)
+        self.streaming_fn = streaming_fn
+        self.status = status
+        self.headers = headers or Header()
+        self.content_type = content_type
+        self.request: Request | None = None
+        self._cookies: CookieJar | None = None
+
+    async def write(self, message: str):
+        await self.response.send(message)
+
+    async def stream(self) -> HTTPResponse:
+        if not self.request:
+            raise ServerError("Attempted response to unknown request")
+        self.response = await self.request.respond(
+            headers=self.headers,
+            status=self.status,
+            content_type=self.content_type,
+        )
+        await self.streaming_fn(self)
+        return self.response
+
+    async def eof(self) -> None:
+        await self.response.eof()
+
+    @property
+    def cookies(self) -> CookieJar:
+        if self._cookies is None:
+            self._cookies = CookieJar(self.headers)
+        return self._cookies
+
+    @property
+    def processed_headers(self):
+        return self.response.processed_headers
+
+    @property
+    def body(self):
+        return self.response.body
+
+    def __call__(self, request: Request) -> ResponseStream:
+        self.request = request
+        return self
+
+    def __await__(self):
+        return self.stream().__await__()
diff --git a/.venv/lib/python3.12/site-packages/sanic/router.py b/.venv/lib/python3.12/site-packages/sanic/router.py
new file mode 100644
index 0000000..b674d12
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/router.py
@@ -0,0 +1,273 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from functools import lru_cache
+from inspect import signature
+from typing import Any
+from uuid import UUID
+
+from sanic_routing import BaseRouter
+from sanic_routing.exceptions import NoMethod
+from sanic_routing.exceptions import NotFound as RoutingNotFound
+from sanic_routing.group import RouteGroup
+from sanic_routing.route import Route
+
+from sanic.constants import HTTP_METHODS
+from sanic.errorpages import check_error_format
+from sanic.exceptions import MethodNotAllowed, NotFound, SanicException
+from sanic.models.handler_types import RouteHandler
+
+
+ROUTER_CACHE_SIZE = 1024
+ALLOWED_LABELS = ("__file_uri__",)
+
+
+class Router(BaseRouter):
+    """The router implementation responsible for routing a `Request` object to the appropriate handler."""  # noqa: E501
+
+    DEFAULT_METHOD = "GET"
+    ALLOWED_METHODS = HTTP_METHODS
+
+    def _get(
+        self, path: str, method: str, host: str | None
+    ) -> tuple[Route, RouteHandler, dict[str, Any]]:
+        try:
+            return self.resolve(
+                path=path,
+                method=method,
+                extra={"host": host} if host else None,
+            )
+        except RoutingNotFound as e:
+            raise NotFound(f"Requested URL {e.path} not found") from None
+        except NoMethod as e:
+            raise MethodNotAllowed(
+                f"Method {method} not allowed for URL {path}",
+                method=method,
+                allowed_methods=tuple(e.allowed_methods)
+                if e.allowed_methods
+                else None,
+            ) from None
+
+    @lru_cache(maxsize=ROUTER_CACHE_SIZE)
+    def get(  # type: ignore
+        self, path: str, method: str, host: str | None
+    ) -> tuple[Route, RouteHandler, dict[str, Any]]:
+        """Retrieve a `Route` object containing the details about how to handle a response for a given request
+
+        :param request: the incoming request object
+        :type request: Request
+        :return: details needed for handling the request and returning the
+            correct response
+        :rtype: Tuple[ Route, RouteHandler, Dict[str, Any]]
+
+        Args:
+            path (str): the path of the route
+            method (str): the HTTP method of the route
+            host (Optional[str]): the host of the route
+
+        Raises:
+            NotFound: if the route is not found
+            MethodNotAllowed: if the method is not allowed for the route
+
+        Returns:
+            Tuple[Route, RouteHandler, Dict[str, Any]]: the route, handler, and match info
+        """  # noqa: E501
+        __tracebackhide__ = True
+        return self._get(path, method, host)
+
+    def add(  # type: ignore
+        self,
+        uri: str,
+        methods: Iterable[str],
+        handler: RouteHandler,
+        host: str | Iterable[str] | None = None,
+        strict_slashes: bool = False,
+        stream: bool = False,
+        ignore_body: bool = False,
+        version: str | float | int | None = None,
+        name: str | None = None,
+        unquote: bool = False,
+        static: bool = False,
+        version_prefix: str = "/v",
+        overwrite: bool = False,
+        error_format: str | None = None,
+    ) -> Route | list[Route]:
+        """Add a handler to the router
+
+        Args:
+            uri (str): The path of the route.
+            methods (Iterable[str]): The types of HTTP methods that should be attached,
+                example: ["GET", "POST", "OPTIONS"].
+            handler (RouteHandler): The sync or async function to be executed.
+            host (Optional[str], optional): Host that the route should be on. Defaults to None.
+            strict_slashes (bool, optional): Whether to apply strict slashes. Defaults to False.
+            stream (bool, optional): Whether to stream the response. Defaults to False.
+            ignore_body (bool, optional): Whether the incoming request body should be read.
+                Defaults to False.
+            version (Union[str, float, int], optional): A version modifier for the uri. Defaults to None.
+            name (Optional[str], optional): An identifying name of the route. Defaults to None.
+
+        Returns:
+            Route: The route object.
+        """  # noqa: E501
+
+        if version is not None:
+            version = str(version).strip("/").lstrip("v")
+            uri = "/".join([f"{version_prefix}{version}", uri.lstrip("/")])
+
+        uri = self._normalize(uri, handler)
+
+        params = dict(
+            path=uri,
+            handler=handler,
+            methods=frozenset(map(str, methods)) if methods else None,
+            name=name,
+            strict=strict_slashes,
+            unquote=unquote,
+            overwrite=overwrite,
+        )
+
+        if isinstance(host, str):
+            hosts = [host]
+        else:
+            hosts = host or [None]  # type: ignore
+
+        routes = []
+
+        for host in hosts:
+            if host:
+                params.update({"requirements": {"host": host}})
+
+            ident = name
+            if len(hosts) > 1:
+                ident = (
+                    f"{name}_{host.replace('.', '_')}"
+                    if name
+                    else "__unnamed__"
+                )
+
+            route = super().add(**params)  # type: ignore
+            route.extra.ident = ident
+            route.extra.ignore_body = ignore_body
+            route.extra.stream = stream
+            route.extra.hosts = hosts
+            route.extra.static = static
+            route.extra.error_format = error_format
+
+            if error_format:
+                check_error_format(route.extra.error_format)
+
+            routes.append(route)
+
+        if len(routes) == 1:
+            return routes[0]
+        return routes
+
+    @lru_cache(maxsize=ROUTER_CACHE_SIZE)
+    def find_route_by_view_name(
+        self, view_name: str, name: str | None = None
+    ) -> Route | None:
+        """Find a route in the router based on the specified view name.
+
+        Args:
+            view_name (str): the name of the view to search for
+            name (Optional[str], optional): the name of the route. Defaults to `None`.
+
+        Returns:
+            Optional[Route]: the route object
+        """  # noqa: E501
+        if not view_name:
+            return None
+
+        route = self.name_index.get(view_name)
+        if not route:
+            full_name = self.ctx.app.generate_name(view_name)
+            route = self.name_index.get(full_name)
+
+        if not route:
+            return None
+
+        return route
+
+    @property
+    def routes_all(self) -> dict[tuple[str, ...], Route]:
+        """Return all routes in the router.
+
+        Returns:
+            Dict[Tuple[str, ...], Route]: a dictionary of routes
+        """
+        return {route.parts: route for route in self.routes}
+
+    @property
+    def routes_static(self) -> dict[tuple[str, ...], RouteGroup]:
+        """Return all static routes in the router.
+
+        _In this context "static" routes do not refer to the `app.static()`
+        method. Instead, they refer to routes that do not contain
+        any path parameters._
+
+        Returns:
+            Dict[Tuple[str, ...], Route]: a dictionary of routes
+        """
+        return self.static_routes
+
+    @property
+    def routes_dynamic(self) -> dict[tuple[str, ...], RouteGroup]:
+        """Return all dynamic routes in the router.
+
+        _Dynamic routes are routes that contain path parameters._
+
+        Returns:
+            Dict[Tuple[str, ...], Route]: a dictionary of routes
+        """
+        return self.dynamic_routes
+
+    @property
+    def routes_regex(self) -> dict[tuple[str, ...], RouteGroup]:
+        """Return all regex routes in the router.
+
+        _Regex routes are routes that contain path parameters with regex
+        expressions, or otherwise need regex to resolve._
+
+        Returns:
+            Dict[Tuple[str, ...], Route]: a dictionary of routes
+        """
+        return self.regex_routes
+
+    def finalize(self, *args, **kwargs) -> None:
+        """Finalize the router.
+
+        Raises:
+            SanicException: if a route contains a parameter name that starts with "__" and is not in ALLOWED_LABELS
+        """  # noqa: E501
+        super().finalize(*args, **kwargs)
+
+        for route in self.dynamic_routes.values():
+            if any(
+                label.startswith("__") and label not in ALLOWED_LABELS
+                for label in route.labels
+            ):
+                raise SanicException(
+                    f"Invalid route: {route}. Parameter names cannot use '__'."
+                )
+
+    def _normalize(self, uri: str, handler: RouteHandler) -> str:
+        if "<" not in uri:
+            return uri
+
+        sig = signature(handler)
+        mapping = {
+            param.name: param.annotation.__name__.lower()
+            for param in sig.parameters.values()
+            if param.annotation in (str, int, float, UUID)
+        }
+
+        reconstruction = []
+        for part in uri.split("/"):
+            if part.startswith("<") and ":" not in part:
+                name = part[1:-1]
+                annotation = mapping.get(name)
+                if annotation:
+                    part = f"<{name}:{annotation}>"
+            reconstruction.append(part)
+        return "/".join(reconstruction)
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/__init__.py b/.venv/lib/python3.12/site-packages/sanic/server/__init__.py
new file mode 100644
index 0000000..a6b1a98
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/__init__.py
@@ -0,0 +1,15 @@
+from sanic.models.server_types import ConnInfo, Signal
+from sanic.server.async_server import AsyncioServer
+from sanic.server.loop import try_use_uvloop
+from sanic.server.protocols.http_protocol import HttpProtocol
+from sanic.server.runners import serve
+
+
+__all__ = (
+    "AsyncioServer",
+    "ConnInfo",
+    "HttpProtocol",
+    "Signal",
+    "serve",
+    "try_use_uvloop",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/async_server.py b/.venv/lib/python3.12/site-packages/sanic/server/async_server.py
new file mode 100644
index 0000000..d9dd5e6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/async_server.py
@@ -0,0 +1,112 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from sanic.exceptions import SanicException
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+class AsyncioServer:
+    """Wraps an asyncio server with functionality that might be useful to a user who needs to manage the server lifecycle manually."""  # noqa: E501
+
+    __slots__ = ("app", "connections", "loop", "serve_coro", "server")
+
+    def __init__(
+        self,
+        app: Sanic,
+        loop,
+        serve_coro,
+        connections,
+    ):
+        # Note, Sanic already called "before_server_start" events
+        # before this helper was even created. So we don't need it here.
+        self.app = app
+        self.connections = connections
+        self.loop = loop
+        self.serve_coro = serve_coro
+        self.server = None
+
+    def startup(self):
+        """Trigger "startup" operations on the app"""
+        return self.app._startup()
+
+    def before_start(self):
+        """Trigger "before_server_start" events"""
+        return self._server_event("init", "before")
+
+    def after_start(self):
+        """Trigger "after_server_start" events"""
+        return self._server_event("init", "after")
+
+    def before_stop(self):
+        """Trigger "before_server_stop" events"""
+        return self._server_event("shutdown", "before")
+
+    def after_stop(self):
+        """Trigger "after_server_stop" events"""
+        return self._server_event("shutdown", "after")
+
+    def is_serving(self) -> bool:
+        """Returns True if the server is running, False otherwise"""
+        if self.server:
+            return self.server.is_serving()
+        return False
+
+    def wait_closed(self):
+        """Wait until the server is closed"""
+        if self.server:
+            return self.server.wait_closed()
+
+    def close(self):
+        """Close the server"""
+        if self.server:
+            self.server.close()
+            coro = self.wait_closed()
+            task = self.loop.create_task(coro)
+            return task
+
+    def start_serving(self):
+        """Start serving requests"""
+        return self._serve(self.server.start_serving)
+
+    def serve_forever(self):
+        """Serve requests until the server is stopped"""
+        return self._serve(self.server.serve_forever)
+
+    def _serve(self, serve_func):
+        if self.server:
+            if not self.app.state.is_started:
+                raise SanicException(
+                    "Cannot run Sanic server without first running "
+                    "await server.startup()"
+                )
+
+            try:
+                return serve_func()
+            except AttributeError:
+                name = serve_func.__name__
+                raise NotImplementedError(
+                    f"server.{name} not available in this version "
+                    "of asyncio or uvloop."
+                )
+
+    def _server_event(self, concern: str, action: str):
+        if not self.app.state.is_started:
+            raise SanicException(
+                "Cannot dispatch server event without "
+                "first running await server.startup()"
+            )
+        return self.app._server_event(concern, action, loop=self.loop)
+
+    def __await__(self):
+        """
+        Starts the asyncio server, returns AsyncServerCoro
+        """
+        task = self.loop.create_task(self.serve_coro)
+        while not task.done():
+            yield
+        self.server = task.result()
+        return self
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/events.py b/.venv/lib/python3.12/site-packages/sanic/server/events.py
new file mode 100644
index 0000000..9dc8bfb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/events.py
@@ -0,0 +1,36 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from inspect import isawaitable
+from typing import TYPE_CHECKING, Any, Callable
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+def trigger_events(
+    events: Iterable[Callable[..., Any]] | None,
+    loop,
+    app: Sanic | None = None,
+    **kwargs,
+):
+    """Trigger event callbacks (functions or async)
+
+    Args:
+        events (Optional[Iterable[Callable[..., Any]]]): [description]
+        loop ([type]): [description]
+        app (Optional[Sanic], optional): [description]. Defaults to None.
+    """
+    if events:
+        for event in events:
+            try:
+                result = event(**kwargs) if not app else event(app, **kwargs)
+            except TypeError:
+                result = (
+                    event(loop, **kwargs)
+                    if not app
+                    else event(app, loop, **kwargs)
+                )
+            if isawaitable(result):
+                loop.run_until_complete(result)
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/goodbye.py b/.venv/lib/python3.12/site-packages/sanic/server/goodbye.py
new file mode 100644
index 0000000..efef8ad
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/goodbye.py
@@ -0,0 +1,31 @@
+# flake8: noqa: E501
+
+import random
+import sys
+
+
+# fmt: off
+ascii_phrases = {
+    'Farewell', 'Bye', 'See you later', 'Take care', 'So long', 'Adieu', 'Cheerio',
+    'Goodbye', 'Adios', 'Au revoir', 'Arrivederci', 'Sayonara', 'Auf Wiedersehen',
+    'Do svidaniya', 'Annyeong', 'Tot ziens', 'Ha det', 'Selamat tinggal',
+    'Hasta luego', 'Nos vemos', 'Salut', 'Ciao', 'A presto',
+    'Dag', 'Tot later', 'Vi ses', 'Sampai jumpa',
+}
+
+non_ascii_phrases = {
+    'Tschüss', 'Zài jiàn', 'Bāi bāi', 'Míngtiān jiàn', 'Adeus', 'Tchau', 'Até logo',
+    'Hejdå', 'À bientôt', 'Bis später', 'Adjø',
+    'じゃね', 'またね', '안녕히 계세요', '잘 가', 'שלום',
+    'להתראות', 'مع السلامة', 'إلى اللقاء', 'وداعاً', 'अलविदा',
+    'फिर मिलेंगे',
+}
+
+all_phrases = ascii_phrases | non_ascii_phrases
+# fmt: on
+
+
+def get_goodbye() -> str:  # pragma: no cover
+    is_utf8 = sys.stdout.encoding.lower() == "utf-8"
+    phrases = all_phrases if is_utf8 else ascii_phrases
+    return random.choice(list(phrases))  # nosec: B311
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/loop.py b/.venv/lib/python3.12/site-packages/sanic/server/loop.py
new file mode 100644
index 0000000..ead4e3a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/loop.py
@@ -0,0 +1,64 @@
+import asyncio
+
+from os import getenv
+
+from sanic.compat import OS_IS_WINDOWS
+from sanic.log import error_logger
+from sanic.utils import str_to_bool
+
+
+def try_use_uvloop() -> None:
+    """Use uvloop instead of the default asyncio loop."""
+    if OS_IS_WINDOWS:
+        error_logger.warning(
+            "You are trying to use uvloop, but uvloop is not compatible "
+            "with your system. You can disable uvloop completely by setting "
+            "the 'USE_UVLOOP' configuration value to false, or simply not "
+            "defining it and letting Sanic handle it for you. Sanic will now "
+            "continue to run using the default event loop."
+        )
+        return
+
+    try:
+        import uvloop  # type: ignore
+    except ImportError:
+        error_logger.warning(
+            "You are trying to use uvloop, but uvloop is not "
+            "installed in your system. In order to use uvloop "
+            "you must first install it. Otherwise, you can disable "
+            "uvloop completely by setting the 'USE_UVLOOP' "
+            "configuration value to false. Sanic will now continue "
+            "to run with the default event loop."
+        )
+        return
+
+    uvloop_install_removed = str_to_bool(getenv("SANIC_NO_UVLOOP", "no"))
+    if uvloop_install_removed:
+        error_logger.info(
+            "You are requesting to run Sanic using uvloop, but the "
+            "install-time 'SANIC_NO_UVLOOP' environment variable (used to "
+            "opt-out of installing uvloop with Sanic) is set to true. If "
+            "you want to prevent Sanic from overriding the event loop policy "
+            "during runtime, set the 'USE_UVLOOP' configuration value to "
+            "false."
+        )
+
+    if not isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy):
+        asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
+
+
+def try_windows_loop():
+    """Try to use the WindowsSelectorEventLoopPolicy instead of the default"""
+    if not OS_IS_WINDOWS:
+        error_logger.warning(
+            "You are trying to use an event loop policy that is not "
+            "compatible with your system. You can simply let Sanic handle "
+            "selecting the best loop for you. Sanic will now continue to run "
+            "using the default event loop."
+        )
+        return
+
+    if not isinstance(
+        asyncio.get_event_loop_policy(), asyncio.WindowsSelectorEventLoopPolicy
+    ):
+        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/protocols/__init__.py b/.venv/lib/python3.12/site-packages/sanic/server/protocols/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/protocols/base_protocol.py b/.venv/lib/python3.12/site-packages/sanic/server/protocols/base_protocol.py
new file mode 100644
index 0000000..cf9a359
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/protocols/base_protocol.py
@@ -0,0 +1,260 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from sanic.exceptions import RequestCancelled
+
+
+if TYPE_CHECKING:
+    from sanic.app import Sanic
+
+import asyncio
+
+from asyncio.transports import Transport
+from time import monotonic as current_time
+
+from sanic.log import error_logger
+from sanic.models.server_types import ConnInfo, Signal
+
+
+class SanicProtocol(asyncio.Protocol):
+    __slots__ = (
+        "app",
+        # event loop, connection
+        "loop",
+        "transport",
+        "connections",
+        "conn_info",
+        "signal",
+        "_can_write",
+        "_time",
+        "_task",
+        "_unix",
+        "_data_received",
+    )
+
+    def __init__(
+        self,
+        *,
+        loop,
+        app: Sanic,
+        signal=None,
+        connections=None,
+        unix=None,
+        **kwargs,
+    ):
+        asyncio.set_event_loop(loop)
+        self.loop = loop
+        self.app: Sanic = app
+        self.signal = signal or Signal()
+        self.transport: Transport | None = None
+        self.connections = connections if connections is not None else set()
+        self.conn_info: ConnInfo | None = None
+        self._can_write = asyncio.Event()
+        self._can_write.set()
+        self._unix = unix
+        self._time = 0.0  # type: float
+        self._task: asyncio.Task | None = None
+        self._data_received = asyncio.Event()
+
+    @property
+    def ctx(self):
+        if self.conn_info is not None:
+            return self.conn_info.ctx
+        else:
+            return None
+
+    async def send(self, data):
+        """
+        Generic data write implementation with backpressure control.
+        """
+        await self._can_write.wait()
+        if self.transport.is_closing():
+            raise RequestCancelled
+        self.transport.write(data)
+        self._time = current_time()
+
+    async def receive_more(self):
+        """
+        Wait until more data is received into the Server protocol's buffer
+        """
+        self.transport.resume_reading()
+        self._data_received.clear()
+        await self._data_received.wait()
+
+    def close(self, timeout: float | None = None):
+        """
+        Attempt close the connection.
+        """
+        if self.transport is None or self.transport.is_closing():
+            # do not attempt to close again, already aborted or closing
+            return
+
+        # Check if write is already paused _before_ close() is called.
+        write_was_paused = not self._can_write.is_set()
+        # Trigger the UVLoop Stream Transport Close routine
+        # Causes a call to connection_lost where further cleanup occurs
+        # Close may fully close the connection now, but if there is still
+        # data in the libuv buffer, then close becomes an async operation
+        self.transport.close()
+        try:
+            # Check write-buffer data left _after_ close is called.
+            # in UVLoop, get the data in the libuv transport write-buffer
+            data_left = self.transport.get_write_buffer_size()
+        # Some asyncio implementations don't support get_write_buffer_size
+        except (AttributeError, NotImplementedError):
+            data_left = 0
+        if write_was_paused or data_left > 0:
+            # don't call resume_writing here, it gets called by the transport
+            # to unpause the protocol when it is ready for more data
+
+            # Schedule the async close checker, to close the connection
+            # after the transport is done, and clean everything up.
+            if timeout is None:
+                # This close timeout needs to be less than the graceful
+                # shutdown timeout. The graceful shutdown _could_ be waiting
+                # for this transport to close before shutting down the app.
+                timeout = self.app.config.GRACEFUL_TCP_CLOSE_TIMEOUT
+                # This is 5s by default.
+        else:
+            # Schedule the async close checker but with no timeout,
+            # this will ensure abort() is called if required.
+            if timeout is None:
+                timeout = 0
+        self.loop.call_soon(
+            _async_protocol_transport_close,
+            self,
+            self.loop,
+            timeout,
+        )
+
+    def abort(self):
+        """
+        Force close the connection.
+        """
+        # Cause a call to connection_lost where further cleanup occurs
+        if self.transport:
+            self.transport.abort()
+            self.transport = None
+
+    # asyncio.Protocol API Callbacks #
+    # ------------------------------ #
+    def connection_made(self, transport):
+        """
+        Generic connection-made, with no connection_task, and no recv_buffer.
+        Override this for protocol-specific connection implementations.
+        """
+        try:
+            transport.set_write_buffer_limits(low=16384, high=65536)
+            self.connections.add(self)
+            self.transport = transport
+            self.conn_info = ConnInfo(self.transport, unix=self._unix)
+        except Exception:
+            error_logger.exception("protocol.connect_made")
+
+    def connection_lost(self, exc):
+        """
+        This is a callback handler that is called from the asyncio
+        transport layer implementation (eg, UVLoop's UVStreamTransport).
+        It is scheduled to be called async after the transport has closed.
+        When data is still in the send buffer, this call to connection_lost
+        will be delayed until _after_ the buffer is finished being sent.
+
+        So we can use this callback as a confirmation callback
+        that the async write-buffer transfer is finished.
+        """
+        try:
+            self.connections.discard(self)
+            # unblock the send queue if it is paused,
+            # this allows the route handler to see
+            # the CancelledError exception
+            self.resume_writing()
+            self.conn_info.lost = True
+            if self._task:
+                self._task.cancel()
+        except BaseException:
+            error_logger.exception("protocol.connection_lost")
+
+    def pause_writing(self):
+        self._can_write.clear()
+
+    def resume_writing(self):
+        self._can_write.set()
+
+    def data_received(self, data: bytes):
+        try:
+            self._time = current_time()
+            if not data:
+                return self.close()
+
+            if self._data_received:
+                self._data_received.set()
+        except BaseException:
+            error_logger.exception("protocol.data_received")
+
+
+def _async_protocol_transport_close(
+    protocol: SanicProtocol,
+    loop: asyncio.AbstractEventLoop,
+    timeout: float,
+):
+    """
+    This function is scheduled to be called after close() is called.
+    It checks that the transport has shut down properly, or waits
+    for any remaining data to be sent, and aborts after a timeout.
+    This is required if the transport is closed while there is an async
+    large async transport write operation in progress.
+    This is observed when NGINX reverse-proxy is the client.
+    """
+    if protocol.transport is None:
+        # abort() is the only method that can make
+        # protocol.transport be None, so abort was already called
+        return
+    # protocol.connection_lost does not set protocol.transport to None
+    # so to detect it a different way with conninfo.lost
+    elif protocol.conn_info is not None and protocol.conn_info.lost:
+        # Terminus. Most connections finish the protocol here!
+        # Connection_lost callback was executed already,
+        # so transport did complete and close itself properly.
+        # No need to call abort().
+
+        # This is the last part of cleanup to do
+        # that is not done by connection_lost handler.
+        # Ensure transport is cleaned up by GC.
+        protocol.transport = None
+        return
+    elif not protocol.transport.is_closing():
+        raise RuntimeError(
+            "You must call transport.close() before "
+            "protocol._async_transport_close() runs."
+        )
+
+    write_is_paused = not protocol._can_write.is_set()
+    try:
+        # in UVLoop, get the data in the libuv write-buffer
+        data_left = protocol.transport.get_write_buffer_size()
+    # Some asyncio implementations don't support get_write_buffer_size
+    except (AttributeError, NotImplementedError):
+        data_left = 0
+    if write_is_paused or data_left > 0:
+        # don't need to call resume_writing here to unpause
+        if timeout <= 0:
+            # timeout is 0 or less, so we can simply abort now
+            loop.call_soon(SanicProtocol.abort, protocol)
+        else:
+            next_check_interval = min(timeout, 0.1)
+            next_check_timeout = timeout - next_check_interval
+            loop.call_later(
+                # Recurse back in after the timeout, to check again
+                next_check_interval,
+                # this next time with reduced timeout.
+                _async_protocol_transport_close,
+                protocol,
+                loop,
+                next_check_timeout,
+            )
+    else:
+        # Not paused, and no data left in the buffer, but transport
+        # is still open, connection_lost has not been called yet.
+        # We can call abort() to fix that.
+        loop.call_soon(SanicProtocol.abort, protocol)
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/protocols/http_protocol.py b/.venv/lib/python3.12/site-packages/sanic/server/protocols/http_protocol.py
new file mode 100644
index 0000000..d28f086
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/protocols/http_protocol.py
@@ -0,0 +1,360 @@
+from __future__ import annotations
+
+import sys
+
+from typing import TYPE_CHECKING
+
+from sanic.http.constants import HTTP
+from sanic.http.http3 import Http3
+from sanic.touchup.meta import TouchUpMeta
+
+
+if TYPE_CHECKING:
+    from sanic.app import Sanic
+
+
+from asyncio import CancelledError
+from time import monotonic as current_time
+
+from sanic.exceptions import (
+    RequestCancelled,
+    RequestTimeout,
+    ServiceUnavailable,
+)
+from sanic.http import Http, Stage
+from sanic.log import (
+    Colors,
+    access_logger,
+    error_logger,
+    logger,
+    websockets_logger,
+)
+from sanic.models.server_types import ConnInfo
+from sanic.request import Request
+from sanic.server.protocols.base_protocol import SanicProtocol
+
+
+ConnectionProtocol = type("ConnectionProtocol", (), {})
+try:
+    from aioquic.asyncio import QuicConnectionProtocol
+    from aioquic.h3.connection import H3_ALPN, H3Connection
+    from aioquic.quic.events import (
+        DatagramFrameReceived,
+        ProtocolNegotiated,
+        QuicEvent,
+    )
+
+    ConnectionProtocol = QuicConnectionProtocol
+except ModuleNotFoundError:  # no cov
+    ...
+
+
+class HttpProtocolMixin:
+    __slots__ = ()
+    __version__: HTTP
+
+    def _setup_connection(self, *args, **kwargs):
+        self._http = self.HTTP_CLASS(self, *args, **kwargs)
+        self._time = current_time()
+        try:
+            self.check_timeouts()
+        except AttributeError:
+            ...
+
+    def _setup(self):
+        self.request: Request | None = None
+        self.access_log = self.app.config.ACCESS_LOG
+        self.request_handler = self.app.handle_request
+        self.error_handler = self.app.error_handler
+        self.request_timeout = self.app.config.REQUEST_TIMEOUT
+        self.response_timeout = self.app.config.RESPONSE_TIMEOUT
+        self.keep_alive_timeout = self.app.config.KEEP_ALIVE_TIMEOUT
+        self.request_max_size = self.app.config.REQUEST_MAX_SIZE
+        self.request_class = self.app.request_class or Request
+
+    @property
+    def http(self):
+        if not hasattr(self, "_http"):
+            return None
+        return self._http
+
+    @property
+    def version(self):
+        return self.__class__.__version__
+
+
+class HttpProtocol(HttpProtocolMixin, SanicProtocol, metaclass=TouchUpMeta):
+    """
+    This class provides implements the HTTP 1.1 protocol on top of our
+    Sanic Server transport
+    """
+
+    HTTP_CLASS = Http
+
+    __touchup__ = (
+        "send",
+        "connection_task",
+    )
+    __version__ = HTTP.VERSION_1
+    __slots__ = (
+        # request params
+        "request",
+        # request config
+        "request_handler",
+        "request_timeout",
+        "response_timeout",
+        "keep_alive_timeout",
+        "request_max_size",
+        "request_class",
+        "error_handler",
+        # enable or disable access log purpose
+        "access_log",
+        # connection management
+        "state",
+        "url",
+        "_handler_task",
+        "_http",
+        "_exception",
+        "recv_buffer",
+        "_callback_check_timeouts",
+    )
+
+    def __init__(
+        self,
+        *,
+        loop,
+        app: Sanic,
+        signal=None,
+        connections=None,
+        state=None,
+        unix=None,
+        **kwargs,
+    ):
+        super().__init__(
+            loop=loop,
+            app=app,
+            signal=signal,
+            connections=connections,
+            unix=unix,
+        )
+        self.url = None
+        self.state = state if state else {}
+        self._setup()
+        if "requests_count" not in self.state:
+            self.state["requests_count"] = 0
+        self._exception = None
+        self._callback_check_timeouts = None
+
+    async def connection_task(self):  # no cov
+        """
+        Run a HTTP connection.
+
+        Timeouts and some additional error handling occur here, while most of
+        everything else happens in class Http or in code called from there.
+        """
+        try:
+            self._setup_connection()
+            await self.app.dispatch(
+                "http.lifecycle.begin",
+                inline=True,
+                context={"conn_info": self.conn_info},
+            )
+            await self._http.http1()
+        except CancelledError:
+            pass
+        except Exception:
+            error_logger.exception("protocol.connection_task uncaught")
+        finally:
+            if (
+                self.access_log
+                and self._http
+                and self.transport
+                and not self._http.upgrade_websocket
+            ):
+                self.log_disconnect()
+            self._http = None
+            self._task = None
+            try:
+                self.close()
+            except BaseException:
+                error_logger.exception("Closing failed")
+            finally:
+                await self.app.dispatch(
+                    "http.lifecycle.complete",
+                    inline=True,
+                    context={"conn_info": self.conn_info},
+                )
+                # Important to keep this Ellipsis here for the TouchUp module
+                ...
+
+    def log_disconnect(self):
+        ip = self.transport.get_extra_info("peername")
+
+        req = self._http.request
+        res = self._http.response
+        extra = {
+            "status": res.status if res else str(self._http.stage),
+            "byte": "DISCONNECTED",
+            "host": f"{id(self):X}"[-5:-1] + "unx",
+            "request": "nil",
+            "duration": "",
+        }
+        if req is not None:
+            if ip := req.client_ip:
+                extra["host"] = f"{ip}:{req.port}"
+            extra["request"] = f"{req.method} {req.url}"
+        access_logger.info("", extra=extra)
+
+    def check_timeouts(self):
+        """
+        Runs itself periodically to enforce any expired timeouts.
+        """
+        try:
+            if not self._task:
+                return
+            duration = current_time() - self._time
+            stage = self._http.stage
+            if stage is Stage.IDLE and duration > self.keep_alive_timeout:
+                logger.debug("KeepAlive Timeout. Closing connection.")
+            elif stage is Stage.REQUEST and duration > self.request_timeout:
+                logger.debug("Request Timeout. Closing connection.")
+                self._http.exception = RequestTimeout("Request Timeout")
+            elif stage is Stage.HANDLER and self._http.upgrade_websocket:
+                websockets_logger.debug(
+                    "Handling websocket. Timeouts disabled."
+                )
+                return
+            elif (
+                stage in (Stage.HANDLER, Stage.RESPONSE, Stage.FAILED)
+                and duration > self.response_timeout
+            ):
+                logger.debug("Response Timeout. Closing connection.")
+                self._http.exception = ServiceUnavailable("Response Timeout")
+            else:
+                interval = (
+                    min(
+                        self.keep_alive_timeout,
+                        self.request_timeout,
+                        self.response_timeout,
+                    )
+                    / 2
+                )
+                _interval = max(0.1, interval)
+                self._callback_check_timeouts = self.loop.call_later(
+                    _interval, self.check_timeouts
+                )
+                return
+            if sys.version_info < (3, 14):
+                self._task.cancel("Cancel connection task with a timeout")
+            else:
+                self._task.cancel()
+        except Exception:
+            error_logger.exception("protocol.check_timeouts")
+
+    def close(self, timeout: float | None = None):
+        """
+        Requires to prevent checking timeouts for closed connections
+        """
+
+        if self._callback_check_timeouts:
+            self._callback_check_timeouts.cancel()
+        return super().close(timeout=timeout)
+
+    async def send(self, data):  # no cov
+        """
+        Writes HTTP data with backpressure control.
+        """
+        await self._can_write.wait()
+        if self.transport.is_closing():
+            raise RequestCancelled
+        await self.app.dispatch(
+            "http.lifecycle.send",
+            inline=True,
+            context={"data": data},
+        )
+        self.transport.write(data)
+        self._time = current_time()
+
+    def close_if_idle(self) -> bool:
+        """
+        Close the connection if a request is not being sent or received
+
+        :return: boolean - True if closed, false if staying open
+        """
+        if self.http is None or self.http.stage is Stage.IDLE:
+            self.close()
+            return True
+        return False
+
+    # -------------------------------------------- #
+    # Only asyncio.Protocol callbacks below this
+    # -------------------------------------------- #
+
+    def connection_made(self, transport):
+        """
+        HTTP-protocol-specific new connection handler
+        """
+        try:
+            # TODO: Benchmark to find suitable write buffer limits
+            transport.set_write_buffer_limits(low=16384, high=65536)
+            self.connections.add(self)
+            self.transport = transport
+            self._task = self.loop.create_task(self.connection_task())
+            self.recv_buffer = bytearray()
+            self.conn_info = ConnInfo(self.transport, unix=self._unix)
+        except Exception:
+            error_logger.exception("protocol.connect_made")
+
+    def data_received(self, data: bytes):
+        try:
+            self._time = current_time()
+            if not data:
+                return self.close()
+            self.recv_buffer += data
+
+            if (
+                len(self.recv_buffer) >= self.app.config.REQUEST_BUFFER_SIZE
+                and self.transport
+            ):
+                self.transport.pause_reading()
+
+            if self._data_received:
+                self._data_received.set()
+        except Exception:
+            error_logger.exception("protocol.data_received")
+
+
+class Http3Protocol(HttpProtocolMixin, ConnectionProtocol):  # type: ignore
+    HTTP_CLASS = Http3
+    __version__ = HTTP.VERSION_3
+
+    def __init__(self, *args, app: Sanic, **kwargs) -> None:
+        self.app = app
+        super().__init__(*args, **kwargs)
+        self._setup()
+        self._connection: H3Connection | None = None
+
+    def quic_event_received(self, event: QuicEvent) -> None:
+        logger.debug(
+            f"{Colors.BLUE}[quic_event_received]: "
+            f"{Colors.PURPLE}{event}{Colors.END}",
+            extra={"verbosity": 2},
+        )
+        if isinstance(event, ProtocolNegotiated):
+            self._setup_connection(transmit=self.transmit)
+            if event.alpn_protocol in H3_ALPN:
+                self._connection = H3Connection(
+                    self._quic, enable_webtransport=True
+                )
+        elif isinstance(event, DatagramFrameReceived):
+            if event.data == b"quack":
+                self._quic.send_datagram_frame(b"quack-ack")
+
+        #  pass event to the HTTP layer
+        if self._connection is not None:
+            for http_event in self._connection.handle_event(event):
+                self._http.http_event_received(http_event)
+
+    @property
+    def connection(self) -> H3Connection | None:
+        return self._connection
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/protocols/websocket_protocol.py b/.venv/lib/python3.12/site-packages/sanic/server/protocols/websocket_protocol.py
new file mode 100644
index 0000000..33c7142
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/protocols/websocket_protocol.py
@@ -0,0 +1,230 @@
+from collections.abc import Sequence
+from typing import cast
+
+
+try:  # websockets >= 11.0
+    from websockets.protocol import State  # type: ignore
+    from websockets.server import ServerProtocol  # type: ignore
+except ImportError:  # websockets < 11.0
+    from websockets.connection import State
+    from websockets.server import ServerConnection as ServerProtocol
+
+from websockets import http11
+from websockets.datastructures import Headers as WSHeaders
+from websockets.typing import Subprotocol
+
+from sanic.exceptions import SanicException
+from sanic.log import access_logger, websockets_logger
+from sanic.request import Request
+from sanic.server import HttpProtocol
+
+from ..websockets.impl import WebsocketImplProtocol
+
+
+OPEN = State.OPEN
+CLOSING = State.CLOSING
+CLOSED = State.CLOSED
+
+
+class WebSocketProtocol(HttpProtocol):
+    __slots__ = (
+        "websocket",
+        "websocket_timeout",
+        "websocket_max_size",
+        "websocket_ping_interval",
+        "websocket_ping_timeout",
+        "websocket_url",
+        "websocket_peer",
+    )
+
+    def __init__(
+        self,
+        *args,
+        websocket_timeout: float = 10.0,
+        websocket_max_size: int | None = None,
+        websocket_ping_interval: float | None = 20.0,
+        websocket_ping_timeout: float | None = 20.0,
+        **kwargs,
+    ):
+        super().__init__(*args, **kwargs)
+        self.websocket: WebsocketImplProtocol | None = None
+        self.websocket_timeout = websocket_timeout
+        self.websocket_max_size = websocket_max_size
+        self.websocket_ping_interval = websocket_ping_interval
+        self.websocket_ping_timeout = websocket_ping_timeout
+        self.websocket_url: str | None = None
+        self.websocket_peer: str | None = None
+
+    def connection_lost(self, exc):
+        if self.websocket is not None:
+            self.websocket.connection_lost(exc)
+        super().connection_lost(exc)
+        self.log_websocket("CLOSE")
+        self.websocket_url = None
+        self.websocket_peer = None
+
+    def data_received(self, data):
+        if self.websocket is not None:
+            self.websocket.data_received(data)
+        else:
+            # Pass it to HttpProtocol handler first
+            # That will (hopefully) upgrade it to a websocket.
+            super().data_received(data)
+
+    def eof_received(self) -> bool | None:
+        if self.websocket is not None:
+            return self.websocket.eof_received()
+        else:
+            return False
+
+    def close(self, timeout: float | None = None):
+        # Called by HttpProtocol at the end of connection_task
+        # If we've upgraded to websocket, we do our own closing
+        if self.websocket is not None:
+            # Note, we don't want to use websocket.close()
+            # That is used for user's application code to send a
+            # websocket close packet. This is different.
+            self.websocket.end_connection(1001)
+        else:
+            super().close()
+
+    def close_if_idle(self):
+        # Called by Sanic Server when shutting down
+        # If we've upgraded to websocket, shut it down
+        if self.websocket is not None:
+            if self.websocket.ws_proto.state in (CLOSING, CLOSED):
+                return True
+            elif self.websocket.loop is not None:
+                self.websocket.loop.create_task(self.websocket.close(1001))
+            else:
+                self.websocket.end_connection(1001)
+        else:
+            return super().close_if_idle()
+
+    @staticmethod
+    def sanic_request_to_ws_request(request: Request):
+        return http11.Request(
+            path=request.path,
+            headers=WSHeaders(request.headers),
+        )
+
+    async def websocket_handshake(
+        self, request, subprotocols: Sequence[str] | None = None
+    ):
+        # let the websockets package do the handshake with the client
+        try:
+            if subprotocols is not None:
+                # subprotocols can be a set or frozenset,
+                # but ServerProtocol needs a list
+                subprotocols = cast(
+                    Sequence[Subprotocol] | None,
+                    list(
+                        [
+                            Subprotocol(subprotocol)
+                            for subprotocol in subprotocols
+                        ]
+                    ),
+                )
+            ws_proto = ServerProtocol(
+                max_size=self.websocket_max_size,
+                subprotocols=subprotocols,
+                state=OPEN,
+                logger=websockets_logger,
+            )
+            resp = ws_proto.accept(self.sanic_request_to_ws_request(request))
+        except Exception:
+            msg = (
+                "Failed to open a WebSocket connection.\n"
+                "See server log for more information.\n"
+            )
+            raise SanicException(msg, status_code=500)
+        if 100 <= resp.status_code <= 299:
+            first_line = (
+                f"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\r\n"
+            ).encode()
+            rbody = bytearray(first_line)
+            rbody += (
+                "".join([f"{k}: {v}\r\n" for k, v in resp.headers.items()])
+            ).encode()
+            rbody += b"\r\n"
+            if resp.body:
+                rbody += resp.body
+                rbody += b"\r\n\r\n"
+            await super().send(rbody)
+        else:
+            raise SanicException(resp.body, resp.status_code)
+        self.websocket = WebsocketImplProtocol(
+            ws_proto,
+            ping_interval=self.websocket_ping_interval,
+            ping_timeout=self.websocket_ping_timeout,
+            close_timeout=self.websocket_timeout,
+        )
+        loop = (
+            request.transport.loop
+            if hasattr(request, "transport")
+            and hasattr(request.transport, "loop")
+            else None
+        )
+        await self.websocket.connection_made(self, loop=loop)
+        self.websocket_url = self._http.request.url
+        self.websocket_peer = f"{id(self):X}"[-5:-1] + "unx"
+        if ip := self._http.request.client_ip:
+            self.websocket_peer = f"{ip}:{self._http.request.port}"
+        self.log_websocket("OPEN")
+        return self.websocket
+
+    def log_websocket(self, message):
+        if not self.access_log or not self.websocket_url:
+            return
+        status = ""
+        close = ""
+        try:
+            # Can we get some useful statistics?
+            p = self.websocket.ws_proto
+            state = p.state
+            if state == CLOSED:
+                codes = {
+                    1000: "NORMAL",
+                    1001: "GOING AWAY",
+                    1005: "NO STATUS",
+                    1006: "ABNORMAL",
+                    1011: "SERVER ERR",
+                }
+                if p.close_code == 1006:
+                    message = "CLOSE_ABN"
+                scode = rcode = 1006  # Abnormal closure (disconnection)
+                sdesc = rdesc = ""
+                if p.close_sent:
+                    scode = p.close_sent.code
+                    sdesc = p.close_sent.reason
+                if p.close_rcvd:
+                    rcode = p.close_rcvd.code
+                    rdesc = p.close_rcvd.reason
+                # Use repr() to escape any control characters
+                sdesc = repr(sdesc[:256]) if sdesc else codes.get(scode, "")
+                rdesc = repr(rdesc[:256]) if rdesc else codes.get(rcode, "")
+                if p.close_rcvd_then_sent or scode == 1006:
+                    status = rcode
+                    close = (
+                        f"{rdesc} from client"
+                        if scode in (rcode, 1006)
+                        else f"{rdesc} ▼▲ {scode} {sdesc}"
+                    )
+                else:
+                    status = scode
+                    close = (
+                        f"{sdesc} from server"
+                        if rcode in (scode, 1006)
+                        else f"{sdesc} ▲▼ {rcode} {rdesc}"
+                    )
+
+        except AttributeError:
+            ...
+        extra = {
+            "status": status,
+            "byte": close,
+            "host": self.websocket_peer,
+            "request": f" 🔌 {self.websocket_url}",
+            "duration": "",
+        }
+        access_logger.info(message, extra=extra)
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/runners.py b/.venv/lib/python3.12/site-packages/sanic/server/runners.py
new file mode 100644
index 0000000..9543ac8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/runners.py
@@ -0,0 +1,404 @@
+from __future__ import annotations
+
+from ssl import SSLContext
+from typing import TYPE_CHECKING
+
+from sanic.config import Config
+from sanic.exceptions import ServerError
+from sanic.http.constants import HTTP
+from sanic.http.tls import get_ssl_context
+
+
+if TYPE_CHECKING:
+    from sanic.app import Sanic
+
+import asyncio
+import os
+import socket
+
+from functools import partial
+from signal import SIG_IGN, SIGINT, SIGTERM
+from signal import signal as signal_func
+
+from sanic.application.ext import setup_ext
+from sanic.compat import OS_IS_WINDOWS, ctrlc_workaround_for_windows
+from sanic.http.http3 import SessionTicketStore, get_config
+from sanic.log import error_logger, server_logger
+from sanic.logging.setup import setup_logging
+from sanic.models.server_types import Signal
+from sanic.server.async_server import AsyncioServer
+from sanic.server.protocols.http_protocol import Http3Protocol, HttpProtocol
+from sanic.server.socket import bind_unix_socket, remove_unix_socket
+
+
+try:
+    from aioquic.asyncio import serve as quic_serve
+
+    HTTP3_AVAILABLE = True
+except ModuleNotFoundError:  # no cov
+    HTTP3_AVAILABLE = False
+
+
+def serve(
+    host,
+    port,
+    app: Sanic,
+    ssl: SSLContext | None = None,
+    sock: socket.socket | None = None,
+    unix: str | None = None,
+    reuse_port: bool = False,
+    loop=None,
+    protocol: type[asyncio.Protocol] = HttpProtocol,
+    backlog: int = 100,
+    register_sys_signals: bool = True,
+    run_multiple: bool = False,
+    run_async: bool = False,
+    connections=None,
+    signal=Signal(),
+    state=None,
+    asyncio_server_kwargs=None,
+    version=HTTP.VERSION_1,
+):
+    """Start asynchronous HTTP Server on an individual process.
+
+    :param host: Address to host on
+    :param port: Port to host on
+    :param before_start: function to be executed before the server starts
+                         listening. Takes arguments `app` instance and `loop`
+    :param after_start: function to be executed after the server starts
+                        listening. Takes  arguments `app` instance and `loop`
+    :param before_stop: function to be executed when a stop signal is
+                        received before it is respected. Takes arguments
+                        `app` instance and `loop`
+    :param after_stop: function to be executed when a stop signal is
+                       received after it is respected. Takes arguments
+                       `app` instance and `loop`
+    :param ssl: SSLContext
+    :param sock: Socket for the server to accept connections from
+    :param unix: Unix socket to listen on instead of TCP port
+    :param reuse_port: `True` for multiple workers
+    :param loop: asyncio compatible event loop
+    :param run_async: bool: Do not create a new event loop for the server,
+                      and return an AsyncServer object rather than running it
+    :param asyncio_server_kwargs: key-value args for asyncio/uvloop
+                                  create_server method
+    :return: Nothing
+
+    Args:
+        host (str): Address to host on
+        port (int): Port to host on
+        app (Sanic): Sanic app instance
+        ssl (Optional[SSLContext], optional): SSLContext. Defaults to `None`.
+        sock (Optional[socket.socket], optional): Socket for the server to
+            accept connections from. Defaults to `None`.
+        unix (Optional[str], optional): Unix socket to listen on instead of
+            TCP port. Defaults to `None`.
+        reuse_port (bool, optional): `True` for multiple workers. Defaults
+            to `False`.
+        loop: asyncio compatible event loop. Defaults
+            to `None`.
+        protocol (Type[asyncio.Protocol], optional): Protocol to use. Defaults
+            to `HttpProtocol`.
+        backlog (int, optional): The maximum number of queued connections
+            passed to socket.listen(). Defaults to `100`.
+        register_sys_signals (bool, optional): Register SIGINT and SIGTERM.
+            Defaults to `True`.
+        run_multiple (bool, optional): Run multiple workers. Defaults
+            to `False`.
+        run_async (bool, optional): Return an AsyncServer object.
+            Defaults to `False`.
+        connections: Connections. Defaults to `None`.
+        signal (Signal, optional): Signal. Defaults to `Signal()`.
+        state: State. Defaults to `None`.
+        asyncio_server_kwargs (Optional[Dict[str, Union[int, float]]], optional):
+            key-value args for asyncio/uvloop create_server method. Defaults
+            to `None`.
+        version (str, optional): HTTP version. Defaults to `HTTP.VERSION_1`.
+
+    Raises:
+        ServerError: Cannot run HTTP/3 server without aioquic installed.
+
+    Returns:
+        AsyncioServer: AsyncioServer object if `run_async` is `True`.
+    """  # noqa: E501
+    if not run_async and not loop:
+        # create new event_loop after fork
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+
+    setup_logging(app.debug, app.config.NO_COLOR, app.config.LOG_EXTRA)
+
+    if app.debug:
+        loop.set_debug(app.debug)
+
+    app.asgi = False
+
+    if version is HTTP.VERSION_3:
+        return _serve_http_3(host, port, app, loop, ssl)
+    return _serve_http_1(
+        host,
+        port,
+        app,
+        ssl,
+        sock,
+        unix,
+        reuse_port,
+        loop,
+        protocol,
+        backlog,
+        register_sys_signals,
+        run_multiple,
+        run_async,
+        connections,
+        signal,
+        state,
+        asyncio_server_kwargs,
+    )
+
+
+def _setup_system_signals(
+    app: Sanic,
+    run_multiple: bool,
+    register_sys_signals: bool,
+    loop: asyncio.AbstractEventLoop,
+) -> None:  # no cov
+    signal_func(SIGINT, SIG_IGN)
+    signal_func(SIGTERM, SIG_IGN)
+    os.environ["SANIC_WORKER_PROCESS"] = "true"
+    # Register signals for graceful termination
+    if register_sys_signals:
+        if OS_IS_WINDOWS:
+            ctrlc_workaround_for_windows(app)
+        else:
+            for _signal in [SIGINT, SIGTERM]:
+                loop.add_signal_handler(
+                    _signal, partial(app.stop, terminate=False)
+                )
+
+
+def _run_shutdown_coro(loop, coro):
+    """Run a shutdown coroutine, handling the case where the loop was stopped.
+
+    When loop.stop() is called during run_forever(), asyncio sets an internal
+    flag that causes the first run_until_complete() to fail. For standard
+    asyncio, we clear the _stopping flag. For uvloop (which doesn't expose
+    this flag), the first attempt may fail but subsequent attempts succeed.
+    """
+    # Clear asyncio's stopped state if accessible
+    if hasattr(loop, "_stopping"):
+        loop._stopping = False
+
+    try:
+        loop.run_until_complete(coro())
+    except (RuntimeError, KeyboardInterrupt):
+        # RuntimeError: loop was stopped (uvloop behavior)
+        # KeyboardInterrupt: signal arrived during select (asyncio behavior)
+        # Try once more - this handles uvloop's behavior where the first
+        # run_until_complete after stop() fails but subsequent calls succeed.
+        if hasattr(loop, "_stopping"):
+            loop._stopping = False
+        try:
+            loop.run_until_complete(coro())
+        except (RuntimeError, KeyboardInterrupt):
+            # If it still fails, the loop is truly unusable
+            pass
+
+
+def _run_server_forever(loop, before_stop, after_stop, cleanup, unix, pid):
+    try:
+        server_logger.info("Worker ready [%s]", pid)
+        loop.run_forever()
+    finally:
+        server_logger.info("Stopping worker [%s]", pid)
+
+        for _signal in [SIGINT, SIGTERM]:
+            try:
+                loop.remove_signal_handler(_signal)
+            except (NotImplementedError, OSError):
+                pass
+
+        _run_shutdown_coro(loop, before_stop)
+
+        if cleanup:
+            cleanup()
+
+        _run_shutdown_coro(loop, after_stop)
+
+        remove_unix_socket(unix)
+        loop.close()
+        server_logger.info("Worker complete [%s]", pid)
+
+
+def _serve_http_1(
+    host,
+    port,
+    app,
+    ssl,
+    sock,
+    unix,
+    reuse_port,
+    loop,
+    protocol,
+    backlog,
+    register_sys_signals,
+    run_multiple,
+    run_async,
+    connections,
+    signal,
+    state,
+    asyncio_server_kwargs,
+):
+    connections = connections if connections is not None else set()
+    protocol_kwargs = _build_protocol_kwargs(protocol, app.config)
+    server = partial(
+        protocol,
+        loop=loop,
+        connections=connections,
+        signal=signal,
+        app=app,
+        state=state,
+        unix=unix,
+        **protocol_kwargs,
+    )
+    asyncio_server_kwargs = (
+        asyncio_server_kwargs if asyncio_server_kwargs else {}
+    )
+    if OS_IS_WINDOWS and sock:
+        pid = os.getpid()
+        sock = sock.share(pid)
+        sock = socket.fromshare(sock)
+    # UNIX sockets are always bound by us (to preserve semantics between modes)
+    elif unix:
+        sock = bind_unix_socket(unix, backlog=backlog)
+    server_coroutine = loop.create_server(
+        server,
+        None if sock else host,
+        None if sock else port,
+        ssl=ssl,
+        reuse_port=reuse_port,
+        sock=sock,
+        backlog=backlog,
+        **asyncio_server_kwargs,
+    )
+
+    setup_ext(app)
+    if run_async:
+        return AsyncioServer(
+            app=app,
+            loop=loop,
+            serve_coro=server_coroutine,
+            connections=connections,
+        )
+
+    pid = os.getpid()
+    server_logger.info("Starting worker [%s]", pid)
+    loop.run_until_complete(app._startup())
+    loop.run_until_complete(app._server_event("init", "before"))
+    app.ack()
+
+    try:
+        http_server = loop.run_until_complete(server_coroutine)
+    except BaseException:
+        error_logger.exception("Unable to start server", exc_info=True)
+        return
+
+    def _cleanup():
+        # Wait for event loop to finish and all connections to drain
+        http_server.close()
+        loop.run_until_complete(http_server.wait_closed())
+
+        # Complete all tasks on the loop
+        signal.stopped = True
+        for connection in connections:
+            connection.close_if_idle()
+
+        # Gracefully shutdown timeout.
+        # We should provide graceful_shutdown_timeout,
+        # instead of letting connection hangs forever.
+        # Let's roughly calcucate time.
+        graceful = app.config.GRACEFUL_SHUTDOWN_TIMEOUT
+        start_shutdown: float = 0
+        while connections and (start_shutdown < graceful):
+            loop.run_until_complete(asyncio.sleep(0.1))
+            start_shutdown = start_shutdown + 0.1
+
+        app.shutdown_tasks(graceful - start_shutdown)
+
+        # Force close non-idle connection after waiting for
+        # graceful_shutdown_timeout
+        for conn in connections:
+            if hasattr(conn, "websocket") and conn.websocket:
+                conn.websocket.fail_connection(code=1001)
+            else:
+                conn.abort()
+
+        try:
+            app.set_serving(False)
+        except (BrokenPipeError, ConnectionResetError, EOFError):
+            pass
+
+    _setup_system_signals(app, run_multiple, register_sys_signals, loop)
+    loop.run_until_complete(app._server_event("init", "after"))
+    app.set_serving(True)
+    _run_server_forever(
+        loop,
+        partial(app._server_event, "shutdown", "before"),
+        partial(app._server_event, "shutdown", "after"),
+        _cleanup,
+        unix,
+        pid,
+    )
+
+
+def _serve_http_3(
+    host,
+    port,
+    app,
+    loop,
+    ssl,
+    register_sys_signals: bool = True,
+    run_multiple: bool = False,
+):
+    if not HTTP3_AVAILABLE:
+        raise ServerError(
+            "Cannot run HTTP/3 server without aioquic installed. "
+        )
+    pid = os.getpid()
+    server_logger.info("Starting worker [%s]", pid)
+    protocol = partial(Http3Protocol, app=app)
+    ticket_store = SessionTicketStore()
+    ssl_context = get_ssl_context(app, ssl)
+    config = get_config(app, ssl_context)
+    coro = quic_serve(
+        host,
+        port,
+        configuration=config,
+        create_protocol=protocol,
+        session_ticket_fetcher=ticket_store.pop,
+        session_ticket_handler=ticket_store.add,
+    )
+    server = AsyncioServer(app, loop, coro, [])
+    loop.run_until_complete(server.startup())
+    loop.run_until_complete(server.before_start())
+    app.ack()
+    loop.run_until_complete(server)
+    _setup_system_signals(app, run_multiple, register_sys_signals, loop)
+    loop.run_until_complete(server.after_start())
+
+    # TODO: Create connection cleanup and graceful shutdown
+    cleanup = None
+    _run_server_forever(
+        loop, server.before_stop, server.after_stop, cleanup, None, pid
+    )
+
+
+def _build_protocol_kwargs(
+    protocol: type[asyncio.Protocol], config: Config
+) -> dict[str, int | float]:
+    if hasattr(protocol, "websocket_handshake"):
+        return {
+            "websocket_max_size": config.WEBSOCKET_MAX_SIZE,
+            "websocket_ping_timeout": config.WEBSOCKET_PING_TIMEOUT,
+            "websocket_ping_interval": config.WEBSOCKET_PING_INTERVAL,
+        }
+    return {}
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/socket.py b/.venv/lib/python3.12/site-packages/sanic/server/socket.py
new file mode 100644
index 0000000..62055ff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/socket.py
@@ -0,0 +1,121 @@
+from __future__ import annotations
+
+import secrets
+import socket
+import stat
+
+from ipaddress import ip_address
+from pathlib import Path
+from typing import Any
+
+from sanic.http.constants import HTTP
+
+
+def bind_socket(host: str, port: int, *, backlog=100) -> socket.socket:
+    """Create TCP server socket.
+    :param host: IPv4, IPv6 or hostname may be specified
+    :param port: TCP port number
+    :param backlog: Maximum number of connections to queue
+    :return: socket.socket object
+    """
+    location = (host, port)
+    # socket.share, socket.fromshare
+    try:  # IP address: family must be specified for IPv6 at least
+        ip = ip_address(host)
+        host = str(ip)
+        sock = socket.socket(
+            socket.AF_INET6 if ip.version == 6 else socket.AF_INET
+        )
+    except ValueError:  # Hostname, may become AF_INET or AF_INET6
+        sock = socket.socket()
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(location)
+    sock.listen(backlog)
+    sock.set_inheritable(True)
+    return sock
+
+
+def bind_unix_socket(
+    path: Path | str, *, mode=0o666, backlog=100
+) -> socket.socket:
+    """Create unix socket.
+    :param path: filesystem path
+    :param backlog: Maximum number of connections to queue
+    :return: socket.socket object
+    """
+
+    # Sanitise and pre-verify socket path
+    path = Path(path)
+    folder = path.parent
+    if not folder.is_dir():
+        raise FileNotFoundError(f"Socket folder does not exist: {folder}")
+    try:
+        if not stat.S_ISSOCK(path.lstat().st_mode):
+            raise FileExistsError(f"Existing file is not a socket: {path}")
+    except FileNotFoundError:
+        pass
+    # Create new socket with a random temporary name
+    tmp_path = path.with_name(f"{path.name}.{secrets.token_urlsafe()}")
+    sock = socket.socket(socket.AF_UNIX)
+    try:
+        # Critical section begins (filename races)
+        sock.bind(tmp_path.as_posix())
+        try:
+            tmp_path.chmod(mode)
+            # Start listening before rename to avoid connection failures
+            sock.listen(backlog)
+            tmp_path.rename(path)
+        except:  # noqa: E722
+            try:
+                tmp_path.unlink()
+            finally:
+                raise
+    except:  # noqa: E722
+        try:
+            sock.close()
+        finally:
+            raise
+    return sock
+
+
+def remove_unix_socket(path: Path | str | None) -> None:
+    """Remove dead unix socket during server exit."""
+    if not path:
+        return
+    try:
+        path = Path(path)
+        if stat.S_ISSOCK(path.lstat().st_mode):
+            # Is it actually dead (doesn't belong to a new server instance)?
+            with socket.socket(socket.AF_UNIX) as testsock:
+                try:
+                    testsock.connect(path.as_posix())
+                except ConnectionRefusedError:
+                    path.unlink()
+    except FileNotFoundError:
+        pass
+
+
+def configure_socket(
+    server_settings: dict[str, Any],
+) -> socket.SocketType | None:
+    # Create a listening socket or use the one in settings
+    if server_settings.get("version") is HTTP.VERSION_3:
+        return None
+    sock = server_settings.get("sock")
+    unix = server_settings["unix"]
+    backlog = server_settings["backlog"]
+    if unix:
+        unix = Path(unix).absolute()
+        sock = bind_unix_socket(unix, backlog=backlog)
+        server_settings["unix"] = unix
+    if sock is None:
+        sock = bind_socket(
+            server_settings["host"],
+            server_settings["port"],
+            backlog=backlog,
+        )
+        sock.set_inheritable(True)
+        server_settings["sock"] = sock
+        server_settings["host"] = None
+        server_settings["port"] = None
+    return sock
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/websockets/__init__.py b/.venv/lib/python3.12/site-packages/sanic/server/websockets/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/websockets/connection.py b/.venv/lib/python3.12/site-packages/sanic/server/websockets/connection.py
new file mode 100644
index 0000000..118f9dd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/websockets/connection.py
@@ -0,0 +1,82 @@
+from collections.abc import Awaitable, MutableMapping
+from typing import Any, Callable
+
+from sanic.exceptions import InvalidUsage
+
+
+ASGIMessage = MutableMapping[str, Any]
+
+
+class WebSocketConnection:
+    """
+    This is for ASGI Connections.
+    It provides an interface similar to WebsocketProtocol, but
+    sends/receives over an ASGI connection.
+    """
+
+    # TODO
+    # - Implement ping/pong
+
+    def __init__(
+        self,
+        send: Callable[[ASGIMessage], Awaitable[None]],
+        receive: Callable[[], Awaitable[ASGIMessage]],
+        subprotocols: list[str] | None = None,
+    ) -> None:
+        self._send = send
+        self._receive = receive
+        self._subprotocols = subprotocols or []
+
+    async def send(self, data: str | bytes, *args, **kwargs) -> None:
+        message: dict[str, str | bytes] = {"type": "websocket.send"}
+
+        if isinstance(data, bytes):
+            message.update({"bytes": data})
+        else:
+            message.update({"text": str(data)})
+
+        await self._send(message)
+
+    async def recv(self, *args, **kwargs) -> str | bytes | None:
+        message = await self._receive()
+
+        if message["type"] == "websocket.receive":
+            try:
+                return message["text"]
+            except KeyError:
+                try:
+                    return message["bytes"]
+                except KeyError:
+                    raise InvalidUsage("Bad ASGI message received")
+        elif message["type"] == "websocket.disconnect":
+            pass
+
+        return None
+
+    receive = recv
+
+    async def accept(self, subprotocols: list[str] | None = None) -> None:
+        subprotocol = None
+        if subprotocols:
+            for subp in subprotocols:
+                if subp in self.subprotocols:
+                    subprotocol = subp
+                    break
+
+        await self._send(
+            {
+                "type": "websocket.accept",
+                "subprotocol": subprotocol,
+            }
+        )
+
+    async def close(self, code: int = 1000, reason: str = "") -> None:
+        pass
+
+    @property
+    def subprotocols(self):
+        return self._subprotocols
+
+    @subprotocols.setter
+    def subprotocols(self, subprotocols: list[str] | None = None):
+        self._subprotocols = subprotocols or []
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/websockets/frame.py b/.venv/lib/python3.12/site-packages/sanic/server/websockets/frame.py
new file mode 100644
index 0000000..9938a95
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/websockets/frame.py
@@ -0,0 +1,293 @@
+import asyncio
+import codecs
+
+from collections.abc import AsyncIterator
+from typing import TYPE_CHECKING
+
+from websockets.frames import Frame, Opcode
+from websockets.typing import Data
+
+from sanic.exceptions import ServerError
+
+
+if TYPE_CHECKING:
+    from .impl import WebsocketImplProtocol
+
+UTF8Decoder = codecs.getincrementaldecoder("utf-8")
+
+
+class WebsocketFrameAssembler:
+    """
+    Assemble a message from frames.
+    Code borrowed from aaugustin/websockets project:
+    https://github.com/aaugustin/websockets/blob/6eb98dd8fa5b2c896b9f6be7e8d117708da82a39/src/websockets/sync/messages.py
+    """
+
+    __slots__ = (
+        "protocol",
+        "read_mutex",
+        "write_mutex",
+        "message_complete",
+        "message_fetched",
+        "get_in_progress",
+        "decoder",
+        "completed_queue",
+        "chunks",
+        "chunks_queue",
+        "paused",
+        "get_id",
+        "put_id",
+    )
+    if TYPE_CHECKING:
+        protocol: "WebsocketImplProtocol"
+        read_mutex: asyncio.Lock
+        write_mutex: asyncio.Lock
+        message_complete: asyncio.Event
+        message_fetched: asyncio.Event
+        completed_queue: asyncio.Queue
+        get_in_progress: bool
+        decoder: codecs.IncrementalDecoder | None
+        # For streaming chunks rather than messages:
+        chunks: list[Data]
+        chunks_queue: asyncio.Queue[Data | None] | None
+        paused: bool
+
+    def __init__(self, protocol) -> None:
+        self.protocol = protocol
+
+        self.read_mutex = asyncio.Lock()
+        self.write_mutex = asyncio.Lock()
+
+        self.completed_queue = asyncio.Queue(maxsize=1)  # type: asyncio.Queue[Data]
+
+        # put() sets this event to tell get() that a message can be fetched.
+        self.message_complete = asyncio.Event()
+        # get() sets this event to let put()
+        self.message_fetched = asyncio.Event()
+
+        # This flag prevents concurrent calls to get() by user code.
+        self.get_in_progress = False
+
+        # Decoder for text frames, None for binary frames.
+        self.decoder = None
+
+        # Buffer data from frames belonging to the same message.
+        self.chunks = []
+
+        # When switching from "buffering" to "streaming", we use a thread-safe
+        # queue for transferring frames from the writing thread (library code)
+        # to the reading thread (user code). We're buffering when chunks_queue
+        # is None and streaming when it's a Queue. None is a sentinel
+        # value marking the end of the stream, superseding message_complete.
+
+        # Stream data from frames belonging to the same message.
+        self.chunks_queue = None
+
+        # Flag to indicate we've paused the protocol
+        self.paused = False
+
+    async def get(self, timeout: float | None = None) -> Data | None:
+        """
+        Read the next message.
+        :meth:`get` returns a single :class:`str` or :class:`bytes`.
+        If the :message was fragmented, :meth:`get` waits until the last frame
+        is received, then it reassembles the message.
+        If ``timeout`` is set and elapses before a complete message is
+        received, :meth:`get` returns ``None``.
+        """
+        completed: bool
+        async with self.read_mutex:
+            if timeout is not None and timeout <= 0:
+                if not self.message_complete.is_set():
+                    return None
+            if self.get_in_progress:
+                # This should be guarded against with the read_mutex,
+                # exception is only here as a failsafe
+                raise ServerError(
+                    "Called get() on Websocket frame assembler "
+                    "while asynchronous get is already in progress."
+                )
+            self.get_in_progress = True
+
+            # If the message_complete event isn't set yet, release the lock to
+            # allow put() to run and eventually set it.
+            # Locking with get_in_progress ensures only one task can get here.
+            if timeout is None:
+                completed = await self.message_complete.wait()
+            elif timeout <= 0:
+                completed = self.message_complete.is_set()
+            else:
+                try:
+                    await asyncio.wait_for(
+                        self.message_complete.wait(), timeout=timeout
+                    )
+                except asyncio.TimeoutError:
+                    ...
+                finally:
+                    completed = self.message_complete.is_set()
+
+            # Unpause the transport, if its paused
+            if self.paused:
+                self.protocol.resume_frames()
+                self.paused = False
+            if not self.get_in_progress:  # no cov
+                # This should be guarded against with the read_mutex,
+                # exception is here as a failsafe
+                raise ServerError(
+                    "State of Websocket frame assembler was modified while an "
+                    "asynchronous get was in progress."
+                )
+            self.get_in_progress = False
+
+            # Waiting for a complete message timed out.
+            if not completed:
+                return None
+            if not self.message_complete.is_set():
+                return None
+
+            self.message_complete.clear()
+
+            joiner: Data = b"" if self.decoder is None else ""
+            # mypy cannot figure out that chunks have the proper type.
+            message: Data = joiner.join(self.chunks)  # type: ignore
+            if self.message_fetched.is_set():
+                # This should be guarded against with the read_mutex,
+                # and get_in_progress check, this exception is here
+                # as a failsafe
+                raise ServerError(
+                    "Websocket get() found a message when "
+                    "state was already fetched."
+                )
+            self.message_fetched.set()
+            self.chunks = []
+            # this should already be None, but set it here for safety
+            self.chunks_queue = None
+            return message
+
+    async def get_iter(self) -> AsyncIterator[Data]:
+        """
+        Stream the next message.
+        Iterating the return value of :meth:`get_iter` yields a :class:`str`
+        or :class:`bytes` for each frame in the message.
+        """
+        async with self.read_mutex:
+            if self.get_in_progress:
+                # This should be guarded against with the read_mutex,
+                # exception is only here as a failsafe
+                raise ServerError(
+                    "Called get_iter on Websocket frame assembler "
+                    "while asynchronous get is already in progress."
+                )
+            self.get_in_progress = True
+
+            chunks = self.chunks
+            self.chunks = []
+            self.chunks_queue = asyncio.Queue()
+
+            # Sending None in chunk_queue supersedes setting message_complete
+            # when switching to "streaming". If message is already complete
+            # when the switch happens, put() didn't send None, so we have to.
+            if self.message_complete.is_set():
+                await self.chunks_queue.put(None)
+
+            # Locking with get_in_progress ensures only one task can get here
+            for c in chunks:
+                yield c
+            while True:
+                chunk = await self.chunks_queue.get()
+                if chunk is None:
+                    break
+                yield chunk
+
+            # Unpause the transport, if its paused
+            if self.paused:
+                self.protocol.resume_frames()
+                self.paused = False
+            if not self.get_in_progress:  # no cov
+                # This should be guarded against with the read_mutex,
+                # exception is here as a failsafe
+                raise ServerError(
+                    "State of Websocket frame assembler was modified while an "
+                    "asynchronous get was in progress."
+                )
+            self.get_in_progress = False
+            if not self.message_complete.is_set():  # no cov
+                # This should be guarded against with the read_mutex,
+                # exception is here as a failsafe
+                raise ServerError(
+                    "Websocket frame assembler chunks queue ended before "
+                    "message was complete."
+                )
+            self.message_complete.clear()
+            if self.message_fetched.is_set():  # no cov
+                # This should be guarded against with the read_mutex,
+                # and get_in_progress check, this exception is
+                # here as a failsafe
+                raise ServerError(
+                    "Websocket get_iter() found a message when state was "
+                    "already fetched."
+                )
+
+            self.message_fetched.set()
+            # this should already be empty, but set it here for safety
+            self.chunks = []
+            self.chunks_queue = None
+
+    async def put(self, frame: Frame) -> None:
+        """
+        Add ``frame`` to the next message.
+        When ``frame`` is the final frame in a message, :meth:`put` waits
+        until the message is fetched, either by calling :meth:`get` or by
+        iterating the return value of :meth:`get_iter`.
+        :meth:`put` assumes that the stream of frames respects the protocol.
+        If it doesn't, the behavior is undefined.
+        """
+
+        async with self.write_mutex:
+            if frame.opcode is Opcode.TEXT:
+                self.decoder = UTF8Decoder(errors="strict")
+            elif frame.opcode is Opcode.BINARY:
+                self.decoder = None
+            elif frame.opcode is Opcode.CONT:
+                pass
+            else:
+                # Ignore control frames.
+                return
+            data: Data
+            if self.decoder is not None:
+                data = self.decoder.decode(frame.data, frame.fin)
+            else:
+                data = frame.data
+            if self.chunks_queue is None:
+                self.chunks.append(data)
+            else:
+                await self.chunks_queue.put(data)
+
+            if not frame.fin:
+                return
+            if not self.get_in_progress:
+                # nobody is waiting for this frame, so try to pause subsequent
+                # frames at the protocol level
+                self.paused = self.protocol.pause_frames()
+            # Message is complete. Wait until it's fetched to return.
+
+            if self.chunks_queue is not None:
+                await self.chunks_queue.put(None)
+            if self.message_complete.is_set():
+                # This should be guarded against with the write_mutex
+                raise ServerError(
+                    "Websocket put() got a new message when a message was "
+                    "already in its chamber."
+                )
+            self.message_complete.set()  # Signal to get() it can serve the
+            if self.message_fetched.is_set():
+                # This should be guarded against with the write_mutex
+                raise ServerError(
+                    "Websocket put() got a new message when the previous "
+                    "message was not yet fetched."
+                )
+
+            # Allow get() to run and eventually set the event.
+            await self.message_fetched.wait()
+            self.message_fetched.clear()
+            self.decoder = None
diff --git a/.venv/lib/python3.12/site-packages/sanic/server/websockets/impl.py b/.venv/lib/python3.12/site-packages/sanic/server/websockets/impl.py
new file mode 100644
index 0000000..d681107
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/server/websockets/impl.py
@@ -0,0 +1,878 @@
+import asyncio
+import secrets
+
+from collections.abc import AsyncIterator, Iterable, Mapping, Sequence
+
+from websockets.exceptions import (
+    ConnectionClosed,
+    ConnectionClosedError,
+    ConnectionClosedOK,
+)
+from websockets.frames import Frame, Opcode
+
+
+try:  # websockets >= 11.0
+    from websockets.protocol import Event, State  # type: ignore
+    from websockets.server import ServerProtocol  # type: ignore
+except ImportError:  # websockets < 11.0
+    from websockets.connection import Event, State  # type: ignore
+    from websockets.server import ServerConnection as ServerProtocol
+
+from websockets.typing import Data
+
+from sanic.log import websockets_logger
+from sanic.server.protocols.base_protocol import SanicProtocol
+
+from ...exceptions import ServerError, WebsocketClosed
+from .frame import WebsocketFrameAssembler
+
+
+OPEN = State.OPEN
+CLOSING = State.CLOSING
+CLOSED = State.CLOSED
+
+
+class WebsocketImplProtocol:
+    ws_proto: ServerProtocol
+    io_proto: SanicProtocol | None
+    loop: asyncio.AbstractEventLoop | None
+    max_queue: int
+    close_timeout: float
+    ping_interval: float | None
+    ping_timeout: float | None
+    assembler: WebsocketFrameAssembler
+    # dict[bytes, asyncio.Future[None]]
+    pings: dict[bytes, asyncio.Future]
+    conn_mutex: asyncio.Lock
+    recv_lock: asyncio.Lock
+    recv_cancel: asyncio.Future | None
+    process_event_mutex: asyncio.Lock
+    can_pause: bool
+    # asyncio.Future[None] | None
+    data_finished_fut: asyncio.Future | None
+    # asyncio.Future[None] | None
+    pause_frame_fut: asyncio.Future | None
+    # asyncio.Future[None] | None
+    connection_lost_waiter: asyncio.Future | None
+    keepalive_ping_task: asyncio.Task | None
+    auto_closer_task: asyncio.Task | None
+
+    def __init__(
+        self,
+        ws_proto,
+        max_queue=None,
+        ping_interval: float | None = 20,
+        ping_timeout: float | None = 20,
+        close_timeout: float = 10,
+        loop=None,
+    ):
+        self.ws_proto = ws_proto
+        self.io_proto = None
+        self.loop = None
+        self.max_queue = max_queue
+        self.close_timeout = close_timeout
+        self.ping_interval = ping_interval
+        self.ping_timeout = ping_timeout
+        self.assembler = WebsocketFrameAssembler(self)
+        self.pings = {}
+        self.conn_mutex = asyncio.Lock()
+        self.recv_lock = asyncio.Lock()
+        self.recv_cancel = None
+        self.process_event_mutex = asyncio.Lock()
+        self.data_finished_fut = None
+        self.can_pause = True
+        self.pause_frame_fut = None
+        self.keepalive_ping_task = None
+        self.auto_closer_task = None
+        self.connection_lost_waiter = None
+
+    @property
+    def subprotocol(self):
+        return self.ws_proto.subprotocol
+
+    def pause_frames(self):
+        if not self.can_pause:
+            return False
+        if self.pause_frame_fut:
+            websockets_logger.debug("Websocket connection already paused.")
+            return False
+        if (not self.loop) or (not self.io_proto):
+            return False
+        if self.io_proto.transport:
+            self.io_proto.transport.pause_reading()
+        self.pause_frame_fut = self.loop.create_future()
+        websockets_logger.debug("Websocket connection paused.")
+        return True
+
+    def resume_frames(self):
+        if not self.pause_frame_fut:
+            websockets_logger.debug("Websocket connection not paused.")
+            return False
+        if (not self.loop) or (not self.io_proto):
+            websockets_logger.debug(
+                "Websocket attempting to resume reading frames, "
+                "but connection is gone."
+            )
+            return False
+        if self.io_proto.transport:
+            self.io_proto.transport.resume_reading()
+        self.pause_frame_fut.set_result(None)
+        self.pause_frame_fut = None
+        websockets_logger.debug("Websocket connection unpaused.")
+        return True
+
+    async def connection_made(
+        self,
+        io_proto: SanicProtocol,
+        loop: asyncio.AbstractEventLoop | None = None,
+    ):
+        if not loop:
+            try:
+                loop = getattr(io_proto, "loop")
+            except AttributeError:
+                loop = asyncio.get_event_loop()
+        if not loop:
+            # This catch is for mypy type checker
+            # to assert loop is not None here.
+            raise ServerError("Connection received with no asyncio loop.")
+        if self.auto_closer_task:
+            raise ServerError(
+                "Cannot call connection_made more than once "
+                "on a websocket connection."
+            )
+        self.loop = loop
+        self.io_proto = io_proto
+        self.connection_lost_waiter = self.loop.create_future()
+        self.data_finished_fut = asyncio.shield(self.loop.create_future())
+
+        if self.ping_interval:
+            self.keepalive_ping_task = asyncio.create_task(
+                self.keepalive_ping()
+            )
+        self.auto_closer_task = asyncio.create_task(
+            self.auto_close_connection()
+        )
+
+    async def wait_for_connection_lost(self, timeout=None) -> bool:
+        """
+        Wait until the TCP connection is closed or ``timeout`` elapses.
+        If timeout is None, wait forever.
+        Recommend you should pass in self.close_timeout as timeout
+
+        Return ``True`` if the connection is closed and ``False`` otherwise.
+
+        """
+        if not self.connection_lost_waiter:
+            return False
+        if self.connection_lost_waiter.done():
+            return True
+        else:
+            try:
+                await asyncio.wait_for(
+                    asyncio.shield(self.connection_lost_waiter), timeout
+                )
+                return True
+            except asyncio.TimeoutError:
+                # Re-check self.connection_lost_waiter.done() synchronously
+                # because connection_lost() could run between the moment the
+                # timeout occurs and the moment this coroutine resumes running
+                return self.connection_lost_waiter.done()
+
+    async def process_events(self, events: Sequence[Event]) -> None:
+        """
+        Process a list of incoming events.
+        """
+        # Wrapped in a mutex lock, to prevent other incoming events
+        # from processing at the same time
+        async with self.process_event_mutex:
+            for event in events:
+                if not isinstance(event, Frame):
+                    # Event is not a frame. Ignore it.
+                    continue
+                if event.opcode == Opcode.PONG:
+                    await self.process_pong(event)
+                elif event.opcode == Opcode.CLOSE:
+                    if self.recv_cancel:
+                        self.recv_cancel.cancel()
+                else:
+                    await self.assembler.put(event)
+
+    async def process_pong(self, frame: Frame) -> None:
+        if frame.data in self.pings:
+            # Acknowledge all pings up to the one matching this pong.
+            ping_ids = []
+            for ping_id, ping in self.pings.items():
+                ping_ids.append(ping_id)
+                if not ping.done():
+                    ping.set_result(None)
+                if ping_id == frame.data:
+                    break
+            else:  # noqa
+                raise ServerError("ping_id is not in self.pings")
+            # Remove acknowledged pings from self.pings.
+            for ping_id in ping_ids:
+                del self.pings[ping_id]
+
+    async def keepalive_ping(self) -> None:
+        """
+        Send a Ping frame and wait for a Pong frame at regular intervals.
+        This coroutine exits when the connection terminates and one of the
+        following happens:
+        - :meth:`ping` raises :exc:`ConnectionClosed`, or
+        - :meth:`auto_close_connection` cancels :attr:`keepalive_ping_task`.
+        """
+        if self.ping_interval is None:
+            return
+
+        try:
+            while True:
+                await asyncio.sleep(self.ping_interval)
+
+                # ping() raises CancelledError if the connection is closed,
+                # when auto_close_connection() cancels keepalive_ping_task.
+
+                # ping() raises ConnectionClosed if the connection is lost,
+                # when connection_lost() calls abort_pings().
+
+                ping_waiter = await self.ping()
+
+                if self.ping_timeout is not None:
+                    try:
+                        await asyncio.wait_for(ping_waiter, self.ping_timeout)
+                    except asyncio.TimeoutError:
+                        websockets_logger.warning(
+                            "Websocket timed out waiting for pong"
+                        )
+                        self.fail_connection(1011)
+                        break
+        except asyncio.CancelledError:
+            # It is expected for this task to be cancelled during during
+            # normal operation, when the connection is closed.
+            websockets_logger.debug(
+                "Websocket keepalive ping task was cancelled."
+            )
+        except (ConnectionClosed, WebsocketClosed):
+            websockets_logger.debug(
+                "Websocket closed. Keepalive ping task exiting."
+            )
+        except Exception as e:
+            websockets_logger.warning(
+                "Unexpected exception in websocket keepalive ping task."
+            )
+            websockets_logger.debug(str(e))
+
+    def _force_disconnect(self) -> bool:
+        """
+        Internal method used by end_connection and fail_connection
+        only when the graceful auto-closer cannot be used
+        """
+        if self.auto_closer_task and not self.auto_closer_task.done():
+            self.auto_closer_task.cancel()
+        if self.data_finished_fut and not self.data_finished_fut.done():
+            self.data_finished_fut.cancel()
+            self.data_finished_fut = None
+        if self.keepalive_ping_task and not self.keepalive_ping_task.done():
+            self.keepalive_ping_task.cancel()
+            self.keepalive_ping_task = None
+        if self.loop and self.io_proto and self.io_proto.transport:
+            self.io_proto.transport.close()
+            self.loop.call_later(
+                self.close_timeout, self.io_proto.transport.abort
+            )
+        # We were never open, or already closed
+        return True
+
+    def fail_connection(self, code: int = 1006, reason: str = "") -> bool:
+        """
+        Fail the WebSocket Connection
+        This requires:
+        1. Stopping all processing of incoming data, which means cancelling
+           pausing the underlying io protocol. The close code will be 1006
+           unless a close frame was received earlier.
+        2. Sending a close frame with an appropriate code if the opening
+           handshake succeeded and the other side is likely to process it.
+        3. Closing the connection. :meth:`auto_close_connection` takes care
+           of this.
+        (The specification describes these steps in the opposite order.)
+        """
+        if self.io_proto and self.io_proto.transport:
+            # Stop new data coming in
+            # In Python Version 3.7: pause_reading is idempotent
+            # ut can be called when the transport is already paused or closed
+            self.io_proto.transport.pause_reading()
+
+            # Keeping fail_connection() synchronous guarantees it can't
+            # get stuck and simplifies the implementation of the callers.
+            # Not draining the write buffer is acceptable in this context.
+
+            # clear the send buffer
+            _ = self.ws_proto.data_to_send()
+            # If we're not already CLOSED or CLOSING, then send the close.
+            if self.ws_proto.state is OPEN:
+                if code in (1000, 1001):
+                    self.ws_proto.send_close(code, reason)
+                else:
+                    self.ws_proto.fail(code, reason)
+                try:
+                    data_to_send = self.ws_proto.data_to_send()
+                    while (
+                        len(data_to_send)
+                        and self.io_proto
+                        and self.io_proto.transport
+                    ):
+                        frame_data = data_to_send.pop(0)
+                        self.io_proto.transport.write(frame_data)
+                except Exception:
+                    # sending close frames may fail if the
+                    # transport closes during this period
+                    ...
+        if code == 1006:
+            # Special case: 1006 consider the transport already closed
+            self.ws_proto.state = CLOSED
+        if self.data_finished_fut and not self.data_finished_fut.done():
+            # We have a graceful auto-closer. Use it to close the connection.
+            self.data_finished_fut.cancel()
+            self.data_finished_fut = None
+        if (not self.auto_closer_task) or self.auto_closer_task.done():
+            return self._force_disconnect()
+        return False
+
+    def end_connection(self, code=1000, reason=""):
+        # This is like slightly more graceful form of fail_connection
+        # Use this instead of close() when you need an immediate
+        # close and cannot await websocket.close() handshake.
+
+        if code == 1006 or not self.io_proto or not self.io_proto.transport:
+            return self.fail_connection(code, reason)
+
+        # Stop new data coming in
+        # In Python Version 3.7: pause_reading is idempotent
+        # i.e. it can be called when the transport is already paused or closed.
+        self.io_proto.transport.pause_reading()
+        if self.ws_proto.state == OPEN:
+            data_to_send = self.ws_proto.data_to_send()
+            self.ws_proto.send_close(code, reason)
+            data_to_send.extend(self.ws_proto.data_to_send())
+            try:
+                while (
+                    len(data_to_send)
+                    and self.io_proto
+                    and self.io_proto.transport
+                ):
+                    frame_data = data_to_send.pop(0)
+                    self.io_proto.transport.write(frame_data)
+            except Exception:
+                # sending close frames may fail if the
+                # transport closes during this period
+                # But that doesn't matter at this point
+                ...
+        if self.data_finished_fut and not self.data_finished_fut.done():
+            # We have the ability to signal the auto-closer
+            # try to trigger it to auto-close the connection
+            self.data_finished_fut.cancel()
+            self.data_finished_fut = None
+        if (not self.auto_closer_task) or self.auto_closer_task.done():
+            # Auto-closer is not running, do force disconnect
+            return self._force_disconnect()
+        return False
+
+    async def auto_close_connection(self) -> None:
+        """
+        Close the WebSocket Connection
+        When the opening handshake succeeds, :meth:`connection_open` starts
+        this coroutine in a task. It waits for the data transfer phase to
+        complete then it closes the TCP connection cleanly.
+        When the opening handshake fails, :meth:`fail_connection` does the
+        same. There's no data transfer phase in that case.
+        """
+        try:
+            # Wait for the data transfer phase to complete.
+            if self.data_finished_fut:
+                try:
+                    await self.data_finished_fut
+                    websockets_logger.debug(
+                        "Websocket task finished. Closing the connection."
+                    )
+                except asyncio.CancelledError:
+                    # Cancelled error is called when data phase is cancelled
+                    # if an error occurred or the client closed the connection
+                    websockets_logger.debug(
+                        "Websocket handler cancelled. Closing the connection."
+                    )
+
+            # Cancel the keepalive ping task.
+            if self.keepalive_ping_task:
+                self.keepalive_ping_task.cancel()
+                self.keepalive_ping_task = None
+
+            # Half-close the TCP connection if possible (when there's no TLS).
+            if (
+                self.io_proto
+                and self.io_proto.transport
+                and self.io_proto.transport.can_write_eof()
+            ):
+                websockets_logger.debug(
+                    "Websocket half-closing TCP connection"
+                )
+                try:
+                    self.io_proto.transport.write_eof()
+                except RuntimeError:
+                    ...
+                if self.connection_lost_waiter:
+                    if await self.wait_for_connection_lost(timeout=0):
+                        return
+        except asyncio.CancelledError:
+            ...
+        except BaseException:
+            websockets_logger.exception("Error closing websocket connection")
+        finally:
+            # Does this still exist?
+            if self.keepalive_ping_task:
+                self.keepalive_ping_task.cancel()
+                self.keepalive_ping_task = None
+            # The try/finally ensures that the transport never remains open,
+            # even if this coroutine is cancelled (for example).
+            if (not self.io_proto) or (not self.io_proto.transport):
+                # we were never open, or done. Can't do any finalization.
+                return
+            elif (
+                self.connection_lost_waiter
+                and self.connection_lost_waiter.done()
+            ):
+                # connection confirmed closed already, proceed to abort waiter
+                ...
+            elif self.io_proto.transport.is_closing():
+                # Connection is already closing (due to half-close above)
+                # proceed to abort waiter
+                ...
+            else:
+                self.io_proto.transport.close()
+            if not self.connection_lost_waiter:
+                # Our connection monitor task isn't running.
+                try:
+                    await asyncio.sleep(self.close_timeout)
+                except asyncio.CancelledError:
+                    ...
+                if self.io_proto and self.io_proto.transport:
+                    self.io_proto.transport.abort()
+            else:
+                if await self.wait_for_connection_lost(
+                    timeout=self.close_timeout
+                ):
+                    # Connection aborted before the timeout expired.
+                    return
+                websockets_logger.warning(
+                    "Timeout waiting for TCP connection to close. Aborting"
+                )
+                if self.io_proto and self.io_proto.transport:
+                    self.io_proto.transport.abort()
+
+    def abort_pings(self) -> None:
+        """
+        Raise ConnectionClosed in pending keepalive pings.
+        They'll never receive a pong once the connection is closed.
+        """
+        if self.ws_proto.state is not CLOSED:
+            raise ServerError(
+                "Webscoket about_pings should only be called "
+                "after connection state is changed to CLOSED"
+            )
+
+        for ping in self.pings.values():
+            ping.set_exception(ConnectionClosedError(None, None))
+            # If the exception is never retrieved, it will be logged when ping
+            # is garbage-collected. This is confusing for users.
+            # Given that ping is done (with an exception), canceling it does
+            # nothing, but it prevents logging the exception.
+            ping.cancel()
+
+    async def close(self, code: int = 1000, reason: str = "") -> None:
+        """
+        Perform the closing handshake.
+        This is a websocket-protocol level close.
+        :meth:`close` waits for the other end to complete the handshake and
+        for the TCP connection to terminate.
+        :meth:`close` is idempotent: it doesn't do anything once the
+        connection is closed.
+        :param code: WebSocket close code
+        :param reason: WebSocket close reason
+        """
+        if code == 1006:
+            self.fail_connection(code, reason)
+            return
+        async with self.conn_mutex:
+            if self.ws_proto.state is OPEN:
+                self.ws_proto.send_close(code, reason)
+                data_to_send = self.ws_proto.data_to_send()
+                await self.send_data(data_to_send)
+
+    async def recv(self, timeout: float | None = None) -> Data | None:
+        """
+        Receive the next message.
+        Return a :class:`str` for a text frame and :class:`bytes` for a binary
+        frame.
+        When the end of the message stream is reached, :meth:`recv` raises
+        :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it
+        raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal
+        connection closure and
+        :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol
+        error or a network failure.
+        If ``timeout`` is ``None``, block until a message is received. Else,
+        if no message is received within ``timeout`` seconds, return ``None``.
+        Set ``timeout`` to ``0`` to check if a message was already received.
+        :raises ~websockets.exceptions.ConnectionClosed: when the
+            connection is closed
+        :raises asyncio.CancelledError: if the websocket closes while waiting
+        :raises ServerError: if two tasks call :meth:`recv` or
+            :meth:`recv_streaming` concurrently
+        """
+
+        if self.recv_lock.locked():
+            raise ServerError(
+                "cannot call recv while another task is "
+                "already waiting for the next message"
+            )
+        await self.recv_lock.acquire()
+        if self.ws_proto.state is CLOSED:
+            self.recv_lock.release()
+            raise WebsocketClosed(
+                "Cannot receive from websocket interface after it is closed."
+            )
+        assembler_get: asyncio.Task | None = None
+        try:
+            self.recv_cancel = asyncio.Future()
+            assembler_get = asyncio.create_task(self.assembler.get(timeout))
+            tasks = (self.recv_cancel, assembler_get)
+            done, pending = await asyncio.wait(
+                tasks,
+                return_when=asyncio.FIRST_COMPLETED,
+            )
+            done_task = next(iter(done))
+            if done_task is self.recv_cancel:
+                # recv was cancelled
+                for p in pending:
+                    p.cancel()
+                raise asyncio.CancelledError()
+            else:
+                self.recv_cancel.cancel()
+                return done_task.result()
+        except asyncio.CancelledError:
+            # recv was cancelled
+            if assembler_get:
+                assembler_get.cancel()
+            raise
+        finally:
+            self.recv_cancel = None
+            self.recv_lock.release()
+
+    async def recv_burst(self, max_recv=256) -> Sequence[Data]:
+        """
+        Receive the messages which have arrived since last checking.
+        Return a :class:`list` containing :class:`str` for a text frame
+        and :class:`bytes` for a binary frame.
+        When the end of the message stream is reached, :meth:`recv_burst`
+        raises :exc:`~websockets.exceptions.ConnectionClosed`. Specifically,
+        it raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a
+        normal connection closure and
+        :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol
+        error or a network failure.
+        :raises ~websockets.exceptions.ConnectionClosed: when the
+            connection is closed
+        :raises ServerError: if two tasks call :meth:`recv_burst` or
+            :meth:`recv_streaming` concurrently
+        """
+
+        if self.recv_lock.locked():
+            raise ServerError(
+                "cannot call recv_burst while another task is already waiting "
+                "for the next message"
+            )
+        await self.recv_lock.acquire()
+        if self.ws_proto.state is CLOSED:
+            self.recv_lock.release()
+            raise WebsocketClosed(
+                "Cannot receive from websocket interface after it is closed."
+            )
+        messages = []
+        assembler_get: asyncio.Task | None = None
+        try:
+            # Prevent pausing the transport when we're
+            # receiving a burst of messages
+            self.can_pause = False
+            self.recv_cancel = asyncio.Future()
+            while True:
+                assembler_get = asyncio.create_task(self.assembler.get(0))
+                tasks = (self.recv_cancel, assembler_get)
+                done, pending = await asyncio.wait(
+                    tasks,
+                    return_when=asyncio.FIRST_COMPLETED,
+                )
+                done_task = next(iter(done))
+                if done_task is self.recv_cancel:
+                    # recv_burst was cancelled
+                    for p in pending:
+                        p.cancel()
+                    raise asyncio.CancelledError()
+                m = done_task.result()
+                if m is None:
+                    # None left in the burst. This is good!
+                    break
+                messages.append(m)
+                if len(messages) >= max_recv:
+                    # Too much data in the pipe. Hit our burst limit.
+                    break
+                # Allow an eventloop iteration for the
+                # next message to pass into the Assembler
+                await asyncio.sleep(0)
+            self.recv_cancel.cancel()
+        except asyncio.CancelledError:
+            # recv_burst was cancelled
+            if assembler_get:
+                assembler_get.cancel()
+            raise
+        finally:
+            self.recv_cancel = None
+            self.can_pause = True
+            self.recv_lock.release()
+        return messages
+
+    async def recv_streaming(self) -> AsyncIterator[Data]:
+        """
+        Receive the next message frame by frame.
+        Return an iterator of :class:`str` for a text frame and :class:`bytes`
+        for a binary frame. The iterator should be exhausted, or else the
+        connection will become unusable.
+        With the exception of the return value, :meth:`recv_streaming` behaves
+        like :meth:`recv`.
+        """
+        if self.recv_lock.locked():
+            raise ServerError(
+                "Cannot call recv_streaming while another task "
+                "is already waiting for the next message"
+            )
+        await self.recv_lock.acquire()
+        if self.ws_proto.state is CLOSED:
+            self.recv_lock.release()
+            raise WebsocketClosed(
+                "Cannot receive from websocket interface after it is closed."
+            )
+        try:
+            cancelled = False
+            self.recv_cancel = asyncio.Future()
+            self.can_pause = False
+            async for m in self.assembler.get_iter():
+                if self.recv_cancel.done():
+                    cancelled = True
+                    break
+                yield m
+            if cancelled:
+                raise asyncio.CancelledError()
+        finally:
+            self.can_pause = True
+            self.recv_cancel = None
+            self.recv_lock.release()
+
+    async def send(self, message: Data | Iterable[Data]) -> None:
+        """
+        Send a message.
+        A string (:class:`str`) is sent as a `Text frame`_. A bytestring or
+        bytes-like object (:class:`bytes`, :class:`bytearray`, or
+        :class:`memoryview`) is sent as a `Binary frame`_.
+        .. _Text frame: https://tools.ietf.org/html/rfc6455#section-5.6
+        .. _Binary frame: https://tools.ietf.org/html/rfc6455#section-5.6
+        :meth:`send` also accepts an iterable of strings, bytestrings, or
+        bytes-like objects. In that case the message is fragmented. Each item
+        is treated as a message fragment and sent in its own frame. All items
+        must be of the same type, or else :meth:`send` will raise a
+        :exc:`TypeError` and the connection will be closed.
+        :meth:`send` rejects dict-like objects because this is often an error.
+        If you wish to send the keys of a dict-like object as fragments, call
+        its :meth:`~dict.keys` method and pass the result to :meth:`send`.
+        :raises TypeError: for unsupported inputs
+        """
+        async with self.conn_mutex:
+            if self.ws_proto.state in (CLOSED, CLOSING):
+                raise WebsocketClosed(
+                    "Cannot write to websocket interface after it is closed."
+                )
+            if (not self.data_finished_fut) or self.data_finished_fut.done():
+                raise ServerError(
+                    "Cannot write to websocket interface after it is finished."
+                )
+
+            # Unfragmented message -- this case must be handled first because
+            # strings and bytes-like objects are iterable.
+
+            if isinstance(message, str):
+                self.ws_proto.send_text(message.encode("utf-8"))
+                await self.send_data(self.ws_proto.data_to_send())
+
+            elif isinstance(message, (bytes, bytearray, memoryview)):
+                self.ws_proto.send_binary(message)
+                await self.send_data(self.ws_proto.data_to_send())
+
+            elif isinstance(message, Mapping):
+                # Catch a common mistake -- passing a dict to send().
+                raise TypeError("data is a dict-like object")
+
+            elif isinstance(message, Iterable):
+                # Fragmented message -- regular iterator.
+                raise NotImplementedError(
+                    "Fragmented websocket messages are not supported."
+                )
+            else:
+                raise TypeError("Websocket data must be bytes, str.")
+
+    async def ping(self, data: Data | None = None) -> asyncio.Future:
+        """
+        Send a ping.
+        Return an :class:`~asyncio.Future` that will be resolved when the
+        corresponding pong is received. You can ignore it if you don't intend
+        to wait.
+        A ping may serve as a keepalive or as a check that the remote endpoint
+        received all messages up to this point::
+            await pong_event = ws.ping()
+            await pong_event # only if you want to wait for the pong
+        By default, the ping contains four random bytes. This payload may be
+        overridden with the optional ``data`` argument which must be a string
+        (which will be encoded to UTF-8) or a bytes-like object.
+        """
+        async with self.conn_mutex:
+            if self.ws_proto.state in (CLOSED, CLOSING):
+                raise WebsocketClosed(
+                    "Cannot send a ping when the websocket interface "
+                    "is closed."
+                )
+            if (not self.io_proto) or (not self.io_proto.loop):
+                raise ServerError(
+                    "Cannot send a ping when the websocket has no I/O "
+                    "protocol attached."
+                )
+            if data is not None:
+                if isinstance(data, str):
+                    data = data.encode("utf-8")
+                elif isinstance(data, (bytearray, memoryview)):
+                    data = bytes(data)
+
+            # Protect against duplicates if a payload is explicitly set.
+            if data in self.pings:
+                raise ValueError(
+                    "already waiting for a pong with the same data"
+                )
+
+            # Generate a unique random payload otherwise.
+            while data is None or data in self.pings:
+                data = secrets.token_bytes(4)
+
+            self.pings[data] = self.io_proto.loop.create_future()
+
+            self.ws_proto.send_ping(data)
+            await self.send_data(self.ws_proto.data_to_send())
+
+            return asyncio.shield(self.pings[data])
+
+    async def pong(self, data: Data = b"") -> None:
+        """
+        Send a pong.
+        An unsolicited pong may serve as a unidirectional heartbeat.
+        The payload may be set with the optional ``data`` argument which must
+        be a string (which will be encoded to UTF-8) or a bytes-like object.
+        """
+        async with self.conn_mutex:
+            if self.ws_proto.state in (CLOSED, CLOSING):
+                # Cannot send pong after transport is shutting down
+                return
+            if isinstance(data, str):
+                data = data.encode("utf-8")
+            elif isinstance(data, (bytearray, memoryview)):
+                data = bytes(data)
+            self.ws_proto.send_pong(data)
+            await self.send_data(self.ws_proto.data_to_send())
+
+    async def send_data(self, data_to_send):
+        for data in data_to_send:
+            if data:
+                await self.io_proto.send(data)
+            else:
+                # Send an EOF - We don't actually send it,
+                # just trigger to autoclose the connection
+                if (
+                    self.auto_closer_task
+                    and not self.auto_closer_task.done()
+                    and self.data_finished_fut
+                    and not self.data_finished_fut.done()
+                ):
+                    # Auto-close the connection
+                    self.data_finished_fut.set_result(None)
+                else:
+                    # This will fail the connection appropriately
+                    SanicProtocol.close(self.io_proto, timeout=1.0)
+
+    async def async_data_received(self, data_to_send, events_to_process):
+        if self.ws_proto.state in (OPEN, CLOSING) and len(data_to_send) > 0:
+            # receiving data can generate data to send (eg, pong for a ping)
+            # send connection.data_to_send()
+            await self.send_data(data_to_send)
+        if len(events_to_process) > 0:
+            await self.process_events(events_to_process)
+
+    def data_received(self, data):
+        self.ws_proto.receive_data(data)
+        data_to_send = self.ws_proto.data_to_send()
+        events_to_process = self.ws_proto.events_received()
+        if len(data_to_send) > 0 or len(events_to_process) > 0:
+            asyncio.create_task(
+                self.async_data_received(data_to_send, events_to_process)
+            )
+
+    async def async_eof_received(self, data_to_send, events_to_process):
+        # receiving EOF can generate data to send
+        # send connection.data_to_send()
+        if self.ws_proto.state in (OPEN, CLOSING) and len(data_to_send) > 0:
+            await self.send_data(data_to_send)
+        if len(events_to_process) > 0:
+            await self.process_events(events_to_process)
+        if self.recv_cancel:
+            self.recv_cancel.cancel()
+        if (
+            self.auto_closer_task
+            and not self.auto_closer_task.done()
+            and self.data_finished_fut
+            and not self.data_finished_fut.done()
+        ):
+            # Auto-close the connection
+            self.data_finished_fut.set_result(None)
+            # Cancel the running handler if its waiting
+        else:
+            # This will fail the connection appropriately
+            SanicProtocol.close(self.io_proto, timeout=1.0)
+
+    def eof_received(self) -> bool | None:
+        self.ws_proto.receive_eof()
+        data_to_send = self.ws_proto.data_to_send()
+        events_to_process = self.ws_proto.events_received()
+        asyncio.create_task(
+            self.async_eof_received(data_to_send, events_to_process)
+        )
+        return False
+
+    def connection_lost(self, exc):
+        """
+        The WebSocket Connection is Closed.
+        """
+        if not self.ws_proto.state == CLOSED:
+            # signal to the websocket connection handler
+            # we've lost the connection
+            self.ws_proto.fail(code=1006)
+            self.ws_proto.state = CLOSED
+
+        self.abort_pings()
+        if self.connection_lost_waiter:
+            self.connection_lost_waiter.set_result(None)
+
+    async def __aiter__(self):
+        try:
+            while True:
+                yield await self.recv()
+        except ConnectionClosedOK:
+            return
diff --git a/.venv/lib/python3.12/site-packages/sanic/signals.py b/.venv/lib/python3.12/site-packages/sanic/signals.py
new file mode 100644
index 0000000..52cebff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/signals.py
@@ -0,0 +1,420 @@
+from __future__ import annotations
+
+import asyncio
+
+from collections import deque
+from dataclasses import dataclass
+from enum import Enum
+from inspect import isawaitable
+from typing import Any, cast
+
+from sanic_routing import BaseRouter, Route, RouteGroup
+from sanic_routing.exceptions import NotFound
+from sanic_routing.utils import path_to_parts
+
+from sanic.exceptions import InvalidSignal
+from sanic.log import error_logger, logger
+from sanic.models.handler_types import SignalHandler
+
+
+class Event(Enum):
+    """Event names for the SignalRouter"""
+
+    SERVER_EXCEPTION_REPORT = "server.exception.report"
+    SERVER_INIT_AFTER = "server.init.after"
+    SERVER_INIT_BEFORE = "server.init.before"
+    SERVER_SHUTDOWN_AFTER = "server.shutdown.after"
+    SERVER_SHUTDOWN_BEFORE = "server.shutdown.before"
+    HTTP_LIFECYCLE_BEGIN = "http.lifecycle.begin"
+    HTTP_LIFECYCLE_COMPLETE = "http.lifecycle.complete"
+    HTTP_LIFECYCLE_EXCEPTION = "http.lifecycle.exception"
+    HTTP_LIFECYCLE_HANDLE = "http.lifecycle.handle"
+    HTTP_LIFECYCLE_READ_BODY = "http.lifecycle.read_body"
+    HTTP_LIFECYCLE_READ_HEAD = "http.lifecycle.read_head"
+    HTTP_LIFECYCLE_REQUEST = "http.lifecycle.request"
+    HTTP_LIFECYCLE_RESPONSE = "http.lifecycle.response"
+    HTTP_ROUTING_AFTER = "http.routing.after"
+    HTTP_ROUTING_BEFORE = "http.routing.before"
+    HTTP_HANDLER_AFTER = "http.handler.after"
+    HTTP_HANDLER_BEFORE = "http.handler.before"
+    HTTP_LIFECYCLE_SEND = "http.lifecycle.send"
+    HTTP_MIDDLEWARE_AFTER = "http.middleware.after"
+    HTTP_MIDDLEWARE_BEFORE = "http.middleware.before"
+    WEBSOCKET_HANDLER_AFTER = "websocket.handler.after"
+    WEBSOCKET_HANDLER_BEFORE = "websocket.handler.before"
+    WEBSOCKET_HANDLER_EXCEPTION = "websocket.handler.exception"
+
+
+RESERVED_NAMESPACES = {
+    "server": (
+        Event.SERVER_EXCEPTION_REPORT.value,
+        Event.SERVER_INIT_AFTER.value,
+        Event.SERVER_INIT_BEFORE.value,
+        Event.SERVER_SHUTDOWN_AFTER.value,
+        Event.SERVER_SHUTDOWN_BEFORE.value,
+    ),
+    "http": (
+        Event.HTTP_LIFECYCLE_BEGIN.value,
+        Event.HTTP_LIFECYCLE_COMPLETE.value,
+        Event.HTTP_LIFECYCLE_EXCEPTION.value,
+        Event.HTTP_LIFECYCLE_HANDLE.value,
+        Event.HTTP_LIFECYCLE_READ_BODY.value,
+        Event.HTTP_LIFECYCLE_READ_HEAD.value,
+        Event.HTTP_LIFECYCLE_REQUEST.value,
+        Event.HTTP_LIFECYCLE_RESPONSE.value,
+        Event.HTTP_ROUTING_AFTER.value,
+        Event.HTTP_ROUTING_BEFORE.value,
+        Event.HTTP_HANDLER_AFTER.value,
+        Event.HTTP_HANDLER_BEFORE.value,
+        Event.HTTP_LIFECYCLE_SEND.value,
+        Event.HTTP_MIDDLEWARE_AFTER.value,
+        Event.HTTP_MIDDLEWARE_BEFORE.value,
+    ),
+    "websocket": {
+        Event.WEBSOCKET_HANDLER_AFTER.value,
+        Event.WEBSOCKET_HANDLER_BEFORE.value,
+        Event.WEBSOCKET_HANDLER_EXCEPTION.value,
+    },
+}
+
+GENERIC_SIGNAL_FORMAT = "__generic__.__signal__.%s"
+
+
+def _blank(): ...
+
+
+class Signal(Route):
+    """A `Route` that is used to dispatch signals to handlers"""
+
+
+@dataclass
+class SignalWaiter:
+    """A record representing a future waiting for a signal"""
+
+    signal: Signal
+    event_definition: str
+    trigger: str = ""
+    requirements: dict[str, str] | None = None
+    exclusive: bool = True
+
+    future: asyncio.Future | None = None
+
+    async def wait(self):
+        """Block until the signal is next dispatched.
+
+        Return the context of the signal dispatch, if any.
+        """
+        loop = asyncio.get_running_loop()
+        self.future = loop.create_future()
+        self.signal.ctx.waiters.append(self)
+        try:
+            return await self.future
+        finally:
+            self.signal.ctx.waiters.remove(self)
+
+    def matches(self, event, condition):
+        return (
+            (condition is None and not self.exclusive)
+            or (condition is None and not self.requirements)
+            or condition == self.requirements
+        ) and (self.trigger or event == self.event_definition)
+
+
+class SignalGroup(RouteGroup):
+    """A `RouteGroup` that is used to dispatch signals to handlers"""
+
+
+class SignalRouter(BaseRouter):
+    """A `BaseRouter` that is used to dispatch signals to handlers"""
+
+    def __init__(self) -> None:
+        super().__init__(
+            delimiter=".",
+            route_class=Signal,
+            group_class=SignalGroup,
+            stacking=True,
+        )
+        self.allow_fail_builtin = True
+        self.ctx.loop = None
+
+    @staticmethod
+    def format_event(event: str | Enum) -> str:
+        """Ensure event strings in proper format
+
+        Args:
+            event (str): event string
+
+        Returns:
+            str: formatted event string
+        """
+        if isinstance(event, Enum):
+            event = str(event.value)
+        if "." not in event:
+            event = GENERIC_SIGNAL_FORMAT % event
+        return event
+
+    def get(  # type: ignore
+        self,
+        event: str | Enum,
+        condition: dict[str, str] | None = None,
+    ):
+        """Get the handlers for a signal
+
+        Args:
+            event (str): The event to get the handlers for
+            condition (Optional[Dict[str, str]], optional): A dictionary of conditions to match against the handlers. Defaults to `None`.
+
+        Returns:
+            Tuple[SignalGroup, List[SignalHandler], Dict[str, Any]]: A tuple of the `SignalGroup` that matched, a list of the handlers that matched, and a dictionary of the params that matched
+
+        Raises:
+            NotFound: If no handlers are found
+        """  # noqa: E501
+        event = self.format_event(event)
+        extra = condition or {}
+        try:
+            group, param_basket = self.find_route(
+                f".{event}",
+                self.DEFAULT_METHOD,
+                self,
+                {"__params__": {}, "__matches__": {}},
+                extra=extra,
+            )
+        except NotFound:
+            message = "Could not find signal %s"
+            terms: list[str | dict[str, str] | None] = [event]
+            if extra:
+                message += " with %s"
+                terms.append(extra)
+            raise NotFound(message % tuple(terms))
+
+        # Regex routes evaluate and can extract params directly. They are set
+        # on param_basket["__params__"]
+        params = param_basket["__params__"]
+        if not params:
+            # If param_basket["__params__"] does not exist, we might have
+            # param_basket["__matches__"], which are indexed based matches
+            # on path segments. They should already be cast types.
+            params = {
+                param.name: param_basket["__matches__"][idx]
+                for idx, param in group.params.items()
+            }
+
+        return group, [route.handler for route in group], params
+
+    async def _dispatch(
+        self,
+        event: str,
+        context: dict[str, Any] | None = None,
+        condition: dict[str, str] | None = None,
+        fail_not_found: bool = True,
+        reverse: bool = False,
+    ) -> Any:
+        event = self.format_event(event)
+        try:
+            group, handlers, params = self.get(event, condition=condition)
+        except NotFound as e:
+            is_reserved = event.split(".", 1)[0] in RESERVED_NAMESPACES
+            if fail_not_found and (not is_reserved or self.allow_fail_builtin):
+                raise e
+            else:
+                if self.ctx.app.debug and self.ctx.app.state.verbosity >= 1:
+                    error_logger.warning(str(e))
+                return None
+
+        if context:
+            params.update(context)
+        params.pop("__trigger__", None)
+
+        signals = group.routes
+        if not reverse:
+            signals = signals[::-1]
+        try:
+            for signal in signals:
+                for waiter in signal.ctx.waiters:
+                    if waiter.matches(event, condition):
+                        waiter.future.set_result(dict(params))
+
+            for signal in signals:
+                requirements = signal.extra.requirements
+                if (
+                    (condition is None and signal.ctx.exclusive is False)
+                    or (condition is None and not requirements)
+                    or (condition == requirements)
+                ) and (signal.ctx.trigger or event == signal.ctx.definition):
+                    maybe_coroutine = signal.handler(**params)
+                    if isawaitable(maybe_coroutine):
+                        retval = await maybe_coroutine
+                        if retval:
+                            return retval
+                    elif maybe_coroutine:
+                        return maybe_coroutine
+            return None
+        except Exception as e:
+            if self.ctx.app.debug and self.ctx.app.state.verbosity >= 1:
+                error_logger.exception(e)
+
+            if event != Event.SERVER_EXCEPTION_REPORT.value:
+                await self.dispatch(
+                    Event.SERVER_EXCEPTION_REPORT.value,
+                    context={"exception": e},
+                )
+                setattr(e, "__dispatched__", True)
+            raise e
+
+    async def dispatch(
+        self,
+        event: str | Enum,
+        *,
+        context: dict[str, Any] | None = None,
+        condition: dict[str, str] | None = None,
+        fail_not_found: bool = True,
+        inline: bool = False,
+        reverse: bool = False,
+    ) -> asyncio.Task | Any:
+        """Dispatch a signal to all handlers that match the event
+
+        Args:
+            event (str): The event to dispatch
+            context (Optional[Dict[str, Any]], optional): A dictionary of context to pass to the handlers. Defaults to `None`.
+            condition (Optional[Dict[str, str]], optional): A dictionary of conditions to match against the handlers. Defaults to `None`.
+            fail_not_found (bool, optional): Whether to raise an exception if no handlers are found. Defaults to `True`.
+            inline (bool, optional): Whether to run the handlers inline. An inline run means it will return the value of the signal handler. When `False` (which is the default) the signal handler will run in a background task. Defaults to `False`.
+            reverse (bool, optional): Whether to run the handlers in reverse order. Defaults to `False`.
+
+        Returns:
+            Union[asyncio.Task, Any]: If `inline` is `True` then the return value of the signal handler will be returned. If `inline` is `False` then an `asyncio.Task` will be returned.
+
+        Raises:
+            RuntimeError: If the signal is dispatched outside of an event loop
+        """  # noqa: E501
+
+        event = self.format_event(event)
+        dispatch = self._dispatch(
+            event,
+            context=context,
+            condition=condition,
+            fail_not_found=fail_not_found and inline,
+            reverse=reverse,
+        )
+        logger.debug(f"Dispatching signal: {event}", extra={"verbosity": 1})
+
+        if inline:
+            return await dispatch
+
+        task = asyncio.get_running_loop().create_task(dispatch)
+        await asyncio.sleep(0)
+        return task
+
+    def get_waiter(
+        self,
+        event: str | Enum,
+        condition: dict[str, Any] | None = None,
+        exclusive: bool = True,
+    ) -> SignalWaiter | None:
+        event_definition = self.format_event(event)
+        name, trigger, _ = self._get_event_parts(event_definition)
+        signal = cast(Signal, self.name_index.get(name))
+        if not signal:
+            return None
+
+        if event_definition.endswith(".*") and not trigger:
+            trigger = "*"
+        return SignalWaiter(
+            signal=signal,
+            event_definition=event_definition,
+            trigger=trigger,
+            requirements=condition,
+            exclusive=bool(exclusive),
+        )
+
+    def _get_event_parts(self, event: str) -> tuple[str, str, str]:
+        parts = self._build_event_parts(event)
+        if parts[2].startswith("<"):
+            name = ".".join([*parts[:-1], "*"])
+            trigger = self._clean_trigger(parts[2])
+        else:
+            name = event
+            trigger = ""
+
+        if not trigger:
+            event = ".".join([*parts[:2], "<__trigger__>"])
+
+        return name, trigger, event
+
+    def add(  # type: ignore
+        self,
+        handler: SignalHandler,
+        event: str | Enum,
+        condition: dict[str, Any] | None = None,
+        exclusive: bool = True,
+        *,
+        priority: int = 0,
+    ) -> Signal:
+        event_definition = self.format_event(event)
+        name, trigger, event_string = self._get_event_parts(event_definition)
+
+        signal = super().add(
+            event_string,
+            handler,
+            name=name,
+            append=True,
+            priority=priority,
+        )  # type: ignore
+
+        signal.ctx.exclusive = exclusive
+        signal.ctx.trigger = trigger
+        signal.ctx.definition = event_definition
+        signal.extra.requirements = condition
+
+        return cast(Signal, signal)
+
+    def finalize(self, do_compile: bool = True, do_optimize: bool = False):
+        """Finalize the router and compile the routes
+
+        Args:
+            do_compile (bool, optional): Whether to compile the routes. Defaults to `True`.
+            do_optimize (bool, optional): Whether to optimize the routes. Defaults to `False`.
+
+        Returns:
+            SignalRouter: The router
+
+        Raises:
+            RuntimeError: If the router is finalized outside of an event loop
+        """  # noqa: E501
+        self.add(_blank, "sanic.__signal__.__init__")
+
+        try:
+            self.ctx.loop = asyncio.get_running_loop()
+        except RuntimeError:
+            raise RuntimeError("Cannot finalize signals outside of event loop")
+
+        for signal in self.routes:
+            signal.ctx.waiters = deque()
+
+        return super().finalize(do_compile=do_compile, do_optimize=do_optimize)
+
+    def _build_event_parts(self, event: str) -> tuple[str, str, str]:
+        parts = path_to_parts(event, self.delimiter)
+        if (
+            len(parts) != 3
+            or parts[0].startswith("<")
+            or parts[1].startswith("<")
+        ):
+            raise InvalidSignal("Invalid signal event: %s" % event)
+
+        if (
+            parts[0] in RESERVED_NAMESPACES
+            and event not in RESERVED_NAMESPACES[parts[0]]
+            and not (parts[2].startswith("<") and parts[2].endswith(">"))
+        ):
+            raise InvalidSignal(
+                "Cannot declare reserved signal event: %s" % event
+            )
+        return parts
+
+    def _clean_trigger(self, trigger: str) -> str:
+        trigger = trigger[1:-1]
+        if ":" in trigger:
+            trigger, _ = trigger.split(":")
+        return trigger
diff --git a/.venv/lib/python3.12/site-packages/sanic/simple.py b/.venv/lib/python3.12/site-packages/sanic/simple.py
new file mode 100644
index 0000000..523f11a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/simple.py
@@ -0,0 +1,18 @@
+from pathlib import Path
+
+from sanic import Sanic
+from sanic.exceptions import SanicException
+
+
+def create_simple_server(directory: Path):
+    if not directory.is_dir():
+        raise SanicException(
+            "Cannot setup Sanic Simple Server without a path to a directory"
+        )
+
+    app = Sanic("SimpleServer")
+    app.static(
+        "/", directory, name="main", directory_view=True, index="index.html"
+    )
+
+    return app
diff --git a/.venv/lib/python3.12/site-packages/sanic/startup/__init__.py b/.venv/lib/python3.12/site-packages/sanic/startup/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/startup/errors.py b/.venv/lib/python3.12/site-packages/sanic/startup/errors.py
new file mode 100644
index 0000000..c01d49f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/startup/errors.py
@@ -0,0 +1,60 @@
+import errno
+import sys
+
+from typing import Callable
+
+from sanic.exceptions import ServerError
+from sanic.log import error_logger
+from sanic.worker.daemon import DaemonError
+
+
+ExceptionHandler = Callable[[Exception], bool]
+
+
+def maybe_handle_startup_error(exc: Exception) -> None:
+    for handler in EXCEPTION_HANDLERS:
+        if handler(exc):
+            sys.exit(1)
+    raise exc
+
+
+def _handle_os_error(exc: Exception) -> bool:
+    if not isinstance(exc, OSError):
+        return False
+
+    if exc.errno == errno.EADDRINUSE:
+        error_logger.error(
+            "Startup failed: Address already in use. \n\n"
+            "Ensure no other process is using the same address and port, "
+            "or configure the server to use a different port."
+        )
+    else:
+        error_logger.error(
+            "Startup failed due to OS error: %s (errno %s)",
+            exc.strerror,
+            exc.errno,
+        )
+    return True
+
+
+def _handle_server_error(exc: Exception) -> bool:
+    if not isinstance(exc, ServerError):
+        return False
+
+    error_logger.error(f"Startup failed due to server error. {exc}")
+    return True
+
+
+def _handle_daemon_error(exc: Exception) -> bool:
+    if not isinstance(exc, DaemonError):
+        return False
+
+    error_logger.error(f"Daemon error: {exc}")
+    return True
+
+
+EXCEPTION_HANDLERS: tuple[ExceptionHandler, ...] = (
+    _handle_os_error,
+    _handle_server_error,
+    _handle_daemon_error,
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/__init__.py b/.venv/lib/python3.12/site-packages/sanic/touchup/__init__.py
new file mode 100644
index 0000000..6fe208a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/__init__.py
@@ -0,0 +1,8 @@
+from .meta import TouchUpMeta
+from .service import TouchUp
+
+
+__all__ = (
+    "TouchUp",
+    "TouchUpMeta",
+)
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/meta.py b/.venv/lib/python3.12/site-packages/sanic/touchup/meta.py
new file mode 100644
index 0000000..011570d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/meta.py
@@ -0,0 +1,22 @@
+from sanic.base.meta import SanicMeta
+from sanic.exceptions import SanicException
+
+from .service import TouchUp
+
+
+class TouchUpMeta(SanicMeta):
+    def __new__(cls, name, bases, attrs, **kwargs):
+        gen_class = super().__new__(cls, name, bases, attrs, **kwargs)
+
+        methods = attrs.get("__touchup__")
+        attrs["__touched__"] = False
+        if methods:
+            for method in methods:
+                if method not in attrs:
+                    raise SanicException(
+                        "Cannot perform touchup on non-existent method: "
+                        f"{name}.{method}"
+                    )
+                TouchUp.register(gen_class, method)
+
+        return gen_class
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/__init__.py b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/__init__.py
new file mode 100644
index 0000000..dd4145a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/__init__.py
@@ -0,0 +1,6 @@
+from .altsvc import AltSvcCheck  # noqa
+from .base import BaseScheme
+from .ode import OptionalDispatchEvent  # noqa
+
+
+__all__ = ("BaseScheme",)
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/altsvc.py b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/altsvc.py
new file mode 100644
index 0000000..8f88cf9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/altsvc.py
@@ -0,0 +1,56 @@
+from __future__ import annotations
+
+from ast import Assign, Constant, NodeTransformer, Subscript
+from typing import TYPE_CHECKING, Any
+
+from sanic.http.constants import HTTP
+
+from .base import BaseScheme
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+class AltSvcCheck(BaseScheme):
+    ident = "ALTSVC"
+
+    def visitors(self) -> list[NodeTransformer]:
+        return [RemoveAltSvc(self.app, self.app.state.verbosity)]
+
+
+class RemoveAltSvc(NodeTransformer):
+    def __init__(self, app: Sanic, verbosity: int = 0) -> None:
+        self._app = app
+        self._verbosity = verbosity
+        self._versions = {
+            info.settings["version"] for info in app.state.server_info
+        }
+
+    def visit_Assign(self, node: Assign) -> Any:
+        if any(self._matches(target) for target in node.targets):
+            if self._should_remove():
+                return None
+            assert isinstance(node.value, Constant)
+            node.value.value = self.value()
+        return node
+
+    def _should_remove(self) -> bool:
+        return len(self._versions) == 1
+
+    @staticmethod
+    def _matches(node) -> bool:
+        return (
+            isinstance(node, Subscript)
+            and isinstance(node.slice, Constant)
+            and node.slice.value == "alt-svc"
+        )
+
+    def value(self):
+        values = []
+        for info in self._app.state.server_info:
+            port = info.settings["port"]
+            version = info.settings["version"]
+            if version is HTTP.VERSION_3:
+                values.append(f'h3=":{port}"')
+        return ", ".join(values)
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/base.py b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/base.py
new file mode 100644
index 0000000..c3a2089
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/base.py
@@ -0,0 +1,37 @@
+from abc import ABC, abstractmethod
+from ast import NodeTransformer, parse
+from inspect import getsource
+from textwrap import dedent
+from typing import Any
+
+
+class BaseScheme(ABC):
+    ident: str
+    _registry: set[type] = set()
+
+    def __init__(self, app) -> None:
+        self.app = app
+
+    @abstractmethod
+    def visitors(self) -> list[NodeTransformer]: ...
+
+    def __init_subclass__(cls):
+        BaseScheme._registry.add(cls)
+
+    def __call__(self):
+        return self.visitors()
+
+    @classmethod
+    def build(cls, method, module_globals, app):
+        raw_source = getsource(method)
+        src = dedent(raw_source)
+        node = parse(src)
+
+        for scheme in cls._registry:
+            for visitor in scheme(app)():
+                node = visitor.visit(node)
+
+        compiled_src = compile(node, method.__name__, "exec")
+        exec_locals: dict[str, Any] = {}
+        exec(compiled_src, module_globals, exec_locals)  # nosec
+        return exec_locals[method.__name__]
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/ode.py b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/ode.py
new file mode 100644
index 0000000..e5ed240
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/schemes/ode.py
@@ -0,0 +1,90 @@
+from ast import Attribute, Await, Expr, NodeTransformer
+from typing import Any
+
+from sanic.log import logger
+
+from .base import BaseScheme
+
+
+class OptionalDispatchEvent(BaseScheme):
+    ident = "ODE"
+    SYNC_SIGNAL_NAMESPACES = "http."
+
+    def __init__(self, app) -> None:
+        super().__init__(app)
+
+        self._sync_events()
+        self._registered_events = [
+            signal.name for signal in app.signal_router.routes
+        ]
+
+    def visitors(self) -> list[NodeTransformer]:
+        return [RemoveDispatch(self._registered_events)]
+
+    def _sync_events(self):
+        all_events = set()
+        app_events = {}
+        for app in self.app.__class__._app_registry.values():
+            if app.state.server_info:
+                app_events[app] = {
+                    signal.name for signal in app.signal_router.routes
+                }
+                all_events.update(app_events[app])
+
+        for app, events in app_events.items():
+            missing = {
+                x
+                for x in all_events.difference(events)
+                if any(x.startswith(y) for y in self.SYNC_SIGNAL_NAMESPACES)
+            }
+            if missing:
+                was_finalized = app.signal_router.finalized
+                if was_finalized:  # no cov
+                    app.signal_router.reset()
+                for event in missing:
+                    app.signal(event)(self.noop)
+                if was_finalized:  # no cov
+                    app.signal_router.finalize()
+
+    @staticmethod
+    async def noop(**_):  # no cov
+        ...
+
+
+class RemoveDispatch(NodeTransformer):
+    def __init__(self, registered_events) -> None:
+        self._registered_events = registered_events
+
+    def visit_Expr(self, node: Expr) -> Any:
+        call = node.value
+        if isinstance(call, Await):
+            call = call.value
+
+        func = getattr(call, "func", None)
+        args = getattr(call, "args", None)
+        if not func or not args:
+            return node
+
+        if isinstance(func, Attribute) and func.attr == "dispatch":
+            event = args[0]
+            if event_name := getattr(event, "value", None):
+                if self._not_registered(event_name):
+                    logger.debug(
+                        f"Disabling event: {event_name}",
+                        extra={"verbosity": 2},
+                    )
+                    return None
+        return node
+
+    def _not_registered(self, event_name):
+        dynamic = []
+        for event in self._registered_events:
+            if event.endswith(">"):
+                namespace_concern, _ = event.rsplit(".", 1)
+                dynamic.append(namespace_concern)
+
+        namespace_concern, _ = event_name.rsplit(".", 1)
+        return (
+            event_name not in self._registered_events
+            and namespace_concern not in dynamic
+        )
diff --git a/.venv/lib/python3.12/site-packages/sanic/touchup/service.py b/.venv/lib/python3.12/site-packages/sanic/touchup/service.py
new file mode 100644
index 0000000..98a080f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/touchup/service.py
@@ -0,0 +1,30 @@
+from inspect import getmembers, getmodule
+
+from .schemes import BaseScheme
+
+
+class TouchUp:
+    _registry: set[tuple[type, str]] = set()
+
+    @classmethod
+    def run(cls, app):
+        for target, method_name in cls._registry:
+            method = getattr(target, method_name)
+
+            if app.test_mode:
+                placeholder = f"_{method_name}"
+                if hasattr(target, placeholder):
+                    method = getattr(target, placeholder)
+                else:
+                    setattr(target, placeholder, method)
+
+            module = getmodule(target)
+            module_globals = dict(getmembers(module))
+            modified = BaseScheme.build(method, module_globals, app)
+            setattr(target, method_name, modified)
+
+            target.__touched__ = True
+
+    @classmethod
+    def register(cls, target, method_name):
+        cls._registry.add((target, method_name))
diff --git a/.venv/lib/python3.12/site-packages/sanic/types/__init__.py b/.venv/lib/python3.12/site-packages/sanic/types/__init__.py
new file mode 100644
index 0000000..043fffb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/types/__init__.py
@@ -0,0 +1,4 @@
+from .hashable_dict import HashableDict
+
+
+__all__ = ("HashableDict",)
diff --git a/.venv/lib/python3.12/site-packages/sanic/types/hashable_dict.py b/.venv/lib/python3.12/site-packages/sanic/types/hashable_dict.py
new file mode 100644
index 0000000..e756a32
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/types/hashable_dict.py
@@ -0,0 +1,3 @@
+class HashableDict(dict):
+    def __hash__(self):
+        return hash(tuple(sorted(self.items())))
diff --git a/.venv/lib/python3.12/site-packages/sanic/types/shared_ctx.py b/.venv/lib/python3.12/site-packages/sanic/types/shared_ctx.py
new file mode 100644
index 0000000..2fff9a3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/types/shared_ctx.py
@@ -0,0 +1,58 @@
+import os
+
+from collections.abc import Iterable
+from types import SimpleNamespace
+from typing import Any
+
+from sanic.log import Colors, error_logger
+
+
+class SharedContext(SimpleNamespace):
+    SAFE = ("_lock",)
+
+    def __init__(self, **kwargs: Any) -> None:
+        super().__init__(**kwargs)
+        self._lock = False
+
+    def __setattr__(self, name: str, value: Any) -> None:
+        if self.is_locked:
+            raise RuntimeError(
+                f"Cannot set {name} on locked SharedContext object"
+            )
+        if not os.environ.get("SANIC_WORKER_NAME"):
+            to_check: Iterable[Any]
+            if not isinstance(value, (tuple, frozenset)):
+                to_check = [value]
+            else:
+                to_check = value
+            for item in to_check:
+                self._check(name, item)
+        super().__setattr__(name, value)
+
+    def _check(self, name: str, value: Any) -> None:
+        if name in self.SAFE:
+            return
+        try:
+            module = value.__module__
+        except AttributeError:
+            module = ""
+        if not any(
+            module.startswith(prefix)
+            for prefix in ("multiprocessing", "ctypes")
+        ):
+            error_logger.warning(
+                f"{Colors.YELLOW}Unsafe object {Colors.PURPLE}{name} "
+                f"{Colors.YELLOW}with type {Colors.PURPLE}{type(value)} "
+                f"{Colors.YELLOW}was added to shared_ctx. It may not "
+                "not function as intended. Consider using the regular "
+                f"ctx.\nFor more information, please see https://sanic.dev/en"
+                "/guide/deployment/manager.html#using-shared-context-between-"
+                f"worker-processes.{Colors.END}"
+            )
+
+    @property
+    def is_locked(self) -> bool:
+        return getattr(self, "_lock", False)
+
+    def lock(self) -> None:
+        self._lock = True
diff --git a/.venv/lib/python3.12/site-packages/sanic/utils.py b/.venv/lib/python3.12/site-packages/sanic/utils.py
new file mode 100644
index 0000000..79c166c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/utils.py
@@ -0,0 +1,140 @@
+import types
+
+from importlib.util import module_from_spec, spec_from_file_location
+from os import environ as os_environ
+from pathlib import Path
+from re import findall as re_findall
+
+from sanic.exceptions import LoadFileException, PyFileError
+from sanic.helpers import import_string
+
+
+def str_to_bool(val: str) -> bool:
+    """Takes string and tries to turn it into bool as human would do.
+
+    If val is in case insensitive (
+        "y", "yes", "yep", "yup", "t",
+        "true", "on", "enable", "enabled", "1"
+    ) returns True.
+    If val is in case insensitive (
+        "n", "no", "f", "nope", "false", "off", "disable", "disabled", "0"
+    ) returns False.
+    Else Raise ValueError."""
+
+    val = val.lower()
+    if val in {
+        "y",
+        "yes",
+        "yep",
+        "yup",
+        "t",
+        "true",
+        "on",
+        "enable",
+        "enabled",
+        "1",
+    }:
+        return True
+    elif val in {
+        "n",
+        "no",
+        "f",
+        "nope",
+        "false",
+        "off",
+        "disable",
+        "disabled",
+        "0",
+    }:
+        return False
+    else:
+        raise ValueError(f"Invalid truth value {val}")
+
+
+def load_module_from_file_location(
+    location: bytes | str | Path, encoding: str = "utf8", *args, **kwargs
+):  # noqa
+    """Returns loaded module provided as a file path.
+
+    :param args:
+        Corresponds to importlib.util.spec_from_file_location location
+        parameters,but with this differences:
+        - It has to be of a string or bytes type.
+        - You can also use here environment variables
+          in format ${some_env_var}.
+          Mark that $some_env_var will not be resolved as environment variable.
+    :encoding:
+        If location parameter is of a bytes type, then use this encoding
+        to decode it into string.
+    :param args:
+        Corresponds to the rest of importlib.util.spec_from_file_location
+        parameters.
+    :param kwargs:
+        Corresponds to the rest of importlib.util.spec_from_file_location
+        parameters.
+
+    For example You can:
+
+        some_module = load_module_from_file_location(
+            "some_module_name",
+            "/some/path/${some_env_var}"
+        )
+    """
+    if isinstance(location, bytes):
+        location = location.decode(encoding)
+
+    if isinstance(location, Path) or "/" in location or "$" in location:
+        if not isinstance(location, Path):
+            # A) Check if location contains any environment variables
+            #    in format ${some_env_var}.
+            env_vars_in_location = set(re_findall(r"\${(.+?)}", location))
+
+            # B) Check these variables exists in environment.
+            not_defined_env_vars = env_vars_in_location.difference(
+                os_environ.keys()
+            )
+            if not_defined_env_vars:
+                raise LoadFileException(
+                    "The following environment variables are not set: "
+                    f"{', '.join(not_defined_env_vars)}"
+                )
+
+            # C) Substitute them in location.
+            for env_var in env_vars_in_location:
+                location = location.replace(
+                    "${" + env_var + "}", os_environ[env_var]
+                )
+
+        location = str(location)
+        if ".py" in location:
+            name = location.split("/")[-1].split(".")[
+                0
+            ]  # get just the file name without path and .py extension
+            _mod_spec = spec_from_file_location(
+                name, location, *args, **kwargs
+            )
+            assert _mod_spec is not None  # type assertion for mypy
+            module = module_from_spec(_mod_spec)
+            _mod_spec.loader.exec_module(module)  # type: ignore
+
+        else:
+            module = types.ModuleType("config")
+            module.__file__ = str(location)
+            try:
+                with open(location) as config_file:
+                    exec(  # nosec
+                        compile(config_file.read(), location, "exec"),
+                        module.__dict__,
+                    )
+            except OSError as e:
+                e.strerror = "Unable to load configuration file (e.strerror)"
+                raise
+            except Exception as e:
+                raise PyFileError(location) from e
+
+        return module
+    else:
+        try:
+            return import_string(location)
+        except ValueError:
+            raise OSError("Unable to load configuration %s" % str(location))
diff --git a/.venv/lib/python3.12/site-packages/sanic/views.py b/.venv/lib/python3.12/site-packages/sanic/views.py
new file mode 100644
index 0000000..fbbed78
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/views.py
@@ -0,0 +1,245 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+)
+
+from sanic.models.handler_types import RouteHandler
+from sanic.request.types import Request
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+    from sanic.blueprints import Blueprint
+
+
+class HTTPMethodView:
+    """Class based implementation for creating and grouping handlers
+
+    Class-based views (CBVs) are an alternative to function-based views. They
+    allow you to reuse common logic, and group related views, while keeping
+    the flexibility of function-based views.
+
+
+    To use a class-based view, subclass the method handler, and implement
+    methods (`get`, `post`, `put`, `patch`, `delete`) for the class
+    to correspond to each HTTP method you want to support.
+
+    For example:
+
+    ```python
+    class DummyView(HTTPMethodView):
+        def get(self, request: Request):
+            return text('I am get method')
+
+        def put(self, request: Request):
+            return text('I am put method')
+    ```
+
+    If someone tries to use a non-implemented method, they will reveive a
+    405 response.
+
+    If you need any url params just include them in method signature, like
+    you would for function-based views.
+
+    ```python
+    class DummyView(HTTPMethodView):
+        def get(self, request: Request, my_param_here: str):
+            return text(f"I am get method with {my_param_here}")
+    ```
+
+    Next, you need to attach the view to the app or blueprint. You can do this
+    in the exact same way as you would for a function-based view, except you
+    should you use `MyView.as_view()` instead of `my_view_handler`.
+
+    ```python
+    app.add_route(DummyView.as_view(), "/")
+    ```
+
+    Alternatively, you can use the `attach` method:
+
+    ```python
+    DummyView.attach(app, "/")
+    ```
+
+    Or, at the time of subclassing:
+
+    ```python
+    class DummyView(HTTPMethodView, attach=app, uri="/"):
+        ...
+    ```
+
+    To add a decorator, you can either:
+
+    1. Add it to the `decorators` list on the class, which will apply it to
+         all methods on the class; or
+    2. Add it to the method directly, which will only apply it to that method.
+
+    ```python
+    class DummyView(HTTPMethodView):
+        decorators = [my_decorator]
+        ...
+
+    # or
+
+    class DummyView(HTTPMethodView):
+        @my_decorator
+        def get(self, request: Request):
+            ...
+    ```
+
+    One catch is that you need to be mindful that the call inside the decorator
+    may need to account for the `self` argument, which is passed to the method
+    as the first argument. Alternatively, you may want to also mark your method
+    as `staticmethod` to avoid this.
+
+    Available attributes at the time of subclassing:
+    - **attach** (Optional[Union[Sanic, Blueprint]]): The app or blueprint to
+        attach the view to.
+    - **uri** (str): The uri to attach the view to.
+    - **methods** (Iterable[str]): The HTTP methods to attach the view to.
+        Defaults to `{"GET"}`.
+    - **host** (Optional[str]): The host to attach the view to.
+    - **strict_slashes** (Optional[bool]): Whether to add a redirect rule for
+        the uri with a trailing slash.
+    - **version** (Optional[int]): The version to attach the view to.
+    - **name** (Optional[str]): The name to attach the view to.
+    - **stream** (bool): Whether the view is a stream handler.
+    - **version_prefix** (str): The prefix to use for the version. Defaults
+        to `"/v"`.
+    """
+
+    decorators: list[Callable[[Callable[..., Any]], Callable[..., Any]]] = []
+
+    def __init_subclass__(
+        cls,
+        attach: Sanic | Blueprint | None = None,
+        uri: str = "",
+        methods: Iterable[str] = frozenset({"GET"}),
+        host: str | None = None,
+        strict_slashes: bool | None = None,
+        version: int | None = None,
+        name: str | None = None,
+        stream: bool = False,
+        version_prefix: str = "/v",
+        **kwargs: Any,
+    ) -> None:
+        super().__init_subclass__(**kwargs)
+        if attach:
+            cls.attach(
+                attach,
+                uri=uri,
+                methods=methods,
+                host=host,
+                strict_slashes=strict_slashes,
+                version=version,
+                name=name,
+                stream=stream,
+                version_prefix=version_prefix,
+            )
+
+    def dispatch_request(self, request: Request, *args, **kwargs):
+        """Dispatch request to appropriate handler method."""
+        method = request.method.lower()
+        handler = getattr(self, method, None)
+
+        if not handler and method == "head":
+            handler = getattr(self, "get")
+        if not handler:
+            # The router will never allow us to get here, but this is
+            # included as a fallback and for completeness.
+            raise NotImplementedError(
+                f"{request.method} is not supported for this endpoint."
+            )
+        return handler(request, *args, **kwargs)
+
+    @classmethod
+    def as_view(cls, *class_args: Any, **class_kwargs: Any) -> RouteHandler:
+        """Return view function for use with the routing system, that dispatches request to appropriate handler method.
+
+        If you need to pass arguments to the class's constructor, you can
+        pass the arguments to `as_view` and they will be passed to the class
+        `__init__` method.
+
+        Args:
+            *class_args: Variable length argument list for the class instantiation.
+            **class_kwargs: Arbitrary keyword arguments for the class instantiation.
+
+        Returns:
+            RouteHandler: The view function.
+
+        Examples:
+            ```python
+            class DummyView(HTTPMethodView):
+                def __init__(self, foo: MyFoo):
+                    self.foo = foo
+
+                async def get(self, request: Request):
+                    return text(self.foo.bar)
+
+            app.add_route(DummyView.as_view(foo=MyFoo()), "/")
+            ```
+        """  # noqa: E501
+
+        def view(*args, **kwargs):
+            self = view.view_class(*class_args, **class_kwargs)
+            return self.dispatch_request(*args, **kwargs)
+
+        if cls.decorators:
+            view.__module__ = cls.__module__
+            for decorator in cls.decorators:
+                view = decorator(view)
+
+        view.view_class = cls  # type: ignore
+        view.__doc__ = cls.__doc__
+        view.__module__ = cls.__module__
+        view.__name__ = cls.__name__
+        return view
+
+    @classmethod
+    def attach(
+        cls,
+        to: Sanic | Blueprint,
+        uri: str,
+        methods: Iterable[str] = frozenset({"GET"}),
+        host: str | None = None,
+        strict_slashes: bool | None = None,
+        version: int | None = None,
+        name: str | None = None,
+        stream: bool = False,
+        version_prefix: str = "/v",
+    ) -> None:
+        """Attaches the view to a Sanic app or Blueprint at the specified URI.
+
+        Args:
+            cls: The class that this method is part of.
+            to (Union[Sanic, Blueprint]): The Sanic application or Blueprint to attach to.
+            uri (str): The URI to bind the view to.
+            methods (Iterable[str], optional): A collection of HTTP methods that the view should respond to. Defaults to `frozenset({"GET"})`.
+            host (Optional[str], optional): A specific host or hosts to bind the view to. Defaults to `None`.
+            strict_slashes (Optional[bool], optional): Enforce or not the trailing slash. Defaults to `None`.
+            version (Optional[int], optional): Version of the API if versioning is used. Defaults to `None`.
+            name (Optional[str], optional): Unique name for the route. Defaults to `None`.
+            stream (bool, optional): Enable or disable streaming for the view. Defaults to `False`.
+            version_prefix (str, optional): The prefix for the version, if versioning is used. Defaults to `"/v"`.
+        """  # noqa: E501
+        to.add_route(
+            cls.as_view(),
+            uri=uri,
+            methods=methods,
+            host=host,
+            strict_slashes=strict_slashes,
+            version=version,
+            name=name,
+            stream=stream,
+            version_prefix=version_prefix,
+        )
+
+
+def stream(func):
+    """Decorator to mark a function as a stream handler."""
+    func.is_stream = True
+    return func
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/__init__.py b/.venv/lib/python3.12/site-packages/sanic/worker/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/constants.py b/.venv/lib/python3.12/site-packages/sanic/worker/constants.py
new file mode 100644
index 0000000..b7a7f35
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/constants.py
@@ -0,0 +1,25 @@
+from enum import IntEnum, auto
+
+from sanic.compat import UpperStrEnum
+
+
+class RestartOrder(UpperStrEnum):
+    """Available restart orders."""
+
+    SHUTDOWN_FIRST = auto()
+    STARTUP_FIRST = auto()
+
+
+class ProcessState(IntEnum):
+    """Process states."""
+
+    NONE = auto()
+    IDLE = auto()
+    RESTARTING = auto()
+    STARTING = auto()
+    STARTED = auto()
+    ACKED = auto()
+    JOINED = auto()
+    TERMINATED = auto()
+    FAILED = auto()
+    COMPLETED = auto()
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/daemon.py b/.venv/lib/python3.12/site-packages/sanic/worker/daemon.py
new file mode 100644
index 0000000..2f43b3f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/daemon.py
@@ -0,0 +1,507 @@
+from __future__ import annotations
+
+import atexit
+import grp
+import os
+import pwd
+import signal
+import sys
+import time
+import uuid
+
+from dataclasses import dataclass
+from pathlib import Path
+
+from sanic.compat import OS_IS_WINDOWS
+from sanic.log import logger
+
+
+try:
+    import fcntl  # type: ignore
+except (ImportError, ModuleNotFoundError):  # pragma: no cover
+    fcntl = None  # type: ignore
+
+
+def _get_default_runtime_dir() -> Path:
+    """
+    Default directory for auto-generated runtime artifacts (pid/lock/log).
+
+    Priority:
+      1) XDG_RUNTIME_DIR/sanic  (preferred, runtime-only)
+      2) ~/.local/state/sanic   (persistent state, modern default)
+      3) ~/.cache/sanic         (fallback)
+      4) cwd                    (last resort)
+    """
+    xdg_runtime = os.environ.get("XDG_RUNTIME_DIR")
+    if xdg_runtime:
+        path = Path(xdg_runtime) / "sanic"
+        try:
+            path.mkdir(mode=0o700, parents=True, exist_ok=True)
+            return path
+        except OSError:
+            pass
+
+    state_dir = Path.home() / ".local" / "state" / "sanic"
+    try:
+        state_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
+        return state_dir
+    except OSError:
+        pass
+
+    cache_dir = Path.home() / ".cache" / "sanic"
+    try:
+        cache_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
+        return cache_dir
+    except OSError:
+        pass
+
+    return Path.cwd()
+
+
+def _sanitize_name(name: str) -> str:
+    return (
+        "".join(
+            c if c.isalnum() or c in ("-", "_", ".") else "_" for c in name
+        ).strip("._")
+        or "sanic"
+    )
+
+
+def _process_exists(pid: int) -> bool:
+    try:
+        os.kill(pid, 0)
+    except OSError:
+        return False
+    return True
+
+
+def _is_sanic_process(pid: int) -> bool:
+    """
+    Best-effort Sanic process identification.
+
+    On Linux, checks /proc//cmdline for 'sanic'. On other platforms,
+    falls back to True if process exists (no strong identification).
+    """
+    proc_cmdline = Path(f"/proc/{pid}/cmdline")
+    if proc_cmdline.exists():
+        try:
+            data = proc_cmdline.read_bytes()
+            return b"sanic" in data
+        except OSError:
+            return False
+    return True
+
+
+@dataclass(frozen=True)
+class PidfileInfo:
+    pid: int
+    started: int | None = None
+    name: str | None = None
+
+
+class DaemonError(Exception):
+    pass
+
+
+class Daemon:
+    """
+    Daemonization helper (Unix only).
+
+    Supports:
+      - Double-fork daemonization
+      - Optional PID file (with Sanic marker + metadata)
+      - Optional lock file (prevents double start / stale PID reuse issues)
+      - Optional logfile redirection
+      - Optional privilege drop (user/group)
+      - SIGHUP handler that preserves pidfile identity
+    """
+
+    def __init__(
+        self,
+        pidfile: str | None = None,
+        logfile: str | None = None,
+        user: str | None = None,
+        group: str | None = None,
+        name: str | None = None,
+        lockfile: str | None = None,
+    ):
+        if OS_IS_WINDOWS:
+            raise DaemonError(
+                "Daemon mode is not supported on Windows. "
+                "Consider using a Windows service or nssm instead."
+            )
+
+        self.name = _sanitize_name(name) if name else None
+
+        self._auto_pidfile = pidfile == "auto" or pidfile == ""
+        self.pidfile: Path | None
+        if self._auto_pidfile:
+            base = _get_default_runtime_dir()
+            # Prefer deterministic name if provided
+            if self.name:
+                self.pidfile = base / f"{self.name}.pid"
+            else:
+                self.pidfile = base / f"sanic-{uuid.uuid4().hex}.pid"
+        elif pidfile:
+            self.pidfile = Path(pidfile)
+        else:
+            self.pidfile = None
+
+        self.logfile = Path(logfile) if logfile else None
+        self.user = user
+        self.group = group
+
+        self._uid: int | None = None
+        self._gid: int | None = None
+        self.pid: int | None = None
+
+        self._lockfile_path: Path | None = Path(lockfile) if lockfile else None
+        self._lock_fd: int | None = None
+
+    @staticmethod
+    def get_pidfile_path(name: str) -> Path:
+        """
+        Get the predictable pidfile path for a given name.
+
+        Args:
+            name: The application/daemon name
+
+        Returns:
+            Path to the pidfile in the default runtime directory
+        """
+        sanitized = _sanitize_name(name)
+        return _get_default_runtime_dir() / f"{sanitized}.pid"
+
+    def validate(self) -> None:
+        self._validate_user_group()
+        self._validate_paths()
+        self._validate_runtime_state()
+
+    def daemonize(self) -> None:
+        """
+        Double-fork daemonization.
+
+        Important: anything meant for the invoking terminal must be
+        printed/logged before calling this method, since stdout/stderr
+        are redirected after detaching.
+        """
+        self.validate()
+
+        # Acquire lock before forking to prevent race condition
+        # The lock is inherited by child processes and remains held
+        self._acquire_lockfile()
+
+        # First fork
+        try:
+            pid = os.fork()
+            if pid > 0:
+                os._exit(0)  # pragma: no cover
+        except OSError as e:
+            raise DaemonError(f"First fork failed: {e}") from e
+
+        os.chdir("/")
+        os.setsid()
+        os.umask(0o022)
+
+        # Second fork
+        try:
+            pid = os.fork()
+            if pid > 0:
+                os._exit(0)  # pragma: no cover
+        except OSError as e:
+            raise DaemonError(f"Second fork failed: {e}") from e
+
+        self.pid = os.getpid()
+
+        self._redirect_streams()
+        self._write_pidfile()
+        self._setup_signal_handlers()
+
+        logger.info(
+            "Sanic daemon started",
+            extra={
+                "daemon_pid": self.pid,
+                "daemon_pidfile": str(self.pidfile) if self.pidfile else None,
+                "daemon_logfile": str(self.logfile) if self.logfile else None,
+                "daemon_name": self.name,
+            },
+        )
+
+    def drop_privileges(self) -> None:
+        """
+        Drop privileges to configured user/group.
+
+        Call after binding privileged ports but before serving requests.
+        """
+        if self._uid is None and self._gid is None:
+            return
+
+        if os.getuid() != 0:
+            logger.warning(
+                "Privilege drop requested but not running as root",
+                extra={"user": self.user, "group": self.group},
+            )
+            return
+
+        if self._gid is not None:
+            try:
+                os.setgid(self._gid)
+                if self.user:
+                    os.initgroups(self.user, self._gid)
+            except PermissionError as e:
+                grp = self.group or self._gid
+                raise DaemonError(
+                    f"Cannot change group to '{grp}'. Are you root?"
+                ) from e
+
+        if self._uid is not None:
+            try:
+                os.setuid(self._uid)
+            except PermissionError as e:
+                usr = self.user or self._uid
+                raise DaemonError(
+                    f"Cannot change user to '{usr}'. Are you root?"
+                ) from e
+
+        logger.info(
+            "Dropped privileges",
+            extra={
+                "user": self.user or "unchanged",
+                "group": self.group or "unchanged",
+            },
+        )
+
+    def _validate_user_group(self) -> None:
+        if not self.user and not self.group:
+            return
+
+        if self.user:
+            try:
+                pw = pwd.getpwnam(self.user)
+            except KeyError as e:
+                raise DaemonError(f"User '{self.user}' does not exist") from e
+            self._uid = pw.pw_uid
+            if not self.group:
+                self._gid = pw.pw_gid
+
+        if self.group:
+            try:
+                gr = grp.getgrnam(self.group)
+            except KeyError as e:
+                raise DaemonError(
+                    f"Group '{self.group}' does not exist"
+                ) from e
+            self._gid = gr.gr_gid
+
+    def _validate_paths(self) -> None:
+        if self.pidfile:
+            self._validate_writable_dir(self.pidfile, "PID file")
+
+        if self.logfile:
+            self._validate_writable_dir(self.logfile, "Log file")
+
+        # Default lockfile beside pidfile if not explicitly set
+        if not self._lockfile_path and self.pidfile:
+            self._lockfile_path = self.pidfile.with_suffix(".lock")
+
+        if self._lockfile_path:
+            self._validate_writable_dir(self._lockfile_path, "Lock file")
+
+    def _validate_runtime_state(self) -> None:
+        # PID file check (stale vs running)
+        if not self.pidfile or not self.pidfile.exists():
+            return
+
+        info = self.read_pidfile_info(self.pidfile)
+        if not info:
+            return
+
+        if _process_exists(info.pid) and _is_sanic_process(info.pid):
+            raise DaemonError(f"Daemon already running with PID {info.pid}")
+
+    def _validate_writable_dir(self, path: Path, label: str) -> None:
+        directory = path.parent
+        if not directory.exists():
+            raise DaemonError(f"{label} directory does not exist: {directory}")
+        if not os.access(directory, os.W_OK):
+            raise DaemonError(
+                f"Cannot write to {label} directory: {directory}"
+            )
+
+    def _write_pidfile(self) -> None:
+        if not self.pidfile:
+            return
+
+        pid = os.getpid()
+        started = int(time.time())
+        name = self.name or ""
+
+        # Format:
+        #   sanic
+        #   pid=123
+        #   started=...
+        #   name=...
+        lines = [
+            "sanic",
+            f"pid={pid}",
+            f"started={started}",
+        ]
+        if name:
+            lines.append(f"name={name}")
+
+        try:
+            self.pidfile.write_text("\n".join(lines) + "\n")
+        except OSError as e:
+            raise DaemonError(
+                f"Failed to write PID file: {self.pidfile}"
+            ) from e
+
+        atexit.register(self._remove_pidfile)
+
+    def _remove_pidfile(self) -> None:
+        if not self.pidfile:
+            return
+        try:
+            self.pidfile.unlink(missing_ok=True)
+        except OSError as e:
+            # Best-effort cleanup: failure to remove the PID file is non-fatal.
+            logger.debug("Failed to remove PID file %s: %s", self.pidfile, e)
+
+    @staticmethod
+    def read_pidfile(pidfile: str | Path) -> int | None:
+        info = Daemon.read_pidfile_info(pidfile)
+        return info.pid if info else None
+
+    @staticmethod
+    def read_pidfile_info(pidfile: str | Path) -> PidfileInfo | None:
+        path = Path(pidfile)
+        if not path.exists():
+            return None
+
+        try:
+            raw = path.read_text().splitlines()
+        except OSError:
+            return None
+
+        if not raw or raw[0].strip() != "sanic":
+            return None
+
+        data: dict[str, str] = {}
+        for line in raw[1:]:
+            if "=" in line:
+                k, v = line.split("=", 1)
+                data[k.strip()] = v.strip()
+
+        try:
+            pid = int(data.get("pid", ""))
+        except ValueError:
+            return None
+
+        started = None
+        if "started" in data:
+            try:
+                started = int(data["started"])
+            except ValueError:
+                started = None
+
+        name = data.get("name") or None
+        return PidfileInfo(pid=pid, started=started, name=name)
+
+    def _acquire_lockfile(self) -> None:
+        if not self._lockfile_path:
+            return
+        if fcntl is None:
+            return
+
+        try:
+            fd = os.open(
+                str(self._lockfile_path), os.O_RDWR | os.O_CREAT, 0o644
+            )
+        except OSError as e:
+            raise DaemonError(
+                f"Failed to open lock file: {self._lockfile_path}"
+            ) from e
+
+        try:
+            fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+        except OSError as e:
+            try:
+                os.close(fd)
+            except OSError:
+                pass
+            raise DaemonError(
+                f"Daemon already running (lock held): {self._lockfile_path}"
+            ) from e
+
+        self._lock_fd = fd
+        atexit.register(self._release_lockfile)
+
+    def _release_lockfile(self) -> None:
+        if self._lock_fd is None:
+            return
+        try:
+            if fcntl is not None:
+                fcntl.flock(self._lock_fd, fcntl.LOCK_UN)
+        except OSError as e:
+            # Best-effort cleanup: failure to unlock is non-fatal.
+            logger.debug(
+                "Failed to unlock file descriptor %s: %s", self._lock_fd, e
+            )
+        try:
+            os.close(self._lock_fd)
+        except OSError as e:
+            # Best-effort cleanup: failure to close is non-fatal.
+            logger.debug(
+                "Failed to close file descriptor %s: %s", self._lock_fd, e
+            )
+        self._lock_fd = None
+        if self._lockfile_path:
+            try:
+                self._lockfile_path.unlink(missing_ok=True)
+            except OSError as e:
+                # Best-effort cleanup: failure to remove lock file
+                # is non-fatal.
+                logger.debug(
+                    "Failed to remove lock file %s: %s", self._lockfile_path, e
+                )
+
+    def _redirect_streams(self) -> None:
+        sys.stdout.flush()
+        sys.stderr.flush()
+
+        stdin_fd = os.open(os.devnull, os.O_RDONLY)
+        os.dup2(stdin_fd, sys.stdin.fileno())
+
+        if self.logfile:
+            log_fd = os.open(
+                str(self.logfile),
+                os.O_WRONLY | os.O_CREAT | os.O_APPEND,
+                0o644,
+            )
+            os.dup2(log_fd, sys.stdout.fileno())
+            os.dup2(log_fd, sys.stderr.fileno())
+            if log_fd > 2:
+                os.close(log_fd)
+        else:
+            devnull_fd = os.open(os.devnull, os.O_RDWR)
+            os.dup2(devnull_fd, sys.stdout.fileno())
+            os.dup2(devnull_fd, sys.stderr.fileno())
+            if devnull_fd > 2:
+                os.close(devnull_fd)
+
+        if stdin_fd > 2:
+            os.close(stdin_fd)
+
+    def _setup_signal_handlers(self) -> None:
+        if not self.pidfile:
+            return
+
+        original = signal.getsignal(signal.SIGHUP)
+
+        def _handle_sighup(signum, frame):
+            # Preserve pidfile identity; ensure it exists.
+            self._write_pidfile()
+            if callable(original):
+                original(signum, frame)
+
+        signal.signal(signal.SIGHUP, _handle_sighup)
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/inspector.py b/.venv/lib/python3.12/site-packages/sanic/worker/inspector.py
new file mode 100644
index 0000000..89a4dea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/inspector.py
@@ -0,0 +1,157 @@
+from __future__ import annotations
+
+from collections.abc import Mapping
+from datetime import datetime
+from inspect import isawaitable
+from multiprocessing.connection import Connection
+from os import environ
+from pathlib import Path
+from typing import Any
+
+from sanic.exceptions import Unauthorized
+from sanic.helpers import Default
+from sanic.log import logger
+from sanic.request import Request
+from sanic.response import json
+
+
+class Inspector:
+    """Inspector for Sanic workers.
+
+    This class is used to create an inspector for Sanic workers. It is
+    instantiated by the worker class and is used to create a Sanic app
+    that can be used to inspect and control the workers and the server.
+
+    It is not intended to be used directly by the user.
+
+    See [Inspector](/en/guide/deployment/inspector) for more information.
+
+    Args:
+        publisher (Connection): The connection to the worker.
+        app_info (Dict[str, Any]): Information about the app.
+        worker_state (Mapping[str, Any]): The state of the worker.
+        host (str): The host to bind the inspector to.
+        port (int): The port to bind the inspector to.
+        api_key (str): The API key to use for authentication.
+        tls_key (Union[Path, str, Default]): The path to the TLS key file.
+        tls_cert (Union[Path, str, Default]): The path to the TLS cert file.
+    """
+
+    def __init__(
+        self,
+        publisher: Connection,
+        app_info: dict[str, Any],
+        worker_state: Mapping[str, Any],
+        host: str,
+        port: int,
+        api_key: str,
+        tls_key: Path | str | Default,
+        tls_cert: Path | str | Default,
+    ):
+        self._publisher = publisher
+        self.app_info = app_info
+        self.worker_state = worker_state
+        self.host = host
+        self.port = port
+        self.api_key = api_key
+        self.tls_key = tls_key
+        self.tls_cert = tls_cert
+
+    def __call__(self, run=True, **_) -> Inspector:
+        from sanic import Sanic
+
+        self.app = Sanic("Inspector")
+        self._setup()
+        if run:
+            self.app.run(
+                host=self.host,
+                port=self.port,
+                single_process=True,
+                ssl={"key": self.tls_key, "cert": self.tls_cert}
+                if not isinstance(self.tls_key, Default)
+                and not isinstance(self.tls_cert, Default)
+                else None,
+            )
+        return self
+
+    def _setup(self):
+        self.app.get("/")(self._info)
+        self.app.post("/")(self._action)
+        if self.api_key:
+            self.app.on_request(self._authentication)
+        environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "true"
+
+    def _authentication(self, request: Request) -> None:
+        if request.token != self.api_key:
+            raise Unauthorized("Bad API key")
+
+    async def _action(self, request: Request, action: str):
+        logger.info("Incoming inspector action: %s", action)
+        output: Any = None
+        method = getattr(self, action, None)
+        if method:
+            kwargs = {}
+            if request.body:
+                kwargs = request.json
+            args = kwargs.pop("args", ())
+            output = method(*args, **kwargs)
+            if isawaitable(output):
+                output = await output
+
+        return await self._respond(request, output)
+
+    async def _info(self, request: Request):
+        return await self._respond(request, self._state_to_json())
+
+    async def _respond(self, request: Request, output: Any):
+        name = request.match_info.get("action", "info")
+        return json({"meta": {"action": name}, "result": output})
+
+    def _state_to_json(self) -> dict[str, Any]:
+        output = {"info": self.app_info}
+        output["workers"] = self._make_safe(dict(self.worker_state))
+        return output
+
+    @staticmethod
+    def _make_safe(obj: dict[str, Any]) -> dict[str, Any]:
+        for key, value in obj.items():
+            if isinstance(value, dict):
+                obj[key] = Inspector._make_safe(value)
+            elif isinstance(value, datetime):
+                obj[key] = value.isoformat()
+        return obj
+
+    def reload(self, zero_downtime: bool = False) -> None:
+        """Reload the workers
+
+        Args:
+            zero_downtime (bool, optional): Whether to use zero downtime
+                reload. Defaults to `False`.
+        """
+        message = "__ALL_PROCESSES__:"
+        if zero_downtime:
+            message += ":STARTUP_FIRST"
+        self._publisher.send(message)
+
+    def scale(self, replicas: str | int) -> str:
+        """Scale the number of workers
+
+        Args:
+            replicas (Union[str, int]): The number of workers to scale to.
+
+        Returns:
+            str: A log message.
+        """
+        num_workers = 1
+        if replicas:
+            num_workers = int(replicas)
+        log_msg = f"Scaling to {num_workers}"
+        logger.info(log_msg)
+        message = f"__SCALE__:{num_workers}"
+        self._publisher.send(message)
+        return log_msg
+
+    def shutdown(self) -> None:
+        """Shutdown the workers"""
+        message = "__TERMINATE__"
+        self._publisher.send(message)
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/loader.py b/.venv/lib/python3.12/site-packages/sanic/worker/loader.py
new file mode 100644
index 0000000..287055f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/loader.py
@@ -0,0 +1,167 @@
+from __future__ import annotations
+
+import os
+import sys
+
+from contextlib import suppress
+from importlib import import_module
+from inspect import isfunction
+from pathlib import Path
+from ssl import SSLContext
+from typing import TYPE_CHECKING, Any, Callable, cast
+
+from sanic.http.tls.context import process_to_context
+from sanic.http.tls.creators import MkcertCreator, TrustmeCreator
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic as SanicApp
+
+DEFAULT_APP_NAME = "app"
+
+
+class AppLoader:
+    """A helper to load application instances.
+
+    Generally used by the worker to load the application instance.
+
+    See [Dynamic Applications](/en/guide/deployment/app-loader) for information on when you may need to use this.
+
+    Args:
+        module_input (str): The module to load the application from.
+        as_factory (bool): Whether the application is a factory.
+        as_simple (bool): Whether the application is a simple server.
+        args (Any): Arguments to pass to the application factory.
+        factory (Callable[[], SanicApp]): A callable that returns a Sanic application instance.
+    """  # noqa: E501
+
+    def __init__(
+        self,
+        module_input: str = "",
+        as_factory: bool = False,
+        as_simple: bool = False,
+        args: Any = None,
+        factory: Callable[[], SanicApp] | None = None,
+    ) -> None:
+        self.module_input = module_input
+        self.module_name = ""
+        self.app_name = ""
+        self.as_factory = as_factory
+        self.as_simple = as_simple
+        self.args = args
+        self.factory = factory
+        self.cwd = os.getcwd()
+
+        if module_input:
+            delimiter = ":" if ":" in module_input else "."
+            if (
+                delimiter in module_input
+                and "\\" not in module_input
+                and "/" not in module_input
+            ):
+                module_name, app_name = module_input.rsplit(delimiter, 1)
+                self.module_name = module_name
+                self.app_name = app_name
+                if self.app_name.endswith("()"):
+                    self.as_factory = True
+                    self.app_name = self.app_name[:-2]
+
+    def load(self) -> SanicApp:
+        module_path = os.path.abspath(self.cwd)
+        if module_path not in sys.path:
+            sys.path.append(module_path)
+
+        if self.factory:
+            return self.factory()
+        else:
+            from sanic.app import Sanic
+            from sanic.simple import create_simple_server
+
+            maybe_path = Path(self.module_input)
+            if self.as_simple or (
+                maybe_path.is_dir()
+                and ("\\" in self.module_input or "/" in self.module_input)
+            ):
+                app = create_simple_server(maybe_path)
+            else:
+                implied_app_name = False
+                if not self.module_name and not self.app_name:
+                    self.module_name = self.module_input
+                    self.app_name = DEFAULT_APP_NAME
+                    implied_app_name = True
+                module = import_module(self.module_name)
+                app = getattr(module, self.app_name, None)
+                if not app and implied_app_name:
+                    raise ValueError(
+                        "Looks like you only supplied a module name. Sanic "
+                        "tried to locate an application instance named "
+                        f"{self.module_name}:app, but was unable to locate "
+                        "an application instance. Please provide a path "
+                        "to a global instance of Sanic(), or a callable that "
+                        "will return a Sanic() application instance."
+                    )
+                if self.as_factory or isfunction(app):
+                    if not callable(app):
+                        type_name = type(app).__name__
+                        raise ValueError(
+                            f"Expected a callable, but got {type_name}."
+                        )
+                    try:
+                        app = app(self.args)
+                    except TypeError:
+                        app = app()
+
+                app_type_name = type(app).__name__
+
+                if (
+                    not isinstance(app, Sanic)
+                    and self.args
+                    and hasattr(self.args, "target")
+                ):
+                    with suppress(ModuleNotFoundError):
+                        maybe_module = import_module(self.module_input)
+                        app = getattr(maybe_module, "app", None)
+                    if not app:
+                        message = (
+                            "Module is not a Sanic app, "
+                            f"it is a {app_type_name}\n"
+                            f"  Perhaps you meant {self.args.target}:app?"
+                        )
+                        raise ValueError(message)
+        return app
+
+
+class CertLoader:
+    _creators = {
+        "mkcert": MkcertCreator,
+        "trustme": TrustmeCreator,
+    }
+
+    def __init__(
+        self,
+        ssl_data: SSLContext | dict[str, str | os.PathLike] | None,
+    ):
+        self._ssl_data = ssl_data
+        self._creator_class = None
+        if not ssl_data or not isinstance(ssl_data, dict):
+            return
+
+        creator_name = cast(str, ssl_data.get("creator"))
+
+        self._creator_class = self._creators.get(creator_name)
+        if not creator_name:
+            return
+
+        if not self._creator_class:
+            raise RuntimeError(f"Unknown certificate creator: {creator_name}")
+
+        self._key = ssl_data["key"]
+        self._cert = ssl_data["cert"]
+        self._localhost = cast(str, ssl_data["localhost"])
+
+    def load(self, app: SanicApp):
+        if not self._creator_class:
+            return process_to_context(self._ssl_data)
+
+        creator = self._creator_class(app, self._key, self._cert)
+        return creator.generate_cert(self._localhost)
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/manager.py b/.venv/lib/python3.12/site-packages/sanic/worker/manager.py
new file mode 100644
index 0000000..5f7e99f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/manager.py
@@ -0,0 +1,550 @@
+from __future__ import annotations
+
+import os
+import signal
+
+from collections.abc import Iterable, MutableMapping
+from contextlib import suppress
+from enum import IntEnum, auto
+from itertools import chain, count
+from multiprocessing.connection import Connection
+from multiprocessing.context import BaseContext
+from random import choice
+from signal import SIGINT, SIGTERM, Signals
+from signal import signal as signal_func
+from typing import Any, Callable
+
+from sanic.compat import OS_IS_WINDOWS
+from sanic.exceptions import ServerKilled
+from sanic.log import error_logger, logger
+from sanic.worker.constants import RestartOrder
+from sanic.worker.process import ProcessState, Worker, WorkerProcess
+from sanic.worker.restarter import Restarter
+
+
+SIGKILL: int
+if not OS_IS_WINDOWS:
+    from signal import SIGKILL
+else:
+    SIGKILL = SIGINT
+
+
+class MonitorCycle(IntEnum):
+    BREAK = auto()
+    CONTINUE = auto()
+
+
+class WorkerManager:
+    """Manage all of the processes.
+
+    This class is used to manage all of the processes. It is instantiated
+    by Sanic when in multiprocess mode (which is OOTB default) and is used
+    to start, stop, and restart the worker processes.
+
+    You can access it to interact with it **ONLY** when on the main process.
+
+    Therefore, you should really only access it from within the
+    `main_process_ready` event listener.
+
+    ```python
+    from sanic import Sanic
+
+    app = Sanic("MyApp")
+
+    @app.main_process_ready
+    async def ready(app: Sanic, _):
+        app.manager.manage("MyProcess", my_process, {"foo": "bar"})
+    ```
+
+    See [Worker Manager](/en/guide/deployment/manager) for more information.
+    """
+
+    THRESHOLD = WorkerProcess.THRESHOLD
+    MAIN_NAME = "Sanic-Main"
+
+    def __init__(
+        self,
+        number: int,
+        serve: Callable[..., Any],
+        server_settings: dict[str, Any],
+        context: BaseContext,
+        monitor_pubsub: tuple[Connection[Any, Any], Connection[Any, Any]],
+        worker_state: MutableMapping[str, Any],
+    ):
+        self.num_server = number
+        self.context = context
+        self.transient: dict[str, Worker] = {}
+        self.durable: dict[str, Worker] = {}
+        self.restarter = Restarter()
+        self.monitor_publisher, self.monitor_subscriber = monitor_pubsub
+        self.worker_state = worker_state
+        self.worker_state[self.MAIN_NAME] = {"pid": self.pid}
+        self._shutting_down = False
+        self._serve = serve
+        self._server_settings = server_settings
+        self._server_count = count()
+
+        if number == 0:
+            raise RuntimeError("Cannot serve with no workers")
+
+        for _ in range(number):
+            self.create_server()
+
+        signal_func(SIGINT, self.shutdown_signal)
+        signal_func(SIGTERM, self.shutdown_signal)
+
+    def manage(
+        self,
+        name: str,
+        func: Callable[..., Any],
+        kwargs: dict[str, Any],
+        transient: bool = False,
+        restartable: bool | None = None,
+        tracked: bool = True,
+        auto_start: bool = True,
+        workers: int = 1,
+        ident: str = "",
+    ) -> Worker:
+        """Instruct Sanic to manage a custom process.
+
+        Args:
+            name (str): A name for the worker process
+            func (Callable[..., Any]): The function to call in the background process
+            kwargs (Dict[str, Any]): Arguments to pass to the function
+            transient (bool, optional): Whether to mark the process as transient. If `True`
+                then the Worker Manager will restart the process along
+                with any global restart (ex: auto-reload), defaults to `False`
+            restartable (Optional[bool], optional): Whether to mark the process as restartable. If
+                `True` then the Worker Manager will be able to restart the process
+                if prompted. If `transient=True`, this property will be implied
+                to be `True`, defaults to `None`
+            tracked (bool, optional): Whether to track the process after completion,
+                defaults to `True`
+            auto_start (bool, optional): Whether to start the process immediately, defaults to `True`
+            workers (int, optional): The number of worker processes to run. Defaults to `1`.
+            ident (str, optional): The identifier for the worker. If not provided, the name
+                passed will be used. Defaults to `""`.
+
+        Returns:
+            Worker: The Worker instance
+        """  # noqa: E501
+        if name in self.transient or name in self.durable:
+            raise ValueError(f"Worker {name} already exists")
+        restartable = restartable if restartable is not None else transient
+        if transient and not restartable:
+            raise ValueError(
+                "Cannot create a transient worker that is not restartable"
+            )
+        container = self.transient if transient else self.durable
+        worker = Worker(
+            ident or name,
+            name,
+            func,
+            kwargs,
+            self.context,
+            self.worker_state,
+            workers,
+            restartable,
+            tracked,
+            auto_start,
+        )
+        container[worker.name] = worker
+        return worker
+
+    def create_server(self) -> Worker:
+        """Create a new server process.
+
+        Returns:
+            Worker: The Worker instance
+        """
+        server_number = next(self._server_count)
+        return self.manage(
+            f"{WorkerProcess.SERVER_LABEL}-{server_number}",
+            self._serve,
+            self._server_settings,
+            transient=True,
+            restartable=True,
+            ident=f"{WorkerProcess.SERVER_IDENTIFIER}{server_number:2}",
+        )
+
+    def shutdown_server(self, name: str | None = None) -> None:
+        """Shutdown a server process.
+
+        Args:
+            name (Optional[str], optional): The name of the server process to shutdown.
+                If `None` then a random server will be chosen. Defaults to `None`.
+        """  # noqa: E501
+        if not name:
+            servers = [
+                worker
+                for worker in self.transient.values()
+                if worker.name.startswith(WorkerProcess.SERVER_LABEL)
+            ]
+            if not servers:
+                error_logger.error(
+                    "Server shutdown failed because a server was not found."
+                )
+                return
+            worker = choice(servers)  # nosec B311
+        else:
+            worker = self.transient[name]
+
+        for process in worker.processes:
+            process.terminate()
+
+        del self.transient[worker.name]
+
+    def run(self):
+        """Run the worker manager."""
+        self.start()
+        self.monitor()
+        self.join()
+        self.terminate()
+        self.cleanup()
+
+    def start(self):
+        """Start the worker processes."""
+        for worker in self.workers:
+            for process in worker.processes:
+                if not worker.auto_start:
+                    process.set_state(ProcessState.NONE, True)
+                    continue
+                process.start()
+
+    def join(self):
+        """Join the worker processes."""
+        logger.debug("Joining processes", extra={"verbosity": 1})
+        joined = set()
+        for process in self.processes:
+            logger.debug(
+                f"Found {process.pid} - {process.state.name}",
+                extra={"verbosity": 1},
+            )
+            if process.state < ProcessState.JOINED:
+                logger.debug(f"Joining {process.pid}", extra={"verbosity": 1})
+                joined.add(process.pid)
+                process.join()
+        if joined:
+            self.join()
+
+    def terminate(self):
+        """Terminate the worker processes."""
+        if not self._shutting_down:
+            for process in self.processes:
+                process.terminate()
+
+    def cleanup(self):
+        """Cleanup the worker processes."""
+        for process in self.processes:
+            process.exit()
+
+    def restart(
+        self,
+        process_names: list[str] | None = None,
+        restart_order=RestartOrder.SHUTDOWN_FIRST,
+        **kwargs,
+    ):
+        """Restart the worker processes.
+
+        Args:
+            process_names (Optional[List[str]], optional): The names of the processes to restart.
+                If `None` then all processes will be restarted. Defaults to `None`.
+            restart_order (RestartOrder, optional): The order in which to restart the processes.
+                Defaults to `RestartOrder.SHUTDOWN_FIRST`.
+        """  # noqa: E501
+        self.restarter.restart(
+            transient_processes=list(self.transient_processes),
+            durable_processes=list(self.durable_processes),
+            process_names=process_names,
+            restart_order=restart_order,
+            **kwargs,
+        )
+
+    def scale(self, num_worker: int):
+        if num_worker <= 0:
+            raise ValueError("Cannot scale to 0 workers.")
+
+        change = num_worker - self.num_server
+        if change == 0:
+            logger.info(
+                f"No change needed. There are already {num_worker} workers."
+            )
+            return
+
+        logger.info(f"Scaling from {self.num_server} to {num_worker} workers")
+        if change > 0:
+            for _ in range(change):
+                worker = self.create_server()
+                for process in worker.processes:
+                    process.start()
+        else:
+            for _ in range(abs(change)):
+                self.shutdown_server()
+        self.num_server = num_worker
+
+    def monitor(self):
+        """Monitor the worker processes.
+
+        First, wait for all of the workers to acknowledge that they are ready.
+        Then, wait for messages from the workers. If a message is received
+        then it is processed and the state of the worker is updated.
+
+        Also used to restart, shutdown, and scale the workers.
+
+        Raises:
+            ServerKilled: Raised when a worker fails to come online.
+        """
+        self.wait_for_ack()
+        while True:
+            try:
+                cycle = self._poll_monitor()
+                if cycle is MonitorCycle.BREAK:
+                    break
+                elif cycle is MonitorCycle.CONTINUE:
+                    continue
+                self._sync_states()
+                self._cleanup_non_tracked_workers()
+            except InterruptedError:
+                if not OS_IS_WINDOWS:
+                    raise
+                break
+
+    def wait_for_ack(self):  # no cov
+        """Wait for all of the workers to acknowledge that they are ready."""
+        misses = 0
+        message = (
+            "It seems that one or more of your workers failed to come "
+            "online in the allowed time. Sanic is shutting down to avoid a "
+            f"deadlock. The current threshold is {self.THRESHOLD / 10}s. "
+            "If this problem persists, please check out the documentation "
+            "https://sanic.dev/en/guide/deployment/manager.html#worker-ack."
+        )
+        while not self._all_workers_ack():
+            if self.monitor_subscriber.poll(0.1):
+                monitor_msg = self.monitor_subscriber.recv()
+                if monitor_msg != "__TERMINATE_EARLY__":
+                    self.monitor_publisher.send(monitor_msg)
+                    continue
+                misses = self.THRESHOLD
+                message = (
+                    "One of your worker processes terminated before startup "
+                    "was completed. Please solve any errors experienced "
+                    "during startup. If you do not see an exception traceback "
+                    "in your error logs, try running Sanic in a single "
+                    "process using --single-process or single_process=True. "
+                    "Once you are confident that the server is able to start "
+                    "without errors you can switch back to multiprocess mode."
+                )
+            misses += 1
+            if misses > self.THRESHOLD:
+                error_logger.error(
+                    "Not all workers acknowledged a successful startup. "
+                    "Shutting down.\n\n" + message
+                )
+                self.kill()
+
+    @property
+    def workers(self) -> list[Worker]:
+        """Get all of the workers."""
+        return list(self.transient.values()) + list(self.durable.values())
+
+    @property
+    def all_workers(self) -> Iterable[tuple[str, Worker]]:
+        return chain(self.transient.items(), self.durable.items())
+
+    @property
+    def processes(self):
+        """Get all of the processes."""
+        for worker in self.workers:
+            for process in worker.processes:
+                if not process.pid:
+                    continue
+                yield process
+
+    @property
+    def transient_processes(self):
+        """Get all of the transient processes."""
+        for worker in self.transient.values():
+            yield from worker.processes
+
+    @property
+    def durable_processes(self):
+        for worker in self.durable.values():
+            yield from worker.processes
+
+    def kill(self):
+        """Kill all of the processes."""
+        for process in self.processes:
+            logger.info("Killing %s [%s]", process.name, process.pid)
+            with suppress(ProcessLookupError):
+                if os.name == "nt":
+                    # Windows has no os.killpg and SIGKILL doesn't seem to work
+                    os.kill(process.pid, signal.CTRL_C_EVENT)
+                else:
+                    try:
+                        os.killpg(os.getpgid(process.pid), SIGKILL)
+                    except OSError:
+                        os.kill(process.pid, SIGKILL)
+        raise ServerKilled
+
+    def shutdown_signal(self, signal, frame):
+        """Handle the shutdown signal."""
+        if self._shutting_down:
+            logger.info("Shutdown interrupted. Killing.")
+            with suppress(ServerKilled):
+                self.kill()
+            return
+
+        logger.info("Received signal %s. Shutting down.", Signals(signal).name)
+        self.monitor_publisher.send(None)
+        self.shutdown()
+
+    def shutdown(self):
+        """Shutdown the worker manager."""
+        for process in self.processes:
+            if process.is_alive():
+                process.terminate()
+        self._shutting_down = True
+
+    def remove_worker(self, worker: Worker) -> None:
+        if worker.tracked:
+            error_logger.error(
+                f"Worker {worker.name} is tracked and cannot be removed."
+            )
+            return
+        if worker.has_alive_processes():
+            error_logger.error(
+                f"Worker {worker.name} has alive processes and cannot be "
+                "removed."
+            )
+            return
+        self.transient.pop(worker.name, None)
+        self.durable.pop(worker.name, None)
+        for process in worker.processes:
+            self.worker_state.pop(process.name, None)
+        logger.info("Removed worker %s", worker.name)
+        del worker
+
+    @property
+    def pid(self):
+        """Get the process ID of the main process."""
+        return os.getpid()
+
+    def _all_workers_ack(self):
+        acked = [
+            worker_state.get("state") == ProcessState.ACKED.name
+            for worker_state in self.worker_state.values()
+            if worker_state.get("server")
+        ]
+        return all(acked) and len(acked) == self.num_server
+
+    def _sync_states(self):
+        for process in self.processes:
+            try:
+                state = self.worker_state[process.name].get("state")
+            except KeyError:
+                process.set_state(ProcessState.TERMINATED, True)
+                continue
+            # Skip state sync if process is restarting to avoid race condition
+            if process.state == ProcessState.RESTARTING:
+                continue
+            if not process.is_alive():
+                state = "FAILED" if process.exitcode else "COMPLETED"
+            if state and process.state.name != state:
+                process.set_state(ProcessState[state], True)
+
+    def _cleanup_non_tracked_workers(self) -> None:
+        to_remove = [
+            worker
+            for worker in self.workers
+            if not worker.tracked and not worker.has_alive_processes()
+        ]
+
+        for worker in to_remove:
+            self.remove_worker(worker)
+
+    def _poll_monitor(self) -> MonitorCycle | None:
+        if self.monitor_subscriber.poll(0.1):
+            message = self.monitor_subscriber.recv()
+            logger.debug(f"Monitor message: {message}", extra={"verbosity": 2})
+            if not message:
+                return MonitorCycle.BREAK
+            elif message == "__TERMINATE__":
+                self._handle_terminate()
+                return MonitorCycle.BREAK
+            elif isinstance(message, tuple) and (
+                len(message) == 7 or len(message) == 8
+            ):
+                self._handle_manage(*message)  # type: ignore
+                return MonitorCycle.CONTINUE
+            elif not isinstance(message, str):
+                error_logger.error(
+                    "Monitor received an invalid message: %s", message
+                )
+                return MonitorCycle.CONTINUE
+            return self._handle_message(message)
+        return None
+
+    def _handle_terminate(self) -> None:
+        self.shutdown()
+
+    def _handle_message(self, message: str) -> MonitorCycle | None:
+        logger.debug(
+            "Incoming monitor message: %s",
+            message,
+            extra={"verbosity": 1},
+        )
+        split_message = message.split(":", 2)
+        if message.startswith("__SCALE__"):
+            self.scale(int(split_message[-1]))
+            return MonitorCycle.CONTINUE
+
+        processes = split_message[0]
+        reloaded_files = split_message[1] if len(split_message) > 1 else None
+        process_names: list[str] | None = [
+            name.strip() for name in processes.split(",")
+        ]
+        if process_names and "__ALL_PROCESSES__" in process_names:
+            process_names = None
+        order = (
+            RestartOrder.STARTUP_FIRST
+            if "STARTUP_FIRST" in split_message
+            else RestartOrder.SHUTDOWN_FIRST
+        )
+        self.restart(
+            process_names=process_names,
+            reloaded_files=reloaded_files,
+            restart_order=order,
+        )
+
+        return None
+
+    def _handle_manage(
+        self,
+        name: str,
+        func: Callable[..., Any],
+        kwargs: dict[str, Any],
+        transient: bool,
+        restartable: bool | None,
+        tracked: bool,
+        auto_start: bool,
+        workers: int,
+    ) -> None:
+        try:
+            worker = self.manage(
+                name,
+                func,
+                kwargs,
+                transient=transient,
+                restartable=restartable,
+                tracked=tracked,
+                auto_start=auto_start,
+                workers=workers,
+            )
+        except Exception:
+            error_logger.exception("Failed to manage worker %s", name)
+        else:
+            if not auto_start:
+                return
+            for process in worker.processes:
+                process.start()
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/multiplexer.py b/.venv/lib/python3.12/site-packages/sanic/worker/multiplexer.py
new file mode 100644
index 0000000..8cbb18c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/multiplexer.py
@@ -0,0 +1,169 @@
+from __future__ import annotations
+
+from multiprocessing.connection import Connection
+from os import environ, getpid
+from typing import Any, Callable
+
+from sanic.log import Colors, logger
+from sanic.worker.process import ProcessState
+from sanic.worker.state import WorkerState
+
+
+class WorkerMultiplexer:
+    """Multiplexer for Sanic workers.
+
+    This is instantiated inside of worker porocesses only. It is used to
+    communicate with the monitor process.
+
+    Args:
+        monitor_publisher (Connection): The connection to the monitor.
+        worker_state (Dict[str, Any]): The state of the worker.
+    """
+
+    def __init__(
+        self,
+        monitor_publisher: Connection,
+        worker_state: dict[str, Any],
+    ):
+        self._monitor_publisher = monitor_publisher
+        self._state = WorkerState(worker_state, self.name)
+
+    def ack(self):
+        """Acknowledge the worker is ready."""
+        logger.debug(
+            f"{Colors.BLUE}Process ack: {Colors.BOLD}{Colors.SANIC}"
+            f"%s {Colors.BLUE}[%s]{Colors.END}",
+            self.name,
+            self.pid,
+        )
+        self._state._state[self.name] = {
+            **self._state._state[self.name],
+            "state": ProcessState.ACKED.name,
+        }
+
+    def manage(
+        self,
+        ident: str,
+        func: Callable[..., Any],
+        kwargs: dict[str, Any],
+        transient: bool = False,
+        restartable: bool | None = None,
+        tracked: bool = False,
+        auto_start: bool = True,
+        workers: int = 1,
+    ) -> None:
+        """Manages the initiation and monitoring of a worker process.
+
+        Args:
+            ident (str): A unique identifier for the worker process.
+            func (Callable[..., Any]): The function to be executed in the worker process.
+            kwargs (Dict[str, Any]): A dictionary of arguments to be passed to `func`.
+            transient (bool, optional): Flag to mark the process as transient. If `True`,
+                the Worker Manager will restart the process with any global restart
+                (e.g., auto-reload). Defaults to `False`.
+            restartable (Optional[bool], optional): Flag to mark the process as restartable. If `True`,
+                the Worker Manager can restart the process if prompted. Defaults to `None`.
+            tracked (bool, optional): Flag to indicate whether the process should be tracked
+                after its completion. Defaults to `False`.
+            auto_start (bool, optional): Flag to indicate whether the process should be started
+            workers (int, optional): The number of worker processes to run. Defaults to 1.
+
+        This method packages the provided arguments into a bundle and sends them back to the
+        main process to be managed by the Worker Manager.
+        """  # noqa: E501
+        bundle = (
+            ident,
+            func,
+            kwargs,
+            transient,
+            restartable,
+            tracked,
+            auto_start,
+            workers,
+        )
+        self._monitor_publisher.send(bundle)
+
+    def set_serving(self, serving: bool) -> None:
+        """Set the worker to serving.
+
+        Args:
+            serving (bool): Whether the worker is serving.
+        """
+        self._state._state[self.name] = {
+            **self._state._state[self.name],
+            "serving": serving,
+        }
+
+    def exit(self):
+        """Run cleanup at worker exit."""
+        try:
+            del self._state._state[self.name]
+        except ConnectionRefusedError:
+            logger.debug("Monitor process has already exited.")
+
+    def restart(
+        self,
+        name: str = "",
+        all_workers: bool = False,
+        zero_downtime: bool = False,
+    ):
+        """Restart the worker.
+
+        Args:
+            name (str): The name of the process to restart.
+            all_workers (bool): Whether to restart all workers.
+            zero_downtime (bool): Whether to restart with zero downtime.
+        """
+        if name and all_workers:
+            raise ValueError(
+                "Ambiguous restart with both a named process and"
+                " all_workers=True"
+            )
+        if not name:
+            name = "__ALL_PROCESSES__:" if all_workers else self.name
+        if not name.endswith(":"):
+            name += ":"
+        if zero_downtime:
+            name += ":STARTUP_FIRST"
+        self._monitor_publisher.send(name)
+
+    reload = restart  # no cov
+    """Alias for restart."""
+
+    def scale(self, num_workers: int):
+        """Scale the number of workers.
+
+        Args:
+            num_workers (int): The number of workers to scale to.
+        """
+        message = f"__SCALE__:{num_workers}"
+        self._monitor_publisher.send(message)
+
+    def terminate(self, early: bool = False):
+        """Terminate the worker.
+
+        Args:
+            early (bool): Whether to terminate early.
+        """
+        message = "__TERMINATE_EARLY__" if early else "__TERMINATE__"
+        self._monitor_publisher.send(message)
+
+    @property
+    def pid(self) -> int:
+        """The process ID of the worker."""
+        return getpid()
+
+    @property
+    def name(self) -> str:
+        """The name of the worker."""
+        return environ.get("SANIC_WORKER_NAME", "")
+
+    @property
+    def state(self):
+        """The state of the worker."""
+        return self._state
+
+    @property
+    def workers(self) -> dict[str, Any]:
+        """The state of all workers."""
+        return self.state.full()
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/process.py b/.venv/lib/python3.12/site-packages/sanic/worker/process.py
new file mode 100644
index 0000000..6d52c92
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/process.py
@@ -0,0 +1,292 @@
+from __future__ import annotations
+
+import os
+
+from collections.abc import MutableMapping
+from datetime import datetime, timezone
+from inspect import signature
+from multiprocessing.context import BaseContext
+from signal import SIGINT
+from threading import Thread
+from time import sleep
+from typing import Any
+
+from sanic.log import Colors, logger
+from sanic.worker.constants import ProcessState, RestartOrder
+
+
+def get_now():
+    now = datetime.now(tz=timezone.utc)
+    return now
+
+
+class WorkerProcess:
+    """A worker process."""
+
+    THRESHOLD = 300  # == 30 seconds
+    SERVER_LABEL = "Server"
+    SERVER_IDENTIFIER = "Srv"
+
+    def __init__(
+        self,
+        factory,
+        name,
+        ident,
+        target,
+        kwargs,
+        worker_state,
+        restartable: bool = False,
+    ):
+        self.state = ProcessState.IDLE
+        self.factory = factory
+        self.name = name
+        self.ident = ident
+        self.target = target
+        self.kwargs = kwargs
+        self.worker_state = worker_state
+        self.restartable = restartable
+        if self.name not in self.worker_state:
+            self.worker_state[self.name] = {
+                "server": self.SERVER_LABEL in self.name
+            }
+        self.spawn()
+
+    def set_state(self, state: ProcessState, force=False):
+        if not force and state < self.state:
+            raise Exception("...")
+        self.state = state
+        self.worker_state[self.name] = {
+            **self.worker_state[self.name],
+            "state": self.state.name,
+        }
+
+    def start(self):
+        os.environ["SANIC_WORKER_NAME"] = self.name
+        os.environ["SANIC_WORKER_IDENTIFIER"] = self.ident
+        logger.debug(
+            f"{Colors.BLUE}Starting a process: {Colors.BOLD}"
+            f"{Colors.SANIC}%s{Colors.END}",
+            self.name,
+        )
+        self.set_state(ProcessState.STARTING)
+        self._current_process.start()
+        self.set_state(ProcessState.STARTED)
+        if not self.worker_state[self.name].get("starts"):
+            self.worker_state[self.name] = {
+                **self.worker_state[self.name],
+                "pid": self.pid,
+                "start_at": get_now(),
+                "starts": 1,
+            }
+        del os.environ["SANIC_WORKER_NAME"]
+        del os.environ["SANIC_WORKER_IDENTIFIER"]
+
+    def join(self):
+        self.set_state(ProcessState.JOINED)
+        self._current_process.join()
+
+    def exit(self):
+        limit = 100
+        while self.is_alive() and limit > 0:
+            sleep(0.1)
+            limit -= 1
+
+        if not self.is_alive():
+            try:
+                del self.worker_state[self.name]
+            except (
+                BrokenPipeError,
+                ConnectionRefusedError,
+                ConnectionResetError,
+                EOFError,
+            ):
+                logger.debug("Monitor process has already exited.")
+            except KeyError:
+                logger.debug("Could not find worker state to delete.")
+
+    def terminate(self):
+        if self.state is not ProcessState.TERMINATED:
+            logger.debug(
+                f"{Colors.BLUE}Terminating a process: "
+                f"{Colors.BOLD}{Colors.SANIC}"
+                f"%s {Colors.BLUE}[%s]{Colors.END}",
+                self.name,
+                self.pid,
+            )
+            try:
+                self.set_state(ProcessState.TERMINATED, force=True)
+            except (BrokenPipeError, ConnectionResetError, EOFError):
+                pass
+            try:
+                os.kill(self.pid, SIGINT)
+            except (KeyError, AttributeError, ProcessLookupError):
+                ...
+
+    def restart(self, restart_order=RestartOrder.SHUTDOWN_FIRST, **kwargs):
+        logger.debug(
+            f"{Colors.BLUE}Restarting a process: {Colors.BOLD}{Colors.SANIC}"
+            f"%s {Colors.BLUE}[%s]{Colors.END}",
+            self.name,
+            self.pid,
+        )
+        self.set_state(ProcessState.RESTARTING, force=True)
+        if restart_order is RestartOrder.SHUTDOWN_FIRST:
+            self._terminate_now()
+        else:
+            self._old_process = self._current_process
+        if self._add_config():
+            self.kwargs.update(
+                {"config": {k.upper(): v for k, v in kwargs.items()}}
+            )
+        try:
+            self.spawn()
+            self.start()
+        except AttributeError:
+            raise RuntimeError("Restart failed")
+
+        if restart_order is RestartOrder.STARTUP_FIRST:
+            self._terminate_soon()
+
+        self.worker_state[self.name] = {
+            **self.worker_state[self.name],
+            "pid": self.pid,
+            "starts": self.worker_state[self.name]["starts"] + 1,
+            "restart_at": get_now(),
+        }
+
+    def is_alive(self):
+        try:
+            return self._current_process.is_alive()
+        except AssertionError:
+            return False
+
+    def spawn(self):
+        if self.state not in (ProcessState.IDLE, ProcessState.RESTARTING):
+            raise Exception("Cannot spawn a worker process until it is idle.")
+        self._current_process = self.factory(
+            name=self.name,
+            target=self.target,
+            kwargs=self.kwargs,
+            daemon=True,
+        )
+
+    @property
+    def pid(self):
+        return self._current_process.pid
+
+    @property
+    def exitcode(self):
+        return self._current_process.exitcode
+
+    def _terminate_now(self):
+        if not self._current_process.is_alive():
+            return
+        logger.debug(
+            f"{Colors.BLUE}Begin restart termination: "
+            f"{Colors.BOLD}{Colors.SANIC}"
+            f"%s {Colors.BLUE}[%s]{Colors.END}",
+            self.name,
+            self._current_process.pid,
+        )
+        self._current_process.terminate()
+
+    def _terminate_soon(self):
+        logger.debug(
+            f"{Colors.BLUE}Begin restart termination: "
+            f"{Colors.BOLD}{Colors.SANIC}"
+            f"%s {Colors.BLUE}[%s]{Colors.END}",
+            self.name,
+            self._current_process.pid,
+        )
+        termination_thread = Thread(target=self._wait_to_terminate)
+        termination_thread.start()
+
+    def _wait_to_terminate(self):
+        logger.debug(
+            f"{Colors.BLUE}Waiting for process to be acked: "
+            f"{Colors.BOLD}{Colors.SANIC}"
+            f"%s {Colors.BLUE}[%s]{Colors.END}",
+            self.name,
+            self._old_process.pid,
+        )
+        misses = 0
+        while self.state is not ProcessState.ACKED:
+            sleep(0.1)
+            misses += 1
+            if misses > self.THRESHOLD:
+                raise TimeoutError(
+                    f"Worker {self.name} failed to come ack within "
+                    f"{self.THRESHOLD / 10} seconds"
+                )
+        else:
+            logger.debug(
+                f"{Colors.BLUE}Process acked. Terminating: "
+                f"{Colors.BOLD}{Colors.SANIC}"
+                f"%s {Colors.BLUE}[%s]{Colors.END}",
+                self.name,
+                self._old_process.pid,
+            )
+            self._old_process.terminate()
+        delattr(self, "_old_process")
+
+    def _add_config(self) -> bool:
+        sig = signature(self.target)
+        if "config" in sig.parameters or any(
+            param.kind == param.VAR_KEYWORD
+            for param in sig.parameters.values()
+        ):
+            return True
+        return False
+
+
+class Worker:
+    WORKER_PREFIX = "Sanic"
+
+    def __init__(
+        self,
+        ident: str,
+        name: str,
+        serve,
+        server_settings,
+        context: BaseContext,
+        worker_state: MutableMapping[str, Any],
+        num: int = 1,
+        restartable: bool = False,
+        tracked: bool = True,
+        auto_start: bool = True,
+    ):
+        self.ident = ident
+        self.name = name
+        self.num = num
+        self.context = context
+        self.serve = serve
+        self.server_settings = server_settings
+        self.worker_state = worker_state
+        self.processes: set[WorkerProcess] = set()
+        self.restartable = restartable
+        self.tracked = tracked
+        self.auto_start = auto_start
+        for _ in range(num):
+            self.create_process()
+
+    def create_process(self) -> WorkerProcess:
+        process = WorkerProcess(
+            # Need to ignore this typing error - The problem is the
+            # BaseContext itself has no Process. But, all of its
+            # implementations do. We can safely ignore as it is a typing
+            # issue in the standard lib.
+            factory=self.context.Process,  # type: ignore
+            name="-".join(
+                [self.WORKER_PREFIX, self.name, str(len(self.processes))]
+            ),
+            ident=self.ident,
+            target=self.serve,
+            kwargs={**self.server_settings},
+            worker_state=self.worker_state,
+            restartable=self.restartable,
+        )
+        self.processes.add(process)
+        return process
+
+    def has_alive_processes(self) -> bool:
+        return any(process.is_alive() for process in self.processes)
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/reloader.py b/.venv/lib/python3.12/site-packages/sanic/worker/reloader.py
new file mode 100644
index 0000000..a87ece0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/reloader.py
@@ -0,0 +1,122 @@
+from __future__ import annotations
+
+import os
+import sys
+
+from asyncio import new_event_loop
+from itertools import chain
+from multiprocessing.connection import Connection
+from pathlib import Path
+from signal import SIGINT, SIGTERM
+from signal import signal as signal_func
+from time import sleep
+
+from sanic.server.events import trigger_events
+from sanic.worker.loader import AppLoader
+
+
+class Reloader:
+    INTERVAL = 1.0  # seconds
+
+    def __init__(
+        self,
+        publisher: Connection,
+        interval: float,
+        reload_dirs: set[Path],
+        app_loader: AppLoader,
+    ):
+        self._publisher = publisher
+        self.interval = interval or self.INTERVAL
+        self.reload_dirs = reload_dirs
+        self.run = True
+        self.app_loader = app_loader
+
+    def __call__(self) -> None:
+        app = self.app_loader.load()
+        signal_func(SIGINT, self.stop)
+        signal_func(SIGTERM, self.stop)
+        mtimes: dict[str, float] = {}
+
+        reloader_start = app.listeners.get("reload_process_start")
+        reloader_stop = app.listeners.get("reload_process_stop")
+        before_trigger = app.listeners.get("before_reload_trigger")
+        after_trigger = app.listeners.get("after_reload_trigger")
+        loop = new_event_loop()
+        if reloader_start:
+            trigger_events(reloader_start, loop, app)
+
+        while self.run:
+            changed = set()
+            for filename in self.files():
+                try:
+                    if self.check_file(filename, mtimes):
+                        path = (
+                            filename
+                            if isinstance(filename, str)
+                            else filename.resolve()
+                        )
+                        changed.add(str(path))
+                except OSError:
+                    continue
+            if changed:
+                if before_trigger:
+                    trigger_events(before_trigger, loop, app)
+                self.reload(",".join(changed) if changed else "unknown")
+                if after_trigger:
+                    trigger_events(after_trigger, loop, app, changed=changed)
+            sleep(self.interval)
+        else:
+            if reloader_stop:
+                trigger_events(reloader_stop, loop, app)
+
+    def stop(self, *_):
+        self.run = False
+
+    def reload(self, reloaded_files):
+        message = f"__ALL_PROCESSES__:{reloaded_files}"
+        self._publisher.send(message)
+
+    def files(self):
+        return chain(
+            self.python_files(),
+            *(d.glob("**/*") for d in self.reload_dirs),
+        )
+
+    def python_files(self):  # no cov
+        """This iterates over all relevant Python files.
+
+        It goes through all
+        loaded files from modules, all files in folders of already loaded
+        modules as well as all files reachable through a package.
+        """
+        # The list call is necessary on Python 3 in case the module
+        # dictionary modifies during iteration.
+        for module in list(sys.modules.values()):
+            if module is None:
+                continue
+            filename = getattr(module, "__file__", None)
+            if filename:
+                old = None
+                while not os.path.isfile(filename):
+                    old = filename
+                    filename = os.path.dirname(filename)
+                    if filename == old:
+                        break
+                else:
+                    if filename[-4:] in (".pyc", ".pyo"):
+                        filename = filename[:-1]
+                    yield filename
+
+    @staticmethod
+    def check_file(filename, mtimes) -> bool:
+        need_reload = False
+
+        mtime = os.stat(filename).st_mtime
+        old_time = mtimes.get(filename)
+        if old_time is None:
+            mtimes[filename] = mtime
+        elif mtime > old_time:
+            mtimes[filename] = mtime
+            need_reload = True
+
+        return need_reload
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/restarter.py b/.venv/lib/python3.12/site-packages/sanic/worker/restarter.py
new file mode 100644
index 0000000..762b107
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/restarter.py
@@ -0,0 +1,90 @@
+from __future__ import annotations
+
+from sanic.log import error_logger
+from sanic.worker.constants import RestartOrder
+from sanic.worker.process import ProcessState, WorkerProcess
+
+
+class Restarter:
+    def restart(
+        self,
+        transient_processes: list[WorkerProcess],
+        durable_processes: list[WorkerProcess],
+        process_names: list[str] | None = None,
+        restart_order=RestartOrder.SHUTDOWN_FIRST,
+        **kwargs,
+    ) -> None:
+        """Restart the worker processes.
+
+        Args:
+            process_names (Optional[List[str]], optional): The names of the processes to restart.
+                If `None`, then all processes will be restarted. Defaults to `None`.
+            restart_order (RestartOrder, optional): The order in which to restart the processes.
+                Defaults to `RestartOrder.SHUTDOWN_FIRST`.
+        """  # noqa: E501
+        restarted = self._restart_transient(
+            transient_processes,
+            process_names or [],
+            restart_order,
+            **kwargs,
+        )
+        restarted |= self._restart_durable(
+            durable_processes,
+            process_names or [],
+            restart_order,
+            **kwargs,
+        )
+
+        if process_names and not restarted:
+            error_logger.error(
+                f"Failed to restart processes: {', '.join(process_names)}"
+            )
+
+    def _restart_transient(
+        self,
+        processes: list[WorkerProcess],
+        process_names: list[str],
+        restart_order: RestartOrder,
+        **kwargs,
+    ) -> set[str]:
+        restarted: set[str] = set()
+        for process in processes:
+            if not process.restartable or (
+                process_names and process.name not in process_names
+            ):
+                continue
+            self._restart_process(process, restart_order, **kwargs)
+            restarted.add(process.name)
+        return restarted
+
+    def _restart_durable(
+        self,
+        processes: list[WorkerProcess],
+        process_names: list[str],
+        restart_order: RestartOrder,
+        **kwargs,
+    ) -> set[str]:
+        restarted: set[str] = set()
+        if not process_names:
+            return restarted
+        for process in processes:
+            if not process.restartable or process.name not in process_names:
+                continue
+            if process.state not in (
+                ProcessState.COMPLETED,
+                ProcessState.FAILED,
+                ProcessState.NONE,
+            ):
+                error_logger.error(
+                    f"Cannot restart process {process.name} because "
+                    "it is not in a final state. Current state is: "
+                    f"{process.state.name}."
+                )
+                continue
+            self._restart_process(process, restart_order, **kwargs)
+            restarted.add(process.name)
+
+        return restarted
+
+    def _restart_process(self, process, restart_order, **kwargs):
+        process.restart(restart_order=restart_order, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/serve.py b/.venv/lib/python3.12/site-packages/sanic/worker/serve.py
new file mode 100644
index 0000000..b7594e1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/serve.py
@@ -0,0 +1,147 @@
+from __future__ import annotations
+
+import asyncio
+import os
+import socket
+import warnings
+
+from functools import partial
+from multiprocessing.connection import Connection
+from ssl import SSLContext
+from typing import Any
+
+from sanic.application.constants import ServerStage
+from sanic.application.state import ApplicationServerInfo
+from sanic.http.constants import HTTP
+from sanic.log import error_logger
+from sanic.logging.setup import setup_logging
+from sanic.models.server_types import Signal
+from sanic.server.protocols.http_protocol import HttpProtocol
+from sanic.server.runners import _serve_http_1, _serve_http_3
+from sanic.worker.loader import AppLoader, CertLoader
+from sanic.worker.multiplexer import WorkerMultiplexer
+from sanic.worker.process import Worker, WorkerProcess
+
+
+def worker_serve(
+    host,
+    port,
+    app_name: str,
+    monitor_publisher: Connection | None,
+    app_loader: AppLoader,
+    worker_state: dict[str, Any] | None = None,
+    server_info: dict[str, list[ApplicationServerInfo]] | None = None,
+    ssl: SSLContext | dict[str, str | os.PathLike[str]] | None = None,
+    sock: socket.socket | None = None,
+    unix: str | None = None,
+    reuse_port: bool = False,
+    loop=None,
+    protocol: type[asyncio.Protocol] = HttpProtocol,
+    backlog: int = 100,
+    register_sys_signals: bool = True,
+    run_multiple: bool = False,
+    run_async: bool = False,
+    connections=None,
+    signal=Signal(),
+    state=None,
+    asyncio_server_kwargs=None,
+    version=HTTP.VERSION_1,
+    config: bytes | str | dict[str, Any] | Any | None = None,
+    passthru: dict[str, Any] | None = None,
+):
+    try:
+        from sanic import Sanic
+
+        if app_loader:
+            app = app_loader.load()
+        else:
+            app = Sanic.get_app(app_name)
+
+        app.refresh(passthru)
+        app.setup_loop()
+        setup_logging(
+            app.state.is_debug, app.config.NO_COLOR, app.config.LOG_EXTRA
+        )
+
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+
+        # Hydrate server info if needed
+        if server_info:
+            for app_name, server_info_objects in server_info.items():
+                a = Sanic.get_app(app_name)
+                if not a.state.server_info:
+                    a.state.server_info = []
+                    for info in server_info_objects:
+                        if not info.settings.get("app"):
+                            info.settings["app"] = a
+                        a.state.server_info.append(info)
+
+        if isinstance(ssl, dict) or app.certloader_class is not CertLoader:
+            cert_loader = app.certloader_class(ssl or {})
+            ssl = cert_loader.load(app)
+            for info in app.state.server_info:
+                info.settings["ssl"] = ssl
+
+        # When in a worker process, do some init
+        worker_name = os.environ.get("SANIC_WORKER_NAME")
+        if worker_name and worker_name.startswith(
+            "-".join([Worker.WORKER_PREFIX, WorkerProcess.SERVER_LABEL])
+        ):
+            # Hydrate apps with any passed server info
+
+            if monitor_publisher is None:
+                raise RuntimeError(
+                    "No restart publisher found in worker process"
+                )
+            if worker_state is None:
+                raise RuntimeError("No worker state found in worker process")
+
+            # Run secondary servers
+            apps = list(Sanic._app_registry.values())
+            app.before_server_start(partial(app._start_servers, apps=apps))
+            for a in apps:
+                a.multiplexer = WorkerMultiplexer(
+                    monitor_publisher, worker_state
+                )
+
+        if app.debug:
+            loop.set_debug(app.debug)
+
+        app.asgi = False
+
+        if app.state.server_info:
+            primary_server_info = app.state.server_info[0]
+            primary_server_info.stage = ServerStage.SERVING
+        if config:
+            app.update_config(config)
+
+        if version is HTTP.VERSION_3:
+            return _serve_http_3(host, port, app, loop, ssl)
+        return _serve_http_1(
+            host,
+            port,
+            app,
+            ssl,
+            sock,
+            unix,
+            reuse_port,
+            loop,
+            protocol,
+            backlog,
+            register_sys_signals,
+            run_multiple,
+            run_async,
+            connections,
+            signal,
+            state,
+            asyncio_server_kwargs,
+        )
+    except Exception as e:
+        warnings.simplefilter("ignore", category=RuntimeWarning)
+        if monitor_publisher:
+            error_logger.exception(e)
+            multiplexer = WorkerMultiplexer(monitor_publisher, {})
+            multiplexer.terminate(True)
+        else:
+            raise e
diff --git a/.venv/lib/python3.12/site-packages/sanic/worker/state.py b/.venv/lib/python3.12/site-packages/sanic/worker/state.py
new file mode 100644
index 0000000..9aa25b6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic/worker/state.py
@@ -0,0 +1,83 @@
+from __future__ import annotations
+
+from collections.abc import ItemsView, Iterator, KeysView, Mapping, ValuesView
+from collections.abc import Mapping as MappingType
+from typing import Any
+
+
+class WorkerState(Mapping):
+    RESTRICTED = (
+        "health",
+        "pid",
+        "requests",
+        "restart_at",
+        "server",
+        "start_at",
+        "starts",
+        "state",
+    )
+
+    def __init__(self, state: dict[str, Any], current: str) -> None:
+        self._name = current
+        self._state = state
+
+    def __getitem__(self, key: str) -> Any:
+        return self._state[self._name][key]
+
+    def __setitem__(self, key: str, value: Any) -> None:
+        if key in self.RESTRICTED:
+            self._write_error([key])
+        self._state[self._name] = {
+            **self._state[self._name],
+            key: value,
+        }
+
+    def __delitem__(self, key: str) -> None:
+        if key in self.RESTRICTED:
+            self._write_error([key])
+        self._state[self._name] = {
+            k: v for k, v in self._state[self._name].items() if k != key
+        }
+
+    def __iter__(self) -> Iterator[Any]:
+        return iter(self._state[self._name])
+
+    def __len__(self) -> int:
+        return len(self._state[self._name])
+
+    def __repr__(self) -> str:
+        return repr(self._state[self._name])
+
+    def __eq__(self, other: object) -> bool:
+        return self._state[self._name] == other
+
+    def keys(self) -> KeysView[str]:
+        return self._state[self._name].keys()
+
+    def values(self) -> ValuesView[Any]:
+        return self._state[self._name].values()
+
+    def items(self) -> ItemsView[str, Any]:
+        return self._state[self._name].items()
+
+    def update(self, mapping: MappingType[str, Any]) -> None:
+        if any(k in self.RESTRICTED for k in mapping.keys()):
+            self._write_error(
+                [k for k in mapping.keys() if k in self.RESTRICTED]
+            )
+        self._state[self._name] = {
+            **self._state[self._name],
+            **mapping,
+        }
+
+    def pop(self) -> None:
+        raise NotImplementedError
+
+    def full(self) -> dict[str, Any]:
+        return dict(self._state)
+
+    def _write_error(self, keys: list[str]) -> None:
+        raise LookupError(
+            f"Cannot set restricted key{'s' if len(keys) > 1 else ''} on "
+            f"WorkerState: {', '.join(keys)}"
+        )
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/LICENSE
new file mode 100644
index 0000000..1377edf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Channel Cat
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/METADATA
new file mode 100644
index 0000000..68a015c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/METADATA
@@ -0,0 +1,136 @@
+Metadata-Version: 2.2
+Name: sanic-ext
+Version: 24.12.0
+Summary: Extend your Sanic installation with some core functionality.
+Home-page: http://github.com/sanic-org/sanic-ext/
+Author: Sanic Community
+License: MIT
+Platform: any
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: License :: OSI Approved :: MIT License
+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
+License-File: LICENSE
+Requires-Dist: pyyaml>=3.0.0
+Provides-Extra: test
+Requires-Dist: sanic_testing>=22.9.0; extra == "test"
+Requires-Dist: coverage; extra == "test"
+Requires-Dist: pytest; extra == "test"
+Requires-Dist: pytest-cov; extra == "test"
+Requires-Dist: pytest-asyncio; extra == "test"
+Requires-Dist: tox; extra == "test"
+Requires-Dist: Jinja2; extra == "test"
+Provides-Extra: dev
+Requires-Dist: sanic_testing>=22.9.0; extra == "dev"
+Requires-Dist: coverage; extra == "dev"
+Requires-Dist: pytest; extra == "dev"
+Requires-Dist: pytest-cov; extra == "dev"
+Requires-Dist: pytest-asyncio; extra == "dev"
+Requires-Dist: tox; extra == "dev"
+Requires-Dist: Jinja2; extra == "dev"
+Requires-Dist: black>=21.4b2; extra == "dev"
+Requires-Dist: flake8>=3.7.7; extra == "dev"
+Requires-Dist: isort>=5.0.0; extra == "dev"
+
+.. image:: https://raw.githubusercontent.com/sanic-org/sanic-assets/master/png/sanic-framework-logo-400x97.png
+    :alt: Sanic | Build fast. Run fast.
+
+Sanic Extensions
+================
+
+.. start-badges
+
+.. list-table::
+    :widths: 15 85
+    :stub-columns: 1
+
+    * - Build
+      - | |PyTest|
+    * - Docs
+      - | |UserGuide|
+    * - Package
+      - | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
+    * - Support
+      - | |Forums| |Discord|
+
+
+.. |UserGuide| image:: https://img.shields.io/badge/user%20guide-sanic-ff0068
+   :target: https://sanicframework.org/en/plugins/sanic-ext/getting-started.html
+.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
+   :target: https://community.sanicframework.org/
+.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
+   :target: https://discord.gg/FARQzAEMAA
+.. |PyTest| image:: https://github.com/sanic-org/sanic-ext/actions/workflows/python-package.yml/badge.svg?branch=main
+   :target: https://github.com/sanic-org/sanic-ext/actions/workflows/python-package.yml
+.. |PyPI| image:: https://img.shields.io/pypi/v/sanic-ext.svg
+   :target: https://pypi.python.org/pypi/sanic-ext/
+.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic-ext.svg
+   :target: https://pypi.python.org/pypi/sanic-ext/
+.. |Code style black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+    :target: https://github.com/ambv/black
+.. |Wheel| image:: https://img.shields.io/pypi/wheel/sanic-ext.svg
+    :alt: PyPI Wheel
+    :target: https://pypi.python.org/pypi/sanic-ext
+.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic-ext.svg
+    :alt: Supported implementations
+    :target: https://pypi.python.org/pypi/sanic-ext
+
+
+.. end-badges
+
+
+`Sanic `_ strives to be "Unopinionated and flexible"::
+
+    Build the way you want to build without letting your tooling constrain you.
+
+But what happens when you want all the goodies? Sanic Extensions is an officially supported Sanic plugin to provide application developers with additional tools and features.
+
+Features
+--------
+
+- Auto create HEAD, OPTIONS, and TRACE endpoints
+- CORS protection
+- Predefined, endpoint-specific response serializers
+- Argument injection into route handlers
+- OpenAPI documentation with Redoc and/or Swagger
+- Request query arguments and body input validation
+
+
+Installation
+------------
+
+::
+
+    pip install sanic[ext]
+    # OR
+    pip install sanic sanic-ext
+
+
+Getting started
+---------------
+
+
+.. code-block:: python
+
+    from sanic import Sanic
+
+    app = Sanic("MyHelloWorldApp")
+    
+Nothing new. Just start using Sanic and it will automatically be extended!
+
+
+Learn more
+----------
+
+
+Go to the `User Guide `_ to learn more
+
+____
+
+.. warning:: Sanic Extensions is still in **ALPHA** release. The API is not likely to change. It will move to **BETA** with v22.3.
+
+
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/RECORD
new file mode 100644
index 0000000..50a6887
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/RECORD
@@ -0,0 +1,214 @@
+sanic_ext-24.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+sanic_ext-24.12.0.dist-info/LICENSE,sha256=phDSSxlr1h3SszXLqcXDk26YIBY3nz5_d2WS2kYZ28Q,1068
+sanic_ext-24.12.0.dist-info/METADATA,sha256=djT-WyscY1IcIVnJpP9SN7yQNhi61ywVltOmwL7iB-o,4462
+sanic_ext-24.12.0.dist-info/RECORD,,
+sanic_ext-24.12.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext-24.12.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
+sanic_ext-24.12.0.dist-info/top_level.txt,sha256=f1EdKcWF8djoLd_5PE6YKONyV4bOwhDnsf0jbhhTc40,16
+sanic_ext/__init__.py,sha256=53CUwx8hdD70V8PIe-YETUzpcB4HKuiaRGWuPosXOiw,684
+sanic_ext/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/__pycache__/bootstrap.cpython-312.pyc,,
+sanic_ext/__pycache__/config.cpython-312.pyc,,
+sanic_ext/__pycache__/exceptions.cpython-312.pyc,,
+sanic_ext/bootstrap.py,sha256=v0u4aX84LpRY0tVm6jzCKBSsH5CN42mgL-qthxOVtm8,7674
+sanic_ext/config.py,sha256=YdeRXTqLDFJ6ibw6k1XvGTjsQ3XSceYTIN3mdQIYeeQ,7096
+sanic_ext/exceptions.py,sha256=U4OF4sz3B7O-092mHNJFRq-CNI671YwxSadagRwPvM0,193
+sanic_ext/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/__pycache__/base.cpython-312.pyc,,
+sanic_ext/extensions/base.py,sha256=fZcUUH0E_XRLg9JStwL92WAO_zq-yVCI_808PVOXoSI,2152
+sanic_ext/extensions/health/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/health/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/health/__pycache__/endpoint.cpython-312.pyc,,
+sanic_ext/extensions/health/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/health/__pycache__/monitor.cpython-312.pyc,,
+sanic_ext/extensions/health/endpoint.py,sha256=SuMCjiVIw2_Bhii07k0MefeobBHN6GIgWujvbGjdXYY,421
+sanic_ext/extensions/health/extension.py,sha256=wvsFNLHbACw8L4mSm2C2sFYKJk6oLasqcM2oNwEHtEk,1052
+sanic_ext/extensions/health/monitor.py,sha256=uB-cbjvcHnlKgeFRiwnMyILj3ZfAWA4vcG4qhiIUlfs,4494
+sanic_ext/extensions/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/http/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/http/__pycache__/cors.cpython-312.pyc,,
+sanic_ext/extensions/http/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/http/__pycache__/methods.cpython-312.pyc,,
+sanic_ext/extensions/http/cors.py,sha256=eUGifKcTvGr_RBdtlKpJfpP9qY0XxPoDqal18Q7v4fw,13104
+sanic_ext/extensions/http/extension.py,sha256=-LD0WwDL_h-3O5bYaHNwcS4S3NdHMA75MHfJMfk62Dk,1162
+sanic_ext/extensions/http/methods.py,sha256=q1yinqne1YJrZVaMFSINmlY3HaRYvxy1XonXoKUW8bE,5417
+sanic_ext/extensions/injection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/injection/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/injection/__pycache__/constructor.cpython-312.pyc,,
+sanic_ext/extensions/injection/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/injection/__pycache__/injector.cpython-312.pyc,,
+sanic_ext/extensions/injection/__pycache__/registry.cpython-312.pyc,,
+sanic_ext/extensions/injection/constructor.py,sha256=uwuvfL0e5OpU4poiYFJa2O2VMQ-NZaxay0trEAvkY4w,5819
+sanic_ext/extensions/injection/extension.py,sha256=Rz4Eik1shCddw6KmhdWLNzdZGTaT8--YXhx6VDZ_ATA,783
+sanic_ext/extensions/injection/injector.py,sha256=7CacEAPbm672W_aPdQXfWr0PKC5ZhRUJp4_kEmkbj7o,4300
+sanic_ext/extensions/injection/registry.py,sha256=c4jWBSOrtSqzeZQ8h2-CVBgOsTt0d3_HbBnoeP3F-lo,3126
+sanic_ext/extensions/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/logging/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/logging/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/logging/__pycache__/extractor.cpython-312.pyc,,
+sanic_ext/extensions/logging/__pycache__/logger.cpython-312.pyc,,
+sanic_ext/extensions/logging/extension.py,sha256=zzadB1FO9PDkDrc1ZQkXiDi08TJ7FNlpAKT0gFwc_J4,797
+sanic_ext/extensions/logging/extractor.py,sha256=cvGdz2xppJMWG5qJVt9dDvJhVqJb0XoDS0fAULQ8EEI,3572
+sanic_ext/extensions/logging/logger.py,sha256=PO4J8Eqs8z94Yljqqp9yg4a2Hmc_Xv3Mr7ORxOck-4M,3770
+sanic_ext/extensions/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/openapi/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/autodoc.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/blueprint.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/builders.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/constants.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/definitions.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/openapi.cpython-312.pyc,,
+sanic_ext/extensions/openapi/__pycache__/types.cpython-312.pyc,,
+sanic_ext/extensions/openapi/autodoc.py,sha256=BA9UfZDaNir92kFuSA6noLKOUSVPXxW6RTejUvJXjzI,2683
+sanic_ext/extensions/openapi/blueprint.py,sha256=idXjTNbZPQEQM1A9SQ9z0Q0MdOpm_MnKq_L5jqV9O2g,9434
+sanic_ext/extensions/openapi/builders.py,sha256=WbuEGcjJLZs7zHV3aJT0OOmoNE1z09SdwDMEGqprs5s,12944
+sanic_ext/extensions/openapi/constants.py,sha256=UROnLKuzp4BaN0UrdzlsZoval24-xQ5opnA7uKrPHMM,821
+sanic_ext/extensions/openapi/definitions.py,sha256=sIFiXA8ZCzwN51RkbRU5-wGfoA5c1n-awFVTpxdjbI8,11761
+sanic_ext/extensions/openapi/extension.py,sha256=FGtKzc_6t0O5l-ooGQHLtQN6i987ziBCNlyrKUGEXjM,1108
+sanic_ext/extensions/openapi/openapi.py,sha256=AQhE94fg8in2VXchKgeMQnfMLQPe5uu8ZavKh0fDaXE,13828
+sanic_ext/extensions/openapi/types.py,sha256=3ovGBHJ_aes_BT54PFbZ_vLEwIOAWl6hXBfuxOEBmrQ,12430
+sanic_ext/extensions/openapi/ui/redoc.html,sha256=Veo0BrNQKNAF_ux_-jJMEQDA6JmOtJoPagYl_ufQ9Jg,672
+sanic_ext/extensions/openapi/ui/swagger.html,sha256=AMv4u5rM6JH6bPhVpAQY8OLMc-3Nz6VN_AQOL_DseYE,1477
+sanic_ext/extensions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extensions/templating/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extensions/templating/__pycache__/engine.cpython-312.pyc,,
+sanic_ext/extensions/templating/__pycache__/extension.cpython-312.pyc,,
+sanic_ext/extensions/templating/__pycache__/render.cpython-312.pyc,,
+sanic_ext/extensions/templating/engine.py,sha256=UoZYLmIsZHscfNZI_BfbmBg-4eZzZ7apOyrJAuvCg28,2346
+sanic_ext/extensions/templating/extension.py,sha256=bhpg3p1FDREiv7ulqrTugaur90zfAX_EatIiMqbSn38,1622
+sanic_ext/extensions/templating/render.py,sha256=chJXXR1FExVrn1R0qz6s7TLXrQgTw6oSKuzTGPgiSuI,2930
+sanic_ext/extras/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extras/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extras/__pycache__/request.cpython-312.pyc,,
+sanic_ext/extras/request.py,sha256=7F73U6OHVV67zZQAsl2UpUOj77TAwxaVVgiUMXkZ0Uk,1060
+sanic_ext/extras/serializer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extras/serializer/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extras/serializer/__pycache__/decorator.cpython-312.pyc,,
+sanic_ext/extras/serializer/decorator.py,sha256=tckv8kcd7M8fd4svUoQNTn3rT7xzufb7VAyqZZpE37U,1021
+sanic_ext/extras/validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/extras/validation/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/check.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/clean.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/decorator.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/schema.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/setup.cpython-312.pyc,,
+sanic_ext/extras/validation/__pycache__/validators.cpython-312.pyc,,
+sanic_ext/extras/validation/check.py,sha256=B2yYt9LbobedSo5l7j8OB4FqsPs4_tr3a5O1-zPJPY4,8399
+sanic_ext/extras/validation/clean.py,sha256=GviU_h2FseLa7F1IBlfxEnWyZLzWc7jC30SYDSVVzxY,452
+sanic_ext/extras/validation/decorator.py,sha256=SfjHnvGygnOCHt7te9TvIPXSEYJ15Oct-LM9jbNSD8U,2482
+sanic_ext/extras/validation/schema.py,sha256=olxs42uhadQehBqT1HS1689XspkxTJ0dqqKDCjYPEzw,3301
+sanic_ext/extras/validation/setup.py,sha256=T2gwRV8yGg9BHcDzOEyrUzS_-a6JGlBhjJBOSyMpKKQ,1843
+sanic_ext/extras/validation/validators.py,sha256=MXq29WxL6Jd1czbPv7EJvt3gJ1jbLeUjh4Z8byIhyV8,1446
+sanic_ext/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_ext/utils/__pycache__/__init__.cpython-312.pyc,,
+sanic_ext/utils/__pycache__/extraction.cpython-312.pyc,,
+sanic_ext/utils/__pycache__/route.cpython-312.pyc,,
+sanic_ext/utils/__pycache__/string.cpython-312.pyc,,
+sanic_ext/utils/__pycache__/typing.cpython-312.pyc,,
+sanic_ext/utils/__pycache__/version.cpython-312.pyc,,
+sanic_ext/utils/extraction.py,sha256=3jU-zH4DYsCs_DP6VmoHqSo9Nx5i84py7hYtCS_8j8c,342
+sanic_ext/utils/route.py,sha256=Zz0cXvUGUiHkikpU0BLnkx2Dzouz2okZmko-XTVcYT8,3790
+sanic_ext/utils/string.py,sha256=YPn6aNVWAMxQpVEkXDQNdPxjqu-4COvjyE6UAq1JIIw,268
+sanic_ext/utils/typing.py,sha256=65Ub9VsUKc9qwp1GNASmbIO102RZFjitbpP678jOW-Q,1633
+sanic_ext/utils/version.py,sha256=9JTxgtCdDSFJfdIKveABHmS-hz2DmMW-sEYac6xSSG8,1320
+tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/__pycache__/__init__.cpython-312.pyc,,
+tests/__pycache__/conftest.cpython-312.pyc,,
+tests/conftest.py,sha256=Hs8nV0gaW8V4cl6pSUDHiaBPI7s0Jx0LHAWMhEh6MrM,1172
+tests/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/__pycache__/test_startup.cpython-312.pyc,,
+tests/extensions/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/http/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/http/__pycache__/test_methods.cpython-312.pyc,,
+tests/extensions/http/test_methods.py,sha256=rAkCW01UXDQ94JBpXmLr2IjM97Ikg0KAQ1j0RkzvdHM,3939
+tests/extensions/injection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/injection/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/injection/__pycache__/test_add_dependency.cpython-312.pyc,,
+tests/extensions/injection/__pycache__/test_constants.cpython-312.pyc,,
+tests/extensions/injection/__pycache__/test_dependency.cpython-312.pyc,,
+tests/extensions/injection/__pycache__/test_injection_config.cpython-312.pyc,,
+tests/extensions/injection/__pycache__/test_injection_registry.cpython-312.pyc,,
+tests/extensions/injection/test_add_dependency.py,sha256=vDly-OQXX3mRdmI4n2w_t1EPTdFF1_gNiGEz1L9QSsM,10475
+tests/extensions/injection/test_constants.py,sha256=I31TKB5lOmq44dmw3RvfkhptW4SgqOiFHNeWaHQKH4Y,1831
+tests/extensions/injection/test_dependency.py,sha256=6qsp7jL2-FAqSCpZdBCy7UhhvGHtYHlt6SrpWlrUpiU,919
+tests/extensions/injection/test_injection_config.py,sha256=Gg9vtH52Jpljx5DDEbOuzza3wO94dEOR3tun8ZjWCXI,1138
+tests/extensions/injection/test_injection_registry.py,sha256=K1MXLPzmwyeU31dqOXA6dU9A5j9H3XWW337yiywoblM,5063
+tests/extensions/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/logging/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/logging/__pycache__/test_custom_background_logger.cpython-312.pyc,,
+tests/extensions/logging/test_custom_background_logger.py,sha256=GmxNmRFix5hRrK9K3-Aa94S9vlao-uUPKATxa_GLvBs,526
+tests/extensions/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/openapi/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_autodoc.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_body.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_config.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_decorators.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_definitions.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_deprecated.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_exclude.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_external_docs.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_func_handler.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_model_fields.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_model_spec.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_parameter.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_paths.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_schema.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_security.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_specification.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_summary.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_tag.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/test_typing.cpython-312.pyc,,
+tests/extensions/openapi/__pycache__/utils.cpython-312.pyc,,
+tests/extensions/openapi/test_autodoc.py,sha256=q96Se_gXuV66SjLLEfnaGqR75k5Wl5GiU8F4HxKHCaY,899
+tests/extensions/openapi/test_body.py,sha256=H-EiSpYEa25_lPFqfqqVuLlV0MOUXGcCK3HQG2VpJ0o,2301
+tests/extensions/openapi/test_config.py,sha256=jIqIyEUMQzLe_GsutoeZSjqUwEadqmR8t0JzOlZrhRc,745
+tests/extensions/openapi/test_decorators.py,sha256=WNSwlNgxYn2mbVJi2Ii3HSAd56QoQuH98WcqiWIkF-4,24247
+tests/extensions/openapi/test_definitions.py,sha256=1x_UpJHvjb_mXQ1So4tYfcyHQ_QCw6vHiqAqrmF_qqU,1315
+tests/extensions/openapi/test_deprecated.py,sha256=BsiB-AXROReIQZShKFxjVN_gKAzwVBNABN1I8tDfL1w,897
+tests/extensions/openapi/test_exclude.py,sha256=pNcO-kWjUKFtLB6KstN7XkzN4BzLE05P4weQFuo_6tQ,1252
+tests/extensions/openapi/test_external_docs.py,sha256=glR1uUh10A0uSkT6h_EN-SIQETFJM4FbmixMjBS5dFo,1590
+tests/extensions/openapi/test_func_handler.py,sha256=wF4D2SelLRLAeuaKfiIVYmfum7Lnf0fgSEQ30HSr_mA,1427
+tests/extensions/openapi/test_model_fields.py,sha256=PVhskMLVe6faMWCuYPdAmYTFc16J0bQ9K2pjTYCf7DM,2386
+tests/extensions/openapi/test_model_spec.py,sha256=DiuiEL_f7UCdcd9FSCqTW8b1RLGY-4m5kyGoUZB5Wxo,2392
+tests/extensions/openapi/test_parameter.py,sha256=70__ya_f3lZhRldvgt7GtI8rLnPVMQaFI78uICHMyTM,4050
+tests/extensions/openapi/test_paths.py,sha256=BdJGj4fXo8wUknPOvJIMvwJNjggkhV1d0nWpxILtx40,1124
+tests/extensions/openapi/test_schema.py,sha256=MY0J9edoIrcbK7ZmZ6vl3VawhChYGIYAjLQHA-6gFy4,1877
+tests/extensions/openapi/test_security.py,sha256=_0OUDpvn64U594mcCQGr1fMJlx4SQgtx8Kjw_8XcRTQ,2603
+tests/extensions/openapi/test_specification.py,sha256=aOklHeLVa1w1yPBTFzjc1PMSdYx4ax7_zJehG28VhE0,884
+tests/extensions/openapi/test_summary.py,sha256=mHCIVYHqKsRSt3__VfXkXEa3E8zc9f0GOoxx_vzqXog,929
+tests/extensions/openapi/test_tag.py,sha256=bWKV20zMzUwi2CtFimEgE2J32kBZvTQ9QyH1sVlQT3A,827
+tests/extensions/openapi/test_typing.py,sha256=QKJeu3wLazD3LbKwicKTOP4WRQ-iAcHN1HTYTDzm_vU,1172
+tests/extensions/openapi/utils.py,sha256=Z35rTOh3oHFZOdY4OMhArarlwuwwolNBJ9TxyE9tRNM,359
+tests/extensions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/templating/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/templating/__pycache__/test_templating.cpython-312.pyc,,
+tests/extensions/templating/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extensions/templating/templates/__pycache__/__init__.cpython-312.pyc,,
+tests/extensions/templating/test_templating.py,sha256=ovVBJrpNnKJqp8n34UZxLCAq_1TYutk2qMdoii33OAc,3902
+tests/extensions/test_startup.py,sha256=pXOFL1CaMJ9z-bEhpfhmonWF5mqeURryR0dA3l4ZRM0,1433
+tests/extra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tests/extra/__models__.py,sha256=5Swev3NJkwHX9RmRuxkHBqRIaN6jb-lD2TQtnG1Vyaw,2225
+tests/extra/__pycache__/__init__.cpython-312.pyc,,
+tests/extra/__pycache__/__models__.cpython-312.pyc,,
+tests/extra/__pycache__/test_parse_hint.cpython-312.pyc,,
+tests/extra/__pycache__/test_request_counted.cpython-312.pyc,,
+tests/extra/__pycache__/test_serializer.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation_attrs.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation_dataclass.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation_msgspec.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation_multiple.cpython-312.pyc,,
+tests/extra/__pycache__/test_validation_pydantic.cpython-312.pyc,,
+tests/extra/test_parse_hint.py,sha256=Vl99-P74b_Bg8K_em8_qtR7FodS5FvebnBrbayILB8E,658
+tests/extra/test_request_counted.py,sha256=VJ_jRwRMcGQM2AqoB2FvGp2POYk-NWVEtmmTp2B7bAY,1042
+tests/extra/test_serializer.py,sha256=YHpLlxGjfZtLBbp1Q-gDOcQkNOmKXfYAlTxBIHatr6o,1903
+tests/extra/test_validation.py,sha256=GD83ByyAh1g4zJcOTOvF5AY68SEnSwZdidqkAK1RXAI,1889
+tests/extra/test_validation_attrs.py,sha256=NtBfBK-Wy8s-clYStNtLiG9yEvyyKqT8kwiUibN3V1A,3279
+tests/extra/test_validation_dataclass.py,sha256=Py_THhj8zLp239U_0JebCqdU0AraOkpFbCrinX3EFfk,16570
+tests/extra/test_validation_msgspec.py,sha256=Jm0B0prnc9me1STcxkQzxYUAOFLFcPr-1fgXO0x-ZLc,16168
+tests/extra/test_validation_multiple.py,sha256=cSWr--F28AqubhNHhwd07-iaiobDaRAb6OVIUmS4xXA,907
+tests/extra/test_validation_pydantic.py,sha256=AJoByFzUZ9YxHddxe19GYLX7uDIj81B3guRV29uIkKY,4342
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/WHEEL
new file mode 100644
index 0000000..ec2f44e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (75.8.2)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/top_level.txt
new file mode 100644
index 0000000..6c718b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext-24.12.0.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+sanic_ext
+tests
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/__init__.py
new file mode 100644
index 0000000..af0b413
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/__init__.py
@@ -0,0 +1,26 @@
+from importlib.metadata import version
+
+from sanic_ext.bootstrap import Extend
+from sanic_ext.config import Config
+from sanic_ext.extensions.base import Extension
+from sanic_ext.extensions.http.cors import cors
+from sanic_ext.extensions.openapi import openapi
+from sanic_ext.extensions.templating.render import render
+from sanic_ext.extras.request import CountedRequest
+from sanic_ext.extras.serializer.decorator import serializer
+from sanic_ext.extras.validation.decorator import validate
+
+
+__version__ = version("sanic-ext")
+
+__all__ = [
+    "Config",
+    "CountedRequest",
+    "Extend",
+    "Extension",
+    "cors",
+    "openapi",
+    "render",
+    "serializer",
+    "validate",
+]
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/bootstrap.py b/.venv/lib/python3.12/site-packages/sanic_ext/bootstrap.py
new file mode 100644
index 0000000..942ee97
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/bootstrap.py
@@ -0,0 +1,224 @@
+from __future__ import annotations
+
+import os
+
+from collections.abc import Mapping
+from types import SimpleNamespace
+from typing import Any, Callable, Optional, Union
+from warnings import warn
+
+from sanic import Sanic, __version__
+from sanic.config import DEFAULT_CONFIG
+from sanic.exceptions import SanicException
+from sanic.helpers import Default, _default
+from sanic.log import logger
+
+from sanic_ext.config import Config, add_fallback_config
+from sanic_ext.extensions.base import Extension
+from sanic_ext.extensions.health.extension import HealthExtension
+from sanic_ext.extensions.http.extension import HTTPExtension
+from sanic_ext.extensions.injection.extension import InjectionExtension
+from sanic_ext.extensions.injection.registry import (
+    ConstantRegistry,
+    InjectionRegistry,
+)
+from sanic_ext.extensions.logging.extension import LoggingExtension
+from sanic_ext.extensions.openapi.builders import SpecificationBuilder
+from sanic_ext.extensions.openapi.extension import OpenAPIExtension
+from sanic_ext.utils.string import camel_to_snake
+from sanic_ext.utils.version import get_version
+
+
+try:
+    from jinja2 import Environment
+
+    from sanic_ext.extensions.templating.engine import Templating
+    from sanic_ext.extensions.templating.extension import TemplatingExtension
+
+    TEMPLATING_ENABLED = True
+except (ImportError, ModuleNotFoundError):
+    TEMPLATING_ENABLED = False
+
+MIN_SUPPORT = (21, 3, 2)
+
+
+class Extend:
+    _pre_registry: list[Union[type[Extension], Extension]] = []
+
+    if TEMPLATING_ENABLED:
+        environment: Environment
+        templating: Templating
+
+    def __init__(
+        self,
+        app: Sanic,
+        *,
+        extensions: Optional[list[Union[type[Extension], Extension]]] = None,
+        built_in_extensions: bool = True,
+        config: Optional[Union[Config, dict[str, Any]]] = None,
+        **kwargs,
+    ) -> None:
+        """
+        Ingress for instantiating sanic-ext
+
+        :param app: Sanic application
+        :type app: Sanic
+        """
+        if not isinstance(app, Sanic):
+            raise SanicException(
+                f"Cannot apply SanicExt to {app.__class__.__name__}"
+            )
+
+        sanic_version = get_version(__version__)
+
+        if MIN_SUPPORT > sanic_version:
+            min_version = ".".join(map(str, MIN_SUPPORT))
+            raise SanicException(
+                f"Sanic Extensions only works with Sanic v{min_version} "
+                f"and above. It looks like you are running {__version__}."
+            )
+
+        self._injection_registry: Optional[InjectionRegistry] = None
+        self._constant_registry: Optional[ConstantRegistry] = None
+        self._openapi: Optional[SpecificationBuilder] = None
+        self.app = app
+        self.extensions: list[Extension] = []
+        self.sanic_version = sanic_version
+        app._ext = self
+        app.ctx._dependencies = SimpleNamespace()
+
+        if not isinstance(config, Config):
+            config = Config.from_dict(config or {})
+        self.config = add_fallback_config(app, config, **kwargs)
+
+        extensions = extensions or []
+        if built_in_extensions:
+            extensions.extend(
+                [
+                    InjectionExtension,
+                    OpenAPIExtension,
+                    HTTPExtension,
+                    HealthExtension,
+                    LoggingExtension,
+                ]
+            )
+
+            if TEMPLATING_ENABLED:
+                extensions.append(TemplatingExtension)
+        extensions.extend(Extend._pre_registry)
+
+        started = set()
+        for ext in extensions:
+            if ext in started:
+                continue
+            extension = Extension.create(ext, app, self.config)
+            extension._startup(self)
+            self.extensions.append(extension)
+            started.add(ext)
+
+    def _display(self):
+        if "SANIC_WORKER_IDENTIFIER" in os.environ:
+            return
+        init_logs = ["Sanic Extensions:"]
+        for extension in self.extensions:
+            label = extension.render_label()
+            if extension.included():
+                init_logs.append(f"  > {extension.name} {label}")
+
+        list(map(logger.info, init_logs))
+
+    def injection(
+        self,
+        type: type,
+        constructor: Optional[Callable[..., Any]] = None,
+    ) -> None:
+        warn(
+            "The 'ext.injection' method has been deprecated and will be "
+            "removed in v22.6. Please use 'ext.add_dependency' instead.",
+            DeprecationWarning,
+        )
+        self.add_dependency(type=type, constructor=constructor)
+
+    def add_dependency(
+        self,
+        type: type,
+        constructor: Optional[Callable[..., Any]] = None,
+        request_arg: Optional[str] = None,
+    ) -> None:
+        """
+        Add a dependency for injection
+
+        :param type: The type of the dependency
+        :type type: Type
+        :param constructor: A callable that will return an instance to be
+            injected, when ``False`` it will call the type, defaults to None
+        :type constructor: Optional[Callable[..., Any]], optional
+        :param request_arg: Explicitly state which argument in the
+            constructor (if any) should be a ``Request`` object, when set to
+            ``None`` the constructor will be introspected to check the
+            type annotations looking for a request object. You should really
+            only use this if you **MUST** use a lambda explression, it is
+            otherwise better to use a properly type annotated constructor,
+            defaults to None
+        :type request_arg: Optional[str], optional
+        :raises SanicException: _description_
+        """
+        if not self._injection_registry:
+            raise SanicException("Injection extension not enabled")
+        self._injection_registry.register(
+            type, constructor, request_arg=request_arg
+        )
+
+    def add_constant(self, name: str, value: Any, overwrite: bool = False):
+        if not self._constant_registry:
+            raise ValueError("Cannot add constant. No registry created.")
+        self._constant_registry.register(name, value, overwrite)
+
+    def load_constants(
+        self,
+        constants: Optional[Mapping[str, Any]] = None,
+        overwrite: Union[Default, bool] = _default,
+    ):
+        if not constants:
+            constants = {
+                k: v
+                for k, v in self.app.config.items()
+                if k.isupper()
+                and k not in DEFAULT_CONFIG
+                and k not in self.config
+                and not k.startswith("_")
+            }
+            if isinstance(overwrite, Default):
+                overwrite = True
+        if isinstance(overwrite, Default):
+            overwrite = False
+        for name, value in constants.items():
+            self.add_constant(name, value, overwrite)
+
+    def dependency(self, obj: Any, name: Optional[str] = None) -> None:
+        if not name:
+            name = camel_to_snake(obj.__class__.__name__)
+        setattr(self.app.ctx._dependencies, name, obj)
+
+        def getter(*_):
+            return obj
+
+        self.add_dependency(obj.__class__, getter)
+
+    @property
+    def openapi(self) -> SpecificationBuilder:
+        if not self._openapi:
+            self._openapi = SpecificationBuilder()
+
+        return self._openapi
+
+    def template(self, template_name: str, **kwargs):
+        return self.templating.template(template_name, **kwargs)
+
+    @classmethod
+    def register(cls, extension: Union[type[Extension], Extension]) -> None:
+        cls._pre_registry.append(extension)
+
+    @classmethod
+    def reset(cls) -> None:
+        cls._pre_registry.clear()
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/config.py b/.venv/lib/python3.12/site-packages/sanic_ext/config.py
new file mode 100644
index 0000000..cdd9117
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/config.py
@@ -0,0 +1,181 @@
+from __future__ import annotations
+
+import os
+
+from collections.abc import Sequence
+from typing import Any, Optional, Union
+
+from sanic import Sanic
+from sanic.config import Config as SanicConfig
+from sanic.exceptions import SanicException
+from sanic.signals import Event
+
+
+PRIORITY = -1_000
+
+
+class Config(SanicConfig):
+    def __init__(
+        self,
+        cors: bool = True,
+        cors_allow_headers: str = "*",
+        cors_always_send: bool = True,
+        cors_automatic_options: bool = True,
+        cors_expose_headers: str = "",
+        cors_max_age: int = 5,
+        cors_methods: str = "",
+        cors_origins: Union[str, list[str]] = "",
+        cors_send_wildcard: bool = False,
+        cors_supports_credentials: bool = False,
+        cors_vary_header: bool = True,
+        health: bool = False,
+        health_endpoint: bool = False,
+        health_max_misses: int = 3,
+        health_missed_threshhold: int = 10,
+        health_monitor: bool = True,
+        health_report_interval: int = 5,
+        health_uri_to_info: str = "",
+        health_url_prefix: str = "/__health__",
+        http_all_methods: bool = True,
+        http_auto_head: bool = True,
+        http_auto_options: bool = True,
+        http_auto_trace: bool = False,
+        injection_signal: Union[str, Event] = Event.HTTP_ROUTING_AFTER,
+        injection_priority: int = PRIORITY,
+        injection_load_custom_constants: bool = False,
+        logging: bool = False,
+        logging_queue_max_size: int = 4096,
+        loggers: list[str] = [
+            "sanic.access",
+            "sanic.error",
+            "sanic.root",
+            "sanic.server",
+            "sanic.websockets",
+        ],
+        oas: bool = True,
+        oas_autodoc: bool = True,
+        oas_custom_file: Optional[os.PathLike] = None,
+        oas_ignore_head: bool = True,
+        oas_ignore_options: bool = True,
+        oas_path_to_redoc_html: Optional[str] = None,
+        oas_path_to_swagger_html: Optional[str] = None,
+        oas_ui_default: Optional[str] = "redoc",
+        oas_ui_redoc: bool = True,
+        oas_ui_redoc_html_title: str = "ReDoc",
+        oas_ui_redoc_custom_css: str = "",
+        oas_ui_swagger: bool = True,
+        oas_ui_swagger_html_title: str = "OpenAPI Swagger",
+        oas_ui_swagger_custom_css: str = "",
+        oas_ui_swagger_version: str = "4.10.3",
+        oas_ui_swagger_oauth2_redirect: str = "/oauth2-redirect.html",
+        oas_uri_to_config: str = "/swagger-config",
+        oas_uri_to_json: str = "/openapi.json",
+        oas_uri_to_redoc: str = "/redoc",
+        oas_uri_to_swagger: str = "/swagger",
+        oas_url_prefix: str = "/docs",
+        swagger_ui_configuration: Optional[dict[str, Any]] = None,
+        templating_path_to_templates: Union[
+            str, os.PathLike, Sequence[Union[str, os.PathLike]]
+        ] = "templates",
+        templating_enable_async: bool = True,
+        trace_excluded_headers: Sequence[str] = ("authorization", "cookie"),
+        **kwargs,
+    ):
+        self.CORS = cors
+        self.CORS_ALLOW_HEADERS = cors_allow_headers
+        self.CORS_ALWAYS_SEND = cors_always_send
+        self.CORS_AUTOMATIC_OPTIONS = cors_automatic_options
+        self.CORS_EXPOSE_HEADERS = cors_expose_headers
+        self.CORS_MAX_AGE = cors_max_age
+        self.CORS_METHODS = cors_methods
+        self.CORS_ORIGINS = cors_origins
+        self.CORS_SEND_WILDCARD = cors_send_wildcard
+        self.CORS_SUPPORTS_CREDENTIALS = cors_supports_credentials
+        self.CORS_VARY_HEADER = cors_vary_header
+        self.HEALTH = health
+        self.HEALTH_ENDPOINT = health_endpoint
+        self.HEALTH_MAX_MISSES = health_max_misses
+        self.HEALTH_MISSED_THRESHHOLD = health_missed_threshhold
+        self.HEALTH_MONITOR = health_monitor
+        self.HEALTH_REPORT_INTERVAL = health_report_interval
+        self.HEALTH_URI_TO_INFO = health_uri_to_info
+        self.HEALTH_URL_PREFIX = health_url_prefix
+        self.HTTP_ALL_METHODS = http_all_methods
+        self.HTTP_AUTO_HEAD = http_auto_head
+        self.HTTP_AUTO_OPTIONS = http_auto_options
+        self.HTTP_AUTO_TRACE = http_auto_trace
+        self.INJECTION_SIGNAL = injection_signal
+        self.INJECTION_PRIORITY = injection_priority
+        self.INJECTION_LOAD_CUSTOM_CONSTANTS = injection_load_custom_constants
+        self.LOGGING = logging
+        self.LOGGING_QUEUE_MAX_SIZE = logging_queue_max_size
+        self.LOGGERS = loggers
+        self.OAS = oas
+        self.OAS_AUTODOC = oas_autodoc
+        self.OAS_CUSTOM_FILE = oas_custom_file
+        self.OAS_IGNORE_HEAD = oas_ignore_head
+        self.OAS_IGNORE_OPTIONS = oas_ignore_options
+        self.OAS_PATH_TO_REDOC_HTML = oas_path_to_redoc_html
+        self.OAS_PATH_TO_SWAGGER_HTML = oas_path_to_swagger_html
+        self.OAS_UI_DEFAULT = oas_ui_default
+        self.OAS_UI_REDOC = oas_ui_redoc
+        self.OAS_UI_REDOC_HTML_TITLE = oas_ui_redoc_html_title
+        self.OAS_UI_REDOC_CUSTOM_CSS = oas_ui_redoc_custom_css
+        self.OAS_UI_SWAGGER = oas_ui_swagger
+        self.OAS_UI_SWAGGER_HTML_TITLE = oas_ui_swagger_html_title
+        self.OAS_UI_SWAGGER_CUSTOM_CSS = oas_ui_swagger_custom_css
+        self.OAS_UI_SWAGGER_VERSION = oas_ui_swagger_version
+        self.OAS_UI_SWAGGER_OAUTH2_REDIRECT = oas_ui_swagger_oauth2_redirect
+        self.OAS_URI_TO_CONFIG = oas_uri_to_config
+        self.OAS_URI_TO_JSON = oas_uri_to_json
+        self.OAS_URI_TO_REDOC = oas_uri_to_redoc
+        self.OAS_URI_TO_SWAGGER = oas_uri_to_swagger
+        self.OAS_URL_PREFIX = oas_url_prefix
+        self.SWAGGER_UI_CONFIGURATION = swagger_ui_configuration or {
+            "apisSorter": "alpha",
+            "operationsSorter": "alpha",
+            "docExpansion": "full",
+        }
+        self.TEMPLATING_PATH_TO_TEMPLATES = templating_path_to_templates
+        self.TEMPLATING_ENABLE_ASYNC = templating_enable_async
+        self.TRACE_EXCLUDED_HEADERS = trace_excluded_headers
+
+        if isinstance(self.TRACE_EXCLUDED_HEADERS, str):
+            self.TRACE_EXCLUDED_HEADERS = tuple(
+                self.TRACE_EXCLUDED_HEADERS.split(",")
+            )
+
+        if isinstance(self.INJECTION_SIGNAL, str):
+            self.INJECTION_SIGNAL = Event(self.INJECTION_SIGNAL)
+
+        valid_signals = ("http.handler.before", "http.routing.after")
+        if self.INJECTION_SIGNAL.value not in valid_signals:
+            raise SanicException(
+                f"Injection signal may only be one of {valid_signals}"
+            )
+
+        self.load({key.upper(): value for key, value in kwargs.items()})
+
+    @classmethod
+    def from_dict(cls, mapping) -> Config:
+        return cls(**mapping)
+
+
+def add_fallback_config(
+    app: Sanic, config: Optional[Config] = None, **kwargs
+) -> Config:
+    if config is None:
+        config = Config(**kwargs)
+
+    app.config.update(
+        {key: value for key, value in config.items() if key not in app.config}
+    )
+    config.update(
+        {
+            key: value
+            for key, value in app.config.items()
+            if key in config and value != config.get(key)
+        }
+    )
+
+    return config
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/exceptions.py b/.venv/lib/python3.12/site-packages/sanic_ext/exceptions.py
new file mode 100644
index 0000000..c04230c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/exceptions.py
@@ -0,0 +1,11 @@
+from sanic.exceptions import SanicException
+
+
+class ValidationError(SanicException):
+    status_code = 400
+
+
+class InitError(SanicException): ...
+
+
+class ExtensionNotFound(SanicException): ...
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/base.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/base.py
new file mode 100644
index 0000000..ac08e56
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/base.py
@@ -0,0 +1,82 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from typing import Any, Union
+
+from sanic.app import Sanic
+from sanic.exceptions import SanicException
+
+from sanic_ext.config import Config
+from sanic_ext.exceptions import InitError
+
+
+class NoDuplicateDict(dict):  # type: ignore
+    def __setitem__(self, key: Any, value: Any) -> None:
+        if key in self:
+            raise KeyError(f"Duplicate key: {key}")
+        return super().__setitem__(key, value)
+
+
+class Extension(ABC):
+    _name_registry: dict[str, type[Extension]] = NoDuplicateDict()
+    _started: bool
+    name: str
+    app: Sanic
+    config: Config
+
+    def __init_subclass__(cls):
+        if not getattr(cls, "name", None) or not cls.name.isalpha():
+            raise InitError(
+                "Extensions must be named, and may only contain "
+                "alphabetic characters"
+            )
+
+        if cls.name in cls._name_registry:
+            raise InitError(f'Extension "{cls.name}" already exists')
+
+        cls._name_registry[cls.name] = cls
+
+    def _startup(self, bootstrap):
+        if self._started:
+            raise SanicException(
+                "Extension already started. Cannot start "
+                f"Extension:{self.name} multiple times."
+            )
+        self.startup(bootstrap)
+        self._started = True
+
+    @abstractmethod
+    def startup(self, bootstrap) -> None: ...
+
+    def label(self):
+        return ""
+
+    def render_label(self):
+        if not self.included:
+            return "~~disabled~~"
+        label = self.label()
+        if not label:
+            return ""
+        return f"[{label}]"
+
+    def included(self):
+        return True
+
+    @classmethod
+    def create(
+        cls,
+        extension: Union[type[Extension], Extension],
+        app: Sanic,
+        config: Config,
+    ) -> Extension:
+        instance = (
+            extension if isinstance(extension, Extension) else extension()
+        )
+        instance.app = app
+        instance.config = config
+        instance._started = False
+        return instance
+
+    @classmethod
+    def reset(cls) -> None:
+        cls._name_registry.clear()
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/endpoint.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/endpoint.py
new file mode 100644
index 0000000..d265a9e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/endpoint.py
@@ -0,0 +1,13 @@
+from sanic import Blueprint, Request, Sanic
+from sanic.response import json
+from sanic.worker.inspector import Inspector
+
+
+def setup_health_endpoint(app: Sanic) -> None:
+    bp = Blueprint("SanicHealth", url_prefix=app.config.HEALTH_URL_PREFIX)
+
+    @bp.get(app.config.HEALTH_URI_TO_INFO)
+    async def info(request: Request):
+        return json(Inspector._make_safe(dict(request.app.m.workers)))
+
+    app.blueprint(bp)
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/extension.py
new file mode 100644
index 0000000..e9e742a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/extension.py
@@ -0,0 +1,30 @@
+from sanic.exceptions import SanicException
+
+from sanic_ext.extensions.health.endpoint import setup_health_endpoint
+
+from ..base import Extension
+from .monitor import HealthMonitor
+
+
+class HealthExtension(Extension):
+    name = "health"
+    MIN_VERSION = (22, 9)
+
+    def startup(self, bootstrap) -> None:
+        if self.config.HEALTH:
+            if self.config.HEALTH_MONITOR:
+                if self.MIN_VERSION > bootstrap.sanic_version:
+                    min_version = ".".join(map(str, self.MIN_VERSION))
+                    sanic_version = ".".join(map(str, bootstrap.sanic_version))
+                    raise SanicException(
+                        f"Health monitoring only works with Sanic "
+                        f"v{min_version} and above. It looks like you are "
+                        f"running {sanic_version}."
+                    )
+                HealthMonitor.setup(self.app)
+
+            if self.config.HEALTH_ENDPOINT:
+                setup_health_endpoint(self.app)
+
+    def included(self):
+        return self.config.HEALTH
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/monitor.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/monitor.py
new file mode 100644
index 0000000..ba48669
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/health/monitor.py
@@ -0,0 +1,162 @@
+from __future__ import annotations
+
+from asyncio import sleep
+from dataclasses import dataclass
+from datetime import datetime, timedelta
+from multiprocessing import Manager
+from queue import Empty, Full
+from signal import SIGINT, SIGTERM
+from signal import signal as signal_func
+from typing import TYPE_CHECKING, Optional
+
+from sanic.application.constants import ServerStage
+from sanic.log import logger
+
+
+if TYPE_CHECKING:
+    from sanic import Sanic
+
+
+class Stale(ValueError): ...
+
+
+@dataclass
+class HealthState:
+    name: str
+    last: Optional[datetime] = None
+    misses: int = 0
+
+    def report(self, timestamp: int) -> None:
+        logger.debug(f"Reporting {self.name}")
+        if self.misses:
+            logger.info(f"Recovered {self.name}")
+        self.last = datetime.fromtimestamp(timestamp)
+        self.misses = 0
+
+    def missed(self) -> None:
+        self.misses += 1
+        logger.info(
+            f"Missed health check for {self.name} "
+            f"({self.misses}/{HealthMonitor.MAX_MISSES})"
+        )
+        if self.misses >= HealthMonitor.MAX_MISSES:
+            raise Stale
+
+    def check(self) -> None:
+        if not self.last:
+            return
+
+        threshhold = timedelta(
+            seconds=(HealthMonitor.MISSED_THRESHHOLD * (self.misses + 1))
+        )
+        if self.last < (datetime.now() - threshhold):
+            self.missed()
+
+    def reset(self) -> None:
+        self.misses = 0
+        self.last = datetime.now()
+
+
+def send_healthy(name, queue):
+    health = (name, datetime.now().timestamp())
+    logger.debug(f"Sending health: {health}", extra={"verbosity": 1})
+    try:
+        queue.put_nowait(health)
+    except Full:
+        ...
+
+
+async def health_check(app: Sanic):
+    sent = datetime.now()
+
+    while app.state.stage is ServerStage.SERVING:
+        now = datetime.now()
+        if sent < now - timedelta(seconds=HealthMonitor.REPORT_INTERVAL):
+            send_healthy(app.m.name, app.shared_ctx.health_queue)
+            sent = now
+        await sleep(0.1)
+
+
+async def start_health_check(app: Sanic):
+    app.add_task(health_check(app), name="health_check")
+
+
+async def prepare_health_monitor(app, *_):
+    HealthMonitor.prepare(app)
+
+
+async def setup_health_monitor(app, *_):
+    health = HealthMonitor(app)
+    process_names = [
+        process.name for process in app.manager.transient_processes
+    ]
+    app.manager.manage(
+        "HealthMonitor",
+        health,
+        {
+            "process_names": process_names,
+            "health_queue": app.shared_ctx.health_queue,
+        },
+    )
+
+
+class HealthMonitor:
+    MAX_MISSES = 3
+    REPORT_INTERVAL = 5
+    MISSED_THRESHHOLD = 10
+
+    def __init__(self, app: Sanic):
+        self.run = True
+        self.monitor_publisher = app.manager.monitor_publisher
+
+    def __call__(self, process_names, health_queue) -> None:
+        signal_func(SIGINT, self.stop)
+        signal_func(SIGTERM, self.stop)
+
+        now = datetime.now()
+        health_state = {
+            process_name: HealthState(last=now, name=process_name)
+            for process_name in process_names
+        }
+        while self.run:
+            try:
+                name, timestamp = health_queue.get(timeout=0.05)
+            except Empty:
+                ...
+            else:
+                health_state[name].report(timestamp)
+
+            for state in health_state.values():
+                try:
+                    state.check()
+                except Stale:
+                    state.reset()
+                    self.monitor_publisher.send(state.name)
+
+    def stop(self, *_):
+        self.run = False
+
+    @classmethod
+    def prepare(cls, app: Sanic):
+        sync_manager = Manager()
+        health_queue = sync_manager.Queue(maxsize=app.state.workers * 2)
+        app.shared_ctx.health_queue = health_queue
+
+    @classmethod
+    def setup(
+        cls,
+        app: Sanic,
+        max_misses: Optional[int] = None,
+        report_interval: Optional[int] = None,
+        missed_threshhold: Optional[int] = None,
+    ):
+        HealthMonitor.MAX_MISSES = max_misses or app.config.HEALTH_MAX_MISSES
+        HealthMonitor.REPORT_INTERVAL = (
+            report_interval or app.config.HEALTH_REPORT_INTERVAL
+        )
+        HealthMonitor.MISSED_THRESHHOLD = (
+            missed_threshhold or app.config.HEALTH_MISSED_THRESHHOLD
+        )
+        app.main_process_start(prepare_health_monitor)
+        app.main_process_ready(setup_health_monitor)
+        app.after_server_start(start_health_check)
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/cors.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/cors.py
new file mode 100644
index 0000000..28572e5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/cors.py
@@ -0,0 +1,396 @@
+import re
+
+from dataclasses import dataclass
+from datetime import timedelta
+from types import SimpleNamespace
+from typing import Any, Optional, Union
+
+from sanic import HTTPResponse, Request, Sanic
+from sanic.exceptions import SanicException
+from sanic.helpers import Default, _default
+from sanic.log import logger
+
+from sanic_ext.config import PRIORITY
+
+
+WILDCARD_PATTERN = re.compile(r".*")
+ORIGIN_HEADER = "access-control-allow-origin"
+ALLOW_HEADERS_HEADER = "access-control-allow-headers"
+ALLOW_METHODS_HEADER = "access-control-allow-methods"
+EXPOSE_HEADER = "access-control-expose-headers"
+CREDENTIALS_HEADER = "access-control-allow-credentials"
+REQUEST_METHOD_HEADER = "access-control-request-method"
+REQUEST_HEADERS_HEADER = "access-control-request-headers"
+MAX_AGE_HEADER = "access-control-max-age"
+VARY_HEADER = "vary"
+
+
+@dataclass(frozen=True)
+class CORSSettings:
+    allow_headers: frozenset[str]
+    allow_methods: frozenset[str]
+    allow_origins: tuple[re.Pattern, ...]
+    always_send: bool
+    automatic_options: bool
+    expose_headers: frozenset[str]
+    max_age: str
+    send_wildcard: bool
+    supports_credentials: bool
+
+
+def add_cors(app: Sanic) -> None:
+    _setup_cors_settings(app)
+
+    @app.on_response
+    async def _add_cors_headers(request, response):
+        preflight = (
+            request.app.ctx.cors.automatic_options
+            and request.method == "OPTIONS"
+        )
+
+        if preflight and not request.headers.get(REQUEST_METHOD_HEADER):
+            logger.info(
+                "No Access-Control-Request-Method header found on request. "
+                "CORS headers will not be applied."
+            )
+            return
+
+        _add_origin_header(request, response)
+
+        if ORIGIN_HEADER not in response.headers:
+            return
+
+        _add_expose_header(request, response)
+        _add_credentials_header(request, response)
+        _add_vary_header(request, response)
+
+        if preflight:
+            _add_max_age_header(request, response)
+            _add_allow_header(request, response)
+            _add_methods_header(request, response)
+
+    @app.before_server_start(priority=PRIORITY)
+    async def _assign_cors_settings(app, _):
+        for group in app.router.groups.values():
+            _cors = SimpleNamespace()
+            for route in group:
+                cors = getattr(route.handler, "__cors__", None)
+                if cors:
+                    for key, value in cors.__dict__.items():
+                        setattr(_cors, key, value)
+
+            for route in group:
+                route.ctx._cors = _cors
+
+
+def cors(
+    *,
+    origin: Union[str, Default] = _default,
+    expose_headers: Union[list[str], Default] = _default,
+    allow_headers: Union[list[str], Default] = _default,
+    allow_methods: Union[list[str], Default] = _default,
+    supports_credentials: Union[bool, Default] = _default,
+    max_age: Union[str, int, timedelta, Default] = _default,
+):
+    def decorator(f):
+        f.__cors__ = SimpleNamespace(
+            _cors_origin=origin,
+            _cors_expose_headers=expose_headers,
+            _cors_supports_credentials=supports_credentials,
+            _cors_allow_origins=(
+                _parse_allow_origins(origin)
+                if origin is not _default
+                else origin
+            ),
+            _cors_allow_headers=(
+                _parse_allow_headers(allow_headers)
+                if allow_headers is not _default
+                else allow_headers
+            ),
+            _cors_allow_methods=(
+                _parse_allow_methods(allow_methods)
+                if allow_methods is not _default
+                else allow_methods
+            ),
+            _cors_max_age=(
+                _parse_max_age(max_age) if max_age is not _default else max_age
+            ),
+        )
+        return f
+
+    return decorator
+
+
+def _setup_cors_settings(app: Sanic) -> None:
+    if app.config.CORS_ORIGINS == "*" and app.config.CORS_SUPPORTS_CREDENTIALS:
+        raise SanicException(
+            "Cannot use supports_credentials in conjunction with "
+            "an origin string of '*'. See: "
+            "http://www.w3.org/TR/cors/#resource-requests"
+        )
+
+    allow_headers = _get_allow_headers(app)
+    allow_methods = _get_allow_methods(app)
+    allow_origins = _get_allow_origins(app)
+    expose_headers = _get_expose_headers(app)
+    max_age = _get_max_age(app)
+
+    app.ctx.cors = CORSSettings(
+        allow_headers=allow_headers,
+        allow_methods=allow_methods,
+        allow_origins=allow_origins,
+        always_send=app.config.CORS_ALWAYS_SEND,
+        automatic_options=app.config.CORS_AUTOMATIC_OPTIONS,
+        expose_headers=expose_headers,
+        max_age=max_age,
+        send_wildcard=(
+            app.config.CORS_SEND_WILDCARD and WILDCARD_PATTERN in allow_origins
+        ),
+        supports_credentials=app.config.CORS_SUPPORTS_CREDENTIALS,
+    )
+
+
+def _get_from_cors_ctx(request: Request, key: str, default: Any = None):
+    if request.route:
+        value = getattr(request.route.ctx._cors, key, default)
+        if value is not _default:
+            return value
+    return default
+
+
+def _add_origin_header(request: Request, response: HTTPResponse) -> None:
+    request_origin = request.headers.get("origin")
+    origin_value = ""
+    allow_origins = _get_from_cors_ctx(
+        request,
+        "_cors_allow_origins",
+        request.app.ctx.cors.allow_origins,
+    )
+    fallback_origin = _get_from_cors_ctx(
+        request,
+        "_cors_origin",
+        request.app.config.CORS_ORIGINS,
+    )
+
+    if request_origin:
+        if request.app.ctx.cors.send_wildcard:
+            origin_value = "*"
+        else:
+            for pattern in allow_origins:
+                if pattern.match(request_origin):
+                    origin_value = request_origin
+                    break
+    elif request.app.ctx.cors.always_send:
+        if WILDCARD_PATTERN in allow_origins:
+            origin_value = "*"
+        else:
+            if isinstance(fallback_origin, str) and "," not in fallback_origin:
+                origin_value = fallback_origin
+            else:
+                origin_value = request.app.config.get("SERVER_NAME", "")
+
+    if origin_value:
+        response.headers[ORIGIN_HEADER] = origin_value
+
+
+def _add_expose_header(request: Request, response: HTTPResponse) -> None:
+    with_credentials = _is_request_with_credentials(request)
+    headers = None
+    expose_headers = _get_from_cors_ctx(
+        request, "_cors_expose_headers", request.app.ctx.cors.expose_headers
+    )
+    # MDN: The value "*" only counts as a special wildcard value for requests
+    # without credentials (requests without HTTP cookies or HTTP
+    # authentication information). In requests with credentials, it is
+    # treated as the literal header name "*" without special semantics.
+    # Note that the Authorization header can't be wildcarded and always
+    # needs to be listed explicitly.
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
+    if not with_credentials and "*" in expose_headers:
+        headers = ["*"]
+    elif expose_headers:
+        headers = expose_headers
+
+    if headers:
+        response.headers[EXPOSE_HEADER] = ",".join(headers)
+
+
+def _add_credentials_header(request: Request, response: HTTPResponse) -> None:
+    supports_credentials = _get_from_cors_ctx(
+        request,
+        "_cors_supports_credentials",
+        request.app.ctx.cors.supports_credentials,
+    )
+    if supports_credentials:
+        response.headers[CREDENTIALS_HEADER] = "true"
+
+
+def _add_allow_header(request: Request, response: HTTPResponse) -> None:
+    with_credentials = _is_request_with_credentials(request)
+    request_headers = {
+        h.strip().lower()
+        for h in request.headers.get(REQUEST_HEADERS_HEADER, "").split(",")
+    }
+    allow_headers = _get_from_cors_ctx(
+        request, "_cors_allow_headers", request.app.ctx.cors.allow_headers
+    )
+
+    # MDN: The value "*" only counts as a special wildcard value for requests
+    # without credentials (requests without HTTP cookies or HTTP
+    # authentication information). In requests with credentials,
+    # it is treated as the literal header name "*" without special semantics.
+    # Note that the Authorization header can't be wildcarded and always needs
+    # to be listed explicitly.
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
+    if not with_credentials and "*" in allow_headers:
+        allow_headers = ["*"]
+    else:
+        allow_headers = request_headers & allow_headers
+
+    if allow_headers:
+        response.headers[ALLOW_HEADERS_HEADER] = ",".join(allow_headers)
+
+
+def _add_max_age_header(request: Request, response: HTTPResponse) -> None:
+    max_age = _get_from_cors_ctx(
+        request, "_cors_max_age", request.app.ctx.cors.max_age
+    )
+    if max_age:
+        response.headers[MAX_AGE_HEADER] = max_age
+
+
+def _add_methods_header(request: Request, response: HTTPResponse) -> None:
+    # MDN: The value "*" only counts as a special wildcard value for requests
+    # without credentials (requests without HTTP cookies or HTTP
+    # authentication information). In requests with credentials, it
+    # is treated as the literal method name "*" without
+    # special semantics.
+    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
+    methods = None
+    with_credentials = _is_request_with_credentials(request)
+    allow_methods = _get_from_cors_ctx(
+        request, "_cors_allow_methods", request.app.ctx.cors.allow_methods
+    )
+
+    if not with_credentials and "*" in allow_methods:
+        methods = {"*"}
+    elif request.route:
+        group = request.app.router.groups.get(request.route.segments)
+        if group:
+            group_methods = {method.lower() for method in group.methods}
+            if allow_methods:
+                methods = group_methods & allow_methods
+            else:
+                methods = group_methods
+
+    if methods:
+        response.headers[ALLOW_METHODS_HEADER] = ",".join(methods).upper()
+
+
+def _add_vary_header(request: Request, response: HTTPResponse) -> None:
+    allow_origins = _get_from_cors_ctx(
+        request,
+        "_cors_allow_origins",
+        request.app.ctx.cors.allow_origins,
+    )
+    if len(allow_origins) > 1:
+        response.headers[VARY_HEADER] = "origin"
+
+
+def _get_allow_origins(app: Sanic) -> tuple[re.Pattern, ...]:
+    origins = app.config.CORS_ORIGINS
+    return _parse_allow_origins(origins)
+
+
+def _parse_allow_origins(
+    value: Union[str, re.Pattern, list[Union[str, re.Pattern]]],
+) -> tuple[re.Pattern, ...]:
+    origins: Optional[
+        Union[list[str], list[re.Pattern], list[Union[str, re.Pattern]]]
+    ] = None
+    if value and isinstance(value, str):
+        if value == "*":
+            origins = [WILDCARD_PATTERN]
+        else:
+            origins = [origin.strip() for origin in value.split(",")]
+    elif isinstance(value, re.Pattern):
+        origins = [value]
+    elif isinstance(value, list):
+        origins = value
+
+    return tuple(
+        pattern
+        if isinstance(pattern, re.Pattern)
+        else re.compile(re.escape(pattern))
+        for pattern in (origins or [])
+    )
+
+
+def _get_expose_headers(app: Sanic) -> frozenset[str]:
+    expose_headers = (
+        (
+            app.config.CORS_EXPOSE_HEADERS
+            if isinstance(
+                app.config.CORS_EXPOSE_HEADERS, (list, set, frozenset, tuple)
+            )
+            else app.config.CORS_EXPOSE_HEADERS.split(",")
+        )
+        if app.config.CORS_EXPOSE_HEADERS
+        else tuple()
+    )
+    return frozenset(header.lower() for header in expose_headers)
+
+
+def _get_allow_headers(app: Sanic) -> frozenset[str]:
+    return _parse_allow_headers(app.config.CORS_ALLOW_HEADERS)
+
+
+def _parse_allow_headers(value: str) -> frozenset[str]:
+    allow_headers = (
+        (
+            value
+            if isinstance(
+                value,
+                (list, set, frozenset, tuple),
+            )
+            else value.split(",")
+        )
+        if value
+        else tuple()
+    )
+    return frozenset(header.lower() for header in allow_headers)
+
+
+def _get_max_age(app: Sanic) -> str:
+    return _parse_max_age(app.config.CORS_MAX_AGE or "")
+
+
+def _parse_max_age(value) -> str:
+    max_age = value or ""
+    if isinstance(max_age, timedelta):
+        max_age = str(int(max_age.total_seconds()))
+    return str(max_age)
+
+
+def _get_allow_methods(app: Sanic) -> frozenset[str]:
+    return _parse_allow_methods(app.config.CORS_METHODS)
+
+
+def _parse_allow_methods(value) -> frozenset[str]:
+    allow_methods = (
+        (
+            value
+            if isinstance(
+                value,
+                (list, set, frozenset, tuple),
+            )
+            else value.split(",")
+        )
+        if value
+        else tuple()
+    )
+    return frozenset(method.lower() for method in allow_methods)
+
+
+def _is_request_with_credentials(request: Request) -> bool:
+    return bool(request.headers.get("authorization") or request.cookies)
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/extension.py
new file mode 100644
index 0000000..18fb2ca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/extension.py
@@ -0,0 +1,34 @@
+from ...exceptions import InitError
+from ..base import Extension
+from .cors import add_cors
+from .methods import add_auto_handlers, add_http_methods
+
+
+class HTTPExtension(Extension):
+    name = "http"
+
+    def startup(self, _) -> None:
+        self.all_methods: bool = self.config.HTTP_ALL_METHODS
+        self.auto_head: bool = self.config.HTTP_AUTO_HEAD
+        self.auto_options: bool = self.config.HTTP_AUTO_OPTIONS
+        self.auto_trace: bool = self.config.HTTP_AUTO_TRACE
+        self.cors: bool = self.config.CORS
+
+        if self.all_methods:
+            add_http_methods(self.app, ["CONNECT", "TRACE"])
+
+        if self.auto_head or self.auto_options or self.auto_trace:
+            add_auto_handlers(
+                self.app, self.auto_head, self.auto_options, self.auto_trace
+            )
+
+        if self.cors:
+            add_cors(self.app)
+        else:
+            return
+
+        if self.app.ctx.cors.automatic_options and not self.auto_options:
+            raise InitError(
+                "Configuration mismatch. If CORS_AUTOMATIC_OPTIONS is set to "
+                "True, then you must run SanicExt with auto_options=True"
+            )
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/methods.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/methods.py
new file mode 100644
index 0000000..de1a246
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/http/methods.py
@@ -0,0 +1,146 @@
+from collections.abc import Sequence
+from functools import partial
+from inspect import isawaitable
+from operator import itemgetter
+from typing import Union
+
+from sanic import Sanic
+from sanic.constants import HTTPMethod
+from sanic.exceptions import SanicException
+from sanic.response import empty, raw
+
+from sanic_ext.config import PRIORITY
+from sanic_ext.extensions.openapi import openapi
+from sanic_ext.utils.route import clean_route_name
+
+
+def add_http_methods(
+    app: Sanic, methods: Sequence[Union[str, HTTPMethod]]
+) -> None:
+    """
+    Adds HTTP methods to an app
+
+    :param app: Your Sanic app
+    :type app: Sanic
+    :param methods: The http methods being added, eg: CONNECT, TRACE
+    :type methods: Sequence[str]
+    """
+
+    app.router.ALLOWED_METHODS = tuple(
+        [*app.router.ALLOWED_METHODS, *methods]  # type: ignore
+    )
+
+
+def add_auto_handlers(
+    app: Sanic, auto_head: bool, auto_options: bool, auto_trace: bool
+) -> None:
+    if auto_trace and "TRACE" not in app.router.ALLOWED_METHODS:
+        raise SanicException(
+            "Cannot use apply(..., auto_trace=True) if TRACE is not an "
+            "allowed HTTP method. Make sure apply(..., all_http_methods=True) "
+            "has been set."
+        )
+
+    async def head_handler(request, get_handler, *args, **kwargs):
+        retval = get_handler(request, *args, **kwargs)
+        if isawaitable(retval):
+            retval = await retval
+        return retval
+
+    async def options_handler(request, methods, *args, **kwargs):
+        resp = empty()
+        resp.headers["allow"] = ",".join([*methods, "OPTIONS"])
+        return resp
+
+    async def trace_handler(request):
+        cleaned_head = b""
+        for line in request.head.split(b"\r\n"):
+            first_word, _ = line.split(b" ", 1)
+
+            if (
+                first_word.lower().replace(b":", b"").decode("utf-8")
+                not in request.app.config.TRACE_EXCLUDED_HEADERS
+            ):
+                cleaned_head += line + b"\r\n"
+
+        message = "\r\n\r\n".join(
+            [part.decode("utf-8") for part in [cleaned_head, request.body]]
+        )
+        return raw(message, content_type="message/http")
+
+    @app.before_server_start(priority=PRIORITY)
+    def _add_handlers(app, _):
+        nonlocal auto_head
+        nonlocal auto_options
+
+        if auto_head:
+            app.router.reset()
+            for group in app.router.groups.values():
+                if "GET" in group.methods and "HEAD" not in group.methods:
+                    for route in group:
+                        if "GET" in route.methods:
+                            host = route.requirements.get("host")
+                            name = f"{route.name}_head"
+                            handler = openapi.definition(
+                                summary=clean_route_name(route.name).title(),
+                                description="Retrieve HEAD details",
+                            )(partial(head_handler, get_handler=route.handler))
+                            handler.__auto_handler__ = True
+                            handler.__route_handler__ = route.handler
+                            app.add_route(
+                                handler=handler,
+                                uri=group.uri,
+                                methods=["HEAD"],
+                                strict_slashes=group.strict,
+                                name=name,
+                                host=host,
+                                unquote=group.unquote,
+                            )
+            app.finalize()
+
+        if auto_trace:
+            app.router.reset()
+            for group in app.router.groups.values():
+                if "TRACE" not in group.methods:
+                    app.add_route(
+                        handler=trace_handler,
+                        uri=group.uri,
+                        methods=["TRACE"],
+                        strict_slashes=group.strict,
+                    )
+            app.finalize()
+
+        if auto_options:
+            app.router.reset()
+            for group in app.router.groups.values():
+                if "OPTIONS" not in group.methods:
+                    if not group.requirements:
+                        hosts = [None]
+                    else:
+                        hosts = set(
+                            map(itemgetter("host"), group.requirements)
+                        )
+
+                    try:
+                        base_route = next(
+                            r for r in group if not r.name.endswith("_head")
+                        )
+                    except StopIteration:
+                        base_route = group[0]
+
+                    name = f"{base_route.name}_options"
+                    handler = openapi.definition(
+                        summary=clean_route_name(base_route.name).title(),
+                        description="Retrieve OPTIONS details",
+                    )(partial(options_handler, methods=group.methods))
+                    handler.__auto_handler__ = True
+                    app.add_route(
+                        handler=handler,
+                        uri=group.uri,
+                        methods=["OPTIONS"],
+                        strict_slashes=group.strict,
+                        name=name,
+                        host=hosts,
+                        unquote=group.unquote,
+                    )
+            app.finalize()
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/constructor.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/constructor.py
new file mode 100644
index 0000000..aa3b1fb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/constructor.py
@@ -0,0 +1,180 @@
+from __future__ import annotations
+
+from dataclasses import is_dataclass
+from inspect import isclass, iscoroutine
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Optional,
+    get_args,
+    get_type_hints,
+)
+
+from sanic import Request
+from sanic.app import Sanic
+from sanic.exceptions import ServerError
+
+from sanic_ext.exceptions import InitError
+from sanic_ext.utils.typing import (
+    is_attrs,
+    is_msgspec,
+    is_optional,
+    is_pydantic,
+)
+
+
+if TYPE_CHECKING:
+    from .registry import ConstantRegistry, InjectionRegistry
+
+
+class Constructor:
+    EXEMPT_ANNOTATIONS = (Request,)
+
+    def __init__(
+        self, func: Callable[..., Any], request_arg: Optional[str] = None
+    ):
+        self.func = func
+        self.injections: dict[str, tuple[type, Constructor]] = {}
+        self.constants: dict[str, Any] = {}
+        self.pass_kwargs: bool = False
+        self.request_arg = request_arg
+
+    def __str__(self) -> str:
+        return f"<{self.__class__.__name__}:{self.func.__name__}>"
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}(func={self.func.__name__})>"
+
+    async def __call__(self, request, **kwargs):
+        try:
+            args = await gather_args(self.injections, request, **kwargs)
+            args.update(self.constants)
+            if self.pass_kwargs:
+                args.update(kwargs)
+
+            if self.request_arg:
+                args[self.request_arg] = request
+
+            retval = self.func(**args)
+
+            if iscoroutine(retval):
+                retval = await retval
+            return retval
+        except TypeError as e:
+            raise ServerError(
+                "Failure to inject dependencies. Make sure that all "
+                f"dependencies for '{self.func.__name__}' have been "
+                "registered."
+            ) from e
+
+    def prepare(
+        self,
+        app: Sanic,
+        injection_registry: InjectionRegistry,
+        constant_registry: ConstantRegistry,
+        allowed_types: set[type[object]],
+    ) -> None:
+        hints = self._get_hints()
+        hints.pop("return", None)
+        missing = []
+        for param, annotation in hints.items():
+            if param in constant_registry:
+                self.constants[param] = getattr(app.config, param.upper())
+            if annotation in allowed_types:
+                self.pass_kwargs = True
+            if is_optional(annotation):
+                annotation = get_args(annotation)[0]
+            if not isclass(annotation):
+                missing.append((param, annotation))
+                continue
+            if issubclass(annotation, Request):
+                self.request_arg = param
+            if (
+                annotation not in self.EXEMPT_ANNOTATIONS
+                and not issubclass(annotation, self.EXEMPT_ANNOTATIONS)
+                and annotation not in allowed_types
+            ):
+                dependency = injection_registry.get(annotation)
+                if not dependency:
+                    missing.append((param, annotation))
+                    continue
+                self.injections[param] = (annotation, dependency)
+
+        if missing:
+            dependencies = "\n".join(
+                [f"  - {param}: {annotation}" for param, annotation in missing]
+            )
+            raise InitError(
+                "Unable to resolve dependencies for "
+                f"'{self.func.__name__}'. Could not find the following "
+                f"dependencies:\n{dependencies}.\nMake sure the dependencies "
+                "are declared using ext.injection. See "
+                "https://sanicframework.org/en/plugins/sanic-ext/injection."
+                "html#injecting-services for more details."
+            )
+
+        checked: set[type[object]] = set()
+        current: set[type[object]] = set()
+        self.check_circular(checked, current)
+
+    def check_circular(
+        self,
+        checked: set[type[object]],
+        current: set[type[object]],
+    ) -> None:
+        dependencies = set(self.injections.values())
+        for dependency, constructor in dependencies:
+            self._visit(dependency, constructor, checked, current)
+
+    def _visit(
+        self,
+        dependency: type[object],
+        constructor: Constructor,
+        checked: set[type[object]],
+        current: set[type[object]],
+    ):
+        if dependency in checked:
+            return
+        elif dependency in current:
+            raise InitError(
+                "Circular dependency injection detected on "
+                f"'{self.func.__name__}'. Check dependencies of "
+                f"'{constructor.func.__name__}' which may contain "
+                f"circular dependency chain with {dependency}."
+            )
+
+        current.add(dependency)
+        constructor.check_circular(checked, current)
+        current.remove(dependency)
+        checked.add(dependency)
+
+    def _get_hints(self):
+        if (
+            not isclass(self.func)
+            or is_dataclass(self.func)
+            or is_attrs(self.func)
+            or is_pydantic(self.func)
+            or is_msgspec(self.func)
+        ):
+            return get_type_hints(self.func)
+        elif isclass(self.func):
+            return get_type_hints(self.func.__init__)
+        raise InitError(f"Cannot get type hints for {self.func}")
+
+
+async def gather_args(injections, request, **kwargs) -> dict[str, Any]:
+    return {
+        name: await do_cast(_type, constructor, request, **kwargs)
+        for name, (_type, constructor) in injections.items()
+    }
+
+
+async def do_cast(_type, constructor, request, **kwargs):
+    cast = constructor if constructor else _type
+    args = [request] if constructor else []
+
+    retval = cast(*args, **kwargs)
+    if iscoroutine(retval):
+        retval = await retval
+    return retval
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/extension.py
new file mode 100644
index 0000000..2bcf474
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/extension.py
@@ -0,0 +1,22 @@
+from ..base import Extension
+from .injector import add_injection
+from .registry import ConstantRegistry, InjectionRegistry
+
+
+class InjectionExtension(Extension):
+    name = "injection"
+
+    def startup(self, bootstrap) -> None:
+        self.constant_registry = ConstantRegistry(self.app.config)
+        self.registry = InjectionRegistry()
+        add_injection(self.app, self.registry, self.constant_registry)
+        bootstrap._injection_registry = self.registry
+        bootstrap._constant_registry = self.constant_registry
+        if self.config.INJECTION_LOAD_CUSTOM_CONSTANTS:
+            self.app.ext.load_constants()
+
+    def label(self):
+        return (
+            f"{self.registry.length} dependencies; "
+            f"{self.constant_registry.length} constants"
+        )
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/injector.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/injector.py
new file mode 100644
index 0000000..6238956
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/injector.py
@@ -0,0 +1,123 @@
+from __future__ import annotations
+
+from functools import partial
+from inspect import getmembers, isclass, isfunction
+from typing import Any, Callable, Optional, get_type_hints
+
+from sanic import Sanic
+from sanic.constants import HTTP_METHODS
+
+from sanic_ext.config import PRIORITY
+from sanic_ext.extensions.injection.constructor import gather_args
+
+from .registry import ConstantRegistry, InjectionRegistry, SignatureRegistry
+
+
+def add_injection(
+    app: Sanic,
+    injection_registry: InjectionRegistry,
+    constant_registry: ConstantRegistry,
+) -> None:
+    signature_registry = _setup_signature_registry(
+        app, injection_registry, constant_registry
+    )
+
+    @app.listener("before_server_start", priority=PRIORITY)
+    async def finalize_injections(app: Sanic, _):
+        router_converters = {
+            allowed[0] for allowed in app.router.regex_types.values()
+        }
+        router_types = set()
+        for converter in router_converters:
+            if isclass(converter):
+                router_types.add(converter)
+            elif isfunction(converter):
+                hints = get_type_hints(converter)
+                if return_type := hints.get("return"):
+                    router_types.add(return_type)
+        injection_registry.finalize(app, constant_registry, router_types)
+
+    injection_signal = app.ext.config.INJECTION_SIGNAL
+    injection_priority = app.ext.config.INJECTION_PRIORITY
+
+    @app.signal(injection_signal, priority=injection_priority)
+    async def inject_kwargs(request, **_):
+        nonlocal signature_registry
+
+        for name in (
+            request.route.name,
+            f"{request.route.name}_{request.method.lower()}",
+        ):
+            dependencies, constants = signature_registry.get(
+                name, (None, None)
+            )
+            if dependencies or constants:
+                break
+
+        if dependencies:
+            injected_args = await gather_args(
+                dependencies, request, **request.match_info
+            )
+            request.match_info.update(injected_args)
+        if constants:
+            request.match_info.update(constants)
+
+
+def _http_method_predicate(member):
+    return isfunction(member) and member.__name__ in HTTP_METHODS
+
+
+def _setup_signature_registry(
+    app: Sanic,
+    injection_registry: InjectionRegistry,
+    constant_registry: ConstantRegistry,
+) -> SignatureRegistry:
+    registry = SignatureRegistry()
+
+    @app.listener("before_server_start", priority=PRIORITY - 1)
+    async def setup_signatures(app, _):
+        nonlocal registry
+
+        for route in app.router.routes:
+            if ".openapi." in route.name:
+                continue
+            handlers = [(route.name, route.handler)]
+            viewclass = getattr(route.handler, "view_class", None)
+            if viewclass:
+                handlers = [
+                    (f"{route.name}_{name}", member)
+                    for name, member in getmembers(
+                        viewclass, _http_method_predicate
+                    )
+                ]
+            for name, handler in handlers:
+                if route_handler := getattr(
+                    handler, "__route_handler__", None
+                ):
+                    handler = route_handler
+                if isinstance(handler, partial):
+                    if handler.func == app._websocket_handler:
+                        handler = handler.args[0]
+                    else:
+                        handler = handler.func
+                try:
+                    hints = get_type_hints(handler)
+                except TypeError:
+                    continue
+
+                dependencies: dict[
+                    str, tuple[type, Optional[Callable[..., Any]]]
+                ] = {}
+                constants: dict[str, Any] = {}
+                for param, annotation in hints.items():
+                    if annotation in injection_registry:
+                        dependencies[param] = (
+                            annotation,
+                            injection_registry[annotation],
+                        )
+                    if param in constant_registry:
+                        constants[param] = app.config[param.upper()]
+
+                registry.register(name, dependencies, constants)
+
+    return registry
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/registry.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/registry.py
new file mode 100644
index 0000000..c23d661
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/injection/registry.py
@@ -0,0 +1,115 @@
+from __future__ import annotations
+
+from typing import Any, Callable, Optional
+
+from sanic.app import Sanic
+from sanic.config import Config
+
+from .constructor import Constructor
+
+
+class InjectionRegistry:
+    def __init__(self):
+        self._registry: dict[type, Optional[Callable[..., Any]]] = {}
+
+    def __getitem__(self, key):
+        return self._registry[key]
+
+    def __str__(self) -> str:
+        return str(self._registry)
+
+    def __contains__(self, other: Any):
+        return other in self._registry
+
+    def get(self, key, default=None):
+        return self._registry.get(key, default)
+
+    def register(
+        self,
+        _type: type,
+        constructor: Optional[Callable[..., Any]],
+        request_arg: Optional[str] = None,
+    ) -> None:
+        constructor = constructor or _type
+        constructor = Constructor(constructor, request_arg=request_arg)
+        self._registry[_type] = constructor
+
+    def finalize(
+        self, app: Sanic, constant_registry: ConstantRegistry, allowed_types
+    ):
+        for constructor in self._registry.values():
+            if isinstance(constructor, Constructor):
+                constructor.prepare(
+                    app, self, constant_registry, allowed_types
+                )
+
+    @property
+    def length(self):
+        return len(self._registry)
+
+
+class SignatureRegistry:
+    def __init__(self):
+        self._registry: dict[
+            str,
+            tuple[
+                dict[str, tuple[type, Optional[Callable[..., Any]]]],
+                dict[str, Any],
+            ],
+        ] = {}
+
+    def __getitem__(self, key):
+        return self._registry[key]
+
+    def __str__(self) -> str:
+        return str(self._registry)
+
+    def get(self, key, default=None):
+        return self._registry.get(key, default)
+
+    def register(
+        self,
+        route_name: str,
+        dependencies: dict[str, tuple[type, Optional[Callable[..., Any]]]],
+        constants: Optional[dict[str, Any]] = None,
+    ) -> None:
+        self._registry[route_name] = (dependencies, constants or {})
+
+
+class ConstantRegistry:
+    def __init__(self, config: Config):
+        self._config = config
+        self._registry: set[str] = set()
+
+    def __str__(self) -> str:
+        return str(self._registry)
+
+    def __iter__(self):
+        return iter(self._registry)
+
+    def __getitem__(self, key):
+        return self._registry[key]
+
+    def __contains__(self, other: Any):
+        return other in self._registry
+
+    def register(self, key: str, value: Any, overwrite: bool):
+        attribute = key.upper()
+        if attribute in self._config and not overwrite:
+            raise ValueError(
+                f"A value for {attribute} has already been assigned"
+            )
+        key = key.lower()
+        setattr(self._config, attribute, value)
+        return self._registry.add(key)
+
+    def get(self, key: str):
+        key = key.lower()
+        if key not in self._registry:
+            raise ValueError
+        attribute = key.upper()
+        return getattr(self._config, attribute)
+
+    @property
+    def length(self):
+        return len(self._registry)
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extension.py
new file mode 100644
index 0000000..d31f79d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extension.py
@@ -0,0 +1,24 @@
+from sanic.exceptions import SanicException
+
+from ..base import Extension
+from .logger import Logger
+
+
+class LoggingExtension(Extension):
+    name = "logging"
+    MIN_VERSION = (22, 9)
+
+    def startup(self, bootstrap) -> None:
+        if self.included():
+            if self.MIN_VERSION > bootstrap.sanic_version:
+                min_version = ".".join(map(str, self.MIN_VERSION))
+                sanic_version = ".".join(map(str, bootstrap.sanic_version))
+                raise SanicException(
+                    f"The logging extension only works with Sanic "
+                    f"v{min_version} and above. It looks like you are "
+                    f"running {sanic_version}."
+                )
+            Logger.setup(self.app)
+
+    def included(self):
+        return self.config.LOGGING
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extractor.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extractor.py
new file mode 100644
index 0000000..fd32202
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/extractor.py
@@ -0,0 +1,116 @@
+import logging
+
+from typing import Any, Optional, TypedDict
+
+
+class LoggerConfig(TypedDict):
+    level: str
+    propagate: bool
+    handlers: list[str]
+
+
+class HandlerConfig(TypedDict):
+    class_: str
+    level: str
+    stream: Optional[str]
+    formatter: Optional[str]
+
+
+class FormatterConfig(TypedDict):
+    class_: str
+    format: Optional[str]
+    datefmt: Optional[str]
+
+
+class LoggingConfig(TypedDict):
+    version: int
+    disable_existing_loggers: bool
+    formatters: dict[str, FormatterConfig]
+    handlers: dict[str, HandlerConfig]
+    loggers: dict[str, LoggerConfig]
+
+
+class LoggingConfigExtractor:
+    def __init__(self):
+        self.version = 1
+        self.disable_existing_loggers = False
+        self.formatters: dict[str, FormatterConfig] = {}
+        self.handlers: dict[str, HandlerConfig] = {}
+        self.loggers: dict[str, LoggerConfig] = {}
+
+    def add_logger(self, logger: logging.Logger):
+        self._extract_logger_config(logger)
+        self._extract_handlers(logger)
+
+    def compile(self) -> LoggingConfig:
+        output = {
+            "version": self.version,
+            "disable_existing_loggers": self.disable_existing_loggers,
+            "formatters": self.formatters,
+            "handlers": self.handlers,
+            "loggers": self.loggers,
+        }
+        return self._clean(output)
+
+    def _extract_logger_config(self, logger: logging.Logger):
+        config: LoggerConfig = {
+            "level": logging.getLevelName(logger.level),
+            "propagate": logger.propagate,
+            "handlers": [handler.get_name() for handler in logger.handlers],
+        }
+        self.loggers[logger.name] = config
+
+    def _extract_handlers(self, logger: logging.Logger):
+        for handler in logger.handlers:
+            self._extract_handler_config(handler)
+
+    def _extract_handler_config(self, handler: logging.Handler):
+        handler_name = handler.get_name()
+        if handler_name in self.handlers:
+            return
+        config: HandlerConfig = {
+            "class_": self._full_name(handler),
+            "level": logging.getLevelName(handler.level),
+            "formatter": (
+                self._formatter_name(handler.formatter)
+                if handler.formatter
+                else None
+            ),
+            "stream": None,
+        }
+        # if (stream := getattr(handler, "stream", None)) and (
+        #     stream_name := getattr(stream, "name", None)
+        # ):
+        #     config["stream"] = stream_name
+        self.handlers[handler_name] = config
+        if handler.formatter:
+            self._extract_formatter_config(handler.formatter)
+
+    def _extract_formatter_config(self, formatter: logging.Formatter):
+        formatter_name = self._formatter_name(formatter)
+        if formatter_name in self.formatters:
+            return
+        config: FormatterConfig = {
+            "class_": self._full_name(formatter),
+            "format": formatter._fmt,
+            "datefmt": formatter.datefmt,
+        }
+        self.formatters[formatter_name] = config
+
+    def _clean(self, d: dict[str, Any]) -> dict[str, Any]:
+        return {
+            k.replace("class_", "class"): self._clean(v)
+            if isinstance(v, dict)
+            else v
+            for k, v in d.items()
+        }
+
+    @staticmethod
+    def _formatter_name(
+        formatter: logging.Formatter, prefix: str = "formatter"
+    ):
+        return f"{prefix}_{formatter.__class__.__name__}".lower()
+
+    @staticmethod
+    def _full_name(obj):
+        return f"{obj.__module__}.{obj.__class__.__name__}"
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/logger.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/logger.py
new file mode 100644
index 0000000..e83ddda
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/logging/logger.py
@@ -0,0 +1,126 @@
+import logging
+
+from collections import defaultdict
+from logging import LogRecord
+from logging.handlers import QueueHandler
+from multiprocessing import Manager
+from queue import Empty, Full
+from signal import SIGINT, SIGTERM
+from signal import signal as signal_func
+
+from sanic import Sanic
+from sanic.log import logger as root_logger
+from sanic.log import logger as server_logger
+from sanic.logging.setup import setup_logging
+
+from sanic_ext.extensions.logging.extractor import LoggingConfigExtractor
+
+
+async def prepare_logger(app: Sanic, *_):
+    Logger.prepare(app)
+
+
+async def setup_logger(app: Sanic, *_):
+    logger = Logger()
+    extractor = LoggingConfigExtractor()
+    for logger_name in app.config.LOGGERS:
+        extractor.add_logger(logging.getLogger(logger_name))
+    app.manager.manage(
+        "Logger",
+        logger,
+        {
+            "queue": app.shared_ctx.logger_queue,
+            "config": extractor.compile(),
+        },
+        transient=True,
+    )
+
+
+class SanicQueueHandler(QueueHandler):
+    def emit(self, record: LogRecord) -> None:
+        try:
+            return super().enqueue(record)
+        except Full:
+            server_logger.warning(
+                "Background logger is full. Emitting log in process."
+            )
+            server_logger.handle(record)
+
+
+async def setup_server_logging(app: Sanic):
+    qhandler = SanicQueueHandler(app.shared_ctx.logger_queue)
+    app.ctx._logger_handlers = defaultdict(list)
+    app.ctx._qhandler = qhandler
+
+    for logger_name in app.config.LOGGERS:
+        logger_instance = logging.getLogger(logger_name)
+        for handler in logger_instance.handlers:
+            logger_instance.removeHandler(handler)
+        logger_instance.addHandler(qhandler)
+
+
+async def remove_server_logging(app: Sanic):
+    for logger, handlers in app.ctx._logger_handlers.items():
+        logger.removeHandler(app.ctx._qhandler)
+        for handler in handlers:
+            logger.addHandler(handler)
+
+
+class Logger:
+    LOGGERS: list[str] = []
+
+    def __init__(self):
+        self.run = True
+        self.loggers = {
+            logger: logging.getLogger(logger) for logger in self.LOGGERS
+        }
+
+    def __call__(self, queue, config) -> None:
+        signal_func(SIGINT, self.stop)
+        signal_func(SIGTERM, self.stop)
+
+        logging.config.dictConfig(config)
+
+        setup_loggers = set(config["loggers"].keys())
+        enabled_loggers = set(self.loggers.keys())
+        missing = enabled_loggers - setup_loggers
+        root_logger.info(
+            f"Setup background logging for: {', '.join(setup_loggers)}"
+        )
+        if missing:
+            root_logger.warning(
+                f"Logger config not found for: {', '.join(missing)}"
+            )
+        setup_logging(True, no_color=False, log_extra=True)
+
+        while self.run:
+            try:
+                record: LogRecord = queue.get(timeout=0.05)
+            except Empty:
+                continue
+            logger = self.loggers.get(record.name)
+            logger.handle(record)
+
+    def stop(self, *_):
+        if self.run:
+            self.run = False
+
+    @classmethod
+    def update_cls_loggers(cls, logger_names: list[str]):
+        cls.LOGGERS = logger_names
+
+    @classmethod
+    def prepare(cls, app: Sanic):
+        sync_manager = Manager()
+        logger_queue = sync_manager.Queue(
+            maxsize=app.config.LOGGING_QUEUE_MAX_SIZE
+        )
+        app.shared_ctx.logger_queue = logger_queue
+        cls.update_cls_loggers(app.config.LOGGERS)
+
+    @classmethod
+    def setup(cls, app: Sanic):
+        app.main_process_start(prepare_logger)
+        app.main_process_ready(setup_logger)
+        app.before_server_start(setup_server_logging)
+        app.before_server_stop(remove_server_logging)
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/autodoc.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/autodoc.py
new file mode 100644
index 0000000..f49b886
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/autodoc.py
@@ -0,0 +1,93 @@
+import inspect
+import warnings
+
+import yaml
+
+
+class OpenAPIDocstringParser:
+    def __init__(self, docstring: str):
+        """
+        Args:
+            docstring (str): docstring of function to be parsed
+        """
+        if docstring is None:
+            docstring = ""
+        self.docstring = inspect.cleandoc(docstring)
+
+    def to_openAPI_2(self) -> dict:
+        """
+        Returns:
+            json style dict: dict to be read for the path by swagger 2.0 UI
+        """
+        raise NotImplementedError()
+
+    def to_openAPI_3(self) -> dict:
+        """
+        Returns:
+            json style dict: dict to be read for the path by swagger 3.0.0 UI
+        """
+        raise NotImplementedError()
+
+
+class YamlStyleParametersParser(OpenAPIDocstringParser):
+    def _parse_no_yaml(self, doc: str) -> dict:
+        """
+        Args:
+            doc (str): section of doc before yaml, or full section of doc
+        Returns:
+            json style dict: dict to be read for the path by swagger UI
+        """
+        # clean again in case further indentation can be removed,
+        # usually this do nothing...
+        doc = inspect.cleandoc(doc)
+
+        if len(doc) == 0:
+            return {}
+
+        lines = doc.split("\n")
+
+        if len(lines) == 1:
+            return {"summary": lines[0]}
+        else:
+            summary = lines.pop(0)
+
+            # remove empty lines at the beginning of the description
+            while len(lines) and lines[0].strip() == "":
+                lines.pop(0)
+
+            if len(lines) == 0:
+                return {"summary": summary}
+            else:
+                # use html tag to preserve linebreaks
+                return {"summary": summary, "description": "
".join(lines)} + + def _parse_yaml(self, doc: str) -> dict: + """ + Args: + doc (str): section of doc detected as openapi yaml + Returns: + json style dict: dict to be read for the path by swagger UI + Warns: + UserWarning if the yaml couldn't be parsed + """ + try: + return yaml.safe_load(doc) + except Exception as e: + warnings.warn(f"error parsing openAPI yaml, ignoring it. ({e})") + return {} + + def _parse_all(self) -> dict: + if "openapi:\n" not in self.docstring: + return self._parse_no_yaml(self.docstring) + + predoc, yamldoc = self.docstring.split("openapi:\n", 1) + + conf = self._parse_no_yaml(predoc) + conf.update(self._parse_yaml(yamldoc)) + return conf + + def to_openAPI_2(self) -> dict: + return self._parse_all() + + def to_openAPI_3(self) -> dict: + return self._parse_all() diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/blueprint.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/blueprint.py new file mode 100644 index 0000000..4162bde --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/blueprint.py @@ -0,0 +1,261 @@ +import inspect + +from functools import lru_cache, partial +from os.path import abspath, dirname, realpath + +from sanic import Request +from sanic.blueprints import Blueprint +from sanic.config import Config +from sanic.log import logger +from sanic.response import file, html, json + +from sanic_ext.config import PRIORITY +from sanic_ext.extensions.openapi.builders import ( + OperationStore, + SpecificationBuilder, +) + +from ...utils.route import ( + clean_route_name, + get_all_routes, + get_blueprinted_routes, +) + + +@lru_cache +def get_oauth2_redirect_html(version: str): + import urllib + + response = urllib.request.urlopen( + f"https://cdn.jsdelivr.net/npm/swagger-ui-dist@{version}/" + "oauth2-redirect.html" + ).read() + + return response.decode("utf-8") + + +def oauth2_handler(request: Request, version: str): + return html(get_oauth2_redirect_html(version)) + + +def blueprint_factory(config: Config): + bp = Blueprint("openapi", url_prefix=config.OAS_URL_PREFIX) + + dir_path = dirname(realpath(__file__)) + dir_path = abspath(dir_path + "/ui") + + for ui in ("redoc", "swagger"): + if getattr(config, f"OAS_UI_{ui}".upper()): + path = getattr(config, f"OAS_PATH_TO_{ui}_HTML".upper()) + uri = getattr(config, f"OAS_URI_TO_{ui}".upper()) + version = getattr(config, f"OAS_UI_{ui}_VERSION".upper(), "") + html_title = getattr(config, f"OAS_UI_{ui}_HTML_TITLE".upper()) + custom_css = getattr(config, f"OAS_UI_{ui}_CUSTOM_CSS".upper()) + html_path = path if path else f"{dir_path}/{ui}.html" + + with open(html_path) as f: + page = f.read() + + def index( + request: Request, page: str, html_title: str, custom_css: str + ): + prefix = ( + request.app.url_for("openapi.index", _external=True) + if getattr(request.app.config, "SERVER_NAME", None) + else getattr(request.app.config, "OAS_URL_PREFIX") + ).rstrip("/") + return html( + page.replace("__VERSION__", version) + .replace("__URL_PREFIX__", prefix) + .replace("__HTML_TITLE__", html_title) + .replace("__HTML_CUSTOM_CSS__", custom_css) + ) + + bp.add_route( + partial( + index, + page=page, + html_title=html_title, + custom_css=custom_css, + ), + uri, + name=ui, + ) + if config.OAS_UI_DEFAULT and config.OAS_UI_DEFAULT == ui: + bp.add_route( + partial( + index, + page=page, + html_title=html_title, + custom_css=custom_css, + ), + "", + name="index", + ) + + if ui == "swagger": + oauth2_redirect_uri = getattr( + config, "OAS_UI_SWAGGER_OAUTH2_REDIRECT" + ) + + bp.add_route( + partial(oauth2_handler, version=version), + oauth2_redirect_uri, + name="oauth2-redirect", + ) + + @bp.get(config.OAS_URI_TO_JSON) + async def spec(request: Request): + if config.OAS_CUSTOM_FILE: + return await file(config.OAS_CUSTOM_FILE) + return json(SpecificationBuilder().build(request.app).serialize()) + + if config.OAS_UI_SWAGGER: + + @bp.get(config.OAS_URI_TO_CONFIG) + def openapi_config(request: Request): + return json(request.app.config.SWAGGER_UI_CONFIGURATION) + + @bp.before_server_start(priority=PRIORITY) + def build_spec(app, loop): + specification = SpecificationBuilder() + # --------------------------------------------------------------- # + # Blueprint Tags + # --------------------------------------------------------------- # + + for blueprint_name, handler in get_blueprinted_routes(app): + operation = OperationStore()[handler] + if not operation.tags: + operation.tag(blueprint_name) + + # --------------------------------------------------------------- # + # Operations + # --------------------------------------------------------------- # + for ( + uri, + route_name, + route_parameters, + method_handlers, + host, + ) in get_all_routes(app, bp.url_prefix): + # --------------------------------------------------------------- # + # Methods + # --------------------------------------------------------------- # + + for method, _handler in method_handlers: + if ( + (method == "OPTIONS" and app.config.OAS_IGNORE_OPTIONS) + or (method == "HEAD" and app.config.OAS_IGNORE_HEAD) + or method == "TRACE" + ): + continue + + if hasattr(_handler, "view_class"): + _handler = getattr(_handler.view_class, method.lower()) + store = OperationStore() + if ( + _handler not in store + and (func := getattr(_handler, "__func__", None)) + and func in store + ): + _handler = func + operation = store[_handler] + + if operation._exclude or "openapi" in operation.tags: + continue + + docstring = inspect.getdoc(_handler) + + if ( + docstring + and app.config.OAS_AUTODOC + and operation._allow_autodoc + ): + operation.autodoc(docstring) + + operation._default["operationId"] = ( + f"{method.lower()}~{route_name}" + ) + operation._default["summary"] = clean_route_name(route_name) + + if host: + if "servers" not in operation._default: + operation._default["servers"] = [] + operation._default["servers"].append({"url": f"//{host}"}) + + for _parameter in route_parameters: + if any( + param.fields["name"] == _parameter.name + for param in operation.parameters + ): + continue + + kwargs = {} + if operation._autodoc and ( + parameters := operation._autodoc.get("parameters") + ): + for param in parameters: + if param.pop("name", None) == _parameter.name: + kwargs["description"] = param.get( + "description" + ) + kwargs["required"] = param.get("required") + if schema := param.get("schema"): + logger.warning( + f"Ignoring the schema {schema} in " + f"'{route_name}' for " + f"'{_parameter.name}'. " + "Instead of using the definition in " + "docstring definition, Sanic will use " + "the actual schema defined for this " + "parameter on the route." + ) + break + + operation.parameter( + _parameter.name, _parameter.cast, "path", **kwargs + ) + + operation._app = app + specification.operation(uri, method, operation) + + add_static_info_to_spec_from_config(app, specification) + + return bp + + +def add_static_info_to_spec_from_config(app, specification): + """ + Reads app.config and sets attributes to specification according to the + desired values. + + Modifies specification in-place and returns None + """ + specification._do_describe( + getattr(app.config, "API_TITLE", "API"), + getattr(app.config, "API_VERSION", "1.0.0"), + getattr(app.config, "API_DESCRIPTION", None), + getattr(app.config, "API_TERMS_OF_SERVICE", None), + ) + + specification._do_license( + getattr(app.config, "API_LICENSE_NAME", None), + getattr(app.config, "API_LICENSE_URL", None), + ) + + specification._do_contact( + getattr(app.config, "API_CONTACT_NAME", None), + getattr(app.config, "API_CONTACT_URL", None), + getattr(app.config, "API_CONTACT_EMAIL", None), + ) + + schemes = getattr(app.config, "API_SCHEMES", ["http"]) or ["http"] + if isinstance(schemes, str): + schemes = [s.strip() for s in schemes.split(",")] + for scheme in schemes: + host = getattr(app.config, "API_HOST", None) + basePath = getattr(app.config, "API_BASEPATH", "") + if host is None or basePath is None: + continue + + specification.url(f"{scheme}://{host}/{basePath}") diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/builders.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/builders.py new file mode 100644 index 0000000..413e1a6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/builders.py @@ -0,0 +1,446 @@ +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional, Union, cast + +from sanic_ext.extensions.openapi.constants import ( + SecuritySchemeAuthorization, + SecuritySchemeLocation, + SecuritySchemeType, +) + +from ...utils.route import remove_nulls, remove_nulls_from_kwargs +from .autodoc import YamlStyleParametersParser +from .definitions import ( + Any, + Components, + Contact, + ExternalDocumentation, + Flows, + Info, + License, + OpenAPI, + Operation, + Parameter, + PathItem, + RequestBody, + Response, + SecurityRequirement, + SecurityScheme, + Server, + Tag, +) + + +if TYPE_CHECKING: + from sanic import Sanic + + +class OperationBuilder: + summary: str + description: str + operationId: str + requestBody: RequestBody + externalDocs: ExternalDocumentation + tags: list[str] + security: list[Any] + parameters: list[Parameter] + responses: dict[str, Response] + callbacks: list[str] # TODO + deprecated: bool = False + + def __init__(self): + self.tags = [] + self.security = [] + self.parameters = [] + self.responses = {} + self._default = {} + self._autodoc = None + self._exclude = False + self._allow_autodoc = True + self._app: Optional[Sanic] = None + + def name(self, value: str): + self.operationId = value + + def describe(self, summary: str = None, description: str = None): + if summary: + self.summary = summary + + if description: + self.description = description + + def document(self, url: str, description: str = None): + self.externalDocs = ExternalDocumentation.make(url, description) + + def tag(self, *args: str): + for arg in args: + if isinstance(arg, Tag): + arg = arg.fields["name"] + self.tags.append(arg) + + def deprecate(self): + self.deprecated = True + + def body(self, content: Any, **kwargs): + self.requestBody = RequestBody.make(content, **kwargs) + + def parameter( + self, name: str, schema: Any, location: str = "query", **kwargs + ): + self.parameters.append( + Parameter.make(name, schema, location, **kwargs) + ) + + def response( + self, status, content: Any = None, description: str = None, **kwargs + ): + response = Response.make(content, description, **kwargs) + if status in self.responses: + self.responses[status]._fields["content"].update( + response.fields["content"] + ) + else: + self.responses[status] = response + + def secured(self, *args, **kwargs): + if not kwargs and len(args) == 1 and isinstance(args[0], dict): + items = args[0] + else: + items = {**{v: [] for v in args}, **kwargs} + gates = {} + + for name, params in items.items(): + gate = name.__name__ if isinstance(name, type) else name + gates[gate] = params + + self.security.append(gates) + + def disable_autodoc(self): + self._allow_autodoc = False + + def build(self): + operation_dict = self._build_merged_dict() + if "responses" not in operation_dict: + # todo -- look into more consistent default response format + operation_dict["responses"] = {"default": {"description": "OK"}} + + return Operation(**operation_dict) + + def _build_merged_dict(self): + defined_dict = self.__dict__.copy() + autodoc_dict = self._autodoc or {} + default_dict = self._default + merged_dict = {} + + for d in (default_dict, autodoc_dict, defined_dict): + cleaned = { + k: v for k, v in d.items() if v and not k.startswith("_") + } + merged_dict.update(cleaned) + + return merged_dict + + def autodoc(self, docstring: str): + y = YamlStyleParametersParser(docstring) + self._autodoc = y.to_openAPI_3() + + def exclude(self, flag: bool = True): + self._exclude = flag + + +class OperationStore(defaultdict): + _singleton = None + + def __new__(cls) -> Any: + if not cls._singleton: + cls._singleton = super().__new__(cls) + return cls._singleton + + def __init__(self): + super().__init__(OperationBuilder) + + @classmethod + def reset(cls): + cls._singleton = None + + +class SpecificationBuilder: + _urls: list[str] + _title: str + _version: str + _description: Optional[str] + _terms: Optional[str] + _contact: Contact + _license: License + _paths: dict[str, dict[str, OperationBuilder]] + _tags: dict[str, Tag] + _security: list[SecurityRequirement] + _components: dict[str, Any] + _servers: list[Server] + # _components: ComponentsBuilder + # deliberately not included + _singleton: Optional[SpecificationBuilder] = None + + def __new__(cls) -> SpecificationBuilder: + if not cls._singleton: + cls._singleton = super().__new__(cls) + cls._setup_instance(cls._singleton) + return cast(SpecificationBuilder, cls._singleton) + + @classmethod + def _setup_instance(cls, instance): + instance._components = defaultdict(dict) + instance._contact = None + instance._description = None + instance._external = None + instance._license = None + instance._paths = defaultdict(dict) + instance._servers = [] + instance._tags = {} + instance._security = [] + instance._terms = None + instance._title = None + instance._urls = [] + instance._version = None + + @classmethod + def reset(cls): + cls._singleton = None + + @property + def tags(self): + return self._tags + + @property + def security(self): + return self._security + + def url(self, value: str): + self._urls.append(value) + + def describe( + self, + title: str, + version: str, + description: Optional[str] = None, + terms: Optional[str] = None, + ): + self._title = title + self._version = version + self._description = description + self._terms = terms + + def _do_describe( + self, + title: str, + version: str, + description: Optional[str] = None, + terms: Optional[str] = None, + ): + if any([self._title, self._version, self._description, self._terms]): + return + self.describe(title, version, description, terms) + + def tag(self, name: str, description: Optional[str] = None, **kwargs): + self._tags[name] = Tag(name, description=description, **kwargs) + + def external(self, url: str, description: Optional[str] = None, **kwargs): + self._external = ExternalDocumentation(url, description=description) + + def secured( + self, + name: str = None, + value: Optional[Union[str, Sequence[str]]] = None, + ): + if value is None: + value = [] + elif isinstance(value, str): + value = [value] + else: + value = list(value) + self._security.append(SecurityRequirement(name=name, value=value)) + + def contact(self, name: str = None, url: str = None, email: str = None): + kwargs = remove_nulls_from_kwargs(name=name, url=url, email=email) + self._contact = Contact(**kwargs) + + def _do_contact( + self, name: str = None, url: str = None, email: str = None + ): + if self._contact: + return + + self.contact(name, url, email) + + def license(self, name: str = None, url: str = None): + if name is not None: + self._license = License(name, url=url) + + def _do_license(self, name: str = None, url: str = None): + if self._license: + return + + self.license(name, url) + + def operation(self, path: str, method: str, operation: OperationBuilder): + for _tag in operation.tags: + if _tag in self._tags.keys(): + continue + + self._tags[_tag] = Tag(_tag) + + self._paths[path][method.lower()] = operation + + def add_component(self, location: str, name: str, obj: Any): + self._components[location].update({name: obj}) + + def has_component(self, location: str, name: str) -> bool: + return name in self._components.get(location, {}) + + def add_security_scheme( + self, + ident: str, + type: Union[str, SecuritySchemeType], + *, + bearer_format: Optional[str] = None, + description: Optional[str] = None, + flows: Optional[Union[Flows, dict[str, Any]]] = None, + location: Union[ + str, SecuritySchemeLocation + ] = SecuritySchemeLocation.HEADER, + name: str = "authorization", + openid_connect_url: Optional[str] = None, + scheme: Union[ + str, SecuritySchemeAuthorization + ] = SecuritySchemeAuthorization.BEARER, + ): + if isinstance(type, str): + type = SecuritySchemeType(type) + if isinstance(location, str): + location = SecuritySchemeLocation(location) + + kwargs: dict[str, Any] = {"type": type, "description": description} + + if type is SecuritySchemeType.API_KEY: + kwargs["location"] = location + kwargs["name"] = name + elif type is SecuritySchemeType.HTTP: + kwargs["scheme"] = scheme + kwargs["bearerFormat"] = bearer_format + elif type is SecuritySchemeType.OAUTH2: + kwargs["flows"] = flows + elif type is SecuritySchemeType.OPEN_ID_CONNECT: + kwargs["openIdConnectUrl"] = openid_connect_url + + self.add_component( + "securitySchemes", + ident, + SecurityScheme(**kwargs), + ) # type: ignore + + def raw(self, data): + if "info" in data: + self.describe( + data["info"].get("title"), + data["info"].get("version"), + data["info"].get("description"), + data["info"].get("terms"), + ) + + if "servers" in data: + for server in data["servers"]: + self._servers.append(Server(**server)) + + if "paths" in data: + self._paths.update(data["paths"]) + + if "components" in data: + for location, component in data["components"].items(): + self._components[location].update(component) + + if "security" in data: + for security in data["security"]: + if not security: + self.secured() + else: + for key, value in security.items(): + self.secured(key, value) + + if "tags" in data: + for tag in data["tags"]: + self.tag(**tag) + + if "externalDocs" in data: + self.external(**data["externalDocs"]) + + def build(self, app: Sanic) -> OpenAPI: + info = self._build_info() + paths = self._build_paths(app) + tags = self._build_tags() + security = self._build_security() + + url_servers = getattr(self, "_urls", None) + servers = self._servers + existing = [ + server.fields["url"].strip("/") for server in self._servers + ] + if url_servers is not None: + for url_server in url_servers: + if url_server.strip("/") not in existing: + servers.append(Server(url=url_server)) + + components = ( + Components(**self._components) if self._components else None + ) + + return OpenAPI( + info, + paths, + tags=tags, + servers=servers, + security=security, + components=components, + externalDocs=self._external, + ) + + def _build_info(self) -> Info: + kwargs = remove_nulls( + { + "description": self._description, + "termsOfService": self._terms, + "license": self._license, + "contact": self._contact, + }, + deep=False, + ) + + return Info(self._title, self._version, **kwargs) + + def _build_tags(self): + return [self._tags[k] for k in self._tags] + + def _build_paths(self, app: Sanic) -> dict: + paths = {} + + for path, operations in self._paths.items(): + paths[path] = PathItem( + **{ + k: v if isinstance(v, dict) else v.build() + for k, v in operations.items() + if isinstance(v, dict) or v._app is app + } + ) + + return paths + + def _build_security(self): + return [ + ( + {sec.fields["name"]: sec.fields["value"]} + if sec.fields["name"] is not None + else {} + ) + for sec in self.security + ] diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/constants.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/constants.py new file mode 100644 index 0000000..3285cfa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/constants.py @@ -0,0 +1,36 @@ +from enum import Enum, auto + + +class BaseEnum(Enum): + def _generate_next_value_(name, start, count, last_values): + parts = name.split("_") + return parts[0].lower() + "".join(part.title() for part in parts[1:]) + + +class SecuritySchemeType(BaseEnum): + API_KEY = auto() + HTTP = auto() + OAUTH2 = auto() + OPEN_ID_CONNECT = auto() + + +class SecuritySchemeLocation(BaseEnum): + QUERY = auto() + HEADER = auto() + COOKIE = auto() + + +class SecuritySchemeAuthorization(BaseEnum): + def _generate_next_value_(name, start, count, last_values): + return name.title() + + BASIC = auto() + BEARER = auto() + DIGEST = auto() + HOBA = "HOBA" + MUTUAL = auto() + NEGOTIATE = auto() + OAUTH = "OAuth" + SCRAM_SHA_1 = "SCRAM-SHA-1" + SCRAM_SHA_256 = "SCRAM-SHA-256" + VAPID = "vapid" diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/definitions.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/definitions.py new file mode 100644 index 0000000..91fc6e0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/definitions.py @@ -0,0 +1,455 @@ +""" +Classes defined from the OpenAPI 3.0 specifications. + +I.e., the objects described https://swagger.io/docs/specification + +""" + +from __future__ import annotations + +from inspect import isclass +from typing import ( + Any, + Literal, + Optional, + Union, + get_type_hints, +) + +from sanic.exceptions import SanicException + +from sanic_ext.utils.typing import ( + contains_annotations, + is_msgspec, + is_pydantic, +) + +from .types import Definition, Schema + + +class Reference(Schema): + def __init__(self, value): + super().__init__(**{"$ref": value}) + + def guard(self, fields: dict[str, Any]): + return fields + + +class Contact(Definition): + name: str + url: str + email: str + + +class License(Definition): + name: str + url: str + + def __init__(self, name: str, **kwargs): + super().__init__(name=name, **kwargs) + + +class Info(Definition): + title: str + description: str + termsOfService: str + contact: Contact + license: License + version: str + + def __init__(self, title: str, version: str, **kwargs): + super().__init__(title=title, version=version, **kwargs) + + +class Example(Definition): + summary: str + description: str + value: Any + externalValue: str + + def __init__(self, value: Any = None, **kwargs): + super().__init__(value=value, **kwargs) + + @staticmethod + def make(value: Any, **kwargs): + return Example(Schema.make(value), **kwargs) + + @staticmethod + def external(value: Any, **kwargs): + return Example(externalValue=value, **kwargs) + + +class MediaType(Definition): + schema: Schema + example: Any + + def __init__(self, schema: Union[Schema, dict[str, Any]], **kwargs): + if isinstance(schema, dict) and contains_annotations(schema): + schema = Schema.make(schema) + super().__init__(schema=schema, **kwargs) + + @staticmethod + def make(value: Any): + if isinstance(value, dict): + kwargs = {} + if "schema" in value: + kwargs = {**value} + value = kwargs.pop("schema") + return MediaType(value, **kwargs) + # See https://github.com/sanic-org/sanic-ext/issues/152 + # The following lines will automatically inject pydantic models as + # components if that feature is desired. Until that decision is made + # this commented out code will remain. + # elif isclass(value) and is_pydantic(value): + # return MediaType(Component(value)) + return MediaType(Schema.make(value)) + + @staticmethod + def all(content: Any): + media_types = ( + content if isinstance(content, dict) else {"*/*": content or {}} + ) + + return {x: MediaType.make(v) for x, v in media_types.items()} + + +class Response(Definition): + content: Union[Any, dict[str, Union[Any, MediaType]]] + description: Optional[str] + status: Union[Literal["default"], int] + + __nullable__ = None + __ignore__ = ["status"] + + def __init__( + self, + content: Optional[Union[Any, dict[str, Union[Any, MediaType]]]] = None, + status: Union[Literal["default"], int] = "default", + description: Optional[str] = None, + **kwargs, + ): + super().__init__( + content=content, + status=status, + description=description, + **kwargs, + ) + + @staticmethod + def make(content, description: Optional[str] = None, **kwargs): + if not description: + description = "Default Response" + + return Response( + MediaType.all(content), description=description, **kwargs + ) + + +class RequestBody(Definition): + description: Optional[str] + required: Optional[bool] + content: Union[Any, dict[str, Union[Any, MediaType]]] + + __nullable__ = None + + def __init__( + self, + content: Union[Any, dict[str, Union[Any, MediaType]]], + required: Optional[bool] = None, + description: Optional[str] = None, + **kwargs, + ): + """Can be initialized with content in one of a few ways: + + RequestBody(SomeModel) + RequestBody({"application/json": SomeModel}) + RequestBody({"application/json": {"name": str}}) + """ + super().__init__( + content=content, + required=required, + description=description, + **kwargs, + ) + + @staticmethod + def make(content: Any, **kwargs): + return RequestBody(MediaType.all(content), **kwargs) + + +class ExternalDocumentation(Definition): + url: str + description: str + + __nullable__ = None + + def __init__(self, url: str, description=None): + super().__init__(url=url, description=description) + + @staticmethod + def make(url: str, description: Optional[str] = None): + return ExternalDocumentation(url, description) + + +class Header(Definition): + name: str + description: str + externalDocs: ExternalDocumentation + + def __init__(self, url: str, description=None): + super().__init__(url=url, description=description) + + @staticmethod + def make(url: str, description: Optional[str] = None): + return Header(url, description) + + +class Parameter(Definition): + name: str + schema: Union[type, Schema] + location: str + description: Optional[str] + required: Optional[bool] + deprecated: Optional[bool] + allowEmptyValue: Optional[bool] + + __nullable__ = None + + def __init__( + self, + name: str, + schema: Union[type, Schema] = str, + location: str = "query", + description: Optional[str] = None, + required: Optional[bool] = None, + deprecated: Optional[bool] = None, + allowEmptyValue: Optional[bool] = None, + **kwargs, + ): + super().__init__( + name=name, + schema=schema, + location=location, + description=description, + required=required, + deprecated=deprecated, + allowEmptyValue=allowEmptyValue, + **kwargs, + ) + + @property + def fields(self): + values = super().fields + + if "location" in values: + values["in"] = values.pop("location") + + return values + + @staticmethod + def make(name: str, schema: type, location: str, **kwargs): + if location == "path" and "required" not in kwargs: + kwargs["required"] = True + + return Parameter(name, Schema.make(schema), location, **kwargs) + + +class Operation(Definition): + tags: list[str] + summary: str + description: str + operationId: str + requestBody: RequestBody + externalDocs: ExternalDocumentation + parameters: list[Parameter] + responses: dict[str, Response] + security: dict[str, list[str]] + callbacks: list[str] # TODO + deprecated: bool + servers: list[dict[str, str]] + + +class PathItem(Definition): + summary: str + description: str + get: Operation + put: Operation + post: Operation + delete: Operation + options: Operation + head: Operation + patch: Operation + trace: Operation + + +class Flow(Definition): + authorizationUrl: str + tokenUrl: str + refreshUrl: str + scopes: dict[str, str] + + +class Flows(Definition): + implicit: Flow + password: Flow + clientCredentials: Flow + authorizationCode: Flow + + +class SecurityRequirement(Definition): + name: str + value: list[str] + + +class SecurityScheme(Definition): + type: str + bearerFormat: str + description: str + flows: Flows + location: str + name: str + openIdConnectUrl: str + scheme: str + + __nullable__ = None + + def __init__(self, type: str, **kwargs): + super().__init__(type=type, **kwargs) + + @property + def fields(self): + values = super().fields + + if "location" in values: + values["in"] = values.pop("location") + + return values + + @staticmethod + def make(_type: str, cls: type, **kwargs): + params: dict[str, Any] = getattr(cls, "__dict__", {}) + return SecurityScheme(_type, **params, **kwargs) + + +class ServerVariable(Definition): + default: str + description: str + enum: list[str] + + def __init__(self, default: str, **kwargs): + super().__init__(default=default, **kwargs) + + +class Server(Definition): + url: str + description: str + variables: dict[str, ServerVariable] + + __nullable__ = None + + def __init__( + self, + url: str, + description: Optional[str] = None, + variables: Optional[dict[str, Any]] = None, + ): + super().__init__( + url=url, description=description, variables=variables or {} + ) + + +class Tag(Definition): + name: str + description: str + externalDocs: ExternalDocumentation + + def __init__(self, name: str, **kwargs): + super().__init__(name=name, **kwargs) + + +class Components(Definition): + # This class is not being used in sanic-openapi right now, but the + # definition is kept here to keep in close accordance with the openapi + # spec, in case it is desired to be added later. + schemas: dict[str, Schema] + responses: dict[str, Response] + parameters: dict[str, Parameter] + examples: dict[str, Example] + requestBodies: dict[str, RequestBody] + headers: dict[str, Header] + securitySchemes: dict[str, SecurityScheme] + links: dict[str, Schema] # TODO + callbacks: dict[str, Schema] # TODO + + +def Component( + obj: Any, *, name: str = "", field: str = "schemas" +) -> Reference: + hints = get_type_hints(Components) + + if field not in hints: + raise AttributeError( + f"Unknown field '{field}'. Must be a valid field per OAS3 " + "requirements. See " + "https://swagger.io/specification/#components-object." + ) + + if not isclass(obj) and not name: + raise SanicException( + f"Components {obj} must be created with a declared name" + ) + + if not name: + name = obj.__name__ + + from sanic_ext.extensions.openapi.builders import SpecificationBuilder + + spec = SpecificationBuilder() + refval = f"#/components/{field}/{name}" + ref = Reference(refval) + + if not spec.has_component(field, name): + prop_info = hints[field] + type_ = prop_info.__args__[1] + if is_msgspec(obj): + import msgspec + + _, definitions = msgspec.json.schema_components( + [obj], ref_template="#/components/schemas/{name}" + ) + if definitions: + for key, value in definitions.items(): + spec.add_component(field, key, value) + elif is_pydantic(obj): + try: + schema = obj.schema + except AttributeError: + schema = obj.__pydantic_model__.schema + component = schema(ref_template="#/components/schemas/{model}") + definitions = component.pop("definitions", None) + if definitions: + for key, value in definitions.items(): + spec.add_component(field, key, value) + spec.add_component(field, name, component) + else: + component = ( + type_.make(obj) if hasattr(type_, "make") else type_(obj) + ) + spec.add_component(field, name, component) + + return ref + + +class OpenAPI(Definition): + openapi: str + info: Info + servers: list[Server] + paths: dict[str, PathItem] + components: Components + security: dict[str, SecurityScheme] + tags: list[Tag] + externalDocs: ExternalDocumentation + + def __init__(self, info: Info, paths: dict[str, PathItem], **kwargs): + use = {k: v for k, v in kwargs.items() if v is not None} + super().__init__(openapi="3.0.3", info=info, paths=paths, **use) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/extension.py new file mode 100644 index 0000000..9853eab --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/extension.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from sanic_ext.extensions.openapi.builders import SpecificationBuilder + +from ..base import Extension +from .blueprint import blueprint_factory + + +if TYPE_CHECKING: + from sanic_ext import Extend + + +class OpenAPIExtension(Extension): + name = "openapi" + + def startup(self, bootstrap: Extend) -> None: + if self.app.config.OAS: + self.bp = blueprint_factory(self.app.config) + self.app.blueprint(self.bp) + bootstrap._openapi = SpecificationBuilder() + + def label(self): + if self.app.config.OAS: + return self._make_url() + + return "" + + def included(self): + return self.config.OAS + + def _make_url(self): + name = f"{self.bp.name}.index" + _server = ( + None + if "SERVER_NAME" in self.app.config + else self.app.serve_location + ) or None + _external = bool(_server) or "SERVER_NAME" in self.app.config + return ( + f"{self.app.url_for(name, _external=_external, _server=_server)}" + ) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/openapi.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/openapi.py new file mode 100644 index 0000000..d7ede5b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/openapi.py @@ -0,0 +1,534 @@ +""" +This module provides decorators which append +documentation to OperationStore() and components created in the blueprints. + +""" + +from collections.abc import Sequence +from functools import wraps +from inspect import isawaitable, isclass +from typing import ( + Any, + Callable, + Literal, + Optional, + TypeVar, + Union, + overload, +) + +from sanic import Blueprint +from sanic.exceptions import InvalidUsage, SanicException + +from sanic_ext.extensions.openapi import definitions +from sanic_ext.extensions.openapi.builders import ( + OperationStore, + SpecificationBuilder, +) +from sanic_ext.extensions.openapi.definitions import Component +from sanic_ext.extensions.openapi.types import ( + Array, + Binary, + Boolean, + Byte, + Date, + DateTime, + Double, + Email, + Float, + Integer, + Long, + Object, + Password, + Schema, + String, + Time, +) +from sanic_ext.extras.validation.setup import do_validation, generate_schema +from sanic_ext.utils.extraction import extract_request + + +__all__ = ( + "definitions", + "body", + "component", + "definition", + "deprecated", + "description", + "document", + "exclude", + "no_autodoc", + "operation", + "parameter", + "response", + "secured", + "summary", + "tag", + "Array", + "Binary", + "Boolean", + "Byte", + "Component", + "Date", + "DateTime", + "Double", + "Email", + "Float", + "Integer", + "Long", + "Object", + "Password", + "String", + "Time", +) + + +def _content_or_component(content): + if isclass(content): + spec = SpecificationBuilder() + if spec._components["schemas"].get(content.__name__): + content = definitions.Component(content) + return content + + +@overload +def exclude(flag: bool = True, *, bp: Blueprint) -> None: ... + + +@overload +def exclude(flag: bool = True) -> Callable: ... + + +def exclude(flag: bool = True, *, bp: Optional[Blueprint] = None): + if bp: + for route in bp.routes: + exclude(flag)(route.handler) + return + + def inner(func): + OperationStore()[func].exclude(flag) + return func + + return inner + + +T = TypeVar("T") + + +def operation(name: str) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].name(name) + return func + + return inner + + +def summary(text: str) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].describe(summary=text) + return func + + return inner + + +def description(text: str) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].describe(description=text) + return func + + return inner + + +def document( + url: Union[str, definitions.ExternalDocumentation], + description: Optional[str] = None, +) -> Callable[[T], T]: + if isinstance(url, definitions.ExternalDocumentation): + description = url.fields["description"] + url = url.fields["url"] + + def inner(func): + OperationStore()[func].document(url, description) + return func + + return inner + + +def tag(*args: Union[str, definitions.Tag]) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].tag(*args) + return func + + return inner + + +def deprecated(maybe_func=None) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].deprecate() + return func + + return inner(maybe_func) if maybe_func else inner + + +def no_autodoc(maybe_func=None) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].disable_autodoc() + return func + + return inner(maybe_func) if maybe_func else inner + + +def body( + content: Any, + *, + validate: bool = False, + body_argument: str = "body", + **kwargs, +) -> Callable[[T], T]: + body_content = _content_or_component(content) + params = {**kwargs} + validation_schema = None + if isinstance(body_content, definitions.RequestBody): + params = {**body_content.fields, **params} + body_content = params.pop("content") + + if validate: + if callable(validate): + model = validate + else: + model = body_content + validation_schema = generate_schema(body_content) + + def inner(func): + @wraps(func) + async def handler(*handler_args, **handler_kwargs): + request = extract_request(*handler_args) + + if validate: + try: + data = request.json + allow_multiple = False + allow_coerce = False + except InvalidUsage: + data = request.form + allow_multiple = True + allow_coerce = True + + await do_validation( + model=model, + data=data, + schema=validation_schema, + request=request, + kwargs=handler_kwargs, + body_argument=body_argument, + allow_multiple=allow_multiple, + allow_coerce=allow_coerce, + ) + + retval = func(*handler_args, **handler_kwargs) + if isawaitable(retval): + retval = await retval + return retval + + if func in OperationStore(): + OperationStore()[handler] = OperationStore().pop(func) + OperationStore()[handler].body(body_content, **params) + return handler + + return inner + + +@overload +def parameter( + *, + parameter: definitions.Parameter, + **kwargs, +) -> Callable[[T], T]: ... + + +@overload +def parameter( + name: None, + schema: None, + location: None, + parameter: definitions.Parameter, + **kwargs, +) -> Callable[[T], T]: ... + + +@overload +def parameter( + name: str, + schema: Optional[Union[type, Schema]] = None, + location: Optional[str] = None, + parameter: None = None, + **kwargs, +) -> Callable[[T], T]: ... + + +def parameter( + name: Optional[str] = None, + schema: Optional[Union[type, Schema]] = None, + location: Optional[str] = None, + parameter: Optional[definitions.Parameter] = None, + **kwargs, +) -> Callable[[T], T]: + if parameter: + if name or schema or location: + raise SanicException( + "When using a parameter object, you cannot pass " + "other arguments." + ) + if not schema: + schema = str + if not location: + location = "query" + + def inner(func: Callable): + if parameter: + # Temporary solution convert in to location, + # need to be changed later. + fields = dict(parameter.fields) + if "in" in fields: + fields["location"] = fields.pop("in") + OperationStore()[func].parameter(**fields) + else: + OperationStore()[func].parameter(name, schema, location, **kwargs) + return func + + return inner + + +def response( + status: Union[Literal["default"], int] = "default", + content: Any = str, + description: Optional[str] = None, + *, + response: Optional[definitions.Response] = None, + **kwargs, +) -> Callable[[T], T]: + if response: + if ( + status != "default" + or content is not str + or description is not None + ): + raise SanicException( + "When using a response object, you cannot pass " + "other arguments." + ) + + status = response.fields["status"] + content = response.fields["content"] + description = response.fields["description"] + + def inner(func): + OperationStore()[func].response(status, content, description, **kwargs) + return func + + return inner + + +def secured(*args, **kwargs) -> Callable[[T], T]: + def inner(func): + OperationStore()[func].secured(*args, **kwargs) + return func + + return inner + + +Model = TypeVar("Model") + + +def component( + model: Optional[Model] = None, + *, + name: Optional[str] = None, + field: str = "schemas", +) -> Callable[[T], T]: + def wrap(m): + return component(m, name=name, field=field) + + if not model: + return wrap + + params = {} + if name: + params["name"] = name + if field: + params["field"] = field + definitions.Component(model, **params) + return model + + +def definition( + *, + exclude: Optional[bool] = None, + operation: Optional[str] = None, + summary: Optional[str] = None, + description: Optional[str] = None, + document: Optional[Union[str, definitions.ExternalDocumentation]] = None, + tag: Optional[ + Union[ + Union[str, definitions.Tag], Sequence[Union[str, definitions.Tag]] + ] + ] = None, + deprecated: bool = False, + body: Optional[Union[dict[str, Any], definitions.RequestBody, Any]] = None, + parameter: Optional[ + Union[ + Union[dict[str, Any], definitions.Parameter, str], + list[Union[dict[str, Any], definitions.Parameter, str]], + ] + ] = None, + response: Optional[ + Union[ + Union[dict[str, Any], definitions.Response, Any], + list[Union[dict[str, Any], definitions.Response]], + ] + ] = None, + secured: Optional[dict[str, Any]] = None, + validate: bool = False, + body_argument: str = "body", +) -> Callable[[T], T]: + validation_schema = None + body_content = None + + def inner(func): + nonlocal validation_schema + nonlocal body_content + + glbl = globals() + + if body: + kwargs = {} + content = body + if isinstance(content, definitions.RequestBody): + kwargs = content.fields + elif isinstance(content, dict): + if "content" in content: + kwargs = content + else: + kwargs["content"] = content + else: + content = _content_or_component(content) + kwargs["content"] = content + + if validate: + kwargs["validate"] = validate + kwargs["body_argument"] = body_argument + + func = glbl["body"](**kwargs)(func) + + if exclude is not None: + func = glbl["exclude"](exclude)(func) + + if operation: + func = glbl["operation"](operation)(func) + + if summary: + func = glbl["summary"](summary)(func) + + if description: + func = glbl["description"](description)(func) + + if document: + kwargs = {} + if isinstance(document, str): + kwargs["url"] = document + else: + kwargs["url"] = document.fields["url"] + kwargs["description"] = document.fields["description"] + + func = glbl["document"](**kwargs)(func) + + if tag: + taglist = [] + op = ( + "extend" + if isinstance(tag, (list, tuple, set, frozenset)) + else "append" + ) + + getattr(taglist, op)(tag) + func = glbl["tag"](*taglist)(func) + + if deprecated: + func = glbl["deprecated"]()(func) + + if parameter: + paramlist = [] + op = ( + "extend" + if isinstance(parameter, (list, tuple, set, frozenset)) + else "append" + ) + getattr(paramlist, op)(parameter) + + for param in paramlist: + kwargs = {} + if isinstance(param, definitions.Parameter): + kwargs = param.fields + if "in" in kwargs: + kwargs["location"] = kwargs.pop("in") + elif isinstance(param, dict) and "name" in param: + kwargs = param + elif isinstance(param, str): + kwargs["name"] = param + else: + raise SanicException( + "parameter must be a Parameter instance, a string, or " + "a dictionary containing at least 'name'." + ) + + if "schema" not in kwargs: + kwargs["schema"] = str + + func = glbl["parameter"](**kwargs)(func) + + if response: + resplist = [] + op = ( + "extend" + if isinstance(response, (list, tuple, set, frozenset)) + else "append" + ) + getattr(resplist, op)(response) + + if len(resplist) > 1 and any( + not isinstance(item, definitions.Response) + and not isinstance(item, dict) + for item in resplist + ): + raise SanicException( + "Cannot use multiple bare custom models to define " + "multiple responses like openapi.definition(response=[" + "MyModel1, MyModel2]). Instead, you should wrap them in a " + "dict or a Response object. See " + "https://sanic.dev/en/plugins/sanic-ext/openapi/decorators" + ".html#response for more details." + ) + + for resp in resplist: + kwargs = {} + if isinstance(resp, definitions.Response): + kwargs = resp.fields + elif isinstance(resp, dict): + if "content" in resp: + kwargs = resp + else: + kwargs["content"] = resp + else: + kwargs["content"] = resp + + if "status" not in kwargs: + kwargs["status"] = "default" + + func = glbl["response"](**kwargs)(func) + + if secured: + func = glbl["secured"](secured)(func) + + return func + + return inner diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/types.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/types.py new file mode 100644 index 0000000..d4e4a44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/types.py @@ -0,0 +1,452 @@ +import json +import uuid + +from dataclasses import MISSING, is_dataclass +from datetime import date, datetime, time +from enum import Enum +from inspect import getmembers, isclass, isfunction, ismethod +from typing import ( + Any, + Optional, + Union, + get_args, + get_origin, + get_type_hints, +) + +from sanic_routing.patterns import alpha, ext, nonemptystr, parse_date, slug + +from sanic_ext.utils.typing import ( + UnionType, + is_attrs, + is_generic, + is_msgspec, + is_pydantic, +) + + +try: + import attrs + + NOTHING: Any = attrs.NOTHING +except ImportError: + NOTHING = object() + +try: + import msgspec + + from msgspec.inspect import Metadata as MsgspecMetadata + from msgspec.inspect import type_info as msgspec_type_info + + MsgspecMetadata: Any = MsgspecMetadata + NODEFAULT: Any = msgspec.NODEFAULT + UNSET: Any = msgspec.UNSET + + class MsgspecAdapter(msgspec.Struct): + name: str + default: Any + metadata: dict + +except ImportError: + + def msgspec_type_info(struct): + pass + + class MsgspecAdapter: + pass + + MsgspecMetadata = object() + NODEFAULT = object() + UNSET = object() + + +class Definition: + __nullable__: Optional[list[str]] = [] + __ignore__: Optional[list[str]] = [] + + def __init__(self, **kwargs): + self._fields: dict[str, Any] = self.guard(kwargs) + + @property + def fields(self): + return self._fields + + def guard(self, fields): + return { + k: v + for k, v in fields.items() + if k in _properties(self).keys() or k.startswith("x-") + } + + def serialize(self): + return { + k: self._value(v) + for k, v in _serialize(self.fields).items() + if ( + k not in self.__ignore__ + and ( + v is not None + or ( + isinstance(self.__nullable__, list) + and (not self.__nullable__ or k in self.__nullable__) + ) + ) + ) + } + + def __str__(self): + return json.dumps(self.serialize()) + + @staticmethod + def _value(value): + if isinstance(value, Enum): + return value.value + return value + + +class Schema(Definition): + title: str + description: str + type: str + format: str + nullable: bool + required: bool + default: None + example: None + oneOf: list[Definition] + anyOf: list[Definition] + allOf: list[Definition] + + additionalProperties: dict[str, str] + multipleOf: int + maximum: int + exclusiveMaximum: bool + minimum: int + exclusiveMinimum: bool + maxLength: int + minLength: int + pattern: str + enum: Union[list[Any], Enum] + + @staticmethod + def make(value, **kwargs): + _type = type(value) + origin = get_origin(value) + args = get_args(value) + if origin in (Union, UnionType): + if type(None) in args: + kwargs["nullable"] = True + + filtered = [arg for arg in args if arg is not type(None)] # noqa + + if len(filtered) == 1: + return Schema.make(filtered[0], **kwargs) + return Schema( + oneOf=[Schema.make(arg) for arg in filtered], **kwargs + ) + + for field in ("type", "format"): + kwargs.pop(field, None) + + if isinstance(value, Schema): + return value + if value is bool: + return Boolean(**kwargs) + elif value is int: + return Integer(**kwargs) + elif value is float: + return Float(**kwargs) + elif value is str or value in (nonemptystr, ext, slug, alpha): + return String(**kwargs) + elif value is bytes: + return Byte(**kwargs) + elif value is bytearray: + return Binary(**kwargs) + elif value is date: + return Date(**kwargs) + elif value is time: + return Time(**kwargs) + elif value is datetime or value is parse_date: + return DateTime(**kwargs) + elif value is uuid.UUID: + return UUID(**kwargs) + elif value is Any: + return AnyValue(**kwargs) + + if _type is bool: + return Boolean(default=value, **kwargs) + elif _type is int: + return Integer(default=value, **kwargs) + elif _type is float: + return Float(default=value, **kwargs) + elif _type is str: + return String(default=value, **kwargs) + elif _type is bytes: + return Byte(default=value, **kwargs) + elif _type is bytearray: + return Binary(default=value, **kwargs) + elif _type is date: + return Date(**kwargs) + elif _type is time: + return Time(**kwargs) + elif _type is datetime: + return DateTime(**kwargs) + elif _type is uuid.UUID: + return UUID(**kwargs) + elif _type is list: + if len(value) == 0: + schema = Schema(nullable=True) + elif len(value) == 1: + schema = Schema.make(value[0]) + else: + schema = Schema(oneOf=[Schema.make(x) for x in value]) + + return Array(schema, **kwargs) + elif _type is dict: + return Object.make(value, **kwargs) + elif ( + (is_generic(value) or is_generic(_type)) + and origin is dict + and len(args) == 2 + ): + kwargs["additionalProperties"] = Schema.make(args[1]) + return Object(**kwargs) + elif (is_generic(value) or is_generic(_type)) and origin is list: + kwargs.pop("items", None) + return Array(Schema.make(args[0]), **kwargs) + elif _type is type(Enum): + available = [item.value for item in value.__members__.values()] + available_types = list({type(item) for item in available}) + schema_type = ( + available_types[0] if len(available_types) == 1 else "string" + ) + return Schema.make( + schema_type, + enum=[item.value for item in value.__members__.values()], + ) + else: + return Object.make(value, **kwargs) + + +class Boolean(Schema): + def __init__(self, **kwargs): + super().__init__(type="boolean", **kwargs) + + +class Integer(Schema): + def __init__(self, **kwargs): + super().__init__(type="integer", format="int32", **kwargs) + + +class Long(Schema): + def __init__(self, **kwargs): + super().__init__(type="integer", format="int64", **kwargs) + + +class Float(Schema): + def __init__(self, **kwargs): + super().__init__(type="number", format="float", **kwargs) + + +class Double(Schema): + def __init__(self, **kwargs): + super().__init__(type="number", format="double", **kwargs) + + +class String(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", **kwargs) + + +class Byte(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="byte", **kwargs) + + +class Binary(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="binary", **kwargs) + + +class Date(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="date", **kwargs) + + +class Time(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="time", **kwargs) + + +class DateTime(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="date-time", **kwargs) + + +class Password(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="password", **kwargs) + + +class Email(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="email", **kwargs) + + +class UUID(Schema): + def __init__(self, **kwargs): + super().__init__(type="string", format="uuid", **kwargs) + + +class AnyValue(Schema): + @classmethod + def make(cls, value: Any, **kwargs): + return cls( + AnyValue={}, + **kwargs, + ) + + +class Object(Schema): + properties: dict[str, Schema] + maxProperties: int + minProperties: int + + def __init__( + self, properties: Optional[dict[str, Schema]] = None, **kwargs + ): + if properties: + kwargs["properties"] = properties + super().__init__(type="object", **kwargs) + + @classmethod + def make(cls, value: Any, **kwargs): + extra: dict[str, Any] = {} + + # Extract from field metadata if msgspec, pydantic, attrs, or dataclass + if isclass(value): + fields = () + if is_pydantic(value): + try: + value = value.__pydantic_model__ + except AttributeError: + ... + extra = value.schema()["properties"] + elif is_attrs(value): + fields = value.__attrs_attrs__ + elif is_dataclass(value): + fields = value.__dataclass_fields__.values() + elif is_msgspec(value): + # adapt to msgspec metadata layout -- annotated type -- + # to match dataclass "metadata" attribute + fields = [ + MsgspecAdapter( + name=f.name, + default=( + MISSING + if f.default in (UNSET, NODEFAULT) + else f.default + ), + metadata=getattr(f.type, "extra", {}), + ) + for f in msgspec_type_info(value).fields + ] + + if fields: + extra = { + field.name: { + "title": field.name.title(), + **( + {"default": field.default} + if field.default not in (MISSING, NOTHING) + else {} + ), + **dict(field.metadata).get("openapi", {}), + } + for field in fields + } + + return cls( + { + k: Schema.make(v, **extra.get(k, {})) + for k, v in _properties(value).items() + }, + **kwargs, + ) + + +class Array(Schema): + items: Any + maxItems: int + minItems: int + uniqueItems: bool + + def __init__(self, items: Any, **kwargs): + super().__init__(type="array", items=Schema.make(items), **kwargs) + + +def _serialize(value) -> Any: + if isinstance(value, Definition): + return value.serialize() + + if isinstance(value, type) and issubclass(value, Enum): + return [item.value for item in value.__members__.values()] + + if isinstance(value, dict): + return {k: _serialize(v) for k, v in value.items()} + + if isinstance(value, list): + return [_serialize(v) for v in value] + + return value + + +def _properties(value: object) -> dict: + try: + fields = { + x: val + for x, v in getmembers(value, _is_property) + if (val := _extract(v)) and x in value.__dict__ + } + except AttributeError: + fields = {} + + cls = value if callable(value) else value.__class__ + extra = value if isinstance(value, dict) else {} + try: + annotations = get_type_hints(cls) + except (NameError, TypeError): + if hasattr(value, "__annotations__"): + annotations = value.__annotations__ + else: + annotations = {} + annotations.pop("return", None) + try: + output = { + k: v + for k, v in {**fields, **annotations, **extra}.items() + if not k.startswith("_") + and not ( + isclass(v) + and isclass(cls) + and v.__qualname__.endswith( + f"{getattr(cls, '__name__', '')}." + f"{getattr(v, '__name__', '')}" + ) + ) + } + except TypeError: + return {} + + return output + + +def _extract(item): + if isinstance(item, property): + hints = get_type_hints(item.fget) + return hints.get("return") + return item + + +def _is_property(item): + return not isfunction(item) and not ismethod(item) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/redoc.html b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/redoc.html new file mode 100644 index 0000000..adb9743 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/redoc.html @@ -0,0 +1,20 @@ + + + + + + + __HTML_TITLE__ + + + + + + + diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/swagger.html b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/swagger.html new file mode 100644 index 0000000..b3c6184 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/openapi/ui/swagger.html @@ -0,0 +1,39 @@ + + + + + + + __HTML_TITLE__ + + + +
+ + + + + diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/engine.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/engine.py new file mode 100644 index 0000000..fc1d1e5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/engine.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from functools import wraps +from inspect import isawaitable +from typing import TYPE_CHECKING, Optional, Union + +from jinja2 import Environment +from sanic.compat import Header +from sanic.request import Request +from sanic.response import HTTPResponse + +from sanic_ext.extensions.templating.render import ( + LazyResponse, + TemplateResponse, +) + + +if TYPE_CHECKING: + from sanic_ext import Config + + +class Templating: + def __init__(self, environment: Environment, config: Config) -> None: + self.environment = environment + self.config = config + + def template( + self, + file_name: str, + status: int = 200, + headers: Optional[Union[Header, dict[str, str]]] = None, + content_type: str = "text/html; charset=utf-8", + **kwargs, + ): + template = self.environment.get_template(file_name) + render = ( + template.render_async + if self.config.TEMPLATING_ENABLE_ASYNC + else template.render + ) + + def decorator(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + context = f(*args, **kwargs) + if isawaitable(context): + context = await context + if isinstance(context, HTTPResponse) and not isinstance( + context, TemplateResponse + ): + return context + + # TODO + # - Allow each of these to be a callable that is executed here + params = { + "status": status, + "content_type": content_type, + "headers": headers, + } + + if isinstance(context, LazyResponse): + for attr in ("status", "headers", "content_type"): + value = getattr(context, attr, None) + if value: + params[attr] = value + context = context.context + + context["request"] = Request.get_current() + + content = render(**context) + if isawaitable(content): + content = await content + + return HTTPResponse(content, **params) + + return decorated_function + + return decorator diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/extension.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/extension.py new file mode 100644 index 0000000..72aa911 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/extension.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import os + +from collections import abc +from collections.abc import Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Union + +from jinja2 import ( + Environment, + FileSystemLoader, + __version__, + select_autoescape, +) + +from sanic_ext.extensions.templating.engine import Templating + +from ..base import Extension + + +if TYPE_CHECKING: + from sanic_ext import Extend + + +class TemplatingExtension(Extension): + name = "templating" + + def startup(self, bootstrap: Extend) -> None: + self._add_template_paths_to_reloader( + self.config.TEMPLATING_PATH_TO_TEMPLATES + ) + loader = FileSystemLoader(self.config.TEMPLATING_PATH_TO_TEMPLATES) + + if not hasattr(bootstrap, "environment"): + bootstrap.environment = Environment( + loader=loader, + autoescape=select_autoescape(), + enable_async=self.config.TEMPLATING_ENABLE_ASYNC, + ) + if not hasattr(bootstrap, "templating"): + bootstrap.templating = Templating( + environment=bootstrap.environment, config=self.config + ) + bootstrap.templating.environment.globals["url_for"] = self.app.url_for + + def label(self): + return f"jinja2=={__version__}" + + def _add_template_paths_to_reloader( + self, path: Union[str, os.PathLike, Sequence[Union[str, os.PathLike]]] + ) -> None: + if not isinstance(path, abc.Iterable) or isinstance(path, str): + path = [path] + + for item in path: + self.app.state.reload_dirs.add(Path(item)) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/render.py b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/render.py new file mode 100644 index 0000000..76b17cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extensions/templating/render.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +from inspect import isawaitable +from typing import TYPE_CHECKING, Any, Optional, Union + +from sanic import Sanic +from sanic.compat import Header +from sanic.exceptions import SanicException +from sanic.request import Request +from sanic.response import HTTPResponse + +from sanic_ext.exceptions import ExtensionNotFound + + +if TYPE_CHECKING: + from jinja2 import Environment + + +class TemplateResponse(HTTPResponse): ... + + +class LazyResponse(TemplateResponse): + __slots__ = ( + "body", + "status", + "content_type", + "headers", + "_cookies", + "context", + ) + + def __init__( + self, + context: dict[str, Any], + status: int = 0, + headers: Optional[Union[Header, dict[str, str]]] = None, + content_type: Optional[str] = None, + ): + super().__init__( + content_type=content_type, status=status, headers=headers + ) + self.context = context + + +async def render( + template_name: str = "", + status: int = 200, + headers: Optional[dict[str, str]] = None, + content_type: str = "text/html; charset=utf-8", + app: Optional[Sanic] = None, + environment: Optional[Environment] = None, + context: Optional[dict[str, Any]] = None, + *, + template_source: str = "", +) -> TemplateResponse: + if app is None: + try: + app = Sanic.get_app() + except SanicException as e: + raise SanicException( + "Cannot render template beause locating the Sanic application " + "was ambiguous. Please return render(..., app=some_app)." + ) from e + + if template_name and template_source: + raise SanicException( + "You must provide template_name OR template_source, not both." + ) + + if environment is None: + try: + environment = app.ext.environment + except AttributeError: + raise ExtensionNotFound( + "The Templating extension does not appear to be enabled. " + "Perhaps jinja2 is not installed." + ) + + kwargs = context if context else {} + + kwargs["request"] = Request.get_current() + + if template_name or template_source: + template = ( + environment.get_template(template_name) + if template_name + else environment.from_string(template_source) + ) + + render = ( + template.render_async + if app.config.TEMPLATING_ENABLE_ASYNC + else template.render + ) + content = render(**kwargs) + if isawaitable(content): + content = await content # type: ignore + + return TemplateResponse( # type: ignore + content, status=status, headers=headers, content_type=content_type + ) + else: + return LazyResponse( + kwargs, status=status, headers=headers, content_type=content_type + ) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/request.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/request.py new file mode 100644 index 0000000..f60daf2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/request.py @@ -0,0 +1,46 @@ +from itertools import count + +from sanic import Request, Sanic +from sanic.compat import Header +from sanic.models.protocol_types import TransportProtocol + + +class CountedRequest(Request): + __slots__ = () + + _counter = count() + count = next(_counter) + + def __init__( + self, + url_bytes: bytes, + headers: Header, + version: str, + method: str, + transport: TransportProtocol, + app: Sanic, + head: bytes = b"", + stream_id: int = 0, + ): + super().__init__( + url_bytes, + headers, + version, + method, + transport, + app, + head, + stream_id, + ) + self.__class__._increment() + if hasattr(self.app, "multiplexer"): + self.app.multiplexer.state["request_count"] = self.__class__.count + + @classmethod + def _increment(cls): + cls.count = next(cls._counter) + + @classmethod + def reset_count(cls): + cls._counter = count() + cls.count = next(cls._counter) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/decorator.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/decorator.py new file mode 100644 index 0000000..064c119 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/serializer/decorator.py @@ -0,0 +1,42 @@ +from functools import wraps +from inspect import isawaitable, signature +from typing import Callable, TypeVar + +from sanic import response + + +T = TypeVar("T") + + +def serializer(func, *, status: int = 200) -> Callable[[T], T]: + sig = signature(func) + simple = len(sig.parameters) == 2 or ( + func + in ( + response.HTTPResponse, + response.file_stream, + response.file, + response.html, + response.json, + response.raw, + response.redirect, + response.text, + ) + ) + + def decorator(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + retval = f(*args, **kwargs) + if isawaitable(retval): + retval = await retval + + if simple: + return func(retval, status=status) + else: + kwargs["status"] = status + return func(retval, *args, **kwargs) + + return decorated_function + + return decorator diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/check.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/check.py new file mode 100644 index 0000000..4389b8b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/check.py @@ -0,0 +1,283 @@ +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import _HAS_DEFAULT_FACTORY # type: ignore +from typing import ( + Any, + Literal, + NamedTuple, + Optional, + Union, + get_args, + get_origin, +) + +from sanic_ext.utils.typing import ( + UnionType, + is_generic, + is_msgspec, + is_optional, +) + + +MISSING: tuple[Any, ...] = (_HAS_DEFAULT_FACTORY,) + +try: + import attrs # noqa + + NOTHING = attrs.NOTHING + ATTRS = True + MISSING = ( + _HAS_DEFAULT_FACTORY, + NOTHING, + ) +except ImportError: + ATTRS = False + + +try: + import msgspec + + MSGSPEC = True +except ImportError: + MSGSPEC = False + + +class Hint(NamedTuple): + hint: Any + model: bool + literal: bool + typed: bool + nullable: bool + origin: Optional[Any] + allowed: tuple[Hint, ...] # type: ignore + allow_missing: bool + + def validate( + self, value, schema, allow_multiple=False, allow_coerce=False + ): + if not self.typed: + if self.model: + return check_data( + self.hint, + value, + schema, + allow_multiple=allow_multiple, + allow_coerce=allow_coerce, + ) + + if ( + allow_multiple + and isinstance(value, list) + and self.coerce_type is not list + and len(value) == 1 + ): + value = value[0] + try: + _check_types(value, self.literal, self.hint) + except ValueError as e: + if allow_coerce: + value = self.coerce(value) + _check_types(value, self.literal, self.hint) + else: + raise e + else: + value = _check_nullability( + value, + self.nullable, + self.allowed, + schema, + allow_multiple, + allow_coerce, + ) + + if not self.nullable: + if self.origin in (Union, Literal, UnionType): + value = _check_inclusion( + value, + self.allowed, + schema, + allow_multiple, + allow_coerce, + ) + elif self.origin is list: + value = _check_list( + value, + self.allowed, + self.hint, + schema, + allow_multiple, + allow_coerce, + ) + elif self.origin is dict: + value = _check_dict( + value, + self.allowed, + self.hint, + schema, + allow_multiple, + allow_coerce, + ) + + if allow_coerce: + value = self.coerce(value) + + return value + + def coerce(self, value): + if is_generic(self.coerce_type): + args = get_args(self.coerce_type) + if get_origin(self.coerce_type) == Literal or ( + all(get_origin(arg) == Literal for arg in args) + ): + return value + if type(None) in args and value is None: + return None + coerce_types = [arg for arg in args if not isinstance(None, arg)] + else: + coerce_types = [self.coerce_type] + for coerce_type in coerce_types: + try: + if isinstance(value, list): + value = [coerce_type(item) for item in value] + elif value is None and self.nullable: + value = None + else: + value = coerce_type(value) + except (ValueError, TypeError): + ... + else: + return value + return value + + @property + def coerce_type(self): + coerce_type = self.hint + if is_optional(coerce_type): + coerce_type = get_args(self.hint)[0] + return coerce_type + + +def check_data(model, data, schema, allow_multiple=False, allow_coerce=False): + if not isinstance(data, dict): + raise TypeError(f"Value '{data}' is not a dict") + sig = schema[model.__name__]["sig"] + hints = schema[model.__name__]["hints"] + bound = sig.bind(**data) + bound.apply_defaults() + params = dict(zip(sig.parameters, bound.args)) + params.update(bound.kwargs) + + hydration_values = {} + try: + for key, value in params.items(): + hint = hints.get(key, Any) + try: + hydration_values[key] = hint.validate( + value, + schema, + allow_multiple=allow_multiple, + allow_coerce=allow_coerce, + ) + except ValueError: + if not hint.allow_missing or value not in MISSING: + raise + except ValueError as e: + raise TypeError(e) + + if MSGSPEC and is_msgspec(model): + try: + return msgspec.convert(hydration_values, model, str_keys=True) + except AttributeError: + return msgspec.from_builtins( + hydration_values, model, str_values=True, str_keys=True + ) + except msgspec.ValidationError as e: + raise TypeError(e) + else: + return model(**hydration_values) + + +def _check_types(value, literal, expected): + if literal: + if expected is Any: + return + elif value != expected: + raise ValueError(f"Value '{value}' must be {expected}") + else: + if MSGSPEC and is_msgspec(expected) and isinstance(value, Mapping): + try: + expected(**value) + except (TypeError, msgspec.ValidationError): + raise ValueError(f"Value '{value}' is not of type {expected}") + elif not isinstance(value, expected): + raise ValueError(f"Value '{value}' is not of type {expected}") + + +def _check_nullability( + value, nullable, allowed, schema, allow_multiple, allow_coerce +): + if not nullable and value is None: + raise ValueError("Value cannot be None") + if nullable and value is not None: + exc = None + for hint in allowed: + try: + value = hint.validate( + value, schema, allow_multiple, allow_coerce + ) + except ValueError as e: + exc = e + else: + break + else: + if exc: + if len(allowed) == 1: + raise exc + else: + options = ", ".join( + [str(option.hint) for option in allowed] + ) + raise ValueError( + f"Value '{value}' must be one of {options}, or None" + ) + return value + + +def _check_inclusion(value, allowed, schema, allow_multiple, allow_coerce): + for option in allowed: + try: + return option.validate(value, schema, allow_multiple, allow_coerce) + except (ValueError, TypeError): + ... + + options = ", ".join([str(option.hint) for option in allowed]) + raise ValueError(f"Value '{value}' must be one of {options}") + + +def _check_list(value, allowed, hint, schema, allow_multiple, allow_coerce): + if isinstance(value, list): + try: + return [ + _check_inclusion( + item, allowed, schema, allow_multiple, allow_coerce + ) + for item in value + ] + except (ValueError, TypeError): + ... + raise ValueError(f"Value '{value}' must be a {hint}") + + +def _check_dict(value, allowed, hint, schema, allow_multiple, allow_coerce): + if isinstance(value, dict): + try: + return { + key: _check_inclusion( + item, allowed, schema, allow_multiple, allow_coerce + ) + for key, item in value.items() + } + except (ValueError, TypeError): + ... + raise ValueError(f"Value '{value}' must be a {hint}") diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/clean.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/clean.py new file mode 100644 index 0000000..25a82f1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/clean.py @@ -0,0 +1,17 @@ +from typing import Any, get_origin, get_type_hints + + +def clean_data(model: type[object], data: dict[str, Any]) -> dict[str, Any]: + hints = get_type_hints(model) + return {key: _coerce(hints[key], value) for key, value in data.items()} + + +def _coerce(param_type, value: Any) -> Any: + if ( + get_origin(param_type) is not list + and isinstance(value, list) + and len(value) == 1 + ): + value = value[0] + + return value diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/decorator.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/decorator.py new file mode 100644 index 0000000..608152d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/decorator.py @@ -0,0 +1,80 @@ +from functools import wraps +from inspect import isawaitable +from typing import Callable, Optional, TypeVar, Union + +from sanic import Request + +from sanic_ext.exceptions import InitError +from sanic_ext.utils.extraction import extract_request + +from .setup import do_validation, generate_schema + + +T = TypeVar("T") + + +def validate( + json: Optional[Union[Callable[[Request], bool], type[object]]] = None, + form: Optional[Union[Callable[[Request], bool], type[object]]] = None, + query: Optional[Union[Callable[[Request], bool], type[object]]] = None, + body_argument: str = "body", + query_argument: str = "query", +) -> Callable[[T], T]: + schemas = { + key: generate_schema(param) + for key, param in ( + ("json", json), + ("form", form), + ("query", query), + ) + } + + if json and form: + raise InitError("Cannot define both a form and json route validator") + + def decorator(f): + @wraps(f) + async def decorated_function(*args, **kwargs): + request = extract_request(*args) + + if schemas["json"]: + await do_validation( + model=json, + data=request.json, + schema=schemas["json"], + request=request, + kwargs=kwargs, + body_argument=body_argument, + allow_multiple=False, + allow_coerce=False, + ) + elif schemas["form"]: + await do_validation( + model=form, + data=request.form, + schema=schemas["form"], + request=request, + kwargs=kwargs, + body_argument=body_argument, + allow_multiple=True, + allow_coerce=True, + ) + if schemas["query"]: + await do_validation( + model=query, + data=request.args, + schema=schemas["query"], + request=request, + kwargs=kwargs, + body_argument=query_argument, + allow_multiple=True, + allow_coerce=True, + ) + retval = f(*args, **kwargs) + if isawaitable(retval): + retval = await retval + return retval + + return decorated_function + + return decorator diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/schema.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/schema.py new file mode 100644 index 0000000..08f5551 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/schema.py @@ -0,0 +1,133 @@ +import types + +from dataclasses import MISSING, Field, is_dataclass +from inspect import isclass, signature +from typing import ( + Any, + Literal, + Optional, + Union, + get_args, + get_origin, + get_type_hints, +) + +from sanic_ext.utils.typing import is_attrs, is_generic, is_msgspec + +from .check import Hint + + +try: + UnionType = types.UnionType # type: ignore +except AttributeError: + UnionType = type("UnionType", (), {}) + +try: + from attr import NOTHING, Attribute +except ModuleNotFoundError: + NOTHING = object() # type: ignore + Attribute = type("Attribute", (), {}) # type: ignore + +try: + from msgspec.inspect import type_info as msgspec_type_info +except ModuleNotFoundError: + + def msgspec_type_info(val): + pass + + +def make_schema(agg, item): + if type(item) in (bool, str, int, float): + return agg + + if is_generic(item) and (args := get_args(item)): + for arg in args: + make_schema(agg, arg) + elif item.__name__ not in agg and ( + is_dataclass(item) or is_attrs(item) or is_msgspec(item) + ): + if is_dataclass(item): + fields = item.__dataclass_fields__ + elif is_msgspec(item): + fields = {f.name: f.type for f in msgspec_type_info(item).fields} + else: + fields = {attr.name: attr for attr in item.__attrs_attrs__} + + sig = signature(item) + hints = parse_hints(get_type_hints(item), fields) + + agg[item.__name__] = { + "sig": sig, + "hints": hints, + } + + for hint in hints.values(): + make_schema(agg, hint.hint) + + return agg + + +def parse_hints( + hints, fields: dict[str, Union[Field, Attribute]] +) -> dict[str, Hint]: + output: dict[str, Hint] = { + name: parse_hint(hint, fields.get(name)) + for name, hint in hints.items() + } + return output + + +def parse_hint(hint, field: Optional[Union[Field, Attribute]] = None): + origin = None + literal = not isclass(hint) + nullable = False + typed = False + model = False + allowed: tuple[Any, ...] = tuple() + allow_missing = False + + if field and ( + ( + isinstance(field, Field) and field.default_factory is not MISSING # type: ignore + ) + or (isinstance(field, Attribute) and field.default is not NOTHING) + ): + allow_missing = True + + if is_dataclass(hint) or is_attrs(hint): + model = True + elif is_generic(hint): + typed = True + literal = False + origin = get_origin(hint) + args = get_args(hint) + nullable = origin in (Union, UnionType) and type(None) in args + + if nullable: + allowed = tuple( + [ + arg + for arg in args + if is_generic(arg) or not isinstance(None, arg) + ] + ) + elif origin is dict: + allowed = (args[1],) + elif ( + origin is list + or origin is Literal + or origin is Union + or origin is UnionType + ): + allowed = args + + return Hint( + hint, + model, + literal, + typed, + nullable, + origin, + tuple([parse_hint(item, None) for item in allowed]), + allow_missing, + ) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/setup.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/setup.py new file mode 100644 index 0000000..b869273 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/setup.py @@ -0,0 +1,69 @@ +from functools import partial +from inspect import isawaitable, isclass + +from sanic.log import logger + +from sanic_ext.exceptions import ValidationError +from sanic_ext.utils.typing import is_msgspec, is_pydantic + +from .schema import make_schema +from .validators import ( + _msgspec_validate_instance, + _validate_annotations, + _validate_instance, + validate_body, +) + + +async def do_validation( + *, + model, + data, + schema, + request, + kwargs, + body_argument, + allow_multiple, + allow_coerce, +): + try: + logger.debug(f"Validating {request.path} using {model}") + if model is not None: + if isclass(model): + validator = _get_validator( + model, schema, allow_multiple, allow_coerce + ) + validation = validate_body(validator, model, data) + kwargs[body_argument] = validation + else: + validation = model( + request=request, data=data, handler_kwargs=kwargs + ) + if isawaitable(validation): + await validation + except TypeError as e: + raise ValidationError(e) + + +def generate_schema(param): + try: + if param is None or is_msgspec(param) or is_pydantic(param): + return param + except TypeError: + ... + + return make_schema({}, param) if isclass(param) else param + + +def _get_validator(model, schema, allow_multiple, allow_coerce): + if is_msgspec(model): + return partial(_msgspec_validate_instance, allow_coerce=allow_coerce) + elif is_pydantic(model): + return partial(_validate_instance, allow_coerce=allow_coerce) + + return partial( + _validate_annotations, + schema=schema, + allow_multiple=allow_multiple, + allow_coerce=allow_coerce, + ) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/validators.py b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/validators.py new file mode 100644 index 0000000..3421961 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/extras/validation/validators.py @@ -0,0 +1,52 @@ +from typing import Any, Callable + +from sanic_ext.exceptions import ValidationError + +from .check import check_data +from .clean import clean_data + + +try: + from pydantic import ValidationError as PydanticValidationError + + VALIDATION_ERROR: tuple[type[Exception], ...] = ( + TypeError, + PydanticValidationError, + ) +except ImportError: + VALIDATION_ERROR = (TypeError,) + + +def validate_body( + validator: Callable[[type[Any], dict[str, Any]], Any], + model: type[Any], + body: dict[str, Any], +) -> Any: + try: + return validator(model, body) + except VALIDATION_ERROR as e: + raise ValidationError( + f"Invalid request body: {model.__name__}. Error: {e}", + extra={"exception": str(e)}, + ) from None + + +def _msgspec_validate_instance(model, body, allow_coerce): + import msgspec + + try: + data = clean_data(model, body) if allow_coerce else body + return msgspec.convert(data, model) + except msgspec.ValidationError as e: + # Convert msgspec.ValidationError into TypeError for consistent + # behaviour with _validate_instance + raise TypeError(str(e)) + + +def _validate_instance(model, body, allow_coerce): + data = clean_data(model, body) if allow_coerce else body + return model(**data) + + +def _validate_annotations(model, body, schema, allow_multiple, allow_coerce): + return check_data(model, body, schema, allow_multiple, allow_coerce) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/py.typed b/.venv/lib/python3.12/site-packages/sanic_ext/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/__init__.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/extraction.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/extraction.py new file mode 100644 index 0000000..bdca0f8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/utils/extraction.py @@ -0,0 +1,13 @@ +from sanic import Request +from sanic.exceptions import SanicException + + +def extract_request(*args) -> Request: + request: Request + if args and isinstance(args[0], Request): + request = args[0] + elif len(args) > 1: + request = args[1] + else: + raise SanicException("Request could not be found") + return request diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/route.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/route.py new file mode 100644 index 0000000..d5e6e46 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/utils/route.py @@ -0,0 +1,124 @@ +import re + + +def clean_route_name(name: str) -> str: + parts = name.split(".", 1) + name = parts[-1] + for target in ("_", ".", " "): + name = name.replace(target, " ") + + return name.title() + + +def get_uri_filter(app): + """ + Return a filter function that takes a URI and returns whether it should + be filter out from the swagger documentation or not. + + Arguments: + app: The application to take `config.API_URI_FILTER` from. Possible + values for this config option are: `slash` (to keep URIs that + end with a `/`), `all` (to keep all URIs). All other values + default to keep all URIs that don't end with a `/`. + + Returns: + `True` if the URI should be *filtered out* from the swagger + documentation, and `False` if it should be kept in the documentation. + """ + choice = getattr(app.config, "API_URI_FILTER", None) + + if choice == "slash": + # Keep URIs that end with a /. + return lambda uri: not uri.endswith("/") + + if choice == "all": + # Keep all URIs. + return lambda uri: False + + # Keep URIs that don't end with a /, (special case: "/"). + return lambda uri: len(uri) > 1 and uri.endswith("/") + + +def remove_nulls(dictionary, deep=True): + """ + Removes all null values from a dictionary. + """ + return { + k: remove_nulls(v, deep) if deep and isinstance(v, dict) else v + for k, v in dictionary.items() + if v is not None + } + + +def remove_nulls_from_kwargs(**kwargs): + return remove_nulls(kwargs, deep=False) + + +def get_blueprinted_routes(app): + for blueprint in app.blueprints.values(): + if not hasattr(blueprint, "routes"): + continue + + for route in blueprint.routes: + if hasattr(route.handler, "view_class"): + # before sanic 21.3, route.handler could be a number of + # different things, so have to type check + for http_method in route.methods: + _handler = getattr( + route.handler.view_class, http_method.lower(), None + ) + if _handler: + yield (blueprint.name, _handler) + else: + yield (blueprint.name, route.handler) + + +def get_all_routes(app, skip_prefix): + uri_filter = get_uri_filter(app) + + for group in app.router.groups.values(): + uri = f"/{group.path}" + + # prior to sanic 21.3 routes came in both forms + # (e.g. /test and /test/ ) + # after sanic 21.3 routes come in one form, + # with an attribute "strict", + # so we simulate that ourselves: + + uris = [uri] + if not group.strict and len(uri) > 1: + alt = uri[:-1] if uri.endswith("/") else f"{uri}/" + uris.append(alt) + + for uri in uris: + if uri_filter(uri): + continue + + if skip_prefix and group.raw_path.startswith( + skip_prefix.lstrip("/") + ): + continue + + for parameter in group.params.values(): + uri = re.sub( + f"<{parameter.name}.*?>", + f"{{{parameter.name}}}", + uri, + ) + + for route in group: + if getattr(route.extra, "static", False): + continue + + method_handlers = [ + (method, route.handler) for method in route.methods + ] + + _, name = route.name.split(".", 1) + yield ( + uri, + name, + route.params.values(), + method_handlers, + route.requirements.get("host"), + ) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/string.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/string.py new file mode 100644 index 0000000..3ab6d10 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/utils/string.py @@ -0,0 +1,13 @@ +import re + + +CAMEL_TO_SNAKE_PATTERNS = ( + re.compile(r"(.)([A-Z][a-z]+)"), + re.compile(r"([a-z0-9])([A-Z])"), +) + + +def camel_to_snake(name: str) -> str: + for pattern in CAMEL_TO_SNAKE_PATTERNS: + name = pattern.sub(r"\1_\2", name) + return name.lower() diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/typing.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/typing.py new file mode 100644 index 0000000..605204e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/utils/typing.py @@ -0,0 +1,79 @@ +import types +import typing + +from inspect import isclass + + +try: + UnionType = types.UnionType # type: ignore +except AttributeError: + UnionType = type("UnionType", (), {}) # type: ignore + +try: + from pydantic import BaseModel + + PYDANTIC = True +except ImportError: + PYDANTIC = False + +try: + import attrs # noqa + + ATTRS = True +except ImportError: + ATTRS = False + +try: + from msgspec import Struct + + MSGSPEC = True +except ImportError: + MSGSPEC = False + + +def is_generic(item): + return ( + isinstance(item, typing._GenericAlias) + or isinstance(item, UnionType) + or hasattr(item, "__origin__") + ) + + +def is_optional(item): + if is_generic(item): + args = typing.get_args(item) + return len(args) == 2 and type(None) in args + return False + + +def is_pydantic(model): + return PYDANTIC and ( + issubclass(model, BaseModel) or hasattr(model, "__pydantic_model__") + ) + + +def is_attrs(model): + return ATTRS and (hasattr(model, "__attrs_attrs__")) + + +def is_msgspec(model): + return MSGSPEC and issubclass(model, Struct) + + +def flat_values( + item: typing.Union[dict[str, typing.Any], typing.Iterable[typing.Any]], +) -> set[typing.Any]: + values = set() + if isinstance(item, dict): + item = item.values() + for value in item: + if isinstance(value, dict) or isinstance(value, list): + values.update(flat_values(value)) + else: + values.add(value) + return values + + +def contains_annotations(d: dict[str, typing.Any]) -> bool: + values = flat_values(d) + return any(isclass(q) or is_generic(q) for q in values) diff --git a/.venv/lib/python3.12/site-packages/sanic_ext/utils/version.py b/.venv/lib/python3.12/site-packages/sanic_ext/utils/version.py new file mode 100644 index 0000000..e3d5a24 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sanic_ext/utils/version.py @@ -0,0 +1,44 @@ +import re + + +# Expression from https://github.com/pypa/packaging +VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # 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
+"""
+PATTERN = re.compile(
+    r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE
+)
+
+
+def get_version(version) -> tuple[int, ...]:
+    match = PATTERN.search(version)
+    if not match:
+        raise ValueError(f"Invalid version: {version}")
+    return tuple(map(int, match.group("release").split(".")))
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/LICENSE
new file mode 100644
index 0000000..3baabde
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Sanic Community Organization
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/METADATA
new file mode 100644
index 0000000..a9b2f07
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/METADATA
@@ -0,0 +1,207 @@
+Metadata-Version: 2.1
+Name: sanic-routing
+Version: 23.12.0
+Summary: Core routing component for Sanic
+Home-page: https://github.com/sanic-org/sanic-routing/
+Author: Adam Hopkins
+Author-email: admhpkns@gmail.com
+License: MIT
+Platform: any
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+# Sanic Routing
+
+## Background
+
+Beginning in v21.3, Sanic makes use of this new AST-style router in two use cases:
+
+1. Routing paths; and
+2. Routing signals.
+
+Therefore, this package comes with a `BaseRouter` that needs to be subclassed in order to be used for its specific needs. 
+
+Most Sanic users should never need to concern themselves with the details here.
+
+## Basic Example
+
+A simple implementation:
+
+```python
+import logging
+
+from sanic_routing import BaseRouter
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+class Router(BaseRouter):
+    def get(self, path, *args, **kwargs):
+        return self.resolve(path, *args, **kwargs)
+
+
+router = Router()
+
+router.add("/", lambda: ...)
+router.finalize()
+router.tree.display()
+logging.info(router.find_route_src)
+
+route, handler, params = router.get("/matchme", method="BASE", extra=None)
+```
+
+The above snippet uses `router.tree.display()` to show how the router has decided to arrange the routes into a tree. In this simple example:
+
+```
+
+     len=1>], dynamic=True>
+```
+
+We can can see the code that the router has generated for us. It is available as a string at `router.find_route_src`.
+
+```python
+def find_route(path, method, router, basket, extra):
+    parts = tuple(path[1:].split(router.delimiter))
+    num = len(parts)
+    
+    # node=1 // part=__dynamic__:str
+    if num == 1:  # CHECK 1
+        try:
+            basket['__matches__'][0] = str(parts[0])
+        except ValueError:
+            pass
+        else:
+            # Return 1
+            return router.dynamic_routes[('<__dynamic__:str>',)][0], basket
+    raise NotFound
+```
+
+_FYI: If you are on Python 3.9, you can see a representation of the source after compilation at `router.find_route_src_compiled`_
+
+## What's it doing?
+
+Therefore, in general implementation requires you to:
+
+1. Define a router with a `get` method;
+2. Add one or more routes;
+3. Finalize the router (`router.finalize()`); and
+4. Call the router's `get` method.
+
+_NOTE: You can call `router.finalize(False)` if you do not want to compile the source code into executable form. This is useful if you only intend to review the generated output._
+
+Every time you call `router.add` you create one (1) new `Route` instance. Even if that one route is created with multiple methods, it generates a single instance. If you `add()` another `Route` that has a similar path structure (but, perhaps has differen methods) they will be grouped together into a `RouteGroup`. It is worth also noting that a `RouteGroup` is created the first time you call `add()`, but subsequent similar routes will reuse the existing grouping instance.
+
+
+When you call `finalize()`, it is taking the defined route groups and arranging them into "nodes" in a hierarchical tree. A single node is a path segment. A `Node` instance can have one or more `RouteGroup` on it where the `Node` is the termination point for that path.
+
+Perhaps an example is easier:
+
+```python
+router.add("/path/to/", lambda: ...)
+router.add("/path/to/", lambda: ...)
+router.add("/path/to/different/", lambda: ...)
+router.add("/path/to/different/", lambda: ..., methods=["one", "two"])
+```
+
+The generated `RouteGroup` instances (3):
+
+```
+ len=1>
+ len=1>
+ len=2>
+```
+
+The generated `Route` instances (4):
+
+```
+>
+>
+>
+>
+```
+
+The Node Tree:
+
+```
+
+    
+        
+            
+                 len=2>], dynamic=True>
+             len=1>], dynamic=True>
+             len=1>], dynamic=True>
+```
+
+And, the generated source code:
+
+```python
+def find_route(path, method, router, basket, extra):
+    parts = tuple(path[1:].split(router.delimiter))
+    num = len(parts)
+    
+    # node=1 // part=path
+    if num > 1:  # CHECK 1
+        if parts[0] == "path":  # CHECK 4
+            
+            # node=1.1 // part=to
+            if num > 2:  # CHECK 1
+                if parts[1] == "to":  # CHECK 4
+                    
+                    # node=1.1.1 // part=different
+                    if num > 3:  # CHECK 1
+                        if parts[2] == "different":  # CHECK 4
+                            
+                            # node=1.1.1.1 // part=__dynamic__:str
+                            if num == 4:  # CHECK 1
+                                try:
+                                    basket['__matches__'][3] = str(parts[3])
+                                except ValueError:
+                                    pass
+                                else:
+                                    if method in frozenset({'one', 'two'}):
+                                        route_idx = 0
+                                    elif method in frozenset({'BASE'}):
+                                        route_idx = 1
+                                    else:
+                                        raise NoMethod
+                                    # Return 1.1.1.1
+                                    return router.dynamic_routes[('path', 'to', 'different', '<__dynamic__:str>')][route_idx], basket
+                    
+                    # node=1.1.2 // part=__dynamic__:int
+                    if num >= 3:  # CHECK 1
+                        try:
+                            basket['__matches__'][2] = int(parts[2])
+                        except ValueError:
+                            pass
+                        else:
+                            if num == 3:  # CHECK 5
+                                # Return 1.1.2
+                                return router.dynamic_routes[('path', 'to', '<__dynamic__:int>')][0], basket
+                    
+                    # node=1.1.3 // part=__dynamic__:str
+                    if num >= 3:  # CHECK 1
+                        try:
+                            basket['__matches__'][2] = str(parts[2])
+                        except ValueError:
+                            pass
+                        else:
+                            if num == 3:  # CHECK 5
+                                # Return 1.1.3
+                                return router.dynamic_routes[('path', 'to', '<__dynamic__:str>')][0], basket
+    raise NotFound
+```
+
+## Special cases
+
+The above example only shows routes that have a dynamic path segment in them (example: ``). But, there are other use cases that are covered differently:
+
+1. *fully static paths* - These are paths with no parameters (example: `/user/login`). These are basically matched against a key/value store.
+2. *regex paths* - If a route as a single regular expression match, then the whole route will be matched via regex. In general, this happens inline not too dissimilar than what we see in the above example.
+3. *special regex paths* - The router comes with a special `path` type (example: ``) that can match on an expanded delimiter. This is also true for any regex that uses the path delimiter in it. These cannot be matched in the normal course since they are of unknown length.
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/RECORD
new file mode 100644
index 0000000..29ca097
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/RECORD
@@ -0,0 +1,26 @@
+sanic_routing-23.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+sanic_routing-23.12.0.dist-info/LICENSE,sha256=ljxq2vUrShwTdHmC287CFLjxv9hsdQmSQjO-wrPapnE,1085
+sanic_routing-23.12.0.dist-info/METADATA,sha256=fYWzzq4DWWNoOwrz_CIHefVfUYMroStFr2XuWOwGI_A,8185
+sanic_routing-23.12.0.dist-info/RECORD,,
+sanic_routing-23.12.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_routing-23.12.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
+sanic_routing-23.12.0.dist-info/top_level.txt,sha256=3J5z7ILGS-_I3KX9fLonNsTlaEWWZu-LQtLzGdhqBsU,14
+sanic_routing/__init__.py,sha256=y3KN7z-yMkRDdZmRU_PMXtB-jyB3q459ghXQzksgyrw,160
+sanic_routing/__pycache__/__init__.cpython-312.pyc,,
+sanic_routing/__pycache__/exceptions.cpython-312.pyc,,
+sanic_routing/__pycache__/group.cpython-312.pyc,,
+sanic_routing/__pycache__/line.cpython-312.pyc,,
+sanic_routing/__pycache__/patterns.cpython-312.pyc,,
+sanic_routing/__pycache__/route.cpython-312.pyc,,
+sanic_routing/__pycache__/router.cpython-312.pyc,,
+sanic_routing/__pycache__/tree.cpython-312.pyc,,
+sanic_routing/__pycache__/utils.cpython-312.pyc,,
+sanic_routing/exceptions.py,sha256=0B3DT52-tUH_8gq_Qd6cxB0YAlMJC5MgOOlO7d0locU,895
+sanic_routing/group.py,sha256=BERhB5H_cF5vAqqwJyi6eu-d3GBJFZ53TYS7GY6aao4,6373
+sanic_routing/line.py,sha256=4ia8gTCKzlcZYjJ5oaMOS-8onEpF-td-YwHB1lZhP2M,362
+sanic_routing/patterns.py,sha256=snKzkKQev_sgGXAIk6THsbJlAxGg9EEQ8ZjRkU9FRgc,5828
+sanic_routing/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_routing/route.py,sha256=LACpeLmJsOtEmZyL8ZjqR-g1JUx6fzWoctCiqUNeg_k,12645
+sanic_routing/router.py,sha256=QXL-2nm6AuSi2YWJ2pSZRoXSE6uSqnTMyK9NxcfUf6g,22788
+sanic_routing/tree.py,sha256=yNGKPYaRTziotpn6QkbMlhCqNs6Nq3X-_mxHKeyVtAA,15845
+sanic_routing/utils.py,sha256=Jm83e21Ktci6HDD6ECoiiMe_CEiZidAnstgzEPejnSo,3330
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/WHEEL
new file mode 100644
index 0000000..98c0d20
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.42.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/top_level.txt
new file mode 100644
index 0000000..f48d8b6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing-23.12.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+sanic_routing
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/__init__.py b/.venv/lib/python3.12/site-packages/sanic_routing/__init__.py
new file mode 100644
index 0000000..a2cbf97
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/__init__.py
@@ -0,0 +1,7 @@
+from .group import RouteGroup
+from .route import Route
+from .router import BaseRouter
+
+
+__version__ = "23.12.0"
+__all__ = ("BaseRouter", "Route", "RouteGroup")
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/exceptions.py b/.venv/lib/python3.12/site-packages/sanic_routing/exceptions.py
new file mode 100644
index 0000000..d739e90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/exceptions.py
@@ -0,0 +1,49 @@
+from typing import Optional, Set
+
+
+class BaseException(Exception):
+    ...
+
+
+class NotFound(BaseException):
+    def __init__(
+        self,
+        message: str = "Not Found",
+        path: Optional[str] = None,
+    ):
+        super().__init__(message)
+        self.path = path
+
+
+class BadMethod(BaseException):
+    ...
+
+
+class NoMethod(BaseException):
+    def __init__(
+        self,
+        message: str = "Method does not exist",
+        method: Optional[str] = None,
+        allowed_methods: Optional[Set[str]] = None,
+        path: Optional[str] = None,
+    ):
+        super().__init__(message)
+        self.method = method
+        self.allowed_methods = allowed_methods
+        self.path = path
+
+
+class FinalizationError(BaseException):
+    ...
+
+
+class InvalidUsage(BaseException):
+    ...
+
+
+class RouteExists(BaseException):
+    ...
+
+
+class ParameterNameConflicts(BaseException):
+    ...
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/group.py b/.venv/lib/python3.12/site-packages/sanic_routing/group.py
new file mode 100644
index 0000000..60b3872
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/group.py
@@ -0,0 +1,206 @@
+from __future__ import annotations
+
+from typing import FrozenSet, List, Optional, Sequence, Tuple
+
+from sanic_routing.route import Requirements, Route
+from sanic_routing.utils import Immutable
+
+from .exceptions import InvalidUsage, RouteExists
+
+
+class RouteGroup:
+    methods_index: Immutable
+    passthru_properties = (
+        "labels",
+        "params",
+        "parts",
+        "path",
+        "pattern",
+        "raw_path",
+        "regex",
+        "router",
+        "segments",
+        "strict",
+        "unquote",
+        "uri",
+    )
+
+    #: The _reconstructed_ path after the Route has been normalized.
+    #: Does not contain preceding ``/``  (see also
+    #: :py:attr:`uri`)
+    path: str
+
+    #: A regex version of the :py:attr:`~sanic_routing.route.Route.path`
+    pattern: Optional[str]
+
+    #: Whether the route requires regular expression evaluation
+    regex: bool
+
+    #: The raw version of the path exploded (see also
+    #: :py:attr:`segments`)
+    parts: Tuple[str, ...]
+
+    #: Same as :py:attr:`parts` except
+    #:  generalized so that any dynamic parts do not
+    #:  include param keys since they have no impact on routing.
+    segments: Tuple[str, ...]
+
+    #: Whether the route should be matched with strict evaluation
+    strict: bool
+
+    #: Whether the route should be unquoted after matching if (for example) it
+    #: is suspected to contain non-URL friendly characters
+    unquote: bool
+
+    #: Since :py:attr:`path` does NOT
+    #:  include a preceding '/', this adds it back.
+    uri: str
+
+    def __init__(self, *routes) -> None:
+        if len(set(route.parts for route in routes)) > 1:
+            raise InvalidUsage("Cannot group routes with differing paths")
+
+        if any(routes[-1].strict != route.strict for route in routes):
+            raise InvalidUsage("Cannot group routes with differing strictness")
+
+        route_list = list(routes)
+        route_list.pop()
+
+        self._routes = routes
+        self.pattern_idx = 0
+
+    def __str__(self):
+        display = (
+            f"path={self.path or self.router.delimiter} len={len(self.routes)}"
+        )
+        return f"<{self.__class__.__name__}: {display}>"
+
+    def __repr__(self) -> str:
+        return str(self)
+
+    def __iter__(self):
+        return iter(self.routes)
+
+    def __getitem__(self, key):
+        return self.routes[key]
+
+    def __getattr__(self, key):
+        # There are a number of properties that all of the routes in the group
+        # share in common. We pass thrm through to make them available
+        # on the RouteGroup, and then cache them so that they are permanent.
+        if key in self.passthru_properties:
+            value = getattr(self[0], key)
+            setattr(self, key, value)
+            return value
+
+        raise AttributeError(f"RouteGroup has no '{key}' attribute")
+
+    def finalize(self):
+        self.methods_index = Immutable(
+            {
+                method: route
+                for route in self._routes
+                for method in route.methods
+            }
+        )
+
+    def prioritize_routes(self) -> None:
+        """
+        Sorts the routes in the group by priority
+        """
+        self._routes = tuple(
+            sorted(self._routes, key=lambda route: route.priority)
+        )
+
+    def reset(self):
+        self.methods_index = dict(self.methods_index)
+
+    def merge(
+        self, group: RouteGroup, overwrite: bool = False, append: bool = False
+    ) -> None:
+        """
+        The purpose of merge is to group routes with the same path, but
+        declarared individually. In other words to group these:
+
+        .. code-block:: python
+
+            @app.get("/path/to")
+            def handler1(...):
+                ...
+
+            @app.post("/path/to")
+            def handler2(...):
+                ...
+
+        The other main purpose is to look for conflicts and
+        raise ``RouteExists``
+
+        A duplicate route is when:
+        1. They have the same path and any overlapping methods; AND
+        2. If they have requirements, they are the same
+
+        :param group: Incoming route group
+        :type group: RouteGroup
+        :param overwrite: whether to allow an otherwise duplicate route group
+            to overwrite the existing, if ``True`` will not raise exception
+            on duplicates, defaults to False
+        :type overwrite: bool, optional
+        :param append: whether to allow an otherwise duplicate route group to
+            append its routes to the existing route group, defaults to False
+        :type append: bool, optional
+        :raises RouteExists: Raised when there is a duplicate
+        """
+        _routes = list(self._routes)
+        for other_route in group.routes:
+            for current_route in self:
+                if (
+                    current_route == other_route
+                    or (
+                        current_route.requirements
+                        and not other_route.requirements
+                    )
+                    or (
+                        not current_route.requirements
+                        and other_route.requirements
+                    )
+                ) and not append:
+                    if not overwrite:
+                        raise RouteExists(
+                            f"Route already registered: {self.raw_path} "
+                            f"[{','.join(self.methods)}]"
+                        )
+                else:
+                    _routes.append(other_route)
+                    _routes.sort(
+                        key=lambda route: route.priority, reverse=True
+                    )
+        self._routes = tuple(_routes)
+
+    @property
+    def depth(self) -> int:
+        """
+        The number of parts in :py:attr:`parts`
+        """
+        return len(self[0].parts)
+
+    @property
+    def dynamic_path(self) -> bool:
+        return any(
+            (param.label == "path") or ("/" in param.label)
+            for param in self.params.values()
+        )
+
+    @property
+    def methods(self) -> FrozenSet[str]:
+        """"""
+        return frozenset(
+            [method for route in self for method in route.methods]
+        )
+
+    @property
+    def routes(self) -> Sequence[Route]:
+        return self._routes
+
+    @property
+    def requirements(self) -> List[Requirements]:
+        return [route.requirements for route in self if route.requirements]
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/line.py b/.venv/lib/python3.12/site-packages/sanic_routing/line.py
new file mode 100644
index 0000000..c3acb86
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/line.py
@@ -0,0 +1,17 @@
+class Line:
+    TAB = "    "
+
+    def __init__(
+        self,
+        src: str,
+        indent: int,
+        offset: int = 0,
+        render: bool = True,
+    ) -> None:
+        self.src = src
+        self.indent = indent
+        self.offset = offset
+        self.render = render
+
+    def __str__(self):
+        return (self.TAB * self.indent) + self.src + "\n"
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/patterns.py b/.venv/lib/python3.12/site-packages/sanic_routing/patterns.py
new file mode 100644
index 0000000..d00f92f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/patterns.py
@@ -0,0 +1,178 @@
+import re
+import typing as t
+import uuid
+
+from datetime import date, datetime
+from types import SimpleNamespace
+from typing import Any, Callable, Dict, Pattern, Tuple, Type
+
+from sanic_routing.exceptions import InvalidUsage, NotFound
+
+
+def parse_date(d) -> date:
+    return datetime.strptime(d, "%Y-%m-%d").date()
+
+
+def alpha(param: str) -> str:
+    if not param.isalpha():
+        raise ValueError(f"Value {param} contains non-alphabetic chracters")
+    return param
+
+
+def slug(param: str) -> str:
+    if not REGEX_TYPES["slug"][1].match(param):
+        raise ValueError(f"Value {param} does not match the slug format")
+    return param
+
+
+def ext(param: str) -> Tuple[str, ...]:
+    parts = tuple(param.split("."))
+    if any(not p for p in parts) or len(parts) == 1:
+        raise ValueError(f"Value {param} does not match filename format")
+    return parts
+
+
+def nonemptystr(param: str) -> str:
+    if not param:
+        raise ValueError(f"Value {param} is an empty string")
+    return param
+
+
+class ParamInfo:
+    __slots__ = (
+        "cast",
+        "ctx",
+        "label",
+        "name",
+        "pattern",
+        "priority",
+        "raw_path",
+        "regex",
+    )
+
+    def __init__(
+        self,
+        name: str,
+        raw_path: str,
+        label: str,
+        cast: t.Callable[[str], t.Any],
+        pattern: re.Pattern,
+        regex: bool,
+        priority: int,
+    ) -> None:
+        self.name = name
+        self.raw_path = raw_path
+        self.label = label
+        self.cast = cast
+        self.pattern = pattern
+        self.regex = regex
+        self.priority = priority
+        self.ctx = SimpleNamespace()
+
+    def process(
+        self,
+        params: t.Dict[str, t.Any],
+        value: t.Union[str, t.Tuple[str, ...]],
+    ) -> None:
+        params[self.name] = value
+
+
+class ExtParamInfo(ParamInfo):
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+        match = REGEX_PARAM_EXT_PATH.search(self.raw_path)
+        if not match:
+            raise InvalidUsage(
+                f"Invalid extension parameter definition: {self.raw_path}"
+            )
+        if match.group(2) == "path":
+            raise InvalidUsage(
+                "Extension parameter matching does not support the "
+                "`path` type."
+            )
+        ext_type = match.group(3)
+        regex_type = REGEX_TYPES.get(match.group(2))
+        self.ctx.cast = None
+        if regex_type:
+            self.ctx.cast = regex_type[0]
+        elif match.group(2):
+            raise InvalidUsage(
+                "Extension parameter matching only supports filename matching "
+                "on known parameter types, and not regular expressions."
+            )
+        self.ctx.allowed = []
+        self.ctx.allowed_sub_count = 0
+        if ext_type:
+            self.ctx.allowed = ext_type.split("|")
+            allowed_subs = {allowed.count(".") for allowed in self.ctx.allowed}
+            if len(allowed_subs) > 1:
+                raise InvalidUsage(
+                    "All allowed extensions within a single route definition "
+                    "must contain the same number of subparts. For example: "
+                    " and  are both "
+                    "acceptable, but  is not."
+                )
+            self.ctx.allowed_sub_count = next(iter(allowed_subs))
+
+            for extension in self.ctx.allowed:
+                if not REGEX_ALLOWED_EXTENSION.match(extension):
+                    raise InvalidUsage(f"Invalid extension: {extension}")
+
+    def process(self, params, value):
+        stop = -1 * (self.ctx.allowed_sub_count + 1)
+        filename = ".".join(value[:stop])
+        ext = ".".join(value[stop:])
+        if self.ctx.allowed and ext not in self.ctx.allowed:
+            raise NotFound(f"Invalid extension: {ext}")
+        if self.ctx.cast:
+            try:
+                filename = self.ctx.cast(filename)
+            except ValueError:
+                raise NotFound(f"Invalid filename: {filename}")
+        params[self.name] = filename
+        params["ext"] = ext
+
+
+EXTENSION = r"[a-z0-9](?:[a-z0-9\.]*[a-z0-9])?"
+PARAM_EXT = (
+    r"<([a-zA-Z_][a-zA-Z0-9_]*)(?:=([a-z]+))?(?::ext(?:=([a-z0-9|\.]+))?)>"
+)
+REGEX_PARAM_NAME = re.compile(r"^<([a-zA-Z_][a-zA-Z0-9_]*)(?::(.*))?>$")
+REGEX_PARAM_EXT_PATH = re.compile(PARAM_EXT)
+REGEX_PARAM_NAME_EXT = re.compile(r"^" + PARAM_EXT + r"$")
+REGEX_ALLOWED_EXTENSION = re.compile(r"^" + EXTENSION + r"$")
+
+# Predefined path parameter types. The value is a tuple consisteing of a
+# callable and a compiled regular expression.
+# The callable should:
+#   1. accept a string input
+#   2. cast the string to desired type
+#   3. raise ValueError if it cannot
+# The regular expression is generally NOT used. Unless the path is forced
+# to use regex patterns.
+REGEX_TYPES_ANNOTATION = Dict[
+    str, Tuple[Callable[[str], Any], Pattern, Type[ParamInfo]]
+]
+REGEX_TYPES: REGEX_TYPES_ANNOTATION = {
+    "strorempty": (str, re.compile(r"^[^/]*$"), ParamInfo),
+    "str": (nonemptystr, re.compile(r"^[^/]+$"), ParamInfo),
+    "ext": (ext, re.compile(r"^[^/]+\." + EXTENSION + r"$"), ExtParamInfo),
+    "slug": (slug, re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$"), ParamInfo),
+    "alpha": (alpha, re.compile(r"^[A-Za-z]+$"), ParamInfo),
+    "path": (str, re.compile(r"^[^/]?.*?$"), ParamInfo),
+    "float": (float, re.compile(r"^-?(?:\d+(?:\.\d*)?|\.\d+)$"), ParamInfo),
+    "int": (int, re.compile(r"^-?\d+$"), ParamInfo),
+    "ymd": (
+        parse_date,
+        re.compile(r"^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$"),
+        ParamInfo,
+    ),
+    "uuid": (
+        uuid.UUID,
+        re.compile(
+            r"^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-"
+            r"[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$"
+        ),
+        ParamInfo,
+    ),
+}
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/py.typed b/.venv/lib/python3.12/site-packages/sanic_routing/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/route.py b/.venv/lib/python3.12/site-packages/sanic_routing/route.py
new file mode 100644
index 0000000..eee17d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/route.py
@@ -0,0 +1,380 @@
+import re
+import typing as t
+
+from types import SimpleNamespace
+from warnings import warn
+
+from .exceptions import InvalidUsage, ParameterNameConflicts
+from .patterns import ParamInfo
+from .utils import Immutable, parts_to_path, path_to_parts
+
+
+class Requirements(Immutable):
+    def __hash__(self):
+        return hash(frozenset(self.items()))
+
+
+class Route:
+    __slots__ = (
+        "_params",
+        "_raw_path",
+        "ctx",
+        "extra",
+        "handler",
+        "labels",
+        "methods",
+        "name",
+        "overloaded",
+        "params",
+        "parts",
+        "path",
+        "pattern",
+        "priority",
+        "regex",
+        "requirements",
+        "router",
+        "static",
+        "strict",
+        "unquote",
+    )
+
+    #: A container for route meta-data
+    ctx: SimpleNamespace
+    #: A container for route application-data
+    extra: SimpleNamespace
+    #: The route handler
+    handler: t.Callable[..., t.Any]
+    #: The HTTP methods that the route can handle
+    methods: t.FrozenSet[str]
+    #: The route name, either generated or as defined in the route definition
+    name: str
+    #: The raw version of the path exploded (see also
+    #: :py:attr:`~sanic_routing.route.Route.segments`)
+    parts: t.Tuple[str, ...]
+    #: The _reconstructed_ path after the Route has been normalized.
+    #: Does not contain preceding ``/``  (see also
+    #: :py:attr:`~sanic_routing.route.Route.uri`)
+    path: str
+    #: A regex version of the :py:attr:`~sanic_routing.route.Route.path`
+    pattern: t.Optional[str]
+    #: Whether the route requires regular expression evaluation
+    regex: bool
+    #: A representation of the non-path route requirements
+    requirements: Requirements
+    #: When ``True``, the route does not have any dynamic path parameters
+    static: bool
+    #: Whether the route should be matched with strict evaluation
+    strict: bool
+    #: Whether the route should be unquoted after matching if (for example) it
+    #: is suspected to contain non-URL friendly characters
+    unquote: bool
+
+    def __init__(
+        self,
+        router,
+        raw_path: str,
+        name: str,
+        handler: t.Callable[..., t.Any],
+        methods: t.Union[t.Sequence[str], t.FrozenSet[str]],
+        requirements: t.Optional[t.Dict[str, t.Any]] = None,
+        strict: bool = False,
+        unquote: bool = False,
+        static: bool = False,
+        regex: bool = False,
+        overloaded: bool = False,
+        *,
+        priority: int = 0,
+    ):
+        self.router = router
+        self.name = name
+        self.handler = handler  # type: ignore
+        self.methods = frozenset(methods)
+        self.requirements = Requirements(requirements or {})
+        self.priority = priority
+
+        self.ctx = SimpleNamespace()
+        self.extra = SimpleNamespace()
+
+        self._params: t.Dict[int, ParamInfo] = {}
+        self._raw_path = raw_path
+
+        # Main goal is to do some normalization. Any dynamic segments
+        # that are missing a type are rewritten with str type
+        ingested_path = self._ingest_path(raw_path)
+
+        # By passing the path back and forth to deconstruct and reconstruct
+        # we can normalize it and make sure we are dealing consistently
+        parts = path_to_parts(ingested_path, self.router.delimiter)
+        self.path = parts_to_path(parts, delimiter=self.router.delimiter)
+        self.parts = parts
+        self.static = static
+        self.regex = regex
+        self.overloaded = overloaded
+        self.pattern = None
+        self.strict: bool = strict
+        self.unquote: bool = unquote
+        self.labels: t.Optional[t.List[str]] = None
+
+        self._setup_params()
+
+    def __str__(self):
+        display = (
+            f"name={self.name} path={self.path or self.router.delimiter}"
+            if self.name and self.name != self.path
+            else f"path={self.path or self.router.delimiter}"
+        )
+        return f"<{self.__class__.__name__}: {display}>"
+
+    def __repr__(self) -> str:
+        return str(self)
+
+    def __eq__(self, other) -> bool:
+        if not isinstance(other, self.__class__):
+            return False
+
+        # Equality specifically uses self.segments and not self.parts.
+        # In general, these properties are nearly identical.
+        # self.segments is generalized and only displays dynamic param types
+        # and self.parts has both the param key and the param type.
+        # In this test, we use the & operator so that we create a union and a
+        # positive equality if there is one or more overlaps in the methods.
+        return bool(
+            (
+                self.segments,
+                self.requirements,
+            )
+            == (
+                other.segments,
+                other.requirements,
+            )
+            and (self.methods & other.methods)
+        )
+
+    def _ingest_path(self, path):
+        segments = []
+        for part in path.split(self.router.delimiter):
+            if part.startswith("<") and ":" not in part:
+                name = part[1:-1]
+                part = f"<{name}:str>"
+            segments.append(part)
+        return self.router.delimiter.join(segments)
+
+    def _setup_params(self):
+        key_path = parts_to_path(
+            path_to_parts(self.raw_path, self.router.delimiter),
+            self.router.delimiter,
+        )
+        if not self.static:
+            parts = path_to_parts(key_path, self.router.delimiter)
+            for idx, part in enumerate(parts):
+                if part.startswith("<"):
+                    (
+                        name,
+                        label,
+                        _type,
+                        pattern,
+                        param_info_class,
+                    ) = self.parse_parameter_string(part[1:-1])
+
+                    self.add_parameter(
+                        idx,
+                        name,
+                        key_path,
+                        label,
+                        _type,
+                        pattern,
+                        param_info_class,
+                    )
+
+    def add_parameter(
+        self,
+        idx: int,
+        name: str,
+        raw_path: str,
+        label: str,
+        cast: t.Type,
+        pattern=None,
+        param_info_class=ParamInfo,
+    ):
+        if pattern and isinstance(pattern, str):
+            if not pattern.startswith("^"):
+                pattern = f"^{pattern}"
+            if not pattern.endswith("$"):
+                pattern = f"{pattern}$"
+
+            pattern = re.compile(pattern)
+
+        is_regex = label not in self.router.regex_types
+        priority = (
+            0
+            if is_regex
+            else list(self.router.regex_types.keys()).index(label)
+        )
+        self._params[idx] = param_info_class(
+            name=name,
+            raw_path=raw_path,
+            label=label,
+            cast=cast,
+            pattern=pattern,
+            regex=is_regex,
+            priority=priority,
+        )
+
+    def _finalize_params(self):
+        params = dict(self._params)
+        label_pairs = set([(param.name, idx) for idx, param in params.items()])
+        labels = [item[0] for item in label_pairs]
+        if len(labels) != len(set(labels)):
+            raise ParameterNameConflicts(
+                f"Duplicate named parameters in: {self._raw_path}"
+            )
+        self.labels = labels
+
+        self.params = dict(
+            sorted(params.items(), key=lambda param: self._sorting(param[1]))
+        )
+
+        if not self.regex and any(
+            ":" in param.label for param in self.params.values()
+        ):
+            raise InvalidUsage(
+                f"Invalid parameter declaration: {self.raw_path}"
+            )
+
+    def _compile_regex(self):
+        components = []
+
+        for part in self.parts:
+            if part.startswith("<"):
+                name, *_, pattern, __ = self.parse_parameter_string(part)
+
+                if not isinstance(pattern, str):
+                    pattern = pattern.pattern.strip("^$")
+                compiled = re.compile(pattern)
+                if compiled.groups == 1:
+                    if compiled.groupindex:
+                        if list(compiled.groupindex)[0] != name:
+                            raise InvalidUsage(
+                                f"Named group ({list(compiled.groupindex)[0]})"
+                                f" must match your named parameter ({name})"
+                            )
+                        components.append(pattern)
+                    else:
+                        if pattern.count("(") > 1:
+                            raise InvalidUsage(
+                                f"Could not compile pattern {pattern}. "
+                                "Try using a named group instead: "
+                                f"'(?P<{name}>your_matching_group)'"
+                            )
+                        beginning, end = pattern.split("(")
+                        components.append(f"{beginning}(?P<{name}>{end}")
+                elif compiled.groups > 1:
+                    raise InvalidUsage(f"Invalid matching pattern {pattern}")
+                else:
+                    components.append(f"(?P<{name}>{pattern})")
+            else:
+                components.append(part)
+
+        self.pattern = self.router.delimiter + self.router.delimiter.join(
+            components
+        )
+
+    def finalize(self):
+        self._finalize_params()
+        if self.regex:
+            self._compile_regex()
+        self.requirements = Immutable(self.requirements)
+
+    def reset(self):
+        self.requirements = dict(self.requirements)
+
+    @property
+    def defined_params(self):
+        return self._params
+
+    @property
+    def raw_path(self):
+        """
+        The raw path from the route definition
+        """
+        return self._raw_path
+
+    @property
+    def segments(self) -> t.Tuple[str, ...]:
+        """
+        Same as :py:attr:`~sanic_routing.route.Route.parts` except
+        generalized so that any dynamic parts do not
+        include param keys since they have no impact on routing.
+        """
+        return tuple(
+            f"<__dynamic__:{self._params[idx].label}>"
+            if idx in self._params
+            else segment
+            for idx, segment in enumerate(self.parts)
+        )
+
+    @property
+    def uri(self):
+        """
+        Since :py:attr:`~sanic_routing.route.Route.path` does NOT
+        include a preceding '/', this adds it back.
+        """
+        return f"{self.router.delimiter}{self.path}"
+
+    def _sorting(self, item) -> int:
+        try:
+            return list(self.router.regex_types.keys()).index(item.label)
+        except ValueError:
+            return len(list(self.router.regex_types.keys()))
+
+    def parse_parameter_string(self, parameter_string: str):
+        """Parse a parameter string into its constituent name, type, and
+        pattern
+
+        For example:
+
+        ```text
+        parse_parameter_string('')` -> ('param_one', '[A-z]', , '[A-z]')
+        ```
+
+        :param parameter_string: String to parse
+        :return: tuple containing
+            (parameter_name, parameter_type, parameter_pattern)
+        """  # noqa: E501
+        # We could receive NAME or NAME:PATTERN
+        parameter_string = parameter_string.strip("<>")
+        name = parameter_string
+        label = "str"
+
+        if ":" in parameter_string:
+            name, label = parameter_string.split(":", 1)
+            if "=" in label:
+                label, _ = label.split("=", 1)
+            if "=" in name:
+                name, _ = name.split("=", 1)
+
+            if not name:
+                raise ValueError(
+                    f"Invalid parameter syntax: {parameter_string}"
+                )
+            if label == "string":
+                warn(
+                    "Use of 'string' as a path parameter type is deprected, "
+                    "and will be removed in Sanic v21.12. "
+                    f"Instead, use <{name}:str>.",
+                    DeprecationWarning,
+                )
+            elif label == "number":
+                warn(
+                    "Use of 'number' as a path parameter type is deprected, "
+                    "and will be removed in Sanic v21.12. "
+                    f"Instead, use <{name}:float>.",
+                    DeprecationWarning,
+                )
+
+        default = (str, label, ParamInfo)
+
+        # Pull from pre-configured types
+        found = self.router.regex_types.get(label, default)
+        _type, pattern, param_info_class = found
+        return name, label, _type, pattern, param_info_class
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/router.py b/.venv/lib/python3.12/site-packages/sanic_routing/router.py
new file mode 100644
index 0000000..2e9d6f9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/router.py
@@ -0,0 +1,639 @@
+import ast
+import sys
+import typing as t
+
+from abc import ABC, abstractmethod
+from types import SimpleNamespace
+from warnings import warn
+
+from sanic_routing.group import RouteGroup
+from sanic_routing.patterns import ParamInfo
+
+from .exceptions import (
+    BadMethod,
+    FinalizationError,
+    InvalidUsage,
+    NoMethod,
+    NotFound,
+)
+from .line import Line
+from .patterns import REGEX_TYPES, REGEX_TYPES_ANNOTATION
+from .route import Route
+from .tree import Node, Tree
+from .utils import parts_to_path, path_to_parts
+
+
+# The below functions might be called by the compiled source code, and
+# therefore should be made available here by import
+import re  # noqa  isort:skip
+from datetime import datetime  # noqa  isort:skip
+from urllib.parse import unquote  # noqa  isort:skip
+from uuid import UUID  # noqa  isort:skip
+from .patterns import parse_date, alpha, slug, nonemptystr  # noqa  isort:skip
+
+
+class BaseRouter(ABC):
+    DEFAULT_METHOD = "BASE"
+    ALLOWED_METHODS: t.Tuple[str, ...] = tuple()
+
+    def __init__(
+        self,
+        delimiter: str = "/",
+        exception: t.Type[NotFound] = NotFound,
+        method_handler_exception: t.Type[NoMethod] = NoMethod,
+        route_class: t.Type[Route] = Route,
+        group_class: t.Type[RouteGroup] = RouteGroup,
+        stacking: bool = False,
+        cascade_not_found: bool = False,
+    ) -> None:
+        self._find_route = None
+        self._matchers = None
+        self.static_routes: t.Dict[t.Tuple[str, ...], RouteGroup] = {}
+        self.dynamic_routes: t.Dict[t.Tuple[str, ...], RouteGroup] = {}
+        self.regex_routes: t.Dict[t.Tuple[str, ...], RouteGroup] = {}
+        self.name_index: t.Dict[str, Route] = {}
+        self.delimiter = delimiter
+        self.exception = exception
+        self.method_handler_exception = method_handler_exception
+        self.route_class = route_class
+        self.group_class = group_class
+        self.tree = Tree(router=self)
+        self.finalized = False
+        self.stacking = stacking
+        self.ctx = SimpleNamespace()
+        self.cascade_not_found = cascade_not_found
+
+        self.regex_types: REGEX_TYPES_ANNOTATION = {}
+
+        for label, (cast, pattern, param_info_class) in REGEX_TYPES.items():
+            self.register_pattern(label, cast, pattern, param_info_class)
+
+    @abstractmethod
+    def get(self, **kwargs):
+        ...
+
+    def resolve(
+        self,
+        path: str,
+        *,
+        method: t.Optional[str] = None,
+        orig: t.Optional[str] = None,
+        extra: t.Optional[t.Dict[str, str]] = None,
+    ) -> t.Tuple[Route, t.Callable[..., t.Any], t.Dict[str, t.Any]]:
+        try:
+            route, param_basket = self.find_route(
+                path,
+                method,
+                self,
+                {"__params__": {}, "__matches__": {}},
+                extra,
+            )
+        except (NotFound, NoMethod) as e:
+            # If we did not find the route, we might need to try routing one
+            # more time to handle strict_slashes
+            if path.endswith(self.delimiter):
+                return self.resolve(
+                    path=path[:-1],
+                    method=method,
+                    orig=path,
+                    extra=extra,
+                )
+            raise e.__class__(str(e), path=path)
+
+        if isinstance(route, RouteGroup):
+            try:
+                route = route.methods_index[method]
+            except KeyError:
+                raise self.method_handler_exception(
+                    f"Method '{method}' not found on {route}",
+                    method=method,
+                    allowed_methods=route.methods,
+                )
+
+        # Convert matched values to parameters
+        params = param_basket["__params__"]
+        if not params or param_basket["__matches__"]:
+            # If param_basket["__params__"] does not exist, we might have
+            # param_basket["__matches__"], which are indexed based matches
+            # on path segments. They should already be cast types.
+            for idx, param in route.params.items():
+                # If the param index does not exist, then rely upon
+                # the __params__
+                try:
+                    value = param_basket["__matches__"][idx]
+                except KeyError:
+                    continue
+
+                # Apply if tuple (from ext) or if it is not a regex matcher
+                if isinstance(value, tuple):
+                    param.process(params, value)
+                elif not route.regex or (
+                    route.regex and param.cast is not str
+                ):
+                    params[param.name] = value
+
+        # Double check that if we made a match it is not a false positive
+        # because of strict_slashes
+        if route.strict and orig and orig[-1] != route.path[-1]:
+            raise self.exception("Path not found", path=path)
+
+        if method not in route.methods:
+            raise self.method_handler_exception(
+                f"Method '{method}' not found on {route}",
+                method=method,
+                allowed_methods=route.methods,
+            )
+
+        return route, route.handler, params
+
+    def add(
+        self,
+        path: str,
+        handler: t.Callable,
+        methods: t.Optional[
+            t.Union[t.Sequence[str], t.FrozenSet[str], str]
+        ] = None,
+        name: t.Optional[str] = None,
+        requirements: t.Optional[t.Dict[str, t.Any]] = None,
+        strict: bool = False,
+        unquote: bool = False,  # noqa
+        overwrite: bool = False,
+        append: bool = False,
+        *,
+        priority: int = 0,
+    ) -> Route:
+        # Can add a route with overwrite, or append, not both.
+        # - overwrite: if matching path exists, replace it
+        # - append: if matching path exists, append handler to it
+        if overwrite and append:
+            raise FinalizationError(
+                "Cannot add a route with both overwrite and append equal "
+                "to True"
+            )
+        if priority and not append:
+            raise FinalizationError(
+                "Cannot add a route with priority if append is False"
+            )
+        if not methods:
+            methods = [self.DEFAULT_METHOD]
+
+        if hasattr(methods, "__iter__") and not isinstance(methods, frozenset):
+            methods = frozenset(methods)
+        elif isinstance(methods, str):
+            methods = frozenset([methods])
+
+        if self.ALLOWED_METHODS and any(
+            method not in self.ALLOWED_METHODS for method in methods
+        ):
+            bad = [
+                method
+                for method in methods
+                if method not in self.ALLOWED_METHODS
+            ]
+            raise BadMethod(
+                f"Bad method: {bad}. Must be one of: {self.ALLOWED_METHODS}"
+            )
+
+        if self.finalized:
+            raise FinalizationError("Cannot finalize router more than once.")
+
+        static = "<" not in path and requirements is None
+        regex = self._is_regex(path)
+
+        # There are generally three pools of routes on the router:
+        # - those that are static patterns with not matching
+        # - those that have one or more dynamic parts, but NO regex
+        # - those that have one or more dynamic parts, with at least one regex
+        if regex:
+            routes = self.regex_routes
+        elif static:
+            routes = self.static_routes
+        else:
+            routes = self.dynamic_routes
+
+        # Only URL encode the static parts of the path
+        path = parts_to_path(
+            path_to_parts(path, self.delimiter), self.delimiter
+        )
+
+        # We need to clean off the delimiters are the beginning, and maybe the
+        # end, depending upon whether we are in strict mode
+        strip = path.lstrip if strict else path.strip
+        path = strip(self.delimiter)
+        route = self.route_class(
+            self,
+            path,
+            name or "",
+            handler=handler,
+            methods=methods,
+            requirements=requirements,
+            strict=strict,
+            unquote=unquote,
+            static=static,
+            regex=regex,
+            priority=priority,
+        )
+        group = self.group_class(route)
+
+        # Catch the scenario where a route is overloaded with and
+        # and without requirements, first as dynamic then as static
+        if static and route.segments in self.dynamic_routes:
+            routes = self.dynamic_routes
+
+        # Catch the reverse scenario where a route is overload first as static
+        # and then as dynamic
+        if not static and route.segments in self.static_routes:
+            existing_group = self.static_routes.pop(route.segments)
+            group.merge(existing_group, overwrite, append)
+
+        else:
+            if route.segments in routes:
+                existing_group = routes[route.segments]
+                group.merge(existing_group, overwrite, append)
+
+            routes[route.segments] = group
+
+        if name:
+            self.name_index[name] = route
+
+        group.finalize()
+
+        return route
+
+    def register_pattern(
+        self,
+        label: str,
+        cast: t.Callable[[str], t.Any],
+        pattern: t.Union[t.Pattern, str],
+        param_info_class: t.Type[ParamInfo] = ParamInfo,
+    ):
+        """
+        Add a custom parameter type to the router. The cast should raise a
+        ValueError if it is an incorrect type. The order of registration is
+        important if it is possible that a single value could pass multiple
+        pattern types. Therefore, patterns are tried in the REVERSE order of
+        registration. All custom patterns will be evaluated before any built-in
+        patterns.
+
+        :param label: The parts that is used to signify the type: example
+
+        :type label: str
+        :param cast: The callable that casts the value to the desired type, or
+            fails trying
+        :type cast: t.Callable[[str], t.Any]
+        :param pattern: A regular expression that could also match the path
+            segment
+        :type pattern: Union[t.Pattern, str]
+        """
+        if not isinstance(label, str):
+            raise InvalidUsage(
+                "When registering a pattern, label must be a "
+                f"string, not label={label}"
+            )
+        if not callable(cast):
+            raise InvalidUsage(
+                "When registering a pattern, cast must be a "
+                f"callable, not cast={cast}"
+            )
+        if not isinstance(pattern, str) and not isinstance(pattern, t.Pattern):
+            raise InvalidUsage(
+                "When registering a pattern, pattern must be a "
+                f"string or a Pattern, not pattern={pattern}, "
+                f"type={type(pattern)}"
+            )
+
+        if isinstance(pattern, str):
+            pattern = re.compile(pattern)
+
+        globals()[cast.__name__] = cast
+        self.regex_types[label] = (cast, pattern, param_info_class)
+
+    def finalize(self, do_compile: bool = True, do_optimize: bool = False):
+        """
+        After all routes are added, we can put everything into a final state
+        and build the routing dource
+
+        :param do_compile: Whether to compile the source, mainly a debugging
+            tool, defaults to True
+        :type do_compile: bool, optional
+        :param do_optimize: Experimental feature that uses AST module to make
+            some optimizations, defaults to False
+        :type do_optimize: bool, optional
+        :raises FinalizationError: Cannot finalize if there are no routes, or
+            the router has already been finalized (can call reset() to undo it)
+        """
+        if self.finalized:
+            raise FinalizationError("Cannot finalize router more than once.")
+        if not self.routes:
+            raise FinalizationError("Cannot finalize with no routes defined.")
+        self.finalized = True
+
+        for group in (
+            list(self.static_routes.values())
+            + list(self.dynamic_routes.values())
+            + list(self.regex_routes.values())
+        ):
+            group.finalize()
+            for route in group.routes:
+                route.finalize()
+            group.prioritize_routes()
+
+        # Evaluates all of the paths and arranges them into a hierarchichal
+        # tree of nodes
+        self._generate_tree()
+
+        # Renders the source code
+        self._render(do_compile, do_optimize)
+
+    def reset(self):
+        self.finalized = False
+        self.tree = Tree(router=self)
+        self._find_route = None
+
+        for group in (
+            list(self.static_routes.values())
+            + list(self.dynamic_routes.values())
+            + list(self.regex_routes.values())
+        ):
+            group.reset()
+            for route in group.routes:
+                route.reset()
+
+    def _get_non_static_non_path_groups(
+        self, has_dynamic_path: bool
+    ) -> t.List[RouteGroup]:
+        """
+        Paths that have some matching params (includes dynamic and regex),
+        but excludes any routes with a  or delimiter in its regex.
+        This is because those special cases need to be evaluated seperately.
+        Anything else can be evaluated in the node tree.
+
+        :param has_dynamic_path: Whether the path catches a path, or path-like
+        type
+        :type has_dynamic_path: bool
+        :return: list of routes that have no path, but do need matching
+        :rtype: List[RouteGroup]
+        """
+        return sorted(
+            [
+                group
+                for group in list(self.dynamic_routes.values())
+                + list(self.regex_routes.values())
+                if group.dynamic_path is has_dynamic_path
+            ],
+            key=lambda x: x.depth,
+            reverse=True,
+        )
+
+    def _generate_tree(self) -> None:
+        self.tree.generate(self._get_non_static_non_path_groups(False))
+        self.tree.finalize()
+
+    def _render(
+        self, do_compile: bool = True, do_optimize: bool = False
+    ) -> None:
+        # Initial boilerplate for the function source
+        src = [
+            Line("def find_route(path, method, router, basket, extra):", 0),
+            Line("parts = tuple(path[1:].split(router.delimiter))", 1),
+        ]
+        delayed = []
+
+        # Add static path matching
+        if self.static_routes:
+            # TODO:
+            # - future improvement would be to decide which option to use
+            #   at runtime based upon the makeup of the router since this
+            #   potentially has an impact on performance
+            src += [
+                Line("try:", 1),
+                Line(
+                    "group = router.static_routes[parts]",
+                    2,
+                ),
+                Line("basket['__raw_path__'] = path", 2),
+                Line("return group, basket", 2),
+                Line("except KeyError:", 1),
+                Line("pass", 2),
+            ]
+            # src += [
+            #     Line("if parts in router.static_routes:", 1),
+            #     Line("route = router.static_routes[parts]", 2),
+            #     Line("basket['__raw_path__'] = route.path", 2),
+            #     Line("return route, basket", 2),
+            # ]
+            # src += [
+            #     Line("if path in router.static_routes:", 1),
+            #     Line("route = router.static_routes.get(path)", 2),
+            #     Line("basket['__raw_path__'] = route.path", 2),
+            #     Line("return route, basket", 2),
+            # ]
+
+        # Add in pre-compiled regular expressions so they do not need to
+        # compile at run time
+        if self.regex_routes:
+            routes = sorted(
+                self.regex_routes.values(),
+                key=lambda route: len(route.parts),
+                reverse=True,
+            )
+            delayed.append(Line("matchers = [", 0))
+            for idx, group in enumerate(routes):
+                group.pattern_idx = idx
+                delayed.append(Line(f"re.compile(r'^{group.pattern}$'),", 1))
+            delayed.append(Line("]", 0))
+
+        # Generate all the dynamic code
+        if self.dynamic_routes or self.regex_routes:
+            src += [Line("num = len(parts)", 1)]
+            src += self.tree.render()
+
+        # Inject regex matching that could not be in the tree
+        for group in self._get_non_static_non_path_groups(True):
+            route_container = (
+                "regex_routes" if group.regex else "dynamic_routes"
+            )
+            route_idx: t.Union[str, int] = 0
+            holder: t.List[Line] = []
+
+            if group.requirements:
+                route_idx = "route_idx"
+                Node()._inject_requirements(holder, 2, group)
+
+            if route_idx == 0 and len(group.routes) > 1:
+                route_idx = "route_idx"
+                Node._inject_method_check(holder, 2, group)
+
+            src.extend(
+                [
+                    Line(
+                        (
+                            "match = router.matchers"
+                            f"[{group.pattern_idx}].match(path)"
+                        ),
+                        1,
+                    ),
+                    Line("if match:", 1),
+                    *holder,
+                    Line("basket['__params__'] = match.groupdict()", 2),
+                    Line(
+                        (
+                            f"return router.{route_container}"
+                            f"[{group.segments}][{route_idx}], basket"
+                        ),
+                        2,
+                    ),
+                ]
+            )
+
+        src.append(Line("raise NotFound", 1))
+        src.extend(delayed)
+
+        self.find_route_src = "".join(
+            map(str, filter(lambda x: x.render, src))
+        )
+        if do_compile:
+            try:
+                syntax_tree = ast.parse(self.find_route_src)
+
+                if do_optimize:
+                    self._optimize(syntax_tree.body[0])
+
+                if sys.version_info.major == 3 and sys.version_info.minor >= 9:
+                    # This is purely a convenience thing. Python 3.9 added this
+                    # feature, so it allows us to see exactly how the
+                    # interpreter will see the code after compiling and any
+                    # optimizing.
+                    setattr(
+                        self,
+                        "find_route_src_compiled",
+                        ast.unparse(syntax_tree),  # type: ignore
+                    )
+
+                # Sometimes there may be missing meta data, so we add it back
+                # before compiling
+                ast.fix_missing_locations(syntax_tree)
+
+                compiled_src = compile(
+                    syntax_tree,
+                    "",
+                    "exec",
+                )
+            except SyntaxError as se:
+                syntax_error = (
+                    f"Line {se.lineno}: {se.msg}\n{se.text}"
+                    f"{' '*max(0,int(se.offset or 0)-1) + '^'}"
+                )
+                raise FinalizationError(
+                    f"Cannot compile route AST:\n{self.find_route_src}"
+                    f"\n{syntax_error}"
+                )
+            ctx: t.Dict[t.Any, t.Any] = {}
+            exec(compiled_src, None, ctx)
+            self._find_route = ctx["find_route"]
+            self._matchers = ctx.get("matchers")
+
+    @property
+    def find_route(self):
+        return self._find_route
+
+    @property
+    def matchers(self):
+        return self._matchers
+
+    @property
+    def groups(self):
+        return {
+            **self.static_routes,
+            **self.dynamic_routes,
+            **self.regex_routes,
+        }
+
+    @property
+    def routes(self):
+        return tuple(
+            [route for group in self.groups.values() for route in group]
+        )
+
+    def _optimize(self, node) -> None:
+        warn(
+            "Router AST optimization is an experimental only feature. "
+            "Results may vary from unoptimized code."
+        )
+        if hasattr(node, "body"):
+            for child in node.body:
+                self._optimize(child)
+
+            # concatenate nested single if blocks
+            # EXAMPLE:
+            #      if parts[1] == "foo":
+            #          if num > 3:
+            # BECOMES:
+            #       if parts[1] == 'foo' and num > 3:
+            # Testing has shown that further recursion does not actually
+            # produce any faster results.
+            if self._is_lone_if(node) and self._is_lone_if(node.body[0]):
+                current = node.body[0]
+                nested = node.body[0].body[0]
+
+                values: t.List[t.Any] = []
+                for test in [current.test, nested.test]:
+                    if isinstance(test, ast.Compare):
+                        values.append(test)
+                    elif isinstance(test, ast.BoolOp) and isinstance(
+                        test.op, ast.And
+                    ):
+                        values.extend(test.values)
+                    else:
+                        ...
+                combined = ast.BoolOp(op=ast.And(), values=values)
+
+                current.test = combined
+                current.body = nested.body
+
+            # Look for identical successive if blocks
+            # EXAMPLE:
+            #       if num == 5:
+            #           foo1()
+            #       if num == 5:
+            #           foo2()
+            # BECOMES:
+            #       if num == 5:
+            #           foo1()
+            #           foo2()
+            if (
+                all(isinstance(child, ast.If) for child in node.body)
+                # TODO: create func to peoperly compare equality of conditions
+                # and len({child.test for child in node.body})
+                and len(node.body) > 1
+            ):
+                first, *rem = node.body
+                for item in rem:
+                    first.body.extend(item.body)
+
+                node.body = [first]
+
+        if hasattr(node, "orelse"):
+            for child in node.orelse:
+                self._optimize(child)
+
+    @staticmethod
+    def _is_lone_if(node):
+        return len(node.body) == 1 and isinstance(node.body[0], ast.If)
+
+    def _is_regex(self, path: str):
+        parts = path_to_parts(path, self.delimiter)
+
+        def requires(part):
+            if not part.startswith("<") or ":" not in part:
+                return False
+
+            _, pattern_type, *__ = part[1:-1].split(":")
+
+            return (
+                part.endswith(":path>")
+                or self.delimiter in part
+                or pattern_type not in self.regex_types
+            )
+
+        return any(requires(part) for part in parts)
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/tree.py b/.venv/lib/python3.12/site-packages/sanic_routing/tree.py
new file mode 100644
index 0000000..f912e2a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/tree.py
@@ -0,0 +1,484 @@
+import typing as t
+
+from logging import getLogger
+
+from .group import RouteGroup
+from .line import Line
+from .patterns import REGEX_PARAM_NAME, REGEX_PARAM_NAME_EXT, alpha, ext, slug
+
+
+logger = getLogger("sanic.root")
+
+
+class Node:
+    def __init__(
+        self,
+        part: str = "",
+        root: bool = False,
+        parent=None,
+        router=None,
+        param=None,
+        unquote=False,
+    ) -> None:
+        self.root = root
+        self.part = part
+        self.parent = parent
+        self.param = param
+        self._children: t.Dict[str, "Node"] = {}
+        self.children: t.Dict[str, "Node"] = {}
+        self.level = 0
+        self.base_indent = 0
+        self.offset = 0
+        self.groups: t.List[RouteGroup] = []
+        self.dynamic = False
+        self.first = False
+        self.last = False
+        self.children_basketed = False
+        self.children_param_injected = False
+        self.has_deferred = False
+        self.equality_check = False
+        self.unquote = unquote
+        self.router = router
+
+    def __str__(self) -> str:
+        internals = ", ".join(
+            f"{prop}={getattr(self, prop)}"
+            for prop in ["part", "level", "groups", "dynamic"]
+            if getattr(self, prop) or prop in ["level"]
+        )
+        return f""
+
+    def __repr__(self) -> str:
+        return str(self)
+
+    @property
+    def ident(self) -> str:
+        prefix = (
+            f"{self.parent.ident}."
+            if self.parent and not self.parent.root
+            else ""
+        )
+        return f"{prefix}{self.idx}"
+
+    @property
+    def idx(self) -> int:
+        if not self.parent:
+            return 1
+        return list(self.parent.children.keys()).index(self.part) + 1
+
+    def finalize_children(self):
+        """
+        Sort the children (if any), and set properties for easy checking
+        # they are at the beginning or end of the line.
+        """
+        self.children = {
+            k: v for k, v in sorted(self._children.items(), key=self._sorting)
+        }
+        if self.children:
+            keys = list(self.children.keys())
+            self.children[keys[0]].first = True
+            self.children[keys[-1]].last = True
+
+            for child in self.children.values():
+                child.finalize_children()
+
+    def display(self) -> None:
+        """
+        Visual display of the tree of nodes
+        """
+        logger.info(" " * 4 * self.level + str(self))
+        for child in self.children.values():
+            child.display()
+
+    def render(self) -> t.Tuple[t.List[Line], t.List[Line]]:
+        # output - code injected into the source as it is being
+        #    called/evaluated
+        # delayed - code that is injected after you do all of its children
+        #    first
+        # final - code that is injected at the very end of all rendering
+        src: t.List[Line] = []
+        delayed: t.List[Line] = []
+        final: t.List[Line] = []
+
+        if not self.root:
+            src, delayed, final = self.to_src()
+        for child in self.children.values():
+            o, f = child.render()
+            src += o
+            final += f
+        return src + delayed, final
+
+    def to_src(self) -> t.Tuple[t.List[Line], t.List[Line], t.List[Line]]:
+        siblings = self.parent.children if self.parent else {}
+        first_sibling: t.Optional[Node] = None
+
+        if not self.first:
+            first_sibling = next(iter(siblings.values()))
+
+        self.base_indent = (
+            bool(self.level >= 1 or self.first) + self.parent.base_indent
+            if self.parent
+            else 0
+        )
+
+        indent = self.base_indent
+
+        # See render() docstring for definition of these three sequences
+        delayed: t.List[Line] = []
+        final: t.List[Line] = []
+        src: t.List[Line] = []
+
+        # Some cleanup to make code easier to read
+        src.append(Line("", indent))
+        src.append(Line(f"# node={self.ident} // part={self.part}", indent))
+
+        level = self.level
+        idx = level - 1
+
+        return_bump = not self.dynamic
+
+        operation = ">"
+        conditional = "if"
+
+        # The "equality_check" is when we do a "==" operation to check
+        # that the incoming path is the same length as a particular target.
+        # Since this could take place in a few different locations, we need
+        # to be able to track if it has been set.
+        if self.groups:
+            operation = "==" if self.level == self.parent.depth else ">="
+            self.equality_check = operation == "=="
+
+        src.append(
+            Line(
+                f"{conditional} num {operation} {level}:  # CHECK 1",
+                indent,
+            )
+        )
+        indent += 1
+
+        if self.dynamic:
+            # Injects code to try casting a segment to all POTENTIAL types that
+            # the defined routes could catch in this location
+            self._inject_param_check(src, indent, idx)
+            indent += 1
+
+        else:
+            if (
+                not self.equality_check
+                and self.groups
+                and not self.first
+                and first_sibling
+            ):
+                self.equality_check = first_sibling.equality_check
+
+            # Maybe try and sneak an equality check in?
+            if_stmt = "if"
+            len_check = (
+                f" and num == {self.level}"
+                if not self.children and not self.equality_check
+                else ""
+            )
+
+            self.equality_check |= bool(len_check)
+
+            src.append(
+                Line(
+                    f'{if_stmt} parts[{idx}] == "{self.part}"{len_check}:'
+                    "  # CHECK 4",
+                    indent,
+                )
+            )
+            self.base_indent += 1
+
+        # Get ready to return some handlers
+        if self.groups:
+            return_indent = indent + return_bump
+            route_idx: t.Union[int, str] = 0
+            location = delayed
+
+            # Do any missing equality_check
+            if not self.equality_check:
+                # If if we have not done an equality check and there are
+                # children nodes, then we know there is a CHECK 1
+                # for the children that starts at the same level, and will
+                # be an exclusive conditional to what is being evaluated here.
+                # Therefore, we can use elif
+                #     example:
+                #         if num == 7:  # CHECK 1
+                #             child_node_stuff
+                #         elif num == 6:  # CHECK 5
+                #             current_node_stuff
+                conditional = "elif" if self.children else "if"
+                operation = "=="
+                location.append(
+                    Line(
+                        f"{conditional} num {operation} {level}:  # CHECK 5",
+                        return_indent,
+                    )
+                )
+                return_indent += 1
+
+            for group in sorted(self.groups, key=self._group_sorting):
+                group_bump = 0
+
+                # If the route had some requirements, let's make sure we check
+                # them in the source
+                if group.requirements:
+                    route_idx = "route_idx"
+                    self._inject_requirements(
+                        location, return_indent + group_bump, group
+                    )
+
+                # This is for any inline regex routes. It sould not include,
+                # path or path-like routes.
+                if group.regex:
+                    self._inject_regex(
+                        location, return_indent + group_bump, group
+                    )
+                    group_bump += 1
+
+                # Since routes are grouped, we need to know which to select
+                # Inside the compiled source, we keep track so we know which
+                # handler to assign this to
+                if route_idx == 0 and len(group.routes) > 1:
+                    route_idx = "route_idx"
+                    self._inject_method_check(
+                        location, return_indent + group_bump, group
+                    )
+
+                # The return.kingdom
+                self._inject_return(
+                    location, return_indent + group_bump, route_idx, group
+                )
+
+        return src, delayed, final
+
+    def add_child(self, child: "Node") -> None:
+        self._children[child.part] = child
+
+    def _inject_param_check(self, location, indent, idx):
+        """
+        Try and cast relevant path segments.
+        """
+        lines = [
+            Line("try:", indent),
+            Line(
+                f"basket['__matches__'][{idx}] = "
+                f"{self.param.cast.__name__}(parts[{idx}])",
+                indent + 1,
+            ),
+            Line("except ValueError:", indent),
+            Line("pass", indent + 1),
+            Line("else:", indent),
+        ]
+        if self.unquote and self._cast_as_str(self.param.cast):
+            lines.append(
+                Line(
+                    f"basket['__matches__'][{idx}] = "
+                    f"unquote(basket['__matches__'][{idx}])",
+                    indent + 1,
+                )
+            )
+        self.base_indent += 1
+
+        location.extend(lines)
+
+    @staticmethod
+    def _cast_as_str(cast) -> bool:
+        return_type_hint = t.get_type_hints(cast).get("return")
+        return cast in (str, ext, slug, alpha) or return_type_hint is str
+
+    @staticmethod
+    def _inject_method_check(location, indent, group):
+        """
+        Sometimes we need to check the routing methods inside the generated src
+        """
+        for i, route in enumerate(group.routes):
+            if_stmt = "if" if i == 0 else "elif"
+            location.extend(
+                [
+                    Line(
+                        f"{if_stmt} method in {route.methods}:",
+                        indent,
+                    ),
+                    Line(f"route_idx = {i}", indent + 1),
+                ]
+            )
+        location.extend(
+            [
+                Line("else:", indent),
+                Line("raise NoMethod", indent + 1),
+            ]
+        )
+
+    def _inject_return(self, location, indent, route_idx, group):
+        """
+        The return statement for the node if needed
+        """
+        routes = "regex_routes" if group.regex else "dynamic_routes"
+        route_return = "" if group.router.stacking else f"[{route_idx}]"
+        location.extend(
+            [
+                Line(f"# Return {self.ident}", indent),
+                Line(
+                    (
+                        f"return router.{routes}[{group.segments}]"
+                        f"{route_return}, basket"
+                    ),
+                    indent,
+                ),
+            ]
+        )
+
+    def _inject_requirements(self, location, indent, group):
+        """
+        Check any extra checks needed for a route. In path routing, for exampe,
+        this is used for matching vhosts.
+        """
+        for k, route in enumerate(group):
+            conditional = "if" if k == 0 else "elif"
+            location.extend(
+                [
+                    Line(
+                        (
+                            f"{conditional} extra == {route.requirements} "
+                            f"and method in {route.methods}:"
+                        ),
+                        indent,
+                    ),
+                    Line((f"route_idx = {k}"), indent + 1),
+                ]
+            )
+
+        location.extend(
+            [
+                Line(("else:"), indent),
+                Line(("raise NotFound"), indent + 1),
+            ]
+        )
+
+    def _inject_regex(self, location, indent, group):
+        """
+        For any path matching that happens in the course of the tree (anything
+        that has a path matching----or similar matching with regex
+        delimiter)
+        """
+        location.extend(
+            [
+                Line(
+                    (
+                        "match = router.matchers"
+                        f"[{group.pattern_idx}].match(path)"
+                    ),
+                    indent,
+                ),
+                Line("if match:", indent),
+                Line(
+                    "basket['__params__'] = match.groupdict()",
+                    indent + 1,
+                ),
+            ]
+        )
+
+    def _sorting(self, item) -> t.Tuple[bool, bool, int, int, int, bool, str]:
+        """
+        Primarily use to sort nodes to determine the order of the nested tree
+        """
+        key, child = item
+        type_ = 0
+        if child.dynamic:
+            type_ = child.param.priority
+
+        return (
+            bool(child.groups),
+            child.dynamic,
+            type_ * -1,
+            child.depth * -1,
+            len(child._children),
+            not bool(
+                child.groups and any(group.regex for group in child.groups)
+            ),
+            key,
+        )
+
+    def _group_sorting(self, item) -> t.Tuple[int, ...]:
+        """
+        When multiple RouteGroups terminate on the same node, we want to
+        evaluate them based upon the priority of the param matching types
+        """
+
+        def get_type(segment):
+            type_ = 0
+            if segment.startswith("<"):
+                key = segment[1:-1]
+                if ":" in key:
+                    key, param_type = key.split(":", 1)
+                    try:
+                        type_ = list(self.router.regex_types.keys()).index(
+                            param_type
+                        )
+                    except ValueError:
+                        type_ = len(list(self.router.regex_types.keys()))
+            return type_ * -1
+
+        segments = tuple(map(get_type, item.parts))
+        return segments
+
+    @property
+    def depth(self):
+        if not self._children:
+            return self.level
+        return max(child.depth for child in self._children.values())
+
+
+class Tree:
+    def __init__(self, router) -> None:
+        self.root = Node(root=True, router=router)
+        self.root.level = 0
+        self.router = router
+
+    def generate(self, groups: t.Iterable[RouteGroup]) -> None:
+        """
+        Arrange RouteGroups into hierarchical nodes and arrange them into
+        a tree
+        """
+        for group in groups:
+            current = self.root
+            current.unquote = current.unquote or group.unquote
+            for level, part in enumerate(group.parts):
+                param = None
+                dynamic = part.startswith("<")
+                if dynamic:
+                    if not REGEX_PARAM_NAME.match(
+                        part
+                    ) and not REGEX_PARAM_NAME_EXT.match(part):
+                        raise ValueError(f"Invalid declaration: {part}")
+                    part = f"__dynamic__:{group.params[level].label}"
+                    param = group.params[level]
+                if part not in current._children:
+                    child = Node(
+                        part=part,
+                        parent=current,
+                        router=self.router,
+                        param=param,
+                        unquote=current.unquote,
+                    )
+                    child.dynamic = dynamic
+                    current.add_child(child)
+                current = current._children[part]
+                current.level = level + 1
+
+            current.groups.append(group)
+
+    def display(self) -> None:
+        """
+        Debug tool to output visual of the tree
+        """
+        self.root.display()
+
+    def render(self) -> t.List[Line]:
+        o, f = self.root.render()
+        return o + f
+
+    def finalize(self):
+        self.root.finalize_children()
diff --git a/.venv/lib/python3.12/site-packages/sanic_routing/utils.py b/.venv/lib/python3.12/site-packages/sanic_routing/utils.py
new file mode 100644
index 0000000..b37d306
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_routing/utils.py
@@ -0,0 +1,97 @@
+import re
+
+from urllib.parse import quote, unquote
+
+from sanic_routing.exceptions import InvalidUsage
+
+from .patterns import REGEX_PARAM_NAME, REGEX_PARAM_NAME_EXT
+
+
+class Immutable(dict):
+    def __setitem__(self, *args):
+        raise TypeError("Cannot change immutable dict")
+
+    def __delitem__(self, *args):
+        raise TypeError("Cannot change immutable dict")
+
+
+def parse_parameter_basket(route, basket, raw_path=None):
+    params = {}
+    if basket:
+        for idx, value in basket.items():
+            for p in route.params[idx]:
+                if not raw_path or p.raw_path == raw_path:
+                    if not p.regex:
+                        raw_path = p.raw_path
+                        params[p.name] = p.cast(value)
+                        break
+                    elif p.pattern.search(value):
+                        raw_path = p.raw_path
+                        if "(" in p.pattern:
+                            groups = p.pattern.match(value)
+                            value = groups.group(1)
+                        params[p.name] = p.cast(value)
+                        break
+
+                    if raw_path:
+                        raise ValueError("Invalid parameter")
+
+                if raw_path and not params[p.name]:
+                    raise ValueError("Invalid parameter")
+
+            if route.unquote:
+                for p in route.params[idx]:
+                    if isinstance(params[p.name], str):
+                        params[p.name] = unquote(params[p.name])
+
+    if raw_path is None:
+        raise ValueError("Invalid parameter")
+    return params, raw_path
+
+
+def path_to_parts(path, delimiter="/"):
+    r"""
+    OK > /foo//bar/
+    OK > /foo/
+    OK > /foo/txt)>/
+    OK > /foo//
+    OK > /foo//txt)d>
+    NOT OK > /foo/txt)d>/
+    """
+    path = unquote(path.lstrip(delimiter))
+    delimiter = re.escape(delimiter)
+    return tuple(
+        part if part.startswith("<") else quote(part)
+        for part in re.split(rf"{delimiter}(?=[^>]*(?:<(?")
+            except AttributeError:
+                try:
+                    match = REGEX_PARAM_NAME_EXT.match(part)
+                    filename_type = ""
+                    extension_type = ""
+                    if match.group(2):
+                        filename_type = f"={match.group(2)}"
+                    if match.group(3):
+                        extension_type = f"={match.group(3)}"
+                    segment = (
+                        f"<{match.group(1)}{filename_type}:"
+                        f"ext{extension_type}>"
+                    )
+                    path.append(segment)
+                except AttributeError:
+                    raise InvalidUsage(f"Invalid declaration: {part}")
+        else:
+            path.append(part)
+    return delimiter.join(path)
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/LICENSE
new file mode 100644
index 0000000..3baabde
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Sanic Community Organization
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/METADATA
new file mode 100644
index 0000000..f92e115
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/METADATA
@@ -0,0 +1,100 @@
+Metadata-Version: 2.1
+Name: sanic-testing
+Version: 24.6.0
+Summary: Core testing clients for Sanic
+Home-page: https://github.com/sanic-org/sanic-testing/
+Author: Adam Hopkins
+Author-email: admhpkns@gmail.com
+License: MIT
+Platform: any
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: License :: OSI Approved :: MIT License
+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
+Description-Content-Type: text/markdown
+License-File: LICENSE
+Requires-Dist: httpx >=0.18
+Provides-Extra: dev
+Requires-Dist: pytest ; extra == 'dev'
+Requires-Dist: sanic >=22.12 ; extra == 'dev'
+Requires-Dist: pytest-asyncio ; extra == 'dev'
+Requires-Dist: setuptools ; (python_version > "3.11") and extra == 'dev'
+
+# Sanic Core Test
+
+This package is meant to be the core testing utility and clients for testing Sanic applications. It is mainly derived from `sanic.testing` which has (or will be) removed from the main Sanic repository in the future.
+
+[Documentation](https://sanicframework.org/en/plugins/sanic-testing/getting-started.html)
+
+## Getting Started
+
+    pip install sanic-testing
+
+The package is meant to create an almost seemless transition. Therefore, after loading the package, it will attach itself to your Sanic instance and insert test clients.
+
+```python
+from sanic import Sanic
+from sanic_testing import TestManager
+
+sanic_app = Sanic(__name__)
+TestManager(sanic_app)
+```
+
+This will provide access to both the sync (`sanic.test_client`) and async (`sanic.asgi_client`) clients. Both of these clients are also available directly on the `TestManager` instance.
+
+## Writing a sync test
+
+Testing should be pretty much the same as when the test client was inside Sanic core. The difference is just that you need to run `TestManager`.
+
+```python
+import pytest
+
+@pytest.fixture
+def app():
+    sanic_app = Sanic(__name__)
+    TestManager(sanic_app)
+
+    @sanic_app.get("/")
+    def basic(request):
+        return response.text("foo")
+
+    return sanic_app
+
+def test_basic_test_client(app):
+    request, response = app.test_client.get("/")
+
+    assert response.body == b"foo"
+    assert response.status == 200
+```
+
+## Writing an async test
+
+Testing of an async method is best done with `pytest-asyncio` installed. Again, the following test should look familiar to anyone that has used `asgi_client` in the Sanic core package before.
+
+The main benefit of using the `asgi_client` is that it is able to reach inside your application, and execute your handlers without ever having to stand up a server or make a network call.
+
+```python
+import pytest
+
+@pytest.fixture
+def app():
+    sanic_app = Sanic(__name__)
+    TestManager(sanic_app)
+
+    @sanic_app.get("/")
+    def basic(request):
+        return response.text("foo")
+
+    return sanic_app
+
+@pytest.mark.asyncio
+async def test_basic_asgi_client(app):
+    request, response = await app.asgi_client.get("/")
+
+    assert response.body == b"foo"
+    assert response.status == 200
+```
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/RECORD
new file mode 100644
index 0000000..17a3941
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/RECORD
@@ -0,0 +1,17 @@
+sanic_testing-24.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+sanic_testing-24.6.0.dist-info/LICENSE,sha256=ljxq2vUrShwTdHmC287CFLjxv9hsdQmSQjO-wrPapnE,1085
+sanic_testing-24.6.0.dist-info/METADATA,sha256=dgDjAgm7DV_x8gaLEELgifpiJ3z8lt8vunb-QaAqlQ8,3167
+sanic_testing-24.6.0.dist-info/RECORD,,
+sanic_testing-24.6.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sanic_testing-24.6.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
+sanic_testing-24.6.0.dist-info/top_level.txt,sha256=Ta3ak4Fh7-aEThXqp5_aYLywnLIaJr-aSU3jAx5lMkw,14
+sanic_testing/__init__.py,sha256=C878kG5Ess32Kc0WyPES2negLC5cT454K_dtGTMsL0g,97
+sanic_testing/__pycache__/__init__.cpython-312.pyc,,
+sanic_testing/__pycache__/manager.cpython-312.pyc,,
+sanic_testing/__pycache__/reusable.cpython-312.pyc,,
+sanic_testing/__pycache__/testing.cpython-312.pyc,,
+sanic_testing/__pycache__/websocket.cpython-312.pyc,,
+sanic_testing/manager.py,sha256=7h0BbrLjcPt3Ju2ifhWF54g004Om-GZtJSUk0UI--5I,348
+sanic_testing/reusable.py,sha256=_qPvPYCmpx1oN_w_mZ-VzlJlVZXuINT4tCQ1nneBN0Q,7499
+sanic_testing/testing.py,sha256=4LfPpJABex4XAmVczQJZy6KN3Yh9s5TwQtn5i0DwsnU,15154
+sanic_testing/websocket.py,sha256=UegUQJyeGOD2cw7l8CSUGuPJCYr0cJLrjlS4gmAcYoc,1352
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/WHEEL
new file mode 100644
index 0000000..edf4ec7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (70.1.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/top_level.txt
new file mode 100644
index 0000000..6dbd238
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing-24.6.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+sanic_testing
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing/__init__.py b/.venv/lib/python3.12/site-packages/sanic_testing/__init__.py
new file mode 100644
index 0000000..ee9b186
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing/__init__.py
@@ -0,0 +1,4 @@
+from sanic_testing.manager import TestManager
+
+__version__ = "24.6.0"
+__all__ = ("TestManager",)
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing/manager.py b/.venv/lib/python3.12/site-packages/sanic_testing/manager.py
new file mode 100644
index 0000000..e170665
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing/manager.py
@@ -0,0 +1,12 @@
+from sanic import Sanic  # type: ignore
+
+from sanic_testing.testing import SanicASGITestClient, SanicTestClient
+
+
+class TestManager:
+    __test__ = False
+
+    def __init__(self, app: Sanic) -> None:
+        self.test_client = SanicTestClient(app)
+        self.asgi_client = SanicASGITestClient(app)
+        app._test_manager = self  # type: ignore
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing/reusable.py b/.venv/lib/python3.12/site-packages/sanic_testing/reusable.py
new file mode 100644
index 0000000..5690dc4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing/reusable.py
@@ -0,0 +1,237 @@
+import asyncio
+import typing
+from functools import partial
+from random import randint
+from typing import Any, Dict, List, Optional, Tuple
+
+import httpx
+from sanic import Sanic
+from sanic.application.state import ApplicationServerInfo
+from sanic.log import logger
+from sanic.request import Request
+
+from sanic_testing.websocket import websocket_proxy
+
+from .testing import HOST, PORT, TestingResponse
+
+
+class ReusableClient:
+    def __init__(
+        self,
+        app: Sanic,
+        host=HOST,
+        port=PORT,
+        loop=None,
+        server_kwargs=None,
+        client_kwargs=None,
+    ):
+        if not loop:
+            loop = asyncio.new_event_loop()
+            asyncio.set_event_loop(loop)
+        server_kwargs = server_kwargs or {}
+        client_kwargs = client_kwargs or {}
+
+        Sanic.test_mode = True
+        self.app = app
+        self.host = host
+        self.port = port or randint(5000, 65000)
+        self._loop = loop
+        self.debug = False
+        self._server = None
+        self.app.state.server_info.append(
+            ApplicationServerInfo(
+                settings={
+                    "version": "1.1",
+                    "ssl": None,
+                    "unix": None,
+                    "sock": None,
+                    "loop": None,
+                    "host": self.host,
+                    "port": self.port,
+                }
+            )
+        )
+
+        self._session = httpx.AsyncClient(verify=False, **client_kwargs)
+        self._server_co = self.app.create_server(
+            host=self.host,
+            debug=self.debug,
+            port=self.port,
+            return_asyncio_server=True,
+            **server_kwargs,
+        )
+
+    def __enter__(self):
+        self.run()
+        return self
+
+    def __exit__(self, *_):
+        self.stop()
+
+    def run(self):
+        self._loop._stopping = False
+        self.app.router.reset()
+        self.app.signal_router.reset()
+        self._run(self.app._startup())
+        self._run(self.app._server_event("init", "before", loop=self._loop))
+        self._server = self._run(self._server_co)
+        self._run(self.app._server_event("init", "after", loop=self._loop))
+
+    def stop(self):
+        self._run(
+            self.app._server_event("shutdown", "before", loop=self._loop)
+        )
+        if self._session:
+            self._run(self._session.aclose())
+            self._session = None
+
+        if self._server:
+            self._server.close()
+            self._run(self._server.wait_closed())
+            self._server = None
+
+        self._run(self.app._server_event("shutdown", "after", loop=self._loop))
+
+    def _sanic_endpoint_test(
+        self,
+        method: str = "get",
+        uri: str = "/",
+        gather_request: bool = True,
+        debug: bool = False,
+        server_kwargs: Optional[Dict[str, Any]] = None,
+        host: Optional[str] = None,
+        port: Optional[int] = None,
+        allow_none: bool = False,
+        *request_args,
+        **request_kwargs,
+    ) -> Tuple[Optional[Request], Optional[TestingResponse]]:
+        request_data: Dict[str, Request] = {}
+        exceptions: List[Exception] = []
+
+        host = host or self.host
+        port = port or self.port
+
+        if gather_request:
+            _collect_request = partial(self._collect_request, request_data)
+            self.app.request_middleware.appendleft(_collect_request)  # type: ignore  # noqa
+
+            for route in self.app.router.routes:
+                if _collect_request not in route.extra.request_middleware:
+                    route.extra.request_middleware.appendleft(_collect_request)
+
+        if uri.startswith(
+            ("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
+        ):
+            url = uri
+        else:
+            uri = uri if uri.startswith("/") else f"/{uri}"
+            scheme = "ws" if method == "websocket" else "http"
+            url = f"{scheme}://{host}:{port}{uri}"
+
+        if exceptions:
+            raise ValueError(f"Exception during request: {exceptions}")
+
+        response = self._run(
+            self._local_request(method, url, *request_args, **request_kwargs)
+        )
+
+        try:
+            self.app.request_middleware.remove(_collect_request)  # type: ignore  # noqa
+        except BaseException:  # noqa
+            pass
+
+        try:
+            request = request_data.get("request") if gather_request else None
+            if response is None:
+                if not allow_none:
+                    raise ValueError(
+                        "No response returned to Sanic Test Client."
+                    )
+            return request, response
+        except BaseException:  # noqa
+            if not allow_none:
+                raise ValueError(
+                    "Request and response object expected, "
+                    f"got ({request}, {response})"
+                )
+
+        return None, None
+
+    async def _local_request(self, method, url, *args, **kwargs):
+        raw_cookies = kwargs.pop("raw_cookies", None)
+
+        if method == "websocket":
+            return await websocket_proxy(url, *args, **kwargs)
+        else:
+            session = self._session
+
+            try:
+                if method == "request":
+                    args = tuple([url] + list(args))
+                    url = kwargs.pop("http_method", "GET").upper()
+                response = await getattr(session, method.lower())(
+                    url, *args, **kwargs
+                )
+            except httpx.HTTPError as e:
+                if hasattr(e, "response"):
+                    response = getattr(e, "response")
+                else:
+                    logger.error(
+                        f"{method.upper()} {url} received no response!",
+                        exc_info=True,
+                    )
+                    return None
+
+            response.__class__ = TestingResponse
+
+            if raw_cookies:
+                response.raw_cookies = {}
+
+                for cookie in response.cookies.jar:
+                    response.raw_cookies[cookie.name] = cookie
+
+            return response
+
+    def _run(self, coro):
+        if not self._loop:
+            raise RuntimeError("Test client has no loop")
+        return self._loop.run_until_complete(coro)
+
+    @staticmethod
+    def _collect_request(data, request):
+        data["request"] = request
+
+    def request(self, *args, **kwargs):
+        return self._sanic_endpoint_test("request", *args, **kwargs)
+
+    def get(self, *args, **kwargs):
+        return self._sanic_endpoint_test("get", *args, **kwargs)
+
+    def post(self, *args, **kwargs):
+        return self._sanic_endpoint_test("post", *args, **kwargs)
+
+    def put(self, *args, **kwargs):
+        return self._sanic_endpoint_test("put", *args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        return self._sanic_endpoint_test("delete", *args, **kwargs)
+
+    def patch(self, *args, **kwargs):
+        return self._sanic_endpoint_test("patch", *args, **kwargs)
+
+    def options(self, *args, **kwargs):
+        return self._sanic_endpoint_test("options", *args, **kwargs)
+
+    def head(self, *args, **kwargs):
+        return self._sanic_endpoint_test("head", *args, **kwargs)
+
+    def websocket(
+        self,
+        *args,
+        mimic: typing.Optional[
+            typing.Callable[..., typing.Coroutine[None, None, typing.Any]]
+        ] = None,
+        **kwargs,
+    ):
+        kwargs["mimic"] = mimic
+        return self._sanic_endpoint_test("websocket", *args, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing/testing.py b/.venv/lib/python3.12/site-packages/sanic_testing/testing.py
new file mode 100644
index 0000000..c78d40e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing/testing.py
@@ -0,0 +1,476 @@
+import typing
+from functools import partial
+from ipaddress import IPv6Address, ip_address
+from json import JSONDecodeError
+from socket import AF_INET6, SOCK_STREAM, socket
+from string import ascii_lowercase
+
+import httpx
+from sanic import Sanic  # type: ignore
+from sanic.asgi import ASGIApp  # type: ignore
+from sanic.exceptions import MethodNotSupported, ServerError  # type: ignore
+from sanic.log import logger  # type: ignore
+from sanic.request import Request  # type: ignore
+from sanic.response import text  # type: ignore
+
+from sanic_testing.websocket import websocket_proxy
+
+ASGI_HOST = "mockserver"
+ASGI_PORT = 1234
+ASGI_BASE_URL = f"http://{ASGI_HOST}:{ASGI_PORT}"
+HOST = "127.0.0.1"
+PORT = None
+
+
+httpx_version = tuple(
+    map(int, httpx.__version__.strip(ascii_lowercase).split("."))
+)
+
+
+class TestingResponse(httpx.Response):
+    @property
+    def status(self):
+        return self.status_code
+
+    @property
+    def body(self):
+        return self.content
+
+    @property
+    def content_type(self):
+        return self.headers.get("content-type")
+
+    @property
+    def json(self):
+        if getattr(self, "_json", None):
+            return self._json
+        try:
+            self._json = super().json()
+        except (JSONDecodeError, UnicodeDecodeError):
+            self._json = None
+
+        return self._json
+
+
+def _blank(*_, **__):
+    ...
+
+
+class SanicTestClient:
+    def __init__(
+        self, app: Sanic, port: typing.Optional[int] = PORT, host: str = HOST
+    ) -> None:
+        """Use port=None to bind to a random port"""
+        Sanic.test_mode = True
+        self.app = app
+        self.port = port
+        self.host = host
+        self._do_request = _blank
+        app.after_server_start(self._run_request)
+
+    def _run_request(self, *args, **kwargs):
+        return self._do_request(*args, **kwargs)
+
+    @classmethod
+    def _start_test_mode(cls, sanic, *args, **kwargs):
+        Sanic.test_mode = True
+
+    @classmethod
+    def _end_test_mode(cls, sanic, *args, **kwargs):
+        Sanic.test_mode = False
+
+    def get_new_session(self, **kwargs) -> httpx.AsyncClient:
+        return httpx.AsyncClient(verify=False, **kwargs)
+
+    async def _local_request(self, method: str, url: str, *args, **kwargs):
+        logger.info(url)
+        raw_cookies = kwargs.pop("raw_cookies", None)
+        session_kwargs = kwargs.pop("session_kwargs", {})
+        if httpx_version >= (0, 20) and method != "websocket":
+            kwargs["follow_redirects"] = True
+            allow_redirects = kwargs.pop("allow_redirects", None)
+            if allow_redirects is not None:
+                kwargs["follow_redirects"] = allow_redirects
+
+        if method == "websocket":
+            return await websocket_proxy(url, *args, **kwargs)
+        else:
+            async with self.get_new_session(**session_kwargs) as session:
+                try:
+                    if method == "request":
+                        args = tuple([url] + list(args))
+                        url = kwargs.pop("http_method", "GET").upper()
+                    response = await getattr(session, method.lower())(
+                        url, *args, **kwargs
+                    )
+                except httpx.HTTPError as e:
+                    if hasattr(e, "response"):
+                        response = getattr(e, "response")
+                    else:
+                        logger.error(
+                            f"{method.upper()} {url} received no response!",
+                            exc_info=True,
+                        )
+                        return None
+
+                response.__class__ = TestingResponse
+
+                if raw_cookies:
+                    response.raw_cookies = {}
+
+                    for cookie in response.cookies.jar:
+                        response.raw_cookies[cookie.name] = cookie
+
+            return response
+
+    @classmethod
+    def _collect_request(cls, results, request):
+        if results[0] is None:
+            results[0] = request
+
+    async def _collect_response(
+        self,
+        method,
+        url,
+        exceptions,
+        results,
+        sanic,
+        loop,
+        **request_kwargs,
+    ):
+        try:
+            response = await self._local_request(method, url, **request_kwargs)
+            results[-1] = response
+            if method == "websocket":
+                await response.ws.close()
+        except Exception as e:
+            logger.exception("Exception")
+            exceptions.append(e)
+        finally:
+            self.app.stop()
+
+    async def _error_handler(self, request, exception):
+        if request.method in ["HEAD", "PATCH", "PUT", "DELETE"]:
+            return text("", exception.status_code, headers=exception.headers)
+        else:
+            return self.app.error_handler.default(request, exception)
+
+    def _sanic_endpoint_test(
+        self,
+        method: str = "get",
+        uri: str = "/",
+        gather_request: bool = True,
+        debug: bool = False,
+        server_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
+        host: typing.Optional[str] = None,
+        allow_none: bool = False,
+        *request_args,
+        **request_kwargs,
+    ) -> typing.Tuple[
+        typing.Optional[Request], typing.Optional[TestingResponse]
+    ]:
+        results = [None, None]
+        exceptions: typing.List[Exception] = []
+
+        server_kwargs = server_kwargs or {"auto_reload": False}
+        _collect_request = partial(self._collect_request, results)
+
+        self.app.router.reset()
+        self.app.signal_router.reset()
+
+        if gather_request:
+            self.app.request_middleware.appendleft(_collect_request)  # type: ignore  # noqa
+
+        try:
+            self.app.exception(MethodNotSupported)(self._error_handler)
+        except ServerError:
+            ...
+
+        if self.port:
+            server_kwargs = dict(
+                host=host or self.host,
+                port=self.port,
+                **server_kwargs,
+            )
+            host, port = host or self.host, self.port
+        else:
+            bind = host or self.host
+            ip = ip_address(bind)
+            if isinstance(ip, IPv6Address):
+                sock = socket(AF_INET6, SOCK_STREAM)
+                port = ASGI_PORT
+            else:
+                sock = socket()
+                port = 0
+            sock.bind((bind, port))
+            server_kwargs = dict(sock=sock, **server_kwargs)
+
+            if isinstance(ip, IPv6Address):
+                host, port, _, _ = sock.getsockname()
+                host = f"[{host}]"
+            else:
+                host, port = sock.getsockname()
+            self.port = port
+
+        if uri.startswith(
+            ("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
+        ):
+            url = uri
+        else:
+            uri = uri if uri.startswith("/") else f"/{uri}"
+            scheme = "ws" if method == "websocket" else "http"
+            url = f"{scheme}://{host}:{port}{uri}"
+        # Tests construct URLs using PORT = None, which means random port not
+        # known until this function is called, so fix that here
+        url = url.replace(":None/", f":{port}/")
+
+        self._do_request = partial(
+            self._collect_response,
+            method,
+            url,
+            exceptions,
+            results,
+            **request_kwargs,
+        )
+
+        self.app.run(  # type: ignore
+            debug=debug,
+            single_process=True,
+            **server_kwargs,
+        )
+
+        if exceptions:
+            raise ValueError(f"Exception during request: {exceptions}")
+
+        if gather_request:
+            try:
+                self.app.request_middleware.remove(_collect_request)  # type: ignore  # noqa
+            except BaseException:  # noqa
+                pass
+
+            try:
+                request, response = results
+                if response is None:
+                    if not allow_none:
+                        raise ValueError(
+                            "No response returned to Sanic Test Client."
+                        )
+                return request, response
+            except BaseException:  # noqa
+                if not allow_none:
+                    raise ValueError(
+                        "Request and response object expected, "
+                        f"got ({results})"
+                    )
+                return None, None
+        else:
+            try:
+                if results[-1] is None:
+                    if not allow_none:
+                        raise ValueError(
+                            "No response returned to Sanic Test Client."
+                        )
+                return None, results[-1]
+            except BaseException:  # noqa
+                if not allow_none:
+                    raise ValueError(
+                        f"Request object expected, got ({results})"
+                    )
+                return None, None
+
+    def request(self, *args, **kwargs):
+        return self._sanic_endpoint_test("request", *args, **kwargs)
+
+    def get(self, *args, **kwargs):
+        return self._sanic_endpoint_test("get", *args, **kwargs)
+
+    def post(self, *args, **kwargs):
+        return self._sanic_endpoint_test("post", *args, **kwargs)
+
+    def put(self, *args, **kwargs):
+        return self._sanic_endpoint_test("put", *args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        return self._sanic_endpoint_test("delete", *args, **kwargs)
+
+    def patch(self, *args, **kwargs):
+        return self._sanic_endpoint_test("patch", *args, **kwargs)
+
+    def options(self, *args, **kwargs):
+        return self._sanic_endpoint_test("options", *args, **kwargs)
+
+    def head(self, *args, **kwargs):
+        return self._sanic_endpoint_test("head", *args, **kwargs)
+
+    def websocket(
+        self,
+        *args,
+        mimic: typing.Optional[
+            typing.Callable[..., typing.Coroutine[None, None, typing.Any]]
+        ] = None,
+        **kwargs,
+    ):
+        kwargs["mimic"] = mimic
+        return self._sanic_endpoint_test("websocket", *args, **kwargs)
+
+
+class TestASGIApp(ASGIApp):
+    async def __call__(self):
+        await super().__call__()
+        return self.request
+
+
+async def app_call_with_return(self, scope, receive, send):
+    asgi_app = await TestASGIApp.create(self, scope, receive, send)
+    return await asgi_app()
+
+
+class SanicASGITestClient(httpx.AsyncClient):
+    def __init__(
+        self,
+        app: Sanic,
+        base_url: str = ASGI_BASE_URL,
+        suppress_exceptions: bool = False,
+    ) -> None:
+        Sanic.test_mode = True
+
+        app.__class__.__call__ = app_call_with_return  # type: ignore
+        app.asgi = True
+
+        self.sanic_app = app
+
+        transport = httpx.ASGITransport(app=app, client=(ASGI_HOST, ASGI_PORT))
+
+        super().__init__(transport=transport, base_url=base_url)
+
+        self.gather_request = True
+        self.last_request = None
+
+    def _collect_request(self, request):
+        if self.gather_request:
+            self.last_request = request
+        else:
+            self.last_request = None
+
+    @classmethod
+    def _start_test_mode(cls, sanic, *args, **kwargs):
+        Sanic.test_mode = True
+
+    @classmethod
+    def _end_test_mode(cls, sanic, *args, **kwargs):
+        Sanic.test_mode = False
+
+    async def request(  # type: ignore
+        self, method, url, gather_request=True, *args, **kwargs
+    ) -> typing.Tuple[
+        typing.Optional[Request], typing.Optional[TestingResponse]
+    ]:
+        self.sanic_app.router.reset()
+        self.sanic_app.signal_router.reset()
+        await self.sanic_app._startup()  # type: ignore
+        await self.sanic_app._server_event("init", "before")
+        await self.sanic_app._server_event("init", "after")
+        for route in self.sanic_app.router.routes:
+            if self._collect_request not in route.extra.request_middleware:
+                route.extra.request_middleware.appendleft(
+                    self._collect_request
+                )
+
+        if not url.startswith(
+            ("http:", "https:", "ftp:", "ftps://", "//", "ws:", "wss:")
+        ):
+            url = url if url.startswith("/") else f"/{url}"
+            scheme = "ws" if method == "websocket" else "http"
+            url = f"{scheme}://{ASGI_HOST}:{ASGI_PORT}{url}"
+
+        if self._collect_request not in self.sanic_app.request_middleware:
+            self.sanic_app.request_middleware.appendleft(
+                self._collect_request  # type: ignore
+            )
+
+        self.gather_request = gather_request
+        response = await super().request(method, url, *args, **kwargs)
+
+        await self.sanic_app._server_event("shutdown", "before")
+        await self.sanic_app._server_event("shutdown", "after")
+
+        response.__class__ = TestingResponse
+
+        if gather_request:
+            return self.last_request, response  # type: ignore
+        return None, response  # type: ignore
+
+    @classmethod
+    async def _ws_receive(cls):
+        return {}
+
+    @classmethod
+    async def _ws_send(cls, message):
+        pass
+
+    async def websocket(
+        self,
+        uri,
+        subprotocols=None,
+        *args,
+        mimic: typing.Optional[
+            typing.Callable[..., typing.Coroutine[None, None, typing.Any]]
+        ] = None,
+        **kwargs,
+    ):
+        if mimic:
+            raise RuntimeError(
+                "SanicASGITestClient does not currently support the mimic "
+                "keyword argument. Please use SanicTestClient instead."
+            )
+        scheme = "ws"
+        path = uri
+        root_path = f"{scheme}://{ASGI_HOST}:{ASGI_PORT}"
+
+        headers = kwargs.get("headers", {})
+        headers.setdefault("connection", "upgrade")
+        headers.setdefault("sec-websocket-key", "testserver==")
+        headers.setdefault("sec-websocket-version", "13")
+        if subprotocols is not None:
+            headers.setdefault(
+                "sec-websocket-protocol", ", ".join(subprotocols)
+            )
+
+        scope = {
+            "type": "websocket",
+            "asgi": {"version": "3.0"},
+            "http_version": "1.1",
+            "headers": [map(lambda y: y.encode(), x) for x in headers.items()],
+            "scheme": scheme,
+            "root_path": root_path,
+            "path": path,
+            "raw_path": path.encode(),
+            "query_string": b"",
+            "subprotocols": subprotocols,
+        }
+
+        self.sanic_app.router.reset()
+        self.sanic_app.signal_router.reset()
+        await self.sanic_app._startup()
+
+        await self.sanic_app(scope, self._ws_receive, self._ws_send)
+
+        return None, {"opened": True}
+
+    def __getstate__(self):
+        # Cookies cannot be pickled, because they contain a ThreadLock
+        try:
+            del self._cookies
+        except AttributeError:
+            pass
+        return self.__dict__
+
+    def __setstate__(self, d):
+        try:
+            del d["_cookies"]
+        except LookupError:
+            pass
+        self.__dict__.update(d)
+        # Need to create a new CookieJar when unpickling,
+        # because it was killed on Pickle
+        self._cookies = httpx.Cookies()
diff --git a/.venv/lib/python3.12/site-packages/sanic_testing/websocket.py b/.venv/lib/python3.12/site-packages/sanic_testing/websocket.py
new file mode 100644
index 0000000..526565a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sanic_testing/websocket.py
@@ -0,0 +1,50 @@
+import typing
+
+from websockets.exceptions import ConnectionClosedOK
+from websockets.legacy.client import connect
+
+
+class WebsocketProxy:
+    def __init__(self, ws):
+        self.ws = ws
+        self.opened = True
+        self.client_received: typing.List[str] = []
+        self.client_sent: typing.List[str] = []
+
+    @property
+    def server_received(self):
+        return self.client_sent
+
+    @property
+    def server_sent(self):
+        return self.client_received
+
+
+async def websocket_proxy(url, *args, **kwargs) -> WebsocketProxy:
+    mimic = kwargs.pop("mimic", None)
+    async with connect(url, *args, **kwargs) as websocket:
+        ws_proxy = WebsocketProxy(websocket)
+
+        if mimic:
+            do_send = websocket.send
+            do_recv = websocket.recv
+
+            async def send(data):
+                ws_proxy.client_sent.append(data)
+                await do_send(data)
+
+            async def recv():
+                message = await do_recv()
+                ws_proxy.client_received.append(message)
+                return message
+
+            websocket.send = send  # type: ignore
+            websocket.recv = recv  # type: ignore
+
+            try:
+                await mimic(websocket)
+            except ConnectionClosedOK:
+                pass
+            else:
+                await websocket.send("")
+    return ws_proxy
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/METADATA
new file mode 100644
index 0000000..6a6b6fc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/METADATA
@@ -0,0 +1,140 @@
+Metadata-Version: 2.4
+Name: setuptools
+Version: 82.0.1
+Summary: Most extensible Python build backend with support for C/C++ extension modules
+Author-email: Python Packaging Authority 
+License-Expression: MIT
+Project-URL: Source, https://github.com/pypa/setuptools
+Project-URL: Documentation, https://setuptools.pypa.io/
+Project-URL: Changelog, https://setuptools.pypa.io/en/stable/history.html
+Keywords: CPAN PyPI distutils eggs package management
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: virtualenv>=13.0.0; extra == "test"
+Requires-Dist: wheel>=0.44.0; extra == "test"
+Requires-Dist: pip>=19.1; extra == "test"
+Requires-Dist: packaging>=24.2; extra == "test"
+Requires-Dist: jaraco.envs>=2.2; extra == "test"
+Requires-Dist: pytest-xdist>=3; extra == "test"
+Requires-Dist: jaraco.path>=3.7.2; extra == "test"
+Requires-Dist: build[virtualenv]>=1.0.3; extra == "test"
+Requires-Dist: filelock>=3.4.0; extra == "test"
+Requires-Dist: ini2toml[lite]>=0.14; extra == "test"
+Requires-Dist: tomli-w>=1.0.0; extra == "test"
+Requires-Dist: pytest-timeout; extra == "test"
+Requires-Dist: pytest-perf; sys_platform != "cygwin" and extra == "test"
+Requires-Dist: jaraco.develop>=7.21; (python_version >= "3.9" and sys_platform != "cygwin") and extra == "test"
+Requires-Dist: pytest-home>=0.5; extra == "test"
+Requires-Dist: pytest-subprocess; extra == "test"
+Requires-Dist: pyproject-hooks!=1.1; extra == "test"
+Requires-Dist: jaraco.test>=5.5; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
+Requires-Dist: pygments-github-lexers==0.0.5; extra == "doc"
+Requires-Dist: sphinx-favicon; extra == "doc"
+Requires-Dist: sphinx-inline-tabs; extra == "doc"
+Requires-Dist: sphinx-reredirects; extra == "doc"
+Requires-Dist: sphinxcontrib-towncrier; extra == "doc"
+Requires-Dist: sphinx-notfound-page<2,>=1; extra == "doc"
+Requires-Dist: pyproject-hooks!=1.1; extra == "doc"
+Requires-Dist: towncrier<24.7; extra == "doc"
+Provides-Extra: ssl
+Provides-Extra: certs
+Provides-Extra: core
+Requires-Dist: packaging>=24.2; extra == "core"
+Requires-Dist: more_itertools>=8.8; extra == "core"
+Requires-Dist: jaraco.text>=3.7; extra == "core"
+Requires-Dist: importlib_metadata>=6; python_version < "3.10" and extra == "core"
+Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "core"
+Requires-Dist: wheel>=0.43.0; extra == "core"
+Requires-Dist: jaraco.functools>=4; extra == "core"
+Requires-Dist: more_itertools; extra == "core"
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
+Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
+Requires-Dist: ruff>=0.13.0; sys_platform != "cygwin" and extra == "check"
+Provides-Extra: cover
+Requires-Dist: pytest-cov; extra == "cover"
+Provides-Extra: enabler
+Requires-Dist: pytest-enabler>=2.2; extra == "enabler"
+Provides-Extra: type
+Requires-Dist: pytest-mypy; extra == "type"
+Requires-Dist: mypy==1.18.*; extra == "type"
+Requires-Dist: importlib_metadata>=7.0.2; python_version < "3.10" and extra == "type"
+Requires-Dist: jaraco.develop>=7.21; sys_platform != "cygwin" and extra == "type"
+Dynamic: license-file
+
+.. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg
+   :target: https://pypi.org/project/setuptools
+
+.. |py-version| image:: https://img.shields.io/pypi/pyversions/setuptools.svg
+
+.. |test-badge| image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. |ruff-badge| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+   :target: https://github.com/astral-sh/ruff
+   :alt: Ruff
+
+.. |docs-badge| image:: https://img.shields.io/readthedocs/setuptools/latest.svg
+   :target: https://setuptools.pypa.io
+
+.. |skeleton-badge| image:: https://img.shields.io/badge/skeleton-2025-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. |codecov-badge| image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white
+   :target: https://codecov.io/gh/pypa/setuptools
+
+.. |tidelift-badge| image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat
+   :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme
+
+.. |discord-badge| image:: https://img.shields.io/discord/803025117553754132
+   :target: https://discord.com/channels/803025117553754132/815945031150993468
+   :alt: Discord
+
+|pypi-version| |py-version| |test-badge| |ruff-badge| |docs-badge| |skeleton-badge| |codecov-badge| |discord-badge|
+
+See the `Quickstart `_
+and the `User's Guide `_ for
+instructions on how to use Setuptools.
+
+Questions and comments should be directed to `GitHub Discussions
+`_.
+Bug reports and especially tested patches may be
+submitted directly to the `bug tracker
+`_.
+
+
+Code of Conduct
+===============
+
+Everyone interacting in the setuptools project's codebases, issue trackers,
+chat rooms, and fora is expected to follow the
+`PSF Code of Conduct `_.
+
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more `_.
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/RECORD
new file mode 100644
index 0000000..132bf2e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/RECORD
@@ -0,0 +1,759 @@
+_distutils_hack/__init__.py,sha256=34HmvLo07j45Uvd2VR-2aRQ7lJD91sTK6zJgn5fphbQ,6755
+_distutils_hack/__pycache__/__init__.cpython-312.pyc,,
+_distutils_hack/__pycache__/override.cpython-312.pyc,,
+_distutils_hack/override.py,sha256=Eu_s-NF6VIZ4Cqd0tbbA5wtWky2IZPNd8et6GLt1mzo,44
+distutils-precedence.pth,sha256=JjjOniUA5XKl4N5_rtZmHrVp0baW_LoHsN0iPaX10iQ,151
+setuptools-82.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools-82.0.1.dist-info/METADATA,sha256=dWg_QOESHrzLvKTWea4tv4oL4LqRUx3yhbKKvpJ6Q_g,6527
+setuptools-82.0.1.dist-info/RECORD,,
+setuptools-82.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
+setuptools-82.0.1.dist-info/entry_points.txt,sha256=zkgthpf_Fa9NVE9p6FKT3Xk9DR1faAcRU4coggsV7jA,2449
+setuptools-82.0.1.dist-info/licenses/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools-82.0.1.dist-info/top_level.txt,sha256=Wsln-wPBdc8NXLNXsnCRyTXUhGi0mUw_l0ZVThaim-I,27
+setuptools/__init__.py,sha256=-UtTG9pdeEZkYiIFT69RckMCqxUdVILjj24g3qeJ_rc,9521
+setuptools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/__pycache__/_core_metadata.cpython-312.pyc,,
+setuptools/__pycache__/_discovery.cpython-312.pyc,,
+setuptools/__pycache__/_entry_points.cpython-312.pyc,,
+setuptools/__pycache__/_imp.cpython-312.pyc,,
+setuptools/__pycache__/_importlib.cpython-312.pyc,,
+setuptools/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/__pycache__/_normalization.cpython-312.pyc,,
+setuptools/__pycache__/_path.cpython-312.pyc,,
+setuptools/__pycache__/_reqs.cpython-312.pyc,,
+setuptools/__pycache__/_scripts.cpython-312.pyc,,
+setuptools/__pycache__/_shutil.cpython-312.pyc,,
+setuptools/__pycache__/_static.cpython-312.pyc,,
+setuptools/__pycache__/archive_util.cpython-312.pyc,,
+setuptools/__pycache__/build_meta.cpython-312.pyc,,
+setuptools/__pycache__/depends.cpython-312.pyc,,
+setuptools/__pycache__/discovery.cpython-312.pyc,,
+setuptools/__pycache__/dist.cpython-312.pyc,,
+setuptools/__pycache__/errors.cpython-312.pyc,,
+setuptools/__pycache__/extension.cpython-312.pyc,,
+setuptools/__pycache__/glob.cpython-312.pyc,,
+setuptools/__pycache__/installer.cpython-312.pyc,,
+setuptools/__pycache__/launch.cpython-312.pyc,,
+setuptools/__pycache__/logging.cpython-312.pyc,,
+setuptools/__pycache__/modified.cpython-312.pyc,,
+setuptools/__pycache__/monkey.cpython-312.pyc,,
+setuptools/__pycache__/msvc.cpython-312.pyc,,
+setuptools/__pycache__/namespaces.cpython-312.pyc,,
+setuptools/__pycache__/unicode_utils.cpython-312.pyc,,
+setuptools/__pycache__/version.cpython-312.pyc,,
+setuptools/__pycache__/warnings.cpython-312.pyc,,
+setuptools/__pycache__/wheel.cpython-312.pyc,,
+setuptools/__pycache__/windows_support.cpython-312.pyc,,
+setuptools/_core_metadata.py,sha256=T7Tjp-WSoN881adev3R1wzXCPnkDHqbC2MgylN1yjS8,11978
+setuptools/_discovery.py,sha256=HxEpgYQ8zUaLOOSp8JIA4y2Mdvn9ysVxspPT-ppfzM4,836
+setuptools/_distutils/__init__.py,sha256=xGYuhWwLG07J0Q49BVnEjPy6wyDcd6veJMDJX7ljlyM,359
+setuptools/_distutils/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_log.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_macos_compat.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_modified.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_msvccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/archive_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/ccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/cmd.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/core.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/cygwinccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/debug.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dep_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dir_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dist.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/errors.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/extension.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/fancy_getopt.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/file_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/filelist.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/log.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/spawn.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/sysconfig.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/text_file.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/unixccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/version.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/versionpredicate.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/zosccompiler.cpython-312.pyc,,
+setuptools/_distutils/_log.py,sha256=i-lNTTcXS8TmWITJ6DODGvtW5z5tMattJQ76h8rZxQU,42
+setuptools/_distutils/_macos_compat.py,sha256=JzUGhF4E5yIITHbUaPobZEWjGHdrrcNV63z86S4RjBc,239
+setuptools/_distutils/_modified.py,sha256=RF1n1CexyDYV3lvGbeXS0s-XCJVboDOIUbA8wEQqYTY,3211
+setuptools/_distutils/_msvccompiler.py,sha256=9PSfSHxvJnHnQL6Sqz4Xcz7iaBIT62p6BheQzGsSlwo,335
+setuptools/_distutils/archive_util.py,sha256=dDZjw4BwhNIyt20hHlrIely4WSiN20HVTGdaPRKGI0o,8484
+setuptools/_distutils/ccompiler.py,sha256=FKVjqzGJ7c-FtouNjhLiaMPm5LKMZHHAruXf8LU216c,524
+setuptools/_distutils/cmd.py,sha256=lzqM-3rwYJNUhJiCYOv04aeUPenym2vyOndFPEOdZgo,21344
+setuptools/_distutils/command/__init__.py,sha256=GfFAzbBqk1qxSH4BdaKioKS4hRRnD44BAmwEN85C4u8,386
+setuptools/_distutils/command/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/_framework_compat.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_clib.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_ext.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_py.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_scripts.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/check.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/clean.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/config.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_data.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_egg_info.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_headers.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_lib.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_scripts.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/sdist.cpython-312.pyc,,
+setuptools/_distutils/command/_framework_compat.py,sha256=0iZdSJYzGRWCCvzRDKE-R0-_yaAYvFMd1ylXb2eYXug,1609
+setuptools/_distutils/command/bdist.py,sha256=jWtk61R7fWNUUNxJV0thTZzU5n80L3Ay1waSiP9kiLA,5854
+setuptools/_distutils/command/bdist_dumb.py,sha256=FF0FpuC3IgFpOjJ1ub3hGU5ERFMBnJOiUjrAI-u4auw,4609
+setuptools/_distutils/command/bdist_rpm.py,sha256=WRRB3k65qubgZO6zwiRMpaEEuG391mju_iJDRJTPvT8,21672
+setuptools/_distutils/command/build.py,sha256=SpHlagf0iNaKVyIhxDfhPFZ8X1-LAWOCQACy-yt2K0w,5923
+setuptools/_distutils/command/build_clib.py,sha256=6u-R8qjmWr6FPECuzDoAyVl1xRBtuEmXJZ2cz2Zk2zg,7733
+setuptools/_distutils/command/build_ext.py,sha256=vnInlUvOVbCS7eCpp3ThqTY4NkQ7WBHcmsROGn8upKw,32676
+setuptools/_distutils/command/build_py.py,sha256=BAJI_X9QlfiTyokU3ZgfDnsy3Xn6aWdxl2T_lToagbk,16606
+setuptools/_distutils/command/build_scripts.py,sha256=UKhGKyXLjGCAQoQVfP2t-DnlMaUVjcXWac6UCs6GWMI,4833
+setuptools/_distutils/command/check.py,sha256=yoNe2MPY4JcTM7rwoIQdfZ75q5Ri058I2coi-Gq9CjM,4946
+setuptools/_distutils/command/clean.py,sha256=UWkfAMmpOZGl0_PjiZJFdyLV2yzGnoimygF8fka907I,2551
+setuptools/_distutils/command/config.py,sha256=zE7F8mSQavwiZwNZj8WtrF6T92lt-K1dUNq9rYIQpgc,12206
+setuptools/_distutils/command/install.py,sha256=-JenB-mua4hc2RI_-W8F9PnP_J-OaFO7E0PJGKxLo1o,30072
+setuptools/_distutils/command/install_data.py,sha256=GzBlUWWKubTYJlP-L0auUriq9cL-5RKOcoyHTttKj0Q,2875
+setuptools/_distutils/command/install_egg_info.py,sha256=0tJ0AqFr8P1bf5wFH9eO7ssx1G3_SLhO9bCbWN-h0f4,2809
+setuptools/_distutils/command/install_headers.py,sha256=5ciKCj8c3XKsYNKdkdMvnypaUCKcoWCDeeZij3fD-Z4,1272
+setuptools/_distutils/command/install_lib.py,sha256=3VQc5P06dv-1QVl8G-raVpTD-bMlPTj1c3NE7adJiyk,8512
+setuptools/_distutils/command/install_scripts.py,sha256=CTzga_-kEjYWguL-Dg_GP83Jax6uw2VnFFwlR8XoAls,1877
+setuptools/_distutils/command/sdist.py,sha256=XGEO5UMznLzPT-F3ftbryqkMsMWLKd15tjObiZJsm8k,19107
+setuptools/_distutils/compat/__init__.py,sha256=J20aXGjJ86Rg41xFLIWlcWCgZ9edMdJ9vvdNEQ87vPQ,522
+setuptools/_distutils/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/compat/__pycache__/numpy.cpython-312.pyc,,
+setuptools/_distutils/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/_distutils/compat/numpy.py,sha256=UFgneZw9w97g4c-yGoAIOyLxUOWQ-fPRIhhfMs7_Ouc,167
+setuptools/_distutils/compat/py39.py,sha256=hOsD6lwZLqZoMnacNJ3P6nUA-LJQhEpVtYTzVH0o96M,1964
+setuptools/_distutils/compilers/C/__pycache__/base.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/__pycache__/cygwin.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/__pycache__/errors.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/__pycache__/msvc.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/__pycache__/unix.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/__pycache__/zos.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/base.py,sha256=0b6_nCJiQtmUkdxL6tFKxVx8vv8AkLZOfRkIS2zGXCc,54550
+setuptools/_distutils/compilers/C/cygwin.py,sha256=QFW4oBPylt6-uR4mdrXbFtGMQQ_mXJ4gB0qPUklbMWQ,11802
+setuptools/_distutils/compilers/C/errors.py,sha256=sKOVzJajMUmNdfywo9UM_QQGsKFcclDhtI5TlCiXMLc,573
+setuptools/_distutils/compilers/C/msvc.py,sha256=c-12nYp2XpD-fUvxfgd559rWZqmmRx9f9mWFwWTsoP4,21386
+setuptools/_distutils/compilers/C/tests/__pycache__/test_base.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/tests/__pycache__/test_cygwin.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/tests/__pycache__/test_mingw.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/tests/__pycache__/test_msvc.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/tests/__pycache__/test_unix.cpython-312.pyc,,
+setuptools/_distutils/compilers/C/tests/test_base.py,sha256=rdhHc56bhXtm5NnN9BSHwr6c69UqzMItZQzlw2AsdMc,2706
+setuptools/_distutils/compilers/C/tests/test_cygwin.py,sha256=UgV2VgUXj3VulcbDc0UBWfEyJDx42tgSwS4LzHix3mY,2701
+setuptools/_distutils/compilers/C/tests/test_mingw.py,sha256=hCmwyywISpRoyOySbFHBL4TprWRV0mUWDKmOLO8XBXE,1900
+setuptools/_distutils/compilers/C/tests/test_msvc.py,sha256=DlGjmZ1mBSMXIgmlu80BKc7V-EJOZuYucwJwFh5dn28,4151
+setuptools/_distutils/compilers/C/tests/test_unix.py,sha256=AyadWw1fR-UeDl2TvIbYBzOJVHkpE_oRRQ3JTJWqaEA,14642
+setuptools/_distutils/compilers/C/unix.py,sha256=YH-y9g_pjBFjaJyHJQkDEBQ7q4D20I2-cWJNdgw-Yho,16531
+setuptools/_distutils/compilers/C/zos.py,sha256=WnGA_PR51_FP89wKbl85_2rcLA28BIKbeG5rPZ7JUig,6568
+setuptools/_distutils/core.py,sha256=GEHKaFC48T3o-_SmH4864GvKyx1IgbVC6ISIPVlx7a4,9364
+setuptools/_distutils/cygwinccompiler.py,sha256=mG_cU8SVZ4amD_VtF5vH6BXP0-kghGsDPbDSXrQ963c,594
+setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139
+setuptools/_distutils/dep_util.py,sha256=xN75p6ZpHhMiHEc-rpL2XilJQynHnDNiafHteaZ4tjU,349
+setuptools/_distutils/dir_util.py,sha256=1i-puqc3g8sTggEPX6NSuqNtGSNEdSy-Qw9qQfC3PSo,6864
+setuptools/_distutils/dist.py,sha256=kPQ81tTJdZikPayJ7OIunkFAh-m8-YqjbpQYQSuMTeI,55699
+setuptools/_distutils/errors.py,sha256=PPE2oDRh5y9Q1beKK9rhdvDaCzQhi4HCXs4KcqfqgZY,3092
+setuptools/_distutils/extension.py,sha256=Foyu4gULcPqm1_U9zrYYHxNk4NqglXv1rbsOk_QrSds,11155
+setuptools/_distutils/fancy_getopt.py,sha256=PjdO-bWCW0imV_UN-MGEw9R2GP2OiE8pHjITgmTAY3Q,17895
+setuptools/_distutils/file_util.py,sha256=O3hNtKxcs4jhCdOtGN23wIrLSvMwoRmMV2caEeXbvdU,7811
+setuptools/_distutils/filelist.py,sha256=MBeSRJmPcKmDv8ooZgSU4BiQPZ0Khwv8l_jhD50XycI,15337
+setuptools/_distutils/log.py,sha256=VyBs5j7z4-K6XTEEBThUc9HyMpoPLGtQpERqbz5ylww,1200
+setuptools/_distutils/spawn.py,sha256=sPnZ3Q3gsBzNBB2zsbfFxzkHJ-AveB6lyK9YqBHQiIw,3963
+setuptools/_distutils/sysconfig.py,sha256=KeI8OHbMuEzHJ8Q0cBez9KZny8iRy6Z6Y0AkMz1jlsU,19728
+setuptools/_distutils/tests/__init__.py,sha256=j-IoPZEtQv3EOPuqNTwalr6GLyRjzCC-OOaNvZzmHsI,1485
+setuptools/_distutils/tests/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/support.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_archive_util.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_bdist.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_bdist_dumb.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_bdist_rpm.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_build.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_build_clib.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_build_ext.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_build_py.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_build_scripts.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_check.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_clean.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_cmd.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_config_cmd.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_core.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_dir_util.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_dist.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_extension.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_file_util.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_filelist.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_install.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_install_data.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_install_headers.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_install_lib.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_install_scripts.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_log.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_modified.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_sdist.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_spawn.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_sysconfig.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_text_file.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_util.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_version.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/test_versionpredicate.cpython-312.pyc,,
+setuptools/_distutils/tests/__pycache__/unix_compat.cpython-312.pyc,,
+setuptools/_distutils/tests/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_distutils/tests/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/tests/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/_distutils/tests/compat/py39.py,sha256=t0GBTM-30jX-9zCfkwlNBFtzzabemx6065mJ0d9_VRw,1026
+setuptools/_distutils/tests/support.py,sha256=tjsYsyxvpTK4NrkCseh2ujvDIGV0Mf_b5SI5fP2T0yM,4099
+setuptools/_distutils/tests/test_archive_util.py,sha256=VaXLjV8kup-Z6rIVXCBW7u21pubcAH_R3hEtSmS81Xs,11441
+setuptools/_distutils/tests/test_bdist.py,sha256=xNHxUsLlHsZQRwkzLb_iSD24s-9Mk-NX2ffBWwOyPyc,1396
+setuptools/_distutils/tests/test_bdist_dumb.py,sha256=QF05MHNhPOdZyh88Xpw8KsO64s7pRFkl8KL-RoV4XK0,2247
+setuptools/_distutils/tests/test_bdist_rpm.py,sha256=Hdm-pwWgyaoGdGbEcGZa8cRhGU45y8gHK8umOanTjik,3932
+setuptools/_distutils/tests/test_build.py,sha256=JJY5XpOZco25ZY0pstxl-iI8mHsWP0ujf5o8aOtuZYY,1742
+setuptools/_distutils/tests/test_build_clib.py,sha256=Mo1ZFb4C1VXBYOGvnallwN7YCnTtr24akLDO8Zi4CsY,4331
+setuptools/_distutils/tests/test_build_ext.py,sha256=QFO9qYVhWWdJu17HXc4x9RMnLZlhk0lAHi9HVppbuX4,22545
+setuptools/_distutils/tests/test_build_py.py,sha256=NsfmRrojOHBXNMqWR_mp5g4PLTgjhD7iZFUffGZFIdw,6882
+setuptools/_distutils/tests/test_build_scripts.py,sha256=cD-FRy-oX55sXRX5Ez5xQCaeHrWajyKc4Xuwv2fe48w,2880
+setuptools/_distutils/tests/test_check.py,sha256=hHSV07qf7YoSxGsTbbsUQ9tssZz5RRNdbrY1s2SwaFI,6226
+setuptools/_distutils/tests/test_clean.py,sha256=hPH6jfIpGFUrvWbF1txkiNVSNaAxt2wq5XjV499zO4E,1240
+setuptools/_distutils/tests/test_cmd.py,sha256=bgRB79mitoOKR1OiyZHnCogvGxt3pWkxeTqIC04lQWQ,3254
+setuptools/_distutils/tests/test_config_cmd.py,sha256=Zs6WX0IfxDvmuC19XzuVNnYCnTr9Y-hl73TAmDSBN4Y,2664
+setuptools/_distutils/tests/test_core.py,sha256=L7XKVAxa-MGoAZeANopnuK9fRKneYhkSQpgw8XQvcF8,3829
+setuptools/_distutils/tests/test_dir_util.py,sha256=E84lC-k4riVUwURyWaQ0Jqx2ui2-io-0RuJa3M7qkJs,4500
+setuptools/_distutils/tests/test_dist.py,sha256=a6wlc5fQJd5qQ6HOndzcupNhjTxvj6-_JLtpuYvaP1M,18793
+setuptools/_distutils/tests/test_extension.py,sha256=-YejLgZCuycFrOBd64pVH0JvwMc9NwhzHvQxvvjXHqk,3670
+setuptools/_distutils/tests/test_file_util.py,sha256=livjnl3FkilQlrB2rFdFQq9nvjEVZHynNya0bfzv_b4,3522
+setuptools/_distutils/tests/test_filelist.py,sha256=rJwkqCUfkGDgWlD22TozsT8ycbupMHB8DXqThzwT1T4,10766
+setuptools/_distutils/tests/test_install.py,sha256=TfCB0ykhIxydIC2Q4SuTAZzSHvteMHgrBL9whoSgK9Q,8618
+setuptools/_distutils/tests/test_install_data.py,sha256=vKq3K97k0hBAnOg38nmwEdf7cEDVr9rTVyCeJolgb4A,2464
+setuptools/_distutils/tests/test_install_headers.py,sha256=PVAYpo_tYl980Qf64DPOmmSvyefIHdU06f7VsJeZykE,936
+setuptools/_distutils/tests/test_install_lib.py,sha256=qri6Rl-maNTQrNDV8DbeXNl0hjsfRIKiI4rfZLrmWBI,3612
+setuptools/_distutils/tests/test_install_scripts.py,sha256=KE3v0cDkFW-90IOID-OmZZGM2mhy-ZkEuuW7UXS2SHw,1600
+setuptools/_distutils/tests/test_log.py,sha256=isFtOufloCyEdZaQOV7cVUr46GwtdVMj43mGBB5XH7k,323
+setuptools/_distutils/tests/test_modified.py,sha256=h1--bOWmtJo1bpVV6uRhdnS9br71CBiNDM1MDwSGpug,4221
+setuptools/_distutils/tests/test_sdist.py,sha256=cfzUhlCA418-1vH9ta3IBs26c_jUBbkJoFOK5GnAyNk,15062
+setuptools/_distutils/tests/test_spawn.py,sha256=eS8w9D7bTxyFLSyRahJWeuh8Kc1F8RWWiY_dSG5B5Bc,4803
+setuptools/_distutils/tests/test_sysconfig.py,sha256=lxM8LsUi1TomjDV4HoYK8u5nUoBkeNL60Uq8PY1DcwU,11986
+setuptools/_distutils/tests/test_text_file.py,sha256=WQWSB5AfdBDZaMA8BFgipJPnsJb_2SKMfL90fSkRVtw,3460
+setuptools/_distutils/tests/test_util.py,sha256=H9zlZ4z4Vh4TfjNYDBsxP7wguQLpxCfJYyOcm1yZU3c,7988
+setuptools/_distutils/tests/test_version.py,sha256=ahfg_mP8wRy1sgwY-_Px5hrjgf6_upTIpnCgpR4yWRk,2750
+setuptools/_distutils/tests/test_versionpredicate.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_distutils/tests/unix_compat.py,sha256=z-op6C2iVdX1aq5BIBR7cqOxijKE97alNwJqHNdLpoI,386
+setuptools/_distutils/text_file.py,sha256=z4dkOJBr9Bo2LG0TNqm8sD63LEEaKSSP0J0bWBrFG3c,12101
+setuptools/_distutils/unixccompiler.py,sha256=1bXJWH4fiu_A2WfriHzf88xjllQTXnnjUkZdRKs9cWU,212
+setuptools/_distutils/util.py,sha256=iGaXP9t2-SQo7vaDR7bKUoEbrGO5QN69xTEtS5eihvk,17534
+setuptools/_distutils/version.py,sha256=vImT5-ECXkQ21oKL0XYFiTqK6NyM09cpzBNoA_34CQU,12619
+setuptools/_distutils/versionpredicate.py,sha256=qBWQ6wTj12ODytoTmIydefIY2jb4uY1sdbgbuLn-IJM,5205
+setuptools/_distutils/zosccompiler.py,sha256=svdiXZ2kdcwKrJKfhUhib03y8gz7aGZKukXH3I7YkBc,58
+setuptools/_entry_points.py,sha256=10TjbzfGdqWGH06lQuPPGDDci-OnXoIzrfpIWba5AZw,2468
+setuptools/_imp.py,sha256=YY1EjZEN-0zYci1cxO10B_adAEOr7i8eK8JoCc9Ierc,2435
+setuptools/_importlib.py,sha256=aKIjcK0HKXNz2D-XTrxaixGn_juTkONwmu3dcheMOF0,223
+setuptools/_itertools.py,sha256=jWRfsIrpC7myooz3hDURj9GtvpswZeKXg2HakmEhNjo,657
+setuptools/_normalization.py,sha256=1H0YXdCuVkUcNX2zQqDyqa-4eWC8VI5vWjGGsbj7n8I,5798
+setuptools/_path.py,sha256=2Bv1q9_Hrd4oizKwcH3pv_05YVR6meovQE6ZtyD45yI,2976
+setuptools/_reqs.py,sha256=RRX-qYsz_fy6K66XchCHcIszK3bSAtU6aO1s3ZaLV14,1380
+setuptools/_scripts.py,sha256=5TrIWDVOuO3cRcfzcZAUBKPRH7K4svQRdQLZKKiD1bQ,11247
+setuptools/_shutil.py,sha256=IQQ1gcPX4X_wPilYGJGxChyMCqG43VOejoQZTIrCTY8,1578
+setuptools/_static.py,sha256=GTR79gESF1_JaK4trLkpDrEuCeEtPlwQW0MRv7VNQX4,4855
+setuptools/_vendor/.lock,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE,sha256=reeNBJgtaZctREqOFKlPh6IzTdOFXMgDSOqOJAqg3y0,7634
+setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA,sha256=OADZuR3O6iBlpu1ieTgzYul6w4uOVrk0P0BO5TGGAJk,15006
+setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD,sha256=K-5gcsvOxjkMVxB8jfywQikYqY7NtaLMNiGH8T1C6W8,1072
+setuptools/_vendor/autocommand-2.2.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt,sha256=AzfhgKKS8EdAwWUTSF8mgeVQbXOY9kokHB6kSqwwqu0,12
+setuptools/_vendor/autocommand/__init__.py,sha256=zko5Rnvolvb-UXjCx_2ArPTGBWwUK5QY4LIQIKYR7As,1037
+setuptools/_vendor/autocommand/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/autocommand/__pycache__/autoasync.cpython-312.pyc,,
+setuptools/_vendor/autocommand/__pycache__/autocommand.cpython-312.pyc,,
+setuptools/_vendor/autocommand/__pycache__/automain.cpython-312.pyc,,
+setuptools/_vendor/autocommand/__pycache__/autoparse.cpython-312.pyc,,
+setuptools/_vendor/autocommand/__pycache__/errors.cpython-312.pyc,,
+setuptools/_vendor/autocommand/autoasync.py,sha256=AMdyrxNS4pqWJfP_xuoOcImOHWD-qT7x06wmKN1Vp-U,5680
+setuptools/_vendor/autocommand/autocommand.py,sha256=hmkEmQ72HtL55gnURVjDOnsfYlGd5lLXbvT4KG496Qw,2505
+setuptools/_vendor/autocommand/automain.py,sha256=A2b8i754Mxc_DjU9WFr6vqYDWlhz0cn8miu8d8EsxV8,2076
+setuptools/_vendor/autocommand/autoparse.py,sha256=WVWmZJPcbzUKXP40raQw_0HD8qPJ2V9VG1eFFmmnFxw,11642
+setuptools/_vendor/autocommand/errors.py,sha256=7aa3roh9Herd6nIKpQHNWEslWE8oq7GiHYVUuRqORnA,886
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA,sha256=ghXFTq132dxaEIolxr3HK1mZqm9iyUmaRANZQSr6WlE,2020
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD,sha256=D2nbcZtUIg1qSt_4v7BKyYr_6j3ItUBUoaeHoXVn9NE,1056
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt,sha256=cGjaLMOoBR1FK0ApojtzWVmViTtJ7JGIK_HwXiEsvtU,10
+setuptools/_vendor/backports/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
+setuptools/_vendor/backports/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile/__init__.py,sha256=Pwf2qUIfB0SolJPCKcx3vz3UEu_aids4g4sAfxy94qg,108491
+setuptools/_vendor/backports/tarfile/__main__.py,sha256=Yw2oGT1afrz2eBskzdPYL8ReB_3liApmhFkN2EbDmc4,59
+setuptools/_vendor/backports/tarfile/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile/__pycache__/__main__.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/backports/tarfile/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile/compat/__pycache__/py38.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile/compat/py38.py,sha256=iYkyt_gvWjLzGUTJD9TuTfMMjOk-ersXZmRlvQYN2qE,568
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/METADATA,sha256=o-OLnuQyYonUhkcE8w4pnudp4jCc6fSnXw3hpQrQo1Y,4670
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/RECORD,sha256=Uqa47g3hXPf9mWJfQ7l80uOUKshvFgVOqQ3kjmbyk1U,1868
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE,sha256=RYUC4S2Xu_ZEOGBqIARKqF6wX7CoqAe7NdvsJT_R_AQ,10278
+setuptools/_vendor/importlib_metadata-8.7.1.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
+setuptools/_vendor/importlib_metadata/__init__.py,sha256=u7Ew4-UkpzNY-ka6k-WRkDhQZS1akkLMfWs2eEnUmGo,37734
+setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_collections.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_compat.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_functools.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_text.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_typing.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/diagnose.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/_adapters.py,sha256=r5i8XLrKT6xmrpoREZhZrfczOYDmrVZeJBW5u0HzIGU,3797
+setuptools/_vendor/importlib_metadata/_collections.py,sha256=CxAhzlF3g1rwu_fMiB53JtRQiUFh0RgiMpoOvmK_ocg,760
+setuptools/_vendor/importlib_metadata/_compat.py,sha256=VC5ZDLlT-BcshauCShdFJvMNLntJJfZzNK1meGa-enw,1313
+setuptools/_vendor/importlib_metadata/_functools.py,sha256=0pA2OoiVK6wnsGq8HvVIzgdkvLiZ0nfnfw7IsndjoHk,3510
+setuptools/_vendor/importlib_metadata/_itertools.py,sha256=nMvp9SfHAQ_JYwK4L2i64lr3GRXGlYlikGTVzWbys_E,5351
+setuptools/_vendor/importlib_metadata/_meta.py,sha256=EtHyiJ5kGzWFDfKyQ2XQp6Vu113CeadKW1Vf6aGc1B4,1765
+setuptools/_vendor/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+setuptools/_vendor/importlib_metadata/_typing.py,sha256=EQKhhsEgz_Sa-FnePI-faC72rNOOQwopjA1i5pG8FDU,367
+setuptools/_vendor/importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_metadata/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/compat/__pycache__/py311.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
+setuptools/_vendor/importlib_metadata/compat/py39.py,sha256=J3W7PUVRPNYMmcvT12RF8ndBU9e8_T0Ac4U87Bsrq70,1187
+setuptools/_vendor/importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
+setuptools/_vendor/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/METADATA,sha256=XC_QkBLJVPE5sQYkl41TNaZUw0AUzQb29GbKaD28nFY,3731
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/RECORD,sha256=Y7k2wwjQ_L4TkOgkVnzAB1NsMqhJrbAieOlfHUxzbDE,1157
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
+setuptools/_vendor/jaraco.text-4.0.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco/context/__init__.py,sha256=br1ydYGo1Xr_Pu1anuEdd-QrjUiz_EY5L_5E4C03L4w,9809
+setuptools/_vendor/jaraco/context/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/jaraco/context/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/functools/__init__.py,sha256=ZJx9cMs2Nvk2xGUl8OjVGkpjdOaNlSzJrN4dGglgX2g,18599
+setuptools/_vendor/jaraco/functools/__init__.pyi,sha256=K4DcbnYIHE5QlMxqf9-cVp-WhycrhuTao4J7O7TMq4Y,3907
+setuptools/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/jaraco/functools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335
+setuptools/_vendor/jaraco/text/__init__.py,sha256=lazNYXo8IhOR1bFigLAyGiiQao6jtO3KGWh8bZZPx3c,16762
+setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/__pycache__/layouts.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/__pycache__/show-newlines.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/__pycache__/strip-prefix.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/__pycache__/to-dvorak.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/__pycache__/to-qwerty.cpython-312.pyc,,
+setuptools/_vendor/jaraco/text/layouts.py,sha256=HTC8aSTLZ7uXipyOXapRMC158juecjK6RVwitfmZ9_w,643
+setuptools/_vendor/jaraco/text/show-newlines.py,sha256=jT0vp4gLhG20hX2lTB-zKo_i3NgKzj79yRAdz4eMzIM,903
+setuptools/_vendor/jaraco/text/strip-prefix.py,sha256=NfVXV8JVNo6nqcuYASfMV7_y4Eo8zMQqlCOGvAnRIVw,412
+setuptools/_vendor/jaraco/text/to-dvorak.py,sha256=36nPPsiifwv6RfpAb--3zpgbIx8ohnnI1aR29IJTO9s,118
+setuptools/_vendor/jaraco/text/to-qwerty.py,sha256=IQoFY9v7vLTEybcput4KBYm_5GR35pmtgZ_xyrmdTgI,118
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/METADATA,sha256=BDXr_FIFXFqZdO0gwXG2RUOD6vnbsVCIFLp62XxZ1xI,4270
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/RECORD,sha256=RZnYds60K37vA6o54jXE6-q2rqJAnb5jRJaFI5PR-Dc,777
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE,sha256=l1WhhRlmbl8PTK49qtPXASvK5IpgCzEjfXXp_hNOZoM,1076
+setuptools/_vendor/jaraco_context-6.1.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/METADATA,sha256=LnnajcNGmSSr46yLIqP-tWkqeb-fR7vIa2U11hhkGEk,2960
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/RECORD,sha256=uq_S1tlMG95FPkyAWsBVEdxTmG4KY7AvZ07w94i-wUY,882
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076
+setuptools/_vendor/jaraco_functools-4.4.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/more_itertools-10.8.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/more_itertools-10.8.0.dist-info/METADATA,sha256=arNRUUWr5YsGfwh8hnYxz0z11lP-2BuWQu4SCGw5BLg,39413
+setuptools/_vendor/more_itertools-10.8.0.dist-info/RECORD,sha256=ntGxNMCqg3IvNumfORiqlhDOOgpRTXk7u3SjaNRBoB0,1095
+setuptools/_vendor/more_itertools-10.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/more_itertools-10.8.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
+setuptools/_vendor/more_itertools/__init__.py,sha256=5F7E_zpoGcEBW_T_3WE0WYYt8j-gJodIuiBcOJxrOv8,149
+setuptools/_vendor/more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/__pycache__/more.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/more.py,sha256=mNPKKu5UI7lRL460vgm0QTCWFiGMVCMosSPxVSdibos,163690
+setuptools/_vendor/more_itertools/more.pyi,sha256=fpEgNX3O66wY5cnT-s5VYDKNUpAcaCyU3iP84It3OOM,27119
+setuptools/_vendor/more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/more_itertools/recipes.py,sha256=Ma-kuBNZDFhaQDbIJgRmnrG86WzaupbOyUV3v8je3xw,41811
+setuptools/_vendor/more_itertools/recipes.pyi,sha256=LNRwN-OL3nkMfQAqx-PPc1fBaetUObb_Z6mdePyzh1c,6226
+setuptools/_vendor/packaging-26.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/packaging-26.0.dist-info/METADATA,sha256=M2K7fWom2iliuo2qpHhc0LrKwhq6kIoRlcyPWVgKJlo,3309
+setuptools/_vendor/packaging-26.0.dist-info/RECORD,sha256=9QUDMzqulReAfS-02B_CfLYKpkRmb1AXaBJibWIkzl0,2113
+setuptools/_vendor/packaging-26.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/packaging-26.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+setuptools/_vendor/packaging/__init__.py,sha256=y4lVbpeBzCGk-IPDw5BGBZ_b0P3ukEEJZAbGYc6Ey8c,494
+setuptools/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_parser.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/markers.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/metadata.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/pylock.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/tags.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/utils.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/version.cpython-312.pyc,,
+setuptools/_vendor/packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211
+setuptools/_vendor/packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559
+setuptools/_vendor/packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707
+setuptools/_vendor/packaging/_parser.py,sha256=U_DajsEx2VoC_F46fSVV3hDKNCWoQYkPkasO3dld0ig,10518
+setuptools/_vendor/packaging/_structures.py,sha256=Hn49Ta8zV9Wo8GiCL8Nl2ARZY983Un3pruZGVNldPwE,1514
+setuptools/_vendor/packaging/_tokenizer.py,sha256=M8EwNIdXeL9NMFuFrQtiOKwjka_xFx8KjRQnfE8O_z8,5421
+setuptools/_vendor/packaging/licenses/__init__.py,sha256=TwXLHZCXwSgdFwRLPxW602T6mSieunSFHM6fp8pgW78,5819
+setuptools/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc,,
+setuptools/_vendor/packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122
+setuptools/_vendor/packaging/markers.py,sha256=ZX-cLvW1S3cZcEc0fHI4z7zSx5U2T19yMpDP_mE-CYw,12771
+setuptools/_vendor/packaging/metadata.py,sha256=CWVZpN_HfoYMSSDuCP7igOvGgqA9AOmpW8f3qTisfnc,39360
+setuptools/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/packaging/pylock.py,sha256=-R1uNfJ4PaLto7Mg62YsGOHgvskuiIEqPwxOywl42Jk,22537
+setuptools/_vendor/packaging/requirements.py,sha256=PMCAWD8aNMnVD-6uZMedhBuAVX2573eZ4yPBLXmz04I,2870
+setuptools/_vendor/packaging/specifiers.py,sha256=EPNPimY_zFivthv1vdjZYz5IqkKGsnKR2yKh-EVyvZw,40797
+setuptools/_vendor/packaging/tags.py,sha256=cXLV1pJD3UtJlDg7Wz3zrfdQhRZqr8jumSAKKAAd2xE,22856
+setuptools/_vendor/packaging/utils.py,sha256=N4c6oZzFJy6klTZ3AnkNz7sSkJesuFWPp68LA3B5dAo,5040
+setuptools/_vendor/packaging/version.py,sha256=7XWlL2IDYLwDYC0ht6cFEhapLwLWbmyo4rb7sEFj0x8,23272
+setuptools/_vendor/platformdirs-4.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/platformdirs-4.4.0.dist-info/METADATA,sha256=u8UhbV9Md7-8VyJyZNUuZrzN5xzPeedeGmBG0CnTAiM,12831
+setuptools/_vendor/platformdirs-4.4.0.dist-info/RECORD,sha256=PQ0vHMAYWTxNi6ojlbrwscHGRbvYwYzQZPSctOTJXqA,1218
+setuptools/_vendor/platformdirs-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/platformdirs-4.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
+setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
+setuptools/_vendor/platformdirs/__init__.py,sha256=iORRy6_lZ9tXLvO0W6fJPn8QV7F532ivl-f2WGmabBc,22284
+setuptools/_vendor/platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493
+setuptools/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/android.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/api.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/version.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc,,
+setuptools/_vendor/platformdirs/android.py,sha256=r0DshVBf-RO1jXJGX8C4Til7F1XWt-bkdWMgmvEiaYg,9013
+setuptools/_vendor/platformdirs/api.py,sha256=wPHOlwOsfz2oqQZ6A2FcCu5kEAj-JondzoNOHYFQ0h8,9281
+setuptools/_vendor/platformdirs/macos.py,sha256=0XoOgin1NK7Qki7iskD-oS8xKxw6bXgoKEgdqpCRAFQ,6322
+setuptools/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/platformdirs/unix.py,sha256=WZmkUA--L3JNRGmz32s35YfoD3ica6xKIPdCV_HhLcs,10458
+setuptools/_vendor/platformdirs/version.py,sha256=i31fi3nNO19D2FdSx8aldD7IFLSqm2YrAo6SmkV0FLM,704
+setuptools/_vendor/platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
+setuptools/_vendor/tomli-2.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/tomli-2.4.0.dist-info/METADATA,sha256=9awKH4-6kItGRs1lUwnpGq2Wm2eHYWrFccpGKjgy_84,10567
+setuptools/_vendor/tomli-2.4.0.dist-info/RECORD,sha256=IlQwzpVkDo1Pzbk82uL8ONaFrsAW0OKT1fuZppWIb-0,822
+setuptools/_vendor/tomli-2.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/tomli-2.4.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+setuptools/_vendor/tomli/__init__.py,sha256=ahtDjGJA2M_wWVvGpzx4YJtWxrWBx6qE-GH5-UYoECA,314
+setuptools/_vendor/tomli/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_parser.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_re.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_types.cpython-312.pyc,,
+setuptools/_vendor/tomli/_parser.py,sha256=txeATLE3zHyZ-ushXtYfrZ3LoIs7JzQF2W2KL1gwJPg,25958
+setuptools/_vendor/tomli/_re.py,sha256=oSNZ_ilFI6chEuQ01YRSoUydBQr_okF_mSdHTkFmv90,3396
+setuptools/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
+setuptools/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+setuptools/_vendor/wheel-0.46.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/wheel-0.46.3.dist-info/METADATA,sha256=IpEKqXyonLzCCgGeJ_4xNgt5KaS9ZsoNMQ-ZpE9szTU,2410
+setuptools/_vendor/wheel-0.46.3.dist-info/RECORD,sha256=sCl8OtoXuHJ-Fd3W0eOMipCpl_d5_Pl4mW9v7GLstwI,1734
+setuptools/_vendor/wheel-0.46.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/wheel-0.46.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+setuptools/_vendor/wheel-0.46.3.dist-info/entry_points.txt,sha256=JJdtSAGTvMLbIkTVZUAMvGKO39FtWfCVF8mp_NH6e4g,110
+setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
+setuptools/_vendor/wheel/__init__.py,sha256=UweKvhe4SyP7zFyDoYo8BOuwTA6q3-_WpMmY2NNO54c,59
+setuptools/_vendor/wheel/__main__.py,sha256=_83wl9tyGU2cHiqfudpubGHdRL5uonPXnzeznznkxzs,512
+setuptools/_vendor/wheel/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/__main__.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/_bdist_wheel.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/_metadata.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/_setuptools_logging.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/bdist_wheel.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/macosx_libfile.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/metadata.cpython-312.pyc,,
+setuptools/_vendor/wheel/__pycache__/wheelfile.cpython-312.pyc,,
+setuptools/_vendor/wheel/_bdist_wheel.py,sha256=bpmNa7_s-CYFkVgXf9ENAYTiJ01XBhRW4pxH1T8XYsI,21729
+setuptools/_vendor/wheel/_commands/__init__.py,sha256=fCRAQZNDyj2JLrufdgPsBlaRS_t_j_aBUMpXj09KZ4E,4432
+setuptools/_vendor/wheel/_commands/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/wheel/_commands/__pycache__/convert.cpython-312.pyc,,
+setuptools/_vendor/wheel/_commands/__pycache__/pack.cpython-312.pyc,,
+setuptools/_vendor/wheel/_commands/__pycache__/tags.cpython-312.pyc,,
+setuptools/_vendor/wheel/_commands/__pycache__/unpack.cpython-312.pyc,,
+setuptools/_vendor/wheel/_commands/convert.py,sha256=0wSJMU0m-6LY16Om8Wmmloy-hJWFZeOmI8hT-2Z7Qms,12743
+setuptools/_vendor/wheel/_commands/pack.py,sha256=o3iwjfRHl7N9ul-M2kHbewLJZnqBLAWf0tzUCwoiTMw,3078
+setuptools/_vendor/wheel/_commands/tags.py,sha256=Rv2ySVb8-qX3osKp3uJgxcIMXkjt43XUD0-zvC6KvnY,4775
+setuptools/_vendor/wheel/_commands/unpack.py,sha256=AjDSS23XYyCSFfifnMutinrpPv-DK_2wbNHkKAUFwgM,1016
+setuptools/_vendor/wheel/_metadata.py,sha256=BP5jC9uC1hyicp7nL4FJ2LYixNFpEJIV_uMDY1KBZBg,6188
+setuptools/_vendor/wheel/_setuptools_logging.py,sha256=-5KC-lne0ilOUWIDfOkqapUWGMFZhuKYDIavIZiB5kM,781
+setuptools/_vendor/wheel/bdist_wheel.py,sha256=HrzYiSzMkh5ohAAhlQnYBS1p8qbr85X6F59xqxd9kBg,1102
+setuptools/_vendor/wheel/macosx_libfile.py,sha256=pL0wm88jRMl_4ASgGlNg_mz69Zmv5xm8JSkjLdwyvIQ,16712
+setuptools/_vendor/wheel/metadata.py,sha256=GknOO7JJiZMlcEe_fiD7nqnDTTLd0sX_-IgipM4L3-4,757
+setuptools/_vendor/wheel/wheelfile.py,sha256=m_g_7TNsEp-j-xnvSr5yDLEFb1nhyObueq9Q5_1_lBA,8720
+setuptools/_vendor/zipp-3.23.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/zipp-3.23.0.dist-info/METADATA,sha256=vdZ9TRbPC_O4k-fRjNPS13StuC837Zhbx3cMYHIms1s,3563
+setuptools/_vendor/zipp-3.23.0.dist-info/RECORD,sha256=O_q2YKJHBPhCKOS7HOHw4_qf-A5cgGNSpiay9GDq2DY,1078
+setuptools/_vendor/zipp-3.23.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/zipp-3.23.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076
+setuptools/_vendor/zipp-3.23.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5
+setuptools/_vendor/zipp/__init__.py,sha256=ieXh9GIMdABjKRX_JUJtP9k5wdBLK4Mt5X4nszSkmYE,11976
+setuptools/_vendor/zipp/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/zipp/__pycache__/_functools.cpython-312.pyc,,
+setuptools/_vendor/zipp/__pycache__/glob.cpython-312.pyc,,
+setuptools/_vendor/zipp/_functools.py,sha256=f6Kt9LxZ4TE-cY1lJVdXSId3memSXmH9IdgMbU-_x2k,575
+setuptools/_vendor/zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/zipp/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/zipp/compat/__pycache__/overlay.cpython-312.pyc,,
+setuptools/_vendor/zipp/compat/__pycache__/py310.cpython-312.pyc,,
+setuptools/_vendor/zipp/compat/__pycache__/py313.cpython-312.pyc,,
+setuptools/_vendor/zipp/compat/overlay.py,sha256=oEIGAnbr8yGjuKTrVSO2ByewPui71uppbX18BLnYTKE,783
+setuptools/_vendor/zipp/compat/py310.py,sha256=S7i6N9mToEn3asNb2ILyjnzvITOXrATD_J4emjyBbDU,256
+setuptools/_vendor/zipp/compat/py313.py,sha256=RndvDNtuY7H2D9ecnnzcPBMZ8mZc42gmXD_IwQAXXAE,654
+setuptools/_vendor/zipp/glob.py,sha256=DLV9LBsDxA6YVW82e3-tkoNrus1h4R-j3BR6VqS0AzE,3382
+setuptools/archive_util.py,sha256=Tl_64hSTtc4y8x7xa98rFVUbG24oArpjzLAYGYP2_sI,7356
+setuptools/build_meta.py,sha256=x3a7i1m4K6gGYC03yj20LRnEtvZpZjetXCJbpgNo6Cg,20234
+setuptools/cli-32.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/cli-64.exe,sha256=u7PeVwdinmpgoMI4zUd7KPB_AGaYL9qVP6b87DkHOko,14336
+setuptools/cli-arm64.exe,sha256=uafQjaiA36yLz1SOuksG-1m28JsX0zFIoPZhgyiSbGE,13824
+setuptools/cli.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/command/__init__.py,sha256=wdSrlNR0P6nCz9_oFtCAiAkeFJMsZa1jPcpXT53f0SM,803
+setuptools/command/__pycache__/__init__.cpython-312.pyc,,
+setuptools/command/__pycache__/_requirestxt.cpython-312.pyc,,
+setuptools/command/__pycache__/alias.cpython-312.pyc,,
+setuptools/command/__pycache__/bdist_egg.cpython-312.pyc,,
+setuptools/command/__pycache__/bdist_rpm.cpython-312.pyc,,
+setuptools/command/__pycache__/bdist_wheel.cpython-312.pyc,,
+setuptools/command/__pycache__/build.cpython-312.pyc,,
+setuptools/command/__pycache__/build_clib.cpython-312.pyc,,
+setuptools/command/__pycache__/build_ext.cpython-312.pyc,,
+setuptools/command/__pycache__/build_py.cpython-312.pyc,,
+setuptools/command/__pycache__/develop.cpython-312.pyc,,
+setuptools/command/__pycache__/dist_info.cpython-312.pyc,,
+setuptools/command/__pycache__/easy_install.cpython-312.pyc,,
+setuptools/command/__pycache__/editable_wheel.cpython-312.pyc,,
+setuptools/command/__pycache__/egg_info.cpython-312.pyc,,
+setuptools/command/__pycache__/install.cpython-312.pyc,,
+setuptools/command/__pycache__/install_egg_info.cpython-312.pyc,,
+setuptools/command/__pycache__/install_lib.cpython-312.pyc,,
+setuptools/command/__pycache__/install_scripts.cpython-312.pyc,,
+setuptools/command/__pycache__/rotate.cpython-312.pyc,,
+setuptools/command/__pycache__/saveopts.cpython-312.pyc,,
+setuptools/command/__pycache__/sdist.cpython-312.pyc,,
+setuptools/command/__pycache__/setopt.cpython-312.pyc,,
+setuptools/command/__pycache__/test.cpython-312.pyc,,
+setuptools/command/_requirestxt.py,sha256=ItYMTJGh_i5TlQstX_nFopqEhkC4PJFadBL2Zd3V670,4228
+setuptools/command/alias.py,sha256=vVxEYVEFMTZznxuXLg-62Y07VV2U-rfhVqmqhTc62Cg,2366
+setuptools/command/bdist_egg.py,sha256=1kWCzqsiDUGw-HiHu6aGcH16Cf7TjV8oe86ILNLLHTM,16832
+setuptools/command/bdist_rpm.py,sha256=LyqI49w48SKk0FmuHsE9MLzX1SuXjL7YMNbZMFZqFII,1435
+setuptools/command/bdist_wheel.py,sha256=2TMl21f7WWeH3m0NhJLVZPVOSWE0w-wZcG2pFseP8Qw,22210
+setuptools/command/build.py,sha256=eI7STMERGGZEpzk1tvJN8p9IOjAAXMcGLzljv2mwI3M,6052
+setuptools/command/build_clib.py,sha256=AbgpPIF_3qL8fZr3JIebI-WHTMTBiMfrFkVQz8K40G4,4528
+setuptools/command/build_ext.py,sha256=SaurTvvtJuOSOD8cm_6Wj1T0DFiyl5Nazv4GYINAgWA,18270
+setuptools/command/build_py.py,sha256=eHhfo9z7Qf79vSzrCnq-oEXayQC0R_nx3hWaTgWGv5M,15826
+setuptools/command/develop.py,sha256=TYKWIzfv3c3wjAYhH5UD8tW6S6ozZi_fpF6IJILm8Kg,1751
+setuptools/command/dist_info.py,sha256=HU752iLLmmYMHbsDBgz2ubRjkgJobugOp8H71LzzDys,3450
+setuptools/command/easy_install.py,sha256=XrN5cV51mfzbCDoapZ6iT8nCzaLpumdwJYRKeMHEjCQ,780
+setuptools/command/editable_wheel.py,sha256=xJOPl_0s5DbwpeLw-SNh75mvEnmwE7OFpnpChlOaNgg,34862
+setuptools/command/egg_info.py,sha256=_yZYJz5CrLh0aYeT2GXBp6bjF0a0QgYvoDR3uBdKJZc,25754
+setuptools/command/install.py,sha256=4x2hiNgBGQrFEXKuPBQMrb8ecSwIfYF4TYHZQLjPVAg,5066
+setuptools/command/install_egg_info.py,sha256=gYOkfDM-G0agy2aMCE8HzreIi4LzUy6acZ6Ka-rKahQ,2020
+setuptools/command/install_lib.py,sha256=9n1_U83eHcERL_a_rv_LhHCkhXlLdqyZ4SdBow-9qcE,4319
+setuptools/command/install_scripts.py,sha256=ZYfF_-viovV5HSLsRzBlNQv5pHv2H0t2ZnL5qzZ4xfM,2445
+setuptools/command/rotate.py,sha256=PywuJoufmiO_5JykOg-oatrYsBnZfA0pG1HJy7Bsb9Q,2134
+setuptools/command/saveopts.py,sha256=k9hiQ_wOKweXWgBWV548PISmFKfYJkV2XHhi92ejz5o,678
+setuptools/command/sdist.py,sha256=nS2xogcTfQZwbZH1AQ5WYh13KCvNda9xwUT57sVxTec,7426
+setuptools/command/setopt.py,sha256=MCM9SM92LwGUC-E2nnvQxX8Yf2ukMj1YAa6KCRlqt_8,5047
+setuptools/command/test.py,sha256=x9z3DDCQi-_3-TRf8yNeQ9QH-G6yR1au7kX0ijedKqU,1400
+setuptools/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/compat/__pycache__/py310.cpython-312.pyc,,
+setuptools/compat/__pycache__/py311.cpython-312.pyc,,
+setuptools/compat/__pycache__/py312.cpython-312.pyc,,
+setuptools/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/compat/py310.py,sha256=JwjQZ3cNTizfpDLNl9GLsUGzBr-tVlMPxmMYVDTlhiI,344
+setuptools/compat/py311.py,sha256=e6tJAFwZEP82hmMBl10HYeSypelo_Ti2wTjKZVKLwOE,790
+setuptools/compat/py312.py,sha256=vYKVtdrdOTsO_R90dJkEXsFwfMJFuIFJflhIgHrjJ-Y,366
+setuptools/compat/py39.py,sha256=BJMtnkfcqyTfccqjYQxfoRtU2nTnWaEESBVkshTiXqY,493
+setuptools/config/NOTICE,sha256=Ld3wiBgpejuJ1D2V_2WdjahXQRCMkTbfo6TYVsBiO9g,493
+setuptools/config/__init__.py,sha256=aiPnL9BJn1O6MfmuNXyn8W2Lp8u9qizRVqwPiOdPIjY,1499
+setuptools/config/__pycache__/__init__.cpython-312.pyc,,
+setuptools/config/__pycache__/_apply_pyprojecttoml.cpython-312.pyc,,
+setuptools/config/__pycache__/expand.cpython-312.pyc,,
+setuptools/config/__pycache__/pyprojecttoml.cpython-312.pyc,,
+setuptools/config/__pycache__/setupcfg.cpython-312.pyc,,
+setuptools/config/_apply_pyprojecttoml.py,sha256=jIpYyQNibH64jOllJhVUtVSpKHySylKJ6IMW7GX3_GI,19442
+setuptools/config/_validate_pyproject/NOTICE,sha256=Ccm86pXKCG-Lxb7RdOQLyDWyl9QPtfhru7Vw_gpVgac,18737
+setuptools/config/_validate_pyproject/__init__.py,sha256=dnp6T7ePP1R5z4OuC7Fd2dkFlIrtIfizUfvpGJP6nz0,1042
+setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/extra_validations.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/formats.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/error_reporting.py,sha256=5jGbcg7zQCEiEA8fAilSkaXnEfXyD7fq5CG-OkmFZmk,11803
+setuptools/config/_validate_pyproject/extra_validations.py,sha256=lQnu5wASBL8iDToODpw5sX21kev01_PjUIGldu1proY,5066
+setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py,sha256=w749JgqKi8clBFcObdcbZVqsmF4oJ_QByhZ1SGbUFNw,1612
+setuptools/config/_validate_pyproject/fastjsonschema_validations.py,sha256=XjMh9s9ezEzqM9yoZ4KoUQ7jCoHAqsRl-KSf30Rlgxo,365819
+setuptools/config/_validate_pyproject/formats.py,sha256=LNRajleuZtyP8BWhMcbuA92APppdiItpEpEeqCjA54Q,15373
+setuptools/config/distutils.schema.json,sha256=Tcp32kRnhwORGw_9p6GEi08lj2h15tQRzOYBbzGmcBU,972
+setuptools/config/expand.py,sha256=STKJ6oNAo6avEBVUiR25WpfIthVPVZQG65Z-5-t78QI,16064
+setuptools/config/pyprojecttoml.py,sha256=UDrCvQGO2c8WvrbwTYUZOdTRhDPFbVNZo5i72sbJ3D0,18786
+setuptools/config/setupcfg.py,sha256=Wnfbrk6D0GQV-Z2AE0b0KnL-WGrw-KQUcsem3naImBw,26695
+setuptools/config/setuptools.schema.json,sha256=Bp6tTwRvSy96_dCd5OqKTifuIcTWOd5wDGRCrwYBzjQ,16047
+setuptools/depends.py,sha256=jKYfjmt_2ZQYVghb8L9bU7LJ6erHJ5ze-K_fKV1BMXk,5965
+setuptools/discovery.py,sha256=XYIeFN20WEsEtssmpzCFlnda-Qxilj8KlLYKaJphAto,21286
+setuptools/dist.py,sha256=aa181F7kGdfeL6To3X2ZvQ8zSiQCSacWRJdhL0v49xk,45184
+setuptools/errors.py,sha256=gY2x2PIaIgy01yRANRC-zcCwxDCqCScgJoCOZFe0yio,3024
+setuptools/extension.py,sha256=GbCxJ9rRk2rZcXS4qgmnzWNG36HlhLfUEvNwB-OKGk8,6818
+setuptools/glob.py,sha256=AC_B33DY8g-CHELxDsJrtwFrpiucSAZsakPFdSOQzhc,6062
+setuptools/gui-32.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/gui-64.exe,sha256=NHG2FA6txkEid9u-_j_vjDRaDxpZd2CGuAo2GMOoPjs,14336
+setuptools/gui-arm64.exe,sha256=5pT0dDQFyLWSb_RX22_n8aEt7HwWqcOGR4TT9OB64Jc,13824
+setuptools/gui.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/installer.py,sha256=Wy_hG1g1r-45E6IWh7lvQ0Pk0QHNl6JJbu3hQmr1_Ek,5184
+setuptools/launch.py,sha256=IBb5lEv69CyuZ9ewIrmKlXh154kdLmP29LKfTMkximE,820
+setuptools/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628
+setuptools/logging.py,sha256=W16iHJ1HcCXYQ0RxyrEfJ83FT4175tCtoYg-E6uSpVI,1261
+setuptools/modified.py,sha256=ZwbfBfCFP88ltvbv_dJDz-t1LsQjnM-JUpgZnnQZjjM,568
+setuptools/monkey.py,sha256=nOD5vgLG7IpKAs7LrnpJxGPaCW54Rzj-onJmm91otoY,3733
+setuptools/msvc.py,sha256=IdCsRhdJLeyZG3gyDrGx2DqZymoay3ndBXC77taHHCA,42909
+setuptools/namespaces.py,sha256=eE1hI5X86TNWvY5u5BC1WiGiOrrJyGOv0pp9vycCmK0,3045
+setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218
+setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138
+setuptools/tests/__init__.py,sha256=AnBfls2iJbTDQzmMKeLRt-9lxhaOHUVOZEgXv89Uwvs,335
+setuptools/tests/__pycache__/__init__.cpython-312.pyc,,
+setuptools/tests/__pycache__/contexts.cpython-312.pyc,,
+setuptools/tests/__pycache__/environment.cpython-312.pyc,,
+setuptools/tests/__pycache__/fixtures.cpython-312.pyc,,
+setuptools/tests/__pycache__/mod_with_constant.cpython-312.pyc,,
+setuptools/tests/__pycache__/namespaces.cpython-312.pyc,,
+setuptools/tests/__pycache__/script-with-bom.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_archive_util.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_bdist_deprecations.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_bdist_egg.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_bdist_wheel.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_build.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_build_clib.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_build_ext.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_build_meta.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_build_py.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_config_discovery.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_core_metadata.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_depends.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_develop.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_dist.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_dist_info.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_distutils_adoption.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_editable_install.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_egg_info.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_extern.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_find_packages.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_find_py_modules.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_glob.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_install_scripts.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_logging.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_manifest.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_namespaces.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_scripts.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_sdist.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_setopt.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_setuptools.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_shutil_wrapper.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_unicode_utils.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_virtualenv.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_warnings.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_wheel.cpython-312.pyc,,
+setuptools/tests/__pycache__/test_windows_wrappers.cpython-312.pyc,,
+setuptools/tests/__pycache__/text.cpython-312.pyc,,
+setuptools/tests/__pycache__/textwrap.cpython-312.pyc,,
+setuptools/tests/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/tests/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/tests/compat/py39.py,sha256=eUy7_F-6KRTOIKl-veshUu6I0EdTSdBZMh0EV0lZ1-g,135
+setuptools/tests/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/config/__pycache__/__init__.cpython-312.pyc,,
+setuptools/tests/config/__pycache__/test_apply_pyprojecttoml.cpython-312.pyc,,
+setuptools/tests/config/__pycache__/test_expand.cpython-312.pyc,,
+setuptools/tests/config/__pycache__/test_pyprojecttoml.cpython-312.pyc,,
+setuptools/tests/config/__pycache__/test_pyprojecttoml_dynamic_deps.cpython-312.pyc,,
+setuptools/tests/config/__pycache__/test_setupcfg.cpython-312.pyc,,
+setuptools/tests/config/downloads/__init__.py,sha256=9ixnDEdyL_arKbUzfuiJftAj9bGxKz8M9alOFZMjx9Y,1827
+setuptools/tests/config/downloads/__pycache__/__init__.cpython-312.pyc,,
+setuptools/tests/config/downloads/__pycache__/preload.cpython-312.pyc,,
+setuptools/tests/config/downloads/preload.py,sha256=sIGGZpY3cmMpMwiJYYYYHG2ifZJkvJgEotRFtiulV1I,450
+setuptools/tests/config/setupcfg_examples.txt,sha256=cAbVvCbkFZuTUL6xRRzRgqyB0rLvJTfvw3D30glo2OE,1912
+setuptools/tests/config/test_apply_pyprojecttoml.py,sha256=qBOXnCwZVQ7wTYfwq83zAAtsGGooJgFh_H9L2avrv4Y,29609
+setuptools/tests/config/test_expand.py,sha256=S0oT6JvgA_oujR4YS4RUuf5gmOt1CTQV66RQDzV8xd4,8933
+setuptools/tests/config/test_pyprojecttoml.py,sha256=tP7iaVmpnv3wAY2eplDLFXzfHGuvxT71SQX9O9Jrwl0,13182
+setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py,sha256=1MRtzcxZag-ElRwVGt0kUk4KPRA-RSwKkibO8-Vy51w,3265
+setuptools/tests/config/test_setupcfg.py,sha256=iwViCQFLGVWZLgCDrBv8HKBBCj-Onr8aNdioD9_lBEo,33712
+setuptools/tests/contexts.py,sha256=Ozdfc2KydF9x9wODUsdun800myLuP27uxoeT06Gbk7M,3166
+setuptools/tests/environment.py,sha256=95_UtTaRiuvwYC9eXKEHbn02kDtZysvZq3UZJmPUj1I,3102
+setuptools/tests/fixtures.py,sha256=jE7pIFQt91dZIUc6Btsffi8OdBqmY_7DNeWI1Stv7i8,12224
+setuptools/tests/indexes/test_links_priority/external.html,sha256=eL9euOuE93JKZdqlXxBOlHbKwIuNuIdq7GBRpsaPMcU,92
+setuptools/tests/indexes/test_links_priority/simple/foobar/index.html,sha256=DD-TKr7UU4zAjHHz4VexYDNSAzR27levSh1c-k3ZdLE,174
+setuptools/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/integration/__pycache__/__init__.cpython-312.pyc,,
+setuptools/tests/integration/__pycache__/helpers.cpython-312.pyc,,
+setuptools/tests/integration/__pycache__/test_pbr.cpython-312.pyc,,
+setuptools/tests/integration/__pycache__/test_pip_install_sdist.cpython-312.pyc,,
+setuptools/tests/integration/helpers.py,sha256=ieQtGuIohqNI_RHMH0yxE602Uz4E8wysUj7XaXYH30Y,2688
+setuptools/tests/integration/test_pbr.py,sha256=2eKuklFNmpnBgA_eEhYPBr6rLLG2Xm4MY6PlcmzZgGU,432
+setuptools/tests/integration/test_pip_install_sdist.py,sha256=SFbvuYF_hDzt6OtsQ5GjFNnxmoJ_eElfvpYsiyyGJ-g,8256
+setuptools/tests/mod_with_constant.py,sha256=X_Kj80M55w1tmQ4f7uZY91ZTALo4hKVT6EHxgYocUMQ,22
+setuptools/tests/namespaces.py,sha256=HPcI3nR5MCFWXpaADIJ1fwKxymcQgBkuw87Ic5PUSAQ,2774
+setuptools/tests/script-with-bom.py,sha256=hRRgIizEULGiG_ZTNoMY46HhKhxpWfy5FGcD6Qbh5fc,18
+setuptools/tests/test_archive_util.py,sha256=buuKdY8XkW26Pe3IKAoBRGHG0MDumnuNoPg2WsAQzIg,845
+setuptools/tests/test_bdist_deprecations.py,sha256=75Xq3gYn79LIIyusEltbHan0bEgAt2e_CaL7KLS8-KQ,775
+setuptools/tests/test_bdist_egg.py,sha256=6PaYN1F3JDbIh1uK0urv7yJFcx98z5dn9SOJ8Mv91l8,1957
+setuptools/tests/test_bdist_wheel.py,sha256=xGHVaggiYobkOuGwkLK2bNejcSh8z2Fiuq-ge_S-yKo,23091
+setuptools/tests/test_build.py,sha256=wJgMz2hwHADcLFg-nXrwRVhus7hjmAeEGgrpIQwCGnA,798
+setuptools/tests/test_build_clib.py,sha256=bX51XRAf4uO7IuHFpjePnoK8mE74N2gsoeEqF-ofgws,3123
+setuptools/tests/test_build_ext.py,sha256=e4ZSxsYPB5zq1KSqGEuATZ0t0PJQzMhjjkKJ-hIjcgc,10099
+setuptools/tests/test_build_meta.py,sha256=ToI7-2LUnHuIPhKN8EkDkSEfLCvLqLI6VJhqwR5pVuU,33320
+setuptools/tests/test_build_py.py,sha256=SIcHFX3YNErcVkWjf0eDRuKmsO9kTymKjfK-PkOi9M0,14201
+setuptools/tests/test_config_discovery.py,sha256=FqV-lOtkqaI-ayzU2zocSdD5TaRAgCZnixNDilKA6FQ,22580
+setuptools/tests/test_core_metadata.py,sha256=p8mW920afKVdL8sC_Fce8zpmfWDCRylBa3dd39Y-qWo,18038
+setuptools/tests/test_depends.py,sha256=yQBXoQbNQlJit6mbRVoz6Bb553f3sNrq02lZimNz5XY,424
+setuptools/tests/test_develop.py,sha256=k-agNgB8DIGR8zlaYMBILu8KOPrL2plorNCexxzNm-s,3136
+setuptools/tests/test_dist.py,sha256=M4FikA-vL0_lZdA_5wry2Z9CBuSugr0Pj54dBZ3peBM,8951
+setuptools/tests/test_dist_info.py,sha256=EihdrU9UZkPV7d19G-K_OtOByp64GvGnf4or-f8Iq54,5005
+setuptools/tests/test_distutils_adoption.py,sha256=_eynrOfyEqXFEmjUJhzpe8GXPyTUPvNSObs4qAAmBy8,5987
+setuptools/tests/test_editable_install.py,sha256=o4DkoytkiL-_rzES8p1q8XYRa9-ha_m6uhrm-EWzdwI,42498
+setuptools/tests/test_egg_info.py,sha256=R7nT27YhVz9oSuDyimAGerWglkbRWiMSPBs5FzcSnBM,44941
+setuptools/tests/test_extern.py,sha256=rpKU6oCcksumLwf5TeKlDluFQ0TUfbPwTLQbpxcFrCU,296
+setuptools/tests/test_find_packages.py,sha256=CTLAcTzWGWBLCcd2aAsUVkvO3ibrlqexFBdDKOWPoq8,7819
+setuptools/tests/test_find_py_modules.py,sha256=zQjuhIG5TQN2SJPix9ARo4DL_w84Ln8QsHDUjjbrtAQ,2404
+setuptools/tests/test_glob.py,sha256=P3JvpH-kXQ4BZ3zvRF-zKxOgwyWzwIaQIz0WHdxS0kk,887
+setuptools/tests/test_install_scripts.py,sha256=scIrJ6a_ssKqg4vIBNaUjmAKHEYLUUZ9WKnPeKnE6gc,3433
+setuptools/tests/test_logging.py,sha256=czGLLdGxUBZKXbea3A0HnSW9lf-3K_CUwaI0gDkVbOM,2096
+setuptools/tests/test_manifest.py,sha256=eMg65pIA52DizB6mpktSU-b8CjwaNCS5MSgL_V1LrFI,18562
+setuptools/tests/test_namespaces.py,sha256=e84seD5g6KIPVSDpawE9Ihc25Je7shooybV1flUsW20,2508
+setuptools/tests/test_scripts.py,sha256=_ra506yQF7n72ROUDcz2r3CTsGsawO1m-1oqA9EQCfw,379
+setuptools/tests/test_sdist.py,sha256=oKMgvBrkfTsPH5jqsrhFxRMbtUDoOy4vKfwll3kGCg0,32808
+setuptools/tests/test_setopt.py,sha256=3VxxM4ATfP-P4AGnDjoWCnHr5-i9CSEQTFYU1-FTnvI,1365
+setuptools/tests/test_setuptools.py,sha256=WqrZ8qbv9uJh2xzRn-wXam-YGzmG8od2spru3L3sMbg,9323
+setuptools/tests/test_shutil_wrapper.py,sha256=g15E11PtZxG-InB2BWNFyH-svObXx2XcMhgMLJPuFnc,641
+setuptools/tests/test_unicode_utils.py,sha256=xWfEEl8jkQCt9othUTXJfFmdyATAFggJs2tTxjbumbw,316
+setuptools/tests/test_virtualenv.py,sha256=g-njC_9JTAs1YVx_1dGJ_Q6RlInO4qKVu9-XAgNb6TY,3730
+setuptools/tests/test_warnings.py,sha256=zwR2zcnCeCeDqILZlJOPAcuyPHoDvGu1OtOVYiLMk74,3347
+setuptools/tests/test_wheel.py,sha256=iMfVTNixu4puf6xvRYjQDw4Zg_OHVCQjSy7SztvVYqE,18722
+setuptools/tests/test_windows_wrappers.py,sha256=wBjXN3iGldkzRGTgKTrx99xqUqwPJ0V-ldyiB1pWD-g,7868
+setuptools/tests/text.py,sha256=a12197pMVTvB6FAWQ0ujT8fIQiLIWJlFAl1UCaDUDfg,123
+setuptools/tests/textwrap.py,sha256=FNNNq_MiaEJx88PnsbJQIRxmj1qmgcAOCXXRsODPJN4,98
+setuptools/unicode_utils.py,sha256=ukMGh8pEAw6F_Ezb-K5D3c-078RgA_GcF0oW6lg4lSs,3189
+setuptools/version.py,sha256=WJCeUuyq74Aok2TeK9-OexZOu8XrlQy7-y0BEuWNovQ,161
+setuptools/warnings.py,sha256=oY0Se5eOqje_FEyjTgonUc0XGwgsrI5cgm1kkwulz_w,3796
+setuptools/wheel.py,sha256=iI-LSDrgHMlJIzQMALVlJRq3Qesgbj7xa6hWZYJyZl4,9532
+setuptools/windows_support.py,sha256=wW4IYLM1Bv7Z1MaauP2xmPjyy-wkmQnXdyvXscAf9fw,726
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/WHEEL
new file mode 100644
index 0000000..14a883f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (82.0.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/entry_points.txt
new file mode 100644
index 0000000..0db0a6c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/entry_points.txt
@@ -0,0 +1,51 @@
+[distutils.commands]
+alias = setuptools.command.alias:alias
+bdist_egg = setuptools.command.bdist_egg:bdist_egg
+bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
+bdist_wheel = setuptools.command.bdist_wheel:bdist_wheel
+build = setuptools.command.build:build
+build_clib = setuptools.command.build_clib:build_clib
+build_ext = setuptools.command.build_ext:build_ext
+build_py = setuptools.command.build_py:build_py
+develop = setuptools.command.develop:develop
+dist_info = setuptools.command.dist_info:dist_info
+easy_install = setuptools.command.easy_install:easy_install
+editable_wheel = setuptools.command.editable_wheel:editable_wheel
+egg_info = setuptools.command.egg_info:egg_info
+install = setuptools.command.install:install
+install_egg_info = setuptools.command.install_egg_info:install_egg_info
+install_lib = setuptools.command.install_lib:install_lib
+install_scripts = setuptools.command.install_scripts:install_scripts
+rotate = setuptools.command.rotate:rotate
+saveopts = setuptools.command.saveopts:saveopts
+sdist = setuptools.command.sdist:sdist
+setopt = setuptools.command.setopt:setopt
+
+[distutils.setup_keywords]
+dependency_links = setuptools.dist:assert_string_list
+eager_resources = setuptools.dist:assert_string_list
+entry_points = setuptools.dist:check_entry_points
+exclude_package_data = setuptools.dist:check_package_data
+extras_require = setuptools.dist:check_extras
+include_package_data = setuptools.dist:assert_bool
+install_requires = setuptools.dist:check_requirements
+namespace_packages = setuptools.dist:check_nsp
+package_data = setuptools.dist:check_package_data
+packages = setuptools.dist:check_packages
+python_requires = setuptools.dist:check_specifier
+setup_requires = setuptools.dist:check_requirements
+use_2to3 = setuptools.dist:invalid_unless_false
+zip_safe = setuptools.dist:assert_bool
+
+[egg_info.writers]
+PKG-INFO = setuptools.command.egg_info:write_pkg_info
+dependency_links.txt = setuptools.command.egg_info:overwrite_arg
+eager_resources.txt = setuptools.command.egg_info:overwrite_arg
+entry_points.txt = setuptools.command.egg_info:write_entries
+namespace_packages.txt = setuptools.command.egg_info:overwrite_arg
+requires.txt = setuptools.command.egg_info:write_requirements
+top_level.txt = setuptools.command.egg_info:write_toplevel_names
+
+[setuptools.finalize_distribution_options]
+keywords = setuptools.dist:Distribution._finalize_setup_keywords
+parent_finalize = setuptools.dist:_Distribution.finalize_options
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/licenses/LICENSE
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/top_level.txt
new file mode 100644
index 0000000..11b11fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-82.0.1.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+_distutils_hack
+setuptools
diff --git a/.venv/lib/python3.12/site-packages/setuptools/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/__init__.py
new file mode 100644
index 0000000..87ca47b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/__init__.py
@@ -0,0 +1,256 @@
+"""Extensions to the 'distutils' for large or complex distributions"""
+# mypy: disable_error_code=override
+# Command.reinitialize_command has an extra **kw param that distutils doesn't have
+# Can't disable on the exact line because distutils doesn't exists on Python 3.12
+# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any,
+# and a [unused-ignore] to be raised on 3.12+
+
+from __future__ import annotations
+
+import functools
+import os
+import sys
+from abc import abstractmethod
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, TypeVar, overload
+
+sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path])  # fmt: skip
+# workaround for #4476
+sys.modules.pop('backports', None)
+
+import _distutils_hack.override  # noqa: F401
+
+from . import logging, monkey
+from .depends import Require
+from .discovery import PackageFinder, PEP420PackageFinder
+from .dist import Distribution
+from .extension import Extension
+from .version import __version__ as __version__
+from .warnings import SetuptoolsDeprecationWarning
+
+import distutils.core
+
+__all__ = [
+    'setup',
+    'Distribution',
+    'Command',
+    'Extension',
+    'Require',
+    'SetuptoolsDeprecationWarning',
+    'find_packages',
+    'find_namespace_packages',
+]
+
+_CommandT = TypeVar("_CommandT", bound="_Command")
+
+bootstrap_install_from = None
+
+find_packages = PackageFinder.find
+find_namespace_packages = PEP420PackageFinder.find
+
+
+def _install_setup_requires(attrs):
+    # Note: do not use `setuptools.Distribution` directly, as
+    # our PEP 517 backend patch `distutils.core.Distribution`.
+    class MinimalDistribution(distutils.core.Distribution):
+        """
+        A minimal version of a distribution for supporting the
+        fetch_build_eggs interface.
+        """
+
+        def __init__(self, attrs: Mapping[str, object]) -> None:
+            _incl = 'dependency_links', 'setup_requires'
+            filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
+            super().__init__(filtered)
+            # Prevent accidentally triggering discovery with incomplete set of attrs
+            self.set_defaults._disable()
+
+        def _get_project_config_files(self, filenames=None):
+            """Ignore ``pyproject.toml``, they are not related to setup_requires"""
+            try:
+                cfg, _toml = super()._split_standard_project_metadata(filenames)
+            except Exception:
+                return filenames, ()
+            return cfg, ()
+
+        def finalize_options(self):
+            """
+            Disable finalize_options to avoid building the working set.
+            Ref #2158.
+            """
+
+    dist = MinimalDistribution(attrs)
+
+    # Honor setup.cfg's options.
+    dist.parse_config_files(ignore_option_errors=True)
+    if dist.setup_requires:
+        _fetch_build_eggs(dist)
+
+
+def _fetch_build_eggs(dist: Distribution):
+    try:
+        dist.fetch_build_eggs(dist.setup_requires)
+    except Exception as ex:
+        msg = """
+        It is possible a package already installed in your system
+        contains an version that is invalid according to PEP 440.
+        You can try `pip install --use-pep517` as a workaround for this problem,
+        or rely on a new virtual environment.
+
+        If the problem refers to a package that is not installed yet,
+        please contact that package's maintainers or distributors.
+        """
+        if "InvalidVersion" in ex.__class__.__name__:
+            if hasattr(ex, "add_note"):
+                ex.add_note(msg)  # PEP 678
+            else:
+                dist.announce(f"\n{msg}\n")
+        raise
+
+
+def setup(**attrs) -> Distribution:
+    logging.configure()
+    # Make sure we have any requirements needed to interpret 'attrs'.
+    _install_setup_requires(attrs)
+    # Override return type of distutils.core.Distribution with setuptools.dist.Distribution
+    # (implicitly implemented via `setuptools.monkey.patch_all`).
+    return distutils.core.setup(**attrs)  # type: ignore[return-value]
+
+
+setup.__doc__ = distutils.core.setup.__doc__
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Command as _Command
+else:
+    _Command = monkey.get_unpatched(distutils.core.Command)
+
+
+class Command(_Command):
+    """
+    Setuptools internal actions are organized using a *command design pattern*.
+    This means that each action (or group of closely related actions) executed during
+    the build should be implemented as a ``Command`` subclass.
+
+    These commands are abstractions and do not necessarily correspond to a command that
+    can (or should) be executed via a terminal, in a CLI fashion (although historically
+    they would).
+
+    When creating a new command from scratch, custom defined classes **SHOULD** inherit
+    from ``setuptools.Command`` and implement a few mandatory methods.
+    Between these mandatory methods, are listed:
+    :meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`.
+
+    A useful analogy for command classes is to think of them as subroutines with local
+    variables called "options".  The options are "declared" in :meth:`initialize_options`
+    and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`,
+    both of which must be defined by every command class. The "body" of the subroutine,
+    (where it does all the work) is the :meth:`run` method.
+    Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set
+    the values for options/attributes based on user's input (or circumstance),
+    which means that the implementation should be careful to not overwrite values in
+    :meth:`finalize_options` unless necessary.
+
+    Please note that other commands (or other parts of setuptools) may also overwrite
+    the values of the command's options/attributes multiple times during the build
+    process.
+    Therefore it is important to consistently implement :meth:`initialize_options` and
+    :meth:`finalize_options`. For example, all derived attributes (or attributes that
+    depend on the value of other attributes) **SHOULD** be recomputed in
+    :meth:`finalize_options`.
+
+    When overwriting existing commands, custom defined classes **MUST** abide by the
+    same APIs implemented by the original class. They also **SHOULD** inherit from the
+    original class.
+    """
+
+    command_consumes_arguments = False
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    dry_run = False  # type: ignore[assignment] # pyright: ignore[reportAssignmentType] (until #4689; see #4872)
+    """
+    For compatibility with vendored bdist_wheel.
+    https://github.com/pypa/setuptools/pull/4872/files#r1986395142
+    """
+
+    def __init__(self, dist: Distribution, **kw) -> None:
+        """
+        Construct the command for dist, updating
+        vars(self) with any keyword parameters.
+        """
+        super().__init__(dist)
+        vars(self).update(kw)
+
+    @overload
+    def reinitialize_command(
+        self, command: str, reinit_subcommands: bool = False, **kw
+    ) -> Command: ...  # override distutils.cmd.Command with setuptools.Command
+    @overload
+    def reinitialize_command(
+        self, command: _CommandT, reinit_subcommands: bool = False, **kw
+    ) -> _CommandT: ...
+    def reinitialize_command(
+        self, command: str | _Command, reinit_subcommands: bool = False, **kw
+    ) -> Command | _Command:
+        cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
+        vars(cmd).update(kw)
+        return cmd  # pyright: ignore[reportReturnType] # pypa/distutils#307
+
+    @abstractmethod
+    def initialize_options(self) -> None:
+        """
+        Set or (reset) all options/attributes/caches used by the command
+        to their default values. Note that these values may be overwritten during
+        the build.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def finalize_options(self) -> None:
+        """
+        Set final values for all options/attributes used by the command.
+        Most of the time, each option/attribute/cache should only be set if it does not
+        have any value yet (e.g. ``if self.attr is None: self.attr = val``).
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def run(self) -> None:
+        """
+        Execute the actions intended by the command.
+        (Side effects **SHOULD** only take place when :meth:`run` is executed,
+        for example, creating new files or writing to the terminal output).
+        """
+        raise NotImplementedError
+
+
+def _find_all_simple(path):
+    """
+    Find all files under 'path'
+    """
+    results = (
+        os.path.join(base, file)
+        for base, dirs, files in os.walk(path, followlinks=True)
+        for file in files
+    )
+    return filter(os.path.isfile, results)
+
+
+def findall(dir=os.curdir):
+    """
+    Find all files under 'dir' and return the list of full filenames.
+    Unless dir is '.', return full filenames with dir prepended.
+    """
+    files = _find_all_simple(dir)
+    if dir == os.curdir:
+        make_rel = functools.partial(os.path.relpath, start=dir)
+        files = map(make_rel, files)
+    return list(files)
+
+
+class sic(str):
+    """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
+
+
+# Apply monkey patches
+monkey.patch_all()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py b/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py
new file mode 100644
index 0000000..a52d5cf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py
@@ -0,0 +1,337 @@
+"""
+Handling of Core Metadata for Python packages (including reading and writing).
+
+See: https://packaging.python.org/en/latest/specifications/core-metadata/
+"""
+
+from __future__ import annotations
+
+import os
+import stat
+import textwrap
+from email import message_from_file
+from email.message import Message
+from tempfile import NamedTemporaryFile
+
+from packaging.markers import Marker
+from packaging.requirements import Requirement
+from packaging.utils import canonicalize_name, canonicalize_version
+from packaging.version import Version
+
+from . import _normalization, _reqs
+from ._static import is_static
+from .warnings import SetuptoolsDeprecationWarning
+
+from distutils.util import rfc822_escape
+
+
+def get_metadata_version(self):
+    mv = getattr(self, 'metadata_version', None)
+    if mv is None:
+        mv = Version('2.4')
+        self.metadata_version = mv
+    return mv
+
+
+def rfc822_unescape(content: str) -> str:
+    """Reverse RFC-822 escaping by removing leading whitespaces from content."""
+    lines = content.splitlines()
+    if len(lines) == 1:
+        return lines[0].lstrip()
+    return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
+
+
+def _read_field_from_msg(msg: Message, field: str) -> str | None:
+    """Read Message header field."""
+    value = msg[field]
+    if value == 'UNKNOWN':
+        return None
+    return value
+
+
+def _read_field_unescaped_from_msg(msg: Message, field: str) -> str | None:
+    """Read Message header field and apply rfc822_unescape."""
+    value = _read_field_from_msg(msg, field)
+    if value is None:
+        return value
+    return rfc822_unescape(value)
+
+
+def _read_list_from_msg(msg: Message, field: str) -> list[str] | None:
+    """Read Message header field and return all results as list."""
+    values = msg.get_all(field, None)
+    if values == []:
+        return None
+    return values
+
+
+def _read_payload_from_msg(msg: Message) -> str | None:
+    value = str(msg.get_payload()).strip()
+    if value == 'UNKNOWN' or not value:
+        return None
+    return value
+
+
+def read_pkg_file(self, file):
+    """Reads the metadata values from a file object."""
+    msg = message_from_file(file)
+
+    self.metadata_version = Version(msg['metadata-version'])
+    self.name = _read_field_from_msg(msg, 'name')
+    self.version = _read_field_from_msg(msg, 'version')
+    self.description = _read_field_from_msg(msg, 'summary')
+    # we are filling author only.
+    self.author = _read_field_from_msg(msg, 'author')
+    self.maintainer = None
+    self.author_email = _read_field_from_msg(msg, 'author-email')
+    self.maintainer_email = None
+    self.url = _read_field_from_msg(msg, 'home-page')
+    self.download_url = _read_field_from_msg(msg, 'download-url')
+    self.license = _read_field_unescaped_from_msg(msg, 'license')
+    self.license_expression = _read_field_unescaped_from_msg(msg, 'license-expression')
+
+    self.long_description = _read_field_unescaped_from_msg(msg, 'description')
+    if self.long_description is None and self.metadata_version >= Version('2.1'):
+        self.long_description = _read_payload_from_msg(msg)
+    self.description = _read_field_from_msg(msg, 'summary')
+
+    if 'keywords' in msg:
+        self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
+
+    self.platforms = _read_list_from_msg(msg, 'platform')
+    self.classifiers = _read_list_from_msg(msg, 'classifier')
+
+    # PEP 314 - these fields only exist in 1.1
+    if self.metadata_version == Version('1.1'):
+        self.requires = _read_list_from_msg(msg, 'requires')
+        self.provides = _read_list_from_msg(msg, 'provides')
+        self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
+    else:
+        self.requires = None
+        self.provides = None
+        self.obsoletes = None
+
+    self.license_files = _read_list_from_msg(msg, 'license-file')
+
+
+def single_line(val):
+    """
+    Quick and dirty validation for Summary pypa/setuptools#1390.
+    """
+    if '\n' in val:
+        # TODO: Replace with `raise ValueError("newlines not allowed")`
+        # after reviewing #2893.
+        msg = "newlines are not allowed in `summary` and will break in the future"
+        SetuptoolsDeprecationWarning.emit("Invalid config.", msg)
+        # due_date is undefined. Controversial change, there was a lot of push back.
+        val = val.strip().split('\n')[0]
+    return val
+
+
+def write_pkg_info(self, base_dir):
+    """Write the PKG-INFO file into the release tree."""
+    temp = ""
+    final = os.path.join(base_dir, 'PKG-INFO')
+    try:
+        # Use a temporary file while writing to avoid race conditions
+        # (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`):
+        with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f:
+            temp = f.name
+            self.write_pkg_file(f)
+        permissions = stat.S_IMODE(os.lstat(temp).st_mode)
+        os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH)
+        os.replace(temp, final)  # atomic operation.
+    finally:
+        if temp and os.path.exists(temp):
+            os.remove(temp)
+
+
+# Based on Python 3.5 version
+def write_pkg_file(self, file):  # noqa: C901  # is too complex (14)  # FIXME
+    """Write the PKG-INFO format data to a file object."""
+    version = self.get_metadata_version()
+
+    def write_field(key, value):
+        file.write(f"{key}: {value}\n")
+
+    write_field('Metadata-Version', str(version))
+    write_field('Name', self.get_name())
+    write_field('Version', self.get_version())
+
+    summary = self.get_description()
+    if summary:
+        write_field('Summary', single_line(summary))
+
+    optional_fields = (
+        ('Home-page', 'url'),
+        ('Download-URL', 'download_url'),
+        ('Author', 'author'),
+        ('Author-email', 'author_email'),
+        ('Maintainer', 'maintainer'),
+        ('Maintainer-email', 'maintainer_email'),
+    )
+
+    for field, attr in optional_fields:
+        attr_val = getattr(self, attr, None)
+        if attr_val is not None:
+            write_field(field, attr_val)
+
+    if license_expression := self.license_expression:
+        write_field('License-Expression', license_expression)
+    elif license := self.get_license():
+        write_field('License', rfc822_escape(license))
+
+    for label, url in self.project_urls.items():
+        write_field('Project-URL', f'{label}, {url}')
+
+    keywords = ','.join(self.get_keywords())
+    if keywords:
+        write_field('Keywords', keywords)
+
+    platforms = self.get_platforms() or []
+    for platform in platforms:
+        write_field('Platform', platform)
+
+    self._write_list(file, 'Classifier', self.get_classifiers())
+
+    # PEP 314
+    self._write_list(file, 'Requires', self.get_requires())
+    self._write_list(file, 'Provides', self.get_provides())
+    self._write_list(file, 'Obsoletes', self.get_obsoletes())
+
+    # Setuptools specific for PEP 345
+    if hasattr(self, 'python_requires'):
+        write_field('Requires-Python', self.python_requires)
+
+    # PEP 566
+    if self.long_description_content_type:
+        write_field('Description-Content-Type', self.long_description_content_type)
+
+    safe_license_files = map(_safe_license_file, self.license_files or [])
+    self._write_list(file, 'License-File', safe_license_files)
+    _write_requirements(self, file)
+
+    for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items():
+        if (val := getattr(self, attr, None)) and not is_static(val):
+            write_field('Dynamic', field)
+
+    long_description = self.get_long_description()
+    if long_description:
+        file.write(f"\n{long_description}")
+        if not long_description.endswith("\n"):
+            file.write("\n")
+
+
+def _write_requirements(self, file):
+    for req in _reqs.parse(self.install_requires):
+        file.write(f"Requires-Dist: {req}\n")
+
+    processed_extras = {}
+    for augmented_extra, reqs in self.extras_require.items():
+        # Historically, setuptools allows "augmented extras": `:`
+        unsafe_extra, _, condition = augmented_extra.partition(":")
+        unsafe_extra = unsafe_extra.strip()
+        extra = _normalization.safe_extra(unsafe_extra)
+
+        if extra:
+            _write_provides_extra(file, processed_extras, extra, unsafe_extra)
+        for req in _reqs.parse_strings(reqs):
+            r = _include_extra(req, extra, condition.strip())
+            file.write(f"Requires-Dist: {r}\n")
+
+    return processed_extras
+
+
+def _include_extra(req: str, extra: str, condition: str) -> Requirement:
+    r = Requirement(req)  # create a fresh object that can be modified
+    parts = (
+        f"({r.marker})" if r.marker else None,
+        f"({condition})" if condition else None,
+        f"extra == {extra!r}" if extra else None,
+    )
+    r.marker = Marker(" and ".join(x for x in parts if x))
+    return r
+
+
+def _write_provides_extra(file, processed_extras, safe, unsafe):
+    previous = processed_extras.get(safe)
+    if previous == unsafe:
+        SetuptoolsDeprecationWarning.emit(
+            'Ambiguity during "extra" normalization for dependencies.',
+            f"""
+            {previous!r} and {unsafe!r} normalize to the same value:\n
+                {safe!r}\n
+            In future versions, setuptools might halt the build process.
+            """,
+            see_url="https://peps.python.org/pep-0685/",
+        )
+    else:
+        processed_extras[safe] = unsafe
+        file.write(f"Provides-Extra: {safe}\n")
+
+
+# from pypa/distutils#244; needed only until that logic is always available
+def get_fullname(self):
+    return _distribution_fullname(self.get_name(), self.get_version())
+
+
+def _distribution_fullname(name: str, version: str) -> str:
+    """
+    >>> _distribution_fullname('setup.tools', '1.0-2')
+    'setup_tools-1.0.post2'
+    >>> _distribution_fullname('setup-tools', '1.2post2')
+    'setup_tools-1.2.post2'
+    >>> _distribution_fullname('setup-tools', '1.0-r2')
+    'setup_tools-1.0.post2'
+    >>> _distribution_fullname('setup.tools', '1.0.post')
+    'setup_tools-1.0.post0'
+    >>> _distribution_fullname('setup.tools', '1.0+ubuntu-1')
+    'setup_tools-1.0+ubuntu.1'
+    """
+    return "{}-{}".format(
+        canonicalize_name(name).replace('-', '_'),
+        canonicalize_version(version, strip_trailing_zero=False),
+    )
+
+
+def _safe_license_file(file):
+    # XXX: Do we need this after the deprecation discussed in #4892, #4896??
+    normalized = os.path.normpath(file).replace(os.sep, "/")
+    if "../" in normalized:
+        return os.path.basename(normalized)  # Temporarily restore pre PEP639 behaviour
+    return normalized
+
+
+_POSSIBLE_DYNAMIC_FIELDS = {
+    # Core Metadata Field x related Distribution attribute
+    "author": "author",
+    "author-email": "author_email",
+    "classifier": "classifiers",
+    "description": "long_description",
+    "description-content-type": "long_description_content_type",
+    "download-url": "download_url",
+    "home-page": "url",
+    "keywords": "keywords",
+    "license": "license",
+    # XXX: License-File is complicated because the user gives globs that are expanded
+    #      during the build. Without special handling it is likely always
+    #      marked as Dynamic, which is an acceptable outcome according to:
+    #      https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677
+    "license-file": "license_files",
+    "license-expression": "license_expression",  # PEP 639
+    "maintainer": "maintainer",
+    "maintainer-email": "maintainer_email",
+    "obsoletes": "obsoletes",
+    # "obsoletes-dist": "obsoletes_dist",  # NOT USED
+    "platform": "platforms",
+    "project-url": "project_urls",
+    "provides": "provides",
+    # "provides-dist": "provides_dist",  # NOT USED
+    "provides-extra": "extras_require",
+    "requires": "requires",
+    "requires-dist": "install_requires",
+    # "requires-external": "requires_external",  # NOT USED
+    "requires-python": "python_requires",
+    "summary": "description",
+    # "supported-platform": "supported_platforms",  # NOT USED
+}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_discovery.py b/.venv/lib/python3.12/site-packages/setuptools/_discovery.py
new file mode 100644
index 0000000..d1b4a0e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_discovery.py
@@ -0,0 +1,33 @@
+import functools
+import operator
+
+import packaging.requirements
+
+
+# from coherent.build.discovery
+def extras_from_dep(dep):
+    try:
+        markers = packaging.requirements.Requirement(dep).marker._markers
+    except AttributeError:
+        markers = ()
+    return set(
+        marker[2].value
+        for marker in markers
+        if isinstance(marker, tuple) and marker[0].value == 'extra'
+    )
+
+
+def extras_from_deps(deps):
+    """
+    >>> extras_from_deps(['requests'])
+    set()
+    >>> extras_from_deps(['pytest; extra == "test"'])
+    {'test'}
+    >>> sorted(extras_from_deps([
+    ...     'requests',
+    ...     'pytest; extra == "test"',
+    ...     'pytest-cov; extra == "test"',
+    ...     'sphinx; extra=="doc"']))
+    ['doc', 'test']
+    """
+    return functools.reduce(operator.or_, map(extras_from_dep, deps), set())
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/__init__.py
new file mode 100644
index 0000000..e374d5c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/__init__.py
@@ -0,0 +1,14 @@
+import importlib
+import sys
+
+__version__, _, _ = sys.version.partition(' ')
+
+
+try:
+    # Allow Debian and pkgsrc (only) to customize system
+    # behavior. Ref pypa/distutils#2 and pypa/distutils#16.
+    # This hook is deprecated and no other environments
+    # should use it.
+    importlib.import_module('_distutils_system_mod')
+except ImportError:
+    pass
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/_log.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_log.py
new file mode 100644
index 0000000..0148f15
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_log.py
@@ -0,0 +1,3 @@
+import logging
+
+log = logging.getLogger()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/_macos_compat.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_macos_compat.py
new file mode 100644
index 0000000..76ecb96
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_macos_compat.py
@@ -0,0 +1,12 @@
+import importlib
+import sys
+
+
+def bypass_compiler_fixup(cmd, args):
+    return cmd
+
+
+if sys.platform == 'darwin':
+    compiler_fixup = importlib.import_module('_osx_support').compiler_fixup
+else:
+    compiler_fixup = bypass_compiler_fixup
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/_modified.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_modified.py
new file mode 100644
index 0000000..f64cab7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_modified.py
@@ -0,0 +1,95 @@
+"""Timestamp comparison of files and groups of files."""
+
+from __future__ import annotations
+
+import functools
+import os.path
+from collections.abc import Callable, Iterable
+from typing import Literal, TypeVar
+
+from jaraco.functools import splat
+
+from .compat.py39 import zip_strict
+from .errors import DistutilsFileError
+
+_SourcesT = TypeVar(
+    "_SourcesT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
+)
+_TargetsT = TypeVar(
+    "_TargetsT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
+)
+
+
+def _newer(source, target):
+    return not os.path.exists(target) or (
+        os.path.getmtime(source) > os.path.getmtime(target)
+    )
+
+
+def newer(
+    source: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+    target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+) -> bool:
+    """
+    Is source modified more recently than target.
+
+    Returns True if 'source' is modified more recently than
+    'target' or if 'target' does not exist.
+
+    Raises DistutilsFileError if 'source' does not exist.
+    """
+    if not os.path.exists(source):
+        raise DistutilsFileError(f"file {os.path.abspath(source)!r} does not exist")
+
+    return _newer(source, target)
+
+
+def newer_pairwise(
+    sources: Iterable[_SourcesT],
+    targets: Iterable[_TargetsT],
+    newer: Callable[[_SourcesT, _TargetsT], bool] = newer,
+) -> tuple[list[_SourcesT], list[_TargetsT]]:
+    """
+    Filter filenames where sources are newer than targets.
+
+    Walk two filename iterables in parallel, testing if each source is newer
+    than its corresponding target.  Returns a pair of lists (sources,
+    targets) where source is newer than target, according to the semantics
+    of 'newer()'.
+    """
+    newer_pairs = filter(splat(newer), zip_strict(sources, targets))
+    return tuple(map(list, zip(*newer_pairs))) or ([], [])
+
+
+def newer_group(
+    sources: Iterable[str | bytes | os.PathLike[str] | os.PathLike[bytes]],
+    target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+    missing: Literal["error", "ignore", "newer"] = "error",
+) -> bool:
+    """
+    Is target out-of-date with respect to any file in sources.
+
+    Return True if 'target' is out-of-date with respect to any file
+    listed in 'sources'. In other words, if 'target' exists and is newer
+    than every file in 'sources', return False; otherwise return True.
+    ``missing`` controls how to handle a missing source file:
+
+    - error (default): allow the ``stat()`` call to fail.
+    - ignore: silently disregard any missing source files.
+    - newer: treat missing source files as "target out of date". This
+      mode is handy in "dry-run" mode: it will pretend to carry out
+      commands that wouldn't work because inputs are missing, but
+      that doesn't matter because dry-run won't run the commands.
+    """
+
+    def missing_as_newer(source):
+        return missing == 'newer' and not os.path.exists(source)
+
+    ignored = os.path.exists if missing == 'ignore' else None
+    return not os.path.exists(target) or any(
+        missing_as_newer(source) or _newer(source, target)
+        for source in filter(ignored, sources)
+    )
+
+
+newer_pairwise_group = functools.partial(newer_pairwise, newer=newer_group)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/_msvccompiler.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_msvccompiler.py
new file mode 100644
index 0000000..d07c86e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/_msvccompiler.py
@@ -0,0 +1,16 @@
+import warnings
+
+from .compilers.C import msvc
+
+__all__ = ["MSVCCompiler"]
+
+MSVCCompiler = msvc.Compiler
+
+
+def __getattr__(name):
+    if name == '_get_vc_env':
+        warnings.warn(
+            "_get_vc_env is private; find an alternative (pypa/distutils#340)"
+        )
+        return msvc._get_vc_env
+    raise AttributeError(name)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/archive_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/archive_util.py
new file mode 100644
index 0000000..4a7fb9c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/archive_util.py
@@ -0,0 +1,284 @@
+"""distutils.archive_util
+
+Utility functions for creating archive files (tarballs, zip files,
+that sort of thing)."""
+
+from __future__ import annotations
+
+import os
+from typing import Literal, overload
+
+try:
+    import zipfile
+except ImportError:
+    zipfile = None
+
+
+from ._log import log
+from .dir_util import mkpath
+from .errors import DistutilsExecError
+from .spawn import spawn
+
+try:
+    from pwd import getpwnam
+except ImportError:
+    getpwnam = None
+
+try:
+    from grp import getgrnam
+except ImportError:
+    getgrnam = None
+
+
+def _get_gid(name):
+    """Returns a gid, given a group name."""
+    if getgrnam is None or name is None:
+        return None
+    try:
+        result = getgrnam(name)
+    except KeyError:
+        result = None
+    if result is not None:
+        return result[2]
+    return None
+
+
+def _get_uid(name):
+    """Returns an uid, given a user name."""
+    if getpwnam is None or name is None:
+        return None
+    try:
+        result = getpwnam(name)
+    except KeyError:
+        result = None
+    if result is not None:
+        return result[2]
+    return None
+
+
+def make_tarball(
+    base_name: str,
+    base_dir: str | os.PathLike[str],
+    compress: Literal["gzip", "bzip2", "xz"] | None = "gzip",
+    verbose: bool = False,
+    owner: str | None = None,
+    group: str | None = None,
+) -> str:
+    """Create a (possibly compressed) tar file from all the files under
+    'base_dir'.
+
+    'compress' must be "gzip" (the default), "bzip2", "xz", or None.
+
+    'owner' and 'group' can be used to define an owner and a group for the
+    archive that is being built. If not provided, the current owner and group
+    will be used.
+
+    The output tar file will be named 'base_dir' +  ".tar", possibly plus
+    the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
+
+    Returns the output filename.
+    """
+    tar_compression = {
+        'gzip': 'gz',
+        'bzip2': 'bz2',
+        'xz': 'xz',
+        None: '',
+    }
+    compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz'}
+
+    # flags for compression program, each element of list will be an argument
+    if compress is not None and compress not in compress_ext.keys():
+        raise ValueError(
+            "bad value for 'compress': must be None, 'gzip', 'bzip2', 'xz'"
+        )
+
+    archive_name = base_name + '.tar'
+    archive_name += compress_ext.get(compress, '')
+
+    mkpath(os.path.dirname(archive_name))
+
+    # creating the tarball
+    import tarfile  # late import so Python build itself doesn't break
+
+    log.info('Creating tar archive')
+
+    uid = _get_uid(owner)
+    gid = _get_gid(group)
+
+    def _set_uid_gid(tarinfo):
+        if gid is not None:
+            tarinfo.gid = gid
+            tarinfo.gname = group
+        if uid is not None:
+            tarinfo.uid = uid
+            tarinfo.uname = owner
+        return tarinfo
+
+    tar = tarfile.open(archive_name, f'w|{tar_compression[compress]}')
+    try:
+        tar.add(base_dir, filter=_set_uid_gid)
+    finally:
+        tar.close()
+
+    return archive_name
+
+
+def make_zipfile(
+    base_name: str,
+    base_dir: str | os.PathLike[str],
+    verbose: bool = False,
+) -> str:
+    """Create a zip file from all the files under 'base_dir'.
+
+    The output zip file will be named 'base_name' + ".zip".  Uses either the
+    "zipfile" Python module (if available) or the InfoZIP "zip" utility
+    (if installed and found on the default search path).  If neither tool is
+    available, raises DistutilsExecError.  Returns the name of the output zip
+    file.
+    """
+    zip_filename = base_name + ".zip"
+    mkpath(os.path.dirname(zip_filename))
+
+    # If zipfile module is not available, try spawning an external
+    # 'zip' command.
+    if zipfile is None:
+        if verbose:
+            zipoptions = "-r"
+        else:
+            zipoptions = "-rq"
+
+        try:
+            spawn(["zip", zipoptions, zip_filename, base_dir])
+        except DistutilsExecError:
+            # XXX really should distinguish between "couldn't find
+            # external 'zip' command" and "zip failed".
+            raise DistutilsExecError(
+                f"unable to create zip file '{zip_filename}': "
+                "could neither import the 'zipfile' module nor "
+                "find a standalone zip utility"
+            )
+
+    else:
+        log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
+
+        try:
+            zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED)
+        except RuntimeError:
+            zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED)
+
+        with zip:
+            if base_dir != os.curdir:
+                path = os.path.normpath(os.path.join(base_dir, ''))
+                zip.write(path, path)
+                log.info("adding '%s'", path)
+            for dirpath, dirnames, filenames in os.walk(base_dir):
+                for name in dirnames:
+                    path = os.path.normpath(os.path.join(dirpath, name, ''))
+                    zip.write(path, path)
+                    log.info("adding '%s'", path)
+                for name in filenames:
+                    path = os.path.normpath(os.path.join(dirpath, name))
+                    if os.path.isfile(path):
+                        zip.write(path, path)
+                        log.info("adding '%s'", path)
+
+    return zip_filename
+
+
+ARCHIVE_FORMATS = {
+    'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
+    'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
+    'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
+    'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
+    'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
+    'zip': (make_zipfile, [], "ZIP file"),
+}
+
+
+def check_archive_formats(formats):
+    """Returns the first format from the 'format' list that is unknown.
+
+    If all formats are known, returns None
+    """
+    for format in formats:
+        if format not in ARCHIVE_FORMATS:
+            return format
+    return None
+
+
+@overload
+def make_archive(
+    base_name: str,
+    format: str,
+    root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+    base_dir: str | None = None,
+    verbose: bool = False,
+    owner: str | None = None,
+    group: str | None = None,
+) -> str: ...
+@overload
+def make_archive(
+    base_name: str | os.PathLike[str],
+    format: str,
+    root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+    base_dir: str | None = None,
+    verbose: bool = False,
+    owner: str | None = None,
+    group: str | None = None,
+) -> str: ...
+def make_archive(
+    base_name: str | os.PathLike[str],
+    format: str,
+    root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+    base_dir: str | None = None,
+    verbose: bool = False,
+    owner: str | None = None,
+    group: str | None = None,
+) -> str:
+    """Create an archive file (eg. zip or tar).
+
+    'base_name' is the name of the file to create, minus any format-specific
+    extension; 'format' is the archive format: one of "zip", "tar", "gztar",
+    "bztar", "xztar", or "ztar".
+
+    'root_dir' is a directory that will be the root directory of the
+    archive; ie. we typically chdir into 'root_dir' before creating the
+    archive.  'base_dir' is the directory where we start archiving from;
+    ie. 'base_dir' will be the common prefix of all files and
+    directories in the archive.  'root_dir' and 'base_dir' both default
+    to the current directory.  Returns the name of the archive file.
+
+    'owner' and 'group' are used when creating a tar archive. By default,
+    uses the current owner and group.
+    """
+    save_cwd = os.getcwd()
+    if root_dir is not None:
+        log.debug("changing into '%s'", root_dir)
+        base_name = os.path.abspath(base_name)
+        os.chdir(root_dir)
+
+    if base_dir is None:
+        base_dir = os.curdir
+
+    kwargs: dict[str, bool | None] = {}
+
+    try:
+        format_info = ARCHIVE_FORMATS[format]
+    except KeyError:
+        raise ValueError(f"unknown archive format '{format}'")
+
+    func = format_info[0]
+    kwargs.update(format_info[1])
+
+    if format != 'zip':
+        kwargs['owner'] = owner
+        kwargs['group'] = group
+
+    try:
+        filename = func(base_name, base_dir, **kwargs)
+    finally:
+        if root_dir is not None:
+            log.debug("changing back to '%s'", save_cwd)
+            os.chdir(save_cwd)
+
+    return filename
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/ccompiler.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/ccompiler.py
new file mode 100644
index 0000000..58bc6a5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/ccompiler.py
@@ -0,0 +1,26 @@
+from .compat.numpy import (  # noqa: F401
+    _default_compilers,
+    compiler_class,
+)
+from .compilers.C import base
+from .compilers.C.base import (
+    gen_lib_options,
+    gen_preprocess_options,
+    get_default_compiler,
+    new_compiler,
+    show_compilers,
+)
+from .compilers.C.errors import CompileError, LinkError
+
+__all__ = [
+    'CompileError',
+    'LinkError',
+    'gen_lib_options',
+    'gen_preprocess_options',
+    'get_default_compiler',
+    'new_compiler',
+    'show_compilers',
+]
+
+
+CCompiler = base.Compiler
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/cmd.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/cmd.py
new file mode 100644
index 0000000..530cc10
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/cmd.py
@@ -0,0 +1,535 @@
+"""distutils.cmd
+
+Provides the Command class, the base class for the command classes
+in the distutils.command package.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import re
+import sys
+from abc import abstractmethod
+from collections.abc import Callable, MutableSequence
+from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
+
+from . import _modified, archive_util, dir_util, file_util, util
+from ._log import log
+from .errors import DistutilsOptionError
+
+if TYPE_CHECKING:
+    # type-only import because of mutual dependence between these classes
+    from distutils.dist import Distribution
+
+    from typing_extensions import TypeVarTuple, Unpack
+
+    _Ts = TypeVarTuple("_Ts")
+
+_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
+_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
+_CommandT = TypeVar("_CommandT", bound="Command")
+
+
+class Command:
+    """Abstract base class for defining command classes, the "worker bees"
+    of the Distutils.  A useful analogy for command classes is to think of
+    them as subroutines with local variables called "options".  The options
+    are "declared" in 'initialize_options()' and "defined" (given their
+    final values, aka "finalized") in 'finalize_options()', both of which
+    must be defined by every command class.  The distinction between the
+    two is necessary because option values might come from the outside
+    world (command line, config file, ...), and any options dependent on
+    other options must be computed *after* these outside influences have
+    been processed -- hence 'finalize_options()'.  The "body" of the
+    subroutine, where it does all its work based on the values of its
+    options, is the 'run()' method, which must also be implemented by every
+    command class.
+    """
+
+    # 'sub_commands' formalizes the notion of a "family" of commands,
+    # eg. "install" as the parent with sub-commands "install_lib",
+    # "install_headers", etc.  The parent of a family of commands
+    # defines 'sub_commands' as a class attribute; it's a list of
+    #    (command_name : string, predicate : unbound_method | string | None)
+    # tuples, where 'predicate' is a method of the parent command that
+    # determines whether the corresponding command is applicable in the
+    # current situation.  (Eg. we "install_headers" is only applicable if
+    # we have any C header files to install.)  If 'predicate' is None,
+    # that command is always applicable.
+    #
+    # 'sub_commands' is usually defined at the *end* of a class, because
+    # predicates can be unbound methods, so they must already have been
+    # defined.  The canonical example is the "install" command.
+    sub_commands: ClassVar[  # Any to work around variance issues
+        list[tuple[str, Callable[[Any], bool] | None]]
+    ] = []
+
+    user_options: ClassVar[
+        # Specifying both because list is invariant. Avoids mypy override assignment issues
+        list[tuple[str, str, str]] | list[tuple[str, str | None, str]]
+    ] = []
+
+    # -- Creation/initialization methods -------------------------------
+
+    def __init__(self, dist: Distribution) -> None:
+        """Create and initialize a new Command object.  Most importantly,
+        invokes the 'initialize_options()' method, which is the real
+        initializer and depends on the actual command being
+        instantiated.
+        """
+        # late import because of mutual dependence between these classes
+        from distutils.dist import Distribution
+
+        if not isinstance(dist, Distribution):
+            raise TypeError("dist must be a Distribution instance")
+        if self.__class__ is Command:
+            raise RuntimeError("Command is an abstract class")
+
+        self.distribution = dist
+        self.initialize_options()
+
+        # Per-command versions of the global flags, so that the user can
+        # customize Distutils' behaviour command-by-command and let some
+        # commands fall back on the Distribution's behaviour. None means
+        # "not defined, check self.distribution's copy".
+
+        # verbose is largely ignored, but needs to be set for
+        # backwards compatibility (I think)?
+        self.verbose = dist.verbose
+
+        # Some commands define a 'self.force' option to ignore file
+        # timestamps, but methods defined *here* assume that
+        # 'self.force' exists for all commands.  So define it here
+        # just to be safe.
+        self.force = None
+
+        # The 'help' flag is just used for command-line parsing, so
+        # none of that complicated bureaucracy is needed.
+        self.help = False
+
+        # 'finalized' records whether or not 'finalize_options()' has been
+        # called.  'finalize_options()' itself should not pay attention to
+        # this flag: it is the business of 'ensure_finalized()', which
+        # always calls 'finalize_options()', to respect/update it.
+        self.finalized = False
+
+    def ensure_finalized(self) -> None:
+        if not self.finalized:
+            self.finalize_options()
+        self.finalized = True
+
+    # Subclasses must define:
+    #   initialize_options()
+    #     provide default values for all options; may be customized by
+    #     setup script, by options from config file(s), or by command-line
+    #     options
+    #   finalize_options()
+    #     decide on the final values for all options; this is called
+    #     after all possible intervention from the outside world
+    #     (command-line, option file, etc.) has been processed
+    #   run()
+    #     run the command: do whatever it is we're here to do,
+    #     controlled by the command's various option values
+
+    @abstractmethod
+    def initialize_options(self) -> None:
+        """Set default values for all the options that this command
+        supports.  Note that these defaults may be overridden by other
+        commands, by the setup script, by config files, or by the
+        command-line.  Thus, this is not the place to code dependencies
+        between options; generally, 'initialize_options()' implementations
+        are just a bunch of "self.foo = None" assignments.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            f"abstract method -- subclass {self.__class__} must override"
+        )
+
+    @abstractmethod
+    def finalize_options(self) -> None:
+        """Set final values for all the options that this command supports.
+        This is always called as late as possible, ie.  after any option
+        assignments from the command-line or from other commands have been
+        done.  Thus, this is the place to code option dependencies: if
+        'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as
+        long as 'foo' still has the same value it was assigned in
+        'initialize_options()'.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            f"abstract method -- subclass {self.__class__} must override"
+        )
+
+    def dump_options(self, header=None, indent=""):
+        from distutils.fancy_getopt import longopt_xlate
+
+        if header is None:
+            header = f"command options for '{self.get_command_name()}':"
+        self.announce(indent + header, level=logging.INFO)
+        indent = indent + "  "
+        for option, _, _ in self.user_options:
+            option = option.translate(longopt_xlate)
+            if option[-1] == "=":
+                option = option[:-1]
+            value = getattr(self, option)
+            self.announce(indent + f"{option} = {value}", level=logging.INFO)
+
+    @abstractmethod
+    def run(self) -> None:
+        """A command's raison d'etre: carry out the action it exists to
+        perform, controlled by the options initialized in
+        'initialize_options()', customized by other commands, the setup
+        script, the command-line, and config files, and finalized in
+        'finalize_options()'.  All terminal output and filesystem
+        interaction should be done by 'run()'.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            f"abstract method -- subclass {self.__class__} must override"
+        )
+
+    def announce(self, msg: object, level: int = logging.DEBUG) -> None:
+        log.log(level, msg)
+
+    def debug_print(self, msg: object) -> None:
+        """Print 'msg' to stdout if the global DEBUG (taken from the
+        DISTUTILS_DEBUG environment variable) flag is true.
+        """
+        from distutils.debug import DEBUG
+
+        if DEBUG:
+            print(msg)
+            sys.stdout.flush()
+
+    # -- Option validation methods -------------------------------------
+    # (these are very handy in writing the 'finalize_options()' method)
+    #
+    # NB. the general philosophy here is to ensure that a particular option
+    # value meets certain type and value constraints.  If not, we try to
+    # force it into conformance (eg. if we expect a list but have a string,
+    # split the string on comma and/or whitespace).  If we can't force the
+    # option into conformance, raise DistutilsOptionError.  Thus, command
+    # classes need do nothing more than (eg.)
+    #   self.ensure_string_list('foo')
+    # and they can be guaranteed that thereafter, self.foo will be
+    # a list of strings.
+
+    def _ensure_stringlike(self, option, what, default=None):
+        val = getattr(self, option)
+        if val is None:
+            setattr(self, option, default)
+            return default
+        elif not isinstance(val, str):
+            raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
+        return val
+
+    def ensure_string(self, option: str, default: str | None = None) -> None:
+        """Ensure that 'option' is a string; if not defined, set it to
+        'default'.
+        """
+        self._ensure_stringlike(option, "string", default)
+
+    def ensure_string_list(self, option: str) -> None:
+        r"""Ensure that 'option' is a list of strings.  If 'option' is
+        currently a string, we split it either on /,\s*/ or /\s+/, so
+        "foo bar baz", "foo,bar,baz", and "foo,   bar baz" all become
+        ["foo", "bar", "baz"].
+        """
+        val = getattr(self, option)
+        if val is None:
+            return
+        elif isinstance(val, str):
+            setattr(self, option, re.split(r',\s*|\s+', val))
+        else:
+            if isinstance(val, list):
+                ok = all(isinstance(v, str) for v in val)
+            else:
+                ok = False
+            if not ok:
+                raise DistutilsOptionError(
+                    f"'{option}' must be a list of strings (got {val!r})"
+                )
+
+    def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
+        val = self._ensure_stringlike(option, what, default)
+        if val is not None and not tester(val):
+            raise DistutilsOptionError(
+                ("error in '%s' option: " + error_fmt) % (option, val)
+            )
+
+    def ensure_filename(self, option: str) -> None:
+        """Ensure that 'option' is the name of an existing file."""
+        self._ensure_tested_string(
+            option, os.path.isfile, "filename", "'%s' does not exist or is not a file"
+        )
+
+    def ensure_dirname(self, option: str) -> None:
+        self._ensure_tested_string(
+            option,
+            os.path.isdir,
+            "directory name",
+            "'%s' does not exist or is not a directory",
+        )
+
+    # -- Convenience methods for commands ------------------------------
+
+    def get_command_name(self) -> str:
+        if hasattr(self, 'command_name'):
+            return self.command_name
+        else:
+            return self.__class__.__name__
+
+    def set_undefined_options(
+        self, src_cmd: str, *option_pairs: tuple[str, str]
+    ) -> None:
+        """Set the values of any "undefined" options from corresponding
+        option values in some other command object.  "Undefined" here means
+        "is None", which is the convention used to indicate that an option
+        has not been changed between 'initialize_options()' and
+        'finalize_options()'.  Usually called from 'finalize_options()' for
+        options that depend on some other command rather than another
+        option of the same command.  'src_cmd' is the other command from
+        which option values will be taken (a command object will be created
+        for it if necessary); the remaining arguments are
+        '(src_option,dst_option)' tuples which mean "take the value of
+        'src_option' in the 'src_cmd' command object, and copy it to
+        'dst_option' in the current command object".
+        """
+        # Option_pairs: list of (src_option, dst_option) tuples
+        src_cmd_obj = self.distribution.get_command_obj(src_cmd)
+        src_cmd_obj.ensure_finalized()
+        for src_option, dst_option in option_pairs:
+            if getattr(self, dst_option) is None:
+                setattr(self, dst_option, getattr(src_cmd_obj, src_option))
+
+    # NOTE: Because distutils is private to Setuptools and not all commands are exposed here,
+    # not every possible command is enumerated in the signature.
+    def get_finalized_command(self, command: str, create: bool = True) -> Command:
+        """Wrapper around Distribution's 'get_command_obj()' method: find
+        (create if necessary and 'create' is true) the command object for
+        'command', call its 'ensure_finalized()' method, and return the
+        finalized command object.
+        """
+        cmd_obj = self.distribution.get_command_obj(command, create)
+        cmd_obj.ensure_finalized()
+        return cmd_obj
+
+    # XXX rename to 'get_reinitialized_command()'? (should do the
+    # same in dist.py, if so)
+    @overload
+    def reinitialize_command(
+        self, command: str, reinit_subcommands: bool = False
+    ) -> Command: ...
+    @overload
+    def reinitialize_command(
+        self, command: _CommandT, reinit_subcommands: bool = False
+    ) -> _CommandT: ...
+    def reinitialize_command(
+        self, command: str | Command, reinit_subcommands=False
+    ) -> Command:
+        return self.distribution.reinitialize_command(command, reinit_subcommands)
+
+    def run_command(self, command: str) -> None:
+        """Run some other command: uses the 'run_command()' method of
+        Distribution, which creates and finalizes the command object if
+        necessary and then invokes its 'run()' method.
+        """
+        self.distribution.run_command(command)
+
+    def get_sub_commands(self) -> list[str]:
+        """Determine the sub-commands that are relevant in the current
+        distribution (ie., that need to be run).  This is based on the
+        'sub_commands' class attribute: each tuple in that list may include
+        a method that we call to determine if the subcommand needs to be
+        run for the current distribution.  Return a list of command names.
+        """
+        commands = []
+        for cmd_name, method in self.sub_commands:
+            if method is None or method(self):
+                commands.append(cmd_name)
+        return commands
+
+    # -- External world manipulation -----------------------------------
+
+    def warn(self, msg: object) -> None:
+        log.warning("warning: %s: %s\n", self.get_command_name(), msg)
+
+    def execute(
+        self,
+        func: Callable[[Unpack[_Ts]], object],
+        args: tuple[Unpack[_Ts]],
+        msg: object = None,
+        level: int = 1,
+    ) -> None:
+        util.execute(func, args, msg)
+
+    def mkpath(self, name: str, mode: int = 0o777) -> None:
+        dir_util.mkpath(name, mode)
+
+    @overload
+    def copy_file(
+        self,
+        infile: str | os.PathLike[str],
+        outfile: _StrPathT,
+        preserve_mode: bool = True,
+        preserve_times: bool = True,
+        link: str | None = None,
+        level: int = 1,
+    ) -> tuple[_StrPathT | str, bool]: ...
+    @overload
+    def copy_file(
+        self,
+        infile: bytes | os.PathLike[bytes],
+        outfile: _BytesPathT,
+        preserve_mode: bool = True,
+        preserve_times: bool = True,
+        link: str | None = None,
+        level: int = 1,
+    ) -> tuple[_BytesPathT | bytes, bool]: ...
+    def copy_file(
+        self,
+        infile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        preserve_mode: bool = True,
+        preserve_times: bool = True,
+        link: str | None = None,
+        level: int = 1,
+    ) -> tuple[str | os.PathLike[str] | bytes | os.PathLike[bytes], bool]:
+        """Copy a file respecting verbose, dry-run and force flags.  (The
+        former two default to whatever is in the Distribution object, and
+        the latter defaults to false for commands that don't define it.)"""
+        return file_util.copy_file(
+            infile,
+            outfile,
+            preserve_mode,
+            preserve_times,
+            not self.force,
+            link,
+        )
+
+    def copy_tree(
+        self,
+        infile: str | os.PathLike[str],
+        outfile: str,
+        preserve_mode: bool = True,
+        preserve_times: bool = True,
+        preserve_symlinks: bool = False,
+        level: int = 1,
+    ) -> list[str]:
+        """Copy an entire directory tree respecting verbose, dry-run,
+        and force flags.
+        """
+        return dir_util.copy_tree(
+            infile,
+            outfile,
+            preserve_mode,
+            preserve_times,
+            preserve_symlinks,
+            not self.force,
+        )
+
+    @overload
+    def move_file(
+        self, src: str | os.PathLike[str], dst: _StrPathT, level: int = 1
+    ) -> _StrPathT | str: ...
+    @overload
+    def move_file(
+        self, src: bytes | os.PathLike[bytes], dst: _BytesPathT, level: int = 1
+    ) -> _BytesPathT | bytes: ...
+    def move_file(
+        self,
+        src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        level: int = 1,
+    ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
+        """Move a file respecting dry-run flag."""
+        return file_util.move_file(src, dst)
+
+    def spawn(
+        self, cmd: MutableSequence[str], search_path: bool = True, level: int = 1
+    ) -> None:
+        """Spawn an external command respecting dry-run flag."""
+        from distutils.spawn import spawn
+
+        spawn(cmd, search_path)
+
+    @overload
+    def make_archive(
+        self,
+        base_name: str,
+        format: str,
+        root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+        base_dir: str | None = None,
+        owner: str | None = None,
+        group: str | None = None,
+    ) -> str: ...
+    @overload
+    def make_archive(
+        self,
+        base_name: str | os.PathLike[str],
+        format: str,
+        root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        base_dir: str | None = None,
+        owner: str | None = None,
+        group: str | None = None,
+    ) -> str: ...
+    def make_archive(
+        self,
+        base_name: str | os.PathLike[str],
+        format: str,
+        root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+        base_dir: str | None = None,
+        owner: str | None = None,
+        group: str | None = None,
+    ) -> str:
+        return archive_util.make_archive(
+            base_name,
+            format,
+            root_dir,
+            base_dir,
+            owner=owner,
+            group=group,
+        )
+
+    def make_file(
+        self,
+        infiles: str | list[str] | tuple[str, ...],
+        outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        func: Callable[[Unpack[_Ts]], object],
+        args: tuple[Unpack[_Ts]],
+        exec_msg: object = None,
+        skip_msg: object = None,
+        level: int = 1,
+    ) -> None:
+        """Special case of 'execute()' for operations that process one or
+        more input files and generate one output file.  Works just like
+        'execute()', except the operation is skipped and a different
+        message printed if 'outfile' already exists and is newer than all
+        files listed in 'infiles'.  If the command defined 'self.force',
+        and it is true, then the command is unconditionally run -- does no
+        timestamp checks.
+        """
+        if skip_msg is None:
+            skip_msg = f"skipping {outfile} (inputs unchanged)"
+
+        # Allow 'infiles' to be a single string
+        if isinstance(infiles, str):
+            infiles = (infiles,)
+        elif not isinstance(infiles, (list, tuple)):
+            raise TypeError("'infiles' must be a string, or a list or tuple of strings")
+
+        if exec_msg is None:
+            exec_msg = "generating {} from {}".format(outfile, ', '.join(infiles))
+
+        # If 'outfile' must be regenerated (either because it doesn't
+        # exist, is out-of-date, or the 'force' flag is true) then
+        # perform the action that presumably regenerates it
+        if self.force or _modified.newer_group(infiles, outfile):
+            self.execute(func, args, exec_msg, level)
+        # Otherwise, print the "skip" message
+        else:
+            log.debug(skip_msg)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/__init__.py
new file mode 100644
index 0000000..0f8a169
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/__init__.py
@@ -0,0 +1,23 @@
+"""distutils.command
+
+Package containing implementation of all the standard Distutils
+commands."""
+
+__all__ = [
+    'build',
+    'build_py',
+    'build_ext',
+    'build_clib',
+    'build_scripts',
+    'clean',
+    'install',
+    'install_lib',
+    'install_headers',
+    'install_scripts',
+    'install_data',
+    'sdist',
+    'bdist',
+    'bdist_dumb',
+    'bdist_rpm',
+    'check',
+]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/_framework_compat.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/_framework_compat.py
new file mode 100644
index 0000000..00d34bc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/_framework_compat.py
@@ -0,0 +1,54 @@
+"""
+Backward compatibility for homebrew builds on macOS.
+"""
+
+import functools
+import os
+import subprocess
+import sys
+import sysconfig
+
+
+@functools.lru_cache
+def enabled():
+    """
+    Only enabled for Python 3.9 framework homebrew builds
+    except ensurepip and venv.
+    """
+    PY39 = (3, 9) < sys.version_info < (3, 10)
+    framework = sys.platform == 'darwin' and sys._framework
+    homebrew = "Cellar" in sysconfig.get_config_var('projectbase')
+    venv = sys.prefix != sys.base_prefix
+    ensurepip = os.environ.get("ENSUREPIP_OPTIONS")
+    return PY39 and framework and homebrew and not venv and not ensurepip
+
+
+schemes = dict(
+    osx_framework_library=dict(
+        stdlib='{installed_base}/{platlibdir}/python{py_version_short}',
+        platstdlib='{platbase}/{platlibdir}/python{py_version_short}',
+        purelib='{homebrew_prefix}/lib/python{py_version_short}/site-packages',
+        platlib='{homebrew_prefix}/{platlibdir}/python{py_version_short}/site-packages',
+        include='{installed_base}/include/python{py_version_short}{abiflags}',
+        platinclude='{installed_platbase}/include/python{py_version_short}{abiflags}',
+        scripts='{homebrew_prefix}/bin',
+        data='{homebrew_prefix}',
+    )
+)
+
+
+@functools.lru_cache
+def vars():
+    if not enabled():
+        return {}
+    homebrew_prefix = subprocess.check_output(['brew', '--prefix'], text=True).strip()
+    return locals()
+
+
+def scheme(name):
+    """
+    Override the selected scheme for posix_prefix.
+    """
+    if not enabled() or not name.endswith('_prefix'):
+        return name
+    return 'osx_framework_library'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist.py
new file mode 100644
index 0000000..07811aa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist.py
@@ -0,0 +1,167 @@
+"""distutils.command.bdist
+
+Implements the Distutils 'bdist' command (create a built [binary]
+distribution)."""
+
+from __future__ import annotations
+
+import os
+import warnings
+from collections.abc import Callable
+from typing import TYPE_CHECKING, ClassVar
+
+from ..core import Command
+from ..errors import DistutilsOptionError, DistutilsPlatformError
+from ..util import get_platform
+
+if TYPE_CHECKING:
+    from typing_extensions import deprecated
+else:
+
+    def deprecated(message):
+        return lambda fn: fn
+
+
+def show_formats():
+    """Print list of available formats (arguments to "--format" option)."""
+    from ..fancy_getopt import FancyGetopt
+
+    formats = [
+        ("formats=" + format, None, bdist.format_commands[format][1])
+        for format in bdist.format_commands
+    ]
+    pretty_printer = FancyGetopt(formats)
+    pretty_printer.print_help("List of available distribution formats:")
+
+
+class ListCompat(dict[str, tuple[str, str]]):
+    # adapter to allow for Setuptools compatibility in format_commands
+    @deprecated("format_commands is now a dict. append is deprecated.")
+    def append(self, item: object) -> None:
+        warnings.warn(
+            "format_commands is now a dict. append is deprecated.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+
+class bdist(Command):
+    description = "create a built (binary) distribution"
+
+    user_options = [
+        ('bdist-base=', 'b', "temporary directory for creating built distributions"),
+        (
+            'plat-name=',
+            'p',
+            "platform name to embed in generated filenames "
+            f"[default: {get_platform()}]",
+        ),
+        ('formats=', None, "formats for distribution (comma-separated list)"),
+        (
+            'dist-dir=',
+            'd',
+            "directory to put final built distributions in [default: dist]",
+        ),
+        ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
+        (
+            'owner=',
+            'u',
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            'group=',
+            'g',
+            "Group name used when creating a tar file [default: current group]",
+        ),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['skip-build']
+
+    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
+        ('help-formats', None, "lists available distribution formats", show_formats),
+    ]
+
+    # The following commands do not take a format option from bdist
+    no_format_option: ClassVar[tuple[str, ...]] = ('bdist_rpm',)
+
+    # This won't do in reality: will need to distinguish RPM-ish Linux,
+    # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
+    default_format: ClassVar[dict[str, str]] = {'posix': 'gztar', 'nt': 'zip'}
+
+    # Define commands in preferred order for the --help-formats option
+    format_commands = ListCompat({
+        'rpm': ('bdist_rpm', "RPM distribution"),
+        'gztar': ('bdist_dumb', "gzip'ed tar file"),
+        'bztar': ('bdist_dumb', "bzip2'ed tar file"),
+        'xztar': ('bdist_dumb', "xz'ed tar file"),
+        'ztar': ('bdist_dumb', "compressed tar file"),
+        'tar': ('bdist_dumb', "tar file"),
+        'zip': ('bdist_dumb', "ZIP file"),
+    })
+
+    # for compatibility until consumers only reference format_commands
+    format_command = format_commands
+
+    def initialize_options(self):
+        self.bdist_base = None
+        self.plat_name = None
+        self.formats = None
+        self.dist_dir = None
+        self.skip_build = False
+        self.group = None
+        self.owner = None
+
+    def finalize_options(self) -> None:
+        # have to finalize 'plat_name' before 'bdist_base'
+        if self.plat_name is None:
+            if self.skip_build:
+                self.plat_name = get_platform()
+            else:
+                self.plat_name = self.get_finalized_command('build').plat_name
+
+        # 'bdist_base' -- parent of per-built-distribution-format
+        # temporary directories (eg. we'll probably have
+        # "build/bdist./dumb", "build/bdist./rpm", etc.)
+        if self.bdist_base is None:
+            build_base = self.get_finalized_command('build').build_base
+            self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name)
+
+        self.ensure_string_list('formats')
+        if self.formats is None:
+            try:
+                self.formats = [self.default_format[os.name]]
+            except KeyError:
+                raise DistutilsPlatformError(
+                    "don't know how to create built distributions "
+                    f"on platform {os.name}"
+                )
+
+        if self.dist_dir is None:
+            self.dist_dir = "dist"
+
+    def run(self) -> None:
+        # Figure out which sub-commands we need to run.
+        commands = []
+        for format in self.formats:
+            try:
+                commands.append(self.format_commands[format][0])
+            except KeyError:
+                raise DistutilsOptionError(f"invalid format '{format}'")
+
+        # Reinitialize and run each command.
+        for i in range(len(self.formats)):
+            cmd_name = commands[i]
+            sub_cmd = self.reinitialize_command(cmd_name)
+            if cmd_name not in self.no_format_option:
+                sub_cmd.format = self.formats[i]
+
+            # passing the owner and group names for tar archiving
+            if cmd_name == 'bdist_dumb':
+                sub_cmd.owner = self.owner
+                sub_cmd.group = self.group
+
+            # If we're going to need to run this command again, tell it to
+            # keep its temporary files around so subsequent runs go faster.
+            if cmd_name in commands[i + 1 :]:
+                sub_cmd.keep_temp = True
+            self.run_command(cmd_name)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_dumb.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_dumb.py
new file mode 100644
index 0000000..c49ef25
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_dumb.py
@@ -0,0 +1,141 @@
+"""distutils.command.bdist_dumb
+
+Implements the Distutils 'bdist_dumb' command (create a "dumb" built
+distribution -- i.e., just an archive to be unpacked under $prefix or
+$exec_prefix)."""
+
+import os
+from distutils._log import log
+from typing import ClassVar
+
+from ..core import Command
+from ..dir_util import ensure_relative, remove_tree
+from ..errors import DistutilsPlatformError
+from ..sysconfig import get_python_version
+from ..util import get_platform
+
+
+class bdist_dumb(Command):
+    description = "create a \"dumb\" built distribution"
+
+    user_options = [
+        ('bdist-dir=', 'd', "temporary directory for creating the distribution"),
+        (
+            'plat-name=',
+            'p',
+            "platform name to embed in generated filenames "
+            f"[default: {get_platform()}]",
+        ),
+        (
+            'format=',
+            'f',
+            "archive format to create (tar, gztar, bztar, xztar, ztar, zip)",
+        ),
+        (
+            'keep-temp',
+            'k',
+            "keep the pseudo-installation tree around after creating the distribution archive",
+        ),
+        ('dist-dir=', 'd', "directory to put final built distributions in"),
+        ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
+        (
+            'relative',
+            None,
+            "build the archive using relative paths [default: false]",
+        ),
+        (
+            'owner=',
+            'u',
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            'group=',
+            'g',
+            "Group name used when creating a tar file [default: current group]",
+        ),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['keep-temp', 'skip-build', 'relative']
+
+    default_format = {'posix': 'gztar', 'nt': 'zip'}
+
+    def initialize_options(self):
+        self.bdist_dir = None
+        self.plat_name = None
+        self.format = None
+        self.keep_temp = False
+        self.dist_dir = None
+        self.skip_build = None
+        self.relative = False
+        self.owner = None
+        self.group = None
+
+    def finalize_options(self):
+        if self.bdist_dir is None:
+            bdist_base = self.get_finalized_command('bdist').bdist_base
+            self.bdist_dir = os.path.join(bdist_base, 'dumb')
+
+        if self.format is None:
+            try:
+                self.format = self.default_format[os.name]
+            except KeyError:
+                raise DistutilsPlatformError(
+                    "don't know how to create dumb built distributions "
+                    f"on platform {os.name}"
+                )
+
+        self.set_undefined_options(
+            'bdist',
+            ('dist_dir', 'dist_dir'),
+            ('plat_name', 'plat_name'),
+            ('skip_build', 'skip_build'),
+        )
+
+    def run(self):
+        if not self.skip_build:
+            self.run_command('build')
+
+        install = self.reinitialize_command('install', reinit_subcommands=True)
+        install.root = self.bdist_dir
+        install.skip_build = self.skip_build
+        install.warn_dir = False
+
+        log.info("installing to %s", self.bdist_dir)
+        self.run_command('install')
+
+        # And make an archive relative to the root of the
+        # pseudo-installation tree.
+        archive_basename = f"{self.distribution.get_fullname()}.{self.plat_name}"
+
+        pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
+        if not self.relative:
+            archive_root = self.bdist_dir
+        else:
+            if self.distribution.has_ext_modules() and (
+                install.install_base != install.install_platbase
+            ):
+                raise DistutilsPlatformError(
+                    "can't make a dumb built distribution where "
+                    f"base and platbase are different ({install.install_base!r}, {install.install_platbase!r})"
+                )
+            else:
+                archive_root = os.path.join(
+                    self.bdist_dir, ensure_relative(install.install_base)
+                )
+
+        # Make the archive
+        filename = self.make_archive(
+            pseudoinstall_root,
+            self.format,
+            root_dir=archive_root,
+            owner=self.owner,
+            group=self.group,
+        )
+        if self.distribution.has_ext_modules():
+            pyversion = get_python_version()
+        else:
+            pyversion = 'any'
+        self.distribution.dist_files.append(('bdist_dumb', pyversion, filename))
+
+        if not self.keep_temp:
+            remove_tree(self.bdist_dir)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_rpm.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_rpm.py
new file mode 100644
index 0000000..dfa5f99
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/bdist_rpm.py
@@ -0,0 +1,597 @@
+"""distutils.command.bdist_rpm
+
+Implements the Distutils 'bdist_rpm' command (create RPM source and binary
+distributions)."""
+
+import os
+import subprocess
+import sys
+from distutils._log import log
+from typing import ClassVar
+
+from ..core import Command
+from ..debug import DEBUG
+from ..errors import (
+    DistutilsExecError,
+    DistutilsFileError,
+    DistutilsOptionError,
+    DistutilsPlatformError,
+)
+from ..file_util import write_file
+from ..sysconfig import get_python_version
+
+
+class bdist_rpm(Command):
+    description = "create an RPM distribution"
+
+    user_options = [
+        ('bdist-base=', None, "base directory for creating built distributions"),
+        (
+            'rpm-base=',
+            None,
+            "base directory for creating RPMs (defaults to \"rpm\" under "
+            "--bdist-base; must be specified for RPM 2)",
+        ),
+        (
+            'dist-dir=',
+            'd',
+            "directory to put final RPM files in (and .spec files if --spec-only)",
+        ),
+        (
+            'python=',
+            None,
+            "path to Python interpreter to hard-code in the .spec file "
+            "[default: \"python\"]",
+        ),
+        (
+            'fix-python',
+            None,
+            "hard-code the exact path to the current Python interpreter in "
+            "the .spec file",
+        ),
+        ('spec-only', None, "only regenerate spec file"),
+        ('source-only', None, "only generate source RPM"),
+        ('binary-only', None, "only generate binary RPM"),
+        ('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"),
+        # More meta-data: too RPM-specific to put in the setup script,
+        # but needs to go in the .spec file -- so we make these options
+        # to "bdist_rpm".  The idea is that packagers would put this
+        # info in setup.cfg, although they are of course free to
+        # supply it on the command line.
+        (
+            'distribution-name=',
+            None,
+            "name of the (Linux) distribution to which this "
+            "RPM applies (*not* the name of the module distribution!)",
+        ),
+        ('group=', None, "package classification [default: \"Development/Libraries\"]"),
+        ('release=', None, "RPM release number"),
+        ('serial=', None, "RPM serial number"),
+        (
+            'vendor=',
+            None,
+            "RPM \"vendor\" (eg. \"Joe Blow \") "
+            "[default: maintainer or author from setup script]",
+        ),
+        (
+            'packager=',
+            None,
+            "RPM packager (eg. \"Jane Doe \") [default: vendor]",
+        ),
+        ('doc-files=', None, "list of documentation files (space or comma-separated)"),
+        ('changelog=', None, "RPM changelog"),
+        ('icon=', None, "name of icon file"),
+        ('provides=', None, "capabilities provided by this package"),
+        ('requires=', None, "capabilities required by this package"),
+        ('conflicts=', None, "capabilities which conflict with this package"),
+        ('build-requires=', None, "capabilities required to build this package"),
+        ('obsoletes=', None, "capabilities made obsolete by this package"),
+        ('no-autoreq', None, "do not automatically calculate dependencies"),
+        # Actions to take when building RPM
+        ('keep-temp', 'k', "don't clean up RPM build directory"),
+        ('no-keep-temp', None, "clean up RPM build directory [default]"),
+        (
+            'use-rpm-opt-flags',
+            None,
+            "compile with RPM_OPT_FLAGS when building from source RPM",
+        ),
+        ('no-rpm-opt-flags', None, "do not pass any RPM CFLAGS to compiler"),
+        ('rpm3-mode', None, "RPM 3 compatibility mode (default)"),
+        ('rpm2-mode', None, "RPM 2 compatibility mode"),
+        # Add the hooks necessary for specifying custom scripts
+        ('prep-script=', None, "Specify a script for the PREP phase of RPM building"),
+        ('build-script=', None, "Specify a script for the BUILD phase of RPM building"),
+        (
+            'pre-install=',
+            None,
+            "Specify a script for the pre-INSTALL phase of RPM building",
+        ),
+        (
+            'install-script=',
+            None,
+            "Specify a script for the INSTALL phase of RPM building",
+        ),
+        (
+            'post-install=',
+            None,
+            "Specify a script for the post-INSTALL phase of RPM building",
+        ),
+        (
+            'pre-uninstall=',
+            None,
+            "Specify a script for the pre-UNINSTALL phase of RPM building",
+        ),
+        (
+            'post-uninstall=',
+            None,
+            "Specify a script for the post-UNINSTALL phase of RPM building",
+        ),
+        ('clean-script=', None, "Specify a script for the CLEAN phase of RPM building"),
+        (
+            'verify-script=',
+            None,
+            "Specify a script for the VERIFY phase of the RPM build",
+        ),
+        # Allow a packager to explicitly force an architecture
+        ('force-arch=', None, "Force an architecture onto the RPM build process"),
+        ('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = [
+        'keep-temp',
+        'use-rpm-opt-flags',
+        'rpm3-mode',
+        'no-autoreq',
+        'quiet',
+    ]
+
+    negative_opt: ClassVar[dict[str, str]] = {
+        'no-keep-temp': 'keep-temp',
+        'no-rpm-opt-flags': 'use-rpm-opt-flags',
+        'rpm2-mode': 'rpm3-mode',
+    }
+
+    def initialize_options(self):
+        self.bdist_base = None
+        self.rpm_base = None
+        self.dist_dir = None
+        self.python = None
+        self.fix_python = None
+        self.spec_only = None
+        self.binary_only = None
+        self.source_only = None
+        self.use_bzip2 = None
+
+        self.distribution_name = None
+        self.group = None
+        self.release = None
+        self.serial = None
+        self.vendor = None
+        self.packager = None
+        self.doc_files = None
+        self.changelog = None
+        self.icon = None
+
+        self.prep_script = None
+        self.build_script = None
+        self.install_script = None
+        self.clean_script = None
+        self.verify_script = None
+        self.pre_install = None
+        self.post_install = None
+        self.pre_uninstall = None
+        self.post_uninstall = None
+        self.prep = None
+        self.provides = None
+        self.requires = None
+        self.conflicts = None
+        self.build_requires = None
+        self.obsoletes = None
+
+        self.keep_temp = False
+        self.use_rpm_opt_flags = True
+        self.rpm3_mode = True
+        self.no_autoreq = False
+
+        self.force_arch = None
+        self.quiet = False
+
+    def finalize_options(self) -> None:
+        self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
+        if self.rpm_base is None:
+            if not self.rpm3_mode:
+                raise DistutilsOptionError("you must specify --rpm-base in RPM 2 mode")
+            self.rpm_base = os.path.join(self.bdist_base, "rpm")
+
+        if self.python is None:
+            if self.fix_python:
+                self.python = sys.executable
+            else:
+                self.python = "python3"
+        elif self.fix_python:
+            raise DistutilsOptionError(
+                "--python and --fix-python are mutually exclusive options"
+            )
+
+        if os.name != 'posix':
+            raise DistutilsPlatformError(
+                f"don't know how to create RPM distributions on platform {os.name}"
+            )
+        if self.binary_only and self.source_only:
+            raise DistutilsOptionError(
+                "cannot supply both '--source-only' and '--binary-only'"
+            )
+
+        # don't pass CFLAGS to pure python distributions
+        if not self.distribution.has_ext_modules():
+            self.use_rpm_opt_flags = False
+
+        self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
+        self.finalize_package_data()
+
+    def finalize_package_data(self) -> None:
+        self.ensure_string('group', "Development/Libraries")
+        self.ensure_string(
+            'vendor',
+            f"{self.distribution.get_contact()} <{self.distribution.get_contact_email()}>",
+        )
+        self.ensure_string('packager')
+        self.ensure_string_list('doc_files')
+        if isinstance(self.doc_files, list):
+            for readme in ('README', 'README.txt'):
+                if os.path.exists(readme) and readme not in self.doc_files:
+                    self.doc_files.append(readme)
+
+        self.ensure_string('release', "1")
+        self.ensure_string('serial')  # should it be an int?
+
+        self.ensure_string('distribution_name')
+
+        self.ensure_string('changelog')
+        # Format changelog correctly
+        self.changelog = self._format_changelog(self.changelog)
+
+        self.ensure_filename('icon')
+
+        self.ensure_filename('prep_script')
+        self.ensure_filename('build_script')
+        self.ensure_filename('install_script')
+        self.ensure_filename('clean_script')
+        self.ensure_filename('verify_script')
+        self.ensure_filename('pre_install')
+        self.ensure_filename('post_install')
+        self.ensure_filename('pre_uninstall')
+        self.ensure_filename('post_uninstall')
+
+        # XXX don't forget we punted on summaries and descriptions -- they
+        # should be handled here eventually!
+
+        # Now *this* is some meta-data that belongs in the setup script...
+        self.ensure_string_list('provides')
+        self.ensure_string_list('requires')
+        self.ensure_string_list('conflicts')
+        self.ensure_string_list('build_requires')
+        self.ensure_string_list('obsoletes')
+
+        self.ensure_string('force_arch')
+
+    def run(self) -> None:  # noqa: C901
+        if DEBUG:
+            print("before _get_package_data():")
+            print("vendor =", self.vendor)
+            print("packager =", self.packager)
+            print("doc_files =", self.doc_files)
+            print("changelog =", self.changelog)
+
+        # make directories
+        if self.spec_only:
+            spec_dir = self.dist_dir
+            self.mkpath(spec_dir)
+        else:
+            rpm_dir = {}
+            for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'):
+                rpm_dir[d] = os.path.join(self.rpm_base, d)
+                self.mkpath(rpm_dir[d])
+            spec_dir = rpm_dir['SPECS']
+
+        # Spec file goes into 'dist_dir' if '--spec-only specified',
+        # build/rpm. otherwise.
+        spec_path = os.path.join(spec_dir, f"{self.distribution.get_name()}.spec")
+        self.execute(
+            write_file, (spec_path, self._make_spec_file()), f"writing '{spec_path}'"
+        )
+
+        if self.spec_only:  # stop if requested
+            return
+
+        # Make a source distribution and copy to SOURCES directory with
+        # optional icon.
+        saved_dist_files = self.distribution.dist_files[:]
+        sdist = self.reinitialize_command('sdist')
+        if self.use_bzip2:
+            sdist.formats = ['bztar']
+        else:
+            sdist.formats = ['gztar']
+        self.run_command('sdist')
+        self.distribution.dist_files = saved_dist_files
+
+        source = sdist.get_archive_files()[0]
+        source_dir = rpm_dir['SOURCES']
+        self.copy_file(source, source_dir)
+
+        if self.icon:
+            if os.path.exists(self.icon):
+                self.copy_file(self.icon, source_dir)
+            else:
+                raise DistutilsFileError(f"icon file '{self.icon}' does not exist")
+
+        # build package
+        log.info("building RPMs")
+        rpm_cmd = ['rpmbuild']
+
+        if self.source_only:  # what kind of RPMs?
+            rpm_cmd.append('-bs')
+        elif self.binary_only:
+            rpm_cmd.append('-bb')
+        else:
+            rpm_cmd.append('-ba')
+        rpm_cmd.extend(['--define', f'__python {self.python}'])
+        if self.rpm3_mode:
+            rpm_cmd.extend(['--define', f'_topdir {os.path.abspath(self.rpm_base)}'])
+        if not self.keep_temp:
+            rpm_cmd.append('--clean')
+
+        if self.quiet:
+            rpm_cmd.append('--quiet')
+
+        rpm_cmd.append(spec_path)
+        # Determine the binary rpm names that should be built out of this spec
+        # file
+        # Note that some of these may not be really built (if the file
+        # list is empty)
+        nvr_string = "%{name}-%{version}-%{release}"
+        src_rpm = nvr_string + ".src.rpm"
+        non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
+        q_cmd = rf"rpm -q --qf '{src_rpm} {non_src_rpm}\n' --specfile '{spec_path}'"
+
+        out = os.popen(q_cmd)
+        try:
+            binary_rpms = []
+            source_rpm = None
+            while True:
+                line = out.readline()
+                if not line:
+                    break
+                ell = line.strip().split()
+                assert len(ell) == 2
+                binary_rpms.append(ell[1])
+                # The source rpm is named after the first entry in the spec file
+                if source_rpm is None:
+                    source_rpm = ell[0]
+
+            status = out.close()
+            if status:
+                raise DistutilsExecError(f"Failed to execute: {q_cmd!r}")
+
+        finally:
+            out.close()
+
+        self.spawn(rpm_cmd)
+
+        if self.distribution.has_ext_modules():
+            pyversion = get_python_version()
+        else:
+            pyversion = 'any'
+
+        if not self.binary_only:
+            srpm = os.path.join(rpm_dir['SRPMS'], source_rpm)
+            assert os.path.exists(srpm)
+            self.move_file(srpm, self.dist_dir)
+            filename = os.path.join(self.dist_dir, source_rpm)
+            self.distribution.dist_files.append(('bdist_rpm', pyversion, filename))
+
+        if not self.source_only:
+            for rpm in binary_rpms:
+                rpm = os.path.join(rpm_dir['RPMS'], rpm)
+                if os.path.exists(rpm):
+                    self.move_file(rpm, self.dist_dir)
+                    filename = os.path.join(self.dist_dir, os.path.basename(rpm))
+                    self.distribution.dist_files.append((
+                        'bdist_rpm',
+                        pyversion,
+                        filename,
+                    ))
+
+    def _dist_path(self, path):
+        return os.path.join(self.dist_dir, os.path.basename(path))
+
+    def _make_spec_file(self):  # noqa: C901
+        """Generate the text of an RPM spec file and return it as a
+        list of strings (one per line).
+        """
+        # definitions and headers
+        spec_file = [
+            '%define name ' + self.distribution.get_name(),
+            '%define version ' + self.distribution.get_version().replace('-', '_'),
+            '%define unmangled_version ' + self.distribution.get_version(),
+            '%define release ' + self.release.replace('-', '_'),
+            '',
+            'Summary: ' + (self.distribution.get_description() or "UNKNOWN"),
+        ]
+
+        # Workaround for #14443 which affects some RPM based systems such as
+        # RHEL6 (and probably derivatives)
+        vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}')
+        # Generate a potential replacement value for __os_install_post (whilst
+        # normalizing the whitespace to simplify the test for whether the
+        # invocation of brp-python-bytecompile passes in __python):
+        vendor_hook = '\n'.join([
+            f'  {line.strip()} \\' for line in vendor_hook.splitlines()
+        ])
+        problem = "brp-python-bytecompile \\\n"
+        fixed = "brp-python-bytecompile %{__python} \\\n"
+        fixed_hook = vendor_hook.replace(problem, fixed)
+        if fixed_hook != vendor_hook:
+            spec_file.append('# Workaround for https://bugs.python.org/issue14443')
+            spec_file.append('%define __os_install_post ' + fixed_hook + '\n')
+
+        # put locale summaries into spec file
+        # XXX not supported for now (hard to put a dictionary
+        # in a config file -- arg!)
+        # for locale in self.summaries.keys():
+        #    spec_file.append('Summary(%s): %s' % (locale,
+        #                                          self.summaries[locale]))
+
+        spec_file.extend([
+            'Name: %{name}',
+            'Version: %{version}',
+            'Release: %{release}',
+        ])
+
+        # XXX yuck! this filename is available from the "sdist" command,
+        # but only after it has run: and we create the spec file before
+        # running "sdist", in case of --spec-only.
+        if self.use_bzip2:
+            spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2')
+        else:
+            spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz')
+
+        spec_file.extend([
+            'License: ' + (self.distribution.get_license() or "UNKNOWN"),
+            'Group: ' + self.group,
+            'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
+            'Prefix: %{_prefix}',
+        ])
+
+        if not self.force_arch:
+            # noarch if no extension modules
+            if not self.distribution.has_ext_modules():
+                spec_file.append('BuildArch: noarch')
+        else:
+            spec_file.append(f'BuildArch: {self.force_arch}')
+
+        for field in (
+            'Vendor',
+            'Packager',
+            'Provides',
+            'Requires',
+            'Conflicts',
+            'Obsoletes',
+        ):
+            val = getattr(self, field.lower())
+            if isinstance(val, list):
+                spec_file.append('{}: {}'.format(field, ' '.join(val)))
+            elif val is not None:
+                spec_file.append(f'{field}: {val}')
+
+        if self.distribution.get_url():
+            spec_file.append('Url: ' + self.distribution.get_url())
+
+        if self.distribution_name:
+            spec_file.append('Distribution: ' + self.distribution_name)
+
+        if self.build_requires:
+            spec_file.append('BuildRequires: ' + ' '.join(self.build_requires))
+
+        if self.icon:
+            spec_file.append('Icon: ' + os.path.basename(self.icon))
+
+        if self.no_autoreq:
+            spec_file.append('AutoReq: 0')
+
+        spec_file.extend([
+            '',
+            '%description',
+            self.distribution.get_long_description() or "",
+        ])
+
+        # put locale descriptions into spec file
+        # XXX again, suppressed because config file syntax doesn't
+        # easily support this ;-(
+        # for locale in self.descriptions.keys():
+        #    spec_file.extend([
+        #        '',
+        #        '%description -l ' + locale,
+        #        self.descriptions[locale],
+        #        ])
+
+        # rpm scripts
+        # figure out default build script
+        def_setup_call = f"{self.python} {os.path.basename(sys.argv[0])}"
+        def_build = f"{def_setup_call} build"
+        if self.use_rpm_opt_flags:
+            def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
+
+        # insert contents of files
+
+        # XXX this is kind of misleading: user-supplied options are files
+        # that we open and interpolate into the spec file, but the defaults
+        # are just text that we drop in as-is.  Hmmm.
+
+        install_cmd = f'{def_setup_call} install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES'
+
+        script_options = [
+            ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"),
+            ('build', 'build_script', def_build),
+            ('install', 'install_script', install_cmd),
+            ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"),
+            ('verifyscript', 'verify_script', None),
+            ('pre', 'pre_install', None),
+            ('post', 'post_install', None),
+            ('preun', 'pre_uninstall', None),
+            ('postun', 'post_uninstall', None),
+        ]
+
+        for rpm_opt, attr, default in script_options:
+            # Insert contents of file referred to, if no file is referred to
+            # use 'default' as contents of script
+            val = getattr(self, attr)
+            if val or default:
+                spec_file.extend([
+                    '',
+                    '%' + rpm_opt,
+                ])
+                if val:
+                    with open(val) as f:
+                        spec_file.extend(f.read().split('\n'))
+                else:
+                    spec_file.append(default)
+
+        # files section
+        spec_file.extend([
+            '',
+            '%files -f INSTALLED_FILES',
+            '%defattr(-,root,root)',
+        ])
+
+        if self.doc_files:
+            spec_file.append('%doc ' + ' '.join(self.doc_files))
+
+        if self.changelog:
+            spec_file.extend([
+                '',
+                '%changelog',
+            ])
+            spec_file.extend(self.changelog)
+
+        return spec_file
+
+    def _format_changelog(self, changelog):
+        """Format the changelog correctly and convert it to a list of strings"""
+        if not changelog:
+            return changelog
+        new_changelog = []
+        for line in changelog.strip().split('\n'):
+            line = line.strip()
+            if line[0] == '*':
+                new_changelog.extend(['', line])
+            elif line[0] == '-':
+                new_changelog.append(line)
+            else:
+                new_changelog.append('  ' + line)
+
+        # strip trailing newline inserted by first changelog entry
+        if not new_changelog[0]:
+            del new_changelog[0]
+
+        return new_changelog
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build.py
new file mode 100644
index 0000000..6a8303a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build.py
@@ -0,0 +1,156 @@
+"""distutils.command.build
+
+Implements the Distutils 'build' command."""
+
+from __future__ import annotations
+
+import os
+import sys
+import sysconfig
+from collections.abc import Callable
+from typing import ClassVar
+
+from ..ccompiler import show_compilers
+from ..core import Command
+from ..errors import DistutilsOptionError
+from ..util import get_platform
+
+
+class build(Command):
+    description = "build everything needed to install"
+
+    user_options = [
+        ('build-base=', 'b', "base directory for build library"),
+        ('build-purelib=', None, "build directory for platform-neutral distributions"),
+        ('build-platlib=', None, "build directory for platform-specific distributions"),
+        (
+            'build-lib=',
+            None,
+            "build directory for all distribution (defaults to either build-purelib or build-platlib",
+        ),
+        ('build-scripts=', None, "build directory for scripts"),
+        ('build-temp=', 't', "temporary build directory"),
+        (
+            'plat-name=',
+            'p',
+            f"platform name to build for, if supported [default: {get_platform()}]",
+        ),
+        ('compiler=', 'c', "specify the compiler type"),
+        ('parallel=', 'j', "number of parallel build jobs"),
+        ('debug', 'g', "compile extensions and libraries with debugging information"),
+        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
+        ('executable=', 'e', "specify final destination interpreter path (build.py)"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['debug', 'force']
+
+    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
+        ('help-compiler', None, "list available compilers", show_compilers),
+    ]
+
+    def initialize_options(self):
+        self.build_base = 'build'
+        # these are decided only after 'build_base' has its final value
+        # (unless overridden by the user or client)
+        self.build_purelib = None
+        self.build_platlib = None
+        self.build_lib = None
+        self.build_temp = None
+        self.build_scripts = None
+        self.compiler = None
+        self.plat_name = None
+        self.debug = None
+        self.force = False
+        self.executable = None
+        self.parallel = None
+
+    def finalize_options(self) -> None:  # noqa: C901
+        if self.plat_name is None:
+            self.plat_name = get_platform()
+        else:
+            # plat-name only supported for windows (other platforms are
+            # supported via ./configure flags, if at all).  Avoid misleading
+            # other platforms.
+            if os.name != 'nt':
+                raise DistutilsOptionError(
+                    "--plat-name only supported on Windows (try "
+                    "using './configure --help' on your platform)"
+                )
+
+        plat_specifier = f".{self.plat_name}-{sys.implementation.cache_tag}"
+
+        # Python 3.13+ with --disable-gil shouldn't share build directories
+        if sysconfig.get_config_var('Py_GIL_DISABLED'):
+            plat_specifier += 't'
+
+        # Make it so Python 2.x and Python 2.x with --with-pydebug don't
+        # share the same build directories. Doing so confuses the build
+        # process for C modules
+        if hasattr(sys, 'gettotalrefcount'):
+            plat_specifier += '-pydebug'
+
+        # 'build_purelib' and 'build_platlib' just default to 'lib' and
+        # 'lib.' under the base build directory.  We only use one of
+        # them for a given distribution, though --
+        if self.build_purelib is None:
+            self.build_purelib = os.path.join(self.build_base, 'lib')
+        if self.build_platlib is None:
+            self.build_platlib = os.path.join(self.build_base, 'lib' + plat_specifier)
+
+        # 'build_lib' is the actual directory that we will use for this
+        # particular module distribution -- if user didn't supply it, pick
+        # one of 'build_purelib' or 'build_platlib'.
+        if self.build_lib is None:
+            if self.distribution.has_ext_modules():
+                self.build_lib = self.build_platlib
+            else:
+                self.build_lib = self.build_purelib
+
+        # 'build_temp' -- temporary directory for compiler turds,
+        # "build/temp."
+        if self.build_temp is None:
+            self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier)
+        if self.build_scripts is None:
+            self.build_scripts = os.path.join(
+                self.build_base,
+                f'scripts-{sys.version_info.major}.{sys.version_info.minor}',
+            )
+
+        if self.executable is None and sys.executable:
+            self.executable = os.path.normpath(sys.executable)
+
+        if isinstance(self.parallel, str):
+            try:
+                self.parallel = int(self.parallel)
+            except ValueError:
+                raise DistutilsOptionError("parallel should be an integer")
+
+    def run(self) -> None:
+        # Run all relevant sub-commands.  This will be some subset of:
+        #  - build_py      - pure Python modules
+        #  - build_clib    - standalone C libraries
+        #  - build_ext     - Python extensions
+        #  - build_scripts - (Python) scripts
+        for cmd_name in self.get_sub_commands():
+            self.run_command(cmd_name)
+
+    # -- Predicates for the sub-command list ---------------------------
+
+    def has_pure_modules(self):
+        return self.distribution.has_pure_modules()
+
+    def has_c_libraries(self):
+        return self.distribution.has_c_libraries()
+
+    def has_ext_modules(self):
+        return self.distribution.has_ext_modules()
+
+    def has_scripts(self):
+        return self.distribution.has_scripts()
+
+    sub_commands = [
+        ('build_py', has_pure_modules),
+        ('build_clib', has_c_libraries),
+        ('build_ext', has_ext_modules),
+        ('build_scripts', has_scripts),
+    ]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_clib.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_clib.py
new file mode 100644
index 0000000..8de5df2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_clib.py
@@ -0,0 +1,199 @@
+"""distutils.command.build_clib
+
+Implements the Distutils 'build_clib' command, to build a C/C++ library
+that is included in the module distribution and needed by an extension
+module."""
+
+# XXX this module has *lots* of code ripped-off quite transparently from
+# build_ext.py -- not surprisingly really, as the work required to build
+# a static library from a collection of C source files is not really all
+# that different from what's required to build a shared object file from
+# a collection of C source files.  Nevertheless, I haven't done the
+# necessary refactoring to account for the overlap in code between the
+# two modules, mainly because a number of subtle details changed in the
+# cut 'n paste.  Sigh.
+from __future__ import annotations
+
+import os
+from collections.abc import Callable
+from distutils._log import log
+from typing import ClassVar
+
+from ..ccompiler import new_compiler, show_compilers
+from ..core import Command
+from ..errors import DistutilsSetupError
+from ..sysconfig import customize_compiler
+
+
+class build_clib(Command):
+    description = "build C/C++ libraries used by Python extensions"
+
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ('build-clib=', 'b', "directory to build C/C++ libraries to"),
+        ('build-temp=', 't', "directory to put temporary build by-products"),
+        ('debug', 'g', "compile with debugging information"),
+        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
+        ('compiler=', 'c', "specify the compiler type"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['debug', 'force']
+
+    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
+        ('help-compiler', None, "list available compilers", show_compilers),
+    ]
+
+    def initialize_options(self):
+        self.build_clib = None
+        self.build_temp = None
+
+        # List of libraries to build
+        self.libraries = None
+
+        # Compilation options for all libraries
+        self.include_dirs = None
+        self.define = None
+        self.undef = None
+        self.debug = None
+        self.force = False
+        self.compiler = None
+
+    def finalize_options(self) -> None:
+        # This might be confusing: both build-clib and build-temp default
+        # to build-temp as defined by the "build" command.  This is because
+        # I think that C libraries are really just temporary build
+        # by-products, at least from the point of view of building Python
+        # extensions -- but I want to keep my options open.
+        self.set_undefined_options(
+            'build',
+            ('build_temp', 'build_clib'),
+            ('build_temp', 'build_temp'),
+            ('compiler', 'compiler'),
+            ('debug', 'debug'),
+            ('force', 'force'),
+        )
+
+        self.libraries = self.distribution.libraries
+        if self.libraries:
+            self.check_library_list(self.libraries)
+
+        if self.include_dirs is None:
+            self.include_dirs = self.distribution.include_dirs or []
+        if isinstance(self.include_dirs, str):
+            self.include_dirs = self.include_dirs.split(os.pathsep)
+
+        # XXX same as for build_ext -- what about 'self.define' and
+        # 'self.undef' ?
+
+    def run(self) -> None:
+        if not self.libraries:
+            return
+
+        self.compiler = new_compiler(compiler=self.compiler, force=self.force)
+        customize_compiler(self.compiler)
+
+        if self.include_dirs is not None:
+            self.compiler.set_include_dirs(self.include_dirs)
+        if self.define is not None:
+            # 'define' option is a list of (name,value) tuples
+            for name, value in self.define:
+                self.compiler.define_macro(name, value)
+        if self.undef is not None:
+            for macro in self.undef:
+                self.compiler.undefine_macro(macro)
+
+        self.build_libraries(self.libraries)
+
+    def check_library_list(self, libraries) -> None:
+        """Ensure that the list of libraries is valid.
+
+        `library` is presumably provided as a command option 'libraries'.
+        This method checks that it is a list of 2-tuples, where the tuples
+        are (library_name, build_info_dict).
+
+        Raise DistutilsSetupError if the structure is invalid anywhere;
+        just returns otherwise.
+        """
+        if not isinstance(libraries, list):
+            raise DistutilsSetupError("'libraries' option must be a list of tuples")
+
+        for lib in libraries:
+            if not isinstance(lib, tuple) and len(lib) != 2:
+                raise DistutilsSetupError("each element of 'libraries' must a 2-tuple")
+
+            name, build_info = lib
+
+            if not isinstance(name, str):
+                raise DistutilsSetupError(
+                    "first element of each tuple in 'libraries' "
+                    "must be a string (the library name)"
+                )
+
+            if '/' in name or (os.sep != '/' and os.sep in name):
+                raise DistutilsSetupError(
+                    f"bad library name '{lib[0]}': may not contain directory separators"
+                )
+
+            if not isinstance(build_info, dict):
+                raise DistutilsSetupError(
+                    "second element of each tuple in 'libraries' "
+                    "must be a dictionary (build info)"
+                )
+
+    def get_library_names(self):
+        # Assume the library list is valid -- 'check_library_list()' is
+        # called from 'finalize_options()', so it should be!
+        if not self.libraries:
+            return None
+
+        lib_names = []
+        for lib_name, _build_info in self.libraries:
+            lib_names.append(lib_name)
+        return lib_names
+
+    def get_source_files(self):
+        self.check_library_list(self.libraries)
+        filenames = []
+        for lib_name, build_info in self.libraries:
+            sources = build_info.get('sources')
+            if sources is None or not isinstance(sources, (list, tuple)):
+                raise DistutilsSetupError(
+                    f"in 'libraries' option (library '{lib_name}'), "
+                    "'sources' must be present and must be "
+                    "a list of source filenames"
+                )
+
+            filenames.extend(sources)
+        return filenames
+
+    def build_libraries(self, libraries) -> None:
+        for lib_name, build_info in libraries:
+            sources = build_info.get('sources')
+            if sources is None or not isinstance(sources, (list, tuple)):
+                raise DistutilsSetupError(
+                    f"in 'libraries' option (library '{lib_name}'), "
+                    "'sources' must be present and must be "
+                    "a list of source filenames"
+                )
+            sources = list(sources)
+
+            log.info("building '%s' library", lib_name)
+
+            # First, compile the source code to object files in the library
+            # directory.  (This should probably change to putting object
+            # files in a temporary build directory.)
+            macros = build_info.get('macros')
+            include_dirs = build_info.get('include_dirs')
+            objects = self.compiler.compile(
+                sources,
+                output_dir=self.build_temp,
+                macros=macros,
+                include_dirs=include_dirs,
+                debug=self.debug,
+            )
+
+            # Now "link" the object files together into a static library.
+            # (On Unix at least, this isn't really linking -- it just
+            # builds an archive.  Whatever.)
+            self.compiler.create_static_lib(
+                objects, lib_name, output_dir=self.build_clib, debug=self.debug
+            )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py
new file mode 100644
index 0000000..df623d7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py
@@ -0,0 +1,811 @@
+"""distutils.command.build_ext
+
+Implements the Distutils 'build_ext' command, for building extension
+modules (currently limited to C extensions, should accommodate C++
+extensions ASAP)."""
+
+from __future__ import annotations
+
+import contextlib
+import os
+import re
+import sys
+from collections.abc import Callable
+from distutils._log import log
+from site import USER_BASE
+from typing import ClassVar
+
+from .._modified import newer_group
+from ..ccompiler import new_compiler, show_compilers
+from ..core import Command
+from ..errors import (
+    CCompilerError,
+    CompileError,
+    DistutilsError,
+    DistutilsOptionError,
+    DistutilsPlatformError,
+    DistutilsSetupError,
+)
+from ..extension import Extension
+from ..sysconfig import customize_compiler, get_config_h_filename, get_python_version
+from ..util import get_platform, is_freethreaded, is_mingw
+
+# An extension name is just a dot-separated list of Python NAMEs (ie.
+# the same as a fully-qualified module name).
+extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
+
+
+class build_ext(Command):
+    description = "build C/C++ extensions (compile/link to build directory)"
+
+    # XXX thoughts on how to deal with complex command-line options like
+    # these, i.e. how to make it so fancy_getopt can suck them off the
+    # command line and make it look like setup.py defined the appropriate
+    # lists of tuples of what-have-you.
+    #   - each command needs a callback to process its command-line options
+    #   - Command.__init__() needs access to its share of the whole
+    #     command line (must ultimately come from
+    #     Distribution.parse_command_line())
+    #   - it then calls the current command class' option-parsing
+    #     callback to deal with weird options like -D, which have to
+    #     parse the option text and churn out some custom data
+    #     structure
+    #   - that data structure (in this case, a list of 2-tuples)
+    #     will then be present in the command object by the time
+    #     we get to finalize_options() (i.e. the constructor
+    #     takes care of both command-line and client options
+    #     in between initialize_options() and finalize_options())
+
+    sep_by = f" (separated by '{os.pathsep}')"
+    user_options = [
+        ('build-lib=', 'b', "directory for compiled extension modules"),
+        ('build-temp=', 't', "directory for temporary files (build by-products)"),
+        (
+            'plat-name=',
+            'p',
+            "platform name to cross-compile for, if supported "
+            f"[default: {get_platform()}]",
+        ),
+        (
+            'inplace',
+            'i',
+            "ignore build-lib and put compiled extensions into the source "
+            "directory alongside your pure Python modules",
+        ),
+        (
+            'include-dirs=',
+            'I',
+            "list of directories to search for header files" + sep_by,
+        ),
+        ('define=', 'D', "C preprocessor macros to define"),
+        ('undef=', 'U', "C preprocessor macros to undefine"),
+        ('libraries=', 'l', "external C libraries to link with"),
+        (
+            'library-dirs=',
+            'L',
+            "directories to search for external C libraries" + sep_by,
+        ),
+        ('rpath=', 'R', "directories to search for shared C libraries at runtime"),
+        ('link-objects=', 'O', "extra explicit link objects to include in the link"),
+        ('debug', 'g', "compile/link with debugging information"),
+        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
+        ('compiler=', 'c', "specify the compiler type"),
+        ('parallel=', 'j', "number of parallel build jobs"),
+        ('swig-cpp', None, "make SWIG create C++ files (default is C)"),
+        ('swig-opts=', None, "list of SWIG command line options"),
+        ('swig=', None, "path to the SWIG executable"),
+        ('user', None, "add user include, library and rpath"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = [
+        'inplace',
+        'debug',
+        'force',
+        'swig-cpp',
+        'user',
+    ]
+
+    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
+        ('help-compiler', None, "list available compilers", show_compilers),
+    ]
+
+    def initialize_options(self):
+        self.extensions = None
+        self.build_lib = None
+        self.plat_name = None
+        self.build_temp = None
+        self.inplace = False
+        self.package = None
+
+        self.include_dirs = None
+        self.define = None
+        self.undef = None
+        self.libraries = None
+        self.library_dirs = None
+        self.rpath = None
+        self.link_objects = None
+        self.debug = None
+        self.force = None
+        self.compiler = None
+        self.swig = None
+        self.swig_cpp = None
+        self.swig_opts = None
+        self.user = None
+        self.parallel = None
+
+    @staticmethod
+    def _python_lib_dir(sysconfig):
+        """
+        Resolve Python's library directory for building extensions
+        that rely on a shared Python library.
+
+        See python/cpython#44264 and python/cpython#48686
+        """
+        if not sysconfig.get_config_var('Py_ENABLE_SHARED'):
+            return
+
+        if sysconfig.python_build:
+            yield '.'
+            return
+
+        if sys.platform == 'zos':
+            # On z/OS, a user is not required to install Python to
+            # a predetermined path, but can use Python portably
+            installed_dir = sysconfig.get_config_var('base')
+            lib_dir = sysconfig.get_config_var('platlibdir')
+            yield os.path.join(installed_dir, lib_dir)
+        else:
+            # building third party extensions
+            yield sysconfig.get_config_var('LIBDIR')
+
+    def finalize_options(self) -> None:  # noqa: C901
+        from distutils import sysconfig
+
+        self.set_undefined_options(
+            'build',
+            ('build_lib', 'build_lib'),
+            ('build_temp', 'build_temp'),
+            ('compiler', 'compiler'),
+            ('debug', 'debug'),
+            ('force', 'force'),
+            ('parallel', 'parallel'),
+            ('plat_name', 'plat_name'),
+        )
+
+        if self.package is None:
+            self.package = self.distribution.ext_package
+
+        self.extensions = self.distribution.ext_modules
+
+        # Make sure Python's include directories (for Python.h, pyconfig.h,
+        # etc.) are in the include search path.
+        py_include = sysconfig.get_python_inc()
+        plat_py_include = sysconfig.get_python_inc(plat_specific=True)
+        if self.include_dirs is None:
+            self.include_dirs = self.distribution.include_dirs or []
+        if isinstance(self.include_dirs, str):
+            self.include_dirs = self.include_dirs.split(os.pathsep)
+
+        # If in a virtualenv, add its include directory
+        # Issue 16116
+        if sys.exec_prefix != sys.base_exec_prefix:
+            self.include_dirs.append(os.path.join(sys.exec_prefix, 'include'))
+
+        # Put the Python "system" include dir at the end, so that
+        # any local include dirs take precedence.
+        self.include_dirs.extend(py_include.split(os.path.pathsep))
+        if plat_py_include != py_include:
+            self.include_dirs.extend(plat_py_include.split(os.path.pathsep))
+
+        self.ensure_string_list('libraries')
+        self.ensure_string_list('link_objects')
+
+        # Life is easier if we're not forever checking for None, so
+        # simplify these options to empty lists if unset
+        if self.libraries is None:
+            self.libraries = []
+        if self.library_dirs is None:
+            self.library_dirs = []
+        elif isinstance(self.library_dirs, str):
+            self.library_dirs = self.library_dirs.split(os.pathsep)
+
+        if self.rpath is None:
+            self.rpath = []
+        elif isinstance(self.rpath, str):
+            self.rpath = self.rpath.split(os.pathsep)
+
+        # for extensions under windows use different directories
+        # for Release and Debug builds.
+        # also Python's library directory must be appended to library_dirs
+        if os.name == 'nt' and not is_mingw():
+            # the 'libs' directory is for binary installs - we assume that
+            # must be the *native* platform.  But we don't really support
+            # cross-compiling via a binary install anyway, so we let it go.
+            self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
+            if sys.base_exec_prefix != sys.prefix:  # Issue 16116
+                self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs'))
+            if self.debug:
+                self.build_temp = os.path.join(self.build_temp, "Debug")
+            else:
+                self.build_temp = os.path.join(self.build_temp, "Release")
+
+            # Append the source distribution include and library directories,
+            # this allows distutils on windows to work in the source tree
+            self.include_dirs.append(os.path.dirname(get_config_h_filename()))
+            self.library_dirs.append(sys.base_exec_prefix)
+
+            # Use the .lib files for the correct architecture
+            if self.plat_name == 'win32':
+                suffix = 'win32'
+            else:
+                # win-amd64
+                suffix = self.plat_name[4:]
+            new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
+            if suffix:
+                new_lib = os.path.join(new_lib, suffix)
+            self.library_dirs.append(new_lib)
+
+        # For extensions under Cygwin, Python's library directory must be
+        # appended to library_dirs
+        if sys.platform[:6] == 'cygwin':
+            if not sysconfig.python_build:
+                # building third party extensions
+                self.library_dirs.append(
+                    os.path.join(
+                        sys.prefix, "lib", "python" + get_python_version(), "config"
+                    )
+                )
+            else:
+                # building python standard extensions
+                self.library_dirs.append('.')
+
+        self.library_dirs.extend(self._python_lib_dir(sysconfig))
+
+        # The argument parsing will result in self.define being a string, but
+        # it has to be a list of 2-tuples.  All the preprocessor symbols
+        # specified by the 'define' option will be set to '1'.  Multiple
+        # symbols can be separated with commas.
+
+        if self.define:
+            defines = self.define.split(',')
+            self.define = [(symbol, '1') for symbol in defines]
+
+        # The option for macros to undefine is also a string from the
+        # option parsing, but has to be a list.  Multiple symbols can also
+        # be separated with commas here.
+        if self.undef:
+            self.undef = self.undef.split(',')
+
+        if self.swig_opts is None:
+            self.swig_opts = []
+        else:
+            self.swig_opts = self.swig_opts.split(' ')
+
+        # Finally add the user include and library directories if requested
+        if self.user:
+            user_include = os.path.join(USER_BASE, "include")
+            user_lib = os.path.join(USER_BASE, "lib")
+            if os.path.isdir(user_include):
+                self.include_dirs.append(user_include)
+            if os.path.isdir(user_lib):
+                self.library_dirs.append(user_lib)
+                self.rpath.append(user_lib)
+
+        if isinstance(self.parallel, str):
+            try:
+                self.parallel = int(self.parallel)
+            except ValueError:
+                raise DistutilsOptionError("parallel should be an integer")
+
+    def run(self) -> None:  # noqa: C901
+        # 'self.extensions', as supplied by setup.py, is a list of
+        # Extension instances.  See the documentation for Extension (in
+        # distutils.extension) for details.
+        #
+        # For backwards compatibility with Distutils 0.8.2 and earlier, we
+        # also allow the 'extensions' list to be a list of tuples:
+        #    (ext_name, build_info)
+        # where build_info is a dictionary containing everything that
+        # Extension instances do except the name, with a few things being
+        # differently named.  We convert these 2-tuples to Extension
+        # instances as needed.
+
+        if not self.extensions:
+            return
+
+        # If we were asked to build any C/C++ libraries, make sure that the
+        # directory where we put them is in the library search path for
+        # linking extensions.
+        if self.distribution.has_c_libraries():
+            build_clib = self.get_finalized_command('build_clib')
+            self.libraries.extend(build_clib.get_library_names() or [])
+            self.library_dirs.append(build_clib.build_clib)
+
+        # Setup the CCompiler object that we'll use to do all the
+        # compiling and linking
+        self.compiler = new_compiler(
+            compiler=self.compiler,
+            verbose=self.verbose,
+            force=self.force,
+        )
+        customize_compiler(self.compiler)
+        # If we are cross-compiling, init the compiler now (if we are not
+        # cross-compiling, init would not hurt, but people may rely on
+        # late initialization of compiler even if they shouldn't...)
+        if os.name == 'nt' and self.plat_name != get_platform():
+            self.compiler.initialize(self.plat_name)
+
+        # The official Windows free threaded Python installer doesn't set
+        # Py_GIL_DISABLED because its pyconfig.h is shared with the
+        # default build, so define it here (pypa/setuptools#4662).
+        if os.name == 'nt' and is_freethreaded():
+            self.compiler.define_macro('Py_GIL_DISABLED', '1')
+
+        # And make sure that any compile/link-related options (which might
+        # come from the command-line or from the setup script) are set in
+        # that CCompiler object -- that way, they automatically apply to
+        # all compiling and linking done here.
+        if self.include_dirs is not None:
+            self.compiler.set_include_dirs(self.include_dirs)
+        if self.define is not None:
+            # 'define' option is a list of (name,value) tuples
+            for name, value in self.define:
+                self.compiler.define_macro(name, value)
+        if self.undef is not None:
+            for macro in self.undef:
+                self.compiler.undefine_macro(macro)
+        if self.libraries is not None:
+            self.compiler.set_libraries(self.libraries)
+        if self.library_dirs is not None:
+            self.compiler.set_library_dirs(self.library_dirs)
+        if self.rpath is not None:
+            self.compiler.set_runtime_library_dirs(self.rpath)
+        if self.link_objects is not None:
+            self.compiler.set_link_objects(self.link_objects)
+
+        # Now actually compile and link everything.
+        self.build_extensions()
+
+    def check_extensions_list(self, extensions) -> None:  # noqa: C901
+        """Ensure that the list of extensions (presumably provided as a
+        command option 'extensions') is valid, i.e. it is a list of
+        Extension objects.  We also support the old-style list of 2-tuples,
+        where the tuples are (ext_name, build_info), which are converted to
+        Extension instances here.
+
+        Raise DistutilsSetupError if the structure is invalid anywhere;
+        just returns otherwise.
+        """
+        if not isinstance(extensions, list):
+            raise DistutilsSetupError(
+                "'ext_modules' option must be a list of Extension instances"
+            )
+
+        for i, ext in enumerate(extensions):
+            if isinstance(ext, Extension):
+                continue  # OK! (assume type-checking done
+                # by Extension constructor)
+
+            if not isinstance(ext, tuple) or len(ext) != 2:
+                raise DistutilsSetupError(
+                    "each element of 'ext_modules' option must be an "
+                    "Extension instance or 2-tuple"
+                )
+
+            ext_name, build_info = ext
+
+            log.warning(
+                "old-style (ext_name, build_info) tuple found in "
+                "ext_modules for extension '%s' "
+                "-- please convert to Extension instance",
+                ext_name,
+            )
+
+            if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)):
+                raise DistutilsSetupError(
+                    "first element of each tuple in 'ext_modules' "
+                    "must be the extension name (a string)"
+                )
+
+            if not isinstance(build_info, dict):
+                raise DistutilsSetupError(
+                    "second element of each tuple in 'ext_modules' "
+                    "must be a dictionary (build info)"
+                )
+
+            # OK, the (ext_name, build_info) dict is type-safe: convert it
+            # to an Extension instance.
+            ext = Extension(ext_name, build_info['sources'])
+
+            # Easy stuff: one-to-one mapping from dict elements to
+            # instance attributes.
+            for key in (
+                'include_dirs',
+                'library_dirs',
+                'libraries',
+                'extra_objects',
+                'extra_compile_args',
+                'extra_link_args',
+            ):
+                val = build_info.get(key)
+                if val is not None:
+                    setattr(ext, key, val)
+
+            # Medium-easy stuff: same syntax/semantics, different names.
+            ext.runtime_library_dirs = build_info.get('rpath')
+            if 'def_file' in build_info:
+                log.warning("'def_file' element of build info dict no longer supported")
+
+            # Non-trivial stuff: 'macros' split into 'define_macros'
+            # and 'undef_macros'.
+            macros = build_info.get('macros')
+            if macros:
+                ext.define_macros = []
+                ext.undef_macros = []
+                for macro in macros:
+                    if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
+                        raise DistutilsSetupError(
+                            "'macros' element of build info dict must be 1- or 2-tuple"
+                        )
+                    if len(macro) == 1:
+                        ext.undef_macros.append(macro[0])
+                    elif len(macro) == 2:
+                        ext.define_macros.append(macro)
+
+            extensions[i] = ext
+
+    def get_source_files(self):
+        self.check_extensions_list(self.extensions)
+        filenames = []
+
+        # Wouldn't it be neat if we knew the names of header files too...
+        for ext in self.extensions:
+            filenames.extend(ext.sources)
+        return filenames
+
+    def get_outputs(self):
+        # Sanity check the 'extensions' list -- can't assume this is being
+        # done in the same run as a 'build_extensions()' call (in fact, we
+        # can probably assume that it *isn't*!).
+        self.check_extensions_list(self.extensions)
+
+        # And build the list of output (built) filenames.  Note that this
+        # ignores the 'inplace' flag, and assumes everything goes in the
+        # "build" tree.
+        return [self.get_ext_fullpath(ext.name) for ext in self.extensions]
+
+    def build_extensions(self) -> None:
+        # First, sanity-check the 'extensions' list
+        self.check_extensions_list(self.extensions)
+        if self.parallel:
+            self._build_extensions_parallel()
+        else:
+            self._build_extensions_serial()
+
+    def _build_extensions_parallel(self):
+        workers = self.parallel
+        if self.parallel is True:
+            workers = os.cpu_count()  # may return None
+        try:
+            from concurrent.futures import ThreadPoolExecutor
+        except ImportError:
+            workers = None
+
+        if workers is None:
+            self._build_extensions_serial()
+            return
+
+        with ThreadPoolExecutor(max_workers=workers) as executor:
+            futures = [
+                executor.submit(self.build_extension, ext) for ext in self.extensions
+            ]
+            for ext, fut in zip(self.extensions, futures):
+                with self._filter_build_errors(ext):
+                    fut.result()
+
+    def _build_extensions_serial(self):
+        for ext in self.extensions:
+            with self._filter_build_errors(ext):
+                self.build_extension(ext)
+
+    @contextlib.contextmanager
+    def _filter_build_errors(self, ext):
+        try:
+            yield
+        except (CCompilerError, DistutilsError, CompileError) as e:
+            if not ext.optional:
+                raise
+            self.warn(f'building extension "{ext.name}" failed: {e}')
+
+    def build_extension(self, ext) -> None:
+        sources = ext.sources
+        if sources is None or not isinstance(sources, (list, tuple)):
+            raise DistutilsSetupError(
+                f"in 'ext_modules' option (extension '{ext.name}'), "
+                "'sources' must be present and must be "
+                "a list of source filenames"
+            )
+        # sort to make the resulting .so file build reproducible
+        sources = sorted(sources)
+
+        ext_path = self.get_ext_fullpath(ext.name)
+        depends = sources + ext.depends
+        if not (self.force or newer_group(depends, ext_path, 'newer')):
+            log.debug("skipping '%s' extension (up-to-date)", ext.name)
+            return
+        else:
+            log.info("building '%s' extension", ext.name)
+
+        # First, scan the sources for SWIG definition files (.i), run
+        # SWIG on 'em to create .c files, and modify the sources list
+        # accordingly.
+        sources = self.swig_sources(sources, ext)
+
+        # Next, compile the source code to object files.
+
+        # XXX not honouring 'define_macros' or 'undef_macros' -- the
+        # CCompiler API needs to change to accommodate this, and I
+        # want to do one thing at a time!
+
+        # Two possible sources for extra compiler arguments:
+        #   - 'extra_compile_args' in Extension object
+        #   - CFLAGS environment variable (not particularly
+        #     elegant, but people seem to expect it and I
+        #     guess it's useful)
+        # The environment variable should take precedence, and
+        # any sensible compiler will give precedence to later
+        # command line args.  Hence we combine them in order:
+        extra_args = ext.extra_compile_args or []
+
+        macros = ext.define_macros[:]
+        for undef in ext.undef_macros:
+            macros.append((undef,))
+
+        objects = self.compiler.compile(
+            sources,
+            output_dir=self.build_temp,
+            macros=macros,
+            include_dirs=ext.include_dirs,
+            debug=self.debug,
+            extra_postargs=extra_args,
+            depends=ext.depends,
+        )
+
+        # XXX outdated variable, kept here in case third-part code
+        # needs it.
+        self._built_objects = objects[:]
+
+        # Now link the object files together into a "shared object" --
+        # of course, first we have to figure out all the other things
+        # that go into the mix.
+        if ext.extra_objects:
+            objects.extend(ext.extra_objects)
+        extra_args = ext.extra_link_args or []
+
+        # Detect target language, if not provided
+        language = ext.language or self.compiler.detect_language(sources)
+
+        self.compiler.link_shared_object(
+            objects,
+            ext_path,
+            libraries=self.get_libraries(ext),
+            library_dirs=ext.library_dirs,
+            runtime_library_dirs=ext.runtime_library_dirs,
+            extra_postargs=extra_args,
+            export_symbols=self.get_export_symbols(ext),
+            debug=self.debug,
+            build_temp=self.build_temp,
+            target_lang=language,
+        )
+
+    def swig_sources(self, sources, extension):
+        """Walk the list of source files in 'sources', looking for SWIG
+        interface (.i) files.  Run SWIG on all that are found, and
+        return a modified 'sources' list with SWIG source files replaced
+        by the generated C (or C++) files.
+        """
+        new_sources = []
+        swig_sources = []
+        swig_targets = {}
+
+        # XXX this drops generated C/C++ files into the source tree, which
+        # is fine for developers who want to distribute the generated
+        # source -- but there should be an option to put SWIG output in
+        # the temp dir.
+
+        if self.swig_cpp:
+            log.warning("--swig-cpp is deprecated - use --swig-opts=-c++")
+
+        if (
+            self.swig_cpp
+            or ('-c++' in self.swig_opts)
+            or ('-c++' in extension.swig_opts)
+        ):
+            target_ext = '.cpp'
+        else:
+            target_ext = '.c'
+
+        for source in sources:
+            (base, ext) = os.path.splitext(source)
+            if ext == ".i":  # SWIG interface file
+                new_sources.append(base + '_wrap' + target_ext)
+                swig_sources.append(source)
+                swig_targets[source] = new_sources[-1]
+            else:
+                new_sources.append(source)
+
+        if not swig_sources:
+            return new_sources
+
+        swig = self.swig or self.find_swig()
+        swig_cmd = [swig, "-python"]
+        swig_cmd.extend(self.swig_opts)
+        if self.swig_cpp:
+            swig_cmd.append("-c++")
+
+        # Do not override commandline arguments
+        if not self.swig_opts:
+            swig_cmd.extend(extension.swig_opts)
+
+        for source in swig_sources:
+            target = swig_targets[source]
+            log.info("swigging %s to %s", source, target)
+            self.spawn(swig_cmd + ["-o", target, source])
+
+        return new_sources
+
+    def find_swig(self):
+        """Return the name of the SWIG executable.  On Unix, this is
+        just "swig" -- it should be in the PATH.  Tries a bit harder on
+        Windows.
+        """
+        if os.name == "posix":
+            return "swig"
+        elif os.name == "nt":
+            # Look for SWIG in its standard installation directory on
+            # Windows (or so I presume!).  If we find it there, great;
+            # if not, act like Unix and assume it's in the PATH.
+            for vers in ("1.3", "1.2", "1.1"):
+                fn = os.path.join(f"c:\\swig{vers}", "swig.exe")
+                if os.path.isfile(fn):
+                    return fn
+            else:
+                return "swig.exe"
+        else:
+            raise DistutilsPlatformError(
+                f"I don't know how to find (much less run) SWIG on platform '{os.name}'"
+            )
+
+    # -- Name generators -----------------------------------------------
+    # (extension names, filenames, whatever)
+    def get_ext_fullpath(self, ext_name: str) -> str:
+        """Returns the path of the filename for a given extension.
+
+        The file is located in `build_lib` or directly in the package
+        (inplace option).
+        """
+        fullname = self.get_ext_fullname(ext_name)
+        modpath = fullname.split('.')
+        filename = self.get_ext_filename(modpath[-1])
+
+        if not self.inplace:
+            # no further work needed
+            # returning :
+            #   build_dir/package/path/filename
+            filename = os.path.join(*modpath[:-1] + [filename])
+            return os.path.join(self.build_lib, filename)
+
+        # the inplace option requires to find the package directory
+        # using the build_py command for that
+        package = '.'.join(modpath[0:-1])
+        build_py = self.get_finalized_command('build_py')
+        package_dir = os.path.abspath(build_py.get_package_dir(package))
+
+        # returning
+        #   package_dir/filename
+        return os.path.join(package_dir, filename)
+
+    def get_ext_fullname(self, ext_name: str) -> str:
+        """Returns the fullname of a given extension name.
+
+        Adds the `package.` prefix"""
+        if self.package is None:
+            return ext_name
+        else:
+            return self.package + '.' + ext_name
+
+    def get_ext_filename(self, ext_name: str) -> str:
+        r"""Convert the name of an extension (eg. "foo.bar") into the name
+        of the file from which it will be loaded (eg. "foo/bar.so", or
+        "foo\bar.pyd").
+        """
+        from ..sysconfig import get_config_var
+
+        ext_path = ext_name.split('.')
+        ext_suffix = get_config_var('EXT_SUFFIX')
+        return os.path.join(*ext_path) + ext_suffix
+
+    def get_export_symbols(self, ext: Extension) -> list[str]:
+        """Return the list of symbols that a shared extension has to
+        export.  This either uses 'ext.export_symbols' or, if it's not
+        provided, "PyInit_" + module_name.  Only relevant on Windows, where
+        the .pyd file (DLL) must export the module "PyInit_" function.
+        """
+        name = self._get_module_name_for_symbol(ext)
+        try:
+            # Unicode module name support as defined in PEP-489
+            # https://peps.python.org/pep-0489/#export-hook-name
+            name.encode('ascii')
+        except UnicodeEncodeError:
+            suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii')
+        else:
+            suffix = "_" + name
+
+        initfunc_name = "PyInit" + suffix
+        if initfunc_name not in ext.export_symbols:
+            ext.export_symbols.append(initfunc_name)
+        return ext.export_symbols
+
+    def _get_module_name_for_symbol(self, ext):
+        # Package name should be used for `__init__` modules
+        # https://github.com/python/cpython/issues/80074
+        # https://github.com/pypa/setuptools/issues/4826
+        parts = ext.name.split(".")
+        if parts[-1] == "__init__" and len(parts) >= 2:
+            return parts[-2]
+        return parts[-1]
+
+    def get_libraries(self, ext: Extension) -> list[str]:  # noqa: C901
+        """Return the list of libraries to link against when building a
+        shared extension.  On most platforms, this is just 'ext.libraries';
+        on Windows, we add the Python library (eg. python20.dll).
+        """
+        # The python library is always needed on Windows.  For MSVC, this
+        # is redundant, since the library is mentioned in a pragma in
+        # pyconfig.h that MSVC groks.  The other Windows compilers all seem
+        # to need it mentioned explicitly, though, so that's what we do.
+        # Append '_d' to the python import library on debug builds.
+        if sys.platform == "win32" and not is_mingw():
+            from .._msvccompiler import MSVCCompiler
+
+            if not isinstance(self.compiler, MSVCCompiler):
+                template = "python%d%d"
+                if self.debug:
+                    template = template + '_d'
+                pythonlib = template % (
+                    sys.hexversion >> 24,
+                    (sys.hexversion >> 16) & 0xFF,
+                )
+                # don't extend ext.libraries, it may be shared with other
+                # extensions, it is a reference to the original list
+                return ext.libraries + [pythonlib]
+        else:
+            # On Android only the main executable and LD_PRELOADs are considered
+            # to be RTLD_GLOBAL, all the dependencies of the main executable
+            # remain RTLD_LOCAL and so the shared libraries must be linked with
+            # libpython when python is built with a shared python library (issue
+            # bpo-21536).
+            # On Cygwin (and if required, other POSIX-like platforms based on
+            # Windows like MinGW) it is simply necessary that all symbols in
+            # shared libraries are resolved at link time.
+            from ..sysconfig import get_config_var
+
+            link_libpython = False
+            if get_config_var('Py_ENABLE_SHARED'):
+                # A native build on an Android device or on Cygwin
+                if hasattr(sys, 'getandroidapilevel'):
+                    link_libpython = True
+                elif sys.platform == 'cygwin' or is_mingw():
+                    link_libpython = True
+                elif '_PYTHON_HOST_PLATFORM' in os.environ:
+                    # We are cross-compiling for one of the relevant platforms
+                    if get_config_var('ANDROID_API_LEVEL') != 0:
+                        link_libpython = True
+                    elif get_config_var('MACHDEP') == 'cygwin':
+                        link_libpython = True
+
+            if link_libpython:
+                ldversion = get_config_var('LDVERSION')
+                return ext.libraries + ['python' + ldversion]
+
+        return ext.libraries
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_py.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_py.py
new file mode 100644
index 0000000..d6ec7d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_py.py
@@ -0,0 +1,404 @@
+"""distutils.command.build_py
+
+Implements the Distutils 'build_py' command."""
+
+import glob
+import importlib.util
+import os
+import sys
+from distutils._log import log
+from typing import ClassVar
+
+from ..core import Command
+from ..errors import DistutilsFileError, DistutilsOptionError
+from ..util import convert_path
+
+
+class build_py(Command):
+    description = "\"build\" pure Python modules (copy to build directory)"
+
+    user_options = [
+        ('build-lib=', 'd', "directory to \"build\" (copy) to"),
+        ('compile', 'c', "compile .py to .pyc"),
+        ('no-compile', None, "don't compile .py files [default]"),
+        (
+            'optimize=',
+            'O',
+            "also compile with optimization: -O1 for \"python -O\", "
+            "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
+        ),
+        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['compile', 'force']
+    negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
+
+    def initialize_options(self):
+        self.build_lib = None
+        self.py_modules = None
+        self.package = None
+        self.package_data = None
+        self.package_dir = None
+        self.compile = False
+        self.optimize = 0
+        self.force = None
+
+    def finalize_options(self) -> None:
+        self.set_undefined_options(
+            'build', ('build_lib', 'build_lib'), ('force', 'force')
+        )
+
+        # Get the distribution options that are aliases for build_py
+        # options -- list of packages and list of modules.
+        self.packages = self.distribution.packages
+        self.py_modules = self.distribution.py_modules
+        self.package_data = self.distribution.package_data
+        self.package_dir = {}
+        if self.distribution.package_dir:
+            for name, path in self.distribution.package_dir.items():
+                self.package_dir[name] = convert_path(path)
+        self.data_files = self.get_data_files()
+
+        # Ick, copied straight from install_lib.py (fancy_getopt needs a
+        # type system!  Hell, *everything* needs a type system!!!)
+        if not isinstance(self.optimize, int):
+            try:
+                self.optimize = int(self.optimize)
+                assert 0 <= self.optimize <= 2
+            except (ValueError, AssertionError):
+                raise DistutilsOptionError("optimize must be 0, 1, or 2")
+
+    def run(self) -> None:
+        # XXX copy_file by default preserves atime and mtime.  IMHO this is
+        # the right thing to do, but perhaps it should be an option -- in
+        # particular, a site administrator might want installed files to
+        # reflect the time of installation rather than the last
+        # modification time before the installed release.
+
+        # XXX copy_file by default preserves mode, which appears to be the
+        # wrong thing to do: if a file is read-only in the working
+        # directory, we want it to be installed read/write so that the next
+        # installation of the same module distribution can overwrite it
+        # without problems.  (This might be a Unix-specific issue.)  Thus
+        # we turn off 'preserve_mode' when copying to the build directory,
+        # since the build directory is supposed to be exactly what the
+        # installation will look like (ie. we preserve mode when
+        # installing).
+
+        # Two options control which modules will be installed: 'packages'
+        # and 'py_modules'.  The former lets us work with whole packages, not
+        # specifying individual modules at all; the latter is for
+        # specifying modules one-at-a-time.
+
+        if self.py_modules:
+            self.build_modules()
+        if self.packages:
+            self.build_packages()
+            self.build_package_data()
+
+        self.byte_compile(self.get_outputs(include_bytecode=False))
+
+    def get_data_files(self):
+        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+        data = []
+        if not self.packages:
+            return data
+        for package in self.packages:
+            # Locate package source directory
+            src_dir = self.get_package_dir(package)
+
+            # Compute package build directory
+            build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+
+            # Length of path to strip from found files
+            plen = 0
+            if src_dir:
+                plen = len(src_dir) + 1
+
+            # Strip directory from globbed filenames
+            filenames = [file[plen:] for file in self.find_data_files(package, src_dir)]
+            data.append((package, src_dir, build_dir, filenames))
+        return data
+
+    def find_data_files(self, package, src_dir):
+        """Return filenames for package's data files in 'src_dir'"""
+        globs = self.package_data.get('', []) + self.package_data.get(package, [])
+        files = []
+        for pattern in globs:
+            # Each pattern has to be converted to a platform-specific path
+            filelist = glob.glob(
+                os.path.join(glob.escape(src_dir), convert_path(pattern))
+            )
+            # Files that match more than one pattern are only added once
+            files.extend([
+                fn for fn in filelist if fn not in files and os.path.isfile(fn)
+            ])
+        return files
+
+    def build_package_data(self) -> None:
+        """Copy data files into build directory"""
+        for _package, src_dir, build_dir, filenames in self.data_files:
+            for filename in filenames:
+                target = os.path.join(build_dir, filename)
+                self.mkpath(os.path.dirname(target))
+                self.copy_file(
+                    os.path.join(src_dir, filename), target, preserve_mode=False
+                )
+
+    def get_package_dir(self, package):
+        """Return the directory, relative to the top of the source
+        distribution, where package 'package' should be found
+        (at least according to the 'package_dir' option, if any)."""
+        path = package.split('.')
+
+        if not self.package_dir:
+            if path:
+                return os.path.join(*path)
+            else:
+                return ''
+        else:
+            tail = []
+            while path:
+                try:
+                    pdir = self.package_dir['.'.join(path)]
+                except KeyError:
+                    tail.insert(0, path[-1])
+                    del path[-1]
+                else:
+                    tail.insert(0, pdir)
+                    return os.path.join(*tail)
+            else:
+                # Oops, got all the way through 'path' without finding a
+                # match in package_dir.  If package_dir defines a directory
+                # for the root (nameless) package, then fallback on it;
+                # otherwise, we might as well have not consulted
+                # package_dir at all, as we just use the directory implied
+                # by 'tail' (which should be the same as the original value
+                # of 'path' at this point).
+                pdir = self.package_dir.get('')
+                if pdir is not None:
+                    tail.insert(0, pdir)
+
+                if tail:
+                    return os.path.join(*tail)
+                else:
+                    return ''
+
+    def check_package(self, package, package_dir):
+        # Empty dir name means current directory, which we can probably
+        # assume exists.  Also, os.path.exists and isdir don't know about
+        # my "empty string means current dir" convention, so we have to
+        # circumvent them.
+        if package_dir != "":
+            if not os.path.exists(package_dir):
+                raise DistutilsFileError(
+                    f"package directory '{package_dir}' does not exist"
+                )
+            if not os.path.isdir(package_dir):
+                raise DistutilsFileError(
+                    f"supposed package directory '{package_dir}' exists, "
+                    "but is not a directory"
+                )
+
+        # Directories without __init__.py are namespace packages (PEP 420).
+        if package:
+            init_py = os.path.join(package_dir, "__init__.py")
+            if os.path.isfile(init_py):
+                return init_py
+
+        # Either not in a package at all (__init__.py not expected), or
+        # __init__.py doesn't exist -- so don't return the filename.
+        return None
+
+    def check_module(self, module, module_file):
+        if not os.path.isfile(module_file):
+            log.warning("file %s (for module %s) not found", module_file, module)
+            return False
+        else:
+            return True
+
+    def find_package_modules(self, package, package_dir):
+        self.check_package(package, package_dir)
+        module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py"))
+        modules = []
+        setup_script = os.path.abspath(self.distribution.script_name)
+
+        for f in module_files:
+            abs_f = os.path.abspath(f)
+            if abs_f != setup_script:
+                module = os.path.splitext(os.path.basename(f))[0]
+                modules.append((package, module, f))
+            else:
+                self.debug_print(f"excluding {setup_script}")
+        return modules
+
+    def find_modules(self):
+        """Finds individually-specified Python modules, ie. those listed by
+        module name in 'self.py_modules'.  Returns a list of tuples (package,
+        module_base, filename): 'package' is a tuple of the path through
+        package-space to the module; 'module_base' is the bare (no
+        packages, no dots) module name, and 'filename' is the path to the
+        ".py" file (relative to the distribution root) that implements the
+        module.
+        """
+        # Map package names to tuples of useful info about the package:
+        #    (package_dir, checked)
+        # package_dir - the directory where we'll find source files for
+        #   this package
+        # checked - true if we have checked that the package directory
+        #   is valid (exists, contains __init__.py, ... ?)
+        packages = {}
+
+        # List of (package, module, filename) tuples to return
+        modules = []
+
+        # We treat modules-in-packages almost the same as toplevel modules,
+        # just the "package" for a toplevel is empty (either an empty
+        # string or empty list, depending on context).  Differences:
+        #   - don't check for __init__.py in directory for empty package
+        for module in self.py_modules:
+            path = module.split('.')
+            package = '.'.join(path[0:-1])
+            module_base = path[-1]
+
+            try:
+                (package_dir, checked) = packages[package]
+            except KeyError:
+                package_dir = self.get_package_dir(package)
+                checked = False
+
+            if not checked:
+                init_py = self.check_package(package, package_dir)
+                packages[package] = (package_dir, 1)
+                if init_py:
+                    modules.append((package, "__init__", init_py))
+
+            # XXX perhaps we should also check for just .pyc files
+            # (so greedy closed-source bastards can distribute Python
+            # modules too)
+            module_file = os.path.join(package_dir, module_base + ".py")
+            if not self.check_module(module, module_file):
+                continue
+
+            modules.append((package, module_base, module_file))
+
+        return modules
+
+    def find_all_modules(self):
+        """Compute the list of all modules that will be built, whether
+        they are specified one-module-at-a-time ('self.py_modules') or
+        by whole packages ('self.packages').  Return a list of tuples
+        (package, module, module_file), just like 'find_modules()' and
+        'find_package_modules()' do."""
+        modules = []
+        if self.py_modules:
+            modules.extend(self.find_modules())
+        if self.packages:
+            for package in self.packages:
+                package_dir = self.get_package_dir(package)
+                m = self.find_package_modules(package, package_dir)
+                modules.extend(m)
+        return modules
+
+    def get_source_files(self):
+        return [module[-1] for module in self.find_all_modules()]
+
+    def get_module_outfile(self, build_dir, package, module):
+        outfile_path = [build_dir] + list(package) + [module + ".py"]
+        return os.path.join(*outfile_path)
+
+    def get_outputs(self, include_bytecode: bool = True) -> list[str]:
+        modules = self.find_all_modules()
+        outputs = []
+        for package, module, _module_file in modules:
+            package = package.split('.')
+            filename = self.get_module_outfile(self.build_lib, package, module)
+            outputs.append(filename)
+            if include_bytecode:
+                if self.compile:
+                    outputs.append(
+                        importlib.util.cache_from_source(filename, optimization='')
+                    )
+                if self.optimize > 0:
+                    outputs.append(
+                        importlib.util.cache_from_source(
+                            filename, optimization=self.optimize
+                        )
+                    )
+
+        outputs += [
+            os.path.join(build_dir, filename)
+            for package, src_dir, build_dir, filenames in self.data_files
+            for filename in filenames
+        ]
+
+        return outputs
+
+    def build_module(self, module, module_file, package):
+        if isinstance(package, str):
+            package = package.split('.')
+        elif not isinstance(package, (list, tuple)):
+            raise TypeError(
+                "'package' must be a string (dot-separated), list, or tuple"
+            )
+
+        # Now put the module source file into the "build" area -- this is
+        # easy, we just copy it somewhere under self.build_lib (the build
+        # directory for Python source).
+        outfile = self.get_module_outfile(self.build_lib, package, module)
+        dir = os.path.dirname(outfile)
+        self.mkpath(dir)
+        return self.copy_file(module_file, outfile, preserve_mode=False)
+
+    def build_modules(self) -> None:
+        modules = self.find_modules()
+        for package, module, module_file in modules:
+            # Now "build" the module -- ie. copy the source file to
+            # self.build_lib (the build directory for Python source).
+            # (Actually, it gets copied to the directory for this package
+            # under self.build_lib.)
+            self.build_module(module, module_file, package)
+
+    def build_packages(self) -> None:
+        for package in self.packages:
+            # Get list of (package, module, module_file) tuples based on
+            # scanning the package directory.  'package' is only included
+            # in the tuple so that 'find_modules()' and
+            # 'find_package_tuples()' have a consistent interface; it's
+            # ignored here (apart from a sanity check).  Also, 'module' is
+            # the *unqualified* module name (ie. no dots, no package -- we
+            # already know its package!), and 'module_file' is the path to
+            # the .py file, relative to the current directory
+            # (ie. including 'package_dir').
+            package_dir = self.get_package_dir(package)
+            modules = self.find_package_modules(package, package_dir)
+
+            # Now loop over the modules we found, "building" each one (just
+            # copy it to self.build_lib).
+            for package_, module, module_file in modules:
+                assert package == package_
+                self.build_module(module, module_file, package)
+
+    def byte_compile(self, files) -> None:
+        if sys.dont_write_bytecode:
+            self.warn('byte-compiling is disabled, skipping.')
+            return
+
+        from ..util import byte_compile
+
+        prefix = self.build_lib
+        if prefix[-1] != os.sep:
+            prefix = prefix + os.sep
+
+        # XXX this code is essentially the same as the 'byte_compile()
+        # method of the "install_lib" command, except for the determination
+        # of the 'prefix' string.  Hmmm.
+        if self.compile:
+            byte_compile(files, optimize=0, force=self.force, prefix=prefix)
+        if self.optimize > 0:
+            byte_compile(
+                files,
+                optimize=self.optimize,
+                force=self.force,
+                prefix=prefix,
+            )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_scripts.py
new file mode 100644
index 0000000..0b551d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/build_scripts.py
@@ -0,0 +1,150 @@
+"""distutils.command.build_scripts
+
+Implements the Distutils 'build_scripts' command."""
+
+import os
+import re
+import tokenize
+from distutils._log import log
+from stat import ST_MODE
+from typing import ClassVar
+
+from .._modified import newer
+from ..core import Command
+from ..util import convert_path
+
+shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$')
+"""
+Pattern matching a Python interpreter indicated in first line of a script.
+"""
+
+# for Setuptools compatibility
+first_line_re = shebang_pattern
+
+
+class build_scripts(Command):
+    description = "\"build\" scripts (copy and fixup #! line)"
+
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ('build-dir=', 'd', "directory to \"build\" (copy) to"),
+        ('force', 'f', "forcibly build everything (ignore file timestamps"),
+        ('executable=', 'e', "specify final destination interpreter path"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['force']
+
+    def initialize_options(self):
+        self.build_dir = None
+        self.scripts = None
+        self.force = None
+        self.executable = None
+
+    def finalize_options(self):
+        self.set_undefined_options(
+            'build',
+            ('build_scripts', 'build_dir'),
+            ('force', 'force'),
+            ('executable', 'executable'),
+        )
+        self.scripts = self.distribution.scripts
+
+    def get_source_files(self):
+        return self.scripts
+
+    def run(self):
+        if not self.scripts:
+            return
+        self.copy_scripts()
+
+    def copy_scripts(self):
+        """
+        Copy each script listed in ``self.scripts``.
+
+        If a script is marked as a Python script (first line matches
+        'shebang_pattern', i.e. starts with ``#!`` and contains
+        "python"), then adjust in the copy the first line to refer to
+        the current Python interpreter.
+        """
+        self.mkpath(self.build_dir)
+        outfiles = []
+        updated_files = []
+        for script in self.scripts:
+            self._copy_script(script, outfiles, updated_files)
+
+        self._change_modes(outfiles)
+
+        return outfiles, updated_files
+
+    def _copy_script(self, script, outfiles, updated_files):
+        shebang_match = None
+        script = convert_path(script)
+        outfile = os.path.join(self.build_dir, os.path.basename(script))
+        outfiles.append(outfile)
+
+        if not self.force and not newer(script, outfile):
+            log.debug("not copying %s (up-to-date)", script)
+            return
+
+        # Always open the file, but ignore failures in dry-run mode
+        # in order to attempt to copy directly.
+        f = tokenize.open(script)
+
+        first_line = f.readline()
+        if not first_line:
+            self.warn(f"{script} is an empty file (skipping)")
+            return
+
+        shebang_match = shebang_pattern.match(first_line)
+
+        updated_files.append(outfile)
+        if shebang_match:
+            log.info("copying and adjusting %s -> %s", script, self.build_dir)
+            post_interp = shebang_match.group(1) or ''
+            shebang = "#!" + self.executable + post_interp + "\n"
+            self._validate_shebang(shebang, f.encoding)
+            with open(outfile, "w", encoding=f.encoding) as outf:
+                outf.write(shebang)
+                outf.writelines(f.readlines())
+            if f:
+                f.close()
+        else:
+            if f:
+                f.close()
+            self.copy_file(script, outfile)
+
+    def _change_modes(self, outfiles):
+        if os.name != 'posix':
+            return
+
+        for file in outfiles:
+            self._change_mode(file)
+
+    def _change_mode(self, file):
+        oldmode = os.stat(file)[ST_MODE] & 0o7777
+        newmode = (oldmode | 0o555) & 0o7777
+        if newmode != oldmode:
+            log.info("changing mode of %s from %o to %o", file, oldmode, newmode)
+            os.chmod(file, newmode)
+
+    @staticmethod
+    def _validate_shebang(shebang, encoding):
+        # 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 encodable to
+        # UTF-8.
+        try:
+            shebang.encode('utf-8')
+        except UnicodeEncodeError:
+            raise ValueError(f"The shebang ({shebang!r}) is not encodable to utf-8")
+
+        # If the script is encoded to a custom encoding (use a
+        # #coding:xxx cookie), the shebang has to be encodable to
+        # the script encoding too.
+        try:
+            shebang.encode(encoding)
+        except UnicodeEncodeError:
+            raise ValueError(
+                f"The shebang ({shebang!r}) is not encodable "
+                f"to the script encoding ({encoding})"
+            )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/check.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/check.py
new file mode 100644
index 0000000..58a823d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/check.py
@@ -0,0 +1,152 @@
+"""distutils.command.check
+
+Implements the Distutils 'check' command.
+"""
+
+import contextlib
+from typing import ClassVar
+
+from ..core import Command
+from ..errors import DistutilsSetupError
+
+with contextlib.suppress(ImportError):
+    import docutils.frontend
+    import docutils.nodes
+    import docutils.parsers.rst
+    import docutils.utils
+
+    class SilentReporter(docutils.utils.Reporter):
+        def __init__(
+            self,
+            source,
+            report_level,
+            halt_level,
+            stream=None,
+            debug=False,
+            encoding='ascii',
+            error_handler='replace',
+        ):
+            self.messages = []
+            super().__init__(
+                source, report_level, halt_level, stream, debug, encoding, error_handler
+            )
+
+        def system_message(self, level, message, *children, **kwargs):
+            self.messages.append((level, message, children, kwargs))
+            return docutils.nodes.system_message(
+                message, *children, level=level, type=self.levels[level], **kwargs
+            )
+
+
+class check(Command):
+    """This command checks the meta-data of the package."""
+
+    description = "perform some checks on the package"
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ('metadata', 'm', 'Verify meta-data'),
+        (
+            'restructuredtext',
+            'r',
+            'Checks if long string meta-data syntax are reStructuredText-compliant',
+        ),
+        ('strict', 's', 'Will exit with an error if a check fails'),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['metadata', 'restructuredtext', 'strict']
+
+    def initialize_options(self):
+        """Sets default values for options."""
+        self.restructuredtext = False
+        self.metadata = 1
+        self.strict = False
+        self._warnings = 0
+
+    def finalize_options(self):
+        pass
+
+    def warn(self, msg):
+        """Counts the number of warnings that occurs."""
+        self._warnings += 1
+        return Command.warn(self, msg)
+
+    def run(self):
+        """Runs the command."""
+        # perform the various tests
+        if self.metadata:
+            self.check_metadata()
+        if self.restructuredtext:
+            if 'docutils' in globals():
+                try:
+                    self.check_restructuredtext()
+                except TypeError as exc:
+                    raise DistutilsSetupError(str(exc))
+            elif self.strict:
+                raise DistutilsSetupError('The docutils package is needed.')
+
+        # let's raise an error in strict mode, if we have at least
+        # one warning
+        if self.strict and self._warnings > 0:
+            raise DistutilsSetupError('Please correct your package.')
+
+    def check_metadata(self):
+        """Ensures that all required elements of meta-data are supplied.
+
+        Required fields:
+            name, version
+
+        Warns if any are missing.
+        """
+        metadata = self.distribution.metadata
+
+        missing = [
+            attr for attr in ('name', 'version') if not getattr(metadata, attr, None)
+        ]
+
+        if missing:
+            self.warn("missing required meta-data: {}".format(', '.join(missing)))
+
+    def check_restructuredtext(self):
+        """Checks if the long string fields are reST-compliant."""
+        data = self.distribution.get_long_description()
+        for warning in self._check_rst_data(data):
+            line = warning[-1].get('line')
+            if line is None:
+                warning = warning[1]
+            else:
+                warning = f'{warning[1]} (line {line})'
+            self.warn(warning)
+
+    def _check_rst_data(self, data):
+        """Returns warnings when the provided data doesn't compile."""
+        # the include and csv_table directives need this to be a path
+        source_path = self.distribution.script_name or 'setup.py'
+        parser = docutils.parsers.rst.Parser()
+        settings = docutils.frontend.OptionParser(
+            components=(docutils.parsers.rst.Parser,)
+        ).get_default_values()
+        settings.tab_width = 4
+        settings.pep_references = None
+        settings.rfc_references = None
+        reporter = SilentReporter(
+            source_path,
+            settings.report_level,
+            settings.halt_level,
+            stream=settings.warning_stream,
+            debug=settings.debug,
+            encoding=settings.error_encoding,
+            error_handler=settings.error_encoding_error_handler,
+        )
+
+        document = docutils.nodes.document(settings, reporter, source=source_path)
+        document.note_source(source_path, -1)
+        try:
+            parser.parse(data, document)
+        except (AttributeError, TypeError) as e:
+            reporter.messages.append((
+                -1,
+                f'Could not finish the parsing: {e}.',
+                '',
+                {},
+            ))
+
+        return reporter.messages
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/clean.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/clean.py
new file mode 100644
index 0000000..fc1711b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/clean.py
@@ -0,0 +1,76 @@
+"""distutils.command.clean
+
+Implements the Distutils 'clean' command."""
+
+# contributed by Bastian Kleineidam , added 2000-03-18
+
+import os
+from distutils._log import log
+from typing import ClassVar
+
+from ..core import Command
+from ..dir_util import remove_tree
+
+
+class clean(Command):
+    description = "clean up temporary files from 'build' command"
+    user_options = [
+        ('build-base=', 'b', "base build directory [default: 'build.build-base']"),
+        (
+            'build-lib=',
+            None,
+            "build directory for all modules [default: 'build.build-lib']",
+        ),
+        ('build-temp=', 't', "temporary build directory [default: 'build.build-temp']"),
+        (
+            'build-scripts=',
+            None,
+            "build directory for scripts [default: 'build.build-scripts']",
+        ),
+        ('bdist-base=', None, "temporary directory for built distributions"),
+        ('all', 'a', "remove all build output, not just temporary by-products"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['all']
+
+    def initialize_options(self):
+        self.build_base = None
+        self.build_lib = None
+        self.build_temp = None
+        self.build_scripts = None
+        self.bdist_base = None
+        self.all = None
+
+    def finalize_options(self):
+        self.set_undefined_options(
+            'build',
+            ('build_base', 'build_base'),
+            ('build_lib', 'build_lib'),
+            ('build_scripts', 'build_scripts'),
+            ('build_temp', 'build_temp'),
+        )
+        self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
+
+    def run(self):
+        # remove the build/temp. directory (unless it's already
+        # gone)
+        if os.path.exists(self.build_temp):
+            remove_tree(self.build_temp)
+        else:
+            log.debug("'%s' does not exist -- can't clean it", self.build_temp)
+
+        if self.all:
+            # remove build directories
+            for directory in (self.build_lib, self.bdist_base, self.build_scripts):
+                if os.path.exists(directory):
+                    remove_tree(directory)
+                else:
+                    log.warning("'%s' does not exist -- can't clean it", directory)
+
+        # just for the heck of it, try to remove the base build directory:
+        # we might have emptied it right now, but if not we don't care
+        try:
+            os.rmdir(self.build_base)
+            log.info("removing '%s'", self.build_base)
+        except OSError:
+            pass
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/config.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/config.py
new file mode 100644
index 0000000..9818418
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/config.py
@@ -0,0 +1,348 @@
+"""distutils.command.config
+
+Implements the Distutils 'config' command, a (mostly) empty command class
+that exists mainly to be sub-classed by specific module distributions and
+applications.  The idea is that while every "config" command is different,
+at least they're all named the same, and users always see "config" in the
+list of standard commands.  Also, this is a good place to put common
+configure-like tasks: "try to compile this C code", or "figure out where
+this header file lives".
+"""
+
+from __future__ import annotations
+
+import os
+import pathlib
+import re
+from collections.abc import Sequence
+from distutils._log import log
+
+from ..ccompiler import CCompiler, CompileError, LinkError, new_compiler
+from ..core import Command
+from ..errors import DistutilsExecError
+from ..sysconfig import customize_compiler
+
+LANG_EXT = {"c": ".c", "c++": ".cxx"}
+
+
+class config(Command):
+    description = "prepare to build"
+
+    user_options = [
+        ('compiler=', None, "specify the compiler type"),
+        ('cc=', None, "specify the compiler executable"),
+        ('include-dirs=', 'I', "list of directories to search for header files"),
+        ('define=', 'D', "C preprocessor macros to define"),
+        ('undef=', 'U', "C preprocessor macros to undefine"),
+        ('libraries=', 'l', "external C libraries to link with"),
+        ('library-dirs=', 'L', "directories to search for external C libraries"),
+        ('noisy', None, "show every action (compile, link, run, ...) taken"),
+        (
+            'dump-source',
+            None,
+            "dump generated source files before attempting to compile them",
+        ),
+    ]
+
+    # The three standard command methods: since the "config" command
+    # does nothing by default, these are empty.
+
+    def initialize_options(self):
+        self.compiler = None
+        self.cc = None
+        self.include_dirs = None
+        self.libraries = None
+        self.library_dirs = None
+
+        # maximal output for now
+        self.noisy = 1
+        self.dump_source = 1
+
+        # list of temporary files generated along-the-way that we have
+        # to clean at some point
+        self.temp_files = []
+
+    def finalize_options(self):
+        if self.include_dirs is None:
+            self.include_dirs = self.distribution.include_dirs or []
+        elif isinstance(self.include_dirs, str):
+            self.include_dirs = self.include_dirs.split(os.pathsep)
+
+        if self.libraries is None:
+            self.libraries = []
+        elif isinstance(self.libraries, str):
+            self.libraries = [self.libraries]
+
+        if self.library_dirs is None:
+            self.library_dirs = []
+        elif isinstance(self.library_dirs, str):
+            self.library_dirs = self.library_dirs.split(os.pathsep)
+
+    def run(self):
+        pass
+
+    # Utility methods for actual "config" commands.  The interfaces are
+    # loosely based on Autoconf macros of similar names.  Sub-classes
+    # may use these freely.
+
+    def _check_compiler(self):
+        """Check that 'self.compiler' really is a CCompiler object;
+        if not, make it one.
+        """
+        if not isinstance(self.compiler, CCompiler):
+            self.compiler = new_compiler(compiler=self.compiler, force=True)
+            customize_compiler(self.compiler)
+            if self.include_dirs:
+                self.compiler.set_include_dirs(self.include_dirs)
+            if self.libraries:
+                self.compiler.set_libraries(self.libraries)
+            if self.library_dirs:
+                self.compiler.set_library_dirs(self.library_dirs)
+
+    def _gen_temp_sourcefile(self, body, headers, lang):
+        filename = "_configtest" + LANG_EXT[lang]
+        with open(filename, "w", encoding='utf-8') as file:
+            if headers:
+                for header in headers:
+                    file.write(f"#include <{header}>\n")
+                file.write("\n")
+            file.write(body)
+            if body[-1] != "\n":
+                file.write("\n")
+        return filename
+
+    def _preprocess(self, body, headers, include_dirs, lang):
+        src = self._gen_temp_sourcefile(body, headers, lang)
+        out = "_configtest.i"
+        self.temp_files.extend([src, out])
+        self.compiler.preprocess(src, out, include_dirs=include_dirs)
+        return (src, out)
+
+    def _compile(self, body, headers, include_dirs, lang):
+        src = self._gen_temp_sourcefile(body, headers, lang)
+        if self.dump_source:
+            dump_file(src, f"compiling '{src}':")
+        (obj,) = self.compiler.object_filenames([src])
+        self.temp_files.extend([src, obj])
+        self.compiler.compile([src], include_dirs=include_dirs)
+        return (src, obj)
+
+    def _link(self, body, headers, include_dirs, libraries, library_dirs, lang):
+        (src, obj) = self._compile(body, headers, include_dirs, lang)
+        prog = os.path.splitext(os.path.basename(src))[0]
+        self.compiler.link_executable(
+            [obj],
+            prog,
+            libraries=libraries,
+            library_dirs=library_dirs,
+            target_lang=lang,
+        )
+
+        if self.compiler.exe_extension is not None:
+            prog = prog + self.compiler.exe_extension
+        self.temp_files.append(prog)
+
+        return (src, obj, prog)
+
+    def _clean(self, *filenames):
+        if not filenames:
+            filenames = self.temp_files
+            self.temp_files = []
+        log.info("removing: %s", ' '.join(filenames))
+        for filename in filenames:
+            try:
+                os.remove(filename)
+            except OSError:
+                pass
+
+    # XXX need access to the header search path and maybe default macros.
+
+    def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"):
+        """Construct a source file from 'body' (a string containing lines
+        of C/C++ code) and 'headers' (a list of header files to include)
+        and run it through the preprocessor.  Return true if the
+        preprocessor succeeded, false if there were any errors.
+        ('body' probably isn't of much use, but what the heck.)
+        """
+        self._check_compiler()
+        ok = True
+        try:
+            self._preprocess(body, headers, include_dirs, lang)
+        except CompileError:
+            ok = False
+
+        self._clean()
+        return ok
+
+    def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, lang="c"):
+        """Construct a source file (just like 'try_cpp()'), run it through
+        the preprocessor, and return true if any line of the output matches
+        'pattern'.  'pattern' should either be a compiled regex object or a
+        string containing a regex.  If both 'body' and 'headers' are None,
+        preprocesses an empty file -- which can be useful to determine the
+        symbols the preprocessor and compiler set by default.
+        """
+        self._check_compiler()
+        src, out = self._preprocess(body, headers, include_dirs, lang)
+
+        if isinstance(pattern, str):
+            pattern = re.compile(pattern)
+
+        with open(out, encoding='utf-8') as file:
+            match = any(pattern.search(line) for line in file)
+
+        self._clean()
+        return match
+
+    def try_compile(self, body, headers=None, include_dirs=None, lang="c"):
+        """Try to compile a source file built from 'body' and 'headers'.
+        Return true on success, false otherwise.
+        """
+        self._check_compiler()
+        try:
+            self._compile(body, headers, include_dirs, lang)
+            ok = True
+        except CompileError:
+            ok = False
+
+        log.info(ok and "success!" or "failure.")
+        self._clean()
+        return ok
+
+    def try_link(
+        self,
+        body,
+        headers=None,
+        include_dirs=None,
+        libraries=None,
+        library_dirs=None,
+        lang="c",
+    ):
+        """Try to compile and link a source file, built from 'body' and
+        'headers', to executable form.  Return true on success, false
+        otherwise.
+        """
+        self._check_compiler()
+        try:
+            self._link(body, headers, include_dirs, libraries, library_dirs, lang)
+            ok = True
+        except (CompileError, LinkError):
+            ok = False
+
+        log.info(ok and "success!" or "failure.")
+        self._clean()
+        return ok
+
+    def try_run(
+        self,
+        body,
+        headers=None,
+        include_dirs=None,
+        libraries=None,
+        library_dirs=None,
+        lang="c",
+    ):
+        """Try to compile, link to an executable, and run a program
+        built from 'body' and 'headers'.  Return true on success, false
+        otherwise.
+        """
+        self._check_compiler()
+        try:
+            src, obj, exe = self._link(
+                body, headers, include_dirs, libraries, library_dirs, lang
+            )
+            self.spawn([exe])
+            ok = True
+        except (CompileError, LinkError, DistutilsExecError):
+            ok = False
+
+        log.info(ok and "success!" or "failure.")
+        self._clean()
+        return ok
+
+    # -- High-level methods --------------------------------------------
+    # (these are the ones that are actually likely to be useful
+    # when implementing a real-world config command!)
+
+    def check_func(
+        self,
+        func,
+        headers=None,
+        include_dirs=None,
+        libraries=None,
+        library_dirs=None,
+        decl=False,
+        call=False,
+    ):
+        """Determine if function 'func' is available by constructing a
+        source file that refers to 'func', and compiles and links it.
+        If everything succeeds, returns true; otherwise returns false.
+
+        The constructed source file starts out by including the header
+        files listed in 'headers'.  If 'decl' is true, it then declares
+        'func' (as "int func()"); you probably shouldn't supply 'headers'
+        and set 'decl' true in the same call, or you might get errors about
+        a conflicting declarations for 'func'.  Finally, the constructed
+        'main()' function either references 'func' or (if 'call' is true)
+        calls it.  'libraries' and 'library_dirs' are used when
+        linking.
+        """
+        self._check_compiler()
+        body = []
+        if decl:
+            body.append(f"int {func} ();")
+        body.append("int main () {")
+        if call:
+            body.append(f"  {func}();")
+        else:
+            body.append(f"  {func};")
+        body.append("}")
+        body = "\n".join(body) + "\n"
+
+        return self.try_link(body, headers, include_dirs, libraries, library_dirs)
+
+    def check_lib(
+        self,
+        library,
+        library_dirs=None,
+        headers=None,
+        include_dirs=None,
+        other_libraries: Sequence[str] = [],
+    ):
+        """Determine if 'library' is available to be linked against,
+        without actually checking that any particular symbols are provided
+        by it.  'headers' will be used in constructing the source file to
+        be compiled, but the only effect of this is to check if all the
+        header files listed are available.  Any libraries listed in
+        'other_libraries' will be included in the link, in case 'library'
+        has symbols that depend on other libraries.
+        """
+        self._check_compiler()
+        return self.try_link(
+            "int main (void) { }",
+            headers,
+            include_dirs,
+            [library] + list(other_libraries),
+            library_dirs,
+        )
+
+    def check_header(self, header, include_dirs=None, library_dirs=None, lang="c"):
+        """Determine if the system header file named by 'header_file'
+        exists and can be found by the preprocessor; return true if so,
+        false otherwise.
+        """
+        return self.try_cpp(
+            body="/* No body */", headers=[header], include_dirs=include_dirs
+        )
+
+
+def dump_file(filename, head=None):
+    """Dumps a file content into log.info.
+
+    If head is not None, will be dumped before the file content.
+    """
+    if head is None:
+        log.info('%s', filename)
+    else:
+        log.info(head)
+    log.info(pathlib.Path(filename).read_text(encoding='utf-8'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install.py
new file mode 100644
index 0000000..dc17e56
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install.py
@@ -0,0 +1,805 @@
+"""distutils.command.install
+
+Implements the Distutils 'install' command."""
+
+from __future__ import annotations
+
+import collections
+import contextlib
+import itertools
+import os
+import sys
+import sysconfig
+from distutils._log import log
+from site import USER_BASE, USER_SITE
+from typing import ClassVar
+
+from ..core import Command
+from ..debug import DEBUG
+from ..errors import DistutilsOptionError, DistutilsPlatformError
+from ..file_util import write_file
+from ..sysconfig import get_config_vars
+from ..util import change_root, convert_path, get_platform, subst_vars
+from . import _framework_compat as fw
+
+HAS_USER_SITE = True
+
+WINDOWS_SCHEME = {
+    'purelib': '{base}/Lib/site-packages',
+    'platlib': '{base}/Lib/site-packages',
+    'headers': '{base}/Include/{dist_name}',
+    'scripts': '{base}/Scripts',
+    'data': '{base}',
+}
+
+INSTALL_SCHEMES = {
+    'posix_prefix': {
+        'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages',
+        'platlib': '{platbase}/{platlibdir}/{implementation_lower}'
+        '{py_version_short}/site-packages',
+        'headers': '{base}/include/{implementation_lower}'
+        '{py_version_short}{abiflags}/{dist_name}',
+        'scripts': '{base}/bin',
+        'data': '{base}',
+    },
+    'posix_home': {
+        'purelib': '{base}/lib/{implementation_lower}',
+        'platlib': '{base}/{platlibdir}/{implementation_lower}',
+        'headers': '{base}/include/{implementation_lower}/{dist_name}',
+        'scripts': '{base}/bin',
+        'data': '{base}',
+    },
+    'nt': WINDOWS_SCHEME,
+    'pypy': {
+        'purelib': '{base}/site-packages',
+        'platlib': '{base}/site-packages',
+        'headers': '{base}/include/{dist_name}',
+        'scripts': '{base}/bin',
+        'data': '{base}',
+    },
+    'pypy_nt': {
+        'purelib': '{base}/site-packages',
+        'platlib': '{base}/site-packages',
+        'headers': '{base}/include/{dist_name}',
+        'scripts': '{base}/Scripts',
+        'data': '{base}',
+    },
+}
+
+# user site schemes
+if HAS_USER_SITE:
+    INSTALL_SCHEMES['nt_user'] = {
+        'purelib': '{usersite}',
+        'platlib': '{usersite}',
+        'headers': '{userbase}/{implementation}{py_version_nodot_plat}'
+        '/Include/{dist_name}',
+        'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts',
+        'data': '{userbase}',
+    }
+
+    INSTALL_SCHEMES['posix_user'] = {
+        'purelib': '{usersite}',
+        'platlib': '{usersite}',
+        'headers': '{userbase}/include/{implementation_lower}'
+        '{py_version_short}{abiflags}/{dist_name}',
+        'scripts': '{userbase}/bin',
+        'data': '{userbase}',
+    }
+
+
+INSTALL_SCHEMES.update(fw.schemes)
+
+
+# The keys to an installation scheme; if any new types of files are to be
+# installed, be sure to add an entry to every installation scheme above,
+# and to SCHEME_KEYS here.
+SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
+
+
+def _load_sysconfig_schemes():
+    with contextlib.suppress(AttributeError):
+        return {
+            scheme: sysconfig.get_paths(scheme, expand=False)
+            for scheme in sysconfig.get_scheme_names()
+        }
+
+
+def _load_schemes():
+    """
+    Extend default schemes with schemes from sysconfig.
+    """
+
+    sysconfig_schemes = _load_sysconfig_schemes() or {}
+
+    return {
+        scheme: {
+            **INSTALL_SCHEMES.get(scheme, {}),
+            **sysconfig_schemes.get(scheme, {}),
+        }
+        for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes))
+    }
+
+
+def _get_implementation():
+    if hasattr(sys, 'pypy_version_info'):
+        return 'PyPy'
+    else:
+        return 'Python'
+
+
+def _select_scheme(ob, name):
+    scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name)))
+    vars(ob).update(_remove_set(ob, _scheme_attrs(scheme)))
+
+
+def _remove_set(ob, attrs):
+    """
+    Include only attrs that are None in ob.
+    """
+    return {key: value for key, value in attrs.items() if getattr(ob, key) is None}
+
+
+def _resolve_scheme(name):
+    os_name, sep, key = name.partition('_')
+    try:
+        resolved = sysconfig.get_preferred_scheme(key)
+    except Exception:
+        resolved = fw.scheme(name)
+    return resolved
+
+
+def _load_scheme(name):
+    return _load_schemes()[name]
+
+
+def _inject_headers(name, scheme):
+    """
+    Given a scheme name and the resolved scheme,
+    if the scheme does not include headers, resolve
+    the fallback scheme for the name and use headers
+    from it. pypa/distutils#88
+    """
+    # Bypass the preferred scheme, which may not
+    # have defined headers.
+    fallback = _load_scheme(name)
+    scheme.setdefault('headers', fallback['headers'])
+    return scheme
+
+
+def _scheme_attrs(scheme):
+    """Resolve install directories by applying the install schemes."""
+    return {f'install_{key}': scheme[key] for key in SCHEME_KEYS}
+
+
+class install(Command):
+    description = "install everything from build directory"
+
+    user_options = [
+        # Select installation scheme and set base director(y|ies)
+        ('prefix=', None, "installation prefix"),
+        ('exec-prefix=', None, "(Unix only) prefix for platform-specific files"),
+        ('home=', None, "(Unix only) home directory to install under"),
+        # Or, just set the base director(y|ies)
+        (
+            'install-base=',
+            None,
+            "base installation directory (instead of --prefix or --home)",
+        ),
+        (
+            'install-platbase=',
+            None,
+            "base installation directory for platform-specific files (instead of --exec-prefix or --home)",
+        ),
+        ('root=', None, "install everything relative to this alternate root directory"),
+        # Or, explicitly set the installation scheme
+        (
+            'install-purelib=',
+            None,
+            "installation directory for pure Python module distributions",
+        ),
+        (
+            'install-platlib=',
+            None,
+            "installation directory for non-pure module distributions",
+        ),
+        (
+            'install-lib=',
+            None,
+            "installation directory for all module distributions (overrides --install-purelib and --install-platlib)",
+        ),
+        ('install-headers=', None, "installation directory for C/C++ headers"),
+        ('install-scripts=', None, "installation directory for Python scripts"),
+        ('install-data=', None, "installation directory for data files"),
+        # Byte-compilation options -- see install_lib.py for details, as
+        # these are duplicated from there (but only install_lib does
+        # anything with them).
+        ('compile', 'c', "compile .py to .pyc [default]"),
+        ('no-compile', None, "don't compile .py files"),
+        (
+            'optimize=',
+            'O',
+            "also compile with optimization: -O1 for \"python -O\", "
+            "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
+        ),
+        # Miscellaneous control options
+        ('force', 'f', "force installation (overwrite any existing files)"),
+        ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
+        # Where to install documentation (eventually!)
+        # ('doc-format=', None, "format of documentation to generate"),
+        # ('install-man=', None, "directory for Unix man pages"),
+        # ('install-html=', None, "directory for HTML documentation"),
+        # ('install-info=', None, "directory for GNU info files"),
+        ('record=', None, "filename in which to record list of installed files"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['compile', 'force', 'skip-build']
+
+    if HAS_USER_SITE:
+        user_options.append((
+            'user',
+            None,
+            f"install in user site-package '{USER_SITE}'",
+        ))
+        boolean_options.append('user')
+
+    negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
+
+    def initialize_options(self) -> None:
+        """Initializes options."""
+        # High-level options: these select both an installation base
+        # and scheme.
+        self.prefix: str | None = None
+        self.exec_prefix: str | None = None
+        self.home: str | None = None
+        self.user = False
+
+        # These select only the installation base; it's up to the user to
+        # specify the installation scheme (currently, that means supplying
+        # the --install-{platlib,purelib,scripts,data} options).
+        self.install_base = None
+        self.install_platbase = None
+        self.root: str | None = None
+
+        # These options are the actual installation directories; if not
+        # supplied by the user, they are filled in using the installation
+        # scheme implied by prefix/exec-prefix/home and the contents of
+        # that installation scheme.
+        self.install_purelib = None  # for pure module distributions
+        self.install_platlib = None  # non-pure (dists w/ extensions)
+        self.install_headers = None  # for C/C++ headers
+        self.install_lib: str | None = None  # set to either purelib or platlib
+        self.install_scripts = None
+        self.install_data = None
+        self.install_userbase = USER_BASE
+        self.install_usersite = USER_SITE
+
+        self.compile = None
+        self.optimize = None
+
+        # Deprecated
+        # These two are for putting non-packagized distributions into their
+        # own directory and creating a .pth file if it makes sense.
+        # 'extra_path' comes from the setup file; 'install_path_file' can
+        # be turned off if it makes no sense to install a .pth file.  (But
+        # better to install it uselessly than to guess wrong and not
+        # install it when it's necessary and would be used!)  Currently,
+        # 'install_path_file' is always true unless some outsider meddles
+        # with it.
+        self.extra_path = None
+        self.install_path_file = True
+
+        # 'force' forces installation, even if target files are not
+        # out-of-date.  'skip_build' skips running the "build" command,
+        # handy if you know it's not necessary.  'warn_dir' (which is *not*
+        # a user option, it's just there so the bdist_* commands can turn
+        # it off) determines whether we warn about installing to a
+        # directory not in sys.path.
+        self.force = False
+        self.skip_build = False
+        self.warn_dir = True
+
+        # These are only here as a conduit from the 'build' command to the
+        # 'install_*' commands that do the real work.  ('build_base' isn't
+        # actually used anywhere, but it might be useful in future.)  They
+        # are not user options, because if the user told the install
+        # command where the build directory is, that wouldn't affect the
+        # build command.
+        self.build_base = None
+        self.build_lib = None
+
+        # Not defined yet because we don't know anything about
+        # documentation yet.
+        # self.install_man = None
+        # self.install_html = None
+        # self.install_info = None
+
+        self.record = None
+
+    # -- Option finalizing methods -------------------------------------
+    # (This is rather more involved than for most commands,
+    # because this is where the policy for installing third-
+    # party Python modules on various platforms given a wide
+    # array of user input is decided.  Yes, it's quite complex!)
+
+    def finalize_options(self) -> None:  # noqa: C901
+        """Finalizes options."""
+        # This method (and its helpers, like 'finalize_unix()',
+        # 'finalize_other()', and 'select_scheme()') is where the default
+        # installation directories for modules, extension modules, and
+        # anything else we care to install from a Python module
+        # distribution.  Thus, this code makes a pretty important policy
+        # statement about how third-party stuff is added to a Python
+        # installation!  Note that the actual work of installation is done
+        # by the relatively simple 'install_*' commands; they just take
+        # their orders from the installation directory options determined
+        # here.
+
+        # Check for errors/inconsistencies in the options; first, stuff
+        # that's wrong on any platform.
+
+        if (self.prefix or self.exec_prefix or self.home) and (
+            self.install_base or self.install_platbase
+        ):
+            raise DistutilsOptionError(
+                "must supply either prefix/exec-prefix/home or install-base/install-platbase -- not both"
+            )
+
+        if self.home and (self.prefix or self.exec_prefix):
+            raise DistutilsOptionError(
+                "must supply either home or prefix/exec-prefix -- not both"
+            )
+
+        if self.user and (
+            self.prefix
+            or self.exec_prefix
+            or self.home
+            or self.install_base
+            or self.install_platbase
+        ):
+            raise DistutilsOptionError(
+                "can't combine user with prefix, "
+                "exec_prefix/home, or install_(plat)base"
+            )
+
+        # Next, stuff that's wrong (or dubious) only on certain platforms.
+        if os.name != "posix":
+            if self.exec_prefix:
+                self.warn("exec-prefix option ignored on this platform")
+                self.exec_prefix = None
+
+        # Now the interesting logic -- so interesting that we farm it out
+        # to other methods.  The goal of these methods is to set the final
+        # values for the install_{lib,scripts,data,...}  options, using as
+        # input a heady brew of prefix, exec_prefix, home, install_base,
+        # install_platbase, user-supplied versions of
+        # install_{purelib,platlib,lib,scripts,data,...}, and the
+        # install schemes.  Phew!
+
+        self.dump_dirs("pre-finalize_{unix,other}")
+
+        if os.name == 'posix':
+            self.finalize_unix()
+        else:
+            self.finalize_other()
+
+        self.dump_dirs("post-finalize_{unix,other}()")
+
+        # Expand configuration variables, tilde, etc. in self.install_base
+        # and self.install_platbase -- that way, we can use $base or
+        # $platbase in the other installation directories and not worry
+        # about needing recursive variable expansion (shudder).
+
+        py_version = sys.version.split()[0]
+        (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix')
+        try:
+            abiflags = sys.abiflags
+        except AttributeError:
+            # sys.abiflags may not be defined on all platforms.
+            abiflags = ''
+        local_vars = {
+            'dist_name': self.distribution.get_name(),
+            'dist_version': self.distribution.get_version(),
+            'dist_fullname': self.distribution.get_fullname(),
+            'py_version': py_version,
+            'py_version_short': f'{sys.version_info.major}.{sys.version_info.minor}',
+            'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}',
+            'sys_prefix': prefix,
+            'prefix': prefix,
+            'sys_exec_prefix': exec_prefix,
+            'exec_prefix': exec_prefix,
+            'abiflags': abiflags,
+            'platlibdir': getattr(sys, 'platlibdir', 'lib'),
+            'implementation_lower': _get_implementation().lower(),
+            'implementation': _get_implementation(),
+        }
+
+        # vars for compatibility on older Pythons
+        compat_vars = dict(
+            # Python 3.9 and earlier
+            py_version_nodot_plat=getattr(sys, 'winver', '').replace('.', ''),
+        )
+
+        if HAS_USER_SITE:
+            local_vars['userbase'] = self.install_userbase
+            local_vars['usersite'] = self.install_usersite
+
+        self.config_vars = collections.ChainMap(
+            local_vars,
+            sysconfig.get_config_vars(),
+            compat_vars,
+            fw.vars(),
+        )
+
+        self.expand_basedirs()
+
+        self.dump_dirs("post-expand_basedirs()")
+
+        # Now define config vars for the base directories so we can expand
+        # everything else.
+        local_vars['base'] = self.install_base
+        local_vars['platbase'] = self.install_platbase
+
+        if DEBUG:
+            from pprint import pprint
+
+            print("config vars:")
+            pprint(dict(self.config_vars))
+
+        # Expand "~" and configuration variables in the installation
+        # directories.
+        self.expand_dirs()
+
+        self.dump_dirs("post-expand_dirs()")
+
+        # Create directories in the home dir:
+        if self.user:
+            self.create_home_path()
+
+        # Pick the actual directory to install all modules to: either
+        # install_purelib or install_platlib, depending on whether this
+        # module distribution is pure or not.  Of course, if the user
+        # already specified install_lib, use their selection.
+        if self.install_lib is None:
+            if self.distribution.has_ext_modules():  # has extensions: non-pure
+                self.install_lib = self.install_platlib
+            else:
+                self.install_lib = self.install_purelib
+
+        # Convert directories from Unix /-separated syntax to the local
+        # convention.
+        self.convert_paths(
+            'lib',
+            'purelib',
+            'platlib',
+            'scripts',
+            'data',
+            'headers',
+            'userbase',
+            'usersite',
+        )
+
+        # Deprecated
+        # Well, we're not actually fully completely finalized yet: we still
+        # have to deal with 'extra_path', which is the hack for allowing
+        # non-packagized module distributions (hello, Numerical Python!) to
+        # get their own directories.
+        self.handle_extra_path()
+        self.install_libbase = self.install_lib  # needed for .pth file
+        self.install_lib = os.path.join(self.install_lib, self.extra_dirs)
+
+        # If a new root directory was supplied, make all the installation
+        # dirs relative to it.
+        if self.root is not None:
+            self.change_roots(
+                'libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers'
+            )
+
+        self.dump_dirs("after prepending root")
+
+        # Find out the build directories, ie. where to install from.
+        self.set_undefined_options(
+            'build', ('build_base', 'build_base'), ('build_lib', 'build_lib')
+        )
+
+        # Punt on doc directories for now -- after all, we're punting on
+        # documentation completely!
+
+    def dump_dirs(self, msg) -> None:
+        """Dumps the list of user options."""
+        if not DEBUG:
+            return
+        from ..fancy_getopt import longopt_xlate
+
+        log.debug(msg + ":")
+        for opt in self.user_options:
+            opt_name = opt[0]
+            if opt_name[-1] == "=":
+                opt_name = opt_name[0:-1]
+            if opt_name in self.negative_opt:
+                opt_name = self.negative_opt[opt_name]
+                opt_name = opt_name.translate(longopt_xlate)
+                val = not getattr(self, opt_name)
+            else:
+                opt_name = opt_name.translate(longopt_xlate)
+                val = getattr(self, opt_name)
+            log.debug("  %s: %s", opt_name, val)
+
+    def finalize_unix(self) -> None:
+        """Finalizes options for posix platforms."""
+        if self.install_base is not None or self.install_platbase is not None:
+            incomplete_scheme = (
+                (
+                    self.install_lib is None
+                    and self.install_purelib is None
+                    and self.install_platlib is None
+                )
+                or self.install_headers is None
+                or self.install_scripts is None
+                or self.install_data is None
+            )
+            if incomplete_scheme:
+                raise DistutilsOptionError(
+                    "install-base or install-platbase supplied, but "
+                    "installation scheme is incomplete"
+                )
+            return
+
+        if self.user:
+            if self.install_userbase is None:
+                raise DistutilsPlatformError("User base directory is not specified")
+            self.install_base = self.install_platbase = self.install_userbase
+            self.select_scheme("posix_user")
+        elif self.home is not None:
+            self.install_base = self.install_platbase = self.home
+            self.select_scheme("posix_home")
+        else:
+            if self.prefix is None:
+                if self.exec_prefix is not None:
+                    raise DistutilsOptionError(
+                        "must not supply exec-prefix without prefix"
+                    )
+
+                # Allow Fedora to add components to the prefix
+                _prefix_addition = getattr(sysconfig, '_prefix_addition', "")
+
+                self.prefix = os.path.normpath(sys.prefix) + _prefix_addition
+                self.exec_prefix = os.path.normpath(sys.exec_prefix) + _prefix_addition
+
+            else:
+                if self.exec_prefix is None:
+                    self.exec_prefix = self.prefix
+
+            self.install_base = self.prefix
+            self.install_platbase = self.exec_prefix
+            self.select_scheme("posix_prefix")
+
+    def finalize_other(self) -> None:
+        """Finalizes options for non-posix platforms"""
+        if self.user:
+            if self.install_userbase is None:
+                raise DistutilsPlatformError("User base directory is not specified")
+            self.install_base = self.install_platbase = self.install_userbase
+            self.select_scheme(os.name + "_user")
+        elif self.home is not None:
+            self.install_base = self.install_platbase = self.home
+            self.select_scheme("posix_home")
+        else:
+            if self.prefix is None:
+                self.prefix = os.path.normpath(sys.prefix)
+
+            self.install_base = self.install_platbase = self.prefix
+            try:
+                self.select_scheme(os.name)
+            except KeyError:
+                raise DistutilsPlatformError(
+                    f"I don't know how to install stuff on '{os.name}'"
+                )
+
+    def select_scheme(self, name) -> None:
+        _select_scheme(self, name)
+
+    def _expand_attrs(self, attrs):
+        for attr in attrs:
+            val = getattr(self, attr)
+            if val is not None:
+                if os.name in ('posix', 'nt'):
+                    val = os.path.expanduser(val)
+                val = subst_vars(val, self.config_vars)
+                setattr(self, attr, val)
+
+    def expand_basedirs(self) -> None:
+        """Calls `os.path.expanduser` on install_base, install_platbase and
+        root."""
+        self._expand_attrs(['install_base', 'install_platbase', 'root'])
+
+    def expand_dirs(self) -> None:
+        """Calls `os.path.expanduser` on install dirs."""
+        self._expand_attrs([
+            'install_purelib',
+            'install_platlib',
+            'install_lib',
+            'install_headers',
+            'install_scripts',
+            'install_data',
+        ])
+
+    def convert_paths(self, *names) -> None:
+        """Call `convert_path` over `names`."""
+        for name in names:
+            attr = "install_" + name
+            setattr(self, attr, convert_path(getattr(self, attr)))
+
+    def handle_extra_path(self) -> None:
+        """Set `path_file` and `extra_dirs` using `extra_path`."""
+        if self.extra_path is None:
+            self.extra_path = self.distribution.extra_path
+
+        if self.extra_path is not None:
+            log.warning(
+                "Distribution option extra_path is deprecated. "
+                "See issue27919 for details."
+            )
+            if isinstance(self.extra_path, str):
+                self.extra_path = self.extra_path.split(',')
+
+            if len(self.extra_path) == 1:
+                path_file = extra_dirs = self.extra_path[0]
+            elif len(self.extra_path) == 2:
+                path_file, extra_dirs = self.extra_path
+            else:
+                raise DistutilsOptionError(
+                    "'extra_path' option must be a list, tuple, or "
+                    "comma-separated string with 1 or 2 elements"
+                )
+
+            # convert to local form in case Unix notation used (as it
+            # should be in setup scripts)
+            extra_dirs = convert_path(extra_dirs)
+        else:
+            path_file = None
+            extra_dirs = ''
+
+        # XXX should we warn if path_file and not extra_dirs? (in which
+        # case the path file would be harmless but pointless)
+        self.path_file = path_file
+        self.extra_dirs = extra_dirs
+
+    def change_roots(self, *names) -> None:
+        """Change the install directories pointed by name using root."""
+        for name in names:
+            attr = "install_" + name
+            setattr(self, attr, change_root(self.root, getattr(self, attr)))
+
+    def create_home_path(self) -> None:
+        """Create directories under ~."""
+        if not self.user:
+            return
+        home = convert_path(os.path.expanduser("~"))
+        for path in self.config_vars.values():
+            if str(path).startswith(home) and not os.path.isdir(path):
+                self.debug_print(f"os.makedirs('{path}', 0o700)")
+                os.makedirs(path, 0o700)
+
+    # -- Command execution methods -------------------------------------
+
+    def run(self):
+        """Runs the command."""
+        # Obviously have to build before we can install
+        if not self.skip_build:
+            self.run_command('build')
+            # If we built for any other platform, we can't install.
+            build_plat = self.distribution.get_command_obj('build').plat_name
+            # check warn_dir - it is a clue that the 'install' is happening
+            # internally, and not to sys.path, so we don't check the platform
+            # matches what we are running.
+            if self.warn_dir and build_plat != get_platform():
+                raise DistutilsPlatformError("Can't install when cross-compiling")
+
+        # Run all sub-commands (at least those that need to be run)
+        for cmd_name in self.get_sub_commands():
+            self.run_command(cmd_name)
+
+        if self.path_file:
+            self.create_path_file()
+
+        # write list of installed files, if requested.
+        if self.record:
+            outputs = self.get_outputs()
+            if self.root:  # strip any package prefix
+                root_len = len(self.root)
+                for counter in range(len(outputs)):
+                    outputs[counter] = outputs[counter][root_len:]
+            self.execute(
+                write_file,
+                (self.record, outputs),
+                f"writing list of installed files to '{self.record}'",
+            )
+
+        sys_path = map(os.path.normpath, sys.path)
+        sys_path = map(os.path.normcase, sys_path)
+        install_lib = os.path.normcase(os.path.normpath(self.install_lib))
+        if (
+            self.warn_dir
+            and not (self.path_file and self.install_path_file)
+            and install_lib not in sys_path
+        ):
+            log.debug(
+                (
+                    "modules installed to '%s', which is not in "
+                    "Python's module search path (sys.path) -- "
+                    "you'll have to change the search path yourself"
+                ),
+                self.install_lib,
+            )
+
+    def create_path_file(self):
+        """Creates the .pth file"""
+        filename = os.path.join(self.install_libbase, self.path_file + ".pth")
+        if self.install_path_file:
+            self.execute(
+                write_file, (filename, [self.extra_dirs]), f"creating {filename}"
+            )
+        else:
+            self.warn(f"path file '{filename}' not created")
+
+    # -- Reporting methods ---------------------------------------------
+
+    def get_outputs(self):
+        """Assembles the outputs of all the sub-commands."""
+        outputs = []
+        for cmd_name in self.get_sub_commands():
+            cmd = self.get_finalized_command(cmd_name)
+            # Add the contents of cmd.get_outputs(), ensuring
+            # that outputs doesn't contain duplicate entries
+            for filename in cmd.get_outputs():
+                if filename not in outputs:
+                    outputs.append(filename)
+
+        if self.path_file and self.install_path_file:
+            outputs.append(os.path.join(self.install_libbase, self.path_file + ".pth"))
+
+        return outputs
+
+    def get_inputs(self):
+        """Returns the inputs of all the sub-commands"""
+        # XXX gee, this looks familiar ;-(
+        inputs = []
+        for cmd_name in self.get_sub_commands():
+            cmd = self.get_finalized_command(cmd_name)
+            inputs.extend(cmd.get_inputs())
+
+        return inputs
+
+    # -- Predicates for sub-command list -------------------------------
+
+    def has_lib(self):
+        """Returns true if the current distribution has any Python
+        modules to install."""
+        return (
+            self.distribution.has_pure_modules() or self.distribution.has_ext_modules()
+        )
+
+    def has_headers(self):
+        """Returns true if the current distribution has any headers to
+        install."""
+        return self.distribution.has_headers()
+
+    def has_scripts(self):
+        """Returns true if the current distribution has any scripts to.
+        install."""
+        return self.distribution.has_scripts()
+
+    def has_data(self):
+        """Returns true if the current distribution has any data to.
+        install."""
+        return self.distribution.has_data_files()
+
+    # 'sub_commands': a list of commands this command might have to run to
+    # get its work done.  See cmd.py for more info.
+    sub_commands = [
+        ('install_lib', has_lib),
+        ('install_headers', has_headers),
+        ('install_scripts', has_scripts),
+        ('install_data', has_data),
+        ('install_egg_info', lambda self: True),
+    ]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_data.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_data.py
new file mode 100644
index 0000000..4ad186e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_data.py
@@ -0,0 +1,94 @@
+"""distutils.command.install_data
+
+Implements the Distutils 'install_data' command, for installing
+platform-independent data files."""
+
+# contributed by Bastian Kleineidam
+
+from __future__ import annotations
+
+import functools
+import os
+from collections.abc import Iterable
+from typing import ClassVar
+
+from ..core import Command
+from ..util import change_root, convert_path
+
+
+class install_data(Command):
+    description = "install data files"
+
+    user_options = [
+        (
+            'install-dir=',
+            'd',
+            "base directory for installing data files [default: installation base dir]",
+        ),
+        ('root=', None, "install everything relative to this alternate root directory"),
+        ('force', 'f', "force installation (overwrite existing files)"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['force']
+
+    def initialize_options(self):
+        self.install_dir = None
+        self.outfiles = []
+        self.root = None
+        self.force = False
+        self.data_files = self.distribution.data_files
+        self.warn_dir = True
+
+    def finalize_options(self) -> None:
+        self.set_undefined_options(
+            'install',
+            ('install_data', 'install_dir'),
+            ('root', 'root'),
+            ('force', 'force'),
+        )
+
+    def run(self) -> None:
+        self.mkpath(self.install_dir)
+        for f in self.data_files:
+            self._copy(f)
+
+    @functools.singledispatchmethod
+    def _copy(self, f: tuple[str | os.PathLike, Iterable[str | os.PathLike]]):
+        # it's a tuple with path to install to and a list of files
+        dir = convert_path(f[0])
+        if not os.path.isabs(dir):
+            dir = os.path.join(self.install_dir, dir)
+        elif self.root:
+            dir = change_root(self.root, dir)
+        self.mkpath(dir)
+
+        if f[1] == []:
+            # If there are no files listed, the user must be
+            # trying to create an empty directory, so add the
+            # directory to the list of output files.
+            self.outfiles.append(dir)
+        else:
+            # Copy files, adding them to the list of output files.
+            for data in f[1]:
+                data = convert_path(data)
+                (out, _) = self.copy_file(data, dir)
+                self.outfiles.append(out)
+
+    @_copy.register(str)
+    @_copy.register(os.PathLike)
+    def _(self, f: str | os.PathLike):
+        # it's a simple file, so copy it
+        f = convert_path(f)
+        if self.warn_dir:
+            self.warn(
+                "setup script did not provide a directory for "
+                f"'{f}' -- installing right in '{self.install_dir}'"
+            )
+        (out, _) = self.copy_file(f, self.install_dir)
+        self.outfiles.append(out)
+
+    def get_inputs(self):
+        return self.data_files or []
+
+    def get_outputs(self):
+        return self.outfiles
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_egg_info.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_egg_info.py
new file mode 100644
index 0000000..6840b34
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_egg_info.py
@@ -0,0 +1,90 @@
+"""
+distutils.command.install_egg_info
+
+Implements the Distutils 'install_egg_info' command, for installing
+a package's PKG-INFO metadata.
+"""
+
+import os
+import re
+import sys
+from typing import ClassVar
+
+from .. import dir_util
+from .._log import log
+from ..cmd import Command
+
+
+class install_egg_info(Command):
+    """Install an .egg-info file for the package"""
+
+    description = "Install package's PKG-INFO metadata as an .egg-info file"
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ('install-dir=', 'd', "directory to install to"),
+    ]
+
+    def initialize_options(self):
+        self.install_dir = None
+
+    @property
+    def basename(self):
+        """
+        Allow basename to be overridden by child class.
+        Ref pypa/distutils#2.
+        """
+        name = to_filename(safe_name(self.distribution.get_name()))
+        version = to_filename(safe_version(self.distribution.get_version()))
+        return f"{name}-{version}-py{sys.version_info.major}.{sys.version_info.minor}.egg-info"
+
+    def finalize_options(self):
+        self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))
+        self.target = os.path.join(self.install_dir, self.basename)
+        self.outputs = [self.target]
+
+    def run(self):
+        target = self.target
+        if os.path.isdir(target) and not os.path.islink(target):
+            dir_util.remove_tree(target)
+        elif os.path.exists(target):
+            self.execute(os.unlink, (self.target,), "Removing " + target)
+        elif not os.path.isdir(self.install_dir):
+            self.execute(
+                os.makedirs, (self.install_dir,), "Creating " + self.install_dir
+            )
+        log.info("Writing %s", target)
+        with open(target, 'w', encoding='UTF-8') as f:
+            self.distribution.metadata.write_pkg_file(f)
+
+    def get_outputs(self):
+        return self.outputs
+
+
+# The following routines are taken from setuptools' pkg_resources module and
+# can be replaced by importing them from pkg_resources once it is included
+# in the stdlib.
+
+
+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
+
+    Spaces become dots, and all other non-alphanumeric characters become
+    dashes, with runs of multiple dashes condensed to a single dash.
+    """
+    version = version.replace(' ', '.')
+    return re.sub('[^A-Za-z0-9.]+', '-', version)
+
+
+def to_filename(name):
+    """Convert a project or version name to its filename-escaped form
+
+    Any '-' characters are currently replaced with '_'.
+    """
+    return name.replace('-', '_')
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_headers.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_headers.py
new file mode 100644
index 0000000..97af137
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_headers.py
@@ -0,0 +1,46 @@
+"""distutils.command.install_headers
+
+Implements the Distutils 'install_headers' command, to install C/C++ header
+files to the Python include directory."""
+
+from typing import ClassVar
+
+from ..core import Command
+
+
+# XXX force is never used
+class install_headers(Command):
+    description = "install C/C++ header files"
+
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ('install-dir=', 'd', "directory to install header files to"),
+        ('force', 'f', "force installation (overwrite existing files)"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['force']
+
+    def initialize_options(self):
+        self.install_dir = None
+        self.force = False
+        self.outfiles = []
+
+    def finalize_options(self):
+        self.set_undefined_options(
+            'install', ('install_headers', 'install_dir'), ('force', 'force')
+        )
+
+    def run(self):
+        headers = self.distribution.headers
+        if not headers:
+            return
+
+        self.mkpath(self.install_dir)
+        for header in headers:
+            (out, _) = self.copy_file(header, self.install_dir)
+            self.outfiles.append(out)
+
+    def get_inputs(self):
+        return self.distribution.headers or []
+
+    def get_outputs(self):
+        return self.outfiles
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_lib.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_lib.py
new file mode 100644
index 0000000..9909a61
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_lib.py
@@ -0,0 +1,236 @@
+"""distutils.command.install_lib
+
+Implements the Distutils 'install_lib' command
+(install all Python modules)."""
+
+from __future__ import annotations
+
+import importlib.util
+import os
+import sys
+from typing import Any, ClassVar
+
+from ..core import Command
+from ..errors import DistutilsOptionError
+
+# Extension for Python source files.
+PYTHON_SOURCE_EXTENSION = ".py"
+
+
+class install_lib(Command):
+    description = "install all Python modules (extensions and pure Python)"
+
+    # The byte-compilation options are a tad confusing.  Here are the
+    # possible scenarios:
+    #   1) no compilation at all (--no-compile --no-optimize)
+    #   2) compile .pyc only (--compile --no-optimize; default)
+    #   3) compile .pyc and "opt-1" .pyc (--compile --optimize)
+    #   4) compile "opt-1" .pyc only (--no-compile --optimize)
+    #   5) compile .pyc and "opt-2" .pyc (--compile --optimize-more)
+    #   6) compile "opt-2" .pyc only (--no-compile --optimize-more)
+    #
+    # The UI for this is two options, 'compile' and 'optimize'.
+    # 'compile' is strictly boolean, and only decides whether to
+    # generate .pyc files.  'optimize' is three-way (0, 1, or 2), and
+    # decides both whether to generate .pyc files and what level of
+    # optimization to use.
+
+    user_options = [
+        ('install-dir=', 'd', "directory to install to"),
+        ('build-dir=', 'b', "build directory (where to install from)"),
+        ('force', 'f', "force installation (overwrite existing files)"),
+        ('compile', 'c', "compile .py to .pyc [default]"),
+        ('no-compile', None, "don't compile .py files"),
+        (
+            'optimize=',
+            'O',
+            "also compile with optimization: -O1 for \"python -O\", "
+            "-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
+        ),
+        ('skip-build', None, "skip the build steps"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['force', 'compile', 'skip-build']
+    negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
+
+    def initialize_options(self):
+        # let the 'install' command dictate our installation directory
+        self.install_dir = None
+        self.build_dir = None
+        self.force = False
+        self.compile = None
+        self.optimize = None
+        self.skip_build = None
+
+    def finalize_options(self) -> None:
+        # Get all the information we need to install pure Python modules
+        # from the umbrella 'install' command -- build (source) directory,
+        # install (target) directory, and whether to compile .py files.
+        self.set_undefined_options(
+            'install',
+            ('build_lib', 'build_dir'),
+            ('install_lib', 'install_dir'),
+            ('force', 'force'),
+            ('compile', 'compile'),
+            ('optimize', 'optimize'),
+            ('skip_build', 'skip_build'),
+        )
+
+        if self.compile is None:
+            self.compile = True
+        if self.optimize is None:
+            self.optimize = False
+
+        if not isinstance(self.optimize, int):
+            try:
+                self.optimize = int(self.optimize)
+            except ValueError:
+                pass
+            if self.optimize not in (0, 1, 2):
+                raise DistutilsOptionError("optimize must be 0, 1, or 2")
+
+    def run(self) -> None:
+        # Make sure we have built everything we need first
+        self.build()
+
+        # Install everything: simply dump the entire contents of the build
+        # directory to the installation directory (that's the beauty of
+        # having a build directory!)
+        outfiles = self.install()
+
+        # (Optionally) compile .py to .pyc
+        if outfiles is not None and self.distribution.has_pure_modules():
+            self.byte_compile(outfiles)
+
+    # -- Top-level worker functions ------------------------------------
+    # (called from 'run()')
+
+    def build(self) -> None:
+        if not self.skip_build:
+            if self.distribution.has_pure_modules():
+                self.run_command('build_py')
+            if self.distribution.has_ext_modules():
+                self.run_command('build_ext')
+
+    # Any: https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#the-any-trick
+    def install(self) -> list[str] | Any:
+        if os.path.isdir(self.build_dir):
+            outfiles = self.copy_tree(self.build_dir, self.install_dir)
+        else:
+            self.warn(
+                f"'{self.build_dir}' does not exist -- no Python modules to install"
+            )
+            return
+        return outfiles
+
+    def byte_compile(self, files) -> None:
+        if sys.dont_write_bytecode:
+            self.warn('byte-compiling is disabled, skipping.')
+            return
+
+        from ..util import byte_compile
+
+        # Get the "--root" directory supplied to the "install" command,
+        # and use it as a prefix to strip off the purported filename
+        # encoded in bytecode files.  This is far from complete, but it
+        # should at least generate usable bytecode in RPM distributions.
+        install_root = self.get_finalized_command('install').root
+
+        if self.compile:
+            byte_compile(
+                files,
+                optimize=0,
+                force=self.force,
+                prefix=install_root,
+            )
+        if self.optimize > 0:
+            byte_compile(
+                files,
+                optimize=self.optimize,
+                force=self.force,
+                prefix=install_root,
+                verbose=self.verbose,
+            )
+
+    # -- Utility methods -----------------------------------------------
+
+    def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):
+        if not has_any:
+            return []
+
+        build_cmd = self.get_finalized_command(build_cmd)
+        build_files = build_cmd.get_outputs()
+        build_dir = getattr(build_cmd, cmd_option)
+
+        prefix_len = len(build_dir) + len(os.sep)
+        outputs = [os.path.join(output_dir, file[prefix_len:]) for file in build_files]
+
+        return outputs
+
+    def _bytecode_filenames(self, py_filenames):
+        bytecode_files = []
+        for py_file in py_filenames:
+            # Since build_py handles package data installation, the
+            # list of outputs can contain more than just .py files.
+            # Make sure we only report bytecode for the .py files.
+            ext = os.path.splitext(os.path.normcase(py_file))[1]
+            if ext != PYTHON_SOURCE_EXTENSION:
+                continue
+            if self.compile:
+                bytecode_files.append(
+                    importlib.util.cache_from_source(py_file, optimization='')
+                )
+            if self.optimize > 0:
+                bytecode_files.append(
+                    importlib.util.cache_from_source(
+                        py_file, optimization=self.optimize
+                    )
+                )
+
+        return bytecode_files
+
+    # -- External interface --------------------------------------------
+    # (called by outsiders)
+
+    def get_outputs(self):
+        """Return the list of files that would be installed if this command
+        were actually run.  Not affected by the "dry-run" flag or whether
+        modules have actually been built yet.
+        """
+        pure_outputs = self._mutate_outputs(
+            self.distribution.has_pure_modules(),
+            'build_py',
+            'build_lib',
+            self.install_dir,
+        )
+        if self.compile:
+            bytecode_outputs = self._bytecode_filenames(pure_outputs)
+        else:
+            bytecode_outputs = []
+
+        ext_outputs = self._mutate_outputs(
+            self.distribution.has_ext_modules(),
+            'build_ext',
+            'build_lib',
+            self.install_dir,
+        )
+
+        return pure_outputs + bytecode_outputs + ext_outputs
+
+    def get_inputs(self):
+        """Get the list of files that are input to this command, ie. the
+        files that get installed as they are named in the build tree.
+        The files in this list correspond one-to-one to the output
+        filenames returned by 'get_outputs()'.
+        """
+        inputs = []
+
+        if self.distribution.has_pure_modules():
+            build_py = self.get_finalized_command('build_py')
+            inputs.extend(build_py.get_outputs())
+
+        if self.distribution.has_ext_modules():
+            build_ext = self.get_finalized_command('build_ext')
+            inputs.extend(build_ext.get_outputs())
+
+        return inputs
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_scripts.py
new file mode 100644
index 0000000..33d235d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/install_scripts.py
@@ -0,0 +1,59 @@
+"""distutils.command.install_scripts
+
+Implements the Distutils 'install_scripts' command, for installing
+Python scripts."""
+
+# contributed by Bastian Kleineidam
+
+import os
+from distutils._log import log
+from stat import ST_MODE
+from typing import ClassVar
+
+from ..core import Command
+
+
+class install_scripts(Command):
+    description = "install scripts (Python or otherwise)"
+
+    user_options = [
+        ('install-dir=', 'd', "directory to install scripts to"),
+        ('build-dir=', 'b', "build directory (where to install from)"),
+        ('force', 'f', "force installation (overwrite existing files)"),
+        ('skip-build', None, "skip the build steps"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = ['force', 'skip-build']
+
+    def initialize_options(self):
+        self.install_dir = None
+        self.force = False
+        self.build_dir = None
+        self.skip_build = None
+
+    def finalize_options(self) -> None:
+        self.set_undefined_options('build', ('build_scripts', 'build_dir'))
+        self.set_undefined_options(
+            'install',
+            ('install_scripts', 'install_dir'),
+            ('force', 'force'),
+            ('skip_build', 'skip_build'),
+        )
+
+    def run(self) -> None:
+        if not self.skip_build:
+            self.run_command('build_scripts')
+        self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
+        if os.name == 'posix':
+            # Set the executable bits (owner, group, and world) on
+            # all the scripts we just installed.
+            for file in self.get_outputs():
+                mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777
+                log.info("changing mode of %s to %o", file, mode)
+                os.chmod(file, mode)
+
+    def get_inputs(self):
+        return self.distribution.scripts or []
+
+    def get_outputs(self):
+        return self.outfiles or []
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/sdist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/sdist.py
new file mode 100644
index 0000000..e64cc82
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/command/sdist.py
@@ -0,0 +1,521 @@
+"""distutils.command.sdist
+
+Implements the Distutils 'sdist' command (create a source distribution)."""
+
+from __future__ import annotations
+
+import os
+import sys
+from collections.abc import Callable
+from distutils import archive_util, dir_util, file_util
+from distutils._log import log
+from glob import glob
+from itertools import filterfalse
+from typing import ClassVar
+
+from ..core import Command
+from ..errors import DistutilsOptionError, DistutilsTemplateError
+from ..filelist import FileList
+from ..text_file import TextFile
+from ..util import convert_path
+
+
+def show_formats():
+    """Print all possible values for the 'formats' option (used by
+    the "--help-formats" command-line option).
+    """
+    from ..archive_util import ARCHIVE_FORMATS
+    from ..fancy_getopt import FancyGetopt
+
+    formats = sorted(
+        ("formats=" + format, None, ARCHIVE_FORMATS[format][2])
+        for format in ARCHIVE_FORMATS.keys()
+    )
+    FancyGetopt(formats).print_help("List of available source distribution formats:")
+
+
+class sdist(Command):
+    description = "create a source distribution (tarball, zip file, etc.)"
+
+    def checking_metadata(self) -> bool:
+        """Callable used for the check sub-command.
+
+        Placed here so user_options can view it"""
+        return self.metadata_check
+
+    user_options = [
+        ('template=', 't', "name of manifest template file [default: MANIFEST.in]"),
+        ('manifest=', 'm', "name of manifest file [default: MANIFEST]"),
+        (
+            'use-defaults',
+            None,
+            "include the default file set in the manifest "
+            "[default; disable with --no-defaults]",
+        ),
+        ('no-defaults', None, "don't include the default file set"),
+        (
+            'prune',
+            None,
+            "specifically exclude files/directories that should not be "
+            "distributed (build tree, RCS/CVS dirs, etc.) "
+            "[default; disable with --no-prune]",
+        ),
+        ('no-prune', None, "don't automatically exclude anything"),
+        (
+            'manifest-only',
+            'o',
+            "just regenerate the manifest and then stop (implies --force-manifest)",
+        ),
+        (
+            'force-manifest',
+            'f',
+            "forcibly regenerate the manifest and carry on as usual. "
+            "Deprecated: now the manifest is always regenerated.",
+        ),
+        ('formats=', None, "formats for source distribution (comma-separated list)"),
+        (
+            'keep-temp',
+            'k',
+            "keep the distribution tree around after creating " + "archive file(s)",
+        ),
+        (
+            'dist-dir=',
+            'd',
+            "directory to put the source distribution archive(s) in [default: dist]",
+        ),
+        (
+            'metadata-check',
+            None,
+            "Ensure that all required elements of meta-data "
+            "are supplied. Warn if any missing. [default]",
+        ),
+        (
+            'owner=',
+            'u',
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            'group=',
+            'g',
+            "Group name used when creating a tar file [default: current group]",
+        ),
+    ]
+
+    boolean_options: ClassVar[list[str]] = [
+        'use-defaults',
+        'prune',
+        'manifest-only',
+        'force-manifest',
+        'keep-temp',
+        'metadata-check',
+    ]
+
+    help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
+        ('help-formats', None, "list available distribution formats", show_formats),
+    ]
+
+    negative_opt: ClassVar[dict[str, str]] = {
+        'no-defaults': 'use-defaults',
+        'no-prune': 'prune',
+    }
+
+    sub_commands = [('check', checking_metadata)]
+
+    READMES: ClassVar[tuple[str, ...]] = ('README', 'README.txt', 'README.rst')
+
+    def initialize_options(self):
+        # 'template' and 'manifest' are, respectively, the names of
+        # the manifest template and manifest file.
+        self.template = None
+        self.manifest = None
+
+        # 'use_defaults': if true, we will include the default file set
+        # in the manifest
+        self.use_defaults = True
+        self.prune = True
+
+        self.manifest_only = False
+        self.force_manifest = False
+
+        self.formats = ['gztar']
+        self.keep_temp = False
+        self.dist_dir = None
+
+        self.archive_files = None
+        self.metadata_check = True
+        self.owner = None
+        self.group = None
+
+    def finalize_options(self) -> None:
+        if self.manifest is None:
+            self.manifest = "MANIFEST"
+        if self.template is None:
+            self.template = "MANIFEST.in"
+
+        self.ensure_string_list('formats')
+
+        bad_format = archive_util.check_archive_formats(self.formats)
+        if bad_format:
+            raise DistutilsOptionError(f"unknown archive format '{bad_format}'")
+
+        if self.dist_dir is None:
+            self.dist_dir = "dist"
+
+    def run(self) -> None:
+        # 'filelist' contains the list of files that will make up the
+        # manifest
+        self.filelist = FileList()
+
+        # Run sub commands
+        for cmd_name in self.get_sub_commands():
+            self.run_command(cmd_name)
+
+        # Do whatever it takes to get the list of files to process
+        # (process the manifest template, read an existing manifest,
+        # whatever).  File list is accumulated in 'self.filelist'.
+        self.get_file_list()
+
+        # If user just wanted us to regenerate the manifest, stop now.
+        if self.manifest_only:
+            return
+
+        # Otherwise, go ahead and create the source distribution tarball,
+        # or zipfile, or whatever.
+        self.make_distribution()
+
+    def get_file_list(self) -> None:
+        """Figure out the list of files to include in the source
+        distribution, and put it in 'self.filelist'.  This might involve
+        reading the manifest template (and writing the manifest), or just
+        reading the manifest, or just using the default file set -- it all
+        depends on the user's options.
+        """
+        # new behavior when using a template:
+        # the file list is recalculated every time because
+        # even if MANIFEST.in or setup.py are not changed
+        # the user might have added some files in the tree that
+        # need to be included.
+        #
+        #  This makes --force the default and only behavior with templates.
+        template_exists = os.path.isfile(self.template)
+        if not template_exists and self._manifest_is_not_generated():
+            self.read_manifest()
+            self.filelist.sort()
+            self.filelist.remove_duplicates()
+            return
+
+        if not template_exists:
+            self.warn(
+                ("manifest template '%s' does not exist " + "(using default file list)")
+                % self.template
+            )
+        self.filelist.findall()
+
+        if self.use_defaults:
+            self.add_defaults()
+
+        if template_exists:
+            self.read_template()
+
+        if self.prune:
+            self.prune_file_list()
+
+        self.filelist.sort()
+        self.filelist.remove_duplicates()
+        self.write_manifest()
+
+    def add_defaults(self) -> None:
+        """Add all the default files to self.filelist:
+          - README or README.txt
+          - setup.py
+          - tests/test*.py and test/test*.py
+          - all pure Python modules mentioned in setup script
+          - all files pointed by package_data (build_py)
+          - all files defined in data_files.
+          - all files defined as scripts.
+          - all C sources listed as part of extensions or C libraries
+            in the setup script (doesn't catch C headers!)
+        Warns if (README or README.txt) or setup.py are missing; everything
+        else is optional.
+        """
+        self._add_defaults_standards()
+        self._add_defaults_optional()
+        self._add_defaults_python()
+        self._add_defaults_data_files()
+        self._add_defaults_ext()
+        self._add_defaults_c_libs()
+        self._add_defaults_scripts()
+
+    @staticmethod
+    def _cs_path_exists(fspath):
+        """
+        Case-sensitive path existence check
+
+        >>> sdist._cs_path_exists(__file__)
+        True
+        >>> sdist._cs_path_exists(__file__.upper())
+        False
+        """
+        if not os.path.exists(fspath):
+            return False
+        # make absolute so we always have a directory
+        abspath = os.path.abspath(fspath)
+        directory, filename = os.path.split(abspath)
+        return filename in os.listdir(directory)
+
+    def _add_defaults_standards(self):
+        standards = [self.READMES, self.distribution.script_name]
+        for fn in standards:
+            if isinstance(fn, tuple):
+                alts = fn
+                got_it = False
+                for fn in alts:
+                    if self._cs_path_exists(fn):
+                        got_it = True
+                        self.filelist.append(fn)
+                        break
+
+                if not got_it:
+                    self.warn(
+                        "standard file not found: should have one of " + ', '.join(alts)
+                    )
+            else:
+                if self._cs_path_exists(fn):
+                    self.filelist.append(fn)
+                else:
+                    self.warn(f"standard file '{fn}' not found")
+
+    def _add_defaults_optional(self):
+        optional = ['tests/test*.py', 'test/test*.py', 'setup.cfg']
+        for pattern in optional:
+            files = filter(os.path.isfile, glob(pattern))
+            self.filelist.extend(files)
+
+    def _add_defaults_python(self):
+        # build_py is used to get:
+        #  - python modules
+        #  - files defined in package_data
+        build_py = self.get_finalized_command('build_py')
+
+        # getting python files
+        if self.distribution.has_pure_modules():
+            self.filelist.extend(build_py.get_source_files())
+
+        # getting package_data files
+        # (computed in build_py.data_files by build_py.finalize_options)
+        for _pkg, src_dir, _build_dir, filenames in build_py.data_files:
+            for filename in filenames:
+                self.filelist.append(os.path.join(src_dir, filename))
+
+    def _add_defaults_data_files(self):
+        # getting distribution.data_files
+        if self.distribution.has_data_files():
+            for item in self.distribution.data_files:
+                if isinstance(item, str):
+                    # plain file
+                    item = convert_path(item)
+                    if os.path.isfile(item):
+                        self.filelist.append(item)
+                else:
+                    # a (dirname, filenames) tuple
+                    dirname, filenames = item
+                    for f in filenames:
+                        f = convert_path(f)
+                        if os.path.isfile(f):
+                            self.filelist.append(f)
+
+    def _add_defaults_ext(self):
+        if self.distribution.has_ext_modules():
+            build_ext = self.get_finalized_command('build_ext')
+            self.filelist.extend(build_ext.get_source_files())
+
+    def _add_defaults_c_libs(self):
+        if self.distribution.has_c_libraries():
+            build_clib = self.get_finalized_command('build_clib')
+            self.filelist.extend(build_clib.get_source_files())
+
+    def _add_defaults_scripts(self):
+        if self.distribution.has_scripts():
+            build_scripts = self.get_finalized_command('build_scripts')
+            self.filelist.extend(build_scripts.get_source_files())
+
+    def read_template(self) -> None:
+        """Read and parse manifest template file named by self.template.
+
+        (usually "MANIFEST.in") The parsing and processing is done by
+        'self.filelist', which updates itself accordingly.
+        """
+        log.info("reading manifest template '%s'", self.template)
+        template = TextFile(
+            self.template,
+            strip_comments=True,
+            skip_blanks=True,
+            join_lines=True,
+            lstrip_ws=True,
+            rstrip_ws=True,
+            collapse_join=True,
+        )
+
+        try:
+            while True:
+                line = template.readline()
+                if line is None:  # end of file
+                    break
+
+                try:
+                    self.filelist.process_template_line(line)
+                # the call above can raise a DistutilsTemplateError for
+                # malformed lines, or a ValueError from the lower-level
+                # convert_path function
+                except (DistutilsTemplateError, ValueError) as msg:
+                    self.warn(
+                        f"{template.filename}, line {int(template.current_line)}: {msg}"
+                    )
+        finally:
+            template.close()
+
+    def prune_file_list(self) -> None:
+        """Prune off branches that might slip into the file list as created
+        by 'read_template()', but really don't belong there:
+          * the build tree (typically "build")
+          * the release tree itself (only an issue if we ran "sdist"
+            previously with --keep-temp, or it aborted)
+          * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories
+        """
+        build = self.get_finalized_command('build')
+        base_dir = self.distribution.get_fullname()
+
+        self.filelist.exclude_pattern(None, prefix=os.fspath(build.build_base))
+        self.filelist.exclude_pattern(None, prefix=base_dir)
+
+        if sys.platform == 'win32':
+            seps = r'/|\\'
+        else:
+            seps = '/'
+
+        vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', '_darcs']
+        vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps)
+        self.filelist.exclude_pattern(vcs_ptrn, is_regex=True)
+
+    def write_manifest(self) -> None:
+        """Write the file list in 'self.filelist' (presumably as filled in
+        by 'add_defaults()' and 'read_template()') to the manifest file
+        named by 'self.manifest'.
+        """
+        if self._manifest_is_not_generated():
+            log.info(
+                f"not writing to manually maintained manifest file '{self.manifest}'"
+            )
+            return
+
+        content = self.filelist.files[:]
+        content.insert(0, '# file GENERATED by distutils, do NOT edit')
+        self.execute(
+            file_util.write_file,
+            (self.manifest, content),
+            f"writing manifest file '{self.manifest}'",
+        )
+
+    def _manifest_is_not_generated(self):
+        # check for special comment used in 3.1.3 and higher
+        if not os.path.isfile(self.manifest):
+            return False
+
+        with open(self.manifest, encoding='utf-8') as fp:
+            first_line = next(fp)
+        return first_line != '# file GENERATED by distutils, do NOT edit\n'
+
+    def read_manifest(self) -> None:
+        """Read the manifest file (named by 'self.manifest') and use it to
+        fill in 'self.filelist', the list of files to include in the source
+        distribution.
+        """
+        log.info("reading manifest file '%s'", self.manifest)
+        with open(self.manifest, encoding='utf-8') as lines:
+            self.filelist.extend(
+                # ignore comments and blank lines
+                filter(None, filterfalse(is_comment, map(str.strip, lines)))
+            )
+
+    def make_release_tree(self, base_dir, files) -> None:
+        """Create the directory tree that will become the source
+        distribution archive.  All directories implied by the filenames in
+        'files' are created under 'base_dir', and then we hard link or copy
+        (if hard linking is unavailable) those files into place.
+        Essentially, this duplicates the developer's source tree, but in a
+        directory named after the distribution, containing only the files
+        to be distributed.
+        """
+        # Create all the directories under 'base_dir' necessary to
+        # put 'files' there; the 'mkpath()' is just so we don't die
+        # if the manifest happens to be empty.
+        self.mkpath(base_dir)
+        dir_util.create_tree(base_dir, files)
+
+        # And walk over the list of files, either making a hard link (if
+        # os.link exists) to each one that doesn't already exist in its
+        # corresponding location under 'base_dir', or copying each file
+        # that's out-of-date in 'base_dir'.  (Usually, all files will be
+        # out-of-date, because by default we blow away 'base_dir' when
+        # we're done making the distribution archives.)
+
+        if hasattr(os, 'link'):  # can make hard links on this system
+            link = 'hard'
+            msg = f"making hard links in {base_dir}..."
+        else:  # nope, have to copy
+            link = None
+            msg = f"copying files to {base_dir}..."
+
+        if not files:
+            log.warning("no files to distribute -- empty manifest?")
+        else:
+            log.info(msg)
+        for file in files:
+            if not os.path.isfile(file):
+                log.warning("'%s' not a regular file -- skipping", file)
+            else:
+                dest = os.path.join(base_dir, file)
+                self.copy_file(file, dest, link=link)
+
+        self.distribution.metadata.write_pkg_info(base_dir)
+
+    def make_distribution(self) -> None:
+        """Create the source distribution(s).  First, we create the release
+        tree with 'make_release_tree()'; then, we create all required
+        archive files (according to 'self.formats') from the release tree.
+        Finally, we clean up by blowing away the release tree (unless
+        'self.keep_temp' is true).  The list of archive files created is
+        stored so it can be retrieved later by 'get_archive_files()'.
+        """
+        # Don't warn about missing meta-data here -- should be (and is!)
+        # done elsewhere.
+        base_dir = self.distribution.get_fullname()
+        base_name = os.path.join(self.dist_dir, base_dir)
+
+        self.make_release_tree(base_dir, self.filelist.files)
+        archive_files = []  # remember names of files we create
+        # tar archive must be created last to avoid overwrite and remove
+        if 'tar' in self.formats:
+            self.formats.append(self.formats.pop(self.formats.index('tar')))
+
+        for fmt in self.formats:
+            file = self.make_archive(
+                base_name, fmt, base_dir=base_dir, owner=self.owner, group=self.group
+            )
+            archive_files.append(file)
+            self.distribution.dist_files.append(('sdist', '', file))
+
+        self.archive_files = archive_files
+
+        if not self.keep_temp:
+            dir_util.remove_tree(base_dir)
+
+    def get_archive_files(self):
+        """Return the list of archive files created when the command
+        was run, or None if the command hasn't run yet.
+        """
+        return self.archive_files
+
+
+def is_comment(line: str) -> bool:
+    return line.startswith('#')
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/__init__.py
new file mode 100644
index 0000000..2c43729
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/__init__.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from typing import TypeVar
+
+_IterableT = TypeVar("_IterableT", bound="Iterable[str]")
+
+
+def consolidate_linker_args(args: _IterableT) -> _IterableT | str:
+    """
+    Ensure the return value is a string for backward compatibility.
+
+    Retain until at least 2025-04-31. See pypa/distutils#246
+    """
+
+    if not all(arg.startswith('-Wl,') for arg in args):
+        return args
+    return '-Wl,' + ','.join(arg.removeprefix('-Wl,') for arg in args)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/numpy.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/numpy.py
new file mode 100644
index 0000000..73eca7a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/numpy.py
@@ -0,0 +1,2 @@
+# required for older numpy versions on Pythons prior to 3.12; see pypa/setuptools#4876
+from ..compilers.C.base import _default_compilers, compiler_class  # noqa: F401
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/py39.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/py39.py
new file mode 100644
index 0000000..1b436d7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compat/py39.py
@@ -0,0 +1,66 @@
+import functools
+import itertools
+import platform
+import sys
+
+
+def add_ext_suffix_39(vars):
+    """
+    Ensure vars contains 'EXT_SUFFIX'. pypa/distutils#130
+    """
+    import _imp
+
+    ext_suffix = _imp.extension_suffixes()[0]
+    vars.update(
+        EXT_SUFFIX=ext_suffix,
+        # sysconfig sets SO to match EXT_SUFFIX, so maintain
+        # that expectation.
+        # https://github.com/python/cpython/blob/785cc6770588de087d09e89a69110af2542be208/Lib/sysconfig.py#L671-L673
+        SO=ext_suffix,
+    )
+
+
+needs_ext_suffix = sys.version_info < (3, 10) and platform.system() == 'Windows'
+add_ext_suffix = add_ext_suffix_39 if needs_ext_suffix else lambda vars: None
+
+
+# from more_itertools
+class UnequalIterablesError(ValueError):
+    def __init__(self, details=None):
+        msg = 'Iterables have different lengths'
+        if details is not None:
+            msg += (': index 0 has length {}; index {} has length {}').format(*details)
+
+        super().__init__(msg)
+
+
+# from more_itertools
+def _zip_equal_generator(iterables):
+    _marker = object()
+    for combo in itertools.zip_longest(*iterables, fillvalue=_marker):
+        for val in combo:
+            if val is _marker:
+                raise UnequalIterablesError()
+        yield combo
+
+
+# from more_itertools
+def _zip_equal(*iterables):
+    # Check whether the iterables are all the same size.
+    try:
+        first_size = len(iterables[0])
+        for i, it in enumerate(iterables[1:], 1):
+            size = len(it)
+            if size != first_size:
+                raise UnequalIterablesError(details=(first_size, i, size))
+        # All sizes are equal, we can use the built-in zip.
+        return zip(*iterables)
+    # If any one of the iterables didn't have a length, start reading
+    # them until one runs out.
+    except TypeError:
+        return _zip_equal_generator(iterables)
+
+
+zip_strict = (
+    _zip_equal if sys.version_info < (3, 10) else functools.partial(zip, strict=True)
+)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py
new file mode 100644
index 0000000..ea6b545
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py
@@ -0,0 +1,1386 @@
+"""distutils.ccompiler
+
+Contains Compiler, an abstract base class that defines the interface
+for the Distutils compiler abstraction model."""
+
+from __future__ import annotations
+
+import os
+import pathlib
+import re
+import sys
+import warnings
+from collections.abc import Callable, Iterable, MutableSequence, Sequence
+from typing import (
+    TYPE_CHECKING,
+    ClassVar,
+    Literal,
+    TypeVar,
+    Union,
+    overload,
+)
+
+from more_itertools import always_iterable
+
+from ..._log import log
+from ..._modified import newer_group
+from ...dir_util import mkpath
+from ...errors import (
+    DistutilsModuleError,
+    DistutilsPlatformError,
+)
+from ...file_util import move_file
+from ...spawn import spawn
+from ...util import execute, is_mingw, split_quoted
+from .errors import (
+    CompileError,
+    LinkError,
+    UnknownFileType,
+)
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias, TypeVarTuple, Unpack
+
+    _Ts = TypeVarTuple("_Ts")
+
+_Macro: TypeAlias = Union[tuple[str], tuple[str, Union[str, None]]]
+_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
+_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
+
+
+class Compiler:
+    """Abstract base class to define the interface that must be implemented
+    by real compiler classes.  Also has some utility methods used by
+    several compiler classes.
+
+    The basic idea behind a compiler abstraction class is that each
+    instance can be used for all the compile/link steps in building a
+    single project.  Thus, attributes common to all of those compile and
+    link steps -- include directories, macros to define, libraries to link
+    against, etc. -- are attributes of the compiler instance.  To allow for
+    variability in how individual files are treated, most of those
+    attributes may be varied on a per-compilation or per-link basis.
+    """
+
+    # 'compiler_type' is a class attribute that identifies this class.  It
+    # keeps code that wants to know what kind of compiler it's dealing with
+    # from having to import all possible compiler classes just to do an
+    # 'isinstance'.  In concrete CCompiler subclasses, 'compiler_type'
+    # should really, really be one of the keys of the 'compiler_class'
+    # dictionary (see below -- used by the 'new_compiler()' factory
+    # function) -- authors of new compiler interface classes are
+    # responsible for updating 'compiler_class'!
+    compiler_type: ClassVar[str] = None  # type: ignore[assignment]
+
+    # XXX things not handled by this compiler abstraction model:
+    #   * client can't provide additional options for a compiler,
+    #     e.g. warning, optimization, debugging flags.  Perhaps this
+    #     should be the domain of concrete compiler abstraction classes
+    #     (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
+    #     class should have methods for the common ones.
+    #   * can't completely override the include or library searchg
+    #     path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
+    #     I'm not sure how widely supported this is even by Unix
+    #     compilers, much less on other platforms.  And I'm even less
+    #     sure how useful it is; maybe for cross-compiling, but
+    #     support for that is a ways off.  (And anyways, cross
+    #     compilers probably have a dedicated binary with the
+    #     right paths compiled in.  I hope.)
+    #   * can't do really freaky things with the library list/library
+    #     dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
+    #     different versions of libfoo.a in different locations.  I
+    #     think this is useless without the ability to null out the
+    #     library search path anyways.
+
+    executables: ClassVar[dict]
+
+    # Subclasses that rely on the standard filename generation methods
+    # implemented below should override these; see the comment near
+    # those methods ('object_filenames()' et. al.) for details:
+    src_extensions: ClassVar[list[str] | None] = None
+    obj_extension: ClassVar[str | None] = None
+    static_lib_extension: ClassVar[str | None] = None
+    shared_lib_extension: ClassVar[str | None] = None
+    static_lib_format: ClassVar[str | None] = None  # format string
+    shared_lib_format: ClassVar[str | None] = None  # prob. same as static_lib_format
+    exe_extension: ClassVar[str | None] = None
+
+    # Default language settings. language_map is used to detect a source
+    # file or Extension target language, checking source filenames.
+    # language_order is used to detect the language precedence, when deciding
+    # what language to use when mixing source types. For example, if some
+    # extension has two files with ".c" extension, and one with ".cpp", it
+    # is still linked as c++.
+    language_map: ClassVar[dict[str, str]] = {
+        ".c": "c",
+        ".cc": "c++",
+        ".cpp": "c++",
+        ".cxx": "c++",
+        ".m": "objc",
+    }
+    language_order: ClassVar[list[str]] = ["c++", "objc", "c"]
+
+    include_dirs: list[str] = []
+    """
+    include dirs specific to this compiler class
+    """
+
+    library_dirs: list[str] = []
+    """
+    library dirs specific to this compiler class
+    """
+
+    def __init__(self, verbose: bool = False, force: bool = False) -> None:
+        self.force = force
+        self.verbose = verbose
+
+        # 'output_dir': a common output directory for object, library,
+        # shared object, and shared library files
+        self.output_dir: str | None = None
+
+        # 'macros': a list of macro definitions (or undefinitions).  A
+        # macro definition is a 2-tuple (name, value), where the value is
+        # either a string or None (no explicit value).  A macro
+        # undefinition is a 1-tuple (name,).
+        self.macros: list[_Macro] = []
+
+        # 'include_dirs': a list of directories to search for include files
+        self.include_dirs = []
+
+        # 'libraries': a list of libraries to include in any link
+        # (library names, not filenames: eg. "foo" not "libfoo.a")
+        self.libraries: list[str] = []
+
+        # 'library_dirs': a list of directories to search for libraries
+        self.library_dirs = []
+
+        # 'runtime_library_dirs': a list of directories to search for
+        # shared libraries/objects at runtime
+        self.runtime_library_dirs: list[str] = []
+
+        # 'objects': a list of object files (or similar, such as explicitly
+        # named library files) to include on any link
+        self.objects: list[str] = []
+
+        for key in self.executables.keys():
+            self.set_executable(key, self.executables[key])
+
+    def set_executables(self, **kwargs: str) -> None:
+        """Define the executables (and options for them) that will be run
+        to perform the various stages of compilation.  The exact set of
+        executables that may be specified here depends on the compiler
+        class (via the 'executables' class attribute), but most will have:
+          compiler      the C/C++ compiler
+          linker_so     linker used to create shared objects and libraries
+          linker_exe    linker used to create binary executables
+          archiver      static library creator
+
+        On platforms with a command-line (Unix, DOS/Windows), each of these
+        is a string that will be split into executable name and (optional)
+        list of arguments.  (Splitting the string is done similarly to how
+        Unix shells operate: words are delimited by spaces, but quotes and
+        backslashes can override this.  See
+        'distutils.util.split_quoted()'.)
+        """
+
+        # Note that some CCompiler implementation classes will define class
+        # attributes 'cpp', 'cc', etc. with hard-coded executable names;
+        # this is appropriate when a compiler class is for exactly one
+        # compiler/OS combination (eg. MSVCCompiler).  Other compiler
+        # classes (UnixCCompiler, in particular) are driven by information
+        # discovered at run-time, since there are many different ways to do
+        # basically the same things with Unix C compilers.
+
+        for key in kwargs:
+            if key not in self.executables:
+                raise ValueError(
+                    f"unknown executable '{key}' for class {self.__class__.__name__}"
+                )
+            self.set_executable(key, kwargs[key])
+
+    def set_executable(self, key, value):
+        if isinstance(value, str):
+            setattr(self, key, split_quoted(value))
+        else:
+            setattr(self, key, value)
+
+    def _find_macro(self, name):
+        i = 0
+        for defn in self.macros:
+            if defn[0] == name:
+                return i
+            i += 1
+        return None
+
+    def _check_macro_definitions(self, definitions):
+        """Ensure that every element of 'definitions' is valid."""
+        for defn in definitions:
+            self._check_macro_definition(*defn)
+
+    def _check_macro_definition(self, defn):
+        """
+        Raise a TypeError if defn is not valid.
+
+        A valid definition is either a (name, value) 2-tuple or a (name,) tuple.
+        """
+        if not isinstance(defn, tuple) or not self._is_valid_macro(*defn):
+            raise TypeError(
+                f"invalid macro definition '{defn}': "
+                "must be tuple (string,), (string, string), or (string, None)"
+            )
+
+    @staticmethod
+    def _is_valid_macro(name, value=None):
+        """
+        A valid macro is a ``name : str`` and a ``value : str | None``.
+
+        >>> Compiler._is_valid_macro('foo', None)
+        True
+        """
+        return isinstance(name, str) and isinstance(value, (str, type(None)))
+
+    # -- Bookkeeping methods -------------------------------------------
+
+    def define_macro(self, name: str, value: str | None = None) -> None:
+        """Define a preprocessor macro for all compilations driven by this
+        compiler object.  The optional parameter 'value' should be a
+        string; if it is not supplied, then the macro will be defined
+        without an explicit value and the exact outcome depends on the
+        compiler used (XXX true? does ANSI say anything about this?)
+        """
+        # Delete from the list of macro definitions/undefinitions if
+        # already there (so that this one will take precedence).
+        i = self._find_macro(name)
+        if i is not None:
+            del self.macros[i]
+
+        self.macros.append((name, value))
+
+    def undefine_macro(self, name: str) -> None:
+        """Undefine a preprocessor macro for all compilations driven by
+        this compiler object.  If the same macro is defined by
+        'define_macro()' and undefined by 'undefine_macro()' the last call
+        takes precedence (including multiple redefinitions or
+        undefinitions).  If the macro is redefined/undefined on a
+        per-compilation basis (ie. in the call to 'compile()'), then that
+        takes precedence.
+        """
+        # Delete from the list of macro definitions/undefinitions if
+        # already there (so that this one will take precedence).
+        i = self._find_macro(name)
+        if i is not None:
+            del self.macros[i]
+
+        undefn = (name,)
+        self.macros.append(undefn)
+
+    def add_include_dir(self, dir: str) -> None:
+        """Add 'dir' to the list of directories that will be searched for
+        header files.  The compiler is instructed to search directories in
+        the order in which they are supplied by successive calls to
+        'add_include_dir()'.
+        """
+        self.include_dirs.append(dir)
+
+    def set_include_dirs(self, dirs: list[str]) -> None:
+        """Set the list of directories that will be searched to 'dirs' (a
+        list of strings).  Overrides any preceding calls to
+        'add_include_dir()'; subsequence calls to 'add_include_dir()' add
+        to the list passed to 'set_include_dirs()'.  This does not affect
+        any list of standard include directories that the compiler may
+        search by default.
+        """
+        self.include_dirs = dirs[:]
+
+    def add_library(self, libname: str) -> None:
+        """Add 'libname' to the list of libraries that will be included in
+        all links driven by this compiler object.  Note that 'libname'
+        should *not* be the name of a file containing a library, but the
+        name of the library itself: the actual filename will be inferred by
+        the linker, the compiler, or the compiler class (depending on the
+        platform).
+
+        The linker will be instructed to link against libraries in the
+        order they were supplied to 'add_library()' and/or
+        'set_libraries()'.  It is perfectly valid to duplicate library
+        names; the linker will be instructed to link against libraries as
+        many times as they are mentioned.
+        """
+        self.libraries.append(libname)
+
+    def set_libraries(self, libnames: list[str]) -> None:
+        """Set the list of libraries to be included in all links driven by
+        this compiler object to 'libnames' (a list of strings).  This does
+        not affect any standard system libraries that the linker may
+        include by default.
+        """
+        self.libraries = libnames[:]
+
+    def add_library_dir(self, dir: str) -> None:
+        """Add 'dir' to the list of directories that will be searched for
+        libraries specified to 'add_library()' and 'set_libraries()'.  The
+        linker will be instructed to search for libraries in the order they
+        are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
+        """
+        self.library_dirs.append(dir)
+
+    def set_library_dirs(self, dirs: list[str]) -> None:
+        """Set the list of library search directories to 'dirs' (a list of
+        strings).  This does not affect any standard library search path
+        that the linker may search by default.
+        """
+        self.library_dirs = dirs[:]
+
+    def add_runtime_library_dir(self, dir: str) -> None:
+        """Add 'dir' to the list of directories that will be searched for
+        shared libraries at runtime.
+        """
+        self.runtime_library_dirs.append(dir)
+
+    def set_runtime_library_dirs(self, dirs: list[str]) -> None:
+        """Set the list of directories to search for shared libraries at
+        runtime to 'dirs' (a list of strings).  This does not affect any
+        standard search path that the runtime linker may search by
+        default.
+        """
+        self.runtime_library_dirs = dirs[:]
+
+    def add_link_object(self, object: str) -> None:
+        """Add 'object' to the list of object files (or analogues, such as
+        explicitly named library files or the output of "resource
+        compilers") to be included in every link driven by this compiler
+        object.
+        """
+        self.objects.append(object)
+
+    def set_link_objects(self, objects: list[str]) -> None:
+        """Set the list of object files (or analogues) to be included in
+        every link to 'objects'.  This does not affect any standard object
+        files that the linker may include by default (such as system
+        libraries).
+        """
+        self.objects = objects[:]
+
+    # -- Private utility methods --------------------------------------
+    # (here for the convenience of subclasses)
+
+    # Helper method to prep compiler in subclass compile() methods
+
+    def _setup_compile(
+        self,
+        outdir: str | None,
+        macros: list[_Macro] | None,
+        incdirs: list[str] | tuple[str, ...] | None,
+        sources,
+        depends,
+        extra,
+    ):
+        """Process arguments and decide which source files to compile."""
+        outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
+
+        if extra is None:
+            extra = []
+
+        # Get the list of expected output (object) files
+        objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)
+        assert len(objects) == len(sources)
+
+        pp_opts = gen_preprocess_options(macros, incdirs)
+
+        build = {}
+        for i in range(len(sources)):
+            src = sources[i]
+            obj = objects[i]
+            ext = os.path.splitext(src)[1]
+            self.mkpath(os.path.dirname(obj))
+            build[obj] = (src, ext)
+
+        return macros, objects, extra, pp_opts, build
+
+    def _get_cc_args(self, pp_opts, debug, before):
+        # works for unixccompiler, cygwinccompiler
+        cc_args = pp_opts + ['-c']
+        if debug:
+            cc_args[:0] = ['-g']
+        if before:
+            cc_args[:0] = before
+        return cc_args
+
+    def _fix_compile_args(
+        self,
+        output_dir: str | None,
+        macros: list[_Macro] | None,
+        include_dirs: list[str] | tuple[str, ...] | None,
+    ) -> tuple[str, list[_Macro], list[str]]:
+        """Typecheck and fix-up some of the arguments to the 'compile()'
+        method, and return fixed-up values.  Specifically: if 'output_dir'
+        is None, replaces it with 'self.output_dir'; ensures that 'macros'
+        is a list, and augments it with 'self.macros'; ensures that
+        'include_dirs' is a list, and augments it with 'self.include_dirs'.
+        Guarantees that the returned values are of the correct type,
+        i.e. for 'output_dir' either string or None, and for 'macros' and
+        'include_dirs' either list or None.
+        """
+        if output_dir is None:
+            output_dir = self.output_dir
+        elif not isinstance(output_dir, str):
+            raise TypeError("'output_dir' must be a string or None")
+
+        if macros is None:
+            macros = list(self.macros)
+        elif isinstance(macros, list):
+            macros = macros + (self.macros or [])
+        else:
+            raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+        if include_dirs is None:
+            include_dirs = list(self.include_dirs)
+        elif isinstance(include_dirs, (list, tuple)):
+            include_dirs = list(include_dirs) + (self.include_dirs or [])
+        else:
+            raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+
+        # add include dirs for class
+        include_dirs += self.__class__.include_dirs
+
+        return output_dir, macros, include_dirs
+
+    def _prep_compile(self, sources, output_dir, depends=None):
+        """Decide which source files must be recompiled.
+
+        Determine the list of object files corresponding to 'sources',
+        and figure out which ones really need to be recompiled.
+        Return a list of all object files and a dictionary telling
+        which source files can be skipped.
+        """
+        # Get the list of expected output (object) files
+        objects = self.object_filenames(sources, output_dir=output_dir)
+        assert len(objects) == len(sources)
+
+        # Return an empty dict for the "which source files can be skipped"
+        # return value to preserve API compatibility.
+        return objects, {}
+
+    def _fix_object_args(
+        self, objects: list[str] | tuple[str, ...], output_dir: str | None
+    ) -> tuple[list[str], str]:
+        """Typecheck and fix up some arguments supplied to various methods.
+        Specifically: ensure that 'objects' is a list; if output_dir is
+        None, replace with self.output_dir.  Return fixed versions of
+        'objects' and 'output_dir'.
+        """
+        if not isinstance(objects, (list, tuple)):
+            raise TypeError("'objects' must be a list or tuple of strings")
+        objects = list(objects)
+
+        if output_dir is None:
+            output_dir = self.output_dir
+        elif not isinstance(output_dir, str):
+            raise TypeError("'output_dir' must be a string or None")
+
+        return (objects, output_dir)
+
+    def _fix_lib_args(
+        self,
+        libraries: list[str] | tuple[str, ...] | None,
+        library_dirs: list[str] | tuple[str, ...] | None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None,
+    ) -> tuple[list[str], list[str], list[str]]:
+        """Typecheck and fix up some of the arguments supplied to the
+        'link_*' methods.  Specifically: ensure that all arguments are
+        lists, and augment them with their permanent versions
+        (eg. 'self.libraries' augments 'libraries').  Return a tuple with
+        fixed versions of all arguments.
+        """
+        if libraries is None:
+            libraries = list(self.libraries)
+        elif isinstance(libraries, (list, tuple)):
+            libraries = list(libraries) + (self.libraries or [])
+        else:
+            raise TypeError("'libraries' (if supplied) must be a list of strings")
+
+        if library_dirs is None:
+            library_dirs = list(self.library_dirs)
+        elif isinstance(library_dirs, (list, tuple)):
+            library_dirs = list(library_dirs) + (self.library_dirs or [])
+        else:
+            raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+
+        # add library dirs for class
+        library_dirs += self.__class__.library_dirs
+
+        if runtime_library_dirs is None:
+            runtime_library_dirs = list(self.runtime_library_dirs)
+        elif isinstance(runtime_library_dirs, (list, tuple)):
+            runtime_library_dirs = list(runtime_library_dirs) + (
+                self.runtime_library_dirs or []
+            )
+        else:
+            raise TypeError(
+                "'runtime_library_dirs' (if supplied) must be a list of strings"
+            )
+
+        return (libraries, library_dirs, runtime_library_dirs)
+
+    def _need_link(self, objects, output_file):
+        """Return true if we need to relink the files listed in 'objects'
+        to recreate 'output_file'.
+        """
+        if self.force:
+            return True
+        newer = newer_group(objects, output_file)
+        return newer
+
+    def detect_language(self, sources: str | list[str]) -> str | None:
+        """Detect the language of a given file, or list of files. Uses
+        language_map, and language_order to do the job.
+        """
+        if not isinstance(sources, list):
+            sources = [sources]
+        lang = None
+        index = len(self.language_order)
+        for source in sources:
+            base, ext = os.path.splitext(source)
+            extlang = self.language_map.get(ext)
+            try:
+                extindex = self.language_order.index(extlang)
+                if extindex < index:
+                    lang = extlang
+                    index = extindex
+            except ValueError:
+                pass
+        return lang
+
+    # -- Worker methods ------------------------------------------------
+    # (must be implemented by subclasses)
+
+    def preprocess(
+        self,
+        source: str | os.PathLike[str],
+        output_file: str | os.PathLike[str] | None = None,
+        macros: list[_Macro] | None = None,
+        include_dirs: list[str] | tuple[str, ...] | None = None,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: Iterable[str] | None = None,
+    ):
+        """Preprocess a single C/C++ source file, named in 'source'.
+        Output will be written to file named 'output_file', or stdout if
+        'output_file' not supplied.  'macros' is a list of macro
+        definitions as for 'compile()', which will augment the macros set
+        with 'define_macro()' and 'undefine_macro()'.  'include_dirs' is a
+        list of directory names that will be added to the default list.
+
+        Raises PreprocessError on failure.
+        """
+        pass
+
+    def compile(
+        self,
+        sources: Sequence[str | os.PathLike[str]],
+        output_dir: str | None = None,
+        macros: list[_Macro] | None = None,
+        include_dirs: list[str] | tuple[str, ...] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: list[str] | None = None,
+        depends: list[str] | tuple[str, ...] | None = None,
+    ) -> list[str]:
+        """Compile one or more source files.
+
+        'sources' must be a list of filenames, most likely C/C++
+        files, but in reality anything that can be handled by a
+        particular compiler and compiler class (eg. MSVCCompiler can
+        handle resource files in 'sources').  Return a list of object
+        filenames, one per source filename in 'sources'.  Depending on
+        the implementation, not all source files will necessarily be
+        compiled, but all corresponding object filenames will be
+        returned.
+
+        If 'output_dir' is given, object files will be put under it, while
+        retaining their original path component.  That is, "foo/bar.c"
+        normally compiles to "foo/bar.o" (for a Unix implementation); if
+        'output_dir' is "build", then it would compile to
+        "build/foo/bar.o".
+
+        'macros', if given, must be a list of macro definitions.  A macro
+        definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
+        The former defines a macro; if the value is None, the macro is
+        defined without an explicit value.  The 1-tuple case undefines a
+        macro.  Later definitions/redefinitions/ undefinitions take
+        precedence.
+
+        'include_dirs', if given, must be a list of strings, the
+        directories to add to the default include file search path for this
+        compilation only.
+
+        'debug' is a boolean; if true, the compiler will be instructed to
+        output debug symbols in (or alongside) the object file(s).
+
+        'extra_preargs' and 'extra_postargs' are implementation- dependent.
+        On platforms that have the notion of a command-line (e.g. Unix,
+        DOS/Windows), they are most likely lists of strings: extra
+        command-line arguments to prepend/append to the compiler command
+        line.  On other platforms, consult the implementation class
+        documentation.  In any event, they are intended as an escape hatch
+        for those occasions when the abstract compiler framework doesn't
+        cut the mustard.
+
+        'depends', if given, is a list of filenames that all targets
+        depend on.  If a source file is older than any file in
+        depends, then the source file will be recompiled.  This
+        supports dependency tracking, but only at a coarse
+        granularity.
+
+        Raises CompileError on failure.
+        """
+        # A concrete compiler class can either override this method
+        # entirely or implement _compile().
+        macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+            output_dir, macros, include_dirs, sources, depends, extra_postargs
+        )
+        cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+        for obj in objects:
+            try:
+                src, ext = build[obj]
+            except KeyError:
+                continue
+            self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+        # Return *all* object filenames, not just the ones we just built.
+        return objects
+
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        """Compile 'src' to product 'obj'."""
+        # A concrete compiler class that does not override compile()
+        # should implement _compile().
+        pass
+
+    def create_static_lib(
+        self,
+        objects: list[str] | tuple[str, ...],
+        output_libname: str,
+        output_dir: str | None = None,
+        debug: bool = False,
+        target_lang: str | None = None,
+    ) -> None:
+        """Link a bunch of stuff together to create a static library file.
+        The "bunch of stuff" consists of the list of object files supplied
+        as 'objects', the extra object files supplied to
+        'add_link_object()' and/or 'set_link_objects()', the libraries
+        supplied to 'add_library()' and/or 'set_libraries()', and the
+        libraries supplied as 'libraries' (if any).
+
+        'output_libname' should be a library name, not a filename; the
+        filename will be inferred from the library name.  'output_dir' is
+        the directory where the library file will be put.
+
+        'debug' is a boolean; if true, debugging information will be
+        included in the library (note that on most platforms, it is the
+        compile step where this matters: the 'debug' flag is included here
+        just for consistency).
+
+        'target_lang' is the target language for which the given objects
+        are being compiled. This allows specific linkage time treatment of
+        certain languages.
+
+        Raises LibError on failure.
+        """
+        pass
+
+    # values for target_desc parameter in link()
+    SHARED_OBJECT = "shared_object"
+    SHARED_LIBRARY = "shared_library"
+    EXECUTABLE = "executable"
+
+    def link(
+        self,
+        target_desc: str,
+        objects: list[str] | tuple[str, ...],
+        output_filename: str,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        export_symbols: Iterable[str] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: list[str] | None = None,
+        build_temp: str | os.PathLike[str] | None = None,
+        target_lang: str | None = None,
+    ):
+        """Link a bunch of stuff together to create an executable or
+        shared library file.
+
+        The "bunch of stuff" consists of the list of object files supplied
+        as 'objects'.  'output_filename' should be a filename.  If
+        'output_dir' is supplied, 'output_filename' is relative to it
+        (i.e. 'output_filename' can provide directory components if
+        needed).
+
+        'libraries' is a list of libraries to link against.  These are
+        library names, not filenames, since they're translated into
+        filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
+        on Unix and "foo.lib" on DOS/Windows).  However, they can include a
+        directory component, which means the linker will look in that
+        specific directory rather than searching all the normal locations.
+
+        'library_dirs', if supplied, should be a list of directories to
+        search for libraries that were specified as bare library names
+        (ie. no directory component).  These are on top of the system
+        default and those supplied to 'add_library_dir()' and/or
+        'set_library_dirs()'.  'runtime_library_dirs' is a list of
+        directories that will be embedded into the shared library and used
+        to search for other shared libraries that *it* depends on at
+        run-time.  (This may only be relevant on Unix.)
+
+        'export_symbols' is a list of symbols that the shared library will
+        export.  (This appears to be relevant only on Windows.)
+
+        'debug' is as for 'compile()' and 'create_static_lib()', with the
+        slight distinction that it actually matters on most platforms (as
+        opposed to 'create_static_lib()', which includes a 'debug' flag
+        mostly for form's sake).
+
+        'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
+        of course that they supply command-line arguments for the
+        particular linker being used).
+
+        'target_lang' is the target language for which the given objects
+        are being compiled. This allows specific linkage time treatment of
+        certain languages.
+
+        Raises LinkError on failure.
+        """
+        raise NotImplementedError
+
+    # Old 'link_*()' methods, rewritten to use the new 'link()' method.
+
+    def link_shared_lib(
+        self,
+        objects: list[str] | tuple[str, ...],
+        output_libname: str,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        export_symbols: Iterable[str] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: list[str] | None = None,
+        build_temp: str | os.PathLike[str] | None = None,
+        target_lang: str | None = None,
+    ):
+        self.link(
+            Compiler.SHARED_LIBRARY,
+            objects,
+            self.library_filename(output_libname, lib_type='shared'),
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def link_shared_object(
+        self,
+        objects: list[str] | tuple[str, ...],
+        output_filename: str,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        export_symbols: Iterable[str] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: list[str] | None = None,
+        build_temp: str | os.PathLike[str] | None = None,
+        target_lang: str | None = None,
+    ):
+        self.link(
+            Compiler.SHARED_OBJECT,
+            objects,
+            output_filename,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def link_executable(
+        self,
+        objects: list[str] | tuple[str, ...],
+        output_progname: str,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: list[str] | None = None,
+        target_lang: str | None = None,
+    ):
+        self.link(
+            Compiler.EXECUTABLE,
+            objects,
+            self.executable_filename(output_progname),
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            None,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            None,
+            target_lang,
+        )
+
+    # -- Miscellaneous methods -----------------------------------------
+    # These are all used by the 'gen_lib_options() function; there is
+    # no appropriate default implementation so subclasses should
+    # implement all of these.
+
+    def library_dir_option(self, dir: str) -> str:
+        """Return the compiler option to add 'dir' to the list of
+        directories searched for libraries.
+        """
+        raise NotImplementedError
+
+    def runtime_library_dir_option(self, dir: str) -> str:
+        """Return the compiler option to add 'dir' to the list of
+        directories searched for runtime libraries.
+        """
+        raise NotImplementedError
+
+    def library_option(self, lib: str) -> str:
+        """Return the compiler option to add 'lib' to the list of libraries
+        linked into the shared library or executable.
+        """
+        raise NotImplementedError
+
+    def has_function(  # noqa: C901
+        self,
+        funcname: str,
+        includes: Iterable[str] | None = None,
+        include_dirs: list[str] | tuple[str, ...] | None = None,
+        libraries: list[str] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+    ) -> bool:
+        """Return a boolean indicating whether funcname is provided as
+        a symbol on the current platform.  The optional arguments can
+        be used to augment the compilation environment.
+
+        The libraries argument is a list of flags to be passed to the
+        linker to make additional symbol definitions available for
+        linking.
+
+        The includes and include_dirs arguments are deprecated.
+        Usually, supplying include files with function declarations
+        will cause function detection to fail even in cases where the
+        symbol is available for linking.
+
+        """
+        # this can't be included at module scope because it tries to
+        # import math which might not be available at that point - maybe
+        # the necessary logic should just be inlined?
+        import tempfile
+
+        if includes is None:
+            includes = []
+        else:
+            warnings.warn("includes is deprecated", DeprecationWarning)
+        if include_dirs is None:
+            include_dirs = []
+        else:
+            warnings.warn("include_dirs is deprecated", DeprecationWarning)
+        if libraries is None:
+            libraries = []
+        if library_dirs is None:
+            library_dirs = []
+        fd, fname = tempfile.mkstemp(".c", funcname, text=True)
+        with os.fdopen(fd, "w", encoding='utf-8') as f:
+            for incl in includes:
+                f.write(f"""#include "{incl}"\n""")
+            if not includes:
+                # Use "char func(void);" as the prototype to follow
+                # what autoconf does.  This prototype does not match
+                # any well-known function the compiler might recognize
+                # as a builtin, so this ends up as a true link test.
+                # Without a fake prototype, the test would need to
+                # know the exact argument types, and the has_function
+                # interface does not provide that level of information.
+                f.write(
+                    f"""\
+#ifdef __cplusplus
+extern "C"
+#endif
+char {funcname}(void);
+"""
+                )
+            f.write(
+                f"""\
+int main (int argc, char **argv) {{
+    {funcname}();
+    return 0;
+}}
+"""
+            )
+
+        try:
+            objects = self.compile([fname], include_dirs=include_dirs)
+        except CompileError:
+            return False
+        finally:
+            os.remove(fname)
+
+        try:
+            self.link_executable(
+                objects, "a.out", libraries=libraries, library_dirs=library_dirs
+            )
+        except (LinkError, TypeError):
+            return False
+        else:
+            os.remove(
+                self.executable_filename("a.out", output_dir=self.output_dir or '')
+            )
+        finally:
+            for fn in objects:
+                os.remove(fn)
+        return True
+
+    def find_library_file(
+        self, dirs: Iterable[str], lib: str, debug: bool = False
+    ) -> str | None:
+        """Search the specified list of directories for a static or shared
+        library file 'lib' and return the full path to that file.  If
+        'debug' true, look for a debugging version (if that makes sense on
+        the current platform).  Return None if 'lib' wasn't found in any of
+        the specified directories.
+        """
+        raise NotImplementedError
+
+    # -- Filename generation methods -----------------------------------
+
+    # The default implementation of the filename generating methods are
+    # prejudiced towards the Unix/DOS/Windows view of the world:
+    #   * object files are named by replacing the source file extension
+    #     (eg. .c/.cpp -> .o/.obj)
+    #   * library files (shared or static) are named by plugging the
+    #     library name and extension into a format string, eg.
+    #     "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+    #   * executables are named by appending an extension (possibly
+    #     empty) to the program name: eg. progname + ".exe" for
+    #     Windows
+    #
+    # To reduce redundant code, these methods expect to find
+    # several attributes in the current object (presumably defined
+    # as class attributes):
+    #   * src_extensions -
+    #     list of C/C++ source file extensions, eg. ['.c', '.cpp']
+    #   * obj_extension -
+    #     object file extension, eg. '.o' or '.obj'
+    #   * static_lib_extension -
+    #     extension for static library files, eg. '.a' or '.lib'
+    #   * shared_lib_extension -
+    #     extension for shared library/object files, eg. '.so', '.dll'
+    #   * static_lib_format -
+    #     format string for generating static library filenames,
+    #     eg. 'lib%s.%s' or '%s.%s'
+    #   * shared_lib_format
+    #     format string for generating shared library filenames
+    #     (probably same as static_lib_format, since the extension
+    #     is one of the intended parameters to the format string)
+    #   * exe_extension -
+    #     extension for executable files, eg. '' or '.exe'
+
+    def object_filenames(
+        self,
+        source_filenames: Iterable[str | os.PathLike[str]],
+        strip_dir: bool = False,
+        output_dir: str | os.PathLike[str] | None = '',
+    ) -> list[str]:
+        if output_dir is None:
+            output_dir = ''
+        return list(
+            self._make_out_path(output_dir, strip_dir, src_name)
+            for src_name in source_filenames
+        )
+
+    @property
+    def out_extensions(self):
+        return dict.fromkeys(self.src_extensions, self.obj_extension)
+
+    def _make_out_path(self, output_dir, strip_dir, src_name):
+        return self._make_out_path_exts(
+            output_dir, strip_dir, src_name, self.out_extensions
+        )
+
+    @classmethod
+    def _make_out_path_exts(cls, output_dir, strip_dir, src_name, extensions):
+        r"""
+        >>> exts = {'.c': '.o'}
+        >>> Compiler._make_out_path_exts('.', False, '/foo/bar.c', exts).replace('\\', '/')
+        './foo/bar.o'
+        >>> Compiler._make_out_path_exts('.', True, '/foo/bar.c', exts).replace('\\', '/')
+        './bar.o'
+        """
+        src = pathlib.PurePath(src_name)
+        # Ensure base is relative to honor output_dir (python/cpython#37775).
+        base = cls._make_relative(src)
+        try:
+            new_ext = extensions[src.suffix]
+        except LookupError:
+            raise UnknownFileType(f"unknown file type '{src.suffix}' (from '{src}')")
+        if strip_dir:
+            base = pathlib.PurePath(base.name)
+        return os.path.join(output_dir, base.with_suffix(new_ext))
+
+    @staticmethod
+    def _make_relative(base: pathlib.Path):
+        return base.relative_to(base.anchor)
+
+    @overload
+    def shared_object_filename(
+        self,
+        basename: str,
+        strip_dir: Literal[False] = False,
+        output_dir: str | os.PathLike[str] = "",
+    ) -> str: ...
+    @overload
+    def shared_object_filename(
+        self,
+        basename: str | os.PathLike[str],
+        strip_dir: Literal[True],
+        output_dir: str | os.PathLike[str] = "",
+    ) -> str: ...
+    def shared_object_filename(
+        self,
+        basename: str | os.PathLike[str],
+        strip_dir: bool = False,
+        output_dir: str | os.PathLike[str] = '',
+    ) -> str:
+        assert output_dir is not None
+        if strip_dir:
+            basename = os.path.basename(basename)
+        return os.path.join(output_dir, basename + self.shared_lib_extension)
+
+    @overload
+    def executable_filename(
+        self,
+        basename: str,
+        strip_dir: Literal[False] = False,
+        output_dir: str | os.PathLike[str] = "",
+    ) -> str: ...
+    @overload
+    def executable_filename(
+        self,
+        basename: str | os.PathLike[str],
+        strip_dir: Literal[True],
+        output_dir: str | os.PathLike[str] = "",
+    ) -> str: ...
+    def executable_filename(
+        self,
+        basename: str | os.PathLike[str],
+        strip_dir: bool = False,
+        output_dir: str | os.PathLike[str] = '',
+    ) -> str:
+        assert output_dir is not None
+        if strip_dir:
+            basename = os.path.basename(basename)
+        return os.path.join(output_dir, basename + (self.exe_extension or ''))
+
+    def library_filename(
+        self,
+        libname: str,
+        lib_type: str = "static",
+        strip_dir: bool = False,
+        output_dir: str | os.PathLike[str] = "",  # or 'shared'
+    ):
+        assert output_dir is not None
+        expected = '"static", "shared", "dylib", "xcode_stub"'
+        if lib_type not in eval(expected):
+            raise ValueError(f"'lib_type' must be {expected}")
+        fmt = getattr(self, lib_type + "_lib_format")
+        ext = getattr(self, lib_type + "_lib_extension")
+
+        dir, base = os.path.split(libname)
+        filename = fmt % (base, ext)
+        if strip_dir:
+            dir = ''
+
+        return os.path.join(output_dir, dir, filename)
+
+    # -- Utility methods -----------------------------------------------
+
+    def announce(self, msg: object, level: int = 1) -> None:
+        log.debug(msg)
+
+    def debug_print(self, msg: object) -> None:
+        from distutils.debug import DEBUG
+
+        if DEBUG:
+            print(msg)
+
+    def warn(self, msg: object) -> None:
+        sys.stderr.write(f"warning: {msg}\n")
+
+    def execute(
+        self,
+        func: Callable[[Unpack[_Ts]], object],
+        args: tuple[Unpack[_Ts]],
+        msg: object = None,
+        level: int = 1,
+    ) -> None:
+        execute(func, args, msg)
+
+    def spawn(
+        self, cmd: MutableSequence[bytes | str | os.PathLike[str]], **kwargs
+    ) -> None:
+        spawn(cmd, **kwargs)
+
+    @overload
+    def move_file(
+        self, src: str | os.PathLike[str], dst: _StrPathT
+    ) -> _StrPathT | str: ...
+    @overload
+    def move_file(
+        self, src: bytes | os.PathLike[bytes], dst: _BytesPathT
+    ) -> _BytesPathT | bytes: ...
+    def move_file(
+        self,
+        src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+        dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+    ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
+        return move_file(src, dst)
+
+    def mkpath(self, name, mode=0o777):
+        mkpath(name, mode)
+
+
+# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
+# type for that platform. Keys are interpreted as re match
+# patterns. Order is important; platform mappings are preferred over
+# OS names.
+_default_compilers = (
+    # Platform string mappings
+    # on a cygwin built python we can use gcc like an ordinary UNIXish
+    # compiler
+    ('cygwin.*', 'unix'),
+    ('zos', 'zos'),
+    # OS name mappings
+    ('posix', 'unix'),
+    ('nt', 'msvc'),
+)
+
+
+def get_default_compiler(osname: str | None = None, platform: str | None = None) -> str:
+    """Determine the default compiler to use for the given platform.
+
+    osname should be one of the standard Python OS names (i.e. the
+    ones returned by os.name) and platform the common value
+    returned by sys.platform for the platform in question.
+
+    The default values are os.name and sys.platform in case the
+    parameters are not given.
+    """
+    if osname is None:
+        osname = os.name
+    if platform is None:
+        platform = sys.platform
+    # Mingw is a special case where sys.platform is 'win32' but we
+    # want to use the 'mingw32' compiler, so check it first
+    if is_mingw():
+        return 'mingw32'
+    for pattern, compiler in _default_compilers:
+        if (
+            re.match(pattern, platform) is not None
+            or re.match(pattern, osname) is not None
+        ):
+            return compiler
+    # Default to Unix compiler
+    return 'unix'
+
+
+# Map compiler types to (module_name, class_name) pairs -- ie. where to
+# find the code that implements an interface to this compiler.  (The module
+# is assumed to be in the 'distutils' package.)
+compiler_class = {
+    'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"),
+    'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"),
+    'cygwin': (
+        'cygwinccompiler',
+        'CygwinCCompiler',
+        "Cygwin port of GNU C Compiler for Win32",
+    ),
+    'mingw32': (
+        'cygwinccompiler',
+        'Mingw32CCompiler',
+        "Mingw32 port of GNU C Compiler for Win32",
+    ),
+    'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
+    'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
+}
+
+
+def show_compilers() -> None:
+    """Print list of available compilers (used by the "--help-compiler"
+    options to "build", "build_ext", "build_clib").
+    """
+    # XXX this "knows" that the compiler option it's describing is
+    # "--compiler", which just happens to be the case for the three
+    # commands that use it.
+    from distutils.fancy_getopt import FancyGetopt
+
+    compilers = sorted(
+        ("compiler=" + compiler, None, compiler_class[compiler][2])
+        for compiler in compiler_class.keys()
+    )
+    pretty_printer = FancyGetopt(compilers)
+    pretty_printer.print_help("List of available compilers:")
+
+
+def new_compiler(
+    plat: str | None = None,
+    compiler: str | None = None,
+    verbose: bool = False,
+    force: bool = False,
+) -> Compiler:
+    """Generate an instance of some CCompiler subclass for the supplied
+    platform/compiler combination.  'plat' defaults to 'os.name'
+    (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
+    for that platform.  Currently only 'posix' and 'nt' are supported, and
+    the default compilers are "traditional Unix interface" (UnixCCompiler
+    class) and Visual C++ (MSVCCompiler class).  Note that it's perfectly
+    possible to ask for a Unix compiler object under Windows, and a
+    Microsoft compiler object under Unix -- if you supply a value for
+    'compiler', 'plat' is ignored.
+    """
+    if plat is None:
+        plat = os.name
+
+    try:
+        if compiler is None:
+            compiler = get_default_compiler(plat)
+
+        (module_name, class_name, long_description) = compiler_class[compiler]
+    except KeyError:
+        msg = f"don't know how to compile C/C++ code on platform '{plat}'"
+        if compiler is not None:
+            msg = msg + f" with '{compiler}' compiler"
+        raise DistutilsPlatformError(msg)
+
+    try:
+        module_name = "distutils." + module_name
+        __import__(module_name)
+        module = sys.modules[module_name]
+        klass = vars(module)[class_name]
+    except ImportError:
+        raise DistutilsModuleError(
+            f"can't compile C/C++ code: unable to load module '{module_name}'"
+        )
+    except KeyError:
+        raise DistutilsModuleError(
+            f"can't compile C/C++ code: unable to find class '{class_name}' "
+            f"in module '{module_name}'"
+        )
+
+    # XXX The None is necessary to preserve backwards compatibility
+    # with classes that expect verbose to be the first positional
+    # argument.
+    return klass(None, force=force)
+
+
+def gen_preprocess_options(
+    macros: Iterable[_Macro], include_dirs: Iterable[str]
+) -> list[str]:
+    """Generate C pre-processor options (-D, -U, -I) as used by at least
+    two types of compilers: the typical Unix compiler and Visual C++.
+    'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
+    means undefine (-U) macro 'name', and (name,value) means define (-D)
+    macro 'name' to 'value'.  'include_dirs' is just a list of directory
+    names to be added to the header file search path (-I).  Returns a list
+    of command-line options suitable for either Unix compilers or Visual
+    C++.
+    """
+    # XXX it would be nice (mainly aesthetic, and so we don't generate
+    # stupid-looking command lines) to go over 'macros' and eliminate
+    # redundant definitions/undefinitions (ie. ensure that only the
+    # latest mention of a particular macro winds up on the command
+    # line).  I don't think it's essential, though, since most (all?)
+    # Unix C compilers only pay attention to the latest -D or -U
+    # mention of a macro on their command line.  Similar situation for
+    # 'include_dirs'.  I'm punting on both for now.  Anyways, weeding out
+    # redundancies like this should probably be the province of
+    # CCompiler, since the data structures used are inherited from it
+    # and therefore common to all CCompiler classes.
+    pp_opts = []
+    for macro in macros:
+        if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
+            raise TypeError(
+                f"bad macro definition '{macro}': "
+                "each element of 'macros' list must be a 1- or 2-tuple"
+            )
+
+        if len(macro) == 1:  # undefine this macro
+            pp_opts.append(f"-U{macro[0]}")
+        elif len(macro) == 2:
+            if macro[1] is None:  # define with no explicit value
+                pp_opts.append(f"-D{macro[0]}")
+            else:
+                # XXX *don't* need to be clever about quoting the
+                # macro value here, because we're going to avoid the
+                # shell at all costs when we spawn the command!
+                pp_opts.append("-D{}={}".format(*macro))
+
+    pp_opts.extend(f"-I{dir}" for dir in include_dirs)
+    return pp_opts
+
+
+def gen_lib_options(
+    compiler: Compiler,
+    library_dirs: Iterable[str],
+    runtime_library_dirs: Iterable[str],
+    libraries: Iterable[str],
+) -> list[str]:
+    """Generate linker options for searching library directories and
+    linking with specific libraries.  'libraries' and 'library_dirs' are,
+    respectively, lists of library names (not filenames!) and search
+    directories.  Returns a list of command-line options suitable for use
+    with some compiler (depending on the two format strings passed in).
+    """
+    lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
+
+    for dir in runtime_library_dirs:
+        lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
+
+    # XXX it's important that we *not* remove redundant library mentions!
+    # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
+    # resolve all symbols.  I just hope we never have to say "-lfoo obj.o
+    # -lbar" to get things to work -- that's certainly a possibility, but a
+    # pretty nasty way to arrange your C code.
+
+    for lib in libraries:
+        (lib_dir, lib_name) = os.path.split(lib)
+        if lib_dir:
+            lib_file = compiler.find_library_file([lib_dir], lib_name)
+            if lib_file:
+                lib_opts.append(lib_file)
+            else:
+                compiler.warn(
+                    f"no library file corresponding to '{lib}' found (skipping)"
+                )
+        else:
+            lib_opts.append(compiler.library_option(lib))
+    return lib_opts
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py
new file mode 100644
index 0000000..f349e9d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py
@@ -0,0 +1,340 @@
+"""distutils.cygwinccompiler
+
+Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
+handles the Cygwin port of the GNU C compiler to Windows.  It also contains
+the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
+cygwin in no-cygwin mode).
+"""
+
+import copy
+import os
+import pathlib
+import shlex
+import sys
+import warnings
+from subprocess import check_output
+
+from ...errors import (
+    DistutilsExecError,
+    DistutilsPlatformError,
+)
+from ...file_util import write_file
+from ...sysconfig import get_config_vars
+from ...version import LooseVersion, suppress_known_deprecation
+from . import unix
+from .errors import (
+    CompileError,
+    Error,
+)
+
+
+def get_msvcr():
+    """No longer needed, but kept for backward compatibility."""
+    return []
+
+
+_runtime_library_dirs_msg = (
+    "Unable to set runtime library search path on Windows, "
+    "usually indicated by `runtime_library_dirs` parameter to Extension"
+)
+
+
+class Compiler(unix.Compiler):
+    """Handles the Cygwin port of the GNU C compiler to Windows."""
+
+    compiler_type = 'cygwin'
+    obj_extension = ".o"
+    static_lib_extension = ".a"
+    shared_lib_extension = ".dll.a"
+    dylib_lib_extension = ".dll"
+    static_lib_format = "lib%s%s"
+    shared_lib_format = "lib%s%s"
+    dylib_lib_format = "cyg%s%s"
+    exe_extension = ".exe"
+
+    def __init__(self, verbose=False, force=False):
+        super().__init__(verbose, force=force)
+
+        status, details = check_config_h()
+        self.debug_print(f"Python's GCC status: {status} (details: {details})")
+        if status is not CONFIG_H_OK:
+            self.warn(
+                "Python's pyconfig.h doesn't seem to support your compiler. "
+                f"Reason: {details}. "
+                "Compiling may fail because of undefined preprocessor macros."
+            )
+
+        self.cc, self.cxx = get_config_vars('CC', 'CXX')
+
+        # Override 'CC' and 'CXX' environment variables for
+        # building using MINGW compiler for MSVC python.
+        self.cc = os.environ.get('CC', self.cc or 'gcc')
+        self.cxx = os.environ.get('CXX', self.cxx or 'g++')
+
+        self.linker_dll = self.cc
+        self.linker_dll_cxx = self.cxx
+        shared_option = "-shared"
+
+        self.set_executables(
+            compiler=f'{self.cc} -mcygwin -O -Wall',
+            compiler_so=f'{self.cc} -mcygwin -mdll -O -Wall',
+            compiler_cxx=f'{self.cxx} -mcygwin -O -Wall',
+            compiler_so_cxx=f'{self.cxx} -mcygwin -mdll -O -Wall',
+            linker_exe=f'{self.cc} -mcygwin',
+            linker_so=f'{self.linker_dll} -mcygwin {shared_option}',
+            linker_exe_cxx=f'{self.cxx} -mcygwin',
+            linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}',
+        )
+
+        self.dll_libraries = get_msvcr()
+
+    @property
+    def gcc_version(self):
+        # Older numpy depended on this existing to check for ancient
+        # gcc versions. This doesn't make much sense with clang etc so
+        # just hardcode to something recent.
+        # https://github.com/numpy/numpy/pull/20333
+        warnings.warn(
+            "gcc_version attribute of CygwinCCompiler is deprecated. "
+            "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        with suppress_known_deprecation():
+            return LooseVersion("11.2.0")
+
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        """Compiles the source by spawning GCC and windres if needed."""
+        if ext in ('.rc', '.res'):
+            # gcc needs '.res' and '.rc' compiled to object files !!!
+            try:
+                self.spawn(["windres", "-i", src, "-o", obj])
+            except DistutilsExecError as msg:
+                raise CompileError(msg)
+        else:  # for other files use the C-compiler
+            try:
+                if self.detect_language(src) == 'c++':
+                    self.spawn(
+                        self.compiler_so_cxx
+                        + cc_args
+                        + [src, '-o', obj]
+                        + extra_postargs
+                    )
+                else:
+                    self.spawn(
+                        self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs
+                    )
+            except DistutilsExecError as msg:
+                raise CompileError(msg)
+
+    def link(
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        export_symbols=None,
+        debug=False,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ):
+        """Link the objects."""
+        # use separate copies, so we can modify the lists
+        extra_preargs = copy.copy(extra_preargs or [])
+        libraries = copy.copy(libraries or [])
+        objects = copy.copy(objects or [])
+
+        if runtime_library_dirs:
+            self.warn(_runtime_library_dirs_msg)
+
+        # Additional libraries
+        libraries.extend(self.dll_libraries)
+
+        # handle export symbols by creating a def-file
+        # with executables this only works with gcc/ld as linker
+        if (export_symbols is not None) and (
+            target_desc != self.EXECUTABLE or self.linker_dll == "gcc"
+        ):
+            # (The linker doesn't do anything if output is up-to-date.
+            # So it would probably better to check if we really need this,
+            # but for this we had to insert some unchanged parts of
+            # UnixCCompiler, and this is not what we want.)
+
+            # we want to put some files in the same directory as the
+            # object files are, build_temp doesn't help much
+            # where are the object files
+            temp_dir = os.path.dirname(objects[0])
+            # name of dll to give the helper files the same base name
+            (dll_name, dll_extension) = os.path.splitext(
+                os.path.basename(output_filename)
+            )
+
+            # generate the filenames for these files
+            def_file = os.path.join(temp_dir, dll_name + ".def")
+
+            # Generate .def file
+            contents = [f"LIBRARY {os.path.basename(output_filename)}", "EXPORTS"]
+            contents.extend(export_symbols)
+            self.execute(write_file, (def_file, contents), f"writing {def_file}")
+
+            # next add options for def-file
+
+            # for gcc/ld the def-file is specified as any object files
+            objects.append(def_file)
+
+        # end: if ((export_symbols is not None) and
+        #        (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
+
+        # who wants symbols and a many times larger output file
+        # should explicitly switch the debug mode on
+        # otherwise we let ld strip the output file
+        # (On my machine: 10KiB < stripped_file < ??100KiB
+        #   unstripped_file = stripped_file + XXX KiB
+        #  ( XXX=254 for a typical python extension))
+        if not debug:
+            extra_preargs.append("-s")
+
+        super().link(
+            target_desc,
+            objects,
+            output_filename,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            None,  # export_symbols, we do this in our def-file
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def runtime_library_dir_option(self, dir):
+        # cygwin doesn't support rpath. While in theory we could error
+        # out like MSVC does, code might expect it to work like on Unix, so
+        # just warn and hope for the best.
+        self.warn(_runtime_library_dirs_msg)
+        return []
+
+    # -- Miscellaneous methods -----------------------------------------
+
+    def _make_out_path(self, output_dir, strip_dir, src_name):
+        # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+        norm_src_name = os.path.normcase(src_name)
+        return super()._make_out_path(output_dir, strip_dir, norm_src_name)
+
+    @property
+    def out_extensions(self):
+        """
+        Add support for rc and res files.
+        """
+        return {
+            **super().out_extensions,
+            **{ext: ext + self.obj_extension for ext in ('.res', '.rc')},
+        }
+
+
+# the same as cygwin plus some additional parameters
+class MinGW32Compiler(Compiler):
+    """Handles the Mingw32 port of the GNU C compiler to Windows."""
+
+    compiler_type = 'mingw32'
+
+    def __init__(self, verbose=False, force=False):
+        super().__init__(verbose, force)
+
+        shared_option = "-shared"
+
+        if is_cygwincc(self.cc):
+            raise Error('Cygwin gcc cannot be used with --compiler=mingw32')
+
+        self.set_executables(
+            compiler=f'{self.cc} -O -Wall',
+            compiler_so=f'{self.cc} -shared -O -Wall',
+            compiler_so_cxx=f'{self.cxx} -shared -O -Wall',
+            compiler_cxx=f'{self.cxx} -O -Wall',
+            linker_exe=f'{self.cc}',
+            linker_so=f'{self.linker_dll} {shared_option}',
+            linker_exe_cxx=f'{self.cxx}',
+            linker_so_cxx=f'{self.linker_dll_cxx} {shared_option}',
+        )
+
+    def runtime_library_dir_option(self, dir):
+        raise DistutilsPlatformError(_runtime_library_dirs_msg)
+
+
+# Because these compilers aren't configured in Python's pyconfig.h file by
+# default, we should at least warn the user if he is using an unmodified
+# version.
+
+CONFIG_H_OK = "ok"
+CONFIG_H_NOTOK = "not ok"
+CONFIG_H_UNCERTAIN = "uncertain"
+
+
+def check_config_h():
+    """Check if the current Python installation appears amenable to building
+    extensions with GCC.
+
+    Returns a tuple (status, details), where 'status' is one of the following
+    constants:
+
+    - CONFIG_H_OK: all is well, go ahead and compile
+    - CONFIG_H_NOTOK: doesn't look good
+    - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
+
+    'details' is a human-readable string explaining the situation.
+
+    Note there are two ways to conclude "OK": either 'sys.version' contains
+    the string "GCC" (implying that this Python was built with GCC), or the
+    installed "pyconfig.h" contains the string "__GNUC__".
+    """
+
+    # XXX since this function also checks sys.version, it's not strictly a
+    # "pyconfig.h" check -- should probably be renamed...
+
+    from distutils import sysconfig
+
+    # if sys.version contains GCC then python was compiled with GCC, and the
+    # pyconfig.h file should be OK
+    if "GCC" in sys.version:
+        return CONFIG_H_OK, "sys.version mentions 'GCC'"
+
+    # Clang would also work
+    if "Clang" in sys.version:
+        return CONFIG_H_OK, "sys.version mentions 'Clang'"
+
+    # let's see if __GNUC__ is mentioned in python.h
+    fn = sysconfig.get_config_h_filename()
+    try:
+        config_h = pathlib.Path(fn).read_text(encoding='utf-8')
+    except OSError as exc:
+        return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}")
+    else:
+        substring = '__GNUC__'
+        if substring in config_h:
+            code = CONFIG_H_OK
+            mention_inflected = 'mentions'
+        else:
+            code = CONFIG_H_NOTOK
+            mention_inflected = 'does not mention'
+        return code, f"{fn!r} {mention_inflected} {substring!r}"
+
+
+def is_cygwincc(cc):
+    """Try to determine if the compiler that would be used is from cygwin."""
+    out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
+    return out_string.strip().endswith(b'cygwin')
+
+
+get_versions = None
+"""
+A stand-in for the previous get_versions() function to prevent failures
+when monkeypatched. See pypa/setuptools#2969.
+"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py
new file mode 100644
index 0000000..0132859
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py
@@ -0,0 +1,24 @@
+class Error(Exception):
+    """Some compile/link operation failed."""
+
+
+class PreprocessError(Error):
+    """Failure to preprocess one or more C/C++ files."""
+
+
+class CompileError(Error):
+    """Failure to compile one or more C/C++ source files."""
+
+
+class LibError(Error):
+    """Failure to create a static library from one or more C/C++ object
+    files."""
+
+
+class LinkError(Error):
+    """Failure to link one or more C/C++ object files into an executable
+    or shared library file."""
+
+
+class UnknownFileType(Error):
+    """Attempt to process an unknown file type."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py
new file mode 100644
index 0000000..1f50fa5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py
@@ -0,0 +1,614 @@
+"""distutils._msvccompiler
+
+Contains MSVCCompiler, an implementation of the abstract CCompiler class
+for Microsoft Visual Studio 2015.
+
+This module requires VS 2015 or later.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+#   finding DevStudio (through the registry)
+# ported to VS 2005 and VS 2008 by Christian Heimes
+# ported to VS 2015 by Steve Dower
+from __future__ import annotations
+
+import contextlib
+import os
+import subprocess
+import unittest.mock as mock
+import warnings
+from collections.abc import Iterable
+
+with contextlib.suppress(ImportError):
+    import winreg
+
+from itertools import count
+
+from ..._log import log
+from ...errors import (
+    DistutilsExecError,
+    DistutilsPlatformError,
+)
+from ...util import get_host_platform, get_platform
+from . import base
+from .base import gen_lib_options
+from .errors import (
+    CompileError,
+    LibError,
+    LinkError,
+)
+
+
+def _find_vc2015():
+    try:
+        key = winreg.OpenKeyEx(
+            winreg.HKEY_LOCAL_MACHINE,
+            r"Software\Microsoft\VisualStudio\SxS\VC7",
+            access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
+        )
+    except OSError:
+        log.debug("Visual C++ is not registered")
+        return None, None
+
+    best_version = 0
+    best_dir = None
+    with key:
+        for i in count():
+            try:
+                v, vc_dir, vt = winreg.EnumValue(key, i)
+            except OSError:
+                break
+            if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
+                try:
+                    version = int(float(v))
+                except (ValueError, TypeError):
+                    continue
+                if version >= 14 and version > best_version:
+                    best_version, best_dir = version, vc_dir
+    return best_version, best_dir
+
+
+def _find_vc2017():
+    """Returns "15, path" based on the result of invoking vswhere.exe
+    If no install is found, returns "None, None"
+
+    The version is returned to avoid unnecessarily changing the function
+    result. It may be ignored when the path is not None.
+
+    If vswhere.exe is not available, by definition, VS 2017 is not
+    installed.
+    """
+    root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
+    if not root:
+        return None, None
+
+    variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64'
+    suitable_components = (
+        f"Microsoft.VisualStudio.Component.VC.Tools.{variant}",
+        "Microsoft.VisualStudio.Workload.WDExpress",
+    )
+
+    for component in suitable_components:
+        # Workaround for `-requiresAny` (only available on VS 2017 > 15.6)
+        with contextlib.suppress(
+            subprocess.CalledProcessError, OSError, UnicodeDecodeError
+        ):
+            path = (
+                subprocess.check_output([
+                    os.path.join(
+                        root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
+                    ),
+                    "-latest",
+                    "-prerelease",
+                    "-requires",
+                    component,
+                    "-property",
+                    "installationPath",
+                    "-products",
+                    "*",
+                ])
+                .decode(encoding="mbcs", errors="strict")
+                .strip()
+            )
+
+            path = os.path.join(path, "VC", "Auxiliary", "Build")
+            if os.path.isdir(path):
+                return 15, path
+
+    return None, None  # no suitable component found
+
+
+PLAT_SPEC_TO_RUNTIME = {
+    'x86': 'x86',
+    'x86_amd64': 'x64',
+    'x86_arm': 'arm',
+    'x86_arm64': 'arm64',
+}
+
+
+def _find_vcvarsall(plat_spec):
+    # bpo-38597: Removed vcruntime return value
+    _, best_dir = _find_vc2017()
+
+    if not best_dir:
+        best_version, best_dir = _find_vc2015()
+
+    if not best_dir:
+        log.debug("No suitable Visual C++ version found")
+        return None, None
+
+    vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
+    if not os.path.isfile(vcvarsall):
+        log.debug("%s cannot be found", vcvarsall)
+        return None, None
+
+    return vcvarsall, None
+
+
+def _get_vc_env(plat_spec):
+    if os.getenv("DISTUTILS_USE_SDK"):
+        return {key.lower(): value for key, value in os.environ.items()}
+
+    vcvarsall, _ = _find_vcvarsall(plat_spec)
+    if not vcvarsall:
+        raise DistutilsPlatformError(
+            'Microsoft Visual C++ 14.0 or greater is required. '
+            'Get it with "Microsoft C++ Build Tools": '
+            'https://visualstudio.microsoft.com/visual-cpp-build-tools/'
+        )
+
+    try:
+        out = subprocess.check_output(
+            f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
+            stderr=subprocess.STDOUT,
+        ).decode('utf-16le', errors='replace')
+    except subprocess.CalledProcessError as exc:
+        log.error(exc.output)
+        raise DistutilsPlatformError(f"Error executing {exc.cmd}")
+
+    env = {
+        key.lower(): value
+        for key, _, value in (line.partition('=') for line in out.splitlines())
+        if key and value
+    }
+
+    return env
+
+
+def _find_exe(exe, paths=None):
+    """Return path to an MSVC executable program.
+
+    Tries to find the program in several places: first, one of the
+    MSVC program search paths from the registry; next, the directories
+    in the PATH environment variable.  If any of those work, return an
+    absolute path that is known to exist.  If none of them work, just
+    return the original program name, 'exe'.
+    """
+    if not paths:
+        paths = os.getenv('path').split(os.pathsep)
+    for p in paths:
+        fn = os.path.join(os.path.abspath(p), exe)
+        if os.path.isfile(fn):
+            return fn
+    return exe
+
+
+_vcvars_names = {
+    'win32': 'x86',
+    'win-amd64': 'amd64',
+    'win-arm32': 'arm',
+    'win-arm64': 'arm64',
+}
+
+
+def _get_vcvars_spec(host_platform, platform):
+    """
+    Given a host platform and platform, determine the spec for vcvarsall.
+
+    Uses the native MSVC host if the host platform would need expensive
+    emulation for x86.
+
+    >>> _get_vcvars_spec('win-arm64', 'win32')
+    'arm64_x86'
+    >>> _get_vcvars_spec('win-arm64', 'win-amd64')
+    'arm64_amd64'
+
+    Otherwise, always cross-compile from x86 to work with the
+    lighter-weight MSVC installs that do not include native 64-bit tools.
+
+    >>> _get_vcvars_spec('win32', 'win32')
+    'x86'
+    >>> _get_vcvars_spec('win-arm32', 'win-arm32')
+    'x86_arm'
+    >>> _get_vcvars_spec('win-amd64', 'win-arm64')
+    'x86_arm64'
+    """
+    if host_platform != 'win-arm64':
+        host_platform = 'win32'
+    vc_hp = _vcvars_names[host_platform]
+    vc_plat = _vcvars_names[platform]
+    return vc_hp if vc_hp == vc_plat else f'{vc_hp}_{vc_plat}'
+
+
+class Compiler(base.Compiler):
+    """Concrete class that implements an interface to Microsoft Visual C++,
+    as defined by the CCompiler abstract class."""
+
+    compiler_type = 'msvc'
+
+    # Just set this so CCompiler's constructor doesn't barf.  We currently
+    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+    # as it really isn't necessary for this sort of single-compiler class.
+    # Would be nice to have a consistent interface with UnixCCompiler,
+    # though, so it's worth thinking about.
+    executables = {}
+
+    # Private class data (need to distinguish C from C++ source for compiler)
+    _c_extensions = ['.c']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx']
+    _rc_extensions = ['.rc']
+    _mc_extensions = ['.mc']
+
+    # Needed for the filename generation methods provided by the
+    # base class, CCompiler.
+    src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
+    res_extension = '.res'
+    obj_extension = '.obj'
+    static_lib_extension = '.lib'
+    shared_lib_extension = '.dll'
+    static_lib_format = shared_lib_format = '%s%s'
+    exe_extension = '.exe'
+
+    def __init__(self, verbose=False, force=False) -> None:
+        super().__init__(verbose, force=force)
+        # target platform (.plat_name is consistent with 'bdist')
+        self.plat_name = None
+        self.initialized = False
+
+    @classmethod
+    def _configure(cls, vc_env):
+        """
+        Set class-level include/lib dirs.
+        """
+        cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
+        cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
+
+    @staticmethod
+    def _parse_path(val):
+        return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
+
+    def initialize(self, plat_name: str | None = None) -> None:
+        # multi-init means we would need to check platform same each time...
+        assert not self.initialized, "don't init multiple times"
+        if plat_name is None:
+            plat_name = get_platform()
+        # sanity check for platforms to prevent obscure errors later.
+        if plat_name not in _vcvars_names:
+            raise DistutilsPlatformError(
+                f"--plat-name must be one of {tuple(_vcvars_names)}"
+            )
+
+        plat_spec = _get_vcvars_spec(get_host_platform(), plat_name)
+
+        vc_env = _get_vc_env(plat_spec)
+        if not vc_env:
+            raise DistutilsPlatformError(
+                "Unable to find a compatible Visual Studio installation."
+            )
+        self._configure(vc_env)
+
+        self._paths = vc_env.get('path', '')
+        paths = self._paths.split(os.pathsep)
+        self.cc = _find_exe("cl.exe", paths)
+        self.linker = _find_exe("link.exe", paths)
+        self.lib = _find_exe("lib.exe", paths)
+        self.rc = _find_exe("rc.exe", paths)  # resource compiler
+        self.mc = _find_exe("mc.exe", paths)  # message compiler
+        self.mt = _find_exe("mt.exe", paths)  # message compiler
+
+        self.preprocess_options = None
+        # bpo-38597: Always compile with dynamic linking
+        # Future releases of Python 3.x will include all past
+        # versions of vcruntime*.dll for compatibility.
+        self.compile_options = ['/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD']
+
+        self.compile_options_debug = [
+            '/nologo',
+            '/Od',
+            '/MDd',
+            '/Zi',
+            '/W3',
+            '/D_DEBUG',
+        ]
+
+        ldflags = ['/nologo', '/INCREMENTAL:NO', '/LTCG']
+
+        ldflags_debug = ['/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL']
+
+        self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
+        self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
+        self.ldflags_shared = [
+            *ldflags,
+            '/DLL',
+            '/MANIFEST:EMBED,ID=2',
+            '/MANIFESTUAC:NO',
+        ]
+        self.ldflags_shared_debug = [
+            *ldflags_debug,
+            '/DLL',
+            '/MANIFEST:EMBED,ID=2',
+            '/MANIFESTUAC:NO',
+        ]
+        self.ldflags_static = [*ldflags]
+        self.ldflags_static_debug = [*ldflags_debug]
+
+        self._ldflags = {
+            (base.Compiler.EXECUTABLE, None): self.ldflags_exe,
+            (base.Compiler.EXECUTABLE, False): self.ldflags_exe,
+            (base.Compiler.EXECUTABLE, True): self.ldflags_exe_debug,
+            (base.Compiler.SHARED_OBJECT, None): self.ldflags_shared,
+            (base.Compiler.SHARED_OBJECT, False): self.ldflags_shared,
+            (base.Compiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
+            (base.Compiler.SHARED_LIBRARY, None): self.ldflags_static,
+            (base.Compiler.SHARED_LIBRARY, False): self.ldflags_static,
+            (base.Compiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
+        }
+
+        self.initialized = True
+
+    # -- Worker methods ------------------------------------------------
+
+    @property
+    def out_extensions(self) -> dict[str, str]:
+        return {
+            **super().out_extensions,
+            **{
+                ext: self.res_extension
+                for ext in self._rc_extensions + self._mc_extensions
+            },
+        }
+
+    def compile(  # noqa: C901
+        self,
+        sources,
+        output_dir=None,
+        macros=None,
+        include_dirs=None,
+        debug=False,
+        extra_preargs=None,
+        extra_postargs=None,
+        depends=None,
+    ):
+        if not self.initialized:
+            self.initialize()
+        compile_info = self._setup_compile(
+            output_dir, macros, include_dirs, sources, depends, extra_postargs
+        )
+        macros, objects, extra_postargs, pp_opts, build = compile_info
+
+        compile_opts = extra_preargs or []
+        compile_opts.append('/c')
+        if debug:
+            compile_opts.extend(self.compile_options_debug)
+        else:
+            compile_opts.extend(self.compile_options)
+
+        add_cpp_opts = False
+
+        for obj in objects:
+            try:
+                src, ext = build[obj]
+            except KeyError:
+                continue
+            if debug:
+                # pass the full pathname to MSVC in debug mode,
+                # this allows the debugger to find the source file
+                # without asking the user to browse for it
+                src = os.path.abspath(src)
+
+            if ext in self._c_extensions:
+                input_opt = f"/Tc{src}"
+            elif ext in self._cpp_extensions:
+                input_opt = f"/Tp{src}"
+                add_cpp_opts = True
+            elif ext in self._rc_extensions:
+                # compile .RC to .RES file
+                input_opt = src
+                output_opt = "/fo" + obj
+                try:
+                    self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
+                except DistutilsExecError as msg:
+                    raise CompileError(msg)
+                continue
+            elif ext in self._mc_extensions:
+                # Compile .MC to .RC file to .RES file.
+                #   * '-h dir' specifies the directory for the
+                #     generated include file
+                #   * '-r dir' specifies the target directory of the
+                #     generated RC file and the binary message resource
+                #     it includes
+                #
+                # For now (since there are no options to change this),
+                # we use the source-directory for the include file and
+                # the build directory for the RC file and message
+                # resources. This works at least for win32all.
+                h_dir = os.path.dirname(src)
+                rc_dir = os.path.dirname(obj)
+                try:
+                    # first compile .MC to .RC and .H file
+                    self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
+                    base, _ = os.path.splitext(os.path.basename(src))
+                    rc_file = os.path.join(rc_dir, base + '.rc')
+                    # then compile .RC to .RES file
+                    self.spawn([self.rc, "/fo" + obj, rc_file])
+
+                except DistutilsExecError as msg:
+                    raise CompileError(msg)
+                continue
+            else:
+                # how to handle this file?
+                raise CompileError(f"Don't know how to compile {src} to {obj}")
+
+            args = [self.cc] + compile_opts + pp_opts
+            if add_cpp_opts:
+                args.append('/EHsc')
+            args.extend((input_opt, "/Fo" + obj))
+            args.extend(extra_postargs)
+
+            try:
+                self.spawn(args)
+            except DistutilsExecError as msg:
+                raise CompileError(msg)
+
+        return objects
+
+    def create_static_lib(
+        self,
+        objects: list[str] | tuple[str, ...],
+        output_libname: str,
+        output_dir: str | None = None,
+        debug: bool = False,
+        target_lang: str | None = None,
+    ) -> None:
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+        if self._need_link(objects, output_filename):
+            lib_args = objects + ['/OUT:' + output_filename]
+            if debug:
+                pass  # XXX what goes here?
+            try:
+                log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
+                self.spawn([self.lib] + lib_args)
+            except DistutilsExecError as msg:
+                raise LibError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    def link(
+        self,
+        target_desc: str,
+        objects: list[str] | tuple[str, ...],
+        output_filename: str,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        export_symbols: Iterable[str] | None = None,
+        debug: bool = False,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: Iterable[str] | None = None,
+        build_temp: str | os.PathLike[str] | None = None,
+        target_lang: str | None = None,
+    ) -> None:
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+        libraries, library_dirs, runtime_library_dirs = fixed_args
+
+        if runtime_library_dirs:
+            self.warn(
+                "I don't know what to do with 'runtime_library_dirs': "
+                + str(runtime_library_dirs)
+            )
+
+        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+        if output_dir is not None:
+            output_filename = os.path.join(output_dir, output_filename)
+
+        if self._need_link(objects, output_filename):
+            ldflags = self._ldflags[target_desc, debug]
+
+            export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
+
+            ld_args = (
+                ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
+            )
+
+            # The MSVC linker generates .lib and .exp files, which cannot be
+            # suppressed by any linker switches. The .lib files may even be
+            # needed! Make sure they are generated in the temporary build
+            # directory. Since they have different names for debug and release
+            # builds, they can go into the same directory.
+            build_temp = os.path.dirname(objects[0])
+            if export_symbols is not None:
+                (dll_name, dll_ext) = os.path.splitext(
+                    os.path.basename(output_filename)
+                )
+                implib_file = os.path.join(build_temp, self.library_filename(dll_name))
+                ld_args.append('/IMPLIB:' + implib_file)
+
+            if extra_preargs:
+                ld_args[:0] = extra_preargs
+            if extra_postargs:
+                ld_args.extend(extra_postargs)
+
+            output_dir = os.path.dirname(os.path.abspath(output_filename))
+            self.mkpath(output_dir)
+            try:
+                log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
+                self.spawn([self.linker] + ld_args)
+            except DistutilsExecError as msg:
+                raise LinkError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    def spawn(self, cmd):
+        env = dict(os.environ, PATH=self._paths)
+        with self._fallback_spawn(cmd, env) as fallback:
+            return super().spawn(cmd, env=env)
+        return fallback.value
+
+    @contextlib.contextmanager
+    def _fallback_spawn(self, cmd, env):
+        """
+        Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
+        so the 'env' kwarg causes a TypeError. Detect this condition and
+        restore the legacy, unsafe behavior.
+        """
+        bag = type('Bag', (), {})()
+        try:
+            yield bag
+        except TypeError as exc:
+            if "unexpected keyword argument 'env'" not in str(exc):
+                raise
+        else:
+            return
+        warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
+        with mock.patch.dict('os.environ', env):
+            bag.value = super().spawn(cmd)
+
+    # -- Miscellaneous methods -----------------------------------------
+    # These are all used by the 'gen_lib_options() function, in
+    # ccompiler.py.
+
+    def library_dir_option(self, dir):
+        return "/LIBPATH:" + dir
+
+    def runtime_library_dir_option(self, dir):
+        raise DistutilsPlatformError(
+            "don't know how to set runtime library search path for MSVC"
+        )
+
+    def library_option(self, lib):
+        return self.library_filename(lib)
+
+    def find_library_file(self, dirs, lib, debug=False):
+        # Prefer a debugging library if found (and requested), but deal
+        # with it if we don't have one.
+        if debug:
+            try_names = [lib + "_d", lib]
+        else:
+            try_names = [lib]
+        for dir in dirs:
+            for name in try_names:
+                libfile = os.path.join(dir, self.library_filename(name))
+                if os.path.isfile(libfile):
+                    return libfile
+        else:
+            # Oops, didn't find it in *any* of 'dirs'
+            return None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py
new file mode 100644
index 0000000..a762e2b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py
@@ -0,0 +1,83 @@
+import platform
+import sysconfig
+import textwrap
+
+import pytest
+
+from .. import base
+
+pytestmark = pytest.mark.usefixtures('suppress_path_mangle')
+
+
+@pytest.fixture
+def c_file(tmp_path):
+    c_file = tmp_path / 'foo.c'
+    gen_headers = ('Python.h',)
+    is_windows = platform.system() == "Windows"
+    plat_headers = ('windows.h',) * is_windows
+    all_headers = gen_headers + plat_headers
+    headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
+    payload = (
+        textwrap.dedent(
+            """
+        #headers
+        void PyInit_foo(void) {}
+        """
+        )
+        .lstrip()
+        .replace('#headers', headers)
+    )
+    c_file.write_text(payload, encoding='utf-8')
+    return c_file
+
+
+def test_set_include_dirs(c_file):
+    """
+    Extensions should build even if set_include_dirs is invoked.
+    In particular, compiler-specific paths should not be overridden.
+    """
+    compiler = base.new_compiler()
+    python = sysconfig.get_paths()['include']
+    compiler.set_include_dirs([python])
+    compiler.compile([c_file])
+
+    # do it again, setting include dirs after any initialization
+    compiler.set_include_dirs([python])
+    compiler.compile([c_file])
+
+
+def test_has_function_prototype():
+    # Issue https://github.com/pypa/setuptools/issues/3648
+    # Test prototype-generating behavior.
+
+    compiler = base.new_compiler()
+
+    # Every C implementation should have these.
+    assert compiler.has_function('abort')
+    assert compiler.has_function('exit')
+    with pytest.deprecated_call(match='includes is deprecated'):
+        # abort() is a valid expression with the  prototype.
+        assert compiler.has_function('abort', includes=['stdlib.h'])
+    with pytest.deprecated_call(match='includes is deprecated'):
+        # But exit() is not valid with the actual prototype in scope.
+        assert not compiler.has_function('exit', includes=['stdlib.h'])
+    # And setuptools_does_not_exist is not declared or defined at all.
+    assert not compiler.has_function('setuptools_does_not_exist')
+    with pytest.deprecated_call(match='includes is deprecated'):
+        assert not compiler.has_function(
+            'setuptools_does_not_exist', includes=['stdio.h']
+        )
+
+
+def test_include_dirs_after_multiple_compile_calls(c_file):
+    """
+    Calling compile multiple times should not change the include dirs
+    (regression test for setuptools issue #3591).
+    """
+    compiler = base.new_compiler()
+    python = sysconfig.get_paths()['include']
+    compiler.set_include_dirs([python])
+    compiler.compile([c_file])
+    assert compiler.include_dirs == [python]
+    compiler.compile([c_file])
+    assert compiler.include_dirs == [python]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py
new file mode 100644
index 0000000..9adf6b8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py
@@ -0,0 +1,76 @@
+"""Tests for distutils.cygwinccompiler."""
+
+import os
+import sys
+from distutils import sysconfig
+from distutils.tests import support
+
+import pytest
+
+from .. import cygwin
+
+
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+    self = request.instance
+    self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+    monkeypatch.setattr(sysconfig, 'get_config_h_filename', self._get_config_h_filename)
+    monkeypatch.setattr(sys, 'version', sys.version)
+
+
+class TestCygwinCCompiler(support.TempdirManager):
+    def _get_config_h_filename(self):
+        return self.python_h
+
+    @pytest.mark.skipif('sys.platform != "cygwin"')
+    @pytest.mark.skipif('not os.path.exists("/usr/lib/libbash.dll.a")')
+    def test_find_library_file(self):
+        from distutils.cygwinccompiler import CygwinCCompiler
+
+        compiler = CygwinCCompiler()
+        link_name = "bash"
+        linkable_file = compiler.find_library_file(["/usr/lib"], link_name)
+        assert linkable_file is not None
+        assert os.path.exists(linkable_file)
+        assert linkable_file == f"/usr/lib/lib{link_name:s}.dll.a"
+
+    @pytest.mark.skipif('sys.platform != "cygwin"')
+    def test_runtime_library_dir_option(self):
+        from distutils.cygwinccompiler import CygwinCCompiler
+
+        compiler = CygwinCCompiler()
+        assert compiler.runtime_library_dir_option('/foo') == []
+
+    def test_check_config_h(self):
+        # check_config_h looks for "GCC" in sys.version first
+        # returns CONFIG_H_OK if found
+        sys.version = (
+            '2.6.1 (r261:67515, Dec  6 2008, 16:42:21) \n[GCC '
+            '4.0.1 (Apple Computer, Inc. build 5370)]'
+        )
+
+        assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
+
+        # then it tries to see if it can find "__GNUC__" in pyconfig.h
+        sys.version = 'something without the *CC word'
+
+        # if the file doesn't exist it returns  CONFIG_H_UNCERTAIN
+        assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_UNCERTAIN
+
+        # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
+        self.write_file(self.python_h, 'xxx')
+        assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_NOTOK
+
+        # and CONFIG_H_OK if __GNUC__ is found
+        self.write_file(self.python_h, 'xxx __GNUC__ xxx')
+        assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
+
+    def test_get_msvcr(self):
+        assert cygwin.get_msvcr() == []
+
+    @pytest.mark.skipif('sys.platform != "cygwin"')
+    def test_dll_libraries_not_none(self):
+        from distutils.cygwinccompiler import CygwinCCompiler
+
+        compiler = CygwinCCompiler()
+        assert compiler.dll_libraries is not None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py
new file mode 100644
index 0000000..dc45687
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py
@@ -0,0 +1,48 @@
+from distutils import sysconfig
+from distutils.errors import DistutilsPlatformError
+from distutils.util import is_mingw, split_quoted
+
+import pytest
+
+from .. import cygwin, errors
+
+
+class TestMinGW32Compiler:
+    @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+    def test_compiler_type(self):
+        compiler = cygwin.MinGW32Compiler()
+        assert compiler.compiler_type == 'mingw32'
+
+    @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+    def test_set_executables(self, monkeypatch):
+        monkeypatch.setenv('CC', 'cc')
+        monkeypatch.setenv('CXX', 'c++')
+
+        compiler = cygwin.MinGW32Compiler()
+
+        assert compiler.compiler == split_quoted('cc -O -Wall')
+        assert compiler.compiler_so == split_quoted('cc -shared -O -Wall')
+        assert compiler.compiler_cxx == split_quoted('c++ -O -Wall')
+        assert compiler.linker_exe == split_quoted('cc')
+        assert compiler.linker_so == split_quoted('cc -shared')
+
+    @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+    def test_runtime_library_dir_option(self):
+        compiler = cygwin.MinGW32Compiler()
+        with pytest.raises(DistutilsPlatformError):
+            compiler.runtime_library_dir_option('/usr/lib')
+
+    @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+    def test_cygwincc_error(self, monkeypatch):
+        monkeypatch.setattr(cygwin, 'is_cygwincc', lambda _: True)
+
+        with pytest.raises(errors.Error):
+            cygwin.MinGW32Compiler()
+
+    @pytest.mark.skipif('sys.platform == "cygwin"')
+    def test_customize_compiler_with_msvc_python(self):
+        # In case we have an MSVC Python build, but still want to use
+        # MinGW32Compiler, then customize_compiler() shouldn't fail at least.
+        # https://github.com/pypa/setuptools/issues/4456
+        compiler = cygwin.MinGW32Compiler()
+        sysconfig.customize_compiler(compiler)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py
new file mode 100644
index 0000000..eca8319
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py
@@ -0,0 +1,136 @@
+import os
+import sys
+import sysconfig
+import threading
+import unittest.mock as mock
+from distutils.errors import DistutilsPlatformError
+from distutils.tests import support
+from distutils.util import get_platform
+
+import pytest
+
+from .. import msvc
+
+needs_winreg = pytest.mark.skipif('not hasattr(msvc, "winreg")')
+
+
+class Testmsvccompiler(support.TempdirManager):
+    def test_no_compiler(self, monkeypatch):
+        # makes sure query_vcvarsall raises
+        # a DistutilsPlatformError if the compiler
+        # is not found
+        def _find_vcvarsall(plat_spec):
+            return None, None
+
+        monkeypatch.setattr(msvc, '_find_vcvarsall', _find_vcvarsall)
+
+        with pytest.raises(DistutilsPlatformError):
+            msvc._get_vc_env(
+                'wont find this version',
+            )
+
+    @pytest.mark.skipif(
+        not sysconfig.get_platform().startswith("win"),
+        reason="Only run test for non-mingw Windows platforms",
+    )
+    @pytest.mark.parametrize(
+        "plat_name, expected",
+        [
+            ("win-arm64", "win-arm64"),
+            ("win-amd64", "win-amd64"),
+            (None, get_platform()),
+        ],
+    )
+    def test_cross_platform_compilation_paths(self, monkeypatch, plat_name, expected):
+        """
+        Ensure a specified target platform is passed to _get_vcvars_spec.
+        """
+        compiler = msvc.Compiler()
+
+        def _get_vcvars_spec(host_platform, platform):
+            assert platform == expected
+
+        monkeypatch.setattr(msvc, '_get_vcvars_spec', _get_vcvars_spec)
+        compiler.initialize(plat_name)
+
+    @needs_winreg
+    def test_get_vc_env_unicode(self):
+        test_var = 'ṰḖṤṪ┅ṼẨṜ'
+        test_value = '₃⁴₅'
+
+        # Ensure we don't early exit from _get_vc_env
+        old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None)
+        os.environ[test_var] = test_value
+        try:
+            env = msvc._get_vc_env('x86')
+            assert test_var.lower() in env
+            assert test_value == env[test_var.lower()]
+        finally:
+            os.environ.pop(test_var)
+            if old_distutils_use_sdk:
+                os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
+
+    @needs_winreg
+    @pytest.mark.parametrize('ver', (2015, 2017))
+    def test_get_vc(self, ver):
+        # This function cannot be mocked, so pass if VC is found
+        # and skip otherwise.
+        lookup = getattr(msvc, f'_find_vc{ver}')
+        expected_version = {2015: 14, 2017: 15}[ver]
+        version, path = lookup()
+        if not version:
+            pytest.skip(f"VS {ver} is not installed")
+        assert version >= expected_version
+        assert os.path.isdir(path)
+
+
+class CheckThread(threading.Thread):
+    exc_info = None
+
+    def run(self):
+        try:
+            super().run()
+        except Exception:
+            self.exc_info = sys.exc_info()
+
+    def __bool__(self):
+        return not self.exc_info
+
+
+class TestSpawn:
+    def test_concurrent_safe(self):
+        """
+        Concurrent calls to spawn should have consistent results.
+        """
+        compiler = msvc.Compiler()
+        compiler._paths = "expected"
+        inner_cmd = 'import os; assert os.environ["PATH"] == "expected"'
+        command = [sys.executable, '-c', inner_cmd]
+
+        threads = [
+            CheckThread(target=compiler.spawn, args=[command]) for n in range(100)
+        ]
+        for thread in threads:
+            thread.start()
+        for thread in threads:
+            thread.join()
+        assert all(threads)
+
+    def test_concurrent_safe_fallback(self):
+        """
+        If CCompiler.spawn has been monkey-patched without support
+        for an env, it should still execute.
+        """
+        from distutils import ccompiler
+
+        compiler = msvc.Compiler()
+        compiler._paths = "expected"
+
+        def CCompiler_spawn(self, cmd):
+            "A spawn without an env argument."
+            assert os.environ["PATH"] == "expected"
+
+        with mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
+            compiler.spawn(["n/a"])
+
+        assert os.environ.get("PATH") != "expected"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py
new file mode 100644
index 0000000..35b6b0e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py
@@ -0,0 +1,413 @@
+"""Tests for distutils.unixccompiler."""
+
+import os
+import sys
+import unittest.mock as mock
+from distutils import sysconfig
+from distutils.compat import consolidate_linker_args
+from distutils.errors import DistutilsPlatformError
+from distutils.tests import support
+from distutils.tests.compat.py39 import EnvironmentVarGuard
+from distutils.util import _clear_cached_macosx_ver
+
+import pytest
+
+from .. import unix
+
+
+@pytest.fixture(autouse=True)
+def save_values(monkeypatch):
+    monkeypatch.setattr(sys, 'platform', sys.platform)
+    monkeypatch.setattr(sysconfig, 'get_config_var', sysconfig.get_config_var)
+    monkeypatch.setattr(sysconfig, 'get_config_vars', sysconfig.get_config_vars)
+
+
+@pytest.fixture(autouse=True)
+def compiler_wrapper(request):
+    class CompilerWrapper(unix.Compiler):
+        def rpath_foo(self):
+            return self.runtime_library_dir_option('/foo')
+
+    request.instance.cc = CompilerWrapper()
+
+
+class TestUnixCCompiler(support.TempdirManager):
+    @pytest.mark.skipif('platform.system == "Windows"')
+    def test_runtime_libdir_option(self):  # noqa: C901
+        # Issue #5900; GitHub Issue #37
+        #
+        # Ensure RUNPATH is added to extension modules with RPATH if
+        # GNU ld is used
+
+        # darwin
+        sys.platform = 'darwin'
+        darwin_ver_var = 'MACOSX_DEPLOYMENT_TARGET'
+        darwin_rpath_flag = '-Wl,-rpath,/foo'
+        darwin_lib_flag = '-L/foo'
+
+        # (macOS version from syscfg, macOS version from env var) -> flag
+        # Version value of None generates two tests: as None and as empty string
+        # Expected flag value of None means an mismatch exception is expected
+        darwin_test_cases = [
+            ((None, None), darwin_lib_flag),
+            ((None, '11'), darwin_rpath_flag),
+            (('10', None), darwin_lib_flag),
+            (('10.3', None), darwin_lib_flag),
+            (('10.3.1', None), darwin_lib_flag),
+            (('10.5', None), darwin_rpath_flag),
+            (('10.5.1', None), darwin_rpath_flag),
+            (('10.3', '10.3'), darwin_lib_flag),
+            (('10.3', '10.5'), darwin_rpath_flag),
+            (('10.5', '10.3'), darwin_lib_flag),
+            (('10.5', '11'), darwin_rpath_flag),
+            (('10.4', '10'), None),
+        ]
+
+        def make_darwin_gcv(syscfg_macosx_ver):
+            def gcv(var):
+                if var == darwin_ver_var:
+                    return syscfg_macosx_ver
+                return "xxx"
+
+            return gcv
+
+        def do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag):
+            env = os.environ
+            msg = f"macOS version = (sysconfig={syscfg_macosx_ver!r}, env={env_macosx_ver!r})"
+
+            # Save
+            old_gcv = sysconfig.get_config_var
+            old_env_macosx_ver = env.get(darwin_ver_var)
+
+            # Setup environment
+            _clear_cached_macosx_ver()
+            sysconfig.get_config_var = make_darwin_gcv(syscfg_macosx_ver)
+            if env_macosx_ver is not None:
+                env[darwin_ver_var] = env_macosx_ver
+            elif darwin_ver_var in env:
+                env.pop(darwin_ver_var)
+
+            # Run the test
+            if expected_flag is not None:
+                assert self.cc.rpath_foo() == expected_flag, msg
+            else:
+                with pytest.raises(
+                    DistutilsPlatformError, match=darwin_ver_var + r' mismatch'
+                ):
+                    self.cc.rpath_foo()
+
+            # Restore
+            if old_env_macosx_ver is not None:
+                env[darwin_ver_var] = old_env_macosx_ver
+            elif darwin_ver_var in env:
+                env.pop(darwin_ver_var)
+            sysconfig.get_config_var = old_gcv
+            _clear_cached_macosx_ver()
+
+        for macosx_vers, expected_flag in darwin_test_cases:
+            syscfg_macosx_ver, env_macosx_ver = macosx_vers
+            do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag)
+            # Bonus test cases with None interpreted as empty string
+            if syscfg_macosx_ver is None:
+                do_darwin_test("", env_macosx_ver, expected_flag)
+            if env_macosx_ver is None:
+                do_darwin_test(syscfg_macosx_ver, "", expected_flag)
+            if syscfg_macosx_ver is None and env_macosx_ver is None:
+                do_darwin_test("", "", expected_flag)
+
+        old_gcv = sysconfig.get_config_var
+
+        # hp-ux
+        sys.platform = 'hp-ux'
+
+        def gcv(v):
+            return 'xxx'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == ['+s', '-L/foo']
+
+        def gcv(v):
+            return 'gcc'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
+
+        def gcv(v):
+            return 'g++'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
+
+        sysconfig.get_config_var = old_gcv
+
+        # GCC GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'gcc'
+            elif v == 'GNULD':
+                return 'yes'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == consolidate_linker_args([
+            '-Wl,--enable-new-dtags',
+            '-Wl,-rpath,/foo',
+        ])
+
+        def gcv(v):
+            if v == 'CC':
+                return 'gcc -pthread -B /bar'
+            elif v == 'GNULD':
+                return 'yes'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == consolidate_linker_args([
+            '-Wl,--enable-new-dtags',
+            '-Wl,-rpath,/foo',
+        ])
+
+        # GCC non-GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'gcc'
+            elif v == 'GNULD':
+                return 'no'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == '-Wl,-R/foo'
+
+        # GCC GNULD with fully qualified configuration prefix
+        # see #7617
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'x86_64-pc-linux-gnu-gcc-4.4.2'
+            elif v == 'GNULD':
+                return 'yes'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == consolidate_linker_args([
+            '-Wl,--enable-new-dtags',
+            '-Wl,-rpath,/foo',
+        ])
+
+        # non-GCC GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'cc'
+            elif v == 'GNULD':
+                return 'yes'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == consolidate_linker_args([
+            '-Wl,--enable-new-dtags',
+            '-Wl,-rpath,/foo',
+        ])
+
+        # non-GCC non-GNULD
+        sys.platform = 'bar'
+
+        def gcv(v):
+            if v == 'CC':
+                return 'cc'
+            elif v == 'GNULD':
+                return 'no'
+
+        sysconfig.get_config_var = gcv
+        assert self.cc.rpath_foo() == '-Wl,-R/foo'
+
+    @pytest.mark.skipif('platform.system == "Windows"')
+    def test_cc_overrides_ldshared(self):
+        # Issue #18080:
+        # ensure that setting CC env variable also changes default linker
+        def gcv(v):
+            if v == 'LDSHARED':
+                return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+            return 'gcc-4.2'
+
+        def gcvs(*args, _orig=sysconfig.get_config_vars):
+            if args:
+                return list(map(sysconfig.get_config_var, args))
+            return _orig()
+
+        sysconfig.get_config_var = gcv
+        sysconfig.get_config_vars = gcvs
+        with EnvironmentVarGuard() as env:
+            env['CC'] = 'my_cc'
+            del env['LDSHARED']
+            sysconfig.customize_compiler(self.cc)
+        assert self.cc.linker_so[0] == 'my_cc'
+
+    @pytest.mark.skipif('platform.system == "Windows"')
+    def test_cxx_commands_used_are_correct(self):
+        def gcv(v):
+            if v == 'LDSHARED':
+                return 'ccache gcc-4.2 -bundle -undefined dynamic_lookup'
+            elif v == 'LDCXXSHARED':
+                return 'ccache g++-4.2 -bundle -undefined dynamic_lookup'
+            elif v == 'CXX':
+                return 'ccache g++-4.2'
+            elif v == 'CC':
+                return 'ccache gcc-4.2'
+            return ''
+
+        def gcvs(*args, _orig=sysconfig.get_config_vars):
+            if args:
+                return list(map(sysconfig.get_config_var, args))
+            return _orig()  # pragma: no cover
+
+        sysconfig.get_config_var = gcv
+        sysconfig.get_config_vars = gcvs
+        with (
+            mock.patch.object(self.cc, 'spawn', return_value=None) as mock_spawn,
+            mock.patch.object(self.cc, '_need_link', return_value=True),
+            mock.patch.object(self.cc, 'mkpath', return_value=None),
+            EnvironmentVarGuard() as env,
+        ):
+            # override environment overrides in case they're specified by CI
+            del env['CXX']
+            del env['LDCXXSHARED']
+
+            sysconfig.customize_compiler(self.cc)
+            assert self.cc.linker_so_cxx[0:2] == ['ccache', 'g++-4.2']
+            assert self.cc.linker_exe_cxx[0:2] == ['ccache', 'g++-4.2']
+            self.cc.link(None, [], 'a.out', target_lang='c++')
+            call_args = mock_spawn.call_args[0][0]
+            expected = ['ccache', 'g++-4.2', '-bundle', '-undefined', 'dynamic_lookup']
+            assert call_args[:5] == expected
+
+            self.cc.link_executable([], 'a.out', target_lang='c++')
+            call_args = mock_spawn.call_args[0][0]
+            expected = ['ccache', 'g++-4.2', '-o', self.cc.executable_filename('a.out')]
+            assert call_args[:4] == expected
+
+            env['LDCXXSHARED'] = 'wrapper g++-4.2 -bundle -undefined dynamic_lookup'
+            env['CXX'] = 'wrapper g++-4.2'
+            sysconfig.customize_compiler(self.cc)
+            assert self.cc.linker_so_cxx[0:2] == ['wrapper', 'g++-4.2']
+            assert self.cc.linker_exe_cxx[0:2] == ['wrapper', 'g++-4.2']
+            self.cc.link(None, [], 'a.out', target_lang='c++')
+            call_args = mock_spawn.call_args[0][0]
+            expected = ['wrapper', 'g++-4.2', '-bundle', '-undefined', 'dynamic_lookup']
+            assert call_args[:5] == expected
+
+            self.cc.link_executable([], 'a.out', target_lang='c++')
+            call_args = mock_spawn.call_args[0][0]
+            expected = [
+                'wrapper',
+                'g++-4.2',
+                '-o',
+                self.cc.executable_filename('a.out'),
+            ]
+            assert call_args[:4] == expected
+
+    @pytest.mark.skipif('platform.system == "Windows"')
+    @pytest.mark.usefixtures('disable_macos_customization')
+    def test_cc_overrides_ldshared_for_cxx_correctly(self):
+        """
+        Ensure that setting CC env variable also changes default linker
+        correctly when building C++ extensions.
+
+        pypa/distutils#126
+        """
+
+        def gcv(v):
+            if v == 'LDSHARED':
+                return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+            elif v == 'LDCXXSHARED':
+                return 'g++-4.2 -bundle -undefined dynamic_lookup '
+            elif v == 'CXX':
+                return 'g++-4.2'
+            elif v == 'CC':
+                return 'gcc-4.2'
+            return ''
+
+        def gcvs(*args, _orig=sysconfig.get_config_vars):
+            if args:
+                return list(map(sysconfig.get_config_var, args))
+            return _orig()
+
+        sysconfig.get_config_var = gcv
+        sysconfig.get_config_vars = gcvs
+        with (
+            mock.patch.object(self.cc, 'spawn', return_value=None) as mock_spawn,
+            mock.patch.object(self.cc, '_need_link', return_value=True),
+            mock.patch.object(self.cc, 'mkpath', return_value=None),
+            EnvironmentVarGuard() as env,
+        ):
+            env['CC'] = 'ccache my_cc'
+            env['CXX'] = 'my_cxx'
+            del env['LDSHARED']
+            sysconfig.customize_compiler(self.cc)
+            assert self.cc.linker_so[0:2] == ['ccache', 'my_cc']
+            self.cc.link(None, [], 'a.out', target_lang='c++')
+            call_args = mock_spawn.call_args[0][0]
+            expected = ['my_cxx', '-bundle', '-undefined', 'dynamic_lookup']
+            assert call_args[:4] == expected
+
+    @pytest.mark.skipif('platform.system == "Windows"')
+    def test_explicit_ldshared(self):
+        # Issue #18080:
+        # ensure that setting CC env variable does not change
+        #   explicit LDSHARED setting for linker
+        def gcv(v):
+            if v == 'LDSHARED':
+                return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+            return 'gcc-4.2'
+
+        def gcvs(*args, _orig=sysconfig.get_config_vars):
+            if args:
+                return list(map(sysconfig.get_config_var, args))
+            return _orig()
+
+        sysconfig.get_config_var = gcv
+        sysconfig.get_config_vars = gcvs
+        with EnvironmentVarGuard() as env:
+            env['CC'] = 'my_cc'
+            env['LDSHARED'] = 'my_ld -bundle -dynamic'
+            sysconfig.customize_compiler(self.cc)
+        assert self.cc.linker_so[0] == 'my_ld'
+
+    def test_has_function(self):
+        # Issue https://github.com/pypa/distutils/issues/64:
+        # ensure that setting output_dir does not raise
+        # FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
+        self.cc.output_dir = 'scratch'
+        os.chdir(self.mkdtemp())
+        self.cc.has_function('abort')
+
+    def test_find_library_file(self, monkeypatch):
+        compiler = unix.Compiler()
+        compiler._library_root = lambda dir: dir
+        monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d)
+
+        libname = 'libabc.dylib' if sys.platform != 'cygwin' else 'cygabc.dll'
+        dirs = ('/foo/bar/missing', '/foo/bar/existing')
+        assert (
+            compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+            == f'/foo/bar/existing/{libname}'
+        )
+        assert (
+            compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+            == f'/foo/bar/existing/{libname}'
+        )
+
+        monkeypatch.setattr(
+            os.path,
+            'exists',
+            lambda d: 'existing' in d and '.a' in d and '.dll.a' not in d,
+        )
+        assert (
+            compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+            == '/foo/bar/existing/libabc.a'
+        )
+        assert (
+            compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+            == '/foo/bar/existing/libabc.a'
+        )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py
new file mode 100644
index 0000000..1231b32
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py
@@ -0,0 +1,422 @@
+"""distutils.unixccompiler
+
+Contains the UnixCCompiler class, a subclass of CCompiler that handles
+the "typical" Unix-style command-line C compiler:
+  * macros defined with -Dname[=value]
+  * macros undefined with -Uname
+  * include search directories specified with -Idir
+  * libraries specified with -lllib
+  * library search directories specified with -Ldir
+  * compile handled by 'cc' (or similar) executable with -c option:
+    compiles .c to .o
+  * link static library handled by 'ar' command (possibly with 'ranlib')
+  * link shared library handled by 'cc -shared'
+"""
+
+from __future__ import annotations
+
+import itertools
+import os
+import re
+import shlex
+import sys
+from collections.abc import Iterable
+
+from ... import sysconfig
+from ..._log import log
+from ..._macos_compat import compiler_fixup
+from ..._modified import newer
+from ...compat import consolidate_linker_args
+from ...errors import DistutilsExecError
+from . import base
+from .base import _Macro, gen_lib_options, gen_preprocess_options
+from .errors import (
+    CompileError,
+    LibError,
+    LinkError,
+)
+
+# XXX Things not currently handled:
+#   * optimization/debug/warning flags; we just use whatever's in Python's
+#     Makefile and live with it.  Is this adequate?  If not, we might
+#     have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
+#     SunCCompiler, and I suspect down that road lies madness.
+#   * even if we don't know a warning flag from an optimization flag,
+#     we need some way for outsiders to feed preprocessor/compiler/linker
+#     flags in to us -- eg. a sysadmin might want to mandate certain flags
+#     via a site config file, or a user might want to set something for
+#     compiling this module distribution only via the setup.py command
+#     line, whatever.  As long as these options come from something on the
+#     current system, they can be as system-dependent as they like, and we
+#     should just happily stuff them into the preprocessor/compiler/linker
+#     options and carry on.
+
+
+def _split_env(cmd):
+    """
+    For macOS, split command into 'env' portion (if any)
+    and the rest of the linker command.
+
+    >>> _split_env(['a', 'b', 'c'])
+    ([], ['a', 'b', 'c'])
+    >>> _split_env(['/usr/bin/env', 'A=3', 'gcc'])
+    (['/usr/bin/env', 'A=3'], ['gcc'])
+    """
+    pivot = 0
+    if os.path.basename(cmd[0]) == "env":
+        pivot = 1
+        while '=' in cmd[pivot]:
+            pivot += 1
+    return cmd[:pivot], cmd[pivot:]
+
+
+def _split_aix(cmd):
+    """
+    AIX platforms prefix the compiler with the ld_so_aix
+    script, so split that from the linker command.
+
+    >>> _split_aix(['a', 'b', 'c'])
+    ([], ['a', 'b', 'c'])
+    >>> _split_aix(['/bin/foo/ld_so_aix', 'gcc'])
+    (['/bin/foo/ld_so_aix'], ['gcc'])
+    """
+    pivot = os.path.basename(cmd[0]) == 'ld_so_aix'
+    return cmd[:pivot], cmd[pivot:]
+
+
+def _linker_params(linker_cmd, compiler_cmd):
+    """
+    The linker command usually begins with the compiler
+    command (possibly multiple elements), followed by zero or more
+    params for shared library building.
+
+    If the LDSHARED env variable overrides the linker command,
+    however, the commands may not match.
+
+    Return the best guess of the linker parameters by stripping
+    the linker command. If the compiler command does not
+    match the linker command, assume the linker command is
+    just the first element.
+
+    >>> _linker_params('gcc foo bar'.split(), ['gcc'])
+    ['foo', 'bar']
+    >>> _linker_params('gcc foo bar'.split(), ['other'])
+    ['foo', 'bar']
+    >>> _linker_params('ccache gcc foo bar'.split(), 'ccache gcc'.split())
+    ['foo', 'bar']
+    >>> _linker_params(['gcc'], ['gcc'])
+    []
+    """
+    c_len = len(compiler_cmd)
+    pivot = c_len if linker_cmd[:c_len] == compiler_cmd else 1
+    return linker_cmd[pivot:]
+
+
+class Compiler(base.Compiler):
+    compiler_type = 'unix'
+
+    # These are used by CCompiler in two places: the constructor sets
+    # instance attributes 'preprocessor', 'compiler', etc. from them, and
+    # 'set_executable()' allows any of these to be set.  The defaults here
+    # are pretty generic; they will probably have to be set by an outsider
+    # (eg. using information discovered by the sysconfig about building
+    # Python extensions).
+    executables = {
+        'preprocessor': None,
+        'compiler': ["cc"],
+        'compiler_so': ["cc"],
+        'compiler_cxx': ["c++"],
+        'compiler_so_cxx': ["c++"],
+        'linker_so': ["cc", "-shared"],
+        'linker_so_cxx': ["c++", "-shared"],
+        'linker_exe': ["cc"],
+        'linker_exe_cxx': ["c++", "-shared"],
+        'archiver': ["ar", "-cr"],
+        'ranlib': None,
+    }
+
+    if sys.platform[:6] == "darwin":
+        executables['ranlib'] = ["ranlib"]
+
+    # Needed for the filename generation methods provided by the base
+    # class, CCompiler.  NB. whoever instantiates/uses a particular
+    # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
+    # reasonable common default here, but it's not necessarily used on all
+    # Unices!
+
+    src_extensions = [".c", ".C", ".cc", ".cxx", ".cpp", ".m"]
+    obj_extension = ".o"
+    static_lib_extension = ".a"
+    shared_lib_extension = ".so"
+    dylib_lib_extension = ".dylib"
+    xcode_stub_lib_extension = ".tbd"
+    static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
+    xcode_stub_lib_format = dylib_lib_format
+    if sys.platform == "cygwin":
+        exe_extension = ".exe"
+        shared_lib_extension = ".dll.a"
+        dylib_lib_extension = ".dll"
+        dylib_lib_format = "cyg%s%s"
+
+    def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+        """Remove standard library path from rpath"""
+        libraries, library_dirs, runtime_library_dirs = super()._fix_lib_args(
+            libraries, library_dirs, runtime_library_dirs
+        )
+        libdir = sysconfig.get_config_var('LIBDIR')
+        if (
+            runtime_library_dirs
+            and libdir.startswith("/usr/lib")
+            and (libdir in runtime_library_dirs)
+        ):
+            runtime_library_dirs.remove(libdir)
+        return libraries, library_dirs, runtime_library_dirs
+
+    def preprocess(
+        self,
+        source: str | os.PathLike[str],
+        output_file: str | os.PathLike[str] | None = None,
+        macros: list[_Macro] | None = None,
+        include_dirs: list[str] | tuple[str, ...] | None = None,
+        extra_preargs: list[str] | None = None,
+        extra_postargs: Iterable[str] | None = None,
+    ):
+        fixed_args = self._fix_compile_args(None, macros, include_dirs)
+        ignore, macros, include_dirs = fixed_args
+        pp_opts = gen_preprocess_options(macros, include_dirs)
+        pp_args = self.preprocessor + pp_opts
+        if output_file:
+            pp_args.extend(['-o', output_file])
+        if extra_preargs:
+            pp_args[:0] = extra_preargs
+        if extra_postargs:
+            pp_args.extend(extra_postargs)
+        pp_args.append(source)
+
+        # reasons to preprocess:
+        # - force is indicated
+        # - output is directed to stdout
+        # - source file is newer than the target
+        preprocess = self.force or output_file is None or newer(source, output_file)
+        if not preprocess:
+            return
+
+        if output_file:
+            self.mkpath(os.path.dirname(output_file))
+
+        try:
+            self.spawn(pp_args)
+        except DistutilsExecError as msg:
+            raise CompileError(msg)
+
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs)
+        compiler_so_cxx = compiler_fixup(self.compiler_so_cxx, cc_args + extra_postargs)
+        try:
+            if self.detect_language(src) == 'c++':
+                self.spawn(
+                    compiler_so_cxx + cc_args + [src, '-o', obj] + extra_postargs
+                )
+            else:
+                self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs)
+        except DistutilsExecError as msg:
+            raise CompileError(msg)
+
+    def create_static_lib(
+        self, objects, output_libname, output_dir=None, debug=False, target_lang=None
+    ):
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+
+        output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+        if self._need_link(objects, output_filename):
+            self.mkpath(os.path.dirname(output_filename))
+            self.spawn(self.archiver + [output_filename] + objects + self.objects)
+
+            # Not many Unices required ranlib anymore -- SunOS 4.x is, I
+            # think the only major Unix that does.  Maybe we need some
+            # platform intelligence here to skip ranlib if it's not
+            # needed -- or maybe Python's configure script took care of
+            # it for us, hence the check for leading colon.
+            if self.ranlib:
+                try:
+                    self.spawn(self.ranlib + [output_filename])
+                except DistutilsExecError as msg:
+                    raise LibError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    def link(
+        self,
+        target_desc,
+        objects: list[str] | tuple[str, ...],
+        output_filename,
+        output_dir: str | None = None,
+        libraries: list[str] | tuple[str, ...] | None = None,
+        library_dirs: list[str] | tuple[str, ...] | None = None,
+        runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+        export_symbols=None,
+        debug=False,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ):
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+        libraries, library_dirs, runtime_library_dirs = fixed_args
+
+        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+        if not isinstance(output_dir, (str, type(None))):
+            raise TypeError("'output_dir' must be a string or None")
+        if output_dir is not None:
+            output_filename = os.path.join(output_dir, output_filename)
+
+        if self._need_link(objects, output_filename):
+            ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
+            if debug:
+                ld_args[:0] = ['-g']
+            if extra_preargs:
+                ld_args[:0] = extra_preargs
+            if extra_postargs:
+                ld_args.extend(extra_postargs)
+            self.mkpath(os.path.dirname(output_filename))
+            try:
+                # Select a linker based on context: linker_exe when
+                # building an executable or linker_so (with shared options)
+                # when building a shared library.
+                building_exe = target_desc == base.Compiler.EXECUTABLE
+                target_cxx = target_lang == "c++"
+                linker = (
+                    (self.linker_exe_cxx if target_cxx else self.linker_exe)
+                    if building_exe
+                    else (self.linker_so_cxx if target_cxx else self.linker_so)
+                )[:]
+
+                if target_cxx and self.compiler_cxx:
+                    env, linker_ne = _split_env(linker)
+                    aix, linker_na = _split_aix(linker_ne)
+                    _, compiler_cxx_ne = _split_env(self.compiler_cxx)
+                    _, linker_exe_ne = _split_env(self.linker_exe_cxx)
+
+                    params = _linker_params(linker_na, linker_exe_ne)
+                    linker = env + aix + compiler_cxx_ne + params
+
+                linker = compiler_fixup(linker, ld_args)
+
+                self.spawn(linker + ld_args)
+            except DistutilsExecError as msg:
+                raise LinkError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    # -- Miscellaneous methods -----------------------------------------
+    # These are all used by the 'gen_lib_options() function, in
+    # ccompiler.py.
+
+    def library_dir_option(self, dir):
+        return "-L" + dir
+
+    def _is_gcc(self):
+        cc_var = sysconfig.get_config_var("CC")
+        compiler = os.path.basename(shlex.split(cc_var)[0])
+        return "gcc" in compiler or "g++" in compiler
+
+    def runtime_library_dir_option(self, dir: str) -> str | list[str]:  # type: ignore[override] # Fixed in pypa/distutils#339
+        # XXX Hackish, at the very least.  See Python bug #445902:
+        # https://bugs.python.org/issue445902
+        # Linkers on different platforms need different options to
+        # specify that directories need to be added to the list of
+        # directories searched for dependencies when a dynamic library
+        # is sought.  GCC on GNU systems (Linux, FreeBSD, ...) has to
+        # be told to pass the -R option through to the linker, whereas
+        # other compilers and gcc on other systems just know this.
+        # Other compilers may need something slightly different.  At
+        # this time, there's no way to determine this information from
+        # the configuration data stored in the Python installation, so
+        # we use this hack.
+        if sys.platform[:6] == "darwin":
+            from distutils.util import get_macosx_target_ver, split_version
+
+            macosx_target_ver = get_macosx_target_ver()
+            if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
+                return "-Wl,-rpath," + dir
+            else:  # no support for -rpath on earlier macOS versions
+                return "-L" + dir
+        elif sys.platform[:7] == "freebsd":
+            return "-Wl,-rpath=" + dir
+        elif sys.platform[:5] == "hp-ux":
+            return [
+                "-Wl,+s" if self._is_gcc() else "+s",
+                "-L" + dir,
+            ]
+
+        # For all compilers, `-Wl` is the presumed way to pass a
+        # compiler option to the linker
+        if sysconfig.get_config_var("GNULD") == "yes":
+            return consolidate_linker_args([
+                # Force RUNPATH instead of RPATH
+                "-Wl,--enable-new-dtags",
+                "-Wl,-rpath," + dir,
+            ])
+        else:
+            return "-Wl,-R" + dir
+
+    def library_option(self, lib):
+        return "-l" + lib
+
+    @staticmethod
+    def _library_root(dir):
+        """
+        macOS users can specify an alternate SDK using'-isysroot'.
+        Calculate the SDK root if it is specified.
+
+        Note that, as of Xcode 7, Apple SDKs may contain textual stub
+        libraries with .tbd extensions rather than the normal .dylib
+        shared libraries installed in /.  The Apple compiler tool
+        chain handles this transparently but it can cause problems
+        for programs that are being built with an SDK and searching
+        for specific libraries.  Callers of find_library_file need to
+        keep in mind that the base filename of the returned SDK library
+        file might have a different extension from that of the library
+        file installed on the running system, for example:
+          /Applications/Xcode.app/Contents/Developer/Platforms/
+              MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
+              usr/lib/libedit.tbd
+        vs
+          /usr/lib/libedit.dylib
+        """
+        cflags = sysconfig.get_config_var('CFLAGS')
+        match = re.search(r'-isysroot\s*(\S+)', cflags)
+
+        apply_root = (
+            sys.platform == 'darwin'
+            and match
+            and (
+                dir.startswith('/System/')
+                or (dir.startswith('/usr/') and not dir.startswith('/usr/local/'))
+            )
+        )
+
+        return os.path.join(match.group(1), dir[1:]) if apply_root else dir
+
+    def find_library_file(self, dirs, lib, debug=False):
+        """
+        Second-guess the linker with not much hard
+        data to go on: GCC seems to prefer the shared library, so
+        assume that *all* Unix C compilers do,
+        ignoring even GCC's "-static" option.
+        """
+        lib_names = (
+            self.library_filename(lib, lib_type=type)
+            for type in 'dylib xcode_stub shared static'.split()
+        )
+
+        roots = map(self._library_root, dirs)
+
+        searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names))
+
+        found = filter(os.path.exists, searched)
+
+        # Return None if it could not be found in any dir.
+        return next(found, None)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py
new file mode 100644
index 0000000..681c631
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py
@@ -0,0 +1,230 @@
+"""distutils.zosccompiler
+
+Contains the selection of the c & c++ compilers on z/OS. There are several
+different c compilers on z/OS, all of them are optional, so the correct
+one needs to be chosen based on the users input. This is compatible with
+the following compilers:
+
+IBM C/C++ For Open Enterprise Languages on z/OS 2.0
+IBM Open XL C/C++ 1.1 for z/OS
+IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5
+IBM z/OS XL C/C++
+"""
+
+import os
+
+from ... import sysconfig
+from ...errors import DistutilsExecError
+from . import unix
+from .errors import CompileError
+
+_cc_args = {
+    'ibm-openxl': [
+        '-m64',
+        '-fvisibility=default',
+        '-fzos-le-char-mode=ascii',
+        '-fno-short-enums',
+    ],
+    'ibm-xlclang': [
+        '-q64',
+        '-qexportall',
+        '-qascii',
+        '-qstrict',
+        '-qnocsect',
+        '-Wa,asa,goff',
+        '-Wa,xplink',
+        '-qgonumber',
+        '-qenum=int',
+        '-Wc,DLL',
+    ],
+    'ibm-xlc': [
+        '-q64',
+        '-qexportall',
+        '-qascii',
+        '-qstrict',
+        '-qnocsect',
+        '-Wa,asa,goff',
+        '-Wa,xplink',
+        '-qgonumber',
+        '-qenum=int',
+        '-Wc,DLL',
+        '-qlanglvl=extc99',
+    ],
+}
+
+_cxx_args = {
+    'ibm-openxl': [
+        '-m64',
+        '-fvisibility=default',
+        '-fzos-le-char-mode=ascii',
+        '-fno-short-enums',
+    ],
+    'ibm-xlclang': [
+        '-q64',
+        '-qexportall',
+        '-qascii',
+        '-qstrict',
+        '-qnocsect',
+        '-Wa,asa,goff',
+        '-Wa,xplink',
+        '-qgonumber',
+        '-qenum=int',
+        '-Wc,DLL',
+    ],
+    'ibm-xlc': [
+        '-q64',
+        '-qexportall',
+        '-qascii',
+        '-qstrict',
+        '-qnocsect',
+        '-Wa,asa,goff',
+        '-Wa,xplink',
+        '-qgonumber',
+        '-qenum=int',
+        '-Wc,DLL',
+        '-qlanglvl=extended0x',
+    ],
+}
+
+_asm_args = {
+    'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'],
+    'ibm-xlclang': [],
+    'ibm-xlc': [],
+}
+
+_ld_args = {
+    'ibm-openxl': [],
+    'ibm-xlclang': ['-Wl,dll', '-q64'],
+    'ibm-xlc': ['-Wl,dll', '-q64'],
+}
+
+
+# Python on z/OS is built with no compiler specific options in it's CFLAGS.
+# But each compiler requires it's own specific options to build successfully,
+# though some of the options are common between them
+class Compiler(unix.Compiler):
+    src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx', '.C']
+    _asm_extensions = ['.s']
+
+    def _get_zos_compiler_name(self):
+        zos_compiler_names = [
+            os.path.basename(binary)
+            for envvar in ('CC', 'CXX', 'LDSHARED')
+            if (binary := os.environ.get(envvar, None))
+        ]
+        if len(zos_compiler_names) == 0:
+            return 'ibm-openxl'
+
+        zos_compilers = {}
+        for compiler in (
+            'ibm-clang',
+            'ibm-clang64',
+            'ibm-clang++',
+            'ibm-clang++64',
+            'clang',
+            'clang++',
+            'clang-14',
+        ):
+            zos_compilers[compiler] = 'ibm-openxl'
+
+        for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'):
+            zos_compilers[compiler] = 'ibm-xlclang'
+
+        for compiler in ('xlc', 'xlC', 'xlc++'):
+            zos_compilers[compiler] = 'ibm-xlc'
+
+        return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl')
+
+    def __init__(self, verbose=False, force=False):
+        super().__init__(verbose, force=force)
+        self.zos_compiler = self._get_zos_compiler_name()
+        sysconfig.customize_compiler(self)
+
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        local_args = []
+        if ext in self._cpp_extensions:
+            compiler = self.compiler_cxx
+            local_args.extend(_cxx_args[self.zos_compiler])
+        elif ext in self._asm_extensions:
+            compiler = self.compiler_so
+            local_args.extend(_cc_args[self.zos_compiler])
+            local_args.extend(_asm_args[self.zos_compiler])
+        else:
+            compiler = self.compiler_so
+            local_args.extend(_cc_args[self.zos_compiler])
+        local_args.extend(cc_args)
+
+        try:
+            self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs)
+        except DistutilsExecError as msg:
+            raise CompileError(msg)
+
+    def runtime_library_dir_option(self, dir):
+        return '-L' + dir
+
+    def link(
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        export_symbols=None,
+        debug=False,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ):
+        # For a built module to use functions from cpython, it needs to use Pythons
+        # side deck file. The side deck is located beside the libpython3.xx.so
+        ldversion = sysconfig.get_config_var('LDVERSION')
+        if sysconfig.python_build:
+            side_deck_path = os.path.join(
+                sysconfig.get_config_var('abs_builddir'),
+                f'libpython{ldversion}.x',
+            )
+        else:
+            side_deck_path = os.path.join(
+                sysconfig.get_config_var('installed_base'),
+                sysconfig.get_config_var('platlibdir'),
+                f'libpython{ldversion}.x',
+            )
+
+        if os.path.exists(side_deck_path):
+            if extra_postargs:
+                extra_postargs.append(side_deck_path)
+            else:
+                extra_postargs = [side_deck_path]
+
+        # Check and replace libraries included side deck files
+        if runtime_library_dirs:
+            for dir in runtime_library_dirs:
+                for library in libraries[:]:
+                    library_side_deck = os.path.join(dir, f'{library}.x')
+                    if os.path.exists(library_side_deck):
+                        libraries.remove(library)
+                        extra_postargs.append(library_side_deck)
+                        break
+
+        # Any required ld args for the given compiler
+        extra_postargs.extend(_ld_args[self.zos_compiler])
+
+        super().link(
+            target_desc,
+            objects,
+            output_filename,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/core.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/core.py
new file mode 100644
index 0000000..bd62546
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/core.py
@@ -0,0 +1,289 @@
+"""distutils.core
+
+The only module that needs to be imported to use the Distutils; provides
+the 'setup' function (which is to be called from the setup script).  Also
+indirectly provides the Distribution and Command classes, although they are
+really defined in distutils.dist and distutils.cmd.
+"""
+
+from __future__ import annotations
+
+import os
+import sys
+import tokenize
+from collections.abc import Iterable
+
+from .cmd import Command
+from .debug import DEBUG
+
+# Mainly import these so setup scripts can "from distutils.core import" them.
+from .dist import Distribution
+from .errors import (
+    CCompilerError,
+    DistutilsArgError,
+    DistutilsError,
+    DistutilsSetupError,
+)
+from .extension import Extension
+
+__all__ = ['Distribution', 'Command', 'Extension', 'setup']
+
+# This is a barebones help message generated displayed when the user
+# runs the setup script with no arguments at all.  More useful help
+# is generated with various --help options: global help, list commands,
+# and per-command help.
+USAGE = """\
+usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
+   or: %(script)s --help [cmd1 cmd2 ...]
+   or: %(script)s --help-commands
+   or: %(script)s cmd --help
+"""
+
+
+def gen_usage(script_name):
+    script = os.path.basename(script_name)
+    return USAGE % locals()
+
+
+# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'.
+_setup_stop_after = None
+_setup_distribution = None
+
+# Legal keyword arguments for the setup() function
+setup_keywords = (
+    'distclass',
+    'script_name',
+    'script_args',
+    'options',
+    'name',
+    'version',
+    'author',
+    'author_email',
+    'maintainer',
+    'maintainer_email',
+    'url',
+    'license',
+    'description',
+    'long_description',
+    'keywords',
+    'platforms',
+    'classifiers',
+    'download_url',
+    'requires',
+    'provides',
+    'obsoletes',
+)
+
+# Legal keyword arguments for the Extension constructor
+extension_keywords = (
+    'name',
+    'sources',
+    'include_dirs',
+    'define_macros',
+    'undef_macros',
+    'library_dirs',
+    'libraries',
+    'runtime_library_dirs',
+    'extra_objects',
+    'extra_compile_args',
+    'extra_link_args',
+    'swig_opts',
+    'export_symbols',
+    'depends',
+    'language',
+)
+
+
+def setup(**attrs):  # noqa: C901
+    """The gateway to the Distutils: do everything your setup script needs
+    to do, in a highly flexible and user-driven way.  Briefly: create a
+    Distribution instance; find and parse config files; parse the command
+    line; run each Distutils command found there, customized by the options
+    supplied to 'setup()' (as keyword arguments), in config files, and on
+    the command line.
+
+    The Distribution instance might be an instance of a class supplied via
+    the 'distclass' keyword argument to 'setup'; if no such class is
+    supplied, then the Distribution class (in dist.py) is instantiated.
+    All other arguments to 'setup' (except for 'cmdclass') are used to set
+    attributes of the Distribution instance.
+
+    The 'cmdclass' argument, if supplied, is a dictionary mapping command
+    names to command classes.  Each command encountered on the command line
+    will be turned into a command class, which is in turn instantiated; any
+    class found in 'cmdclass' is used in place of the default, which is
+    (for command 'foo_bar') class 'foo_bar' in module
+    'distutils.command.foo_bar'.  The command class must provide a
+    'user_options' attribute which is a list of option specifiers for
+    'distutils.fancy_getopt'.  Any command-line options between the current
+    and the next command are used to set attributes of the current command
+    object.
+
+    When the entire command-line has been successfully parsed, calls the
+    'run()' method on each command object in turn.  This method will be
+    driven entirely by the Distribution object (which each command object
+    has a reference to, thanks to its constructor), and the
+    command-specific options that became attributes of each command
+    object.
+    """
+
+    global _setup_stop_after, _setup_distribution
+
+    # Determine the distribution class -- either caller-supplied or
+    # our Distribution (see below).
+    klass = attrs.get('distclass')
+    if klass:
+        attrs.pop('distclass')
+    else:
+        klass = Distribution
+
+    if 'script_name' not in attrs:
+        attrs['script_name'] = os.path.basename(sys.argv[0])
+    if 'script_args' not in attrs:
+        attrs['script_args'] = sys.argv[1:]
+
+    # Create the Distribution instance, using the remaining arguments
+    # (ie. everything except distclass) to initialize it
+    try:
+        _setup_distribution = dist = klass(attrs)
+    except DistutilsSetupError as msg:
+        if 'name' not in attrs:
+            raise SystemExit(f"error in setup command: {msg}")
+        else:
+            raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))
+
+    if _setup_stop_after == "init":
+        return dist
+
+    # Find and parse the config file(s): they will override options from
+    # the setup script, but be overridden by the command line.
+    dist.parse_config_files()
+
+    if DEBUG:
+        print("options (after parsing config files):")
+        dist.dump_option_dicts()
+
+    if _setup_stop_after == "config":
+        return dist
+
+    # Parse the command line and override config files; any
+    # command-line errors are the end user's fault, so turn them into
+    # SystemExit to suppress tracebacks.
+    try:
+        ok = dist.parse_command_line()
+    except DistutilsArgError as msg:
+        raise SystemExit(gen_usage(dist.script_name) + f"\nerror: {msg}")
+
+    if DEBUG:
+        print("options (after parsing command line):")
+        dist.dump_option_dicts()
+
+    if _setup_stop_after == "commandline":
+        return dist
+
+    # And finally, run all the commands found on the command line.
+    if ok:
+        return run_commands(dist)
+
+    return dist
+
+
+# setup ()
+
+
+def run_commands(dist):
+    """Given a Distribution object run all the commands,
+    raising ``SystemExit`` errors in the case of failure.
+
+    This function assumes that either ``sys.argv`` or ``dist.script_args``
+    is already set accordingly.
+    """
+    try:
+        dist.run_commands()
+    except KeyboardInterrupt:
+        raise SystemExit("interrupted")
+    except OSError as exc:
+        if DEBUG:
+            sys.stderr.write(f"error: {exc}\n")
+            raise
+        else:
+            raise SystemExit(f"error: {exc}")
+
+    except (DistutilsError, CCompilerError) as msg:
+        if DEBUG:
+            raise
+        else:
+            raise SystemExit("error: " + str(msg))
+
+    return dist
+
+
+def run_setup(script_name, script_args: Iterable[str] | None = None, stop_after="run"):
+    """Run a setup script in a somewhat controlled environment, and
+    return the Distribution instance that drives things.  This is useful
+    if you need to find out the distribution meta-data (passed as
+    keyword args from 'script' to 'setup()', or the contents of the
+    config files or command-line.
+
+    'script_name' is a file that will be read and run with 'exec()';
+    'sys.argv[0]' will be replaced with 'script' for the duration of the
+    call.  'script_args' is a list of strings; if supplied,
+    'sys.argv[1:]' will be replaced by 'script_args' for the duration of
+    the call.
+
+    'stop_after' tells 'setup()' when to stop processing; possible
+    values:
+      init
+        stop after the Distribution instance has been created and
+        populated with the keyword arguments to 'setup()'
+      config
+        stop after config files have been parsed (and their data
+        stored in the Distribution instance)
+      commandline
+        stop after the command-line ('sys.argv[1:]' or 'script_args')
+        have been parsed (and the data stored in the Distribution)
+      run [default]
+        stop after all commands have been run (the same as if 'setup()'
+        had been called in the usual way
+
+    Returns the Distribution instance, which provides all information
+    used to drive the Distutils.
+    """
+    if stop_after not in ('init', 'config', 'commandline', 'run'):
+        raise ValueError(f"invalid value for 'stop_after': {stop_after!r}")
+
+    global _setup_stop_after, _setup_distribution
+    _setup_stop_after = stop_after
+
+    save_argv = sys.argv.copy()
+    g = {'__file__': script_name, '__name__': '__main__'}
+    try:
+        try:
+            sys.argv[0] = script_name
+            if script_args is not None:
+                sys.argv[1:] = script_args
+            # tokenize.open supports automatic encoding detection
+            with tokenize.open(script_name) as f:
+                code = f.read().replace(r'\r\n', r'\n')
+                exec(code, g)
+        finally:
+            sys.argv = save_argv
+            _setup_stop_after = None
+    except SystemExit:
+        # Hmm, should we do something if exiting with a non-zero code
+        # (ie. error)?
+        pass
+
+    if _setup_distribution is None:
+        raise RuntimeError(
+            "'distutils.core.setup()' was never called -- "
+            f"perhaps '{script_name}' is not a Distutils setup script?"
+        )
+
+    # I wonder if the setup script's namespace -- g and l -- would be of
+    # any interest to callers?
+    # print "_setup_distribution:", _setup_distribution
+    return _setup_distribution
+
+
+# run_setup ()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/cygwinccompiler.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/cygwinccompiler.py
new file mode 100644
index 0000000..de89e3c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/cygwinccompiler.py
@@ -0,0 +1,31 @@
+from .compilers.C import cygwin
+from .compilers.C.cygwin import (
+    CONFIG_H_NOTOK,
+    CONFIG_H_OK,
+    CONFIG_H_UNCERTAIN,
+    check_config_h,
+    get_msvcr,
+    is_cygwincc,
+)
+
+__all__ = [
+    'CONFIG_H_NOTOK',
+    'CONFIG_H_OK',
+    'CONFIG_H_UNCERTAIN',
+    'CygwinCCompiler',
+    'Mingw32CCompiler',
+    'check_config_h',
+    'get_msvcr',
+    'is_cygwincc',
+]
+
+
+CygwinCCompiler = cygwin.Compiler
+Mingw32CCompiler = cygwin.MinGW32Compiler
+
+
+get_versions = None
+"""
+A stand-in for the previous get_versions() function to prevent failures
+when monkeypatched. See pypa/setuptools#2969.
+"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/debug.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/debug.py
new file mode 100644
index 0000000..daf1660
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/debug.py
@@ -0,0 +1,5 @@
+import os
+
+# If DISTUTILS_DEBUG is anything other than the empty string, we run in
+# debug mode.
+DEBUG = os.environ.get('DISTUTILS_DEBUG')
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/dep_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dep_util.py
new file mode 100644
index 0000000..09a8a2e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dep_util.py
@@ -0,0 +1,14 @@
+import warnings
+
+from . import _modified
+
+
+def __getattr__(name):
+    if name not in ['newer', 'newer_group', 'newer_pairwise']:
+        raise AttributeError(name)
+    warnings.warn(
+        "dep_util is Deprecated. Use functions from setuptools instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    return getattr(_modified, name)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/dir_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dir_util.py
new file mode 100644
index 0000000..61b4575
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dir_util.py
@@ -0,0 +1,232 @@
+"""distutils.dir_util
+
+Utility functions for manipulating directories and directory trees."""
+
+import functools
+import itertools
+import os
+import pathlib
+
+from . import file_util
+from ._log import log
+from .errors import DistutilsFileError, DistutilsInternalError
+
+
+class SkipRepeatAbsolutePaths(set):
+    """
+    Cache for mkpath.
+
+    In addition to cheapening redundant calls, eliminates redundant
+    "creating /foo/bar/baz" messages in dry-run mode.
+    """
+
+    def __init__(self):
+        SkipRepeatAbsolutePaths.instance = self
+
+    @classmethod
+    def clear(cls):
+        super(cls, cls.instance).clear()
+
+    def wrap(self, func):
+        @functools.wraps(func)
+        def wrapper(path, *args, **kwargs):
+            if path.absolute() in self:
+                return
+            result = func(path, *args, **kwargs)
+            self.add(path.absolute())
+            return result
+
+        return wrapper
+
+
+# Python 3.8 compatibility
+wrapper = SkipRepeatAbsolutePaths().wrap
+
+
+@functools.singledispatch
+@wrapper
+def mkpath(name: pathlib.Path, mode=0o777, verbose=True) -> None:
+    """Create a directory and any missing ancestor directories.
+
+    If the directory already exists (or if 'name' is the empty string, which
+    means the current directory, which of course exists), then do nothing.
+    Raise DistutilsFileError if unable to create some directory along the way
+    (eg. some sub-path exists, but is a file rather than a directory).
+    If 'verbose' is true, log the directory created.
+    """
+    if verbose and not name.is_dir():
+        log.info("creating %s", name)
+
+    try:
+        name.mkdir(mode=mode, parents=True, exist_ok=True)
+    except OSError as exc:
+        raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}")
+
+
+@mkpath.register
+def _(name: str, *args, **kwargs):
+    return mkpath(pathlib.Path(name), *args, **kwargs)
+
+
+@mkpath.register
+def _(name: None, *args, **kwargs):
+    """
+    Detect a common bug -- name is None.
+    """
+    raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})")
+
+
+def create_tree(base_dir, files, mode=0o777, verbose=True):
+    """Create all the empty directories under 'base_dir' needed to put 'files'
+    there.
+
+    'base_dir' is just the name of a directory which doesn't necessarily
+    exist yet; 'files' is a list of filenames to be interpreted relative to
+    'base_dir'.  'base_dir' + the directory portion of every file in 'files'
+    will be created if it doesn't already exist.  'mode' and 'verbose'
+    flags are as for 'mkpath()'.
+    """
+    # First get the list of directories to create
+    need_dir = set(os.path.join(base_dir, os.path.dirname(file)) for file in files)
+
+    # Now create them
+    for dir in sorted(need_dir):
+        mkpath(dir, mode, verbose=verbose)
+
+
+def copy_tree(
+    src,
+    dst,
+    preserve_mode=True,
+    preserve_times=True,
+    preserve_symlinks=False,
+    update=False,
+    verbose=True,
+):
+    """Copy an entire directory tree 'src' to a new location 'dst'.
+
+    Both 'src' and 'dst' must be directory names.  If 'src' is not a
+    directory, raise DistutilsFileError.  If 'dst' does not exist, it is
+    created with 'mkpath()'.  The end result of the copy is that every
+    file in 'src' is copied to 'dst', and directories under 'src' are
+    recursively copied to 'dst'.  Return the list of files that were
+    copied or might have been copied, using their output name.  The
+    return value is unaffected by 'update': it is simply
+    the list of all files under 'src', with the names changed to be
+    under 'dst'.
+
+    'preserve_mode' and 'preserve_times' are the same as for
+    'copy_file'; note that they only apply to regular files, not to
+    directories.  If 'preserve_symlinks' is true, symlinks will be
+    copied as symlinks (on platforms that support them!); otherwise
+    (the default), the destination of the symlink will be copied.
+    'update' and 'verbose' are the same as for 'copy_file'.
+    """
+    if not os.path.isdir(src):
+        raise DistutilsFileError(f"cannot copy tree '{src}': not a directory")
+    try:
+        names = os.listdir(src)
+    except OSError as e:
+        raise DistutilsFileError(f"error listing files in '{src}': {e.strerror}")
+
+    mkpath(dst, verbose=verbose)
+
+    copy_one = functools.partial(
+        _copy_one,
+        src=src,
+        dst=dst,
+        preserve_symlinks=preserve_symlinks,
+        verbose=verbose,
+        preserve_mode=preserve_mode,
+        preserve_times=preserve_times,
+        update=update,
+    )
+    return list(itertools.chain.from_iterable(map(copy_one, names)))
+
+
+def _copy_one(
+    name,
+    *,
+    src,
+    dst,
+    preserve_symlinks,
+    verbose,
+    preserve_mode,
+    preserve_times,
+    update,
+):
+    src_name = os.path.join(src, name)
+    dst_name = os.path.join(dst, name)
+
+    if name.startswith('.nfs'):
+        # skip NFS rename files
+        return
+
+    if preserve_symlinks and os.path.islink(src_name):
+        link_dest = os.readlink(src_name)
+        if verbose >= 1:
+            log.info("linking %s -> %s", dst_name, link_dest)
+        os.symlink(link_dest, dst_name)
+        yield dst_name
+
+    elif os.path.isdir(src_name):
+        yield from copy_tree(
+            src_name,
+            dst_name,
+            preserve_mode,
+            preserve_times,
+            preserve_symlinks,
+            update,
+            verbose=verbose,
+        )
+    else:
+        file_util.copy_file(
+            src_name,
+            dst_name,
+            preserve_mode,
+            preserve_times,
+            update,
+            verbose=verbose,
+        )
+        yield dst_name
+
+
+def _build_cmdtuple(path, cmdtuples):
+    """Helper for remove_tree()."""
+    for f in os.listdir(path):
+        real_f = os.path.join(path, f)
+        if os.path.isdir(real_f) and not os.path.islink(real_f):
+            _build_cmdtuple(real_f, cmdtuples)
+        else:
+            cmdtuples.append((os.remove, real_f))
+    cmdtuples.append((os.rmdir, path))
+
+
+def remove_tree(directory, verbose=True):
+    """Recursively remove an entire directory tree.
+
+    Any errors are ignored (apart from being reported to stdout if 'verbose'
+    is true).
+    """
+    if verbose >= 1:
+        log.info("removing '%s' (and everything under it)", directory)
+    cmdtuples = []
+    _build_cmdtuple(directory, cmdtuples)
+    for cmd in cmdtuples:
+        try:
+            cmd[0](cmd[1])
+            # Clear the cache
+            SkipRepeatAbsolutePaths.clear()
+        except OSError as exc:
+            log.warning("error removing %s: %s", directory, exc)
+
+
+def ensure_relative(path):
+    """Take the full path 'path', and make it a relative path.
+
+    This is useful to make 'path' the second argument to os.path.join().
+    """
+    drive, path = os.path.splitdrive(path)
+    if path[0:1] == os.sep:
+        path = drive + path[1:]
+    return path
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/dist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dist.py
new file mode 100644
index 0000000..e04e487
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/dist.py
@@ -0,0 +1,1384 @@
+"""distutils.dist
+
+Provides the Distribution class, which represents the module distribution
+being built/installed/distributed.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import logging
+import os
+import pathlib
+import re
+import sys
+import warnings
+from collections.abc import Iterable, MutableMapping
+from email import message_from_file
+from typing import (
+    IO,
+    TYPE_CHECKING,
+    Any,
+    ClassVar,
+    Literal,
+    TypeVar,
+    Union,
+    overload,
+)
+
+from packaging.utils import canonicalize_name, canonicalize_version
+
+from ._log import log
+from .debug import DEBUG
+from .errors import (
+    DistutilsArgError,
+    DistutilsClassError,
+    DistutilsModuleError,
+    DistutilsOptionError,
+)
+from .fancy_getopt import FancyGetopt, translate_longopt
+from .util import check_environ, rfc822_escape, strtobool
+
+if TYPE_CHECKING:
+    from _typeshed import SupportsWrite
+    from typing_extensions import TypeAlias
+
+    # type-only import because of mutual dependence between these modules
+    from .cmd import Command
+
+_CommandT = TypeVar("_CommandT", bound="Command")
+_OptionsList: TypeAlias = list[
+    Union[tuple[str, Union[str, None], str, int], tuple[str, Union[str, None], str]]
+]
+
+
+# Regex to define acceptable Distutils command names.  This is not *quite*
+# the same as a Python NAME -- I don't allow leading underscores.  The fact
+# that they're very similar is no coincidence; the default naming scheme is
+# to look for a Python module named after the command.
+command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+
+
+def _ensure_list(value: str | Iterable[str], fieldname) -> str | list[str]:
+    if isinstance(value, str):
+        # a string containing comma separated values is okay.  It will
+        # be converted to a list by Distribution.finalize_options().
+        pass
+    elif not isinstance(value, list):
+        # passing a tuple or an iterator perhaps, warn and convert
+        typename = type(value).__name__
+        msg = "Warning: '{fieldname}' should be a list, got type '{typename}'"
+        msg = msg.format(**locals())
+        log.warning(msg)
+        value = list(value)
+    return value
+
+
+class Distribution:
+    """The core of the Distutils.  Most of the work hiding behind 'setup'
+    is really done within a Distribution instance, which farms the work out
+    to the Distutils commands specified on the command line.
+
+    Setup scripts will almost never instantiate Distribution directly,
+    unless the 'setup()' function is totally inadequate to their needs.
+    However, it is conceivable that a setup script might wish to subclass
+    Distribution for some specialized purpose, and then pass the subclass
+    to 'setup()' as the 'distclass' keyword argument.  If so, it is
+    necessary to respect the expectations that 'setup' has of Distribution.
+    See the code for 'setup()', in core.py, for details.
+    """
+
+    # 'global_options' describes the command-line options that may be
+    # supplied to the setup script prior to any actual commands.
+    # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of
+    # these global options.  This list should be kept to a bare minimum,
+    # since every global option is also valid as a command option -- and we
+    # don't want to pollute the commands with too many options that they
+    # have minimal control over.
+    # The fourth entry for verbose means that it can be repeated.
+    global_options: ClassVar[_OptionsList] = [
+        ('verbose', 'v', "run verbosely (default)", 1),
+        ('quiet', 'q', "run quietly (turns verbosity off)"),
+        ('help', 'h', "show detailed help message"),
+        ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
+    ]
+
+    # 'common_usage' is a short (2-3 line) string describing the common
+    # usage of the setup script.
+    common_usage: ClassVar[str] = """\
+Common commands: (see '--help-commands' for more)
+
+  setup.py build      will build the package underneath 'build/'
+  setup.py install    will install the package
+"""
+
+    # options that are not propagated to the commands
+    display_options: ClassVar[_OptionsList] = [
+        ('help-commands', None, "list all available commands"),
+        ('name', None, "print package name"),
+        ('version', 'V', "print package version"),
+        ('fullname', None, "print -"),
+        ('author', None, "print the author's name"),
+        ('author-email', None, "print the author's email address"),
+        ('maintainer', None, "print the maintainer's name"),
+        ('maintainer-email', None, "print the maintainer's email address"),
+        ('contact', None, "print the maintainer's name if known, else the author's"),
+        (
+            'contact-email',
+            None,
+            "print the maintainer's email address if known, else the author's",
+        ),
+        ('url', None, "print the URL for this package"),
+        ('license', None, "print the license of the package"),
+        ('licence', None, "alias for --license"),
+        ('description', None, "print the package description"),
+        ('long-description', None, "print the long package description"),
+        ('platforms', None, "print the list of platforms"),
+        ('classifiers', None, "print the list of classifiers"),
+        ('keywords', None, "print the list of keywords"),
+        ('provides', None, "print the list of packages/modules provided"),
+        ('requires', None, "print the list of packages/modules required"),
+        ('obsoletes', None, "print the list of packages/modules made obsolete"),
+    ]
+    display_option_names: ClassVar[list[str]] = [
+        translate_longopt(x[0]) for x in display_options
+    ]
+
+    # negative options are options that exclude other options
+    negative_opt: ClassVar[dict[str, str]] = {'quiet': 'verbose'}
+
+    # -- Creation/initialization methods -------------------------------
+
+    # Can't Unpack a TypedDict with optional properties, so using Any instead
+    def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None:  # noqa: C901
+        """Construct a new Distribution instance: initialize all the
+        attributes of a Distribution, and then use 'attrs' (a dictionary
+        mapping attribute names to values) to assign some of those
+        attributes their "real" values.  (Any attributes not mentioned in
+        'attrs' will be assigned to some null value: 0, None, an empty list
+        or dictionary, etc.)  Most importantly, initialize the
+        'command_obj' attribute to the empty dictionary; this will be
+        filled in with real command objects by 'parse_command_line()'.
+        """
+
+        # Default values for our command-line options
+        self.verbose = True
+        self.help = False
+        for attr in self.display_option_names:
+            setattr(self, attr, False)
+
+        # Store the distribution meta-data (name, version, author, and so
+        # forth) in a separate object -- we're getting to have enough
+        # information here (and enough command-line options) that it's
+        # worth it.  Also delegate 'get_XXX()' methods to the 'metadata'
+        # object in a sneaky and underhanded (but efficient!) way.
+        self.metadata = DistributionMetadata()
+        for basename in self.metadata._METHOD_BASENAMES:
+            method_name = "get_" + basename
+            setattr(self, method_name, getattr(self.metadata, method_name))
+
+        # 'cmdclass' maps command names to class objects, so we
+        # can 1) quickly figure out which class to instantiate when
+        # we need to create a new command object, and 2) have a way
+        # for the setup script to override command classes
+        self.cmdclass: dict[str, type[Command]] = {}
+
+        # 'command_packages' is a list of packages in which commands
+        # are searched for.  The factory for command 'foo' is expected
+        # to be named 'foo' in the module 'foo' in one of the packages
+        # named here.  This list is searched from the left; an error
+        # is raised if no named package provides the command being
+        # searched for.  (Always access using get_command_packages().)
+        self.command_packages: str | list[str] | None = None
+
+        # 'script_name' and 'script_args' are usually set to sys.argv[0]
+        # and sys.argv[1:], but they can be overridden when the caller is
+        # not necessarily a setup script run from the command-line.
+        self.script_name: str | os.PathLike[str] | None = None
+        self.script_args: list[str] | None = None
+
+        # 'command_options' is where we store command options between
+        # parsing them (from config files, the command-line, etc.) and when
+        # they are actually needed -- ie. when the command in question is
+        # instantiated.  It is a dictionary of dictionaries of 2-tuples:
+        #   command_options = { command_name : { option : (source, value) } }
+        self.command_options: dict[str, dict[str, tuple[str, str]]] = {}
+
+        # 'dist_files' is the list of (command, pyversion, file) that
+        # have been created by any dist commands run so far. This is
+        # filled regardless of whether the run is dry or not. pyversion
+        # gives sysconfig.get_python_version() if the dist file is
+        # specific to a Python version, 'any' if it is good for all
+        # Python versions on the target platform, and '' for a source
+        # file. pyversion should not be used to specify minimum or
+        # maximum required Python versions; use the metainfo for that
+        # instead.
+        self.dist_files: list[tuple[str, str, str]] = []
+
+        # These options are really the business of various commands, rather
+        # than of the Distribution itself.  We provide aliases for them in
+        # Distribution as a convenience to the developer.
+        self.packages = None
+        self.package_data: dict[str, list[str]] = {}
+        self.package_dir = None
+        self.py_modules = None
+        self.libraries = None
+        self.headers = None
+        self.ext_modules = None
+        self.ext_package = None
+        self.include_dirs = None
+        self.extra_path = None
+        self.scripts = None
+        self.data_files = None
+        self.password = ''
+
+        # And now initialize bookkeeping stuff that can't be supplied by
+        # the caller at all.  'command_obj' maps command names to
+        # Command instances -- that's how we enforce that every command
+        # class is a singleton.
+        self.command_obj: dict[str, Command] = {}
+
+        # 'have_run' maps command names to boolean values; it keeps track
+        # of whether we have actually run a particular command, to make it
+        # cheap to "run" a command whenever we think we might need to -- if
+        # it's already been done, no need for expensive filesystem
+        # operations, we just check the 'have_run' dictionary and carry on.
+        # It's only safe to query 'have_run' for a command class that has
+        # been instantiated -- a false value will be inserted when the
+        # command object is created, and replaced with a true value when
+        # the command is successfully run.  Thus it's probably best to use
+        # '.get()' rather than a straight lookup.
+        self.have_run: dict[str, bool] = {}
+
+        # Now we'll use the attrs dictionary (ultimately, keyword args from
+        # the setup script) to possibly override any or all of these
+        # distribution options.
+
+        if attrs:
+            # Pull out the set of command options and work on them
+            # specifically.  Note that this order guarantees that aliased
+            # command options will override any supplied redundantly
+            # through the general options dictionary.
+            options = attrs.get('options')
+            if options is not None:
+                del attrs['options']
+                for command, cmd_options in options.items():
+                    opt_dict = self.get_option_dict(command)
+                    for opt, val in cmd_options.items():
+                        opt_dict[opt] = ("setup script", val)
+
+            if 'licence' in attrs:
+                attrs['license'] = attrs['licence']
+                del attrs['licence']
+                msg = "'licence' distribution option is deprecated; use 'license'"
+                warnings.warn(msg)
+
+            # Now work on the rest of the attributes.  Any attribute that's
+            # not already defined is invalid!
+            for key, val in attrs.items():
+                if hasattr(self.metadata, "set_" + key):
+                    getattr(self.metadata, "set_" + key)(val)
+                elif hasattr(self.metadata, key):
+                    setattr(self.metadata, key, val)
+                elif hasattr(self, key):
+                    setattr(self, key, val)
+                else:
+                    msg = f"Unknown distribution option: {key!r}"
+                    warnings.warn(msg)
+
+        # no-user-cfg is handled before other command line args
+        # because other args override the config files, and this
+        # one is needed before we can load the config files.
+        # If attrs['script_args'] wasn't passed, assume false.
+        #
+        # This also make sure we just look at the global options
+        self.want_user_cfg = True
+
+        if self.script_args is not None:
+            # Coerce any possible iterable from attrs into a list
+            self.script_args = list(self.script_args)
+            for arg in self.script_args:
+                if not arg.startswith('-'):
+                    break
+                if arg == '--no-user-cfg':
+                    self.want_user_cfg = False
+                    break
+
+        self.finalize_options()
+
+    def get_option_dict(self, command):
+        """Get the option dictionary for a given command.  If that
+        command's option dictionary hasn't been created yet, then create it
+        and return the new dictionary; otherwise, return the existing
+        option dictionary.
+        """
+        dict = self.command_options.get(command)
+        if dict is None:
+            dict = self.command_options[command] = {}
+        return dict
+
+    def dump_option_dicts(self, header=None, commands=None, indent: str = "") -> None:
+        from pprint import pformat
+
+        if commands is None:  # dump all command option dicts
+            commands = sorted(self.command_options.keys())
+
+        if header is not None:
+            self.announce(indent + header)
+            indent = indent + "  "
+
+        if not commands:
+            self.announce(indent + "no commands known yet")
+            return
+
+        for cmd_name in commands:
+            opt_dict = self.command_options.get(cmd_name)
+            if opt_dict is None:
+                self.announce(indent + f"no option dict for '{cmd_name}' command")
+            else:
+                self.announce(indent + f"option dict for '{cmd_name}' command:")
+                out = pformat(opt_dict)
+                for line in out.split('\n'):
+                    self.announce(indent + "  " + line)
+
+    # -- Config file finding/parsing methods ---------------------------
+
+    def find_config_files(self):
+        """Find as many configuration files as should be processed for this
+        platform, and return a list of filenames in the order in which they
+        should be parsed.  The filenames returned are guaranteed to exist
+        (modulo nasty race conditions).
+
+        There are multiple possible config files:
+        - distutils.cfg in the Distutils installation directory (i.e.
+          where the top-level Distutils __inst__.py file lives)
+        - a file in the user's home directory named .pydistutils.cfg
+          on Unix and pydistutils.cfg on Windows/Mac; may be disabled
+          with the ``--no-user-cfg`` option
+        - setup.cfg in the current directory
+        - a file named by an environment variable
+        """
+        check_environ()
+        files = [str(path) for path in self._gen_paths() if os.path.isfile(path)]
+
+        if DEBUG:
+            self.announce("using config files: {}".format(', '.join(files)))
+
+        return files
+
+    def _gen_paths(self):
+        # The system-wide Distutils config file
+        sys_dir = pathlib.Path(sys.modules['distutils'].__file__).parent
+        yield sys_dir / "distutils.cfg"
+
+        # The per-user config file
+        prefix = '.' * (os.name == 'posix')
+        filename = prefix + 'pydistutils.cfg'
+        if self.want_user_cfg:
+            with contextlib.suppress(RuntimeError):
+                yield pathlib.Path('~').expanduser() / filename
+
+        # All platforms support local setup.cfg
+        yield pathlib.Path('setup.cfg')
+
+        # Additional config indicated in the environment
+        with contextlib.suppress(TypeError):
+            yield pathlib.Path(os.getenv("DIST_EXTRA_CONFIG"))
+
+    def parse_config_files(self, filenames=None):  # noqa: C901
+        from configparser import ConfigParser
+
+        # Ignore install directory options if we have a venv
+        if sys.prefix != sys.base_prefix:
+            ignore_options = [
+                'install-base',
+                'install-platbase',
+                'install-lib',
+                'install-platlib',
+                'install-purelib',
+                'install-headers',
+                'install-scripts',
+                'install-data',
+                'prefix',
+                'exec-prefix',
+                'home',
+                'user',
+                'root',
+            ]
+        else:
+            ignore_options = []
+
+        ignore_options = frozenset(ignore_options)
+
+        if filenames is None:
+            filenames = self.find_config_files()
+
+        if DEBUG:
+            self.announce("Distribution.parse_config_files():")
+
+        parser = ConfigParser()
+        for filename in filenames:
+            if DEBUG:
+                self.announce(f"  reading {filename}")
+            parser.read(filename, encoding='utf-8')
+            for section in parser.sections():
+                options = parser.options(section)
+                opt_dict = self.get_option_dict(section)
+
+                for opt in options:
+                    if opt != '__name__' and opt not in ignore_options:
+                        val = parser.get(section, opt)
+                        opt = opt.replace('-', '_')
+                        opt_dict[opt] = (filename, val)
+
+            # Make the ConfigParser forget everything (so we retain
+            # the original filenames that options come from)
+            parser.__init__()
+
+        # If there was a "global" section in the config file, use it
+        # to set Distribution options.
+
+        if 'global' in self.command_options:
+            for opt, (_src, val) in self.command_options['global'].items():
+                alias = self.negative_opt.get(opt)
+                try:
+                    if alias:
+                        setattr(self, alias, not strtobool(val))
+                    elif opt in ('verbose',):  # ugh!
+                        setattr(self, opt, strtobool(val))
+                    else:
+                        setattr(self, opt, val)
+                except ValueError as msg:
+                    raise DistutilsOptionError(msg)
+
+    # -- Command-line parsing methods ----------------------------------
+
+    def parse_command_line(self):
+        """Parse the setup script's command line, taken from the
+        'script_args' instance attribute (which defaults to 'sys.argv[1:]'
+        -- see 'setup()' in core.py).  This list is first processed for
+        "global options" -- options that set attributes of the Distribution
+        instance.  Then, it is alternately scanned for Distutils commands
+        and options for that command.  Each new command terminates the
+        options for the previous command.  The allowed options for a
+        command are determined by the 'user_options' attribute of the
+        command class -- thus, we have to be able to load command classes
+        in order to parse the command line.  Any error in that 'options'
+        attribute raises DistutilsGetoptError; any error on the
+        command-line raises DistutilsArgError.  If no Distutils commands
+        were found on the command line, raises DistutilsArgError.  Return
+        true if command-line was successfully parsed and we should carry
+        on with executing commands; false if no errors but we shouldn't
+        execute commands (currently, this only happens if user asks for
+        help).
+        """
+        #
+        # We now have enough information to show the Macintosh dialog
+        # that allows the user to interactively specify the "command line".
+        #
+        toplevel_options = self._get_toplevel_options()
+
+        # We have to parse the command line a bit at a time -- global
+        # options, then the first command, then its options, and so on --
+        # because each command will be handled by a different class, and
+        # the options that are valid for a particular class aren't known
+        # until we have loaded the command class, which doesn't happen
+        # until we know what the command is.
+
+        self.commands = []
+        parser = FancyGetopt(toplevel_options + self.display_options)
+        parser.set_negative_aliases(self.negative_opt)
+        parser.set_aliases({'licence': 'license'})
+        args = parser.getopt(args=self.script_args, object=self)
+        option_order = parser.get_option_order()
+        logging.getLogger().setLevel(logging.WARN - 10 * self.verbose)
+
+        # for display options we return immediately
+        if self.handle_display_options(option_order):
+            return
+        while args:
+            args = self._parse_command_opts(parser, args)
+            if args is None:  # user asked for help (and got it)
+                return
+
+        # Handle the cases of --help as a "global" option, ie.
+        # "setup.py --help" and "setup.py --help command ...".  For the
+        # former, we show global options (--verbose, --dry-run, etc.)
+        # and display-only options (--name, --version, etc.); for the
+        # latter, we omit the display-only options and show help for
+        # each command listed on the command line.
+        if self.help:
+            self._show_help(
+                parser, display_options=len(self.commands) == 0, commands=self.commands
+            )
+            return
+
+        # Oops, no commands found -- an end-user error
+        if not self.commands:
+            raise DistutilsArgError("no commands supplied")
+
+        # All is well: return true
+        return True
+
+    def _get_toplevel_options(self):
+        """Return the non-display options recognized at the top level.
+
+        This includes options that are recognized *only* at the top
+        level as well as options recognized for commands.
+        """
+        return self.global_options + [
+            (
+                "command-packages=",
+                None,
+                "list of packages that provide distutils commands",
+            ),
+        ]
+
+    def _parse_command_opts(self, parser, args):  # noqa: C901
+        """Parse the command-line options for a single command.
+        'parser' must be a FancyGetopt instance; 'args' must be the list
+        of arguments, starting with the current command (whose options
+        we are about to parse).  Returns a new version of 'args' with
+        the next command at the front of the list; will be the empty
+        list if there are no more commands on the command line.  Returns
+        None if the user asked for help on this command.
+        """
+        # late import because of mutual dependence between these modules
+        from distutils.cmd import Command
+
+        # Pull the current command from the head of the command line
+        command = args[0]
+        if not command_re.match(command):
+            raise SystemExit(f"invalid command name '{command}'")
+        self.commands.append(command)
+
+        # Dig up the command class that implements this command, so we
+        # 1) know that it's a valid command, and 2) know which options
+        # it takes.
+        try:
+            cmd_class = self.get_command_class(command)
+        except DistutilsModuleError as msg:
+            raise DistutilsArgError(msg)
+
+        # Require that the command class be derived from Command -- want
+        # to be sure that the basic "command" interface is implemented.
+        if not issubclass(cmd_class, Command):
+            raise DistutilsClassError(
+                f"command class {cmd_class} must subclass Command"
+            )
+
+        # Also make sure that the command object provides a list of its
+        # known options.
+        if not (
+            hasattr(cmd_class, 'user_options')
+            and isinstance(cmd_class.user_options, list)
+        ):
+            msg = (
+                "command class %s must provide "
+                "'user_options' attribute (a list of tuples)"
+            )
+            raise DistutilsClassError(msg % cmd_class)
+
+        # If the command class has a list of negative alias options,
+        # merge it in with the global negative aliases.
+        negative_opt = self.negative_opt
+        if hasattr(cmd_class, 'negative_opt'):
+            negative_opt = negative_opt.copy()
+            negative_opt.update(cmd_class.negative_opt)
+
+        # Check for help_options in command class.  They have a different
+        # format (tuple of four) so we need to preprocess them here.
+        if hasattr(cmd_class, 'help_options') and isinstance(
+            cmd_class.help_options, list
+        ):
+            help_options = fix_help_options(cmd_class.help_options)
+        else:
+            help_options = []
+
+        # All commands support the global options too, just by adding
+        # in 'global_options'.
+        parser.set_option_table(
+            self.global_options + cmd_class.user_options + help_options
+        )
+        parser.set_negative_aliases(negative_opt)
+        (args, opts) = parser.getopt(args[1:])
+        if hasattr(opts, 'help') and opts.help:
+            self._show_help(parser, display_options=False, commands=[cmd_class])
+            return
+
+        if hasattr(cmd_class, 'help_options') and isinstance(
+            cmd_class.help_options, list
+        ):
+            help_option_found = 0
+            for help_option, _short, _desc, func in cmd_class.help_options:
+                if hasattr(opts, parser.get_attr_name(help_option)):
+                    help_option_found = 1
+                    if callable(func):
+                        func()
+                    else:
+                        raise DistutilsClassError(
+                            f"invalid help function {func!r} for help option '{help_option}': "
+                            "must be a callable object (function, etc.)"
+                        )
+
+            if help_option_found:
+                return
+
+        # Put the options from the command-line into their official
+        # holding pen, the 'command_options' dictionary.
+        opt_dict = self.get_option_dict(command)
+        for name, value in vars(opts).items():
+            opt_dict[name] = ("command line", value)
+
+        return args
+
+    def finalize_options(self) -> None:
+        """Set final values for all the options on the Distribution
+        instance, analogous to the .finalize_options() method of Command
+        objects.
+        """
+        for attr in ('keywords', 'platforms'):
+            value = getattr(self.metadata, attr)
+            if value is None:
+                continue
+            if isinstance(value, str):
+                value = [elm.strip() for elm in value.split(',')]
+                setattr(self.metadata, attr, value)
+
+    def _show_help(
+        self, parser, global_options=True, display_options=True, commands: Iterable = ()
+    ):
+        """Show help for the setup script command-line in the form of
+        several lists of command-line options.  'parser' should be a
+        FancyGetopt instance; do not expect it to be returned in the
+        same state, as its option table will be reset to make it
+        generate the correct help text.
+
+        If 'global_options' is true, lists the global options:
+        --verbose, --dry-run, etc.  If 'display_options' is true, lists
+        the "display-only" options: --name, --version, etc.  Finally,
+        lists per-command help for every command name or command class
+        in 'commands'.
+        """
+        # late import because of mutual dependence between these modules
+        from distutils.cmd import Command
+        from distutils.core import gen_usage
+
+        if global_options:
+            if display_options:
+                options = self._get_toplevel_options()
+            else:
+                options = self.global_options
+            parser.set_option_table(options)
+            parser.print_help(self.common_usage + "\nGlobal options:")
+            print()
+
+        if display_options:
+            parser.set_option_table(self.display_options)
+            parser.print_help(
+                "Information display options (just display information, ignore any commands)"
+            )
+            print()
+
+        for command in commands:
+            if isinstance(command, type) and issubclass(command, Command):
+                klass = command
+            else:
+                klass = self.get_command_class(command)
+            if hasattr(klass, 'help_options') and isinstance(klass.help_options, list):
+                parser.set_option_table(
+                    klass.user_options + fix_help_options(klass.help_options)
+                )
+            else:
+                parser.set_option_table(klass.user_options)
+            parser.print_help(f"Options for '{klass.__name__}' command:")
+            print()
+
+        print(gen_usage(self.script_name))
+
+    def handle_display_options(self, option_order):
+        """If there were any non-global "display-only" options
+        (--help-commands or the metadata display options) on the command
+        line, display the requested info and return true; else return
+        false.
+        """
+        from distutils.core import gen_usage
+
+        # User just wants a list of commands -- we'll print it out and stop
+        # processing now (ie. if they ran "setup --help-commands foo bar",
+        # we ignore "foo bar").
+        if self.help_commands:
+            self.print_commands()
+            print()
+            print(gen_usage(self.script_name))
+            return 1
+
+        # If user supplied any of the "display metadata" options, then
+        # display that metadata in the order in which the user supplied the
+        # metadata options.
+        any_display_options = 0
+        is_display_option = set()
+        for option in self.display_options:
+            is_display_option.add(option[0])
+
+        for opt, val in option_order:
+            if val and opt in is_display_option:
+                opt = translate_longopt(opt)
+                value = getattr(self.metadata, "get_" + opt)()
+                if opt in ('keywords', 'platforms'):
+                    print(','.join(value))
+                elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'):
+                    print('\n'.join(value))
+                else:
+                    print(value)
+                any_display_options = 1
+
+        return any_display_options
+
+    def print_command_list(self, commands, header, max_length) -> None:
+        """Print a subset of the list of all commands -- used by
+        'print_commands()'.
+        """
+        print(header + ":")
+
+        for cmd in commands:
+            klass = self.cmdclass.get(cmd)
+            if not klass:
+                klass = self.get_command_class(cmd)
+            try:
+                description = klass.description
+            except AttributeError:
+                description = "(no description available)"
+
+            print(f"  {cmd:<{max_length}}  {description}")
+
+    def print_commands(self) -> None:
+        """Print out a help message listing all available commands with a
+        description of each.  The list is divided into "standard commands"
+        (listed in distutils.command.__all__) and "extra commands"
+        (mentioned in self.cmdclass, but not a standard command).  The
+        descriptions come from the command class attribute
+        'description'.
+        """
+        import distutils.command
+
+        std_commands = distutils.command.__all__
+        is_std = set(std_commands)
+
+        extra_commands = [cmd for cmd in self.cmdclass.keys() if cmd not in is_std]
+
+        max_length = 0
+        for cmd in std_commands + extra_commands:
+            if len(cmd) > max_length:
+                max_length = len(cmd)
+
+        self.print_command_list(std_commands, "Standard commands", max_length)
+        if extra_commands:
+            print()
+            self.print_command_list(extra_commands, "Extra commands", max_length)
+
+    def get_command_list(self):
+        """Get a list of (command, description) tuples.
+        The list is divided into "standard commands" (listed in
+        distutils.command.__all__) and "extra commands" (mentioned in
+        self.cmdclass, but not a standard command).  The descriptions come
+        from the command class attribute 'description'.
+        """
+        # Currently this is only used on Mac OS, for the Mac-only GUI
+        # Distutils interface (by Jack Jansen)
+        import distutils.command
+
+        std_commands = distutils.command.__all__
+        is_std = set(std_commands)
+
+        extra_commands = [cmd for cmd in self.cmdclass.keys() if cmd not in is_std]
+
+        rv = []
+        for cmd in std_commands + extra_commands:
+            klass = self.cmdclass.get(cmd)
+            if not klass:
+                klass = self.get_command_class(cmd)
+            try:
+                description = klass.description
+            except AttributeError:
+                description = "(no description available)"
+            rv.append((cmd, description))
+        return rv
+
+    # -- Command class/object methods ----------------------------------
+
+    def get_command_packages(self):
+        """Return a list of packages from which commands are loaded."""
+        pkgs = self.command_packages
+        if not isinstance(pkgs, list):
+            if pkgs is None:
+                pkgs = ''
+            pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != '']
+            if "distutils.command" not in pkgs:
+                pkgs.insert(0, "distutils.command")
+            self.command_packages = pkgs
+        return pkgs
+
+    def get_command_class(self, command: str) -> type[Command]:
+        """Return the class that implements the Distutils command named by
+        'command'.  First we check the 'cmdclass' dictionary; if the
+        command is mentioned there, we fetch the class object from the
+        dictionary and return it.  Otherwise we load the command module
+        ("distutils.command." + command) and fetch the command class from
+        the module.  The loaded class is also stored in 'cmdclass'
+        to speed future calls to 'get_command_class()'.
+
+        Raises DistutilsModuleError if the expected module could not be
+        found, or if that module does not define the expected class.
+        """
+        klass = self.cmdclass.get(command)
+        if klass:
+            return klass
+
+        for pkgname in self.get_command_packages():
+            module_name = f"{pkgname}.{command}"
+            klass_name = command
+
+            try:
+                __import__(module_name)
+                module = sys.modules[module_name]
+            except ImportError:
+                continue
+
+            try:
+                klass = getattr(module, klass_name)
+            except AttributeError:
+                raise DistutilsModuleError(
+                    f"invalid command '{command}' (no class '{klass_name}' in module '{module_name}')"
+                )
+
+            self.cmdclass[command] = klass
+            return klass
+
+        raise DistutilsModuleError(f"invalid command '{command}'")
+
+    @overload
+    def get_command_obj(
+        self, command: str, create: Literal[True] = True
+    ) -> Command: ...
+    @overload
+    def get_command_obj(
+        self, command: str, create: Literal[False]
+    ) -> Command | None: ...
+    def get_command_obj(self, command: str, create: bool = True) -> Command | None:
+        """Return the command object for 'command'.  Normally this object
+        is cached on a previous call to 'get_command_obj()'; if no command
+        object for 'command' is in the cache, then we either create and
+        return it (if 'create' is true) or return None.
+        """
+        cmd_obj = self.command_obj.get(command)
+        if not cmd_obj and create:
+            if DEBUG:
+                self.announce(
+                    "Distribution.get_command_obj(): "
+                    f"creating '{command}' command object"
+                )
+
+            klass = self.get_command_class(command)
+            cmd_obj = self.command_obj[command] = klass(self)
+            self.have_run[command] = False
+
+            # Set any options that were supplied in config files
+            # or on the command line.  (NB. support for error
+            # reporting is lame here: any errors aren't reported
+            # until 'finalize_options()' is called, which means
+            # we won't report the source of the error.)
+            options = self.command_options.get(command)
+            if options:
+                self._set_command_options(cmd_obj, options)
+
+        return cmd_obj
+
+    def _set_command_options(self, command_obj, option_dict=None):  # noqa: C901
+        """Set the options for 'command_obj' from 'option_dict'.  Basically
+        this means copying elements of a dictionary ('option_dict') to
+        attributes of an instance ('command').
+
+        'command_obj' must be a Command instance.  If 'option_dict' is not
+        supplied, uses the standard option dictionary for this command
+        (from 'self.command_options').
+        """
+        command_name = command_obj.get_command_name()
+        if option_dict is None:
+            option_dict = self.get_option_dict(command_name)
+
+        if DEBUG:
+            self.announce(f"  setting options for '{command_name}' command:")
+        for option, (source, value) in option_dict.items():
+            if DEBUG:
+                self.announce(f"    {option} = {value} (from {source})")
+            try:
+                bool_opts = [translate_longopt(o) for o in command_obj.boolean_options]
+            except AttributeError:
+                bool_opts = []
+            try:
+                neg_opt = command_obj.negative_opt
+            except AttributeError:
+                neg_opt = {}
+
+            try:
+                is_string = isinstance(value, str)
+                if option in neg_opt and is_string:
+                    setattr(command_obj, neg_opt[option], not strtobool(value))
+                elif option in bool_opts and is_string:
+                    setattr(command_obj, option, strtobool(value))
+                elif hasattr(command_obj, option):
+                    setattr(command_obj, option, value)
+                else:
+                    raise DistutilsOptionError(
+                        f"error in {source}: command '{command_name}' has no such option '{option}'"
+                    )
+            except ValueError as msg:
+                raise DistutilsOptionError(msg)
+
+    @overload
+    def reinitialize_command(
+        self, command: str, reinit_subcommands: bool = False
+    ) -> Command: ...
+    @overload
+    def reinitialize_command(
+        self, command: _CommandT, reinit_subcommands: bool = False
+    ) -> _CommandT: ...
+    def reinitialize_command(
+        self, command: str | Command, reinit_subcommands=False
+    ) -> Command:
+        """Reinitializes a command to the state it was in when first
+        returned by 'get_command_obj()': ie., initialized but not yet
+        finalized.  This provides the opportunity to sneak option
+        values in programmatically, overriding or supplementing
+        user-supplied values from the config files and command line.
+        You'll have to re-finalize the command object (by calling
+        'finalize_options()' or 'ensure_finalized()') before using it for
+        real.
+
+        'command' should be a command name (string) or command object.  If
+        'reinit_subcommands' is true, also reinitializes the command's
+        sub-commands, as declared by the 'sub_commands' class attribute (if
+        it has one).  See the "install" command for an example.  Only
+        reinitializes the sub-commands that actually matter, ie. those
+        whose test predicates return true.
+
+        Returns the reinitialized command object.
+        """
+        from distutils.cmd import Command
+
+        if not isinstance(command, Command):
+            command_name = command
+            command = self.get_command_obj(command_name)
+        else:
+            command_name = command.get_command_name()
+
+        if not command.finalized:
+            return command
+        command.initialize_options()
+        command.finalized = False
+        self.have_run[command_name] = False
+        self._set_command_options(command)
+
+        if reinit_subcommands:
+            for sub in command.get_sub_commands():
+                self.reinitialize_command(sub, reinit_subcommands)
+
+        return command
+
+    # -- Methods that operate on the Distribution ----------------------
+
+    def announce(self, msg, level: int = logging.INFO) -> None:
+        log.log(level, msg)
+
+    def run_commands(self) -> None:
+        """Run each command that was seen on the setup script command line.
+        Uses the list of commands found and cache of command objects
+        created by 'get_command_obj()'.
+        """
+        for cmd in self.commands:
+            self.run_command(cmd)
+
+    # -- Methods that operate on its Commands --------------------------
+
+    def run_command(self, command: str) -> None:
+        """Do whatever it takes to run a command (including nothing at all,
+        if the command has already been run).  Specifically: if we have
+        already created and run the command named by 'command', return
+        silently without doing anything.  If the command named by 'command'
+        doesn't even have a command object yet, create one.  Then invoke
+        'run()' on that command object (or an existing one).
+        """
+        # Already been here, done that? then return silently.
+        if self.have_run.get(command):
+            return
+
+        log.info("running %s", command)
+        cmd_obj = self.get_command_obj(command)
+        cmd_obj.ensure_finalized()
+        cmd_obj.run()
+        self.have_run[command] = True
+
+    # -- Distribution query methods ------------------------------------
+
+    def has_pure_modules(self) -> bool:
+        return len(self.packages or self.py_modules or []) > 0
+
+    def has_ext_modules(self) -> bool:
+        return self.ext_modules and len(self.ext_modules) > 0
+
+    def has_c_libraries(self) -> bool:
+        return self.libraries and len(self.libraries) > 0
+
+    def has_modules(self) -> bool:
+        return self.has_pure_modules() or self.has_ext_modules()
+
+    def has_headers(self) -> bool:
+        return self.headers and len(self.headers) > 0
+
+    def has_scripts(self) -> bool:
+        return self.scripts and len(self.scripts) > 0
+
+    def has_data_files(self) -> bool:
+        return self.data_files and len(self.data_files) > 0
+
+    def is_pure(self) -> bool:
+        return (
+            self.has_pure_modules()
+            and not self.has_ext_modules()
+            and not self.has_c_libraries()
+        )
+
+    # -- Metadata query methods ----------------------------------------
+
+    # If you're looking for 'get_name()', 'get_version()', and so forth,
+    # they are defined in a sneaky way: the constructor binds self.get_XXX
+    # to self.metadata.get_XXX.  The actual code is in the
+    # DistributionMetadata class, below.
+    if TYPE_CHECKING:
+        # Unfortunately this means we need to specify them manually or not expose statically
+        def _(self) -> None:
+            self.get_name = self.metadata.get_name
+            self.get_version = self.metadata.get_version
+            self.get_fullname = self.metadata.get_fullname
+            self.get_author = self.metadata.get_author
+            self.get_author_email = self.metadata.get_author_email
+            self.get_maintainer = self.metadata.get_maintainer
+            self.get_maintainer_email = self.metadata.get_maintainer_email
+            self.get_contact = self.metadata.get_contact
+            self.get_contact_email = self.metadata.get_contact_email
+            self.get_url = self.metadata.get_url
+            self.get_license = self.metadata.get_license
+            self.get_licence = self.metadata.get_licence
+            self.get_description = self.metadata.get_description
+            self.get_long_description = self.metadata.get_long_description
+            self.get_keywords = self.metadata.get_keywords
+            self.get_platforms = self.metadata.get_platforms
+            self.get_classifiers = self.metadata.get_classifiers
+            self.get_download_url = self.metadata.get_download_url
+            self.get_requires = self.metadata.get_requires
+            self.get_provides = self.metadata.get_provides
+            self.get_obsoletes = self.metadata.get_obsoletes
+
+        # Default attributes generated in __init__ from self.display_option_names
+        help_commands: bool
+        name: str | Literal[False]
+        version: str | Literal[False]
+        fullname: str | Literal[False]
+        author: str | Literal[False]
+        author_email: str | Literal[False]
+        maintainer: str | Literal[False]
+        maintainer_email: str | Literal[False]
+        contact: str | Literal[False]
+        contact_email: str | Literal[False]
+        url: str | Literal[False]
+        license: str | Literal[False]
+        licence: str | Literal[False]
+        description: str | Literal[False]
+        long_description: str | Literal[False]
+        platforms: str | list[str] | Literal[False]
+        classifiers: str | list[str] | Literal[False]
+        keywords: str | list[str] | Literal[False]
+        provides: list[str] | Literal[False]
+        requires: list[str] | Literal[False]
+        obsoletes: list[str] | Literal[False]
+
+
+class DistributionMetadata:
+    """Dummy class to hold the distribution meta-data: name, version,
+    author, and so forth.
+    """
+
+    _METHOD_BASENAMES = (
+        "name",
+        "version",
+        "author",
+        "author_email",
+        "maintainer",
+        "maintainer_email",
+        "url",
+        "license",
+        "description",
+        "long_description",
+        "keywords",
+        "platforms",
+        "fullname",
+        "contact",
+        "contact_email",
+        "classifiers",
+        "download_url",
+        # PEP 314
+        "provides",
+        "requires",
+        "obsoletes",
+    )
+
+    def __init__(
+        self, path: str | bytes | os.PathLike[str] | os.PathLike[bytes] | None = None
+    ) -> None:
+        if path is not None:
+            self.read_pkg_file(open(path))
+        else:
+            self.name: str | None = None
+            self.version: str | None = None
+            self.author: str | None = None
+            self.author_email: str | None = None
+            self.maintainer: str | None = None
+            self.maintainer_email: str | None = None
+            self.url: str | None = None
+            self.license: str | None = None
+            self.description: str | None = None
+            self.long_description: str | None = None
+            self.keywords: str | list[str] | None = None
+            self.platforms: str | list[str] | None = None
+            self.classifiers: str | list[str] | None = None
+            self.download_url: str | None = None
+            # PEP 314
+            self.provides: str | list[str] | None = None
+            self.requires: str | list[str] | None = None
+            self.obsoletes: str | list[str] | None = None
+
+    def read_pkg_file(self, file: IO[str]) -> None:
+        """Reads the metadata values from a file object."""
+        msg = message_from_file(file)
+
+        def _read_field(name: str) -> str | None:
+            value = msg[name]
+            if value and value != "UNKNOWN":
+                return value
+            return None
+
+        def _read_list(name):
+            values = msg.get_all(name, None)
+            if values == []:
+                return None
+            return values
+
+        metadata_version = msg['metadata-version']
+        self.name = _read_field('name')
+        self.version = _read_field('version')
+        self.description = _read_field('summary')
+        # we are filling author only.
+        self.author = _read_field('author')
+        self.maintainer = None
+        self.author_email = _read_field('author-email')
+        self.maintainer_email = None
+        self.url = _read_field('home-page')
+        self.license = _read_field('license')
+
+        if 'download-url' in msg:
+            self.download_url = _read_field('download-url')
+        else:
+            self.download_url = None
+
+        self.long_description = _read_field('description')
+        self.description = _read_field('summary')
+
+        if 'keywords' in msg:
+            self.keywords = _read_field('keywords').split(',')
+
+        self.platforms = _read_list('platform')
+        self.classifiers = _read_list('classifier')
+
+        # PEP 314 - these fields only exist in 1.1
+        if metadata_version == '1.1':
+            self.requires = _read_list('requires')
+            self.provides = _read_list('provides')
+            self.obsoletes = _read_list('obsoletes')
+        else:
+            self.requires = None
+            self.provides = None
+            self.obsoletes = None
+
+    def write_pkg_info(self, base_dir: str | os.PathLike[str]) -> None:
+        """Write the PKG-INFO file into the release tree."""
+        with open(
+            os.path.join(base_dir, 'PKG-INFO'), 'w', encoding='UTF-8'
+        ) as pkg_info:
+            self.write_pkg_file(pkg_info)
+
+    def write_pkg_file(self, file: SupportsWrite[str]) -> None:
+        """Write the PKG-INFO format data to a file object."""
+        version = '1.0'
+        if (
+            self.provides
+            or self.requires
+            or self.obsoletes
+            or self.classifiers
+            or self.download_url
+        ):
+            version = '1.1'
+
+        # required fields
+        file.write(f'Metadata-Version: {version}\n')
+        file.write(f'Name: {self.get_name()}\n')
+        file.write(f'Version: {self.get_version()}\n')
+
+        def maybe_write(header, val):
+            if val:
+                file.write(f"{header}: {val}\n")
+
+        # optional fields
+        maybe_write("Summary", self.get_description())
+        maybe_write("Home-page", self.get_url())
+        maybe_write("Author", self.get_contact())
+        maybe_write("Author-email", self.get_contact_email())
+        maybe_write("License", self.get_license())
+        maybe_write("Download-URL", self.download_url)
+        maybe_write("Description", rfc822_escape(self.get_long_description() or ""))
+        maybe_write("Keywords", ",".join(self.get_keywords()))
+
+        self._write_list(file, 'Platform', self.get_platforms())
+        self._write_list(file, 'Classifier', self.get_classifiers())
+
+        # PEP 314
+        self._write_list(file, 'Requires', self.get_requires())
+        self._write_list(file, 'Provides', self.get_provides())
+        self._write_list(file, 'Obsoletes', self.get_obsoletes())
+
+    def _write_list(self, file, name, values):
+        values = values or []
+        for value in values:
+            file.write(f'{name}: {value}\n')
+
+    # -- Metadata query methods ----------------------------------------
+
+    def get_name(self) -> str:
+        return self.name or "UNKNOWN"
+
+    def get_version(self) -> str:
+        return self.version or "0.0.0"
+
+    def get_fullname(self) -> str:
+        return self._fullname(self.get_name(), self.get_version())
+
+    @staticmethod
+    def _fullname(name: str, version: str) -> str:
+        """
+        >>> DistributionMetadata._fullname('setup.tools', '1.0-2')
+        'setup_tools-1.0.post2'
+        >>> DistributionMetadata._fullname('setup-tools', '1.2post2')
+        'setup_tools-1.2.post2'
+        >>> DistributionMetadata._fullname('setup-tools', '1.0-r2')
+        'setup_tools-1.0.post2'
+        >>> DistributionMetadata._fullname('setup.tools', '1.0.post')
+        'setup_tools-1.0.post0'
+        >>> DistributionMetadata._fullname('setup.tools', '1.0+ubuntu-1')
+        'setup_tools-1.0+ubuntu.1'
+        """
+        return "{}-{}".format(
+            canonicalize_name(name).replace('-', '_'),
+            canonicalize_version(version, strip_trailing_zero=False),
+        )
+
+    def get_author(self) -> str | None:
+        return self.author
+
+    def get_author_email(self) -> str | None:
+        return self.author_email
+
+    def get_maintainer(self) -> str | None:
+        return self.maintainer
+
+    def get_maintainer_email(self) -> str | None:
+        return self.maintainer_email
+
+    def get_contact(self) -> str | None:
+        return self.maintainer or self.author
+
+    def get_contact_email(self) -> str | None:
+        return self.maintainer_email or self.author_email
+
+    def get_url(self) -> str | None:
+        return self.url
+
+    def get_license(self) -> str | None:
+        return self.license
+
+    get_licence = get_license
+
+    def get_description(self) -> str | None:
+        return self.description
+
+    def get_long_description(self) -> str | None:
+        return self.long_description
+
+    def get_keywords(self) -> str | list[str]:
+        return self.keywords or []
+
+    def set_keywords(self, value: str | Iterable[str]) -> None:
+        self.keywords = _ensure_list(value, 'keywords')
+
+    def get_platforms(self) -> str | list[str] | None:
+        return self.platforms
+
+    def set_platforms(self, value: str | Iterable[str]) -> None:
+        self.platforms = _ensure_list(value, 'platforms')
+
+    def get_classifiers(self) -> str | list[str]:
+        return self.classifiers or []
+
+    def set_classifiers(self, value: str | Iterable[str]) -> None:
+        self.classifiers = _ensure_list(value, 'classifiers')
+
+    def get_download_url(self) -> str | None:
+        return self.download_url
+
+    # PEP 314
+    def get_requires(self) -> str | list[str]:
+        return self.requires or []
+
+    def set_requires(self, value: Iterable[str]) -> None:
+        import distutils.versionpredicate
+
+        for v in value:
+            distutils.versionpredicate.VersionPredicate(v)
+        self.requires = list(value)
+
+    def get_provides(self) -> str | list[str]:
+        return self.provides or []
+
+    def set_provides(self, value: Iterable[str]) -> None:
+        value = [v.strip() for v in value]
+        for v in value:
+            import distutils.versionpredicate
+
+            distutils.versionpredicate.split_provision(v)
+        self.provides = value
+
+    def get_obsoletes(self) -> str | list[str]:
+        return self.obsoletes or []
+
+    def set_obsoletes(self, value: Iterable[str]) -> None:
+        import distutils.versionpredicate
+
+        for v in value:
+            distutils.versionpredicate.VersionPredicate(v)
+        self.obsoletes = list(value)
+
+
+def fix_help_options(options):
+    """Convert a 4-tuple 'help_options' list as found in various command
+    classes to the 3-tuple form required by FancyGetopt.
+    """
+    return [opt[0:3] for opt in options]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/errors.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/errors.py
new file mode 100644
index 0000000..409d21f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/errors.py
@@ -0,0 +1,108 @@
+"""
+Exceptions used by the Distutils modules.
+
+Distutils modules may raise these or standard exceptions,
+including :exc:`SystemExit`.
+"""
+
+# compiler exceptions aliased for compatibility
+from .compilers.C.errors import CompileError as CompileError
+from .compilers.C.errors import Error as _Error
+from .compilers.C.errors import LibError as LibError
+from .compilers.C.errors import LinkError as LinkError
+from .compilers.C.errors import PreprocessError as PreprocessError
+from .compilers.C.errors import UnknownFileType as _UnknownFileType
+
+CCompilerError = _Error
+UnknownFileError = _UnknownFileType
+
+
+class DistutilsError(Exception):
+    """The root of all Distutils evil."""
+
+    pass
+
+
+class DistutilsModuleError(DistutilsError):
+    """Unable to load an expected module, or to find an expected class
+    within some module (in particular, command modules and classes)."""
+
+    pass
+
+
+class DistutilsClassError(DistutilsError):
+    """Some command class (or possibly distribution class, if anyone
+    feels a need to subclass Distribution) is found not to be holding
+    up its end of the bargain, ie. implementing some part of the
+    "command "interface."""
+
+    pass
+
+
+class DistutilsGetoptError(DistutilsError):
+    """The option table provided to 'fancy_getopt()' is bogus."""
+
+    pass
+
+
+class DistutilsArgError(DistutilsError):
+    """Raised by fancy_getopt in response to getopt.error -- ie. an
+    error in the command line usage."""
+
+    pass
+
+
+class DistutilsFileError(DistutilsError):
+    """Any problems in the filesystem: expected file not found, etc.
+    Typically this is for problems that we detect before OSError
+    could be raised."""
+
+    pass
+
+
+class DistutilsOptionError(DistutilsError):
+    """Syntactic/semantic errors in command options, such as use of
+    mutually conflicting options, or inconsistent options,
+    badly-spelled values, etc.  No distinction is made between option
+    values originating in the setup script, the command line, config
+    files, or what-have-you -- but if we *know* something originated in
+    the setup script, we'll raise DistutilsSetupError instead."""
+
+    pass
+
+
+class DistutilsSetupError(DistutilsError):
+    """For errors that can be definitely blamed on the setup script,
+    such as invalid keyword arguments to 'setup()'."""
+
+    pass
+
+
+class DistutilsPlatformError(DistutilsError):
+    """We don't know how to do something on the current platform (but
+    we do know how to do it on some platform) -- eg. trying to compile
+    C files on a platform not supported by a CCompiler subclass."""
+
+    pass
+
+
+class DistutilsExecError(DistutilsError):
+    """Any problems executing an external program (such as the C
+    compiler, when compiling C files)."""
+
+    pass
+
+
+class DistutilsInternalError(DistutilsError):
+    """Internal inconsistencies or impossibilities (obviously, this
+    should never be seen if the code is working!)."""
+
+    pass
+
+
+class DistutilsTemplateError(DistutilsError):
+    """Syntax error in a file list template."""
+
+
+class DistutilsByteCompileError(DistutilsError):
+    """Byte compile error."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/extension.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/extension.py
new file mode 100644
index 0000000..f514112
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/extension.py
@@ -0,0 +1,258 @@
+"""distutils.extension
+
+Provides the Extension class, used to describe C/C++ extension
+modules in setup scripts."""
+
+from __future__ import annotations
+
+import os
+import warnings
+from collections.abc import Iterable
+
+# This class is really only used by the "build_ext" command, so it might
+# make sense to put it in distutils.command.build_ext.  However, that
+# module is already big enough, and I want to make this class a bit more
+# complex to simplify some common cases ("foo" module in "foo.c") and do
+# better error-checking ("foo.c" actually exists).
+#
+# Also, putting this in build_ext.py means every setup script would have to
+# import that large-ish module (indirectly, through distutils.core) in
+# order to do anything.
+
+
+class Extension:
+    """Just a collection of attributes that describes an extension
+    module and everything needed to build it (hopefully in a portable
+    way, but there are hooks that let you be as unportable as you need).
+
+    Instance attributes:
+      name : string
+        the full name of the extension, including any packages -- ie.
+        *not* a filename or pathname, but Python dotted name
+      sources : Iterable[string | os.PathLike]
+        iterable of source filenames (except strings, which could be misinterpreted
+        as a single filename), relative to the distribution root (where the setup
+        script lives), in Unix form (slash-separated) for portability. Can be any
+        non-string iterable (list, tuple, set, etc.) containing strings or
+        PathLike objects. Source files may be C, C++, SWIG (.i), platform-specific
+        resource files, or whatever else is recognized by the "build_ext" command
+        as source for a Python extension.
+      include_dirs : [string]
+        list of directories to search for C/C++ header files (in Unix
+        form for portability)
+      define_macros : [(name : string, value : string|None)]
+        list of macros to define; each macro is defined using a 2-tuple,
+        where 'value' is either the string to define it to or None to
+        define it without a particular value (equivalent of "#define
+        FOO" in source or -DFOO on Unix C compiler command line)
+      undef_macros : [string]
+        list of macros to undefine explicitly
+      library_dirs : [string]
+        list of directories to search for C/C++ libraries at link time
+      libraries : [string]
+        list of library names (not filenames or paths) to link against
+      runtime_library_dirs : [string]
+        list of directories to search for C/C++ libraries at run time
+        (for shared extensions, this is when the extension is loaded)
+      extra_objects : [string]
+        list of extra files to link with (eg. object files not implied
+        by 'sources', static library that must be explicitly specified,
+        binary resource files, etc.)
+      extra_compile_args : [string]
+        any extra platform- and compiler-specific information to use
+        when compiling the source files in 'sources'.  For platforms and
+        compilers where "command line" makes sense, this is typically a
+        list of command-line arguments, but for other platforms it could
+        be anything.
+      extra_link_args : [string]
+        any extra platform- and compiler-specific information to use
+        when linking object files together to create the extension (or
+        to create a new static Python interpreter).  Similar
+        interpretation as for 'extra_compile_args'.
+      export_symbols : [string]
+        list of symbols to be exported from a shared extension.  Not
+        used on all platforms, and not generally necessary for Python
+        extensions, which typically export exactly one symbol: "init" +
+        extension_name.
+      swig_opts : [string]
+        any extra options to pass to SWIG if a source file has the .i
+        extension.
+      depends : [string]
+        list of files that the extension depends on
+      language : string
+        extension language (i.e. "c", "c++", "objc"). Will be detected
+        from the source extensions if not provided.
+      optional : boolean
+        specifies that a build failure in the extension should not abort the
+        build process, but simply not install the failing extension.
+    """
+
+    # When adding arguments to this constructor, be sure to update
+    # setup_keywords in core.py.
+    def __init__(
+        self,
+        name: str,
+        sources: Iterable[str | os.PathLike[str]],
+        include_dirs: list[str] | None = None,
+        define_macros: list[tuple[str, str | None]] | None = None,
+        undef_macros: list[str] | None = None,
+        library_dirs: list[str] | None = None,
+        libraries: list[str] | None = None,
+        runtime_library_dirs: list[str] | None = None,
+        extra_objects: list[str] | None = None,
+        extra_compile_args: list[str] | None = None,
+        extra_link_args: list[str] | None = None,
+        export_symbols: list[str] | None = None,
+        swig_opts: list[str] | None = None,
+        depends: list[str] | None = None,
+        language: str | None = None,
+        optional: bool | None = None,
+        **kw,  # To catch unknown keywords
+    ):
+        if not isinstance(name, str):
+            raise TypeError("'name' must be a string")
+
+        # handle the string case first; since strings are iterable, disallow them
+        if isinstance(sources, str):
+            raise TypeError(
+                "'sources' must be an iterable of strings or PathLike objects, not a string"
+            )
+
+        # now we check if it's iterable and contains valid types
+        try:
+            self.sources = list(map(os.fspath, sources))
+        except TypeError:
+            raise TypeError(
+                "'sources' must be an iterable of strings or PathLike objects"
+            )
+
+        self.name = name
+        self.include_dirs = include_dirs or []
+        self.define_macros = define_macros or []
+        self.undef_macros = undef_macros or []
+        self.library_dirs = library_dirs or []
+        self.libraries = libraries or []
+        self.runtime_library_dirs = runtime_library_dirs or []
+        self.extra_objects = extra_objects or []
+        self.extra_compile_args = extra_compile_args or []
+        self.extra_link_args = extra_link_args or []
+        self.export_symbols = export_symbols or []
+        self.swig_opts = swig_opts or []
+        self.depends = depends or []
+        self.language = language
+        self.optional = optional
+
+        # If there are unknown keyword options, warn about them
+        if len(kw) > 0:
+            options = [repr(option) for option in kw]
+            options = ', '.join(sorted(options))
+            msg = f"Unknown Extension options: {options}"
+            warnings.warn(msg)
+
+    def __repr__(self):
+        return f'<{self.__class__.__module__}.{self.__class__.__qualname__}({self.name!r}) at {id(self):#x}>'
+
+
+def read_setup_file(filename):  # noqa: C901
+    """Reads a Setup file and returns Extension instances."""
+    from distutils.sysconfig import _variable_rx, expand_makefile_vars, parse_makefile
+    from distutils.text_file import TextFile
+    from distutils.util import split_quoted
+
+    # First pass over the file to gather "VAR = VALUE" assignments.
+    vars = parse_makefile(filename)
+
+    # Second pass to gobble up the real content: lines of the form
+    #    ... [ ...] [ ...] [ ...]
+    file = TextFile(
+        filename,
+        strip_comments=True,
+        skip_blanks=True,
+        join_lines=True,
+        lstrip_ws=True,
+        rstrip_ws=True,
+    )
+    try:
+        extensions = []
+
+        while True:
+            line = file.readline()
+            if line is None:  # eof
+                break
+            if _variable_rx.match(line):  # VAR=VALUE, handled in first pass
+                continue
+
+            if line[0] == line[-1] == "*":
+                file.warn(f"'{line}' lines not handled yet")
+                continue
+
+            line = expand_makefile_vars(line, vars)
+            words = split_quoted(line)
+
+            # NB. this parses a slightly different syntax than the old
+            # makesetup script: here, there must be exactly one extension per
+            # line, and it must be the first word of the line.  I have no idea
+            # why the old syntax supported multiple extensions per line, as
+            # they all wind up being the same.
+
+            module = words[0]
+            ext = Extension(module, [])
+            append_next_word = None
+
+            for word in words[1:]:
+                if append_next_word is not None:
+                    append_next_word.append(word)
+                    append_next_word = None
+                    continue
+
+                suffix = os.path.splitext(word)[1]
+                switch = word[0:2]
+                value = word[2:]
+
+                if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"):
+                    # hmm, should we do something about C vs. C++ sources?
+                    # or leave it up to the CCompiler implementation to
+                    # worry about?
+                    ext.sources.append(word)
+                elif switch == "-I":
+                    ext.include_dirs.append(value)
+                elif switch == "-D":
+                    equals = value.find("=")
+                    if equals == -1:  # bare "-DFOO" -- no value
+                        ext.define_macros.append((value, None))
+                    else:  # "-DFOO=blah"
+                        ext.define_macros.append((value[0:equals], value[equals + 2 :]))
+                elif switch == "-U":
+                    ext.undef_macros.append(value)
+                elif switch == "-C":  # only here 'cause makesetup has it!
+                    ext.extra_compile_args.append(word)
+                elif switch == "-l":
+                    ext.libraries.append(value)
+                elif switch == "-L":
+                    ext.library_dirs.append(value)
+                elif switch == "-R":
+                    ext.runtime_library_dirs.append(value)
+                elif word == "-rpath":
+                    append_next_word = ext.runtime_library_dirs
+                elif word == "-Xlinker":
+                    append_next_word = ext.extra_link_args
+                elif word == "-Xcompiler":
+                    append_next_word = ext.extra_compile_args
+                elif switch == "-u":
+                    ext.extra_link_args.append(word)
+                    if not value:
+                        append_next_word = ext.extra_link_args
+                elif suffix in (".a", ".so", ".sl", ".o", ".dylib"):
+                    # NB. a really faithful emulation of makesetup would
+                    # append a .o file to extra_objects only if it
+                    # had a slash in it; otherwise, it would s/.o/.c/
+                    # and append it to sources.  Hmmmm.
+                    ext.extra_objects.append(word)
+                else:
+                    file.warn(f"unrecognized argument '{word}'")
+
+            extensions.append(ext)
+    finally:
+        file.close()
+
+    return extensions
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/fancy_getopt.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/fancy_getopt.py
new file mode 100644
index 0000000..1a1d3a0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/fancy_getopt.py
@@ -0,0 +1,471 @@
+"""distutils.fancy_getopt
+
+Wrapper around the standard getopt module that provides the following
+additional features:
+  * short and long options are tied together
+  * options have help strings, so fancy_getopt could potentially
+    create a complete usage summary
+  * options set attributes of a passed-in object
+"""
+
+from __future__ import annotations
+
+import getopt
+import re
+import string
+import sys
+from collections.abc import Sequence
+from typing import Any
+
+from .errors import DistutilsArgError, DistutilsGetoptError
+
+# Much like command_re in distutils.core, this is close to but not quite
+# the same as a Python NAME -- except, in the spirit of most GNU
+# utilities, we use '-' in place of '_'.  (The spirit of LISP lives on!)
+# The similarities to NAME are again not a coincidence...
+longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)'
+longopt_re = re.compile(rf'^{longopt_pat}$')
+
+# For recognizing "negative alias" options, eg. "quiet=!verbose"
+neg_alias_re = re.compile(f"^({longopt_pat})=!({longopt_pat})$")
+
+# This is used to translate long options to legitimate Python identifiers
+# (for use as attributes of some object).
+longopt_xlate = str.maketrans('-', '_')
+
+
+class FancyGetopt:
+    """Wrapper around the standard 'getopt()' module that provides some
+    handy extra functionality:
+      * short and long options are tied together
+      * options have help strings, and help text can be assembled
+        from them
+      * options set attributes of a passed-in object
+      * boolean options can have "negative aliases" -- eg. if
+        --quiet is the "negative alias" of --verbose, then "--quiet"
+        on the command line sets 'verbose' to false
+    """
+
+    def __init__(self, option_table=None):
+        # The option table is (currently) a list of tuples.  The
+        # tuples may have 3 or four values:
+        #   (long_option, short_option, help_string [, repeatable])
+        # if an option takes an argument, its long_option should have '='
+        # appended; short_option should just be a single character, no ':'
+        # in any case.  If a long_option doesn't have a corresponding
+        # short_option, short_option should be None.  All option tuples
+        # must have long options.
+        self.option_table = option_table
+
+        # 'option_index' maps long option names to entries in the option
+        # table (ie. those 3-tuples).
+        self.option_index = {}
+        if self.option_table:
+            self._build_index()
+
+        # 'alias' records (duh) alias options; {'foo': 'bar'} means
+        # --foo is an alias for --bar
+        self.alias = {}
+
+        # 'negative_alias' keeps track of options that are the boolean
+        # opposite of some other option
+        self.negative_alias = {}
+
+        # These keep track of the information in the option table.  We
+        # don't actually populate these structures until we're ready to
+        # parse the command-line, since the 'option_table' passed in here
+        # isn't necessarily the final word.
+        self.short_opts = []
+        self.long_opts = []
+        self.short2long = {}
+        self.attr_name = {}
+        self.takes_arg = {}
+
+        # And 'option_order' is filled up in 'getopt()'; it records the
+        # original order of options (and their values) on the command-line,
+        # but expands short options, converts aliases, etc.
+        self.option_order = []
+
+    def _build_index(self):
+        self.option_index.clear()
+        for option in self.option_table:
+            self.option_index[option[0]] = option
+
+    def set_option_table(self, option_table):
+        self.option_table = option_table
+        self._build_index()
+
+    def add_option(self, long_option, short_option=None, help_string=None):
+        if long_option in self.option_index:
+            raise DistutilsGetoptError(
+                f"option conflict: already an option '{long_option}'"
+            )
+        else:
+            option = (long_option, short_option, help_string)
+            self.option_table.append(option)
+            self.option_index[long_option] = option
+
+    def has_option(self, long_option):
+        """Return true if the option table for this parser has an
+        option with long name 'long_option'."""
+        return long_option in self.option_index
+
+    def get_attr_name(self, long_option):
+        """Translate long option name 'long_option' to the form it
+        has as an attribute of some object: ie., translate hyphens
+        to underscores."""
+        return long_option.translate(longopt_xlate)
+
+    def _check_alias_dict(self, aliases, what):
+        assert isinstance(aliases, dict)
+        for alias, opt in aliases.items():
+            if alias not in self.option_index:
+                raise DistutilsGetoptError(
+                    f"invalid {what} '{alias}': option '{alias}' not defined"
+                )
+            if opt not in self.option_index:
+                raise DistutilsGetoptError(
+                    f"invalid {what} '{alias}': aliased option '{opt}' not defined"
+                )
+
+    def set_aliases(self, alias):
+        """Set the aliases for this option parser."""
+        self._check_alias_dict(alias, "alias")
+        self.alias = alias
+
+    def set_negative_aliases(self, negative_alias):
+        """Set the negative aliases for this option parser.
+        'negative_alias' should be a dictionary mapping option names to
+        option names, both the key and value must already be defined
+        in the option table."""
+        self._check_alias_dict(negative_alias, "negative alias")
+        self.negative_alias = negative_alias
+
+    def _grok_option_table(self):  # noqa: C901
+        """Populate the various data structures that keep tabs on the
+        option table.  Called by 'getopt()' before it can do anything
+        worthwhile.
+        """
+        self.long_opts = []
+        self.short_opts = []
+        self.short2long.clear()
+        self.repeat = {}
+
+        for option in self.option_table:
+            if len(option) == 3:
+                long, short, help = option
+                repeat = 0
+            elif len(option) == 4:
+                long, short, help, repeat = option
+            else:
+                # the option table is part of the code, so simply
+                # assert that it is correct
+                raise ValueError(f"invalid option tuple: {option!r}")
+
+            # Type- and value-check the option names
+            if not isinstance(long, str) or len(long) < 2:
+                raise DistutilsGetoptError(
+                    f"invalid long option '{long}': must be a string of length >= 2"
+                )
+
+            if not ((short is None) or (isinstance(short, str) and len(short) == 1)):
+                raise DistutilsGetoptError(
+                    f"invalid short option '{short}': must a single character or None"
+                )
+
+            self.repeat[long] = repeat
+            self.long_opts.append(long)
+
+            if long[-1] == '=':  # option takes an argument?
+                if short:
+                    short = short + ':'
+                long = long[0:-1]
+                self.takes_arg[long] = True
+            else:
+                # Is option is a "negative alias" for some other option (eg.
+                # "quiet" == "!verbose")?
+                alias_to = self.negative_alias.get(long)
+                if alias_to is not None:
+                    if self.takes_arg[alias_to]:
+                        raise DistutilsGetoptError(
+                            f"invalid negative alias '{long}': "
+                            f"aliased option '{alias_to}' takes a value"
+                        )
+
+                    self.long_opts[-1] = long  # XXX redundant?!
+                self.takes_arg[long] = False
+
+            # If this is an alias option, make sure its "takes arg" flag is
+            # the same as the option it's aliased to.
+            alias_to = self.alias.get(long)
+            if alias_to is not None:
+                if self.takes_arg[long] != self.takes_arg[alias_to]:
+                    raise DistutilsGetoptError(
+                        f"invalid alias '{long}': inconsistent with "
+                        f"aliased option '{alias_to}' (one of them takes a value, "
+                        "the other doesn't"
+                    )
+
+            # Now enforce some bondage on the long option name, so we can
+            # later translate it to an attribute name on some object.  Have
+            # to do this a bit late to make sure we've removed any trailing
+            # '='.
+            if not longopt_re.match(long):
+                raise DistutilsGetoptError(
+                    f"invalid long option name '{long}' "
+                    "(must be letters, numbers, hyphens only"
+                )
+
+            self.attr_name[long] = self.get_attr_name(long)
+            if short:
+                self.short_opts.append(short)
+                self.short2long[short[0]] = long
+
+    def getopt(self, args: Sequence[str] | None = None, object=None):  # noqa: C901
+        """Parse command-line options in args. Store as attributes on object.
+
+        If 'args' is None or not supplied, uses 'sys.argv[1:]'.  If
+        'object' is None or not supplied, creates a new OptionDummy
+        object, stores option values there, and returns a tuple (args,
+        object).  If 'object' is supplied, it is modified in place and
+        'getopt()' just returns 'args'; in both cases, the returned
+        'args' is a modified copy of the passed-in 'args' list, which
+        is left untouched.
+        """
+        if args is None:
+            args = sys.argv[1:]
+        if object is None:
+            object = OptionDummy()
+            created_object = True
+        else:
+            created_object = False
+
+        self._grok_option_table()
+
+        short_opts = ' '.join(self.short_opts)
+        try:
+            opts, args = getopt.getopt(args, short_opts, self.long_opts)
+        except getopt.error as msg:
+            raise DistutilsArgError(msg)
+
+        for opt, val in opts:
+            if len(opt) == 2 and opt[0] == '-':  # it's a short option
+                opt = self.short2long[opt[1]]
+            else:
+                assert len(opt) > 2 and opt[:2] == '--'
+                opt = opt[2:]
+
+            alias = self.alias.get(opt)
+            if alias:
+                opt = alias
+
+            if not self.takes_arg[opt]:  # boolean option?
+                assert val == '', "boolean option can't have value"
+                alias = self.negative_alias.get(opt)
+                if alias:
+                    opt = alias
+                    val = 0
+                else:
+                    val = 1
+
+            attr = self.attr_name[opt]
+            # The only repeating option at the moment is 'verbose'.
+            # It has a negative option -q quiet, which should set verbose = False.
+            if val and self.repeat.get(attr) is not None:
+                val = getattr(object, attr, 0) + 1
+            setattr(object, attr, val)
+            self.option_order.append((opt, val))
+
+        # for opts
+        if created_object:
+            return args, object
+        else:
+            return args
+
+    def get_option_order(self):
+        """Returns the list of (option, value) tuples processed by the
+        previous run of 'getopt()'.  Raises RuntimeError if
+        'getopt()' hasn't been called yet.
+        """
+        if self.option_order is None:
+            raise RuntimeError("'getopt()' hasn't been called yet")
+        else:
+            return self.option_order
+
+    def generate_help(self, header=None):  # noqa: C901
+        """Generate help text (a list of strings, one per suggested line of
+        output) from the option table for this FancyGetopt object.
+        """
+        # Blithely assume the option table is good: probably wouldn't call
+        # 'generate_help()' unless you've already called 'getopt()'.
+
+        # First pass: determine maximum length of long option names
+        max_opt = 0
+        for option in self.option_table:
+            long = option[0]
+            short = option[1]
+            ell = len(long)
+            if long[-1] == '=':
+                ell = ell - 1
+            if short is not None:
+                ell = ell + 5  # " (-x)" where short == 'x'
+            if ell > max_opt:
+                max_opt = ell
+
+        opt_width = max_opt + 2 + 2 + 2  # room for indent + dashes + gutter
+
+        # Typical help block looks like this:
+        #   --foo       controls foonabulation
+        # Help block for longest option looks like this:
+        #   --flimflam  set the flim-flam level
+        # and with wrapped text:
+        #   --flimflam  set the flim-flam level (must be between
+        #               0 and 100, except on Tuesdays)
+        # Options with short names will have the short name shown (but
+        # it doesn't contribute to max_opt):
+        #   --foo (-f)  controls foonabulation
+        # If adding the short option would make the left column too wide,
+        # we push the explanation off to the next line
+        #   --flimflam (-l)
+        #               set the flim-flam level
+        # Important parameters:
+        #   - 2 spaces before option block start lines
+        #   - 2 dashes for each long option name
+        #   - min. 2 spaces between option and explanation (gutter)
+        #   - 5 characters (incl. space) for short option name
+
+        # Now generate lines of help text.  (If 80 columns were good enough
+        # for Jesus, then 78 columns are good enough for me!)
+        line_width = 78
+        text_width = line_width - opt_width
+        big_indent = ' ' * opt_width
+        if header:
+            lines = [header]
+        else:
+            lines = ['Option summary:']
+
+        for option in self.option_table:
+            long, short, help = option[:3]
+            text = wrap_text(help, text_width)
+            if long[-1] == '=':
+                long = long[0:-1]
+
+            # Case 1: no short option at all (makes life easy)
+            if short is None:
+                if text:
+                    lines.append(f"  --{long:<{max_opt}}  {text[0]}")
+                else:
+                    lines.append(f"  --{long:<{max_opt}}")
+
+            # Case 2: we have a short option, so we have to include it
+            # just after the long option
+            else:
+                opt_names = f"{long} (-{short})"
+                if text:
+                    lines.append(f"  --{opt_names:<{max_opt}}  {text[0]}")
+                else:
+                    lines.append(f"  --{opt_names:<{max_opt}}")
+
+            for ell in text[1:]:
+                lines.append(big_indent + ell)
+        return lines
+
+    def print_help(self, header=None, file=None):
+        if file is None:
+            file = sys.stdout
+        for line in self.generate_help(header):
+            file.write(line + "\n")
+
+
+def fancy_getopt(options, negative_opt, object, args: Sequence[str] | None):
+    parser = FancyGetopt(options)
+    parser.set_negative_aliases(negative_opt)
+    return parser.getopt(args, object)
+
+
+WS_TRANS = {ord(_wschar): ' ' for _wschar in string.whitespace}
+
+
+def wrap_text(text, width):
+    """wrap_text(text : string, width : int) -> [string]
+
+    Split 'text' into multiple lines of no more than 'width' characters
+    each, and return the list of strings that results.
+    """
+    if text is None:
+        return []
+    if len(text) <= width:
+        return [text]
+
+    text = text.expandtabs()
+    text = text.translate(WS_TRANS)
+    chunks = re.split(r'( +|-+)', text)
+    chunks = [ch for ch in chunks if ch]  # ' - ' results in empty strings
+    lines = []
+
+    while chunks:
+        cur_line = []  # list of chunks (to-be-joined)
+        cur_len = 0  # length of current line
+
+        while chunks:
+            ell = len(chunks[0])
+            if cur_len + ell <= width:  # can squeeze (at least) this chunk in
+                cur_line.append(chunks[0])
+                del chunks[0]
+                cur_len = cur_len + ell
+            else:  # this line is full
+                # drop last chunk if all space
+                if cur_line and cur_line[-1][0] == ' ':
+                    del cur_line[-1]
+                break
+
+        if chunks:  # any chunks left to process?
+            # if the current line is still empty, then we had a single
+            # chunk that's too big too fit on a line -- so we break
+            # down and break it up at the line width
+            if cur_len == 0:
+                cur_line.append(chunks[0][0:width])
+                chunks[0] = chunks[0][width:]
+
+            # all-whitespace chunks at the end of a line can be discarded
+            # (and we know from the re.split above that if a chunk has
+            # *any* whitespace, it is *all* whitespace)
+            if chunks[0][0] == ' ':
+                del chunks[0]
+
+        # and store this line in the list-of-all-lines -- as a single
+        # string, of course!
+        lines.append(''.join(cur_line))
+
+    return lines
+
+
+def translate_longopt(opt):
+    """Convert a long option name to a valid Python identifier by
+    changing "-" to "_".
+    """
+    return opt.translate(longopt_xlate)
+
+
+class OptionDummy:
+    """Dummy class just used as a place to hold command-line option
+    values as instance attributes."""
+
+    def __init__(self, options: Sequence[Any] = []):
+        """Create a new OptionDummy instance.  The attributes listed in
+        'options' will be initialized to None."""
+        for opt in options:
+            setattr(self, opt, None)
+
+
+if __name__ == "__main__":
+    text = """\
+Tra-la-la, supercalifragilisticexpialidocious.
+How *do* you spell that odd word, anyways?
+(Someone ask Mary -- she'll know [or she'll
+say, "How should I know?"].)"""
+
+    for w in (10, 20, 30, 40):
+        print(f"width: {w}")
+        print("\n".join(wrap_text(text, w)))
+        print()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/file_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/file_util.py
new file mode 100644
index 0000000..a9724b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/file_util.py
@@ -0,0 +1,228 @@
+"""distutils.file_util
+
+Utility functions for operating on single files.
+"""
+
+import os
+
+from ._log import log
+from .errors import DistutilsFileError
+
+# for generating verbose output in 'copy_file()'
+_copy_action = {None: 'copying', 'hard': 'hard linking', 'sym': 'symbolically linking'}
+
+
+def _copy_file_contents(src, dst, buffer_size=16 * 1024):  # noqa: C901
+    """Copy the file 'src' to 'dst'; both must be filenames.  Any error
+    opening either file, reading from 'src', or writing to 'dst', raises
+    DistutilsFileError.  Data is read/written in chunks of 'buffer_size'
+    bytes (default 16k).  No attempt is made to handle anything apart from
+    regular files.
+    """
+    # Stolen from shutil module in the standard library, but with
+    # custom error-handling added.
+    fsrc = None
+    fdst = None
+    try:
+        try:
+            fsrc = open(src, 'rb')
+        except OSError as e:
+            raise DistutilsFileError(f"could not open '{src}': {e.strerror}")
+
+        if os.path.exists(dst):
+            try:
+                os.unlink(dst)
+            except OSError as e:
+                raise DistutilsFileError(f"could not delete '{dst}': {e.strerror}")
+
+        try:
+            fdst = open(dst, 'wb')
+        except OSError as e:
+            raise DistutilsFileError(f"could not create '{dst}': {e.strerror}")
+
+        while True:
+            try:
+                buf = fsrc.read(buffer_size)
+            except OSError as e:
+                raise DistutilsFileError(f"could not read from '{src}': {e.strerror}")
+
+            if not buf:
+                break
+
+            try:
+                fdst.write(buf)
+            except OSError as e:
+                raise DistutilsFileError(f"could not write to '{dst}': {e.strerror}")
+    finally:
+        if fdst:
+            fdst.close()
+        if fsrc:
+            fsrc.close()
+
+
+def copy_file(  # noqa: C901
+    src,
+    dst,
+    preserve_mode=True,
+    preserve_times=True,
+    update=False,
+    link=None,
+    verbose=True,
+):
+    """Copy a file 'src' to 'dst'.  If 'dst' is a directory, then 'src' is
+    copied there with the same name; otherwise, it must be a filename.  (If
+    the file exists, it will be ruthlessly clobbered.)  If 'preserve_mode'
+    is true (the default), the file's mode (type and permission bits, or
+    whatever is analogous on the current platform) is copied.  If
+    'preserve_times' is true (the default), the last-modified and
+    last-access times are copied as well.  If 'update' is true, 'src' will
+    only be copied if 'dst' does not exist, or if 'dst' does exist but is
+    older than 'src'.
+
+    'link' allows you to make hard links (os.link) or symbolic links
+    (os.symlink) instead of copying: set it to "hard" or "sym"; if it is
+    None (the default), files are copied.  Don't set 'link' on systems that
+    don't support it: 'copy_file()' doesn't check if hard or symbolic
+    linking is available. If hardlink fails, falls back to
+    _copy_file_contents().
+
+    Under Mac OS, uses the native file copy function in macostools; on
+    other systems, uses '_copy_file_contents()' to copy file contents.
+
+    Return a tuple (dest_name, copied): 'dest_name' is the actual name of
+    the output file, and 'copied' is true if the file was copied.
+    """
+    # XXX if the destination file already exists, we clobber it if
+    # copying, but blow up if linking.  Hmmm.  And I don't know what
+    # macostools.copyfile() does.  Should definitely be consistent, and
+    # should probably blow up if destination exists and we would be
+    # changing it (ie. it's not already a hard/soft link to src OR
+    # (not update) and (src newer than dst).
+
+    from distutils._modified import newer
+    from stat import S_IMODE, ST_ATIME, ST_MODE, ST_MTIME
+
+    if not os.path.isfile(src):
+        raise DistutilsFileError(
+            f"can't copy '{src}': doesn't exist or not a regular file"
+        )
+
+    if os.path.isdir(dst):
+        dir = dst
+        dst = os.path.join(dst, os.path.basename(src))
+    else:
+        dir = os.path.dirname(dst)
+
+    if update and not newer(src, dst):
+        if verbose >= 1:
+            log.debug("not copying %s (output up-to-date)", src)
+        return (dst, False)
+
+    try:
+        action = _copy_action[link]
+    except KeyError:
+        raise ValueError(f"invalid value '{link}' for 'link' argument")
+
+    if verbose >= 1:
+        if os.path.basename(dst) == os.path.basename(src):
+            log.info("%s %s -> %s", action, src, dir)
+        else:
+            log.info("%s %s -> %s", action, src, dst)
+
+    # If linking (hard or symbolic), use the appropriate system call
+    # (Unix only, of course, but that's the caller's responsibility)
+    if link == 'hard':
+        if not (os.path.exists(dst) and os.path.samefile(src, dst)):
+            try:
+                os.link(src, dst)
+            except OSError:
+                # If hard linking fails, fall back on copying file
+                # (some special filesystems don't support hard linking
+                #  even under Unix, see issue #8876).
+                pass
+            else:
+                return (dst, True)
+    elif link == 'sym':
+        if not (os.path.exists(dst) and os.path.samefile(src, dst)):
+            os.symlink(src, dst)
+            return (dst, True)
+
+    # Otherwise (non-Mac, not linking), copy the file contents and
+    # (optionally) copy the times and mode.
+    _copy_file_contents(src, dst)
+    if preserve_mode or preserve_times:
+        st = os.stat(src)
+
+        # According to David Ascher , utime() should be done
+        # before chmod() (at least under NT).
+        if preserve_times:
+            os.utime(dst, (st[ST_ATIME], st[ST_MTIME]))
+        if preserve_mode:
+            os.chmod(dst, S_IMODE(st[ST_MODE]))
+
+    return (dst, True)
+
+
+# XXX I suspect this is Unix-specific -- need porting help!
+def move_file(src, dst, verbose=True):  # noqa: C901
+    """Move a file 'src' to 'dst'.  If 'dst' is a directory, the file will
+    be moved into it with the same name; otherwise, 'src' is just renamed
+    to 'dst'.  Return the new full name of the file.
+
+    Handles cross-device moves on Unix using 'copy_file()'.  What about
+    other systems???
+    """
+    import errno
+    from os.path import basename, dirname, exists, isdir, isfile
+
+    if verbose >= 1:
+        log.info("moving %s -> %s", src, dst)
+
+    if not isfile(src):
+        raise DistutilsFileError(f"can't move '{src}': not a regular file")
+
+    if isdir(dst):
+        dst = os.path.join(dst, basename(src))
+    elif exists(dst):
+        raise DistutilsFileError(
+            f"can't move '{src}': destination '{dst}' already exists"
+        )
+
+    if not isdir(dirname(dst)):
+        raise DistutilsFileError(
+            f"can't move '{src}': destination '{dst}' not a valid path"
+        )
+
+    copy_it = False
+    try:
+        os.rename(src, dst)
+    except OSError as e:
+        (num, msg) = e.args
+        if num == errno.EXDEV:
+            copy_it = True
+        else:
+            raise DistutilsFileError(f"couldn't move '{src}' to '{dst}': {msg}")
+
+    if copy_it:
+        copy_file(src, dst, verbose=verbose)
+        try:
+            os.unlink(src)
+        except OSError as e:
+            (num, msg) = e.args
+            try:
+                os.unlink(dst)
+            except OSError:
+                pass
+            raise DistutilsFileError(
+                f"couldn't move '{src}' to '{dst}' by copy/delete: "
+                f"delete '{src}' failed: {msg}"
+            )
+    return dst
+
+
+def write_file(filename, contents):
+    """Create a file with the specified name and write 'contents' (a
+    sequence of strings without line terminators) to it.
+    """
+    with open(filename, 'w', encoding='utf-8') as f:
+        f.writelines(line + '\n' for line in contents)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/filelist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/filelist.py
new file mode 100644
index 0000000..70dc0fd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/filelist.py
@@ -0,0 +1,431 @@
+"""distutils.filelist
+
+Provides the FileList class, used for poking about the filesystem
+and building lists of files.
+"""
+
+from __future__ import annotations
+
+import fnmatch
+import functools
+import os
+import re
+from collections.abc import Iterable
+from typing import Literal, overload
+
+from ._log import log
+from .errors import DistutilsInternalError, DistutilsTemplateError
+from .util import convert_path
+
+
+class FileList:
+    """A list of files built by on exploring the filesystem and filtered by
+    applying various patterns to what we find there.
+
+    Instance attributes:
+      dir
+        directory from which files will be taken -- only used if
+        'allfiles' not supplied to constructor
+      files
+        list of filenames currently being built/filtered/manipulated
+      allfiles
+        complete list of files under consideration (ie. without any
+        filtering applied)
+    """
+
+    def __init__(self, warn: object = None, debug_print: object = None) -> None:
+        # ignore argument to FileList, but keep them for backwards
+        # compatibility
+        self.allfiles: Iterable[str] | None = None
+        self.files: list[str] = []
+
+    def set_allfiles(self, allfiles: Iterable[str]) -> None:
+        self.allfiles = allfiles
+
+    def findall(self, dir: str | os.PathLike[str] = os.curdir) -> None:
+        self.allfiles = findall(dir)
+
+    def debug_print(self, msg: object) -> None:
+        """Print 'msg' to stdout if the global DEBUG (taken from the
+        DISTUTILS_DEBUG environment variable) flag is true.
+        """
+        from distutils.debug import DEBUG
+
+        if DEBUG:
+            print(msg)
+
+    # Collection methods
+
+    def append(self, item: str) -> None:
+        self.files.append(item)
+
+    def extend(self, items: Iterable[str]) -> None:
+        self.files.extend(items)
+
+    def sort(self) -> None:
+        # Not a strict lexical sort!
+        sortable_files = sorted(map(os.path.split, self.files))
+        self.files = []
+        for sort_tuple in sortable_files:
+            self.files.append(os.path.join(*sort_tuple))
+
+    # Other miscellaneous utility methods
+
+    def remove_duplicates(self) -> None:
+        # Assumes list has been sorted!
+        for i in range(len(self.files) - 1, 0, -1):
+            if self.files[i] == self.files[i - 1]:
+                del self.files[i]
+
+    # "File template" methods
+
+    def _parse_template_line(self, line):
+        words = line.split()
+        action = words[0]
+
+        patterns = dir = dir_pattern = None
+
+        if action in ('include', 'exclude', 'global-include', 'global-exclude'):
+            if len(words) < 2:
+                raise DistutilsTemplateError(
+                    f"'{action}' expects   ..."
+                )
+            patterns = [convert_path(w) for w in words[1:]]
+        elif action in ('recursive-include', 'recursive-exclude'):
+            if len(words) < 3:
+                raise DistutilsTemplateError(
+                    f"'{action}' expects    ..."
+                )
+            dir = convert_path(words[1])
+            patterns = [convert_path(w) for w in words[2:]]
+        elif action in ('graft', 'prune'):
+            if len(words) != 2:
+                raise DistutilsTemplateError(
+                    f"'{action}' expects a single "
+                )
+            dir_pattern = convert_path(words[1])
+        else:
+            raise DistutilsTemplateError(f"unknown action '{action}'")
+
+        return (action, patterns, dir, dir_pattern)
+
+    def process_template_line(self, line: str) -> None:  # noqa: C901
+        # 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 (dir_pattern).
+        (action, patterns, dir, dir_pattern) = self._parse_template_line(line)
+
+        # 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':
+            self.debug_print("include " + ' '.join(patterns))
+            for pattern in patterns:
+                if not self.include_pattern(pattern, anchor=True):
+                    log.warning("warning: no files found matching '%s'", pattern)
+
+        elif action == 'exclude':
+            self.debug_print("exclude " + ' '.join(patterns))
+            for pattern in patterns:
+                if not self.exclude_pattern(pattern, anchor=True):
+                    log.warning(
+                        "warning: no previously-included files found matching '%s'",
+                        pattern,
+                    )
+
+        elif action == 'global-include':
+            self.debug_print("global-include " + ' '.join(patterns))
+            for pattern in patterns:
+                if not self.include_pattern(pattern, anchor=False):
+                    log.warning(
+                        (
+                            "warning: no files found matching '%s' "
+                            "anywhere in distribution"
+                        ),
+                        pattern,
+                    )
+
+        elif action == 'global-exclude':
+            self.debug_print("global-exclude " + ' '.join(patterns))
+            for pattern in patterns:
+                if not self.exclude_pattern(pattern, anchor=False):
+                    log.warning(
+                        (
+                            "warning: no previously-included files matching "
+                            "'%s' found anywhere in distribution"
+                        ),
+                        pattern,
+                    )
+
+        elif action == 'recursive-include':
+            self.debug_print("recursive-include {} {}".format(dir, ' '.join(patterns)))
+            for pattern in patterns:
+                if not self.include_pattern(pattern, prefix=dir):
+                    msg = "warning: no files found matching '%s' under directory '%s'"
+                    log.warning(msg, pattern, dir)
+
+        elif action == 'recursive-exclude':
+            self.debug_print("recursive-exclude {} {}".format(dir, ' '.join(patterns)))
+            for pattern in patterns:
+                if not self.exclude_pattern(pattern, prefix=dir):
+                    log.warning(
+                        (
+                            "warning: no previously-included files matching "
+                            "'%s' found under directory '%s'"
+                        ),
+                        pattern,
+                        dir,
+                    )
+
+        elif action == 'graft':
+            self.debug_print("graft " + dir_pattern)
+            if not self.include_pattern(None, prefix=dir_pattern):
+                log.warning("warning: no directories found matching '%s'", dir_pattern)
+
+        elif action == 'prune':
+            self.debug_print("prune " + dir_pattern)
+            if not self.exclude_pattern(None, prefix=dir_pattern):
+                log.warning(
+                    ("no previously-included directories found matching '%s'"),
+                    dir_pattern,
+                )
+        else:
+            raise DistutilsInternalError(
+                f"this cannot happen: invalid action '{action}'"
+            )
+
+    # Filtering/selection methods
+    @overload
+    def include_pattern(
+        self,
+        pattern: str,
+        anchor: bool = True,
+        prefix: str | None = None,
+        is_regex: Literal[False] = False,
+    ) -> bool: ...
+    @overload
+    def include_pattern(
+        self,
+        pattern: str | re.Pattern[str],
+        anchor: bool = True,
+        prefix: str | None = None,
+        *,
+        is_regex: Literal[True],
+    ) -> bool: ...
+    @overload
+    def include_pattern(
+        self,
+        pattern: str | re.Pattern[str],
+        anchor: bool,
+        prefix: str | None,
+        is_regex: Literal[True],
+    ) -> bool: ...
+    def include_pattern(
+        self,
+        pattern: str | re.Pattern,
+        anchor: bool = True,
+        prefix: str | None = None,
+        is_regex: bool = False,
+    ) -> bool:
+        """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, False otherwise.
+        """
+        # XXX docstring lying about what the special chars are?
+        files_found = False
+        pattern_re = translate_pattern(pattern, anchor, prefix, is_regex)
+        self.debug_print(f"include_pattern: applying regex r'{pattern_re.pattern}'")
+
+        # delayed loading of allfiles list
+        if self.allfiles is None:
+            self.findall()
+
+        for name in self.allfiles:
+            if pattern_re.search(name):
+                self.debug_print(" adding " + name)
+                self.files.append(name)
+                files_found = True
+        return files_found
+
+    @overload
+    def exclude_pattern(
+        self,
+        pattern: str,
+        anchor: bool = True,
+        prefix: str | None = None,
+        is_regex: Literal[False] = False,
+    ) -> bool: ...
+    @overload
+    def exclude_pattern(
+        self,
+        pattern: str | re.Pattern[str],
+        anchor: bool = True,
+        prefix: str | None = None,
+        *,
+        is_regex: Literal[True],
+    ) -> bool: ...
+    @overload
+    def exclude_pattern(
+        self,
+        pattern: str | re.Pattern[str],
+        anchor: bool,
+        prefix: str | None,
+        is_regex: Literal[True],
+    ) -> bool: ...
+    def exclude_pattern(
+        self,
+        pattern: str | re.Pattern,
+        anchor: bool = True,
+        prefix: str | None = None,
+        is_regex: bool = False,
+    ) -> bool:
+        """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, False otherwise.
+        """
+        files_found = False
+        pattern_re = translate_pattern(pattern, anchor, prefix, is_regex)
+        self.debug_print(f"exclude_pattern: applying regex r'{pattern_re.pattern}'")
+        for i in range(len(self.files) - 1, -1, -1):
+            if pattern_re.search(self.files[i]):
+                self.debug_print(" removing " + self.files[i])
+                del self.files[i]
+                files_found = True
+        return files_found
+
+
+# Utility functions
+
+
+def _find_all_simple(path):
+    """
+    Find all files under 'path'
+    """
+    all_unique = _UniqueDirs.filter(os.walk(path, followlinks=True))
+    results = (
+        os.path.join(base, file) for base, dirs, files in all_unique for file in files
+    )
+    return filter(os.path.isfile, results)
+
+
+class _UniqueDirs(set):
+    """
+    Exclude previously-seen dirs from walk results,
+    avoiding infinite recursion.
+    Ref https://bugs.python.org/issue44497.
+    """
+
+    def __call__(self, walk_item):
+        """
+        Given an item from an os.walk result, determine
+        if the item represents a unique dir for this instance
+        and if not, prevent further traversal.
+        """
+        base, dirs, files = walk_item
+        stat = os.stat(base)
+        candidate = stat.st_dev, stat.st_ino
+        found = candidate in self
+        if found:
+            del dirs[:]
+        self.add(candidate)
+        return not found
+
+    @classmethod
+    def filter(cls, items):
+        return filter(cls(), items)
+
+
+def findall(dir: str | os.PathLike[str] = os.curdir):
+    """
+    Find all files under 'dir' and return the list of full filenames.
+    Unless dir is '.', return full filenames with dir prepended.
+    """
+    files = _find_all_simple(dir)
+    if dir == os.curdir:
+        make_rel = functools.partial(os.path.relpath, start=dir)
+        files = map(make_rel, files)
+    return list(files)
+
+
+def glob_to_re(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 = rf'\1[^{sep}]'
+    pattern_re = re.sub(r'((?= 2:
+        set_threshold(logging.DEBUG)
+
+
+class Log(logging.Logger):
+    """distutils.log.Log is deprecated, please use an alternative from `logging`."""
+
+    def __init__(self, threshold=WARN):
+        warnings.warn(Log.__doc__)  # avoid DeprecationWarning to ensure warn is shown
+        super().__init__(__name__, level=threshold)
+
+    @property
+    def threshold(self):
+        return self.level
+
+    @threshold.setter
+    def threshold(self, level):
+        self.setLevel(level)
+
+    warn = logging.Logger.warning
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/spawn.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/spawn.py
new file mode 100644
index 0000000..e47f1d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/spawn.py
@@ -0,0 +1,130 @@
+"""distutils.spawn
+
+Provides the 'spawn()' function, a front-end to various platform-
+specific functions for launching another program in a sub-process.
+"""
+
+from __future__ import annotations
+
+import os
+import platform
+import shutil
+import subprocess
+import sys
+import warnings
+from collections.abc import Mapping, MutableSequence
+from typing import TYPE_CHECKING, TypeVar, overload
+
+from ._log import log
+from .debug import DEBUG
+from .errors import DistutilsExecError
+
+if TYPE_CHECKING:
+    from subprocess import _ENV
+
+
+_MappingT = TypeVar("_MappingT", bound=Mapping)
+
+
+def _debug(cmd):
+    """
+    Render a subprocess command differently depending on DEBUG.
+    """
+    return cmd if DEBUG else cmd[0]
+
+
+def _inject_macos_ver(env: _MappingT | None) -> _MappingT | dict[str, str | int] | None:
+    if platform.system() != 'Darwin':
+        return env
+
+    from .util import MACOSX_VERSION_VAR, get_macosx_target_ver
+
+    target_ver = get_macosx_target_ver()
+    update = {MACOSX_VERSION_VAR: target_ver} if target_ver else {}
+    return {**_resolve(env), **update}
+
+
+@overload
+def _resolve(env: None) -> os._Environ[str]: ...
+@overload
+def _resolve(env: _MappingT) -> _MappingT: ...
+def _resolve(env: _MappingT | None) -> _MappingT | os._Environ[str]:
+    return os.environ if env is None else env
+
+
+def spawn(
+    cmd: MutableSequence[bytes | str | os.PathLike[str]],
+    search_path: bool = True,
+    verbose: bool = False,
+    env: _ENV | None = None,
+) -> None:
+    """Run another program, specified as a command list 'cmd', in a new process.
+
+    'cmd' is just the argument list for the new process, ie.
+    cmd[0] is the program to run and cmd[1:] are the rest of its arguments.
+    There is no way to run a program with a name different from that of its
+    executable.
+
+    If 'search_path' is true (the default), the system's executable
+    search path will be used to find the program; otherwise, cmd[0]
+    must be the exact path to the executable.
+
+    Raise DistutilsExecError if running the program fails in any way; just
+    return on success.
+    """
+    log.info(subprocess.list2cmdline(cmd))
+
+    if search_path:
+        executable = shutil.which(cmd[0])
+        if executable is not None:
+            cmd[0] = executable
+
+    try:
+        subprocess.check_call(cmd, env=_inject_macos_ver(env))
+    except OSError as exc:
+        raise DistutilsExecError(
+            f"command {_debug(cmd)!r} failed: {exc.args[-1]}"
+        ) from exc
+    except subprocess.CalledProcessError as err:
+        raise DistutilsExecError(
+            f"command {_debug(cmd)!r} failed with exit code {err.returncode}"
+        ) from err
+
+
+def find_executable(executable: str, path: str | None = None) -> str | None:
+    """Tries to find 'executable' in the directories listed in 'path'.
+
+    A string listing directories separated by 'os.pathsep'; defaults to
+    os.environ['PATH'].  Returns the complete filename or None if not found.
+    """
+    warnings.warn(
+        'Use shutil.which instead of find_executable', DeprecationWarning, stacklevel=2
+    )
+    _, ext = os.path.splitext(executable)
+    if (sys.platform == 'win32') and (ext != '.exe'):
+        executable = executable + '.exe'
+
+    if os.path.isfile(executable):
+        return executable
+
+    if path is None:
+        path = os.environ.get('PATH', None)
+        # bpo-35755: Don't fall through if PATH is the empty string
+        if path is None:
+            try:
+                path = os.confstr("CS_PATH")
+            except (AttributeError, ValueError):
+                # os.confstr() or CS_PATH is not available
+                path = os.defpath
+
+    # PATH='' doesn't match, whereas PATH=':' looks in the current directory
+    if not path:
+        return None
+
+    paths = path.split(os.pathsep)
+    for p in paths:
+        f = os.path.join(p, executable)
+        if os.path.isfile(f):
+            # the file exists, we have a shot at spawn working
+            return f
+    return None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/sysconfig.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/sysconfig.py
new file mode 100644
index 0000000..7ddc869
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/sysconfig.py
@@ -0,0 +1,598 @@
+"""Provide access to Python's configuration information.  The specific
+configuration variables available depend heavily on the platform and
+configuration.  The values may be retrieved using
+get_config_var(name), and the list of variables is available via
+get_config_vars().keys().  Additional convenience functions are also
+available.
+
+Written by:   Fred L. Drake, Jr.
+Email:        
+"""
+
+from __future__ import annotations
+
+import functools
+import os
+import pathlib
+import re
+import sys
+import sysconfig
+from typing import TYPE_CHECKING, Literal, overload
+
+from jaraco.functools import pass_none
+
+from .ccompiler import CCompiler
+from .compat import py39
+from .errors import DistutilsPlatformError
+from .util import is_mingw
+
+if TYPE_CHECKING:
+    from typing_extensions import deprecated
+else:
+
+    def deprecated(message):
+        return lambda fn: fn
+
+
+IS_PYPY = '__pypy__' in sys.builtin_module_names
+
+# These are needed in a couple of spots, so just compute them once.
+PREFIX = os.path.normpath(sys.prefix)
+EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
+BASE_PREFIX = os.path.normpath(sys.base_prefix)
+BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
+
+# Path to the base directory of the project. On Windows the binary may
+# live in project/PCbuild/win32 or project/PCbuild/amd64.
+# set for cross builds
+if "_PYTHON_PROJECT_BASE" in os.environ:
+    project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
+else:
+    if sys.executable:
+        project_base = os.path.dirname(os.path.abspath(sys.executable))
+    else:
+        # sys.executable can be empty if argv[0] has been changed and Python is
+        # unable to retrieve the real program name
+        project_base = os.getcwd()
+
+
+def _is_python_source_dir(d):
+    """
+    Return True if the target directory appears to point to an
+    un-installed Python.
+    """
+    modules = pathlib.Path(d).joinpath('Modules')
+    return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local'))
+
+
+_sys_home = getattr(sys, '_home', None)
+
+
+def _is_parent(dir_a, dir_b):
+    """
+    Return True if a is a parent of b.
+    """
+    return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b))
+
+
+if os.name == 'nt':
+
+    @pass_none
+    def _fix_pcbuild(d):
+        # In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX.
+        prefixes = PREFIX, BASE_PREFIX
+        matched = (
+            prefix
+            for prefix in prefixes
+            if _is_parent(d, os.path.join(prefix, "PCbuild"))
+        )
+        return next(matched, d)
+
+    project_base = _fix_pcbuild(project_base)
+    _sys_home = _fix_pcbuild(_sys_home)
+
+
+def _python_build():
+    if _sys_home:
+        return _is_python_source_dir(_sys_home)
+    return _is_python_source_dir(project_base)
+
+
+python_build = _python_build()
+
+
+# Calculate the build qualifier flags if they are defined.  Adding the flags
+# to the include and lib directories only makes sense for an installation, not
+# an in-source build.
+build_flags = ''
+try:
+    if not python_build:
+        build_flags = sys.abiflags
+except AttributeError:
+    # It's not a configure-based build, so the sys module doesn't have
+    # this attribute, which is fine.
+    pass
+
+
+def get_python_version():
+    """Return a string containing the major and minor Python version,
+    leaving off the patchlevel.  Sample return values could be '1.5'
+    or '2.2'.
+    """
+    return f'{sys.version_info.major}.{sys.version_info.minor}'
+
+
+def get_python_inc(plat_specific: bool = False, prefix: str | None = None) -> str:
+    """Return the directory containing installed Python header files.
+
+    If 'plat_specific' is false (the default), this is the path to the
+    non-platform-specific header files, i.e. Python.h and so on;
+    otherwise, this is the path to platform-specific header files
+    (namely pyconfig.h).
+
+    If 'prefix' is supplied, use it instead of sys.base_prefix or
+    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
+    """
+    default_prefix = BASE_EXEC_PREFIX if plat_specific else BASE_PREFIX
+    resolved_prefix = prefix if prefix is not None else default_prefix
+    # MinGW imitates posix like layout, but os.name != posix
+    os_name = "posix" if is_mingw() else os.name
+    try:
+        getter = globals()[f'_get_python_inc_{os_name}']
+    except KeyError:
+        raise DistutilsPlatformError(
+            "I don't know where Python installs its C header files "
+            f"on platform '{os.name}'"
+        )
+    return getter(resolved_prefix, prefix, plat_specific)
+
+
+@pass_none
+def _extant(path):
+    """
+    Replace path with None if it doesn't exist.
+    """
+    return path if os.path.exists(path) else None
+
+
+def _get_python_inc_posix(prefix, spec_prefix, plat_specific):
+    return (
+        _get_python_inc_posix_python(plat_specific)
+        or _extant(_get_python_inc_from_config(plat_specific, spec_prefix))
+        or _get_python_inc_posix_prefix(prefix)
+    )
+
+
+def _get_python_inc_posix_python(plat_specific):
+    """
+    Assume the executable is in the build directory. The
+    pyconfig.h file should be in the same directory. Since
+    the build directory may not be the source directory,
+    use "srcdir" from the makefile to find the "Include"
+    directory.
+    """
+    if not python_build:
+        return
+    if plat_specific:
+        return _sys_home or project_base
+    incdir = os.path.join(get_config_var('srcdir'), 'Include')
+    return os.path.normpath(incdir)
+
+
+def _get_python_inc_from_config(plat_specific, spec_prefix):
+    """
+    If no prefix was explicitly specified, provide the include
+    directory from the config vars. Useful when
+    cross-compiling, since the config vars may come from
+    the host
+    platform Python installation, while the current Python
+    executable is from the build platform installation.
+
+    >>> monkeypatch = getfixture('monkeypatch')
+    >>> gpifc = _get_python_inc_from_config
+    >>> monkeypatch.setitem(gpifc.__globals__, 'get_config_var', str.lower)
+    >>> gpifc(False, '/usr/bin/')
+    >>> gpifc(False, '')
+    >>> gpifc(False, None)
+    'includepy'
+    >>> gpifc(True, None)
+    'confincludepy'
+    """
+    if spec_prefix is None:
+        return get_config_var('CONF' * plat_specific + 'INCLUDEPY')
+
+
+def _get_python_inc_posix_prefix(prefix):
+    implementation = 'pypy' if IS_PYPY else 'python'
+    python_dir = implementation + get_python_version() + build_flags
+    return os.path.join(prefix, "include", python_dir)
+
+
+def _get_python_inc_nt(prefix, spec_prefix, plat_specific):
+    if python_build:
+        # Include both include dirs to ensure we can find pyconfig.h
+        return (
+            os.path.join(prefix, "include")
+            + os.path.pathsep
+            + os.path.dirname(sysconfig.get_config_h_filename())
+        )
+    return os.path.join(prefix, "include")
+
+
+# allow this behavior to be monkey-patched. Ref pypa/distutils#2.
+def _posix_lib(standard_lib, libpython, early_prefix, prefix):
+    if standard_lib:
+        return libpython
+    else:
+        return os.path.join(libpython, "site-packages")
+
+
+def get_python_lib(
+    plat_specific: bool = False, standard_lib: bool = False, prefix: str | None = None
+) -> str:
+    """Return the directory containing the Python library (standard or
+    site additions).
+
+    If 'plat_specific' is true, return the directory containing
+    platform-specific modules, i.e. any module from a non-pure-Python
+    module distribution; otherwise, return the platform-shared library
+    directory.  If 'standard_lib' is true, return the directory
+    containing standard Python library modules; otherwise, return the
+    directory for site-specific modules.
+
+    If 'prefix' is supplied, use it instead of sys.base_prefix or
+    sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
+    """
+
+    early_prefix = prefix
+
+    if prefix is None:
+        if standard_lib:
+            prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
+        else:
+            prefix = plat_specific and EXEC_PREFIX or PREFIX
+
+    if os.name == "posix" or is_mingw():
+        if plat_specific or standard_lib:
+            # Platform-specific modules (any module from a non-pure-Python
+            # module distribution) or standard Python library modules.
+            libdir = getattr(sys, "platlibdir", "lib")
+        else:
+            # Pure Python
+            libdir = "lib"
+        implementation = 'pypy' if IS_PYPY else 'python'
+        libpython = os.path.join(prefix, libdir, implementation + get_python_version())
+        return _posix_lib(standard_lib, libpython, early_prefix, prefix)
+    elif os.name == "nt":
+        if standard_lib:
+            return os.path.join(prefix, "Lib")
+        else:
+            return os.path.join(prefix, "Lib", "site-packages")
+    else:
+        raise DistutilsPlatformError(
+            f"I don't know where Python installs its library on platform '{os.name}'"
+        )
+
+
+@functools.lru_cache
+def _customize_macos():
+    """
+    Perform first-time customization of compiler-related
+    config vars on macOS. Use after a compiler is known
+    to be needed. This customization exists primarily to support Pythons
+    from binary installers. The kind and paths to build tools on
+    the user system may vary significantly from the system
+    that Python itself was built on.  Also the user OS
+    version and build tools may not support the same set
+    of CPU architectures for universal builds.
+    """
+
+    sys.platform == "darwin" and __import__('_osx_support').customize_compiler(
+        get_config_vars()
+    )
+
+
+def customize_compiler(compiler: CCompiler) -> None:
+    """Do any platform-specific customization of a CCompiler instance.
+
+    Mainly needed on Unix, so we can plug in the information that
+    varies across Unices and is stored in Python's Makefile.
+    """
+    if compiler.compiler_type in ["unix", "cygwin"] or (
+        compiler.compiler_type == "mingw32" and is_mingw()
+    ):
+        _customize_macos()
+
+        (
+            cc,
+            cxx,
+            cflags,
+            ccshared,
+            ldshared,
+            ldcxxshared,
+            shlib_suffix,
+            ar,
+            ar_flags,
+        ) = get_config_vars(
+            'CC',
+            'CXX',
+            'CFLAGS',
+            'CCSHARED',
+            'LDSHARED',
+            'LDCXXSHARED',
+            'SHLIB_SUFFIX',
+            'AR',
+            'ARFLAGS',
+        )
+
+        cxxflags = cflags
+
+        if 'CC' in os.environ:
+            newcc = os.environ['CC']
+            if 'LDSHARED' not in os.environ and ldshared.startswith(cc):
+                # If CC is overridden, use that as the default
+                #       command for LDSHARED as well
+                ldshared = newcc + ldshared[len(cc) :]
+            cc = newcc
+        cxx = os.environ.get('CXX', cxx)
+        ldshared = os.environ.get('LDSHARED', ldshared)
+        ldcxxshared = os.environ.get('LDCXXSHARED', ldcxxshared)
+        cpp = os.environ.get(
+            'CPP',
+            cc + " -E",  # not always
+        )
+
+        ldshared = _add_flags(ldshared, 'LD')
+        ldcxxshared = _add_flags(ldcxxshared, 'LD')
+        cflags = os.environ.get('CFLAGS', cflags)
+        ldshared = _add_flags(ldshared, 'C')
+        cxxflags = os.environ.get('CXXFLAGS', cxxflags)
+        ldcxxshared = _add_flags(ldcxxshared, 'CXX')
+        cpp = _add_flags(cpp, 'CPP')
+        cflags = _add_flags(cflags, 'CPP')
+        cxxflags = _add_flags(cxxflags, 'CPP')
+        ldshared = _add_flags(ldshared, 'CPP')
+        ldcxxshared = _add_flags(ldcxxshared, 'CPP')
+
+        ar = os.environ.get('AR', ar)
+
+        archiver = ar + ' ' + os.environ.get('ARFLAGS', ar_flags)
+        cc_cmd = cc + ' ' + cflags
+        cxx_cmd = cxx + ' ' + cxxflags
+
+        compiler.set_executables(
+            preprocessor=cpp,
+            compiler=cc_cmd,
+            compiler_so=cc_cmd + ' ' + ccshared,
+            compiler_cxx=cxx_cmd,
+            compiler_so_cxx=cxx_cmd + ' ' + ccshared,
+            linker_so=ldshared,
+            linker_so_cxx=ldcxxshared,
+            linker_exe=cc,
+            linker_exe_cxx=cxx,
+            archiver=archiver,
+        )
+
+        if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None):
+            compiler.set_executables(ranlib=os.environ['RANLIB'])
+
+        compiler.shared_lib_extension = shlib_suffix
+
+
+def get_config_h_filename() -> str:
+    """Return full pathname of installed pyconfig.h file."""
+    return sysconfig.get_config_h_filename()
+
+
+def get_makefile_filename() -> str:
+    """Return full pathname of installed Makefile from the Python build."""
+    return sysconfig.get_makefile_filename()
+
+
+def parse_config_h(fp, g=None):
+    """Parse a config.h-style file.
+
+    A dictionary containing name/value pairs is returned.  If an
+    optional dictionary is passed in as the second argument, it is
+    used instead of a new dictionary.
+    """
+    return sysconfig.parse_config_h(fp, vars=g)
+
+
+# Regexes needed for parsing Makefile (and similar syntaxes,
+# like old-style Setup files).
+_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
+_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
+_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
+
+
+def parse_makefile(fn, g=None):  # noqa: C901
+    """Parse a Makefile-style file.
+
+    A dictionary containing name/value pairs is returned.  If an
+    optional dictionary is passed in as the second argument, it is
+    used instead of a new dictionary.
+    """
+    from distutils.text_file import TextFile
+
+    fp = TextFile(
+        fn,
+        strip_comments=True,
+        skip_blanks=True,
+        join_lines=True,
+        errors="surrogateescape",
+    )
+
+    if g is None:
+        g = {}
+    done = {}
+    notdone = {}
+
+    while True:
+        line = fp.readline()
+        if line is None:  # eof
+            break
+        m = _variable_rx.match(line)
+        if m:
+            n, v = m.group(1, 2)
+            v = v.strip()
+            # `$$' is a literal `$' in make
+            tmpv = v.replace('$$', '')
+
+            if "$" in tmpv:
+                notdone[n] = v
+            else:
+                try:
+                    v = int(v)
+                except ValueError:
+                    # insert literal `$'
+                    done[n] = v.replace('$$', '$')
+                else:
+                    done[n] = v
+
+    # Variables with a 'PY_' prefix in the makefile. These need to
+    # be made available without that prefix through sysconfig.
+    # Special care is needed to ensure that variable expansion works, even
+    # if the expansion uses the name without a prefix.
+    renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
+
+    # do variable interpolation here
+    while notdone:
+        for name in list(notdone):
+            value = notdone[name]
+            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
+            if m:
+                n = m.group(1)
+                found = True
+                if n in done:
+                    item = str(done[n])
+                elif n in notdone:
+                    # get it on a subsequent round
+                    found = False
+                elif n in os.environ:
+                    # do it like make: fall back to environment
+                    item = os.environ[n]
+
+                elif n in renamed_variables:
+                    if name.startswith('PY_') and name[3:] in renamed_variables:
+                        item = ""
+
+                    elif 'PY_' + n in notdone:
+                        found = False
+
+                    else:
+                        item = str(done['PY_' + n])
+                else:
+                    done[n] = item = ""
+                if found:
+                    after = value[m.end() :]
+                    value = value[: m.start()] + item + after
+                    if "$" in after:
+                        notdone[name] = value
+                    else:
+                        try:
+                            value = int(value)
+                        except ValueError:
+                            done[name] = value.strip()
+                        else:
+                            done[name] = value
+                        del notdone[name]
+
+                        if name.startswith('PY_') and name[3:] in renamed_variables:
+                            name = name[3:]
+                            if name not in done:
+                                done[name] = value
+            else:
+                # bogus variable reference; just drop it since we can't deal
+                del notdone[name]
+
+    fp.close()
+
+    # strip spurious spaces
+    for k, v in done.items():
+        if isinstance(v, str):
+            done[k] = v.strip()
+
+    # save the results in the global dictionary
+    g.update(done)
+    return g
+
+
+def expand_makefile_vars(s, vars):
+    """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
+    'string' according to 'vars' (a dictionary mapping variable names to
+    values).  Variables not present in 'vars' are silently expanded to the
+    empty string.  The variable values in 'vars' should not contain further
+    variable expansions; if 'vars' is the output of 'parse_makefile()',
+    you're fine.  Returns a variable-expanded version of 's'.
+    """
+
+    # This algorithm does multiple expansion, so if vars['foo'] contains
+    # "${bar}", it will expand ${foo} to ${bar}, and then expand
+    # ${bar}... and so forth.  This is fine as long as 'vars' comes from
+    # 'parse_makefile()', which takes care of such expansions eagerly,
+    # according to make's variable expansion semantics.
+
+    while True:
+        m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
+        if m:
+            (beg, end) = m.span()
+            s = s[0:beg] + vars.get(m.group(1)) + s[end:]
+        else:
+            break
+    return s
+
+
+_config_vars = None
+
+
+@overload
+def get_config_vars() -> dict[str, str | int]: ...
+@overload
+def get_config_vars(arg: str, /, *args: str) -> list[str | int]: ...
+def get_config_vars(*args: str) -> list[str | int] | dict[str, str | int]:
+    """With no arguments, return a dictionary of all configuration
+    variables relevant for the current platform.  Generally this includes
+    everything needed to build extensions and install both pure modules and
+    extensions.  On Unix, this means every variable defined in Python's
+    installed Makefile; on Windows it's a much smaller set.
+
+    With arguments, return a list of values that result from looking up
+    each argument in the configuration variable dictionary.
+    """
+    global _config_vars
+    if _config_vars is None:
+        _config_vars = sysconfig.get_config_vars().copy()
+        py39.add_ext_suffix(_config_vars)
+
+    return [_config_vars.get(name) for name in args] if args else _config_vars
+
+
+@overload
+@deprecated(
+    "SO is deprecated, use EXT_SUFFIX. Support will be removed when this module is synchronized with stdlib Python 3.11"
+)
+def get_config_var(name: Literal["SO"]) -> int | str | None: ...
+@overload
+def get_config_var(name: str) -> int | str | None: ...
+def get_config_var(name: str) -> int | str | None:
+    """Return the value of a single variable using the dictionary
+    returned by 'get_config_vars()'.  Equivalent to
+    get_config_vars().get(name)
+    """
+    if name == 'SO':
+        import warnings
+
+        warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
+    return get_config_vars().get(name)
+
+
+@pass_none
+def _add_flags(value: str, type: str) -> str:
+    """
+    Add any flags from the environment for the given type.
+
+    type is the prefix to FLAGS in the environment key (e.g. "C" for "CFLAGS").
+    """
+    flags = os.environ.get(f'{type}FLAGS')
+    return f'{value} {flags}' if flags else value
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/__init__.py
new file mode 100644
index 0000000..5a8ab06
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/__init__.py
@@ -0,0 +1,42 @@
+"""
+Test suite for distutils.
+
+Tests for the command classes in the distutils.command package are
+included in distutils.tests as well, instead of using a separate
+distutils.command.tests package, since command identification is done
+by import rather than matching pre-defined names.
+"""
+
+import shutil
+from collections.abc import Sequence
+
+
+def missing_compiler_executable(cmd_names: Sequence[str] = []):  # pragma: no cover
+    """Check if the compiler components used to build the interpreter exist.
+
+    Check for the existence of the compiler executables whose names are listed
+    in 'cmd_names' or all the compiler executables when 'cmd_names' is empty
+    and return the first missing executable or None when none is found
+    missing.
+
+    """
+    from distutils import ccompiler, errors, sysconfig
+
+    compiler = ccompiler.new_compiler()
+    sysconfig.customize_compiler(compiler)
+    if compiler.compiler_type == "msvc":
+        # MSVC has no executables, so check whether initialization succeeds
+        try:
+            compiler.initialize()
+        except errors.DistutilsPlatformError:
+            return "msvc"
+    for name in compiler.executables:
+        if cmd_names and name not in cmd_names:
+            continue
+        cmd = getattr(compiler, name)
+        if cmd_names:
+            assert cmd is not None, f"the '{name}' executable is not configured"
+        elif not cmd:
+            continue
+        if shutil.which(cmd[0]) is None:
+            return cmd[0]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/py39.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/py39.py
new file mode 100644
index 0000000..aca3939
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/compat/py39.py
@@ -0,0 +1,40 @@
+import sys
+
+if sys.version_info >= (3, 10):
+    from test.support.import_helper import (
+        CleanImport as CleanImport,
+    )
+    from test.support.import_helper import (
+        DirsOnSysPath as DirsOnSysPath,
+    )
+    from test.support.os_helper import (
+        EnvironmentVarGuard as EnvironmentVarGuard,
+    )
+    from test.support.os_helper import (
+        rmtree as rmtree,
+    )
+    from test.support.os_helper import (
+        skip_unless_symlink as skip_unless_symlink,
+    )
+    from test.support.os_helper import (
+        unlink as unlink,
+    )
+else:
+    from test.support import (
+        CleanImport as CleanImport,
+    )
+    from test.support import (
+        DirsOnSysPath as DirsOnSysPath,
+    )
+    from test.support import (
+        EnvironmentVarGuard as EnvironmentVarGuard,
+    )
+    from test.support import (
+        rmtree as rmtree,
+    )
+    from test.support import (
+        skip_unless_symlink as skip_unless_symlink,
+    )
+    from test.support import (
+        unlink as unlink,
+    )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/support.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/support.py
new file mode 100644
index 0000000..9cd2b8a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/support.py
@@ -0,0 +1,134 @@
+"""Support code for distutils test cases."""
+
+import itertools
+import os
+import pathlib
+import shutil
+import sys
+import sysconfig
+import tempfile
+from distutils.core import Distribution
+
+import pytest
+from more_itertools import always_iterable
+
+
+@pytest.mark.usefixtures('distutils_managed_tempdir')
+class TempdirManager:
+    """
+    Mix-in class that handles temporary directories for test cases.
+    """
+
+    def mkdtemp(self):
+        """Create a temporary directory that will be cleaned up.
+
+        Returns the path of the directory.
+        """
+        d = tempfile.mkdtemp()
+        self.tempdirs.append(d)
+        return d
+
+    def write_file(self, path, content='xxx'):
+        """Writes a file in the given path.
+
+        path can be a string or a sequence.
+        """
+        pathlib.Path(*always_iterable(path)).write_text(content, encoding='utf-8')
+
+    def create_dist(self, pkg_name='foo', **kw):
+        """Will generate a test environment.
+
+        This function creates:
+         - a Distribution instance using keywords
+         - a temporary directory with a package structure
+
+        It returns the package directory and the distribution
+        instance.
+        """
+        tmp_dir = self.mkdtemp()
+        pkg_dir = os.path.join(tmp_dir, pkg_name)
+        os.mkdir(pkg_dir)
+        dist = Distribution(attrs=kw)
+
+        return pkg_dir, dist
+
+
+class DummyCommand:
+    """Class to store options for retrieval via set_undefined_options()."""
+
+    def __init__(self, **kwargs):
+        vars(self).update(kwargs)
+
+    def ensure_finalized(self):
+        pass
+
+
+def copy_xxmodule_c(directory):
+    """Helper for tests that need the xxmodule.c source file.
+
+    Example use:
+
+        def test_compile(self):
+            copy_xxmodule_c(self.tmpdir)
+            self.assertIn('xxmodule.c', os.listdir(self.tmpdir))
+
+    If the source file can be found, it will be copied to *directory*.  If not,
+    the test will be skipped.  Errors during copy are not caught.
+    """
+    shutil.copy(_get_xxmodule_path(), os.path.join(directory, 'xxmodule.c'))
+
+
+def _get_xxmodule_path():
+    source_name = 'xxmodule.c' if sys.version_info > (3, 9) else 'xxmodule-3.8.c'
+    return os.path.join(os.path.dirname(__file__), source_name)
+
+
+def fixup_build_ext(cmd):
+    """Function needed to make build_ext tests pass.
+
+    When Python was built with --enable-shared on Unix, -L. is not enough to
+    find libpython.so, because regrtest runs in a tempdir, not in the
+    source directory where the .so lives.
+
+    When Python was built with in debug mode on Windows, build_ext commands
+    need their debug attribute set, and it is not done automatically for
+    some reason.
+
+    This function handles both of these things.  Example use:
+
+        cmd = build_ext(dist)
+        support.fixup_build_ext(cmd)
+        cmd.ensure_finalized()
+
+    Unlike most other Unix platforms, Mac OS X embeds absolute paths
+    to shared libraries into executables, so the fixup is not needed there.
+    """
+    if os.name == 'nt':
+        cmd.debug = sys.executable.endswith('_d.exe')
+    elif sysconfig.get_config_var('Py_ENABLE_SHARED'):
+        # To further add to the shared builds fun on Unix, we can't just add
+        # library_dirs to the Extension() instance because that doesn't get
+        # plumbed through to the final compiler command.
+        runshared = sysconfig.get_config_var('RUNSHARED')
+        if runshared is None:
+            cmd.library_dirs = ['.']
+        else:
+            if sys.platform == 'darwin':
+                cmd.library_dirs = []
+            else:
+                name, equals, value = runshared.partition('=')
+                cmd.library_dirs = [d for d in value.split(os.pathsep) if d]
+
+
+def combine_markers(cls):
+    """
+    pytest will honor markers as found on the class, but when
+    markers are on multiple subclasses, only one appears. Use
+    this decorator to combine those markers.
+    """
+    cls.pytestmark = [
+        mark
+        for base in itertools.chain([cls], cls.__bases__)
+        for mark in getattr(base, 'pytestmark', [])
+    ]
+    return cls
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_archive_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_archive_util.py
new file mode 100644
index 0000000..a9cc5eb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_archive_util.py
@@ -0,0 +1,342 @@
+"""Tests for distutils.archive_util."""
+
+import functools
+import operator
+import os
+import pathlib
+import sys
+import tarfile
+from distutils import archive_util
+from distutils.archive_util import (
+    ARCHIVE_FORMATS,
+    check_archive_formats,
+    make_archive,
+    make_tarball,
+    make_zipfile,
+)
+from distutils.spawn import spawn
+from distutils.tests import support
+from os.path import splitdrive
+
+import path
+import pytest
+from test.support import patch
+
+from .unix_compat import UID_0_SUPPORT, grp, pwd, require_uid_0, require_unix_id
+
+
+def can_fs_encode(filename):
+    """
+    Return True if the filename can be saved in the file system.
+    """
+    if os.path.supports_unicode_filenames:
+        return True
+    try:
+        filename.encode(sys.getfilesystemencoding())
+    except UnicodeEncodeError:
+        return False
+    return True
+
+
+def all_equal(values):
+    return functools.reduce(operator.eq, values)
+
+
+def same_drive(*paths):
+    return all_equal(pathlib.Path(path).drive for path in paths)
+
+
+class ArchiveUtilTestCase(support.TempdirManager):
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_make_tarball(self, name='archive'):
+        # creating something to tar
+        tmpdir = self._create_files()
+        self._make_tarball(tmpdir, name, '.tar.gz')
+        # trying an uncompressed one
+        self._make_tarball(tmpdir, name, '.tar', compress=None)
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_make_tarball_gzip(self):
+        tmpdir = self._create_files()
+        self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip')
+
+    def test_make_tarball_bzip2(self):
+        pytest.importorskip('bz2')
+        tmpdir = self._create_files()
+        self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2')
+
+    def test_make_tarball_xz(self):
+        pytest.importorskip('lzma')
+        tmpdir = self._create_files()
+        self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz')
+
+    @pytest.mark.skipif("not can_fs_encode('årchiv')")
+    def test_make_tarball_latin1(self):
+        """
+        Mirror test_make_tarball, except filename contains latin characters.
+        """
+        self.test_make_tarball('årchiv')  # note this isn't a real word
+
+    @pytest.mark.skipif("not can_fs_encode('のアーカイブ')")
+    def test_make_tarball_extended(self):
+        """
+        Mirror test_make_tarball, except filename contains extended
+        characters outside the latin charset.
+        """
+        self.test_make_tarball('のアーカイブ')  # japanese for archive
+
+    def _make_tarball(self, tmpdir, target_name, suffix, **kwargs):
+        tmpdir2 = self.mkdtemp()
+        if same_drive(tmpdir, tmpdir2):
+            pytest.skip("source and target should be on same drive")
+
+        base_name = os.path.join(tmpdir2, target_name)
+
+        # working with relative paths to avoid tar warnings
+        with path.Path(tmpdir):
+            make_tarball(splitdrive(base_name)[1], 'dist', **kwargs)
+
+        # check if the compressed tarball was created
+        tarball = base_name + suffix
+        assert os.path.exists(tarball)
+        assert self._tarinfo(tarball) == self._created_files
+
+    def _tarinfo(self, path):
+        tar = tarfile.open(path)
+        try:
+            names = tar.getnames()
+            names.sort()
+            return names
+        finally:
+            tar.close()
+
+    _zip_created_files = [
+        'dist/',
+        'dist/file1',
+        'dist/file2',
+        'dist/sub/',
+        'dist/sub/file3',
+        'dist/sub2/',
+    ]
+    _created_files = [p.rstrip('/') for p in _zip_created_files]
+
+    def _create_files(self):
+        # creating something to tar
+        tmpdir = self.mkdtemp()
+        dist = os.path.join(tmpdir, 'dist')
+        os.mkdir(dist)
+        self.write_file([dist, 'file1'], 'xxx')
+        self.write_file([dist, 'file2'], 'xxx')
+        os.mkdir(os.path.join(dist, 'sub'))
+        self.write_file([dist, 'sub', 'file3'], 'xxx')
+        os.mkdir(os.path.join(dist, 'sub2'))
+        return tmpdir
+
+    @pytest.mark.usefixtures('needs_zlib')
+    @pytest.mark.skipif("not (shutil.which('tar') and shutil.which('gzip'))")
+    def test_tarfile_vs_tar(self):
+        tmpdir = self._create_files()
+        tmpdir2 = self.mkdtemp()
+        base_name = os.path.join(tmpdir2, 'archive')
+        old_dir = os.getcwd()
+        os.chdir(tmpdir)
+        try:
+            make_tarball(base_name, 'dist')
+        finally:
+            os.chdir(old_dir)
+
+        # check if the compressed tarball was created
+        tarball = base_name + '.tar.gz'
+        assert os.path.exists(tarball)
+
+        # now create another tarball using `tar`
+        tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
+        tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist']
+        gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar']
+        old_dir = os.getcwd()
+        os.chdir(tmpdir)
+        try:
+            spawn(tar_cmd)
+            spawn(gzip_cmd)
+        finally:
+            os.chdir(old_dir)
+
+        assert os.path.exists(tarball2)
+        # let's compare both tarballs
+        assert self._tarinfo(tarball) == self._created_files
+        assert self._tarinfo(tarball2) == self._created_files
+
+        # trying an uncompressed one
+        base_name = os.path.join(tmpdir2, 'archive')
+        old_dir = os.getcwd()
+        os.chdir(tmpdir)
+        try:
+            make_tarball(base_name, 'dist', compress=None)
+        finally:
+            os.chdir(old_dir)
+        tarball = base_name + '.tar'
+        assert os.path.exists(tarball)
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_make_zipfile(self):
+        zipfile = pytest.importorskip('zipfile')
+        # creating something to tar
+        tmpdir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        with path.Path(tmpdir):
+            make_zipfile(base_name, 'dist')
+
+        # check if the compressed tarball was created
+        tarball = base_name + '.zip'
+        assert os.path.exists(tarball)
+        with zipfile.ZipFile(tarball) as zf:
+            assert sorted(zf.namelist()) == self._zip_created_files
+
+    def test_make_zipfile_no_zlib(self):
+        zipfile = pytest.importorskip('zipfile')
+        patch(self, archive_util.zipfile, 'zlib', None)  # force zlib ImportError
+
+        called = []
+        zipfile_class = zipfile.ZipFile
+
+        def fake_zipfile(*a, **kw):
+            if kw.get('compression', None) == zipfile.ZIP_STORED:
+                called.append((a, kw))
+            return zipfile_class(*a, **kw)
+
+        patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile)
+
+        # create something to tar and compress
+        tmpdir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        with path.Path(tmpdir):
+            make_zipfile(base_name, 'dist')
+
+        tarball = base_name + '.zip'
+        assert called == [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]
+        assert os.path.exists(tarball)
+        with zipfile.ZipFile(tarball) as zf:
+            assert sorted(zf.namelist()) == self._zip_created_files
+
+    def test_check_archive_formats(self):
+        assert check_archive_formats(['gztar', 'xxx', 'zip']) == 'xxx'
+        assert (
+            check_archive_formats(['gztar', 'bztar', 'xztar', 'ztar', 'tar', 'zip'])
+            is None
+        )
+
+    def test_make_archive(self):
+        tmpdir = self.mkdtemp()
+        base_name = os.path.join(tmpdir, 'archive')
+        with pytest.raises(ValueError):
+            make_archive(base_name, 'xxx')
+
+    def test_make_archive_cwd(self):
+        current_dir = os.getcwd()
+
+        def _breaks(*args, **kw):
+            raise RuntimeError()
+
+        ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file')
+        try:
+            try:
+                make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
+            except Exception:
+                pass
+            assert os.getcwd() == current_dir
+        finally:
+            ARCHIVE_FORMATS.pop('xxx')
+
+    def test_make_archive_tar(self):
+        base_dir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        res = make_archive(base_name, 'tar', base_dir, 'dist')
+        assert os.path.exists(res)
+        assert os.path.basename(res) == 'archive.tar'
+        assert self._tarinfo(res) == self._created_files
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_make_archive_gztar(self):
+        base_dir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        res = make_archive(base_name, 'gztar', base_dir, 'dist')
+        assert os.path.exists(res)
+        assert os.path.basename(res) == 'archive.tar.gz'
+        assert self._tarinfo(res) == self._created_files
+
+    def test_make_archive_bztar(self):
+        pytest.importorskip('bz2')
+        base_dir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        res = make_archive(base_name, 'bztar', base_dir, 'dist')
+        assert os.path.exists(res)
+        assert os.path.basename(res) == 'archive.tar.bz2'
+        assert self._tarinfo(res) == self._created_files
+
+    def test_make_archive_xztar(self):
+        pytest.importorskip('lzma')
+        base_dir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        res = make_archive(base_name, 'xztar', base_dir, 'dist')
+        assert os.path.exists(res)
+        assert os.path.basename(res) == 'archive.tar.xz'
+        assert self._tarinfo(res) == self._created_files
+
+    def test_make_archive_owner_group(self):
+        # testing make_archive with owner and group, with various combinations
+        # this works even if there's not gid/uid support
+        if UID_0_SUPPORT:
+            group = grp.getgrgid(0)[0]
+            owner = pwd.getpwuid(0)[0]
+        else:
+            group = owner = 'root'
+
+        base_dir = self._create_files()
+        root_dir = self.mkdtemp()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        res = make_archive(
+            base_name, 'zip', root_dir, base_dir, owner=owner, group=group
+        )
+        assert os.path.exists(res)
+
+        res = make_archive(base_name, 'zip', root_dir, base_dir)
+        assert os.path.exists(res)
+
+        res = make_archive(
+            base_name, 'tar', root_dir, base_dir, owner=owner, group=group
+        )
+        assert os.path.exists(res)
+
+        res = make_archive(
+            base_name, 'tar', root_dir, base_dir, owner='kjhkjhkjg', group='oihohoh'
+        )
+        assert os.path.exists(res)
+
+    @pytest.mark.usefixtures('needs_zlib')
+    @require_unix_id
+    @require_uid_0
+    def test_tarfile_root_owner(self):
+        tmpdir = self._create_files()
+        base_name = os.path.join(self.mkdtemp(), 'archive')
+        old_dir = os.getcwd()
+        os.chdir(tmpdir)
+        group = grp.getgrgid(0)[0]
+        owner = pwd.getpwuid(0)[0]
+        try:
+            archive_name = make_tarball(
+                base_name, 'dist', compress=None, owner=owner, group=group
+            )
+        finally:
+            os.chdir(old_dir)
+
+        # check if the compressed tarball was created
+        assert os.path.exists(archive_name)
+
+        # now checks the rights
+        archive = tarfile.open(archive_name)
+        try:
+            for member in archive.getmembers():
+                assert member.uid == 0
+                assert member.gid == 0
+        finally:
+            archive.close()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist.py
new file mode 100644
index 0000000..d5696fc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist.py
@@ -0,0 +1,47 @@
+"""Tests for distutils.command.bdist."""
+
+from distutils.command.bdist import bdist
+from distutils.tests import support
+
+
+class TestBuild(support.TempdirManager):
+    def test_formats(self):
+        # let's create a command and make sure
+        # we can set the format
+        dist = self.create_dist()[1]
+        cmd = bdist(dist)
+        cmd.formats = ['gztar']
+        cmd.ensure_finalized()
+        assert cmd.formats == ['gztar']
+
+        # what formats does bdist offer?
+        formats = [
+            'bztar',
+            'gztar',
+            'rpm',
+            'tar',
+            'xztar',
+            'zip',
+            'ztar',
+        ]
+        found = sorted(cmd.format_commands)
+        assert found == formats
+
+    def test_skip_build(self):
+        # bug #10946: bdist --skip-build should trickle down to subcommands
+        dist = self.create_dist()[1]
+        cmd = bdist(dist)
+        cmd.skip_build = True
+        cmd.ensure_finalized()
+        dist.command_obj['bdist'] = cmd
+
+        names = [
+            'bdist_dumb',
+        ]  # bdist_rpm does not support --skip-build
+
+        for name in names:
+            subcmd = cmd.get_finalized_command(name)
+            if getattr(subcmd, '_unsupported', False):
+                # command is not supported on this build
+                continue
+            assert subcmd.skip_build, f'{name} should take --skip-build from bdist'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_dumb.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_dumb.py
new file mode 100644
index 0000000..1fc51d2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_dumb.py
@@ -0,0 +1,78 @@
+"""Tests for distutils.command.bdist_dumb."""
+
+import os
+import sys
+import zipfile
+from distutils.command.bdist_dumb import bdist_dumb
+from distutils.core import Distribution
+from distutils.tests import support
+
+import pytest
+
+SETUP_PY = """\
+from distutils.core import setup
+import foo
+
+setup(name='foo', version='0.1', py_modules=['foo'],
+      url='xxx', author='xxx', author_email='xxx')
+
+"""
+
+
+@support.combine_markers
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+@pytest.mark.usefixtures('save_cwd')
+class TestBuildDumb(
+    support.TempdirManager,
+):
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_simple_built(self):
+        # let's create a simple package
+        tmp_dir = self.mkdtemp()
+        pkg_dir = os.path.join(tmp_dir, 'foo')
+        os.mkdir(pkg_dir)
+        self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
+        self.write_file((pkg_dir, 'foo.py'), '#')
+        self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+        self.write_file((pkg_dir, 'README'), '')
+
+        dist = Distribution({
+            'name': 'foo',
+            'version': '0.1',
+            'py_modules': ['foo'],
+            'url': 'xxx',
+            'author': 'xxx',
+            'author_email': 'xxx',
+        })
+        dist.script_name = 'setup.py'
+        os.chdir(pkg_dir)
+
+        sys.argv = ['setup.py']
+        cmd = bdist_dumb(dist)
+
+        # so the output is the same no matter
+        # what is the platform
+        cmd.format = 'zip'
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # see what we have
+        dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+        base = f"{dist.get_fullname()}.{cmd.plat_name}.zip"
+
+        assert dist_created == [base]
+
+        # now let's check what we have in the zip file
+        fp = zipfile.ZipFile(os.path.join('dist', base))
+        try:
+            contents = fp.namelist()
+        finally:
+            fp.close()
+
+        contents = sorted(filter(None, map(os.path.basename, contents)))
+        wanted = ['foo-0.1-py{}.{}.egg-info'.format(*sys.version_info[:2]), 'foo.py']
+        if not sys.dont_write_bytecode:
+            wanted.append(f'foo.{sys.implementation.cache_tag}.pyc')
+        assert contents == sorted(wanted)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_rpm.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_rpm.py
new file mode 100644
index 0000000..7505143
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_bdist_rpm.py
@@ -0,0 +1,127 @@
+"""Tests for distutils.command.bdist_rpm."""
+
+import os
+import shutil  # noqa: F401
+import sys
+from distutils.command.bdist_rpm import bdist_rpm
+from distutils.core import Distribution
+from distutils.tests import support
+
+import pytest
+from test.support import requires_zlib
+
+SETUP_PY = """\
+from distutils.core import setup
+import foo
+
+setup(name='foo', version='0.1', py_modules=['foo'],
+      url='xxx', author='xxx', author_email='xxx')
+
+"""
+
+
+@pytest.fixture(autouse=True)
+def sys_executable_encodable():
+    try:
+        sys.executable.encode('UTF-8')
+    except UnicodeEncodeError:
+        pytest.skip("sys.executable is not encodable to UTF-8")
+
+
+mac_woes = pytest.mark.skipif(
+    "not sys.platform.startswith('linux')",
+    reason='spurious sdtout/stderr output under macOS',
+)
+
+
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+@pytest.mark.usefixtures('save_cwd')
+class TestBuildRpm(
+    support.TempdirManager,
+):
+    @mac_woes
+    @requires_zlib()
+    @pytest.mark.skipif("not shutil.which('rpm')")
+    @pytest.mark.skipif("not shutil.which('rpmbuild')")
+    def test_quiet(self):
+        # let's create a package
+        tmp_dir = self.mkdtemp()
+        os.environ['HOME'] = tmp_dir  # to confine dir '.rpmdb' creation
+        pkg_dir = os.path.join(tmp_dir, 'foo')
+        os.mkdir(pkg_dir)
+        self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
+        self.write_file((pkg_dir, 'foo.py'), '#')
+        self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+        self.write_file((pkg_dir, 'README'), '')
+
+        dist = Distribution({
+            'name': 'foo',
+            'version': '0.1',
+            'py_modules': ['foo'],
+            'url': 'xxx',
+            'author': 'xxx',
+            'author_email': 'xxx',
+        })
+        dist.script_name = 'setup.py'
+        os.chdir(pkg_dir)
+
+        sys.argv = ['setup.py']
+        cmd = bdist_rpm(dist)
+        cmd.fix_python = True
+
+        # running in quiet mode
+        cmd.quiet = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+        assert 'foo-0.1-1.noarch.rpm' in dist_created
+
+        # bug #2945: upload ignores bdist_rpm files
+        assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm') in dist.dist_files
+        assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm') in dist.dist_files
+
+    @mac_woes
+    @requires_zlib()
+    # https://bugs.python.org/issue1533164
+    @pytest.mark.skipif("not shutil.which('rpm')")
+    @pytest.mark.skipif("not shutil.which('rpmbuild')")
+    def test_no_optimize_flag(self):
+        # let's create a package that breaks bdist_rpm
+        tmp_dir = self.mkdtemp()
+        os.environ['HOME'] = tmp_dir  # to confine dir '.rpmdb' creation
+        pkg_dir = os.path.join(tmp_dir, 'foo')
+        os.mkdir(pkg_dir)
+        self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
+        self.write_file((pkg_dir, 'foo.py'), '#')
+        self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+        self.write_file((pkg_dir, 'README'), '')
+
+        dist = Distribution({
+            'name': 'foo',
+            'version': '0.1',
+            'py_modules': ['foo'],
+            'url': 'xxx',
+            'author': 'xxx',
+            'author_email': 'xxx',
+        })
+        dist.script_name = 'setup.py'
+        os.chdir(pkg_dir)
+
+        sys.argv = ['setup.py']
+        cmd = bdist_rpm(dist)
+        cmd.fix_python = True
+
+        cmd.quiet = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+        assert 'foo-0.1-1.noarch.rpm' in dist_created
+
+        # bug #2945: upload ignores bdist_rpm files
+        assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm') in dist.dist_files
+        assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm') in dist.dist_files
+
+        os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build.py
new file mode 100644
index 0000000..f7fe69a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build.py
@@ -0,0 +1,49 @@
+"""Tests for distutils.command.build."""
+
+import os
+import sys
+from distutils.command.build import build
+from distutils.tests import support
+from sysconfig import get_config_var, get_platform
+
+
+class TestBuild(support.TempdirManager):
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build(dist)
+        cmd.finalize_options()
+
+        # if not specified, plat_name gets the current platform
+        assert cmd.plat_name == get_platform()
+
+        # build_purelib is build + lib
+        wanted = os.path.join(cmd.build_base, 'lib')
+        assert cmd.build_purelib == wanted
+
+        # build_platlib is 'build/lib.platform-cache_tag[-pydebug]'
+        # examples:
+        #   build/lib.macosx-10.3-i386-cpython39
+        plat_spec = f'.{cmd.plat_name}-{sys.implementation.cache_tag}'
+        if get_config_var('Py_GIL_DISABLED'):
+            plat_spec += 't'
+        if hasattr(sys, 'gettotalrefcount'):
+            assert cmd.build_platlib.endswith('-pydebug')
+            plat_spec += '-pydebug'
+        wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
+        assert cmd.build_platlib == wanted
+
+        # by default, build_lib = build_purelib
+        assert cmd.build_lib == cmd.build_purelib
+
+        # build_temp is build/temp.
+        wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
+        assert cmd.build_temp == wanted
+
+        # build_scripts is build/scripts-x.x
+        wanted = os.path.join(
+            cmd.build_base, f'scripts-{sys.version_info.major}.{sys.version_info.minor}'
+        )
+        assert cmd.build_scripts == wanted
+
+        # executable is os.path.normpath(sys.executable)
+        assert cmd.executable == os.path.normpath(sys.executable)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_clib.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_clib.py
new file mode 100644
index 0000000..f76f26b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_clib.py
@@ -0,0 +1,134 @@
+"""Tests for distutils.command.build_clib."""
+
+import os
+from distutils.command.build_clib import build_clib
+from distutils.errors import DistutilsSetupError
+from distutils.tests import missing_compiler_executable, support
+
+import pytest
+
+
+class TestBuildCLib(support.TempdirManager):
+    def test_check_library_dist(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        # 'libraries' option must be a list
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_library_list('foo')
+
+        # each element of 'libraries' must a 2-tuple
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_library_list(['foo1', 'foo2'])
+
+        # first element of each tuple in 'libraries'
+        # must be a string (the library name)
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_library_list([(1, 'foo1'), ('name', 'foo2')])
+
+        # library name may not contain directory separators
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_library_list(
+                [('name', 'foo1'), ('another/name', 'foo2')],
+            )
+
+        # second element of each tuple must be a dictionary (build info)
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_library_list(
+                [('name', {}), ('another', 'foo2')],
+            )
+
+        # those work
+        libs = [('name', {}), ('name', {'ok': 'good'})]
+        cmd.check_library_list(libs)
+
+    def test_get_source_files(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        # "in 'libraries' option 'sources' must be present and must be
+        # a list of source filenames
+        cmd.libraries = [('name', {})]
+        with pytest.raises(DistutilsSetupError):
+            cmd.get_source_files()
+
+        cmd.libraries = [('name', {'sources': 1})]
+        with pytest.raises(DistutilsSetupError):
+            cmd.get_source_files()
+
+        cmd.libraries = [('name', {'sources': ['a', 'b']})]
+        assert cmd.get_source_files() == ['a', 'b']
+
+        cmd.libraries = [('name', {'sources': ('a', 'b')})]
+        assert cmd.get_source_files() == ['a', 'b']
+
+        cmd.libraries = [
+            ('name', {'sources': ('a', 'b')}),
+            ('name2', {'sources': ['c', 'd']}),
+        ]
+        assert cmd.get_source_files() == ['a', 'b', 'c', 'd']
+
+    def test_build_libraries(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        class FakeCompiler:
+            def compile(*args, **kw):
+                pass
+
+            create_static_lib = compile
+
+        cmd.compiler = FakeCompiler()
+
+        # build_libraries is also doing a bit of typo checking
+        lib = [('name', {'sources': 'notvalid'})]
+        with pytest.raises(DistutilsSetupError):
+            cmd.build_libraries(lib)
+
+        lib = [('name', {'sources': list()})]
+        cmd.build_libraries(lib)
+
+        lib = [('name', {'sources': tuple()})]
+        cmd.build_libraries(lib)
+
+    def test_finalize_options(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        cmd.include_dirs = 'one-dir'
+        cmd.finalize_options()
+        assert cmd.include_dirs == ['one-dir']
+
+        cmd.include_dirs = None
+        cmd.finalize_options()
+        assert cmd.include_dirs == []
+
+        cmd.distribution.libraries = 'WONTWORK'
+        with pytest.raises(DistutilsSetupError):
+            cmd.finalize_options()
+
+    @pytest.mark.skipif('platform.system() == "Windows"')
+    def test_run(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = build_clib(dist)
+
+        foo_c = os.path.join(pkg_dir, 'foo.c')
+        self.write_file(foo_c, 'int main(void) { return 1;}\n')
+        cmd.libraries = [('foo', {'sources': [foo_c]})]
+
+        build_temp = os.path.join(pkg_dir, 'build')
+        os.mkdir(build_temp)
+        cmd.build_temp = build_temp
+        cmd.build_clib = build_temp
+
+        # Before we run the command, we want to make sure
+        # all commands are present on the system.
+        ccmd = missing_compiler_executable()
+        if ccmd is not None:
+            self.skipTest(f'The {ccmd!r} command is not found')
+
+        # this should work
+        cmd.run()
+
+        # let's check the result
+        assert 'libfoo.a' in os.listdir(build_temp)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_ext.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_ext.py
new file mode 100644
index 0000000..dab0507
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_ext.py
@@ -0,0 +1,628 @@
+import contextlib
+import glob
+import importlib
+import os.path
+import platform
+import re
+import shutil
+import site
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+from distutils import sysconfig
+from distutils.command.build_ext import build_ext
+from distutils.core import Distribution
+from distutils.errors import (
+    CompileError,
+    DistutilsPlatformError,
+    DistutilsSetupError,
+    UnknownFileError,
+)
+from distutils.extension import Extension
+from distutils.tests import missing_compiler_executable
+from distutils.tests.support import TempdirManager, copy_xxmodule_c, fixup_build_ext
+from io import StringIO
+
+import jaraco.path
+import path
+import pytest
+from test import support
+
+from .compat import py39 as import_helper
+
+
+@pytest.fixture()
+def user_site_dir(request):
+    self = request.instance
+    self.tmp_dir = self.mkdtemp()
+    self.tmp_path = path.Path(self.tmp_dir)
+    from distutils.command import build_ext
+
+    orig_user_base = site.USER_BASE
+
+    site.USER_BASE = self.mkdtemp()
+    build_ext.USER_BASE = site.USER_BASE
+
+    # bpo-30132: On Windows, a .pdb file may be created in the current
+    # working directory. Create a temporary working directory to cleanup
+    # everything at the end of the test.
+    with self.tmp_path:
+        yield
+
+    site.USER_BASE = orig_user_base
+    build_ext.USER_BASE = orig_user_base
+
+    if sys.platform == 'cygwin':
+        time.sleep(1)
+
+
+@contextlib.contextmanager
+def safe_extension_import(name, path):
+    with import_helper.CleanImport(name):
+        with extension_redirect(name, path) as new_path:
+            with import_helper.DirsOnSysPath(new_path):
+                yield
+
+
+@contextlib.contextmanager
+def extension_redirect(mod, path):
+    """
+    Tests will fail to tear down an extension module if it's been imported.
+
+    Before importing, copy the file to a temporary directory that won't
+    be cleaned up. Yield the new path.
+    """
+    if platform.system() != "Windows" and sys.platform != "cygwin":
+        yield path
+        return
+    with import_helper.DirsOnSysPath(path):
+        spec = importlib.util.find_spec(mod)
+    filename = os.path.basename(spec.origin)
+    trash_dir = tempfile.mkdtemp(prefix='deleteme')
+    dest = os.path.join(trash_dir, os.path.basename(filename))
+    shutil.copy(spec.origin, dest)
+    yield trash_dir
+    # TODO: can the file be scheduled for deletion?
+
+
+@pytest.mark.usefixtures('user_site_dir')
+class TestBuildExt(TempdirManager):
+    def build_ext(self, *args, **kwargs):
+        return build_ext(*args, **kwargs)
+
+    @pytest.mark.parametrize("copy_so", [False])
+    def test_build_ext(self, copy_so):
+        missing_compiler_executable()
+        copy_xxmodule_c(self.tmp_dir)
+        xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
+        xx_ext = Extension('xx', [xx_c])
+        if sys.platform != "win32":
+            if not copy_so:
+                xx_ext = Extension(
+                    'xx',
+                    [xx_c],
+                    library_dirs=['/usr/lib'],
+                    libraries=['z'],
+                    runtime_library_dirs=['/usr/lib'],
+                )
+            elif sys.platform == 'linux':
+                libz_so = {
+                    os.path.realpath(name) for name in glob.iglob('/usr/lib*/libz.so*')
+                }
+                libz_so = sorted(libz_so, key=lambda lib_path: len(lib_path))
+                shutil.copyfile(libz_so[-1], '/tmp/libxx_z.so')
+
+                xx_ext = Extension(
+                    'xx',
+                    [xx_c],
+                    library_dirs=['/tmp'],
+                    libraries=['xx_z'],
+                    runtime_library_dirs=['/tmp'],
+                )
+        dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
+        dist.package_dir = self.tmp_dir
+        cmd = self.build_ext(dist)
+        fixup_build_ext(cmd)
+        cmd.build_lib = self.tmp_dir
+        cmd.build_temp = self.tmp_dir
+
+        old_stdout = sys.stdout
+        if not support.verbose:
+            # silence compiler output
+            sys.stdout = StringIO()
+        try:
+            cmd.ensure_finalized()
+            cmd.run()
+        finally:
+            sys.stdout = old_stdout
+
+        with safe_extension_import('xx', self.tmp_dir):
+            self._test_xx(copy_so)
+
+        if sys.platform == 'linux' and copy_so:
+            os.unlink('/tmp/libxx_z.so')
+
+    @staticmethod
+    def _test_xx(copy_so):
+        import xx  # type: ignore[import-not-found] # Module generated for tests
+
+        for attr in ('error', 'foo', 'new', 'roj'):
+            assert hasattr(xx, attr)
+
+        assert xx.foo(2, 5) == 7
+        assert xx.foo(13, 15) == 28
+        assert xx.new().demo() is None
+        if support.HAVE_DOCSTRINGS:
+            doc = 'This is a template module just for instruction.'
+            assert xx.__doc__ == doc
+        assert isinstance(xx.Null(), xx.Null)
+        assert isinstance(xx.Str(), xx.Str)
+
+        if sys.platform == 'linux':
+            so_headers = subprocess.check_output(
+                ["readelf", "-d", xx.__file__], universal_newlines=True
+            )
+            import pprint
+
+            pprint.pprint(so_headers)
+            rpaths = [
+                rpath
+                for line in so_headers.split("\n")
+                if "RPATH" in line or "RUNPATH" in line
+                for rpath in line.split()[2][1:-1].split(":")
+            ]
+            if not copy_so:
+                pprint.pprint(rpaths)
+                # Linked against a library in /usr/lib{,64}
+                assert "/usr/lib" not in rpaths and "/usr/lib64" not in rpaths
+            else:
+                # Linked against a library in /tmp
+                assert "/tmp" in rpaths
+                # The import is the real test here
+
+    def test_solaris_enable_shared(self):
+        dist = Distribution({'name': 'xx'})
+        cmd = self.build_ext(dist)
+        old = sys.platform
+
+        sys.platform = 'sunos'  # fooling finalize_options
+        from distutils.sysconfig import _config_vars
+
+        old_var = _config_vars.get('Py_ENABLE_SHARED')
+        _config_vars['Py_ENABLE_SHARED'] = True
+        try:
+            cmd.ensure_finalized()
+        finally:
+            sys.platform = old
+            if old_var is None:
+                del _config_vars['Py_ENABLE_SHARED']
+            else:
+                _config_vars['Py_ENABLE_SHARED'] = old_var
+
+        # make sure we get some library dirs under solaris
+        assert len(cmd.library_dirs) > 0
+
+    def test_user_site(self):
+        import site
+
+        dist = Distribution({'name': 'xx'})
+        cmd = self.build_ext(dist)
+
+        # making sure the user option is there
+        options = [name for name, short, label in cmd.user_options]
+        assert 'user' in options
+
+        # setting a value
+        cmd.user = True
+
+        # setting user based lib and include
+        lib = os.path.join(site.USER_BASE, 'lib')
+        incl = os.path.join(site.USER_BASE, 'include')
+        os.mkdir(lib)
+        os.mkdir(incl)
+
+        # let's run finalize
+        cmd.ensure_finalized()
+
+        # see if include_dirs and library_dirs
+        # were set
+        assert lib in cmd.library_dirs
+        assert lib in cmd.rpath
+        assert incl in cmd.include_dirs
+
+    def test_optional_extension(self):
+        # this extension will fail, but let's ignore this failure
+        # with the optional argument.
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.ensure_finalized()
+        with pytest.raises((UnknownFileError, CompileError)):
+            cmd.run()  # should raise an error
+
+        modules = [Extension('foo', ['xxx'], optional=True)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.ensure_finalized()
+        cmd.run()  # should pass
+
+    def test_finalize_options(self):
+        # Make sure Python's include directories (for Python.h, pyconfig.h,
+        # etc.) are in the include search path.
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.finalize_options()
+
+        py_include = sysconfig.get_python_inc()
+        for p in py_include.split(os.path.pathsep):
+            assert p in cmd.include_dirs
+
+        plat_py_include = sysconfig.get_python_inc(plat_specific=True)
+        for p in plat_py_include.split(os.path.pathsep):
+            assert p in cmd.include_dirs
+
+        # make sure cmd.libraries is turned into a list
+        # if it's a string
+        cmd = self.build_ext(dist)
+        cmd.libraries = 'my_lib, other_lib lastlib'
+        cmd.finalize_options()
+        assert cmd.libraries == ['my_lib', 'other_lib', 'lastlib']
+
+        # make sure cmd.library_dirs is turned into a list
+        # if it's a string
+        cmd = self.build_ext(dist)
+        cmd.library_dirs = f'my_lib_dir{os.pathsep}other_lib_dir'
+        cmd.finalize_options()
+        assert 'my_lib_dir' in cmd.library_dirs
+        assert 'other_lib_dir' in cmd.library_dirs
+
+        # make sure rpath is turned into a list
+        # if it's a string
+        cmd = self.build_ext(dist)
+        cmd.rpath = f'one{os.pathsep}two'
+        cmd.finalize_options()
+        assert cmd.rpath == ['one', 'two']
+
+        # make sure cmd.link_objects is turned into a list
+        # if it's a string
+        cmd = build_ext(dist)
+        cmd.link_objects = 'one two,three'
+        cmd.finalize_options()
+        assert cmd.link_objects == ['one', 'two', 'three']
+
+        # XXX more tests to perform for win32
+
+        # make sure define is turned into 2-tuples
+        # strings if they are ','-separated strings
+        cmd = self.build_ext(dist)
+        cmd.define = 'one,two'
+        cmd.finalize_options()
+        assert cmd.define == [('one', '1'), ('two', '1')]
+
+        # make sure undef is turned into a list of
+        # strings if they are ','-separated strings
+        cmd = self.build_ext(dist)
+        cmd.undef = 'one,two'
+        cmd.finalize_options()
+        assert cmd.undef == ['one', 'two']
+
+        # make sure swig_opts is turned into a list
+        cmd = self.build_ext(dist)
+        cmd.swig_opts = None
+        cmd.finalize_options()
+        assert cmd.swig_opts == []
+
+        cmd = self.build_ext(dist)
+        cmd.swig_opts = '1 2'
+        cmd.finalize_options()
+        assert cmd.swig_opts == ['1', '2']
+
+    def test_check_extensions_list(self):
+        dist = Distribution()
+        cmd = self.build_ext(dist)
+        cmd.finalize_options()
+
+        # 'extensions' option must be a list of Extension instances
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_extensions_list('foo')
+
+        # each element of 'ext_modules' option must be an
+        # Extension instance or 2-tuple
+        exts = [('bar', 'foo', 'bar'), 'foo']
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_extensions_list(exts)
+
+        # first element of each tuple in 'ext_modules'
+        # must be the extension name (a string) and match
+        # a python dotted-separated name
+        exts = [('foo-bar', '')]
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_extensions_list(exts)
+
+        # second element of each tuple in 'ext_modules'
+        # must be a dictionary (build info)
+        exts = [('foo.bar', '')]
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_extensions_list(exts)
+
+        # ok this one should pass
+        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', 'some': 'bar'})]
+        cmd.check_extensions_list(exts)
+        ext = exts[0]
+        assert isinstance(ext, Extension)
+
+        # check_extensions_list adds in ext the values passed
+        # when they are in ('include_dirs', 'library_dirs', 'libraries'
+        # 'extra_objects', 'extra_compile_args', 'extra_link_args')
+        assert ext.libraries == 'foo'
+        assert not hasattr(ext, 'some')
+
+        # 'macros' element of build info dict must be 1- or 2-tuple
+        exts = [
+            (
+                'foo.bar',
+                {
+                    'sources': [''],
+                    'libraries': 'foo',
+                    'some': 'bar',
+                    'macros': [('1', '2', '3'), 'foo'],
+                },
+            )
+        ]
+        with pytest.raises(DistutilsSetupError):
+            cmd.check_extensions_list(exts)
+
+        exts[0][1]['macros'] = [('1', '2'), ('3',)]
+        cmd.check_extensions_list(exts)
+        assert exts[0].undef_macros == ['3']
+        assert exts[0].define_macros == [('1', '2')]
+
+    def test_get_source_files(self):
+        modules = [Extension('foo', ['xxx'], optional=False)]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.ensure_finalized()
+        assert cmd.get_source_files() == ['xxx']
+
+    def test_unicode_module_names(self):
+        modules = [
+            Extension('foo', ['aaa'], optional=False),
+            Extension('föö', ['uuu'], optional=False),
+        ]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.ensure_finalized()
+        assert re.search(r'foo(_d)?\..*', cmd.get_ext_filename(modules[0].name))
+        assert re.search(r'föö(_d)?\..*', cmd.get_ext_filename(modules[1].name))
+        assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo']
+        assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa']
+
+    def test_export_symbols__init__(self):
+        # https://github.com/python/cpython/issues/80074
+        # https://github.com/pypa/setuptools/issues/4826
+        modules = [
+            Extension('foo.__init__', ['aaa']),
+            Extension('föö.__init__', ['uuu']),
+        ]
+        dist = Distribution({'name': 'xx', 'ext_modules': modules})
+        cmd = self.build_ext(dist)
+        cmd.ensure_finalized()
+        assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo']
+        assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa']
+
+    def test_compiler_option(self):
+        # cmd.compiler is an option and
+        # should not be overridden by a compiler instance
+        # when the command is run
+        dist = Distribution()
+        cmd = self.build_ext(dist)
+        cmd.compiler = 'unix'
+        cmd.ensure_finalized()
+        cmd.run()
+        assert cmd.compiler == 'unix'
+
+    def test_get_outputs(self):
+        missing_compiler_executable()
+        tmp_dir = self.mkdtemp()
+        c_file = os.path.join(tmp_dir, 'foo.c')
+        self.write_file(c_file, 'void PyInit_foo(void) {}\n')
+        ext = Extension('foo', [c_file], optional=False)
+        dist = Distribution({'name': 'xx', 'ext_modules': [ext]})
+        cmd = self.build_ext(dist)
+        fixup_build_ext(cmd)
+        cmd.ensure_finalized()
+        assert len(cmd.get_outputs()) == 1
+
+        cmd.build_lib = os.path.join(self.tmp_dir, 'build')
+        cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
+
+        # issue #5977 : distutils build_ext.get_outputs
+        # returns wrong result with --inplace
+        other_tmp_dir = os.path.realpath(self.mkdtemp())
+        old_wd = os.getcwd()
+        os.chdir(other_tmp_dir)
+        try:
+            cmd.inplace = True
+            cmd.run()
+            so_file = cmd.get_outputs()[0]
+        finally:
+            os.chdir(old_wd)
+        assert os.path.exists(so_file)
+        ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
+        assert so_file.endswith(ext_suffix)
+        so_dir = os.path.dirname(so_file)
+        assert so_dir == other_tmp_dir
+
+        cmd.inplace = False
+        cmd.compiler = None
+        cmd.run()
+        so_file = cmd.get_outputs()[0]
+        assert os.path.exists(so_file)
+        assert so_file.endswith(ext_suffix)
+        so_dir = os.path.dirname(so_file)
+        assert so_dir == cmd.build_lib
+
+        # inplace = False, cmd.package = 'bar'
+        build_py = cmd.get_finalized_command('build_py')
+        build_py.package_dir = {'': 'bar'}
+        path = cmd.get_ext_fullpath('foo')
+        # checking that the last directory is the build_dir
+        path = os.path.split(path)[0]
+        assert path == cmd.build_lib
+
+        # inplace = True, cmd.package = 'bar'
+        cmd.inplace = True
+        other_tmp_dir = os.path.realpath(self.mkdtemp())
+        old_wd = os.getcwd()
+        os.chdir(other_tmp_dir)
+        try:
+            path = cmd.get_ext_fullpath('foo')
+        finally:
+            os.chdir(old_wd)
+        # checking that the last directory is bar
+        path = os.path.split(path)[0]
+        lastdir = os.path.split(path)[-1]
+        assert lastdir == 'bar'
+
+    def test_ext_fullpath(self):
+        ext = sysconfig.get_config_var('EXT_SUFFIX')
+        # building lxml.etree inplace
+        # etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
+        # etree_ext = Extension('lxml.etree', [etree_c])
+        # dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
+        dist = Distribution()
+        cmd = self.build_ext(dist)
+        cmd.inplace = True
+        cmd.distribution.package_dir = {'': 'src'}
+        cmd.distribution.packages = ['lxml', 'lxml.html']
+        curdir = os.getcwd()
+        wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
+        path = cmd.get_ext_fullpath('lxml.etree')
+        assert wanted == path
+
+        # building lxml.etree not inplace
+        cmd.inplace = False
+        cmd.build_lib = os.path.join(curdir, 'tmpdir')
+        wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
+        path = cmd.get_ext_fullpath('lxml.etree')
+        assert wanted == path
+
+        # building twisted.runner.portmap not inplace
+        build_py = cmd.get_finalized_command('build_py')
+        build_py.package_dir = {}
+        cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
+        path = cmd.get_ext_fullpath('twisted.runner.portmap')
+        wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext)
+        assert wanted == path
+
+        # building twisted.runner.portmap inplace
+        cmd.inplace = True
+        path = cmd.get_ext_fullpath('twisted.runner.portmap')
+        wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
+        assert wanted == path
+
+    @pytest.mark.skipif('platform.system() != "Darwin"')
+    @pytest.mark.usefixtures('save_env')
+    def test_deployment_target_default(self):
+        # Issue 9516: Test that, in the absence of the environment variable,
+        # an extension module is compiled with the same deployment target as
+        #  the interpreter.
+        self._try_compile_deployment_target('==', None)
+
+    @pytest.mark.skipif('platform.system() != "Darwin"')
+    @pytest.mark.usefixtures('save_env')
+    def test_deployment_target_too_low(self):
+        # Issue 9516: Test that an extension module is not allowed to be
+        # compiled with a deployment target less than that of the interpreter.
+        with pytest.raises(DistutilsPlatformError):
+            self._try_compile_deployment_target('>', '10.1')
+
+    @pytest.mark.skipif('platform.system() != "Darwin"')
+    @pytest.mark.usefixtures('save_env')
+    def test_deployment_target_higher_ok(self):  # pragma: no cover
+        # Issue 9516: Test that an extension module can be compiled with a
+        # deployment target higher than that of the interpreter: the ext
+        # module may depend on some newer OS feature.
+        deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+        if deptarget:
+            # increment the minor version number (i.e. 10.6 -> 10.7)
+            deptarget = [int(x) for x in deptarget.split('.')]
+            deptarget[-1] += 1
+            deptarget = '.'.join(str(i) for i in deptarget)
+            self._try_compile_deployment_target('<', deptarget)
+
+    def _try_compile_deployment_target(self, operator, target):  # pragma: no cover
+        if target is None:
+            if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
+                del os.environ['MACOSX_DEPLOYMENT_TARGET']
+        else:
+            os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
+
+        jaraco.path.build(
+            {
+                'deptargetmodule.c': textwrap.dedent(f"""\
+                    #include 
+
+                    int dummy;
+
+                    #if TARGET {operator} MAC_OS_X_VERSION_MIN_REQUIRED
+                    #else
+                    #error "Unexpected target"
+                    #endif
+
+                    """),
+            },
+            self.tmp_path,
+        )
+
+        # get the deployment target that the interpreter was built with
+        target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+        target = tuple(map(int, target.split('.')[0:2]))
+        # format the target value as defined in the Apple
+        # Availability Macros.  We can't use the macro names since
+        # at least one value we test with will not exist yet.
+        if target[:2] < (10, 10):
+            # for 10.1 through 10.9.x -> "10n0"
+            tmpl = '{:02}{:01}0'
+        else:
+            # for 10.10 and beyond -> "10nn00"
+            if len(target) >= 2:
+                tmpl = '{:02}{:02}00'
+            else:
+                # 11 and later can have no minor version (11 instead of 11.0)
+                tmpl = '{:02}0000'
+        target = tmpl.format(*target)
+        deptarget_ext = Extension(
+            'deptarget',
+            [self.tmp_path / 'deptargetmodule.c'],
+            extra_compile_args=[f'-DTARGET={target}'],
+        )
+        dist = Distribution({'name': 'deptarget', 'ext_modules': [deptarget_ext]})
+        dist.package_dir = self.tmp_dir
+        cmd = self.build_ext(dist)
+        cmd.build_lib = self.tmp_dir
+        cmd.build_temp = self.tmp_dir
+
+        try:
+            old_stdout = sys.stdout
+            if not support.verbose:
+                # silence compiler output
+                sys.stdout = StringIO()
+            try:
+                cmd.ensure_finalized()
+                cmd.run()
+            finally:
+                sys.stdout = old_stdout
+
+        except CompileError:
+            self.fail("Wrong deployment target during compilation")
+
+
+class TestParallelBuildExt(TestBuildExt):
+    def build_ext(self, *args, **kwargs):
+        build_ext = super().build_ext(*args, **kwargs)
+        build_ext.parallel = True
+        return build_ext
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_py.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_py.py
new file mode 100644
index 0000000..b316ed4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_py.py
@@ -0,0 +1,196 @@
+"""Tests for distutils.command.build_py."""
+
+import os
+import sys
+from distutils.command.build_py import build_py
+from distutils.core import Distribution
+from distutils.errors import DistutilsFileError
+from distutils.tests import support
+
+import jaraco.path
+import pytest
+
+
+@support.combine_markers
+class TestBuildPy(support.TempdirManager):
+    def test_package_data(self):
+        sources = self.mkdtemp()
+        jaraco.path.build(
+            {
+                '__init__.py': "# Pretend this is a package.",
+                'README.txt': 'Info about this package',
+            },
+            sources,
+        )
+
+        destination = self.mkdtemp()
+
+        dist = Distribution({"packages": ["pkg"], "package_dir": {"pkg": sources}})
+        # script_name need not exist, it just need to be initialized
+        dist.script_name = os.path.join(sources, "setup.py")
+        dist.command_obj["build"] = support.DummyCommand(
+            force=False, build_lib=destination
+        )
+        dist.packages = ["pkg"]
+        dist.package_data = {"pkg": ["README.txt"]}
+        dist.package_dir = {"pkg": sources}
+
+        cmd = build_py(dist)
+        cmd.compile = True
+        cmd.ensure_finalized()
+        assert cmd.package_data == dist.package_data
+
+        cmd.run()
+
+        # This makes sure the list of outputs includes byte-compiled
+        # files for Python modules but not for package data files
+        # (there shouldn't *be* byte-code files for those!).
+        assert len(cmd.get_outputs()) == 3
+        pkgdest = os.path.join(destination, "pkg")
+        files = os.listdir(pkgdest)
+        pycache_dir = os.path.join(pkgdest, "__pycache__")
+        assert "__init__.py" in files
+        assert "README.txt" in files
+        if sys.dont_write_bytecode:
+            assert not os.path.exists(pycache_dir)
+        else:
+            pyc_files = os.listdir(pycache_dir)
+            assert f"__init__.{sys.implementation.cache_tag}.pyc" in pyc_files
+
+    def test_empty_package_dir(self):
+        # See bugs #1668596/#1720897
+        sources = self.mkdtemp()
+        jaraco.path.build({'__init__.py': '', 'doc': {'testfile': ''}}, sources)
+
+        os.chdir(sources)
+        dist = Distribution({
+            "packages": ["pkg"],
+            "package_dir": {"pkg": ""},
+            "package_data": {"pkg": ["doc/*"]},
+        })
+        # script_name need not exist, it just need to be initialized
+        dist.script_name = os.path.join(sources, "setup.py")
+        dist.script_args = ["build"]
+        dist.parse_command_line()
+
+        try:
+            dist.run_commands()
+        except DistutilsFileError:
+            self.fail("failed package_data test when package_dir is ''")
+
+    @pytest.mark.skipif('sys.dont_write_bytecode')
+    def test_byte_compile(self):
+        project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+        os.chdir(project_dir)
+        self.write_file('boiledeggs.py', 'import antigravity')
+        cmd = build_py(dist)
+        cmd.compile = True
+        cmd.build_lib = 'here'
+        cmd.finalize_options()
+        cmd.run()
+
+        found = os.listdir(cmd.build_lib)
+        assert sorted(found) == ['__pycache__', 'boiledeggs.py']
+        found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+        assert found == [f'boiledeggs.{sys.implementation.cache_tag}.pyc']
+
+    @pytest.mark.skipif('sys.dont_write_bytecode')
+    def test_byte_compile_optimized(self):
+        project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+        os.chdir(project_dir)
+        self.write_file('boiledeggs.py', 'import antigravity')
+        cmd = build_py(dist)
+        cmd.compile = False
+        cmd.optimize = 1
+        cmd.build_lib = 'here'
+        cmd.finalize_options()
+        cmd.run()
+
+        found = os.listdir(cmd.build_lib)
+        assert sorted(found) == ['__pycache__', 'boiledeggs.py']
+        found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+        expect = f'boiledeggs.{sys.implementation.cache_tag}.opt-1.pyc'
+        assert sorted(found) == [expect]
+
+    def test_dir_in_package_data(self):
+        """
+        A directory in package_data should not be added to the filelist.
+        """
+        # See bug 19286
+        sources = self.mkdtemp()
+        jaraco.path.build(
+            {
+                'pkg': {
+                    '__init__.py': '',
+                    'doc': {
+                        'testfile': '',
+                        # create a directory that could be incorrectly detected as a file
+                        'otherdir': {},
+                    },
+                }
+            },
+            sources,
+        )
+
+        os.chdir(sources)
+        dist = Distribution({"packages": ["pkg"], "package_data": {"pkg": ["doc/*"]}})
+        # script_name need not exist, it just need to be initialized
+        dist.script_name = os.path.join(sources, "setup.py")
+        dist.script_args = ["build"]
+        dist.parse_command_line()
+
+        try:
+            dist.run_commands()
+        except DistutilsFileError:
+            self.fail("failed package_data when data dir includes a dir")
+
+    def test_dont_write_bytecode(self, caplog):
+        # makes sure byte_compile is not used
+        dist = self.create_dist()[1]
+        cmd = build_py(dist)
+        cmd.compile = True
+        cmd.optimize = 1
+
+        old_dont_write_bytecode = sys.dont_write_bytecode
+        sys.dont_write_bytecode = True
+        try:
+            cmd.byte_compile([])
+        finally:
+            sys.dont_write_bytecode = old_dont_write_bytecode
+
+        assert 'byte-compiling is disabled' in caplog.records[0].message
+
+    def test_namespace_package_does_not_warn(self, caplog):
+        """
+        Originally distutils implementation did not account for PEP 420
+        and included warns for package directories that did not contain
+        ``__init__.py`` files.
+        After the acceptance of PEP 420, these warnings don't make more sense
+        so we want to ensure there are not displayed to not confuse the users.
+        """
+        # Create a fake project structure with a package namespace:
+        tmp = self.mkdtemp()
+        jaraco.path.build({'ns': {'pkg': {'module.py': ''}}}, tmp)
+        os.chdir(tmp)
+
+        # Configure the package:
+        attrs = {
+            "name": "ns.pkg",
+            "packages": ["ns", "ns.pkg"],
+            "script_name": "setup.py",
+        }
+        dist = Distribution(attrs)
+
+        # Run code paths that would trigger the trap:
+        cmd = dist.get_command_obj("build_py")
+        cmd.finalize_options()
+        modules = cmd.find_all_modules()
+        assert len(modules) == 1
+        module_path = modules[0][-1]
+        assert module_path.replace(os.sep, "/") == "ns/pkg/module.py"
+
+        cmd.run()
+
+        assert not any(
+            "package init file" in msg and "not found" in msg for msg in caplog.messages
+        )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_scripts.py
new file mode 100644
index 0000000..3582f69
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_build_scripts.py
@@ -0,0 +1,96 @@
+"""Tests for distutils.command.build_scripts."""
+
+import os
+import textwrap
+from distutils import sysconfig
+from distutils.command.build_scripts import build_scripts
+from distutils.core import Distribution
+from distutils.tests import support
+
+import jaraco.path
+
+
+class TestBuildScripts(support.TempdirManager):
+    def test_default_settings(self):
+        cmd = self.get_build_scripts_cmd("/foo/bar", [])
+        assert not cmd.force
+        assert cmd.build_dir is None
+
+        cmd.finalize_options()
+
+        assert cmd.force
+        assert cmd.build_dir == "/foo/bar"
+
+    def test_build(self):
+        source = self.mkdtemp()
+        target = self.mkdtemp()
+        expected = self.write_sample_scripts(source)
+
+        cmd = self.get_build_scripts_cmd(
+            target, [os.path.join(source, fn) for fn in expected]
+        )
+        cmd.finalize_options()
+        cmd.run()
+
+        built = os.listdir(target)
+        for name in expected:
+            assert name in built
+
+    def get_build_scripts_cmd(self, target, scripts):
+        import sys
+
+        dist = Distribution()
+        dist.scripts = scripts
+        dist.command_obj["build"] = support.DummyCommand(
+            build_scripts=target, force=True, executable=sys.executable
+        )
+        return build_scripts(dist)
+
+    @staticmethod
+    def write_sample_scripts(dir):
+        spec = {
+            'script1.py': textwrap.dedent("""
+                #! /usr/bin/env python2.3
+                # bogus script w/ Python sh-bang
+                pass
+                """).lstrip(),
+            'script2.py': textwrap.dedent("""
+                #!/usr/bin/python
+                # bogus script w/ Python sh-bang
+                pass
+                """).lstrip(),
+            'shell.sh': textwrap.dedent("""
+                #!/bin/sh
+                # bogus shell script w/ sh-bang
+                exit 0
+                """).lstrip(),
+        }
+        jaraco.path.build(spec, dir)
+        return list(spec)
+
+    def test_version_int(self):
+        source = self.mkdtemp()
+        target = self.mkdtemp()
+        expected = self.write_sample_scripts(source)
+
+        cmd = self.get_build_scripts_cmd(
+            target, [os.path.join(source, fn) for fn in expected]
+        )
+        cmd.finalize_options()
+
+        # https://bugs.python.org/issue4524
+        #
+        # On linux-g++-32 with command line `./configure --enable-ipv6
+        # --with-suffix=3`, python is compiled okay but the build scripts
+        # failed when writing the name of the executable
+        old = sysconfig.get_config_vars().get('VERSION')
+        sysconfig._config_vars['VERSION'] = 4
+        try:
+            cmd.run()
+        finally:
+            if old is not None:
+                sysconfig._config_vars['VERSION'] = old
+
+        built = os.listdir(target)
+        for name in expected:
+            assert name in built
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_check.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_check.py
new file mode 100644
index 0000000..b672b1f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_check.py
@@ -0,0 +1,194 @@
+"""Tests for distutils.command.check."""
+
+import os
+import textwrap
+from distutils.command.check import check
+from distutils.errors import DistutilsSetupError
+from distutils.tests import support
+
+import pytest
+
+try:
+    import pygments
+except ImportError:
+    pygments = None
+
+
+HERE = os.path.dirname(__file__)
+
+
+@support.combine_markers
+class TestCheck(support.TempdirManager):
+    def _run(self, metadata=None, cwd=None, **options):
+        if metadata is None:
+            metadata = {}
+        if cwd is not None:
+            old_dir = os.getcwd()
+            os.chdir(cwd)
+        pkg_info, dist = self.create_dist(**metadata)
+        cmd = check(dist)
+        cmd.initialize_options()
+        for name, value in options.items():
+            setattr(cmd, name, value)
+        cmd.ensure_finalized()
+        cmd.run()
+        if cwd is not None:
+            os.chdir(old_dir)
+        return cmd
+
+    def test_check_metadata(self):
+        # let's run the command with no metadata at all
+        # by default, check is checking the metadata
+        # should have some warnings
+        cmd = self._run()
+        assert cmd._warnings == 1
+
+        # now let's add the required fields
+        # and run it again, to make sure we don't get
+        # any warning anymore
+        metadata = {
+            'url': 'xxx',
+            'author': 'xxx',
+            'author_email': 'xxx',
+            'name': 'xxx',
+            'version': 'xxx',
+        }
+        cmd = self._run(metadata)
+        assert cmd._warnings == 0
+
+        # now with the strict mode, we should
+        # get an error if there are missing metadata
+        with pytest.raises(DistutilsSetupError):
+            self._run({}, **{'strict': 1})
+
+        # and of course, no error when all metadata are present
+        cmd = self._run(metadata, strict=True)
+        assert cmd._warnings == 0
+
+        # now a test with non-ASCII characters
+        metadata = {
+            'url': 'xxx',
+            'author': '\u00c9ric',
+            'author_email': 'xxx',
+            'name': 'xxx',
+            'version': 'xxx',
+            'description': 'Something about esszet \u00df',
+            'long_description': 'More things about esszet \u00df',
+        }
+        cmd = self._run(metadata)
+        assert cmd._warnings == 0
+
+    def test_check_author_maintainer(self):
+        for kind in ("author", "maintainer"):
+            # ensure no warning when author_email or maintainer_email is given
+            # (the spec allows these fields to take the form "Name ")
+            metadata = {
+                'url': 'xxx',
+                kind + '_email': 'Name ',
+                'name': 'xxx',
+                'version': 'xxx',
+            }
+            cmd = self._run(metadata)
+            assert cmd._warnings == 0
+
+            # the check should not warn if only email is given
+            metadata[kind + '_email'] = 'name@email.com'
+            cmd = self._run(metadata)
+            assert cmd._warnings == 0
+
+            # the check should not warn if only the name is given
+            metadata[kind] = "Name"
+            del metadata[kind + '_email']
+            cmd = self._run(metadata)
+            assert cmd._warnings == 0
+
+    def test_check_document(self):
+        pytest.importorskip('docutils')
+        pkg_info, dist = self.create_dist()
+        cmd = check(dist)
+
+        # let's see if it detects broken rest
+        broken_rest = 'title\n===\n\ntest'
+        msgs = cmd._check_rst_data(broken_rest)
+        assert len(msgs) == 1
+
+        # and non-broken rest
+        rest = 'title\n=====\n\ntest'
+        msgs = cmd._check_rst_data(rest)
+        assert len(msgs) == 0
+
+    def test_check_restructuredtext(self):
+        pytest.importorskip('docutils')
+        # let's see if it detects broken rest in long_description
+        broken_rest = 'title\n===\n\ntest'
+        pkg_info, dist = self.create_dist(long_description=broken_rest)
+        cmd = check(dist)
+        cmd.check_restructuredtext()
+        assert cmd._warnings == 1
+
+        # let's see if we have an error with strict=True
+        metadata = {
+            'url': 'xxx',
+            'author': 'xxx',
+            'author_email': 'xxx',
+            'name': 'xxx',
+            'version': 'xxx',
+            'long_description': broken_rest,
+        }
+        with pytest.raises(DistutilsSetupError):
+            self._run(metadata, **{'strict': 1, 'restructuredtext': 1})
+
+        # and non-broken rest, including a non-ASCII character to test #12114
+        metadata['long_description'] = 'title\n=====\n\ntest \u00df'
+        cmd = self._run(metadata, strict=True, restructuredtext=True)
+        assert cmd._warnings == 0
+
+        # check that includes work to test #31292
+        metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst'
+        cmd = self._run(metadata, cwd=HERE, strict=True, restructuredtext=True)
+        assert cmd._warnings == 0
+
+    def test_check_restructuredtext_with_syntax_highlight(self):
+        pytest.importorskip('docutils')
+        # Don't fail if there is a `code` or `code-block` directive
+
+        example_rst_docs = [
+            textwrap.dedent(
+                """\
+            Here's some code:
+
+            .. code:: python
+
+                def foo():
+                    pass
+            """
+            ),
+            textwrap.dedent(
+                """\
+            Here's some code:
+
+            .. code-block:: python
+
+                def foo():
+                    pass
+            """
+            ),
+        ]
+
+        for rest_with_code in example_rst_docs:
+            pkg_info, dist = self.create_dist(long_description=rest_with_code)
+            cmd = check(dist)
+            cmd.check_restructuredtext()
+            msgs = cmd._check_rst_data(rest_with_code)
+            if pygments is not None:
+                assert len(msgs) == 0
+            else:
+                assert len(msgs) == 1
+                assert (
+                    str(msgs[0][1])
+                    == 'Cannot analyze code. Pygments package not found.'
+                )
+
+    def test_check_all(self):
+        with pytest.raises(DistutilsSetupError):
+            self._run({}, **{'strict': 1, 'restructuredtext': 1})
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_clean.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_clean.py
new file mode 100644
index 0000000..cc78f30
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_clean.py
@@ -0,0 +1,45 @@
+"""Tests for distutils.command.clean."""
+
+import os
+from distutils.command.clean import clean
+from distutils.tests import support
+
+
+class TestClean(support.TempdirManager):
+    def test_simple_run(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = clean(dist)
+
+        # let's add some elements clean should remove
+        dirs = [
+            (d, os.path.join(pkg_dir, d))
+            for d in (
+                'build_temp',
+                'build_lib',
+                'bdist_base',
+                'build_scripts',
+                'build_base',
+            )
+        ]
+
+        for name, path in dirs:
+            os.mkdir(path)
+            setattr(cmd, name, path)
+            if name == 'build_base':
+                continue
+            for f in ('one', 'two', 'three'):
+                self.write_file(os.path.join(path, f))
+
+        # let's run the command
+        cmd.all = 1
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # make sure the files where removed
+        for _name, path in dirs:
+            assert not os.path.exists(path), f'{path} was not removed'
+
+        # let's run the command again (should spit warnings but succeed)
+        cmd.all = 1
+        cmd.ensure_finalized()
+        cmd.run()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_cmd.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_cmd.py
new file mode 100644
index 0000000..76e8f59
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_cmd.py
@@ -0,0 +1,107 @@
+"""Tests for distutils.cmd."""
+
+import os
+from distutils import debug
+from distutils.cmd import Command
+from distutils.dist import Distribution
+from distutils.errors import DistutilsOptionError
+
+import pytest
+
+
+class MyCmd(Command):
+    def initialize_options(self):
+        pass
+
+
+@pytest.fixture
+def cmd(request):
+    return MyCmd(Distribution())
+
+
+class TestCommand:
+    def test_ensure_string_list(self, cmd):
+        cmd.not_string_list = ['one', 2, 'three']
+        cmd.yes_string_list = ['one', 'two', 'three']
+        cmd.not_string_list2 = object()
+        cmd.yes_string_list2 = 'ok'
+        cmd.ensure_string_list('yes_string_list')
+        cmd.ensure_string_list('yes_string_list2')
+
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_string_list('not_string_list')
+
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_string_list('not_string_list2')
+
+        cmd.option1 = 'ok,dok'
+        cmd.ensure_string_list('option1')
+        assert cmd.option1 == ['ok', 'dok']
+
+        cmd.option2 = ['xxx', 'www']
+        cmd.ensure_string_list('option2')
+
+        cmd.option3 = ['ok', 2]
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_string_list('option3')
+
+    def test_make_file(self, cmd):
+        # making sure it raises when infiles is not a string or a list/tuple
+        with pytest.raises(TypeError):
+            cmd.make_file(infiles=True, outfile='', func='func', args=())
+
+        # making sure execute gets called properly
+        def _execute(func, args, exec_msg, level):
+            assert exec_msg == 'generating out from in'
+
+        cmd.force = True
+        cmd.execute = _execute
+        cmd.make_file(infiles='in', outfile='out', func='func', args=())
+
+    def test_dump_options(self, cmd):
+        msgs = []
+
+        def _announce(msg, level):
+            msgs.append(msg)
+
+        cmd.announce = _announce
+        cmd.option1 = 1
+        cmd.option2 = 1
+        cmd.user_options = [('option1', '', ''), ('option2', '', '')]
+        cmd.dump_options()
+
+        wanted = ["command options for 'MyCmd':", '  option1 = 1', '  option2 = 1']
+        assert msgs == wanted
+
+    def test_ensure_string(self, cmd):
+        cmd.option1 = 'ok'
+        cmd.ensure_string('option1')
+
+        cmd.option2 = None
+        cmd.ensure_string('option2', 'xxx')
+        assert hasattr(cmd, 'option2')
+
+        cmd.option3 = 1
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_string('option3')
+
+    def test_ensure_filename(self, cmd):
+        cmd.option1 = __file__
+        cmd.ensure_filename('option1')
+        cmd.option2 = 'xxx'
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_filename('option2')
+
+    def test_ensure_dirname(self, cmd):
+        cmd.option1 = os.path.dirname(__file__) or os.curdir
+        cmd.ensure_dirname('option1')
+        cmd.option2 = 'xxx'
+        with pytest.raises(DistutilsOptionError):
+            cmd.ensure_dirname('option2')
+
+    def test_debug_print(self, cmd, capsys, monkeypatch):
+        cmd.debug_print('xxx')
+        assert capsys.readouterr().out == ''
+        monkeypatch.setattr(debug, 'DEBUG', True)
+        cmd.debug_print('xxx')
+        assert capsys.readouterr().out == 'xxx\n'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_config_cmd.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_config_cmd.py
new file mode 100644
index 0000000..ebee2ef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_config_cmd.py
@@ -0,0 +1,87 @@
+"""Tests for distutils.command.config."""
+
+import os
+import sys
+from distutils._log import log
+from distutils.command.config import config, dump_file
+from distutils.tests import missing_compiler_executable, support
+
+import more_itertools
+import path
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def info_log(request, monkeypatch):
+    self = request.instance
+    self._logs = []
+    monkeypatch.setattr(log, 'info', self._info)
+
+
+@support.combine_markers
+class TestConfig(support.TempdirManager):
+    def _info(self, msg, *args):
+        for line in msg.splitlines():
+            self._logs.append(line)
+
+    def test_dump_file(self):
+        this_file = path.Path(__file__).with_suffix('.py')
+        with this_file.open(encoding='utf-8') as f:
+            numlines = more_itertools.ilen(f)
+
+        dump_file(this_file, 'I am the header')
+        assert len(self._logs) == numlines + 1
+
+    @pytest.mark.skipif('platform.system() == "Windows"')
+    def test_search_cpp(self):
+        cmd = missing_compiler_executable(['preprocessor'])
+        if cmd is not None:
+            self.skipTest(f'The {cmd!r} command is not found')
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+        cmd._check_compiler()
+        compiler = cmd.compiler
+        if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower():
+            self.skipTest(
+                'xlc: The -E option overrides the -P, -o, and -qsyntaxonly options'
+            )
+
+        # simple pattern searches
+        match = cmd.search_cpp(pattern='xxx', body='/* xxx */')
+        assert match == 0
+
+        match = cmd.search_cpp(pattern='_configtest', body='/* xxx */')
+        assert match == 1
+
+    def test_finalize_options(self):
+        # finalize_options does a bit of transformation
+        # on options
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+        cmd.include_dirs = f'one{os.pathsep}two'
+        cmd.libraries = 'one'
+        cmd.library_dirs = f'three{os.pathsep}four'
+        cmd.ensure_finalized()
+
+        assert cmd.include_dirs == ['one', 'two']
+        assert cmd.libraries == ['one']
+        assert cmd.library_dirs == ['three', 'four']
+
+    def test_clean(self):
+        # _clean removes files
+        tmp_dir = self.mkdtemp()
+        f1 = os.path.join(tmp_dir, 'one')
+        f2 = os.path.join(tmp_dir, 'two')
+
+        self.write_file(f1, 'xxx')
+        self.write_file(f2, 'xxx')
+
+        for f in (f1, f2):
+            assert os.path.exists(f)
+
+        pkg_dir, dist = self.create_dist()
+        cmd = config(dist)
+        cmd._clean(f1, f2)
+
+        for f in (f1, f2):
+            assert not os.path.exists(f)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_core.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_core.py
new file mode 100644
index 0000000..bad3fb7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_core.py
@@ -0,0 +1,130 @@
+"""Tests for distutils.core."""
+
+import distutils.core
+import io
+import os
+import sys
+from distutils.dist import Distribution
+
+import pytest
+
+# setup script that uses __file__
+setup_using___file__ = """\
+
+__file__
+
+from distutils.core import setup
+setup()
+"""
+
+setup_prints_cwd = """\
+
+import os
+print(os.getcwd())
+
+from distutils.core import setup
+setup()
+"""
+
+setup_does_nothing = """\
+from distutils.core import setup
+setup()
+"""
+
+
+setup_defines_subclass = """\
+from distutils.core import setup
+from distutils.command.install import install as _install
+
+class install(_install):
+    sub_commands = _install.sub_commands + ['cmd']
+
+setup(cmdclass={'install': install})
+"""
+
+setup_within_if_main = """\
+from distutils.core import setup
+
+def main():
+    return setup(name="setup_within_if_main")
+
+if __name__ == "__main__":
+    main()
+"""
+
+
+@pytest.fixture(autouse=True)
+def save_stdout(monkeypatch):
+    monkeypatch.setattr(sys, 'stdout', sys.stdout)
+
+
+@pytest.fixture
+def temp_file(tmp_path):
+    return tmp_path / 'file'
+
+
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+class TestCore:
+    def test_run_setup_provides_file(self, temp_file):
+        # Make sure the script can use __file__; if that's missing, the test
+        # setup.py script will raise NameError.
+        temp_file.write_text(setup_using___file__, encoding='utf-8')
+        distutils.core.run_setup(temp_file)
+
+    def test_run_setup_preserves_sys_argv(self, temp_file):
+        # Make sure run_setup does not clobber sys.argv
+        argv_copy = sys.argv.copy()
+        temp_file.write_text(setup_does_nothing, encoding='utf-8')
+        distutils.core.run_setup(temp_file)
+        assert sys.argv == argv_copy
+
+    def test_run_setup_defines_subclass(self, temp_file):
+        # Make sure the script can use __file__; if that's missing, the test
+        # setup.py script will raise NameError.
+        temp_file.write_text(setup_defines_subclass, encoding='utf-8')
+        dist = distutils.core.run_setup(temp_file)
+        install = dist.get_command_obj('install')
+        assert 'cmd' in install.sub_commands
+
+    def test_run_setup_uses_current_dir(self, tmp_path):
+        """
+        Test that the setup script is run with the current directory
+        as its own current directory.
+        """
+        sys.stdout = io.StringIO()
+        cwd = os.getcwd()
+
+        # Create a directory and write the setup.py file there:
+        setup_py = tmp_path / 'setup.py'
+        setup_py.write_text(setup_prints_cwd, encoding='utf-8')
+        distutils.core.run_setup(setup_py)
+
+        output = sys.stdout.getvalue()
+        if output.endswith("\n"):
+            output = output[:-1]
+        assert cwd == output
+
+    def test_run_setup_within_if_main(self, temp_file):
+        temp_file.write_text(setup_within_if_main, encoding='utf-8')
+        dist = distutils.core.run_setup(temp_file, stop_after="config")
+        assert isinstance(dist, Distribution)
+        assert dist.get_name() == "setup_within_if_main"
+
+    def test_run_commands(self, temp_file):
+        sys.argv = ['setup.py', 'build']
+        temp_file.write_text(setup_within_if_main, encoding='utf-8')
+        dist = distutils.core.run_setup(temp_file, stop_after="commandline")
+        assert 'build' not in dist.have_run
+        distutils.core.run_commands(dist)
+        assert 'build' in dist.have_run
+
+    def test_debug_mode(self, capsys, monkeypatch):
+        # this covers the code called when DEBUG is set
+        sys.argv = ['setup.py', '--name']
+        distutils.core.setup(name='bar')
+        assert capsys.readouterr().out == 'bar\n'
+        monkeypatch.setattr(distutils.core, 'DEBUG', True)
+        distutils.core.setup(name='bar')
+        wanted = "options (after parsing config files):\n"
+        assert capsys.readouterr().out.startswith(wanted)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dir_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dir_util.py
new file mode 100644
index 0000000..326cb34
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dir_util.py
@@ -0,0 +1,139 @@
+"""Tests for distutils.dir_util."""
+
+import os
+import pathlib
+import stat
+import sys
+import unittest.mock as mock
+from distutils import dir_util, errors
+from distutils.dir_util import (
+    copy_tree,
+    create_tree,
+    ensure_relative,
+    mkpath,
+    remove_tree,
+)
+from distutils.tests import support
+
+import jaraco.path
+import path
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+    self = request.instance
+    tmp_dir = self.mkdtemp()
+    self.root_target = os.path.join(tmp_dir, 'deep')
+    self.target = os.path.join(self.root_target, 'here')
+    self.target2 = os.path.join(tmp_dir, 'deep2')
+
+
+class TestDirUtil(support.TempdirManager):
+    def test_mkpath_remove_tree_verbosity(self, caplog):
+        mkpath(self.target, verbose=False)
+        assert not caplog.records
+        remove_tree(self.root_target, verbose=False)
+
+        mkpath(self.target, verbose=True)
+        wanted = [f'creating {self.target}']
+        assert caplog.messages == wanted
+        caplog.clear()
+
+        remove_tree(self.root_target, verbose=True)
+        wanted = [f"removing '{self.root_target}' (and everything under it)"]
+        assert caplog.messages == wanted
+
+    @pytest.mark.skipif("platform.system() == 'Windows'")
+    def test_mkpath_with_custom_mode(self):
+        # Get and set the current umask value for testing mode bits.
+        umask = os.umask(0o002)
+        os.umask(umask)
+        mkpath(self.target, 0o700)
+        assert stat.S_IMODE(os.stat(self.target).st_mode) == 0o700 & ~umask
+        mkpath(self.target2, 0o555)
+        assert stat.S_IMODE(os.stat(self.target2).st_mode) == 0o555 & ~umask
+
+    def test_create_tree_verbosity(self, caplog):
+        create_tree(self.root_target, ['one', 'two', 'three'], verbose=False)
+        assert caplog.messages == []
+        remove_tree(self.root_target, verbose=False)
+
+        wanted = [f'creating {self.root_target}']
+        create_tree(self.root_target, ['one', 'two', 'three'], verbose=True)
+        assert caplog.messages == wanted
+
+        remove_tree(self.root_target, verbose=False)
+
+    def test_copy_tree_verbosity(self, caplog):
+        mkpath(self.target, verbose=False)
+
+        copy_tree(self.target, self.target2, verbose=False)
+        assert caplog.messages == []
+
+        remove_tree(self.root_target, verbose=False)
+
+        mkpath(self.target, verbose=False)
+        a_file = path.Path(self.target) / 'ok.txt'
+        jaraco.path.build({'ok.txt': 'some content'}, self.target)
+
+        wanted = [f'copying {a_file} -> {self.target2}']
+        copy_tree(self.target, self.target2, verbose=True)
+        assert caplog.messages == wanted
+
+        remove_tree(self.root_target, verbose=False)
+        remove_tree(self.target2, verbose=False)
+
+    def test_copy_tree_skips_nfs_temp_files(self):
+        mkpath(self.target, verbose=False)
+
+        jaraco.path.build({'ok.txt': 'some content', '.nfs123abc': ''}, self.target)
+
+        copy_tree(self.target, self.target2)
+        assert os.listdir(self.target2) == ['ok.txt']
+
+        remove_tree(self.root_target, verbose=False)
+        remove_tree(self.target2, verbose=False)
+
+    def test_ensure_relative(self):
+        if os.sep == '/':
+            assert ensure_relative('/home/foo') == 'home/foo'
+            assert ensure_relative('some/path') == 'some/path'
+        else:  # \\
+            assert ensure_relative('c:\\home\\foo') == 'c:home\\foo'
+            assert ensure_relative('home\\foo') == 'home\\foo'
+
+    def test_copy_tree_exception_in_listdir(self):
+        """
+        An exception in listdir should raise a DistutilsFileError
+        """
+        with (
+            mock.patch("os.listdir", side_effect=OSError()),
+            pytest.raises(errors.DistutilsFileError),
+        ):
+            src = self.tempdirs[-1]
+            dir_util.copy_tree(src, None)
+
+    def test_mkpath_exception_uncached(self, monkeypatch, tmp_path):
+        """
+        Caching should not remember failed attempts.
+
+        pypa/distutils#304
+        """
+
+        class FailPath(pathlib.Path):
+            def mkdir(self, *args, **kwargs):
+                raise OSError("Failed to create directory")
+
+            if sys.version_info < (3, 12):
+                _flavour = pathlib.Path()._flavour
+
+        target = tmp_path / 'foodir'
+
+        with pytest.raises(errors.DistutilsFileError):
+            mkpath(FailPath(target))
+
+        assert not target.exists()
+
+        mkpath(target)
+        assert target.exists()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dist.py
new file mode 100644
index 0000000..2c5beeb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_dist.py
@@ -0,0 +1,552 @@
+"""Tests for distutils.dist."""
+
+import email
+import email.generator
+import email.policy
+import functools
+import io
+import os
+import sys
+import textwrap
+import unittest.mock as mock
+import warnings
+from distutils.cmd import Command
+from distutils.dist import Distribution, fix_help_options
+from distutils.tests import support
+from typing import ClassVar
+
+import jaraco.path
+import pytest
+
+pydistutils_cfg = '.' * (os.name == 'posix') + 'pydistutils.cfg'
+
+
+class test_dist(Command):
+    """Sample distutils extension command."""
+
+    user_options: ClassVar[list[tuple[str, str, str]]] = [
+        ("sample-option=", "S", "help text"),
+    ]
+
+    def initialize_options(self):
+        self.sample_option = None
+
+
+class TestDistribution(Distribution):
+    """Distribution subclasses that avoids the default search for
+    configuration files.
+
+    The ._config_files attribute must be set before
+    .parse_config_files() is called.
+    """
+
+    def find_config_files(self):
+        return self._config_files
+
+
+@pytest.fixture
+def clear_argv():
+    del sys.argv[1:]
+
+
+@support.combine_markers
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+class TestDistributionBehavior(support.TempdirManager):
+    def create_distribution(self, configfiles=()):
+        d = TestDistribution()
+        d._config_files = configfiles
+        d.parse_config_files()
+        d.parse_command_line()
+        return d
+
+    def test_command_packages_unspecified(self, clear_argv):
+        sys.argv.append("build")
+        d = self.create_distribution()
+        assert d.get_command_packages() == ["distutils.command"]
+
+    def test_command_packages_cmdline(self, clear_argv):
+        from distutils.tests.test_dist import test_dist
+
+        sys.argv.extend([
+            "--command-packages",
+            "foo.bar,distutils.tests",
+            "test_dist",
+            "-Ssometext",
+        ])
+        d = self.create_distribution()
+        # let's actually try to load our test command:
+        assert d.get_command_packages() == [
+            "distutils.command",
+            "foo.bar",
+            "distutils.tests",
+        ]
+        cmd = d.get_command_obj("test_dist")
+        assert isinstance(cmd, test_dist)
+        assert cmd.sample_option == "sometext"
+
+    @pytest.mark.skipif(
+        'distutils' not in Distribution.parse_config_files.__module__,
+        reason='Cannot test when virtualenv has monkey-patched Distribution',
+    )
+    def test_venv_install_options(self, tmp_path, clear_argv):
+        sys.argv.append("install")
+        file = str(tmp_path / 'file')
+
+        fakepath = '/somedir'
+
+        jaraco.path.build({
+            file: f"""
+                    [install]
+                    install-base = {fakepath}
+                    install-platbase = {fakepath}
+                    install-lib = {fakepath}
+                    install-platlib = {fakepath}
+                    install-purelib = {fakepath}
+                    install-headers = {fakepath}
+                    install-scripts = {fakepath}
+                    install-data = {fakepath}
+                    prefix = {fakepath}
+                    exec-prefix = {fakepath}
+                    home = {fakepath}
+                    user = {fakepath}
+                    root = {fakepath}
+                    """,
+        })
+
+        # Base case: Not in a Virtual Environment
+        with mock.patch.multiple(sys, prefix='/a', base_prefix='/a'):
+            d = self.create_distribution([file])
+
+        option_tuple = (file, fakepath)
+
+        result_dict = {
+            'install_base': option_tuple,
+            'install_platbase': option_tuple,
+            'install_lib': option_tuple,
+            'install_platlib': option_tuple,
+            'install_purelib': option_tuple,
+            'install_headers': option_tuple,
+            'install_scripts': option_tuple,
+            'install_data': option_tuple,
+            'prefix': option_tuple,
+            'exec_prefix': option_tuple,
+            'home': option_tuple,
+            'user': option_tuple,
+            'root': option_tuple,
+        }
+
+        assert sorted(d.command_options.get('install').keys()) == sorted(
+            result_dict.keys()
+        )
+
+        for key, value in d.command_options.get('install').items():
+            assert value == result_dict[key]
+
+        # Test case: In a Virtual Environment
+        with mock.patch.multiple(sys, prefix='/a', base_prefix='/b'):
+            d = self.create_distribution([file])
+
+        for key in result_dict.keys():
+            assert key not in d.command_options.get('install', {})
+
+    def test_command_packages_configfile(self, tmp_path, clear_argv):
+        sys.argv.append("build")
+        file = str(tmp_path / "file")
+        jaraco.path.build({
+            file: """
+                    [global]
+                    command_packages = foo.bar, splat
+                    """,
+        })
+
+        d = self.create_distribution([file])
+        assert d.get_command_packages() == ["distutils.command", "foo.bar", "splat"]
+
+        # ensure command line overrides config:
+        sys.argv[1:] = ["--command-packages", "spork", "build"]
+        d = self.create_distribution([file])
+        assert d.get_command_packages() == ["distutils.command", "spork"]
+
+        # Setting --command-packages to '' should cause the default to
+        # be used even if a config file specified something else:
+        sys.argv[1:] = ["--command-packages", "", "build"]
+        d = self.create_distribution([file])
+        assert d.get_command_packages() == ["distutils.command"]
+
+    def test_empty_options(self, request):
+        # an empty options dictionary should not stay in the
+        # list of attributes
+
+        # catching warnings
+        warns = []
+
+        def _warn(msg):
+            warns.append(msg)
+
+        request.addfinalizer(
+            functools.partial(setattr, warnings, 'warn', warnings.warn)
+        )
+        warnings.warn = _warn
+        dist = Distribution(
+            attrs={
+                'author': 'xxx',
+                'name': 'xxx',
+                'version': 'xxx',
+                'url': 'xxxx',
+                'options': {},
+            }
+        )
+
+        assert len(warns) == 0
+        assert 'options' not in dir(dist)
+
+    def test_finalize_options(self):
+        attrs = {'keywords': 'one,two', 'platforms': 'one,two'}
+
+        dist = Distribution(attrs=attrs)
+        dist.finalize_options()
+
+        # finalize_option splits platforms and keywords
+        assert dist.metadata.platforms == ['one', 'two']
+        assert dist.metadata.keywords == ['one', 'two']
+
+        attrs = {'keywords': 'foo bar', 'platforms': 'foo bar'}
+        dist = Distribution(attrs=attrs)
+        dist.finalize_options()
+        assert dist.metadata.platforms == ['foo bar']
+        assert dist.metadata.keywords == ['foo bar']
+
+    def test_get_command_packages(self):
+        dist = Distribution()
+        assert dist.command_packages is None
+        cmds = dist.get_command_packages()
+        assert cmds == ['distutils.command']
+        assert dist.command_packages == ['distutils.command']
+
+        dist.command_packages = 'one,two'
+        cmds = dist.get_command_packages()
+        assert cmds == ['distutils.command', 'one', 'two']
+
+    def test_announce(self):
+        # make sure the level is known
+        dist = Distribution()
+        with pytest.raises(TypeError):
+            dist.announce('ok', level='ok2')
+
+    def test_find_config_files_disable(self, temp_home):
+        # Ticket #1180: Allow user to disable their home config file.
+        jaraco.path.build({pydistutils_cfg: '[distutils]\n'}, temp_home)
+
+        d = Distribution()
+        all_files = d.find_config_files()
+
+        d = Distribution(attrs={'script_args': ['--no-user-cfg']})
+        files = d.find_config_files()
+
+        # make sure --no-user-cfg disables the user cfg file
+        assert len(all_files) - 1 == len(files)
+
+    def test_script_args_list_coercion(self):
+        d = Distribution(attrs={'script_args': ('build', '--no-user-cfg')})
+
+        # make sure script_args is a list even if it started as a different iterable
+        assert d.script_args == ['build', '--no-user-cfg']
+
+    @pytest.mark.skipif(
+        'platform.system() == "Windows"',
+        reason='Windows does not honor chmod 000',
+    )
+    def test_find_config_files_permission_error(self, fake_home):
+        """
+        Finding config files should not fail when directory is inaccessible.
+        """
+        fake_home.joinpath(pydistutils_cfg).write_text('', encoding='utf-8')
+        fake_home.chmod(0o000)
+        Distribution().find_config_files()
+
+
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+class TestMetadata(support.TempdirManager):
+    def format_metadata(self, dist):
+        sio = io.StringIO()
+        dist.metadata.write_pkg_file(sio)
+        return sio.getvalue()
+
+    def test_simple_metadata(self):
+        attrs = {"name": "package", "version": "1.0"}
+        dist = Distribution(attrs)
+        meta = self.format_metadata(dist)
+        assert "Metadata-Version: 1.0" in meta
+        assert "provides:" not in meta.lower()
+        assert "requires:" not in meta.lower()
+        assert "obsoletes:" not in meta.lower()
+
+    def test_provides(self):
+        attrs = {
+            "name": "package",
+            "version": "1.0",
+            "provides": ["package", "package.sub"],
+        }
+        dist = Distribution(attrs)
+        assert dist.metadata.get_provides() == ["package", "package.sub"]
+        assert dist.get_provides() == ["package", "package.sub"]
+        meta = self.format_metadata(dist)
+        assert "Metadata-Version: 1.1" in meta
+        assert "requires:" not in meta.lower()
+        assert "obsoletes:" not in meta.lower()
+
+    def test_provides_illegal(self):
+        with pytest.raises(ValueError):
+            Distribution(
+                {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]},
+            )
+
+    def test_requires(self):
+        attrs = {
+            "name": "package",
+            "version": "1.0",
+            "requires": ["other", "another (==1.0)"],
+        }
+        dist = Distribution(attrs)
+        assert dist.metadata.get_requires() == ["other", "another (==1.0)"]
+        assert dist.get_requires() == ["other", "another (==1.0)"]
+        meta = self.format_metadata(dist)
+        assert "Metadata-Version: 1.1" in meta
+        assert "provides:" not in meta.lower()
+        assert "Requires: other" in meta
+        assert "Requires: another (==1.0)" in meta
+        assert "obsoletes:" not in meta.lower()
+
+    def test_requires_illegal(self):
+        with pytest.raises(ValueError):
+            Distribution(
+                {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]},
+            )
+
+    def test_requires_to_list(self):
+        attrs = {"name": "package", "requires": iter(["other"])}
+        dist = Distribution(attrs)
+        assert isinstance(dist.metadata.requires, list)
+
+    def test_obsoletes(self):
+        attrs = {
+            "name": "package",
+            "version": "1.0",
+            "obsoletes": ["other", "another (<1.0)"],
+        }
+        dist = Distribution(attrs)
+        assert dist.metadata.get_obsoletes() == ["other", "another (<1.0)"]
+        assert dist.get_obsoletes() == ["other", "another (<1.0)"]
+        meta = self.format_metadata(dist)
+        assert "Metadata-Version: 1.1" in meta
+        assert "provides:" not in meta.lower()
+        assert "requires:" not in meta.lower()
+        assert "Obsoletes: other" in meta
+        assert "Obsoletes: another (<1.0)" in meta
+
+    def test_obsoletes_illegal(self):
+        with pytest.raises(ValueError):
+            Distribution(
+                {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]},
+            )
+
+    def test_obsoletes_to_list(self):
+        attrs = {"name": "package", "obsoletes": iter(["other"])}
+        dist = Distribution(attrs)
+        assert isinstance(dist.metadata.obsoletes, list)
+
+    def test_classifier(self):
+        attrs = {
+            'name': 'Boa',
+            'version': '3.0',
+            'classifiers': ['Programming Language :: Python :: 3'],
+        }
+        dist = Distribution(attrs)
+        assert dist.get_classifiers() == ['Programming Language :: Python :: 3']
+        meta = self.format_metadata(dist)
+        assert 'Metadata-Version: 1.1' in meta
+
+    def test_classifier_invalid_type(self, caplog):
+        attrs = {
+            'name': 'Boa',
+            'version': '3.0',
+            'classifiers': ('Programming Language :: Python :: 3',),
+        }
+        d = Distribution(attrs)
+        # should have warning about passing a non-list
+        assert 'should be a list' in caplog.messages[0]
+        # should be converted to a list
+        assert isinstance(d.metadata.classifiers, list)
+        assert d.metadata.classifiers == list(attrs['classifiers'])
+
+    def test_keywords(self):
+        attrs = {
+            'name': 'Monty',
+            'version': '1.0',
+            'keywords': ['spam', 'eggs', 'life of brian'],
+        }
+        dist = Distribution(attrs)
+        assert dist.get_keywords() == ['spam', 'eggs', 'life of brian']
+
+    def test_keywords_invalid_type(self, caplog):
+        attrs = {
+            'name': 'Monty',
+            'version': '1.0',
+            'keywords': ('spam', 'eggs', 'life of brian'),
+        }
+        d = Distribution(attrs)
+        # should have warning about passing a non-list
+        assert 'should be a list' in caplog.messages[0]
+        # should be converted to a list
+        assert isinstance(d.metadata.keywords, list)
+        assert d.metadata.keywords == list(attrs['keywords'])
+
+    def test_platforms(self):
+        attrs = {
+            'name': 'Monty',
+            'version': '1.0',
+            'platforms': ['GNU/Linux', 'Some Evil Platform'],
+        }
+        dist = Distribution(attrs)
+        assert dist.get_platforms() == ['GNU/Linux', 'Some Evil Platform']
+
+    def test_platforms_invalid_types(self, caplog):
+        attrs = {
+            'name': 'Monty',
+            'version': '1.0',
+            'platforms': ('GNU/Linux', 'Some Evil Platform'),
+        }
+        d = Distribution(attrs)
+        # should have warning about passing a non-list
+        assert 'should be a list' in caplog.messages[0]
+        # should be converted to a list
+        assert isinstance(d.metadata.platforms, list)
+        assert d.metadata.platforms == list(attrs['platforms'])
+
+    def test_download_url(self):
+        attrs = {
+            'name': 'Boa',
+            'version': '3.0',
+            'download_url': 'http://example.org/boa',
+        }
+        dist = Distribution(attrs)
+        meta = self.format_metadata(dist)
+        assert 'Metadata-Version: 1.1' in meta
+
+    def test_long_description(self):
+        long_desc = textwrap.dedent(
+            """\
+        example::
+              We start here
+            and continue here
+          and end here."""
+        )
+        attrs = {"name": "package", "version": "1.0", "long_description": long_desc}
+
+        dist = Distribution(attrs)
+        meta = self.format_metadata(dist)
+        meta = meta.replace('\n' + 8 * ' ', '\n')
+        assert long_desc in meta
+
+    def test_custom_pydistutils(self, temp_home):
+        """
+        pydistutils.cfg is found
+        """
+        jaraco.path.build({pydistutils_cfg: ''}, temp_home)
+        config_path = temp_home / pydistutils_cfg
+
+        assert str(config_path) in Distribution().find_config_files()
+
+    def test_extra_pydistutils(self, monkeypatch, tmp_path):
+        jaraco.path.build({'overrides.cfg': ''}, tmp_path)
+        filename = tmp_path / 'overrides.cfg'
+        monkeypatch.setenv('DIST_EXTRA_CONFIG', str(filename))
+        assert str(filename) in Distribution().find_config_files()
+
+    def test_fix_help_options(self):
+        help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
+        fancy_options = fix_help_options(help_tuples)
+        assert fancy_options[0] == ('a', 'b', 'c')
+        assert fancy_options[1] == (1, 2, 3)
+
+    def test_show_help(self, request, capsys):
+        # smoke test, just makes sure some help is displayed
+        dist = Distribution()
+        sys.argv = []
+        dist.help = True
+        dist.script_name = 'setup.py'
+        dist.parse_command_line()
+
+        output = [
+            line for line in capsys.readouterr().out.split('\n') if line.strip() != ''
+        ]
+        assert output
+
+    def test_read_metadata(self):
+        attrs = {
+            "name": "package",
+            "version": "1.0",
+            "long_description": "desc",
+            "description": "xxx",
+            "download_url": "http://example.com",
+            "keywords": ['one', 'two'],
+            "requires": ['foo'],
+        }
+
+        dist = Distribution(attrs)
+        metadata = dist.metadata
+
+        # write it then reloads it
+        PKG_INFO = io.StringIO()
+        metadata.write_pkg_file(PKG_INFO)
+        PKG_INFO.seek(0)
+        metadata.read_pkg_file(PKG_INFO)
+
+        assert metadata.name == "package"
+        assert metadata.version == "1.0"
+        assert metadata.description == "xxx"
+        assert metadata.download_url == 'http://example.com'
+        assert metadata.keywords == ['one', 'two']
+        assert metadata.platforms is None
+        assert metadata.obsoletes is None
+        assert metadata.requires == ['foo']
+
+    def test_round_trip_through_email_generator(self):
+        """
+        In pypa/setuptools#4033, it was shown that once PKG-INFO is
+        re-generated using ``email.generator.Generator``, some control
+        characters might cause problems.
+        """
+        # Given a PKG-INFO file ...
+        attrs = {
+            "name": "package",
+            "version": "1.0",
+            "long_description": "hello\x0b\nworld\n",
+        }
+        dist = Distribution(attrs)
+        metadata = dist.metadata
+
+        with io.StringIO() as buffer:
+            metadata.write_pkg_file(buffer)
+            msg = buffer.getvalue()
+
+        # ... when it is read and re-written using stdlib's email library,
+        orig = email.message_from_string(msg)
+        policy = email.policy.EmailPolicy(
+            utf8=True,
+            mangle_from_=False,
+            max_line_length=0,
+        )
+        with io.StringIO() as buffer:
+            email.generator.Generator(buffer, policy=policy).flatten(orig)
+
+            buffer.seek(0)
+            regen = email.message_from_file(buffer)
+
+        # ... then it should be the same as the original
+        # (except for the specific line break characters)
+        orig_desc = set(orig["Description"].splitlines())
+        regen_desc = set(regen["Description"].splitlines())
+        assert regen_desc == orig_desc
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_extension.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_extension.py
new file mode 100644
index 0000000..5e8e768
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_extension.py
@@ -0,0 +1,117 @@
+"""Tests for distutils.extension."""
+
+import os
+import pathlib
+import warnings
+from distutils.extension import Extension, read_setup_file
+
+import pytest
+from test.support.warnings_helper import check_warnings
+
+
+class TestExtension:
+    def test_read_setup_file(self):
+        # trying to read a Setup file
+        # (sample extracted from the PyGame project)
+        setup = os.path.join(os.path.dirname(__file__), 'Setup.sample')
+
+        exts = read_setup_file(setup)
+        names = [ext.name for ext in exts]
+        names.sort()
+
+        # here are the extensions read_setup_file should have created
+        # out of the file
+        wanted = [
+            '_arraysurfarray',
+            '_camera',
+            '_numericsndarray',
+            '_numericsurfarray',
+            'base',
+            'bufferproxy',
+            'cdrom',
+            'color',
+            'constants',
+            'display',
+            'draw',
+            'event',
+            'fastevent',
+            'font',
+            'gfxdraw',
+            'image',
+            'imageext',
+            'joystick',
+            'key',
+            'mask',
+            'mixer',
+            'mixer_music',
+            'mouse',
+            'movie',
+            'overlay',
+            'pixelarray',
+            'pypm',
+            'rect',
+            'rwobject',
+            'scrap',
+            'surface',
+            'surflock',
+            'time',
+            'transform',
+        ]
+
+        assert names == wanted
+
+    def test_extension_init(self):
+        # the first argument, which is the name, must be a string
+        with pytest.raises(TypeError):
+            Extension(1, [])
+        ext = Extension('name', [])
+        assert ext.name == 'name'
+
+        # the second argument, which is the list of files, must
+        # be an iterable of strings or PathLike objects, and not a string
+        with pytest.raises(TypeError):
+            Extension('name', 'file')
+        with pytest.raises(TypeError):
+            Extension('name', ['file', 1])
+        ext = Extension('name', ['file1', 'file2'])
+        assert ext.sources == ['file1', 'file2']
+        ext = Extension('name', [pathlib.Path('file1'), pathlib.Path('file2')])
+        assert ext.sources == ['file1', 'file2']
+
+        # any non-string iterable of strings or PathLike objects should work
+        ext = Extension('name', ('file1', 'file2'))  # tuple
+        assert ext.sources == ['file1', 'file2']
+        ext = Extension('name', {'file1', 'file2'})  # set
+        assert sorted(ext.sources) == ['file1', 'file2']
+        ext = Extension('name', iter(['file1', 'file2']))  # iterator
+        assert ext.sources == ['file1', 'file2']
+        ext = Extension('name', [pathlib.Path('file1'), 'file2'])  # mixed types
+        assert ext.sources == ['file1', 'file2']
+
+        # others arguments have defaults
+        for attr in (
+            'include_dirs',
+            'define_macros',
+            'undef_macros',
+            'library_dirs',
+            'libraries',
+            'runtime_library_dirs',
+            'extra_objects',
+            'extra_compile_args',
+            'extra_link_args',
+            'export_symbols',
+            'swig_opts',
+            'depends',
+        ):
+            assert getattr(ext, attr) == []
+
+        assert ext.language is None
+        assert ext.optional is None
+
+        # if there are unknown keyword options, warn about them
+        with check_warnings() as w:
+            warnings.simplefilter('always')
+            ext = Extension('name', ['file1', 'file2'], chic=True)
+
+        assert len(w.warnings) == 1
+        assert str(w.warnings[0].message) == "Unknown Extension options: 'chic'"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_file_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_file_util.py
new file mode 100644
index 0000000..a75d4a0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_file_util.py
@@ -0,0 +1,95 @@
+"""Tests for distutils.file_util."""
+
+import errno
+import os
+import unittest.mock as mock
+from distutils.errors import DistutilsFileError
+from distutils.file_util import copy_file, move_file
+
+import jaraco.path
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def stuff(request, tmp_path):
+    self = request.instance
+    self.source = tmp_path / 'f1'
+    self.target = tmp_path / 'f2'
+    self.target_dir = tmp_path / 'd1'
+
+
+class TestFileUtil:
+    def test_move_file_verbosity(self, caplog):
+        jaraco.path.build({self.source: 'some content'})
+
+        move_file(self.source, self.target, verbose=False)
+        assert not caplog.messages
+
+        # back to original state
+        move_file(self.target, self.source, verbose=False)
+
+        move_file(self.source, self.target, verbose=True)
+        wanted = [f'moving {self.source} -> {self.target}']
+        assert caplog.messages == wanted
+
+        # back to original state
+        move_file(self.target, self.source, verbose=False)
+
+        caplog.clear()
+        # now the target is a dir
+        os.mkdir(self.target_dir)
+        move_file(self.source, self.target_dir, verbose=True)
+        wanted = [f'moving {self.source} -> {self.target_dir}']
+        assert caplog.messages == wanted
+
+    def test_move_file_exception_unpacking_rename(self):
+        # see issue 22182
+        with (
+            mock.patch("os.rename", side_effect=OSError("wrong", 1)),
+            pytest.raises(DistutilsFileError),
+        ):
+            jaraco.path.build({self.source: 'spam eggs'})
+            move_file(self.source, self.target, verbose=False)
+
+    def test_move_file_exception_unpacking_unlink(self):
+        # see issue 22182
+        with (
+            mock.patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")),
+            mock.patch("os.unlink", side_effect=OSError("wrong", 1)),
+            pytest.raises(DistutilsFileError),
+        ):
+            jaraco.path.build({self.source: 'spam eggs'})
+            move_file(self.source, self.target, verbose=False)
+
+    def test_copy_file_hard_link(self):
+        jaraco.path.build({self.source: 'some content'})
+        # Check first that copy_file() will not fall back on copying the file
+        # instead of creating the hard link.
+        try:
+            os.link(self.source, self.target)
+        except OSError as e:
+            self.skipTest(f'os.link: {e}')
+        else:
+            self.target.unlink()
+        st = os.stat(self.source)
+        copy_file(self.source, self.target, link='hard')
+        st2 = os.stat(self.source)
+        st3 = os.stat(self.target)
+        assert os.path.samestat(st, st2), (st, st2)
+        assert os.path.samestat(st2, st3), (st2, st3)
+        assert self.source.read_text(encoding='utf-8') == 'some content'
+
+    def test_copy_file_hard_link_failure(self):
+        # If hard linking fails, copy_file() falls back on copying file
+        # (some special filesystems don't support hard linking even under
+        #  Unix, see issue #8876).
+        jaraco.path.build({self.source: 'some content'})
+        st = os.stat(self.source)
+        with mock.patch("os.link", side_effect=OSError(0, "linking unsupported")):
+            copy_file(self.source, self.target, link='hard')
+        st2 = os.stat(self.source)
+        st3 = os.stat(self.target)
+        assert os.path.samestat(st, st2), (st, st2)
+        assert not os.path.samestat(st2, st3), (st2, st3)
+        for fn in (self.source, self.target):
+            assert fn.read_text(encoding='utf-8') == 'some content'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_filelist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_filelist.py
new file mode 100644
index 0000000..130e6fb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_filelist.py
@@ -0,0 +1,336 @@
+"""Tests for distutils.filelist."""
+
+import logging
+import os
+import re
+from distutils import debug, filelist
+from distutils.errors import DistutilsTemplateError
+from distutils.filelist import FileList, glob_to_re, translate_pattern
+
+import jaraco.path
+import pytest
+
+from .compat import py39 as os_helper
+
+MANIFEST_IN = """\
+include ok
+include xo
+exclude xo
+include foo.tmp
+include buildout.cfg
+global-include *.x
+global-include *.txt
+global-exclude *.tmp
+recursive-include f *.oo
+recursive-exclude global *.x
+graft dir
+prune dir3
+"""
+
+
+def make_local_path(s):
+    """Converts '/' in a string to os.sep"""
+    return s.replace('/', os.sep)
+
+
+class TestFileList:
+    def assertNoWarnings(self, caplog):
+        warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING]
+        assert not warnings
+        caplog.clear()
+
+    def assertWarnings(self, caplog):
+        warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING]
+        assert warnings
+        caplog.clear()
+
+    def test_glob_to_re(self):
+        sep = os.sep
+        if os.sep == '\\':
+            sep = re.escape(os.sep)
+
+        for glob, regex in (
+            # simple cases
+            ('foo*', r'(?s:foo[^%(sep)s]*)\Z'),
+            ('foo?', r'(?s:foo[^%(sep)s])\Z'),
+            ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'),
+            # special cases
+            (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'),
+            (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'),
+            ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'),
+            (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z'),
+        ):
+            regex = regex % {'sep': sep}
+            assert glob_to_re(glob) == regex
+
+    def test_process_template_line(self):
+        # testing  all MANIFEST.in template patterns
+        file_list = FileList()
+        mlp = make_local_path
+
+        # simulated file list
+        file_list.allfiles = [
+            'foo.tmp',
+            'ok',
+            'xo',
+            'four.txt',
+            'buildout.cfg',
+            # filelist does not filter out VCS directories,
+            # it's sdist that does
+            mlp('.hg/last-message.txt'),
+            mlp('global/one.txt'),
+            mlp('global/two.txt'),
+            mlp('global/files.x'),
+            mlp('global/here.tmp'),
+            mlp('f/o/f.oo'),
+            mlp('dir/graft-one'),
+            mlp('dir/dir2/graft2'),
+            mlp('dir3/ok'),
+            mlp('dir3/sub/ok.txt'),
+        ]
+
+        for line in MANIFEST_IN.split('\n'):
+            if line.strip() == '':
+                continue
+            file_list.process_template_line(line)
+
+        wanted = [
+            'ok',
+            'buildout.cfg',
+            'four.txt',
+            mlp('.hg/last-message.txt'),
+            mlp('global/one.txt'),
+            mlp('global/two.txt'),
+            mlp('f/o/f.oo'),
+            mlp('dir/graft-one'),
+            mlp('dir/dir2/graft2'),
+        ]
+
+        assert file_list.files == wanted
+
+    def test_debug_print(self, capsys, monkeypatch):
+        file_list = FileList()
+        file_list.debug_print('xxx')
+        assert capsys.readouterr().out == ''
+
+        monkeypatch.setattr(debug, 'DEBUG', True)
+        file_list.debug_print('xxx')
+        assert capsys.readouterr().out == 'xxx\n'
+
+    def test_set_allfiles(self):
+        file_list = FileList()
+        files = ['a', 'b', 'c']
+        file_list.set_allfiles(files)
+        assert file_list.allfiles == files
+
+    def test_remove_duplicates(self):
+        file_list = FileList()
+        file_list.files = ['a', 'b', 'a', 'g', 'c', 'g']
+        # files must be sorted beforehand (sdist does it)
+        file_list.sort()
+        file_list.remove_duplicates()
+        assert file_list.files == ['a', 'b', 'c', 'g']
+
+    def test_translate_pattern(self):
+        # not regex
+        assert hasattr(translate_pattern('a', anchor=True, is_regex=False), 'search')
+
+        # is a regex
+        regex = re.compile('a')
+        assert translate_pattern(regex, anchor=True, is_regex=True) == regex
+
+        # plain string flagged as regex
+        assert hasattr(translate_pattern('a', anchor=True, is_regex=True), 'search')
+
+        # glob support
+        assert translate_pattern('*.py', anchor=True, is_regex=False).search(
+            'filelist.py'
+        )
+
+    def test_exclude_pattern(self):
+        # return False if no match
+        file_list = FileList()
+        assert not file_list.exclude_pattern('*.py')
+
+        # return True if files match
+        file_list = FileList()
+        file_list.files = ['a.py', 'b.py']
+        assert file_list.exclude_pattern('*.py')
+
+        # test excludes
+        file_list = FileList()
+        file_list.files = ['a.py', 'a.txt']
+        file_list.exclude_pattern('*.py')
+        assert file_list.files == ['a.txt']
+
+    def test_include_pattern(self):
+        # return False if no match
+        file_list = FileList()
+        file_list.set_allfiles([])
+        assert not file_list.include_pattern('*.py')
+
+        # return True if files match
+        file_list = FileList()
+        file_list.set_allfiles(['a.py', 'b.txt'])
+        assert file_list.include_pattern('*.py')
+
+        # test * matches all files
+        file_list = FileList()
+        assert file_list.allfiles is None
+        file_list.set_allfiles(['a.py', 'b.txt'])
+        file_list.include_pattern('*')
+        assert file_list.allfiles == ['a.py', 'b.txt']
+
+    def test_process_template(self, caplog):
+        mlp = make_local_path
+        # invalid lines
+        file_list = FileList()
+        for action in (
+            'include',
+            'exclude',
+            'global-include',
+            'global-exclude',
+            'recursive-include',
+            'recursive-exclude',
+            'graft',
+            'prune',
+            'blarg',
+        ):
+            with pytest.raises(DistutilsTemplateError):
+                file_list.process_template_line(action)
+
+        # include
+        file_list = FileList()
+        file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
+
+        file_list.process_template_line('include *.py')
+        assert file_list.files == ['a.py']
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('include *.rb')
+        assert file_list.files == ['a.py']
+        self.assertWarnings(caplog)
+
+        # exclude
+        file_list = FileList()
+        file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
+
+        file_list.process_template_line('exclude *.py')
+        assert file_list.files == ['b.txt', mlp('d/c.py')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('exclude *.rb')
+        assert file_list.files == ['b.txt', mlp('d/c.py')]
+        self.assertWarnings(caplog)
+
+        # global-include
+        file_list = FileList()
+        file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
+
+        file_list.process_template_line('global-include *.py')
+        assert file_list.files == ['a.py', mlp('d/c.py')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('global-include *.rb')
+        assert file_list.files == ['a.py', mlp('d/c.py')]
+        self.assertWarnings(caplog)
+
+        # global-exclude
+        file_list = FileList()
+        file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
+
+        file_list.process_template_line('global-exclude *.py')
+        assert file_list.files == ['b.txt']
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('global-exclude *.rb')
+        assert file_list.files == ['b.txt']
+        self.assertWarnings(caplog)
+
+        # recursive-include
+        file_list = FileList()
+        file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')])
+
+        file_list.process_template_line('recursive-include d *.py')
+        assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('recursive-include e *.py')
+        assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
+        self.assertWarnings(caplog)
+
+        # recursive-exclude
+        file_list = FileList()
+        file_list.files = ['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')]
+
+        file_list.process_template_line('recursive-exclude d *.py')
+        assert file_list.files == ['a.py', mlp('d/c.txt')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('recursive-exclude e *.py')
+        assert file_list.files == ['a.py', mlp('d/c.txt')]
+        self.assertWarnings(caplog)
+
+        # graft
+        file_list = FileList()
+        file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')])
+
+        file_list.process_template_line('graft d')
+        assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('graft e')
+        assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
+        self.assertWarnings(caplog)
+
+        # prune
+        file_list = FileList()
+        file_list.files = ['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')]
+
+        file_list.process_template_line('prune d')
+        assert file_list.files == ['a.py', mlp('f/f.py')]
+        self.assertNoWarnings(caplog)
+
+        file_list.process_template_line('prune e')
+        assert file_list.files == ['a.py', mlp('f/f.py')]
+        self.assertWarnings(caplog)
+
+
+class TestFindAll:
+    @os_helper.skip_unless_symlink
+    def test_missing_symlink(self, temp_cwd):
+        os.symlink('foo', 'bar')
+        assert filelist.findall() == []
+
+    def test_basic_discovery(self, temp_cwd):
+        """
+        When findall is called with no parameters or with
+        '.' as the parameter, the dot should be omitted from
+        the results.
+        """
+        jaraco.path.build({'foo': {'file1.txt': ''}, 'bar': {'file2.txt': ''}})
+        file1 = os.path.join('foo', 'file1.txt')
+        file2 = os.path.join('bar', 'file2.txt')
+        expected = [file2, file1]
+        assert sorted(filelist.findall()) == expected
+
+    def test_non_local_discovery(self, tmp_path):
+        """
+        When findall is called with another path, the full
+        path name should be returned.
+        """
+        jaraco.path.build({'file1.txt': ''}, tmp_path)
+        expected = [str(tmp_path / 'file1.txt')]
+        assert filelist.findall(tmp_path) == expected
+
+    @os_helper.skip_unless_symlink
+    def test_symlink_loop(self, tmp_path):
+        jaraco.path.build(
+            {
+                'link-to-parent': jaraco.path.Symlink('.'),
+                'somefile': '',
+            },
+            tmp_path,
+        )
+        files = filelist.findall(tmp_path)
+        assert len(files) == 1
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install.py
new file mode 100644
index 0000000..b3ffb2e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install.py
@@ -0,0 +1,245 @@
+"""Tests for distutils.command.install."""
+
+import logging
+import os
+import pathlib
+import site
+import sys
+from distutils import sysconfig
+from distutils.command import install as install_module
+from distutils.command.build_ext import build_ext
+from distutils.command.install import INSTALL_SCHEMES, install
+from distutils.core import Distribution
+from distutils.errors import DistutilsOptionError
+from distutils.extension import Extension
+from distutils.tests import missing_compiler_executable, support
+from distutils.util import is_mingw
+
+import pytest
+
+
+def _make_ext_name(modname):
+    return modname + sysconfig.get_config_var('EXT_SUFFIX')
+
+
+@support.combine_markers
+@pytest.mark.usefixtures('save_env')
+class TestInstall(
+    support.TempdirManager,
+):
+    @pytest.mark.xfail(
+        'platform.system() == "Windows" and sys.version_info > (3, 11)',
+        reason="pypa/distutils#148",
+    )
+    def test_home_installation_scheme(self):
+        # This ensure two things:
+        # - that --home generates the desired set of directory names
+        # - test --home is supported on all platforms
+        builddir = self.mkdtemp()
+        destination = os.path.join(builddir, "installation")
+
+        dist = Distribution({"name": "foopkg"})
+        # script_name need not exist, it just need to be initialized
+        dist.script_name = os.path.join(builddir, "setup.py")
+        dist.command_obj["build"] = support.DummyCommand(
+            build_base=builddir,
+            build_lib=os.path.join(builddir, "lib"),
+        )
+
+        cmd = install(dist)
+        cmd.home = destination
+        cmd.ensure_finalized()
+
+        assert cmd.install_base == destination
+        assert cmd.install_platbase == destination
+
+        def check_path(got, expected):
+            got = os.path.normpath(got)
+            expected = os.path.normpath(expected)
+            assert got == expected
+
+        impl_name = sys.implementation.name.replace("cpython", "python")
+        libdir = os.path.join(destination, "lib", impl_name)
+        check_path(cmd.install_lib, libdir)
+        _platlibdir = getattr(sys, "platlibdir", "lib")
+        platlibdir = os.path.join(destination, _platlibdir, impl_name)
+        check_path(cmd.install_platlib, platlibdir)
+        check_path(cmd.install_purelib, libdir)
+        check_path(
+            cmd.install_headers,
+            os.path.join(destination, "include", impl_name, "foopkg"),
+        )
+        check_path(cmd.install_scripts, os.path.join(destination, "bin"))
+        check_path(cmd.install_data, destination)
+
+    def test_user_site(self, monkeypatch):
+        # test install with --user
+        # preparing the environment for the test
+        self.tmpdir = self.mkdtemp()
+        orig_site = site.USER_SITE
+        orig_base = site.USER_BASE
+        monkeypatch.setattr(site, 'USER_BASE', os.path.join(self.tmpdir, 'B'))
+        monkeypatch.setattr(site, 'USER_SITE', os.path.join(self.tmpdir, 'S'))
+        monkeypatch.setattr(install_module, 'USER_BASE', site.USER_BASE)
+        monkeypatch.setattr(install_module, 'USER_SITE', site.USER_SITE)
+
+        def _expanduser(path):
+            if path.startswith('~'):
+                return os.path.normpath(self.tmpdir + path[1:])
+            return path
+
+        monkeypatch.setattr(os.path, 'expanduser', _expanduser)
+
+        for key in ('nt_user', 'posix_user'):
+            assert key in INSTALL_SCHEMES
+
+        dist = Distribution({'name': 'xx'})
+        cmd = install(dist)
+
+        # making sure the user option is there
+        options = [name for name, short, label in cmd.user_options]
+        assert 'user' in options
+
+        # setting a value
+        cmd.user = True
+
+        # user base and site shouldn't be created yet
+        assert not os.path.exists(site.USER_BASE)
+        assert not os.path.exists(site.USER_SITE)
+
+        # let's run finalize
+        cmd.ensure_finalized()
+
+        # now they should
+        assert os.path.exists(site.USER_BASE)
+        assert os.path.exists(site.USER_SITE)
+
+        assert 'userbase' in cmd.config_vars
+        assert 'usersite' in cmd.config_vars
+
+        actual_headers = os.path.relpath(cmd.install_headers, site.USER_BASE)
+        if os.name == 'nt' and not is_mingw():
+            site_path = os.path.relpath(os.path.dirname(orig_site), orig_base)
+            include = os.path.join(site_path, 'Include')
+        else:
+            include = sysconfig.get_python_inc(0, '')
+        expect_headers = os.path.join(include, 'xx')
+
+        assert os.path.normcase(actual_headers) == os.path.normcase(expect_headers)
+
+    def test_handle_extra_path(self):
+        dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
+        cmd = install(dist)
+
+        # two elements
+        cmd.handle_extra_path()
+        assert cmd.extra_path == ['path', 'dirs']
+        assert cmd.extra_dirs == 'dirs'
+        assert cmd.path_file == 'path'
+
+        # one element
+        cmd.extra_path = ['path']
+        cmd.handle_extra_path()
+        assert cmd.extra_path == ['path']
+        assert cmd.extra_dirs == 'path'
+        assert cmd.path_file == 'path'
+
+        # none
+        dist.extra_path = cmd.extra_path = None
+        cmd.handle_extra_path()
+        assert cmd.extra_path is None
+        assert cmd.extra_dirs == ''
+        assert cmd.path_file is None
+
+        # three elements (no way !)
+        cmd.extra_path = 'path,dirs,again'
+        with pytest.raises(DistutilsOptionError):
+            cmd.handle_extra_path()
+
+    def test_finalize_options(self):
+        dist = Distribution({'name': 'xx'})
+        cmd = install(dist)
+
+        # must supply either prefix/exec-prefix/home or
+        # install-base/install-platbase -- not both
+        cmd.prefix = 'prefix'
+        cmd.install_base = 'base'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+
+        # must supply either home or prefix/exec-prefix -- not both
+        cmd.install_base = None
+        cmd.home = 'home'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+
+        # can't combine user with prefix/exec_prefix/home or
+        # install_(plat)base
+        cmd.prefix = None
+        cmd.user = 'user'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+
+    def test_record(self):
+        install_dir = self.mkdtemp()
+        project_dir, dist = self.create_dist(py_modules=['hello'], scripts=['sayhi'])
+        os.chdir(project_dir)
+        self.write_file('hello.py', "def main(): print('o hai')")
+        self.write_file('sayhi', 'from hello import main; main()')
+
+        cmd = install(dist)
+        dist.command_obj['install'] = cmd
+        cmd.root = install_dir
+        cmd.record = os.path.join(project_dir, 'filelist')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        content = pathlib.Path(cmd.record).read_text(encoding='utf-8')
+
+        found = [pathlib.Path(line).name for line in content.splitlines()]
+        expected = [
+            'hello.py',
+            f'hello.{sys.implementation.cache_tag}.pyc',
+            'sayhi',
+            'UNKNOWN-0.0.0-py{}.{}.egg-info'.format(*sys.version_info[:2]),
+        ]
+        assert found == expected
+
+    def test_record_extensions(self):
+        cmd = missing_compiler_executable()
+        if cmd is not None:
+            pytest.skip(f'The {cmd!r} command is not found')
+        install_dir = self.mkdtemp()
+        project_dir, dist = self.create_dist(
+            ext_modules=[Extension('xx', ['xxmodule.c'])]
+        )
+        os.chdir(project_dir)
+        support.copy_xxmodule_c(project_dir)
+
+        buildextcmd = build_ext(dist)
+        support.fixup_build_ext(buildextcmd)
+        buildextcmd.ensure_finalized()
+
+        cmd = install(dist)
+        dist.command_obj['install'] = cmd
+        dist.command_obj['build_ext'] = buildextcmd
+        cmd.root = install_dir
+        cmd.record = os.path.join(project_dir, 'filelist')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        content = pathlib.Path(cmd.record).read_text(encoding='utf-8')
+
+        found = [pathlib.Path(line).name for line in content.splitlines()]
+        expected = [
+            _make_ext_name('xx'),
+            'UNKNOWN-0.0.0-py{}.{}.egg-info'.format(*sys.version_info[:2]),
+        ]
+        assert found == expected
+
+    def test_debug_mode(self, caplog, monkeypatch):
+        # this covers the code called when DEBUG is set
+        monkeypatch.setattr(install_module, 'DEBUG', True)
+        caplog.set_level(logging.DEBUG)
+        self.test_record()
+        assert any(rec for rec in caplog.records if rec.levelno == logging.DEBUG)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_data.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_data.py
new file mode 100644
index 0000000..c800f86
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_data.py
@@ -0,0 +1,74 @@
+"""Tests for distutils.command.install_data."""
+
+import os
+import pathlib
+from distutils.command.install_data import install_data
+from distutils.tests import support
+
+import pytest
+
+
+@pytest.mark.usefixtures('save_env')
+class TestInstallData(
+    support.TempdirManager,
+):
+    def test_simple_run(self):
+        pkg_dir, dist = self.create_dist()
+        cmd = install_data(dist)
+        cmd.install_dir = inst = os.path.join(pkg_dir, 'inst')
+
+        # data_files can contain
+        #  - simple files
+        #  - a Path object
+        #  - a tuple with a path, and a list of file
+        one = os.path.join(pkg_dir, 'one')
+        self.write_file(one, 'xxx')
+        inst2 = os.path.join(pkg_dir, 'inst2')
+        two = os.path.join(pkg_dir, 'two')
+        self.write_file(two, 'xxx')
+        three = pathlib.Path(pkg_dir) / 'three'
+        self.write_file(three, 'xxx')
+
+        cmd.data_files = [one, (inst2, [two]), three]
+        assert cmd.get_inputs() == [one, (inst2, [two]), three]
+
+        # let's run the command
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        assert len(cmd.get_outputs()) == 3
+        rthree = os.path.split(one)[-1]
+        assert os.path.exists(os.path.join(inst, rthree))
+        rtwo = os.path.split(two)[-1]
+        assert os.path.exists(os.path.join(inst2, rtwo))
+        rone = os.path.split(one)[-1]
+        assert os.path.exists(os.path.join(inst, rone))
+        cmd.outfiles = []
+
+        # let's try with warn_dir one
+        cmd.warn_dir = True
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        assert len(cmd.get_outputs()) == 3
+        assert os.path.exists(os.path.join(inst, rthree))
+        assert os.path.exists(os.path.join(inst2, rtwo))
+        assert os.path.exists(os.path.join(inst, rone))
+        cmd.outfiles = []
+
+        # now using root and empty dir
+        cmd.root = os.path.join(pkg_dir, 'root')
+        inst5 = os.path.join(pkg_dir, 'inst5')
+        four = os.path.join(cmd.install_dir, 'four')
+        self.write_file(four, 'xx')
+        cmd.data_files = [one, (inst2, [two]), three, ('inst5', [four]), (inst5, [])]
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the result
+        assert len(cmd.get_outputs()) == 5
+        assert os.path.exists(os.path.join(inst, rthree))
+        assert os.path.exists(os.path.join(inst2, rtwo))
+        assert os.path.exists(os.path.join(inst, rone))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_headers.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_headers.py
new file mode 100644
index 0000000..2c74f06
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_headers.py
@@ -0,0 +1,33 @@
+"""Tests for distutils.command.install_headers."""
+
+import os
+from distutils.command.install_headers import install_headers
+from distutils.tests import support
+
+import pytest
+
+
+@pytest.mark.usefixtures('save_env')
+class TestInstallHeaders(
+    support.TempdirManager,
+):
+    def test_simple_run(self):
+        # we have two headers
+        header_list = self.mkdtemp()
+        header1 = os.path.join(header_list, 'header1')
+        header2 = os.path.join(header_list, 'header2')
+        self.write_file(header1)
+        self.write_file(header2)
+        headers = [header1, header2]
+
+        pkg_dir, dist = self.create_dist(headers=headers)
+        cmd = install_headers(dist)
+        assert cmd.get_inputs() == headers
+
+        # let's run the command
+        cmd.install_dir = os.path.join(pkg_dir, 'inst')
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # let's check the results
+        assert len(cmd.get_outputs()) == 2
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_lib.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_lib.py
new file mode 100644
index 0000000..f685a57
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_lib.py
@@ -0,0 +1,110 @@
+"""Tests for distutils.command.install_data."""
+
+import importlib.util
+import os
+import sys
+from distutils.command.install_lib import install_lib
+from distutils.errors import DistutilsOptionError
+from distutils.extension import Extension
+from distutils.tests import support
+
+import pytest
+
+
+@support.combine_markers
+@pytest.mark.usefixtures('save_env')
+class TestInstallLib(
+    support.TempdirManager,
+):
+    def test_finalize_options(self):
+        dist = self.create_dist()[1]
+        cmd = install_lib(dist)
+
+        cmd.finalize_options()
+        assert cmd.compile == 1
+        assert cmd.optimize == 0
+
+        # optimize must be 0, 1, or 2
+        cmd.optimize = 'foo'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+        cmd.optimize = '4'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+
+        cmd.optimize = '2'
+        cmd.finalize_options()
+        assert cmd.optimize == 2
+
+    @pytest.mark.skipif('sys.dont_write_bytecode')
+    def test_byte_compile(self):
+        project_dir, dist = self.create_dist()
+        os.chdir(project_dir)
+        cmd = install_lib(dist)
+        cmd.compile = cmd.optimize = 1
+
+        f = os.path.join(project_dir, 'foo.py')
+        self.write_file(f, '# python file')
+        cmd.byte_compile([f])
+        pyc_file = importlib.util.cache_from_source('foo.py', optimization='')
+        pyc_opt_file = importlib.util.cache_from_source(
+            'foo.py', optimization=cmd.optimize
+        )
+        assert os.path.exists(pyc_file)
+        assert os.path.exists(pyc_opt_file)
+
+    def test_get_outputs(self):
+        project_dir, dist = self.create_dist()
+        os.chdir(project_dir)
+        os.mkdir('spam')
+        cmd = install_lib(dist)
+
+        # setting up a dist environment
+        cmd.compile = cmd.optimize = 1
+        cmd.install_dir = self.mkdtemp()
+        f = os.path.join(project_dir, 'spam', '__init__.py')
+        self.write_file(f, '# python package')
+        cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+        cmd.distribution.packages = ['spam']
+        cmd.distribution.script_name = 'setup.py'
+
+        # get_outputs should return 4 elements: spam/__init__.py and .pyc,
+        # foo.import-tag-abiflags.so / foo.pyd
+        outputs = cmd.get_outputs()
+        assert len(outputs) == 4, outputs
+
+    def test_get_inputs(self):
+        project_dir, dist = self.create_dist()
+        os.chdir(project_dir)
+        os.mkdir('spam')
+        cmd = install_lib(dist)
+
+        # setting up a dist environment
+        cmd.compile = cmd.optimize = 1
+        cmd.install_dir = self.mkdtemp()
+        f = os.path.join(project_dir, 'spam', '__init__.py')
+        self.write_file(f, '# python package')
+        cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+        cmd.distribution.packages = ['spam']
+        cmd.distribution.script_name = 'setup.py'
+
+        # get_inputs should return 2 elements: spam/__init__.py and
+        # foo.import-tag-abiflags.so / foo.pyd
+        inputs = cmd.get_inputs()
+        assert len(inputs) == 2, inputs
+
+    def test_dont_write_bytecode(self, caplog):
+        # makes sure byte_compile is not used
+        dist = self.create_dist()[1]
+        cmd = install_lib(dist)
+        cmd.compile = True
+        cmd.optimize = 1
+
+        old_dont_write_bytecode = sys.dont_write_bytecode
+        sys.dont_write_bytecode = True
+        try:
+            cmd.byte_compile([])
+        finally:
+            sys.dont_write_bytecode = old_dont_write_bytecode
+
+        assert 'byte-compiling is disabled' in caplog.messages[0]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_scripts.py
new file mode 100644
index 0000000..868b1c2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_install_scripts.py
@@ -0,0 +1,52 @@
+"""Tests for distutils.command.install_scripts."""
+
+import os
+from distutils.command.install_scripts import install_scripts
+from distutils.core import Distribution
+from distutils.tests import support
+
+from . import test_build_scripts
+
+
+class TestInstallScripts(support.TempdirManager):
+    def test_default_settings(self):
+        dist = Distribution()
+        dist.command_obj["build"] = support.DummyCommand(build_scripts="/foo/bar")
+        dist.command_obj["install"] = support.DummyCommand(
+            install_scripts="/splat/funk",
+            force=True,
+            skip_build=True,
+        )
+        cmd = install_scripts(dist)
+        assert not cmd.force
+        assert not cmd.skip_build
+        assert cmd.build_dir is None
+        assert cmd.install_dir is None
+
+        cmd.finalize_options()
+
+        assert cmd.force
+        assert cmd.skip_build
+        assert cmd.build_dir == "/foo/bar"
+        assert cmd.install_dir == "/splat/funk"
+
+    def test_installation(self):
+        source = self.mkdtemp()
+
+        expected = test_build_scripts.TestBuildScripts.write_sample_scripts(source)
+
+        target = self.mkdtemp()
+        dist = Distribution()
+        dist.command_obj["build"] = support.DummyCommand(build_scripts=source)
+        dist.command_obj["install"] = support.DummyCommand(
+            install_scripts=target,
+            force=True,
+            skip_build=True,
+        )
+        cmd = install_scripts(dist)
+        cmd.finalize_options()
+        cmd.run()
+
+        installed = os.listdir(target)
+        for name in expected:
+            assert name in installed
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_log.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_log.py
new file mode 100644
index 0000000..d67779f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_log.py
@@ -0,0 +1,12 @@
+"""Tests for distutils.log"""
+
+import logging
+from distutils._log import log
+
+
+class TestLog:
+    def test_non_ascii(self, caplog):
+        caplog.set_level(logging.DEBUG)
+        log.debug('Dεbug\tMėssãge')
+        log.fatal('Fαtal\tÈrrōr')
+        assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr']
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_modified.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_modified.py
new file mode 100644
index 0000000..e35cec2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_modified.py
@@ -0,0 +1,126 @@
+"""Tests for distutils._modified."""
+
+import os
+import types
+from distutils._modified import newer, newer_group, newer_pairwise, newer_pairwise_group
+from distutils.errors import DistutilsFileError
+from distutils.tests import support
+
+import pytest
+
+
+class TestDepUtil(support.TempdirManager):
+    def test_newer(self):
+        tmpdir = self.mkdtemp()
+        new_file = os.path.join(tmpdir, 'new')
+        old_file = os.path.abspath(__file__)
+
+        # Raise DistutilsFileError if 'new_file' does not exist.
+        with pytest.raises(DistutilsFileError):
+            newer(new_file, old_file)
+
+        # Return true if 'new_file' exists and is more recently modified than
+        # 'old_file', or if 'new_file' exists and 'old_file' doesn't.
+        self.write_file(new_file)
+        assert newer(new_file, 'I_dont_exist')
+        assert newer(new_file, old_file)
+
+        # Return false if both exist and 'old_file' is the same age or younger
+        # than 'new_file'.
+        assert not newer(old_file, new_file)
+
+    def _setup_1234(self):
+        tmpdir = self.mkdtemp()
+        sources = os.path.join(tmpdir, 'sources')
+        targets = os.path.join(tmpdir, 'targets')
+        os.mkdir(sources)
+        os.mkdir(targets)
+        one = os.path.join(sources, 'one')
+        two = os.path.join(sources, 'two')
+        three = os.path.abspath(__file__)  # I am the old file
+        four = os.path.join(targets, 'four')
+        self.write_file(one)
+        self.write_file(two)
+        self.write_file(four)
+        return one, two, three, four
+
+    def test_newer_pairwise(self):
+        one, two, three, four = self._setup_1234()
+
+        assert newer_pairwise([one, two], [three, four]) == ([one], [three])
+
+    def test_newer_pairwise_mismatch(self):
+        one, two, three, four = self._setup_1234()
+
+        with pytest.raises(ValueError):
+            newer_pairwise([one], [three, four])
+
+        with pytest.raises(ValueError):
+            newer_pairwise([one, two], [three])
+
+    def test_newer_pairwise_empty(self):
+        assert newer_pairwise([], []) == ([], [])
+
+    def test_newer_pairwise_fresh(self):
+        one, two, three, four = self._setup_1234()
+
+        assert newer_pairwise([one, three], [two, four]) == ([], [])
+
+    def test_newer_group(self):
+        tmpdir = self.mkdtemp()
+        sources = os.path.join(tmpdir, 'sources')
+        os.mkdir(sources)
+        one = os.path.join(sources, 'one')
+        two = os.path.join(sources, 'two')
+        three = os.path.join(sources, 'three')
+        old_file = os.path.abspath(__file__)
+
+        # return true if 'old_file' is out-of-date with respect to any file
+        # listed in 'sources'.
+        self.write_file(one)
+        self.write_file(two)
+        self.write_file(three)
+        assert newer_group([one, two, three], old_file)
+        assert not newer_group([one, two, old_file], three)
+
+        # missing handling
+        os.remove(one)
+        with pytest.raises(OSError):
+            newer_group([one, two, old_file], three)
+
+        assert not newer_group([one, two, old_file], three, missing='ignore')
+
+        assert newer_group([one, two, old_file], three, missing='newer')
+
+
+@pytest.fixture
+def groups_target(tmp_path):
+    """
+    Set up some older sources, a target, and newer sources.
+
+    Returns a simple namespace with these values.
+    """
+    filenames = ['older.c', 'older.h', 'target.o', 'newer.c', 'newer.h']
+    paths = [tmp_path / name for name in filenames]
+
+    for mtime, path in enumerate(paths):
+        path.write_text('', encoding='utf-8')
+
+        # make sure modification times are sequential
+        os.utime(path, (mtime, mtime))
+
+    return types.SimpleNamespace(older=paths[:2], target=paths[2], newer=paths[3:])
+
+
+def test_newer_pairwise_group(groups_target):
+    older = newer_pairwise_group([groups_target.older], [groups_target.target])
+    newer = newer_pairwise_group([groups_target.newer], [groups_target.target])
+    assert older == ([], [])
+    assert newer == ([groups_target.newer], [groups_target.target])
+
+
+def test_newer_group_no_sources_no_target(tmp_path):
+    """
+    Consider no sources and no target "newer".
+    """
+    assert newer_group([], str(tmp_path / 'does-not-exist'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sdist.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sdist.py
new file mode 100644
index 0000000..6b1a376
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sdist.py
@@ -0,0 +1,470 @@
+"""Tests for distutils.command.sdist."""
+
+import os
+import pathlib
+import shutil  # noqa: F401
+import tarfile
+import zipfile
+from distutils.archive_util import ARCHIVE_FORMATS
+from distutils.command.sdist import sdist, show_formats
+from distutils.core import Distribution
+from distutils.errors import DistutilsOptionError
+from distutils.filelist import FileList
+from os.path import join
+from textwrap import dedent
+
+import jaraco.path
+import path
+import pytest
+from more_itertools import ilen
+
+from . import support
+from .unix_compat import grp, pwd, require_uid_0, require_unix_id
+
+SETUP_PY = """
+from distutils.core import setup
+import somecode
+
+setup(name='fake')
+"""
+
+MANIFEST = """\
+# file GENERATED by distutils, do NOT edit
+README
+buildout.cfg
+inroot.txt
+setup.py
+data%(sep)sdata.dt
+scripts%(sep)sscript.py
+some%(sep)sfile.txt
+some%(sep)sother_file.txt
+somecode%(sep)s__init__.py
+somecode%(sep)sdoc.dat
+somecode%(sep)sdoc.txt
+"""
+
+
+@pytest.fixture(autouse=True)
+def project_dir(request, distutils_managed_tempdir):
+    self = request.instance
+    self.tmp_dir = self.mkdtemp()
+    jaraco.path.build(
+        {
+            'somecode': {
+                '__init__.py': '#',
+            },
+            'README': 'xxx',
+            'setup.py': SETUP_PY,
+        },
+        self.tmp_dir,
+    )
+    with path.Path(self.tmp_dir):
+        yield
+
+
+def clean_lines(filepath):
+    with pathlib.Path(filepath).open(encoding='utf-8') as f:
+        yield from filter(None, map(str.strip, f))
+
+
+class TestSDist(support.TempdirManager):
+    def get_cmd(self, metadata=None):
+        """Returns a cmd"""
+        if metadata is None:
+            metadata = {
+                'name': 'ns.fake--pkg',
+                'version': '1.0',
+                'url': 'xxx',
+                'author': 'xxx',
+                'author_email': 'xxx',
+            }
+        dist = Distribution(metadata)
+        dist.script_name = 'setup.py'
+        dist.packages = ['somecode']
+        dist.include_package_data = True
+        cmd = sdist(dist)
+        cmd.dist_dir = 'dist'
+        return dist, cmd
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_prune_file_list(self):
+        # this test creates a project with some VCS dirs and an NFS rename
+        # file, then launches sdist to check they get pruned on all systems
+
+        # creating VCS directories with some files in them
+        os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
+        self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
+
+        os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
+        self.write_file((self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx')
+
+        os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
+        self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx')
+
+        self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx')
+
+        # now building a sdist
+        dist, cmd = self.get_cmd()
+
+        # zip is available universally
+        # (tar might not be installed under win32)
+        cmd.formats = ['zip']
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # now let's check what we have
+        dist_folder = join(self.tmp_dir, 'dist')
+        files = os.listdir(dist_folder)
+        assert files == ['ns_fake_pkg-1.0.zip']
+
+        zip_file = zipfile.ZipFile(join(dist_folder, 'ns_fake_pkg-1.0.zip'))
+        try:
+            content = zip_file.namelist()
+        finally:
+            zip_file.close()
+
+        # making sure everything has been pruned correctly
+        expected = [
+            '',
+            'PKG-INFO',
+            'README',
+            'setup.py',
+            'somecode/',
+            'somecode/__init__.py',
+        ]
+        assert sorted(content) == ['ns_fake_pkg-1.0/' + x for x in expected]
+
+    @pytest.mark.usefixtures('needs_zlib')
+    @pytest.mark.skipif("not shutil.which('tar')")
+    @pytest.mark.skipif("not shutil.which('gzip')")
+    def test_make_distribution(self):
+        # now building a sdist
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar then a tar
+        cmd.formats = ['gztar', 'tar']
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have two files
+        dist_folder = join(self.tmp_dir, 'dist')
+        result = os.listdir(dist_folder)
+        result.sort()
+        assert result == ['ns_fake_pkg-1.0.tar', 'ns_fake_pkg-1.0.tar.gz']
+
+        os.remove(join(dist_folder, 'ns_fake_pkg-1.0.tar'))
+        os.remove(join(dist_folder, 'ns_fake_pkg-1.0.tar.gz'))
+
+        # now trying a tar then a gztar
+        cmd.formats = ['tar', 'gztar']
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        result = os.listdir(dist_folder)
+        result.sort()
+        assert result == ['ns_fake_pkg-1.0.tar', 'ns_fake_pkg-1.0.tar.gz']
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_add_defaults(self):
+        # https://bugs.python.org/issue2279
+
+        # add_default should also include
+        # data_files and package_data
+        dist, cmd = self.get_cmd()
+
+        # filling data_files by pointing files
+        # in package_data
+        dist.package_data = {'': ['*.cfg', '*.dat'], 'somecode': ['*.txt']}
+        self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+        self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
+
+        # adding some data in data_files
+        data_dir = join(self.tmp_dir, 'data')
+        os.mkdir(data_dir)
+        self.write_file((data_dir, 'data.dt'), '#')
+        some_dir = join(self.tmp_dir, 'some')
+        os.mkdir(some_dir)
+        # make sure VCS directories are pruned (#14004)
+        hg_dir = join(self.tmp_dir, '.hg')
+        os.mkdir(hg_dir)
+        self.write_file((hg_dir, 'last-message.txt'), '#')
+        # a buggy regex used to prevent this from working on windows (#6884)
+        self.write_file((self.tmp_dir, 'buildout.cfg'), '#')
+        self.write_file((self.tmp_dir, 'inroot.txt'), '#')
+        self.write_file((some_dir, 'file.txt'), '#')
+        self.write_file((some_dir, 'other_file.txt'), '#')
+
+        dist.data_files = [
+            ('data', ['data/data.dt', 'buildout.cfg', 'inroot.txt', 'notexisting']),
+            'some/file.txt',
+            'some/other_file.txt',
+        ]
+
+        # adding a script
+        script_dir = join(self.tmp_dir, 'scripts')
+        os.mkdir(script_dir)
+        self.write_file((script_dir, 'script.py'), '#')
+        dist.scripts = [join('scripts', 'script.py')]
+
+        cmd.formats = ['zip']
+        cmd.use_defaults = True
+
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # now let's check what we have
+        dist_folder = join(self.tmp_dir, 'dist')
+        files = os.listdir(dist_folder)
+        assert files == ['ns_fake_pkg-1.0.zip']
+
+        zip_file = zipfile.ZipFile(join(dist_folder, 'ns_fake_pkg-1.0.zip'))
+        try:
+            content = zip_file.namelist()
+        finally:
+            zip_file.close()
+
+        # making sure everything was added
+        expected = [
+            '',
+            'PKG-INFO',
+            'README',
+            'buildout.cfg',
+            'data/',
+            'data/data.dt',
+            'inroot.txt',
+            'scripts/',
+            'scripts/script.py',
+            'setup.py',
+            'some/',
+            'some/file.txt',
+            'some/other_file.txt',
+            'somecode/',
+            'somecode/__init__.py',
+            'somecode/doc.dat',
+            'somecode/doc.txt',
+        ]
+        assert sorted(content) == ['ns_fake_pkg-1.0/' + x for x in expected]
+
+        # checking the MANIFEST
+        manifest = pathlib.Path(self.tmp_dir, 'MANIFEST').read_text(encoding='utf-8')
+        assert manifest == MANIFEST % {'sep': os.sep}
+
+    @staticmethod
+    def warnings(messages, prefix='warning: '):
+        return [msg for msg in messages if msg.startswith(prefix)]
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_metadata_check_option(self, caplog):
+        # testing the `medata-check` option
+        dist, cmd = self.get_cmd(metadata={})
+
+        # this should raise some warnings !
+        # with the `check` subcommand
+        cmd.ensure_finalized()
+        cmd.run()
+        assert len(self.warnings(caplog.messages, 'warning: check: ')) == 1
+
+        # trying with a complete set of metadata
+        caplog.clear()
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        cmd.metadata_check = False
+        cmd.run()
+        assert len(self.warnings(caplog.messages, 'warning: check: ')) == 0
+
+    def test_show_formats(self, capsys):
+        show_formats()
+
+        # the output should be a header line + one line per format
+        num_formats = len(ARCHIVE_FORMATS.keys())
+        output = [
+            line
+            for line in capsys.readouterr().out.split('\n')
+            if line.strip().startswith('--formats=')
+        ]
+        assert len(output) == num_formats
+
+    def test_finalize_options(self):
+        dist, cmd = self.get_cmd()
+        cmd.finalize_options()
+
+        # default options set by finalize
+        assert cmd.manifest == 'MANIFEST'
+        assert cmd.template == 'MANIFEST.in'
+        assert cmd.dist_dir == 'dist'
+
+        # formats has to be a string splitable on (' ', ',') or
+        # a stringlist
+        cmd.formats = 1
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+        cmd.formats = ['zip']
+        cmd.finalize_options()
+
+        # formats has to be known
+        cmd.formats = 'supazipa'
+        with pytest.raises(DistutilsOptionError):
+            cmd.finalize_options()
+
+    # the following tests make sure there is a nice error message instead
+    # of a traceback when parsing an invalid manifest template
+
+    def _check_template(self, content, caplog):
+        dist, cmd = self.get_cmd()
+        os.chdir(self.tmp_dir)
+        self.write_file('MANIFEST.in', content)
+        cmd.ensure_finalized()
+        cmd.filelist = FileList()
+        cmd.read_template()
+        assert len(self.warnings(caplog.messages)) == 1
+
+    def test_invalid_template_unknown_command(self, caplog):
+        self._check_template('taunt knights *', caplog)
+
+    def test_invalid_template_wrong_arguments(self, caplog):
+        # this manifest command takes one argument
+        self._check_template('prune', caplog)
+
+    @pytest.mark.skipif("platform.system() != 'Windows'")
+    def test_invalid_template_wrong_path(self, caplog):
+        # on Windows, trailing slashes are not allowed
+        # this used to crash instead of raising a warning: #8286
+        self._check_template('include examples/', caplog)
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_get_file_list(self):
+        # make sure MANIFEST is recalculated
+        dist, cmd = self.get_cmd()
+
+        # filling data_files by pointing files in package_data
+        dist.package_data = {'somecode': ['*.txt']}
+        self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+        cmd.formats = ['gztar']
+        cmd.ensure_finalized()
+        cmd.run()
+
+        assert ilen(clean_lines(cmd.manifest)) == 5
+
+        # adding a file
+        self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
+
+        # make sure build_py is reinitialized, like a fresh run
+        build_py = dist.get_command_obj('build_py')
+        build_py.finalized = False
+        build_py.ensure_finalized()
+
+        cmd.run()
+
+        manifest2 = list(clean_lines(cmd.manifest))
+
+        # do we have the new file in MANIFEST ?
+        assert len(manifest2) == 6
+        assert 'doc2.txt' in manifest2[-1]
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_manifest_marker(self):
+        # check that autogenerated MANIFESTs have a marker
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        cmd.run()
+
+        assert (
+            next(clean_lines(cmd.manifest))
+            == '# file GENERATED by distutils, do NOT edit'
+        )
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_manifest_comments(self):
+        # make sure comments don't cause exceptions or wrong includes
+        contents = dedent(
+            """\
+            # bad.py
+            #bad.py
+            good.py
+            """
+        )
+        dist, cmd = self.get_cmd()
+        cmd.ensure_finalized()
+        self.write_file((self.tmp_dir, cmd.manifest), contents)
+        self.write_file((self.tmp_dir, 'good.py'), '# pick me!')
+        self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
+        self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
+        cmd.run()
+        assert cmd.filelist.files == ['good.py']
+
+    @pytest.mark.usefixtures('needs_zlib')
+    def test_manual_manifest(self):
+        # check that a MANIFEST without a marker is left alone
+        dist, cmd = self.get_cmd()
+        cmd.formats = ['gztar']
+        cmd.ensure_finalized()
+        self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
+        self.write_file(
+            (self.tmp_dir, 'README.manual'),
+            'This project maintains its MANIFEST file itself.',
+        )
+        cmd.run()
+        assert cmd.filelist.files == ['README.manual']
+
+        assert list(clean_lines(cmd.manifest)) == ['README.manual']
+
+        archive_name = join(self.tmp_dir, 'dist', 'ns_fake_pkg-1.0.tar.gz')
+        archive = tarfile.open(archive_name)
+        try:
+            filenames = [tarinfo.name for tarinfo in archive]
+        finally:
+            archive.close()
+        assert sorted(filenames) == [
+            'ns_fake_pkg-1.0',
+            'ns_fake_pkg-1.0/PKG-INFO',
+            'ns_fake_pkg-1.0/README.manual',
+        ]
+
+    @pytest.mark.usefixtures('needs_zlib')
+    @require_unix_id
+    @require_uid_0
+    @pytest.mark.skipif("not shutil.which('tar')")
+    @pytest.mark.skipif("not shutil.which('gzip')")
+    def test_make_distribution_owner_group(self):
+        # now building a sdist
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar and specifying the owner+group
+        cmd.formats = ['gztar']
+        cmd.owner = pwd.getpwuid(0)[0]
+        cmd.group = grp.getgrgid(0)[0]
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have the good rights
+        archive_name = join(self.tmp_dir, 'dist', 'ns_fake_pkg-1.0.tar.gz')
+        archive = tarfile.open(archive_name)
+        try:
+            for member in archive.getmembers():
+                assert member.uid == 0
+                assert member.gid == 0
+        finally:
+            archive.close()
+
+        # building a sdist again
+        dist, cmd = self.get_cmd()
+
+        # creating a gztar
+        cmd.formats = ['gztar']
+        cmd.ensure_finalized()
+        cmd.run()
+
+        # making sure we have the good rights
+        archive_name = join(self.tmp_dir, 'dist', 'ns_fake_pkg-1.0.tar.gz')
+        archive = tarfile.open(archive_name)
+
+        # note that we are not testing the group ownership here
+        # because, depending on the platforms and the container
+        # rights (see #7408)
+        try:
+            for member in archive.getmembers():
+                assert member.uid == os.getuid()
+        finally:
+            archive.close()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_spawn.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_spawn.py
new file mode 100644
index 0000000..3b9fc92
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_spawn.py
@@ -0,0 +1,141 @@
+"""Tests for distutils.spawn."""
+
+import os
+import stat
+import sys
+import unittest.mock as mock
+from distutils.errors import DistutilsExecError
+from distutils.spawn import find_executable, spawn
+from distutils.tests import support
+
+import path
+import pytest
+from test.support import unix_shell
+
+from .compat import py39 as os_helper
+
+
+class TestSpawn(support.TempdirManager):
+    @pytest.mark.skipif("os.name not in ('nt', 'posix')")
+    def test_spawn(self):
+        tmpdir = self.mkdtemp()
+
+        # creating something executable
+        # through the shell that returns 1
+        if sys.platform != 'win32':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, f'#!{unix_shell}\nexit 1')
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 1')
+
+        os.chmod(exe, 0o777)
+        with pytest.raises(DistutilsExecError):
+            spawn([exe])
+
+        # now something that works
+        if sys.platform != 'win32':
+            exe = os.path.join(tmpdir, 'foo.sh')
+            self.write_file(exe, f'#!{unix_shell}\nexit 0')
+        else:
+            exe = os.path.join(tmpdir, 'foo.bat')
+            self.write_file(exe, 'exit 0')
+
+        os.chmod(exe, 0o777)
+        spawn([exe])  # should work without any error
+
+    def test_find_executable(self, tmp_path):
+        program_path = self._make_executable(tmp_path, '.exe')
+        program = program_path.name
+        program_noeext = program_path.with_suffix('').name
+        filename = str(program_path)
+        tmp_dir = path.Path(tmp_path)
+
+        # test path parameter
+        rv = find_executable(program, path=tmp_dir)
+        assert rv == filename
+
+        if sys.platform == 'win32':
+            # test without ".exe" extension
+            rv = find_executable(program_noeext, path=tmp_dir)
+            assert rv == filename
+
+        # test find in the current directory
+        with tmp_dir:
+            rv = find_executable(program)
+            assert rv == program
+
+        # test non-existent program
+        dont_exist_program = "dontexist_" + program
+        rv = find_executable(dont_exist_program, path=tmp_dir)
+        assert rv is None
+
+        # PATH='': no match, except in the current directory
+        with os_helper.EnvironmentVarGuard() as env:
+            env['PATH'] = ''
+            with (
+                mock.patch(
+                    'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
+                ),
+                mock.patch('distutils.spawn.os.defpath', tmp_dir),
+            ):
+                rv = find_executable(program)
+                assert rv is None
+
+                # look in current directory
+                with tmp_dir:
+                    rv = find_executable(program)
+                    assert rv == program
+
+        # PATH=':': explicitly looks in the current directory
+        with os_helper.EnvironmentVarGuard() as env:
+            env['PATH'] = os.pathsep
+            with (
+                mock.patch('distutils.spawn.os.confstr', return_value='', create=True),
+                mock.patch('distutils.spawn.os.defpath', ''),
+            ):
+                rv = find_executable(program)
+                assert rv is None
+
+                # look in current directory
+                with tmp_dir:
+                    rv = find_executable(program)
+                    assert rv == program
+
+        # missing PATH: test os.confstr("CS_PATH") and os.defpath
+        with os_helper.EnvironmentVarGuard() as env:
+            env.pop('PATH', None)
+
+            # without confstr
+            with (
+                mock.patch(
+                    'distutils.spawn.os.confstr', side_effect=ValueError, create=True
+                ),
+                mock.patch('distutils.spawn.os.defpath', tmp_dir),
+            ):
+                rv = find_executable(program)
+                assert rv == filename
+
+            # with confstr
+            with (
+                mock.patch(
+                    'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
+                ),
+                mock.patch('distutils.spawn.os.defpath', ''),
+            ):
+                rv = find_executable(program)
+                assert rv == filename
+
+    @staticmethod
+    def _make_executable(tmp_path, ext):
+        # Give the temporary program a suffix regardless of platform.
+        # It's needed on Windows and not harmful on others.
+        program = tmp_path.joinpath('program').with_suffix(ext)
+        program.write_text("", encoding='utf-8')
+        program.chmod(stat.S_IXUSR)
+        return program
+
+    def test_spawn_missing_exe(self):
+        with pytest.raises(DistutilsExecError) as ctx:
+            spawn(['does-not-exist'])
+        assert "command 'does-not-exist' failed" in str(ctx.value)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sysconfig.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sysconfig.py
new file mode 100644
index 0000000..43d77c2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_sysconfig.py
@@ -0,0 +1,319 @@
+"""Tests for distutils.sysconfig."""
+
+import contextlib
+import distutils
+import os
+import pathlib
+import subprocess
+import sys
+from distutils import sysconfig
+from distutils.ccompiler import new_compiler  # noqa: F401
+from distutils.unixccompiler import UnixCCompiler
+
+import jaraco.envs
+import path
+import pytest
+from jaraco.text import trim
+from test.support import swap_item
+
+
+def _gen_makefile(root, contents):
+    jaraco.path.build({'Makefile': trim(contents)}, root)
+    return root / 'Makefile'
+
+
+@pytest.mark.usefixtures('save_env')
+class TestSysconfig:
+    def test_get_config_h_filename(self):
+        config_h = sysconfig.get_config_h_filename()
+        assert os.path.isfile(config_h)
+
+    @pytest.mark.skipif("platform.system() == 'Windows'")
+    @pytest.mark.skipif("sys.implementation.name != 'cpython'")
+    def test_get_makefile_filename(self):
+        makefile = sysconfig.get_makefile_filename()
+        assert os.path.isfile(makefile)
+
+    def test_get_python_lib(self, tmp_path):
+        assert sysconfig.get_python_lib() != sysconfig.get_python_lib(prefix=tmp_path)
+
+    def test_get_config_vars(self):
+        cvars = sysconfig.get_config_vars()
+        assert isinstance(cvars, dict)
+        assert cvars
+
+    @pytest.mark.skipif('sysconfig.IS_PYPY')
+    @pytest.mark.skipif('sysconfig.python_build')
+    @pytest.mark.xfail('platform.system() == "Windows"')
+    def test_srcdir_simple(self):
+        # See #15364.
+        srcdir = pathlib.Path(sysconfig.get_config_var('srcdir'))
+
+        assert srcdir.absolute()
+        assert srcdir.is_dir()
+
+        makefile = pathlib.Path(sysconfig.get_makefile_filename())
+        assert makefile.parent.samefile(srcdir)
+
+    @pytest.mark.skipif('sysconfig.IS_PYPY')
+    @pytest.mark.skipif('not sysconfig.python_build')
+    def test_srcdir_python_build(self):
+        # See #15364.
+        srcdir = pathlib.Path(sysconfig.get_config_var('srcdir'))
+
+        # The python executable has not been installed so srcdir
+        # should be a full source checkout.
+        Python_h = srcdir.joinpath('Include', 'Python.h')
+        assert Python_h.is_file()
+        assert sysconfig._is_python_source_dir(srcdir)
+        assert sysconfig._is_python_source_dir(str(srcdir))
+
+    def test_srcdir_independent_of_cwd(self):
+        """
+        srcdir should be independent of the current working directory
+        """
+        # See #15364.
+        srcdir = sysconfig.get_config_var('srcdir')
+        with path.Path('..'):
+            srcdir2 = sysconfig.get_config_var('srcdir')
+        assert srcdir == srcdir2
+
+    def customize_compiler(self):
+        # make sure AR gets caught
+        class compiler:
+            compiler_type = 'unix'
+            executables = UnixCCompiler.executables
+
+            def __init__(self):
+                self.exes = {}
+
+            def set_executables(self, **kw):
+                for k, v in kw.items():
+                    self.exes[k] = v
+
+        sysconfig_vars = {
+            'AR': 'sc_ar',
+            'CC': 'sc_cc',
+            'CXX': 'sc_cxx',
+            'ARFLAGS': '--sc-arflags',
+            'CFLAGS': '--sc-cflags',
+            'CCSHARED': '--sc-ccshared',
+            'LDSHARED': 'sc_ldshared',
+            'SHLIB_SUFFIX': 'sc_shutil_suffix',
+        }
+
+        comp = compiler()
+        with contextlib.ExitStack() as cm:
+            for key, value in sysconfig_vars.items():
+                cm.enter_context(swap_item(sysconfig._config_vars, key, value))
+            sysconfig.customize_compiler(comp)
+
+        return comp
+
+    @pytest.mark.skipif("not isinstance(new_compiler(), UnixCCompiler)")
+    @pytest.mark.usefixtures('disable_macos_customization')
+    def test_customize_compiler(self):
+        # Make sure that sysconfig._config_vars is initialized
+        sysconfig.get_config_vars()
+
+        os.environ['AR'] = 'env_ar'
+        os.environ['CC'] = 'env_cc'
+        os.environ['CPP'] = 'env_cpp'
+        os.environ['CXX'] = 'env_cxx --env-cxx-flags'
+        os.environ['LDSHARED'] = 'env_ldshared'
+        os.environ['LDFLAGS'] = '--env-ldflags'
+        os.environ['ARFLAGS'] = '--env-arflags'
+        os.environ['CFLAGS'] = '--env-cflags'
+        os.environ['CPPFLAGS'] = '--env-cppflags'
+        os.environ['RANLIB'] = 'env_ranlib'
+
+        comp = self.customize_compiler()
+        assert comp.exes['archiver'] == 'env_ar --env-arflags'
+        assert comp.exes['preprocessor'] == 'env_cpp --env-cppflags'
+        assert comp.exes['compiler'] == 'env_cc --env-cflags --env-cppflags'
+        assert comp.exes['compiler_so'] == (
+            'env_cc --env-cflags --env-cppflags --sc-ccshared'
+        )
+        assert (
+            comp.exes['compiler_cxx']
+            == 'env_cxx --env-cxx-flags --sc-cflags --env-cppflags'
+        )
+        assert comp.exes['linker_exe'] == 'env_cc'
+        assert comp.exes['linker_so'] == (
+            'env_ldshared --env-ldflags --env-cflags --env-cppflags'
+        )
+        assert comp.shared_lib_extension == 'sc_shutil_suffix'
+
+        if sys.platform == "darwin":
+            assert comp.exes['ranlib'] == 'env_ranlib'
+        else:
+            assert 'ranlib' not in comp.exes
+
+        del os.environ['AR']
+        del os.environ['CC']
+        del os.environ['CPP']
+        del os.environ['CXX']
+        del os.environ['LDSHARED']
+        del os.environ['LDFLAGS']
+        del os.environ['ARFLAGS']
+        del os.environ['CFLAGS']
+        del os.environ['CPPFLAGS']
+        del os.environ['RANLIB']
+
+        comp = self.customize_compiler()
+        assert comp.exes['archiver'] == 'sc_ar --sc-arflags'
+        assert comp.exes['preprocessor'] == 'sc_cc -E'
+        assert comp.exes['compiler'] == 'sc_cc --sc-cflags'
+        assert comp.exes['compiler_so'] == 'sc_cc --sc-cflags --sc-ccshared'
+        assert comp.exes['compiler_cxx'] == 'sc_cxx --sc-cflags'
+        assert comp.exes['linker_exe'] == 'sc_cc'
+        assert comp.exes['linker_so'] == 'sc_ldshared'
+        assert comp.shared_lib_extension == 'sc_shutil_suffix'
+        assert 'ranlib' not in comp.exes
+
+    def test_parse_makefile_base(self, tmp_path):
+        makefile = _gen_makefile(
+            tmp_path,
+            """
+            CONFIG_ARGS=  '--arg1=optarg1' 'ENV=LIB'
+            VAR=$OTHER
+            OTHER=foo
+            """,
+        )
+        d = sysconfig.parse_makefile(makefile)
+        assert d == {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}
+
+    def test_parse_makefile_literal_dollar(self, tmp_path):
+        makefile = _gen_makefile(
+            tmp_path,
+            """
+            CONFIG_ARGS=  '--arg1=optarg1' 'ENV=\\$$LIB'
+            VAR=$OTHER
+            OTHER=foo
+            """,
+        )
+        d = sysconfig.parse_makefile(makefile)
+        assert d == {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}
+
+    def test_sysconfig_module(self):
+        import sysconfig as global_sysconfig
+
+        assert global_sysconfig.get_config_var('CFLAGS') == sysconfig.get_config_var(
+            'CFLAGS'
+        )
+        assert global_sysconfig.get_config_var('LDFLAGS') == sysconfig.get_config_var(
+            'LDFLAGS'
+        )
+
+    # On macOS, binary installers support extension module building on
+    # various levels of the operating system with differing Xcode
+    # configurations, requiring customization of some of the
+    # compiler configuration directives to suit the environment on
+    # the installed machine. Some of these customizations may require
+    # running external programs and are thus deferred until needed by
+    # the first extension module build. Only
+    # the Distutils version of sysconfig is used for extension module
+    # builds, which happens earlier in the Distutils tests. This may
+    # cause the following tests to fail since no tests have caused
+    # the global version of sysconfig to call the customization yet.
+    # The solution for now is to simply skip this test in this case.
+    # The longer-term solution is to only have one version of sysconfig.
+    @pytest.mark.skipif("sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER')")
+    def test_sysconfig_compiler_vars(self):
+        import sysconfig as global_sysconfig
+
+        if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
+            pytest.skip('compiler flags customized')
+        assert global_sysconfig.get_config_var('LDSHARED') == sysconfig.get_config_var(
+            'LDSHARED'
+        )
+        assert global_sysconfig.get_config_var('CC') == sysconfig.get_config_var('CC')
+
+    @pytest.mark.skipif("not sysconfig.get_config_var('EXT_SUFFIX')")
+    def test_SO_deprecation(self):
+        with pytest.warns(DeprecationWarning):
+            sysconfig.get_config_var('SO')
+
+    def test_customize_compiler_before_get_config_vars(self, tmp_path):
+        # Issue #21923: test that a Distribution compiler
+        # instance can be called without an explicit call to
+        # get_config_vars().
+        jaraco.path.build(
+            {
+                'file': trim("""
+                    from distutils.core import Distribution
+                    config = Distribution().get_command_obj('config')
+                    # try_compile may pass or it may fail if no compiler
+                    # is found but it should not raise an exception.
+                    rc = config.try_compile('int x;')
+                    """)
+            },
+            tmp_path,
+        )
+        p = subprocess.Popen(
+            [sys.executable, tmp_path / 'file'],
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+            universal_newlines=True,
+            encoding='utf-8',
+        )
+        outs, errs = p.communicate()
+        assert 0 == p.returncode, "Subprocess failed: " + outs
+
+    def test_parse_config_h(self):
+        config_h = sysconfig.get_config_h_filename()
+        input = {}
+        with open(config_h, encoding="utf-8") as f:
+            result = sysconfig.parse_config_h(f, g=input)
+        assert input is result
+        with open(config_h, encoding="utf-8") as f:
+            result = sysconfig.parse_config_h(f)
+        assert isinstance(result, dict)
+
+    @pytest.mark.skipif("platform.system() != 'Windows'")
+    @pytest.mark.skipif("sys.implementation.name != 'cpython'")
+    def test_win_ext_suffix(self):
+        assert sysconfig.get_config_var("EXT_SUFFIX").endswith(".pyd")
+        assert sysconfig.get_config_var("EXT_SUFFIX") != ".pyd"
+
+    @pytest.mark.skipif("platform.system() != 'Windows'")
+    @pytest.mark.skipif("sys.implementation.name != 'cpython'")
+    @pytest.mark.skipif(
+        '\\PCbuild\\'.casefold() not in sys.executable.casefold(),
+        reason='Need sys.executable to be in a source tree',
+    )
+    def test_win_build_venv_from_source_tree(self, tmp_path):
+        """Ensure distutils.sysconfig detects venvs from source tree builds."""
+        env = jaraco.envs.VEnv()
+        env.create_opts = env.clean_opts
+        env.root = tmp_path
+        env.ensure_env()
+        cmd = [
+            env.exe(),
+            "-c",
+            "import distutils.sysconfig; print(distutils.sysconfig.python_build)",
+        ]
+        distutils_path = os.path.dirname(os.path.dirname(distutils.__file__))
+        out = subprocess.check_output(
+            cmd, env={**os.environ, "PYTHONPATH": distutils_path}
+        )
+        assert out == "True"
+
+    def test_get_python_inc_missing_config_dir(self, monkeypatch):
+        """
+        In portable Python installations, the sysconfig will be broken,
+        pointing to the directories where the installation was built and
+        not where it currently is. In this case, ensure that the missing
+        directory isn't used for get_python_inc.
+
+        See pypa/distutils#178.
+        """
+
+        def override(name):
+            if name == 'INCLUDEPY':
+                return '/does-not-exist'
+            return sysconfig.get_config_var(name)
+
+        monkeypatch.setattr(sysconfig, 'get_config_var', override)
+
+        assert os.path.exists(sysconfig.get_python_inc())
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_text_file.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_text_file.py
new file mode 100644
index 0000000..f511156
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_text_file.py
@@ -0,0 +1,127 @@
+"""Tests for distutils.text_file."""
+
+from distutils.tests import support
+from distutils.text_file import TextFile
+
+import jaraco.path
+import path
+
+TEST_DATA = """# test file
+
+line 3 \\
+# intervening comment
+  continues on next line
+"""
+
+
+class TestTextFile(support.TempdirManager):
+    def test_class(self):
+        # old tests moved from text_file.__main__
+        # so they are really called by the buildbots
+
+        # result 1: no fancy options
+        result1 = [
+            '# test file\n',
+            '\n',
+            'line 3 \\\n',
+            '# intervening comment\n',
+            '  continues on next line\n',
+        ]
+
+        # result 2: just strip comments
+        result2 = ["\n", "line 3 \\\n", "  continues on next line\n"]
+
+        # result 3: just strip blank lines
+        result3 = [
+            "# test file\n",
+            "line 3 \\\n",
+            "# intervening comment\n",
+            "  continues on next line\n",
+        ]
+
+        # result 4: default, strip comments, blank lines,
+        # and trailing whitespace
+        result4 = ["line 3 \\", "  continues on next line"]
+
+        # result 5: strip comments and blanks, plus join lines (but don't
+        # "collapse" joined lines
+        result5 = ["line 3   continues on next line"]
+
+        # result 6: strip comments and blanks, plus join lines (and
+        # "collapse" joined lines
+        result6 = ["line 3 continues on next line"]
+
+        def test_input(count, description, file, expected_result):
+            result = file.readlines()
+            assert result == expected_result
+
+        tmp_path = path.Path(self.mkdtemp())
+        filename = tmp_path / 'test.txt'
+        jaraco.path.build({filename.name: TEST_DATA}, tmp_path)
+
+        in_file = TextFile(
+            filename,
+            strip_comments=False,
+            skip_blanks=False,
+            lstrip_ws=False,
+            rstrip_ws=False,
+        )
+        try:
+            test_input(1, "no processing", in_file, result1)
+        finally:
+            in_file.close()
+
+        in_file = TextFile(
+            filename,
+            strip_comments=True,
+            skip_blanks=False,
+            lstrip_ws=False,
+            rstrip_ws=False,
+        )
+        try:
+            test_input(2, "strip comments", in_file, result2)
+        finally:
+            in_file.close()
+
+        in_file = TextFile(
+            filename,
+            strip_comments=False,
+            skip_blanks=True,
+            lstrip_ws=False,
+            rstrip_ws=False,
+        )
+        try:
+            test_input(3, "strip blanks", in_file, result3)
+        finally:
+            in_file.close()
+
+        in_file = TextFile(filename)
+        try:
+            test_input(4, "default processing", in_file, result4)
+        finally:
+            in_file.close()
+
+        in_file = TextFile(
+            filename,
+            strip_comments=True,
+            skip_blanks=True,
+            join_lines=True,
+            rstrip_ws=True,
+        )
+        try:
+            test_input(5, "join lines without collapsing", in_file, result5)
+        finally:
+            in_file.close()
+
+        in_file = TextFile(
+            filename,
+            strip_comments=True,
+            skip_blanks=True,
+            join_lines=True,
+            rstrip_ws=True,
+            collapse_join=True,
+        )
+        try:
+            test_input(6, "join lines with collapsing", in_file, result6)
+        finally:
+            in_file.close()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_util.py
new file mode 100644
index 0000000..00c9743
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_util.py
@@ -0,0 +1,243 @@
+"""Tests for distutils.util."""
+
+import email
+import email.generator
+import email.policy
+import io
+import os
+import pathlib
+import sys
+import sysconfig as stdlib_sysconfig
+import unittest.mock as mock
+from copy import copy
+from distutils import sysconfig, util
+from distutils.errors import DistutilsByteCompileError, DistutilsPlatformError
+from distutils.util import (
+    byte_compile,
+    change_root,
+    check_environ,
+    convert_path,
+    get_host_platform,
+    get_platform,
+    grok_environment_error,
+    rfc822_escape,
+    split_quoted,
+    strtobool,
+)
+
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def environment(monkeypatch):
+    monkeypatch.setattr(os, 'name', os.name)
+    monkeypatch.setattr(sys, 'platform', sys.platform)
+    monkeypatch.setattr(sys, 'version', sys.version)
+    monkeypatch.setattr(os, 'sep', os.sep)
+    monkeypatch.setattr(os.path, 'join', os.path.join)
+    monkeypatch.setattr(os.path, 'isabs', os.path.isabs)
+    monkeypatch.setattr(os.path, 'splitdrive', os.path.splitdrive)
+    monkeypatch.setattr(sysconfig, '_config_vars', copy(sysconfig._config_vars))
+
+
+@pytest.mark.usefixtures('save_env')
+class TestUtil:
+    def test_get_host_platform(self):
+        with mock.patch('os.name', 'nt'):
+            with mock.patch('sys.version', '... [... (ARM64)]'):
+                assert get_host_platform() == 'win-arm64'
+            with mock.patch('sys.version', '... [... (ARM)]'):
+                assert get_host_platform() == 'win-arm32'
+
+        with mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
+            assert get_host_platform() == stdlib_sysconfig.get_platform()
+
+    def test_get_platform(self):
+        with mock.patch('os.name', 'nt'):
+            with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
+                assert get_platform() == 'win32'
+            with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
+                assert get_platform() == 'win-amd64'
+            with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
+                assert get_platform() == 'win-arm32'
+            with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}):
+                assert get_platform() == 'win-arm64'
+
+    def test_convert_path(self):
+        expected = os.sep.join(('', 'home', 'to', 'my', 'stuff'))
+        assert convert_path('/home/to/my/stuff') == expected
+        assert convert_path(pathlib.Path('/home/to/my/stuff')) == expected
+        assert convert_path('.') == os.curdir
+
+    def test_change_root(self):
+        # linux/mac
+        os.name = 'posix'
+
+        def _isabs(path):
+            return path[0] == '/'
+
+        os.path.isabs = _isabs
+
+        def _join(*path):
+            return '/'.join(path)
+
+        os.path.join = _join
+
+        assert change_root('/root', '/old/its/here') == '/root/old/its/here'
+        assert change_root('/root', 'its/here') == '/root/its/here'
+
+        # windows
+        os.name = 'nt'
+        os.sep = '\\'
+
+        def _isabs(path):
+            return path.startswith('c:\\')
+
+        os.path.isabs = _isabs
+
+        def _splitdrive(path):
+            if path.startswith('c:'):
+                return ('', path.replace('c:', ''))
+            return ('', path)
+
+        os.path.splitdrive = _splitdrive
+
+        def _join(*path):
+            return '\\'.join(path)
+
+        os.path.join = _join
+
+        assert (
+            change_root('c:\\root', 'c:\\old\\its\\here') == 'c:\\root\\old\\its\\here'
+        )
+        assert change_root('c:\\root', 'its\\here') == 'c:\\root\\its\\here'
+
+        # BugsBunny os (it's a great os)
+        os.name = 'BugsBunny'
+        with pytest.raises(DistutilsPlatformError):
+            change_root('c:\\root', 'its\\here')
+
+        # XXX platforms to be covered: mac
+
+    def test_check_environ(self):
+        util.check_environ.cache_clear()
+        os.environ.pop('HOME', None)
+
+        check_environ()
+
+        assert os.environ['PLAT'] == get_platform()
+
+    @pytest.mark.skipif("os.name != 'posix'")
+    def test_check_environ_getpwuid(self):
+        util.check_environ.cache_clear()
+        os.environ.pop('HOME', None)
+
+        import pwd
+
+        # only set pw_dir field, other fields are not used
+        result = pwd.struct_passwd((
+            None,
+            None,
+            None,
+            None,
+            None,
+            '/home/distutils',
+            None,
+        ))
+        with mock.patch.object(pwd, 'getpwuid', return_value=result):
+            check_environ()
+            assert os.environ['HOME'] == '/home/distutils'
+
+        util.check_environ.cache_clear()
+        os.environ.pop('HOME', None)
+
+        # bpo-10496: Catch pwd.getpwuid() error
+        with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError):
+            check_environ()
+            assert 'HOME' not in os.environ
+
+    def test_split_quoted(self):
+        assert split_quoted('""one"" "two" \'three\' \\four') == [
+            'one',
+            'two',
+            'three',
+            'four',
+        ]
+
+    def test_strtobool(self):
+        yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1')
+        no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N')
+
+        for y in yes:
+            assert strtobool(y)
+
+        for n in no:
+            assert not strtobool(n)
+
+    indent = 8 * ' '
+
+    @pytest.mark.parametrize(
+        "given,wanted",
+        [
+            # 0x0b, 0x0c, ..., etc are also considered a line break by Python
+            ("hello\x0b\nworld\n", f"hello\x0b{indent}\n{indent}world\n{indent}"),
+            ("hello\x1eworld", f"hello\x1e{indent}world"),
+            ("", ""),
+            (
+                "I am a\npoor\nlonesome\nheader\n",
+                f"I am a\n{indent}poor\n{indent}lonesome\n{indent}header\n{indent}",
+            ),
+        ],
+    )
+    def test_rfc822_escape(self, given, wanted):
+        """
+        We want to ensure a multi-line header parses correctly.
+
+        For interoperability, the escaped value should also "round-trip" over
+        `email.generator.Generator.flatten` and `email.message_from_*`
+        (see pypa/setuptools#4033).
+
+        The main issue is that internally `email.policy.EmailPolicy` uses
+        `splitlines` which will split on some control chars. If all the new lines
+        are not prefixed with spaces, the parser will interrupt reading
+        the current header and produce an incomplete value, while
+        incorrectly interpreting the rest of the headers as part of the payload.
+        """
+        res = rfc822_escape(given)
+
+        policy = email.policy.EmailPolicy(
+            utf8=True,
+            mangle_from_=False,
+            max_line_length=0,
+        )
+        with io.StringIO() as buffer:
+            raw = f"header: {res}\nother-header: 42\n\npayload\n"
+            orig = email.message_from_string(raw)
+            email.generator.Generator(buffer, policy=policy).flatten(orig)
+            buffer.seek(0)
+            regen = email.message_from_file(buffer)
+
+        for msg in (orig, regen):
+            assert msg.get_payload() == "payload\n"
+            assert msg["other-header"] == "42"
+            # Generator may replace control chars with `\n`
+            assert set(msg["header"].splitlines()) == set(res.splitlines())
+
+        assert res == wanted
+
+    def test_dont_write_bytecode(self):
+        # makes sure byte_compile raise a DistutilsError
+        # if sys.dont_write_bytecode is True
+        old_dont_write_bytecode = sys.dont_write_bytecode
+        sys.dont_write_bytecode = True
+        try:
+            with pytest.raises(DistutilsByteCompileError):
+                byte_compile([])
+        finally:
+            sys.dont_write_bytecode = old_dont_write_bytecode
+
+    def test_grok_environment_error(self):
+        # test obsolete function to ensure backward compat (#4931)
+        exc = OSError("Unable to find batch file")
+        msg = grok_environment_error(exc)
+        assert msg == "error: Unable to find batch file"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_version.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_version.py
new file mode 100644
index 0000000..b68f097
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_version.py
@@ -0,0 +1,80 @@
+"""Tests for distutils.version."""
+
+import distutils
+from distutils.version import LooseVersion, StrictVersion
+
+import pytest
+
+
+@pytest.fixture(autouse=True)
+def suppress_deprecation():
+    with distutils.version.suppress_known_deprecation():
+        yield
+
+
+class TestVersion:
+    def test_prerelease(self):
+        version = StrictVersion('1.2.3a1')
+        assert version.version == (1, 2, 3)
+        assert version.prerelease == ('a', 1)
+        assert str(version) == '1.2.3a1'
+
+        version = StrictVersion('1.2.0')
+        assert str(version) == '1.2'
+
+    def test_cmp_strict(self):
+        versions = (
+            ('1.5.1', '1.5.2b2', -1),
+            ('161', '3.10a', ValueError),
+            ('8.02', '8.02', 0),
+            ('3.4j', '1996.07.12', ValueError),
+            ('3.2.pl0', '3.1.1.6', ValueError),
+            ('2g6', '11g', ValueError),
+            ('0.9', '2.2', -1),
+            ('1.2.1', '1.2', 1),
+            ('1.1', '1.2.2', -1),
+            ('1.2', '1.1', 1),
+            ('1.2.1', '1.2.2', -1),
+            ('1.2.2', '1.2', 1),
+            ('1.2', '1.2.2', -1),
+            ('0.4.0', '0.4', 0),
+            ('1.13++', '5.5.kw', ValueError),
+        )
+
+        for v1, v2, wanted in versions:
+            try:
+                res = StrictVersion(v1)._cmp(StrictVersion(v2))
+            except ValueError:
+                if wanted is ValueError:
+                    continue
+                else:
+                    raise AssertionError(f"cmp({v1}, {v2}) shouldn't raise ValueError")
+            assert res == wanted, f'cmp({v1}, {v2}) should be {wanted}, got {res}'
+            res = StrictVersion(v1)._cmp(v2)
+            assert res == wanted, f'cmp({v1}, {v2}) should be {wanted}, got {res}'
+            res = StrictVersion(v1)._cmp(object())
+            assert res is NotImplemented, (
+                f'cmp({v1}, {v2}) should be NotImplemented, got {res}'
+            )
+
+    def test_cmp(self):
+        versions = (
+            ('1.5.1', '1.5.2b2', -1),
+            ('161', '3.10a', 1),
+            ('8.02', '8.02', 0),
+            ('3.4j', '1996.07.12', -1),
+            ('3.2.pl0', '3.1.1.6', 1),
+            ('2g6', '11g', -1),
+            ('0.960923', '2.2beta29', -1),
+            ('1.13++', '5.5.kw', -1),
+        )
+
+        for v1, v2, wanted in versions:
+            res = LooseVersion(v1)._cmp(LooseVersion(v2))
+            assert res == wanted, f'cmp({v1}, {v2}) should be {wanted}, got {res}'
+            res = LooseVersion(v1)._cmp(v2)
+            assert res == wanted, f'cmp({v1}, {v2}) should be {wanted}, got {res}'
+            res = LooseVersion(v1)._cmp(object())
+            assert res is NotImplemented, (
+                f'cmp({v1}, {v2}) should be NotImplemented, got {res}'
+            )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_versionpredicate.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/test_versionpredicate.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/unix_compat.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/unix_compat.py
new file mode 100644
index 0000000..a5d9ee4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/tests/unix_compat.py
@@ -0,0 +1,17 @@
+import sys
+
+try:
+    import grp
+    import pwd
+except ImportError:
+    grp = pwd = None
+
+import pytest
+
+UNIX_ID_SUPPORT = grp and pwd
+UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin"
+
+require_unix_id = pytest.mark.skipif(
+    not UNIX_ID_SUPPORT, reason="Requires grp and pwd support"
+)
+require_uid_0 = pytest.mark.skipif(not UID_0_SUPPORT, reason="Requires UID 0 support")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/text_file.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/text_file.py
new file mode 100644
index 0000000..89d9048
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/text_file.py
@@ -0,0 +1,286 @@
+"""text_file
+
+provides the TextFile class, which gives an interface to text files
+that (optionally) takes care of stripping comments, ignoring blank
+lines, and joining lines with backslashes."""
+
+import sys
+
+
+class TextFile:
+    """Provides a file-like object that takes care of all the things you
+    commonly want to do when processing a text file that has some
+    line-by-line syntax: strip comments (as long as "#" is your
+    comment character), skip blank lines, join adjacent lines by
+    escaping the newline (ie. backslash at end of line), strip
+    leading and/or trailing whitespace.  All of these are optional
+    and independently controllable.
+
+    Provides a 'warn()' method so you can generate warning messages that
+    report physical line number, even if the logical line in question
+    spans multiple physical lines.  Also provides 'unreadline()' for
+    implementing line-at-a-time lookahead.
+
+    Constructor is called as:
+
+        TextFile (filename=None, file=None, **options)
+
+    It bombs (RuntimeError) if both 'filename' and 'file' are None;
+    'filename' should be a string, and 'file' a file object (or
+    something that provides 'readline()' and 'close()' methods).  It is
+    recommended that you supply at least 'filename', so that TextFile
+    can include it in warning messages.  If 'file' is not supplied,
+    TextFile creates its own using 'io.open()'.
+
+    The options are all boolean, and affect the value returned by
+    'readline()':
+      strip_comments [default: true]
+        strip from "#" to end-of-line, as well as any whitespace
+        leading up to the "#" -- unless it is escaped by a backslash
+      lstrip_ws [default: false]
+        strip leading whitespace from each line before returning it
+      rstrip_ws [default: true]
+        strip trailing whitespace (including line terminator!) from
+        each line before returning it
+      skip_blanks [default: true}
+        skip lines that are empty *after* stripping comments and
+        whitespace.  (If both lstrip_ws and rstrip_ws are false,
+        then some lines may consist of solely whitespace: these will
+        *not* be skipped, even if 'skip_blanks' is true.)
+      join_lines [default: false]
+        if a backslash is the last non-newline character on a line
+        after stripping comments and whitespace, join the following line
+        to it to form one "logical line"; if N consecutive lines end
+        with a backslash, then N+1 physical lines will be joined to
+        form one logical line.
+      collapse_join [default: false]
+        strip leading whitespace from lines that are joined to their
+        predecessor; only matters if (join_lines and not lstrip_ws)
+      errors [default: 'strict']
+        error handler used to decode the file content
+
+    Note that since 'rstrip_ws' can strip the trailing newline, the
+    semantics of 'readline()' must differ from those of the builtin file
+    object's 'readline()' method!  In particular, 'readline()' returns
+    None for end-of-file: an empty string might just be a blank line (or
+    an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is
+    not."""
+
+    default_options = {
+        'strip_comments': 1,
+        'skip_blanks': 1,
+        'lstrip_ws': 0,
+        'rstrip_ws': 1,
+        'join_lines': 0,
+        'collapse_join': 0,
+        'errors': 'strict',
+    }
+
+    def __init__(self, filename=None, file=None, **options):
+        """Construct a new TextFile object.  At least one of 'filename'
+        (a string) and 'file' (a file-like object) must be supplied.
+        They keyword argument options are described above and affect
+        the values returned by 'readline()'."""
+        if filename is None and file is None:
+            raise RuntimeError(
+                "you must supply either or both of 'filename' and 'file'"
+            )
+
+        # set values for all options -- either from client option hash
+        # or fallback to default_options
+        for opt in self.default_options.keys():
+            if opt in options:
+                setattr(self, opt, options[opt])
+            else:
+                setattr(self, opt, self.default_options[opt])
+
+        # sanity check client option hash
+        for opt in options.keys():
+            if opt not in self.default_options:
+                raise KeyError(f"invalid TextFile option '{opt}'")
+
+        if file is None:
+            self.open(filename)
+        else:
+            self.filename = filename
+            self.file = file
+            self.current_line = 0  # assuming that file is at BOF!
+
+        # 'linebuf' is a stack of lines that will be emptied before we
+        # actually read from the file; it's only populated by an
+        # 'unreadline()' operation
+        self.linebuf = []
+
+    def open(self, filename):
+        """Open a new file named 'filename'.  This overrides both the
+        'filename' and 'file' arguments to the constructor."""
+        self.filename = filename
+        self.file = open(self.filename, errors=self.errors, encoding='utf-8')
+        self.current_line = 0
+
+    def close(self):
+        """Close the current file and forget everything we know about it
+        (filename, current line number)."""
+        file = self.file
+        self.file = None
+        self.filename = None
+        self.current_line = None
+        file.close()
+
+    def gen_error(self, msg, line=None):
+        outmsg = []
+        if line is None:
+            line = self.current_line
+        outmsg.append(self.filename + ", ")
+        if isinstance(line, (list, tuple)):
+            outmsg.append("lines {}-{}: ".format(*line))
+        else:
+            outmsg.append(f"line {int(line)}: ")
+        outmsg.append(str(msg))
+        return "".join(outmsg)
+
+    def error(self, msg, line=None):
+        raise ValueError("error: " + self.gen_error(msg, line))
+
+    def warn(self, msg, line=None):
+        """Print (to stderr) a warning message tied to the current logical
+        line in the current file.  If the current logical line in the
+        file spans multiple physical lines, the warning refers to the
+        whole range, eg. "lines 3-5".  If 'line' supplied, it overrides
+        the current line number; it may be a list or tuple to indicate a
+        range of physical lines, or an integer for a single physical
+        line."""
+        sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n")
+
+    def readline(self):  # noqa: C901
+        """Read and return a single logical line from the current file (or
+        from an internal buffer if lines have previously been "unread"
+        with 'unreadline()').  If the 'join_lines' option is true, this
+        may involve reading multiple physical lines concatenated into a
+        single string.  Updates the current line number, so calling
+        'warn()' after 'readline()' emits a warning about the physical
+        line(s) just read.  Returns None on end-of-file, since the empty
+        string can occur if 'rstrip_ws' is true but 'strip_blanks' is
+        not."""
+        # If any "unread" lines waiting in 'linebuf', return the top
+        # one.  (We don't actually buffer read-ahead data -- lines only
+        # get put in 'linebuf' if the client explicitly does an
+        # 'unreadline()'.
+        if self.linebuf:
+            line = self.linebuf[-1]
+            del self.linebuf[-1]
+            return line
+
+        buildup_line = ''
+
+        while True:
+            # read the line, make it None if EOF
+            line = self.file.readline()
+            if line == '':
+                line = None
+
+            if self.strip_comments and line:
+                # Look for the first "#" in the line.  If none, never
+                # mind.  If we find one and it's the first character, or
+                # is not preceded by "\", then it starts a comment --
+                # strip the comment, strip whitespace before it, and
+                # carry on.  Otherwise, it's just an escaped "#", so
+                # unescape it (and any other escaped "#"'s that might be
+                # lurking in there) and otherwise leave the line alone.
+
+                pos = line.find("#")
+                if pos == -1:  # no "#" -- no comments
+                    pass
+
+                # It's definitely a comment -- either "#" is the first
+                # character, or it's elsewhere and unescaped.
+                elif pos == 0 or line[pos - 1] != "\\":
+                    # Have to preserve the trailing newline, because it's
+                    # the job of a later step (rstrip_ws) to remove it --
+                    # and if rstrip_ws is false, we'd better preserve it!
+                    # (NB. this means that if the final line is all comment
+                    # and has no trailing newline, we will think that it's
+                    # EOF; I think that's OK.)
+                    eol = (line[-1] == '\n') and '\n' or ''
+                    line = line[0:pos] + eol
+
+                    # If all that's left is whitespace, then skip line
+                    # *now*, before we try to join it to 'buildup_line' --
+                    # that way constructs like
+                    #   hello \\
+                    #   # comment that should be ignored
+                    #   there
+                    # result in "hello there".
+                    if line.strip() == "":
+                        continue
+                else:  # it's an escaped "#"
+                    line = line.replace("\\#", "#")
+
+            # did previous line end with a backslash? then accumulate
+            if self.join_lines and buildup_line:
+                # oops: end of file
+                if line is None:
+                    self.warn("continuation line immediately precedes end-of-file")
+                    return buildup_line
+
+                if self.collapse_join:
+                    line = line.lstrip()
+                line = buildup_line + line
+
+                # careful: pay attention to line number when incrementing it
+                if isinstance(self.current_line, list):
+                    self.current_line[1] = self.current_line[1] + 1
+                else:
+                    self.current_line = [self.current_line, self.current_line + 1]
+            # just an ordinary line, read it as usual
+            else:
+                if line is None:  # eof
+                    return None
+
+                # still have to be careful about incrementing the line number!
+                if isinstance(self.current_line, list):
+                    self.current_line = self.current_line[1] + 1
+                else:
+                    self.current_line = self.current_line + 1
+
+            # strip whitespace however the client wants (leading and
+            # trailing, or one or the other, or neither)
+            if self.lstrip_ws and self.rstrip_ws:
+                line = line.strip()
+            elif self.lstrip_ws:
+                line = line.lstrip()
+            elif self.rstrip_ws:
+                line = line.rstrip()
+
+            # blank line (whether we rstrip'ed or not)? skip to next line
+            # if appropriate
+            if line in ('', '\n') and self.skip_blanks:
+                continue
+
+            if self.join_lines:
+                if line[-1] == '\\':
+                    buildup_line = line[:-1]
+                    continue
+
+                if line[-2:] == '\\\n':
+                    buildup_line = line[0:-2] + '\n'
+                    continue
+
+            # well, I guess there's some actual content there: return it
+            return line
+
+    def readlines(self):
+        """Read and return the list of all logical lines remaining in the
+        current file."""
+        lines = []
+        while True:
+            line = self.readline()
+            if line is None:
+                return lines
+            lines.append(line)
+
+    def unreadline(self, line):
+        """Push 'line' (a string) onto an internal buffer that will be
+        checked by future 'readline()' calls.  Handy for implementing
+        a parser with line-at-a-time lookahead."""
+        self.linebuf.append(line)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/unixccompiler.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/unixccompiler.py
new file mode 100644
index 0000000..20b8ce6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/unixccompiler.py
@@ -0,0 +1,9 @@
+import importlib
+
+from .compilers.C import unix
+
+UnixCCompiler = unix.Compiler
+
+# ensure import of unixccompiler implies ccompiler imported
+# (pypa/setuptools#4871)
+importlib.import_module('distutils.ccompiler')
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/util.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/util.py
new file mode 100644
index 0000000..47bb5af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/util.py
@@ -0,0 +1,506 @@
+"""distutils.util
+
+Miscellaneous utility functions -- anything that doesn't fit into
+one of the other *util.py modules.
+"""
+
+from __future__ import annotations
+
+import functools
+import importlib.util
+import os
+import pathlib
+import re
+import string
+import subprocess
+import sys
+import sysconfig
+import tempfile
+from collections.abc import Callable, Iterable, Mapping
+from typing import TYPE_CHECKING, AnyStr
+
+from jaraco.functools import pass_none
+
+from ._log import log
+from ._modified import newer
+from .errors import DistutilsByteCompileError, DistutilsPlatformError
+from .spawn import spawn
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeVarTuple, Unpack
+
+    _Ts = TypeVarTuple("_Ts")
+
+
+def get_host_platform() -> str:
+    """
+    Return a string that identifies the current platform. Use this
+    function to distinguish platform-specific build directories and
+    platform-specific built distributions.
+    """
+
+    # This function initially exposed platforms as defined in Python 3.9
+    # even with older Python versions when distutils was split out.
+    # Now it delegates to stdlib sysconfig.
+
+    return sysconfig.get_platform()
+
+
+def get_platform() -> str:
+    if os.name == 'nt':
+        TARGET_TO_PLAT = {
+            'x86': 'win32',
+            'x64': 'win-amd64',
+            'arm': 'win-arm32',
+            'arm64': 'win-arm64',
+        }
+        target = os.environ.get('VSCMD_ARG_TGT_ARCH')
+        return TARGET_TO_PLAT.get(target) or get_host_platform()
+    return get_host_platform()
+
+
+if sys.platform == 'darwin':
+    _syscfg_macosx_ver = None  # cache the version pulled from sysconfig
+MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET'
+
+
+def _clear_cached_macosx_ver():
+    """For testing only. Do not call."""
+    global _syscfg_macosx_ver
+    _syscfg_macosx_ver = None
+
+
+def get_macosx_target_ver_from_syscfg():
+    """Get the version of macOS latched in the Python interpreter configuration.
+    Returns the version as a string or None if can't obtain one. Cached."""
+    global _syscfg_macosx_ver
+    if _syscfg_macosx_ver is None:
+        from distutils import sysconfig
+
+        ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or ''
+        if ver:
+            _syscfg_macosx_ver = ver
+    return _syscfg_macosx_ver
+
+
+def get_macosx_target_ver():
+    """Return the version of macOS for which we are building.
+
+    The target version defaults to the version in sysconfig latched at time
+    the Python interpreter was built, unless overridden by an environment
+    variable. If neither source has a value, then None is returned"""
+
+    syscfg_ver = get_macosx_target_ver_from_syscfg()
+    env_ver = os.environ.get(MACOSX_VERSION_VAR)
+
+    if env_ver:
+        # Validate overridden version against sysconfig version, if have both.
+        # Ensure that the deployment target of the build process is not less
+        # than 10.3 if the interpreter was built for 10.3 or later.  This
+        # ensures extension modules are built with correct compatibility
+        # values, specifically LDSHARED which can use
+        # '-undefined dynamic_lookup' which only works on >= 10.3.
+        if (
+            syscfg_ver
+            and split_version(syscfg_ver) >= [10, 3]
+            and split_version(env_ver) < [10, 3]
+        ):
+            my_msg = (
+                '$' + MACOSX_VERSION_VAR + ' mismatch: '
+                f'now "{env_ver}" but "{syscfg_ver}" during configure; '
+                'must use 10.3 or later'
+            )
+            raise DistutilsPlatformError(my_msg)
+        return env_ver
+    return syscfg_ver
+
+
+def split_version(s: str) -> list[int]:
+    """Convert a dot-separated string into a list of numbers for comparisons"""
+    return [int(n) for n in s.split('.')]
+
+
+@pass_none
+def convert_path(pathname: str | os.PathLike[str]) -> str:
+    r"""
+    Allow for pathlib.Path inputs, coax to a native path string.
+
+    If None is passed, will just pass it through as
+    Setuptools relies on this behavior.
+
+    >>> convert_path(None) is None
+    True
+
+    Removes empty paths.
+
+    >>> convert_path('foo/./bar').replace('\\', '/')
+    'foo/bar'
+    """
+    return os.fspath(pathlib.PurePath(pathname))
+
+
+def change_root(
+    new_root: AnyStr | os.PathLike[AnyStr], pathname: AnyStr | os.PathLike[AnyStr]
+) -> AnyStr:
+    """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.
+    """
+    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] == os.sep:
+            path = path[1:]
+        return os.path.join(new_root, path)
+
+    raise DistutilsPlatformError(f"nothing known about platform '{os.name}'")
+
+
+@functools.lru_cache
+def check_environ() -> None:
+    """Ensure that 'os.environ' has all the environment variables we
+    guarantee that users can use in config files, command-line options,
+    etc.  Currently this includes:
+      HOME - user's home directory (Unix only)
+      PLAT - description of the current platform, including hardware
+             and OS (see 'get_platform()')
+    """
+    if os.name == 'posix' and 'HOME' not in os.environ:
+        try:
+            import pwd
+
+            os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
+        except (ImportError, KeyError):
+            # bpo-10496: if the current user identifier doesn't exist in the
+            # password database, do nothing
+            pass
+
+    if 'PLAT' not in os.environ:
+        os.environ['PLAT'] = get_platform()
+
+
+def subst_vars(s, local_vars: Mapping[str, object]) -> str:
+    """
+    Perform variable substitution on 'string'.
+    Variables are indicated by format-style braces ("{var}").
+    Variable is substituted by the value found in the 'local_vars'
+    dictionary or in 'os.environ' if it's not in 'local_vars'.
+    'os.environ' is first checked/augmented to guarantee that it contains
+    certain values: see 'check_environ()'.  Raise ValueError for any
+    variables not found in either 'local_vars' or 'os.environ'.
+    """
+    check_environ()
+    lookup = dict(os.environ)
+    lookup.update((name, str(value)) for name, value in local_vars.items())
+    try:
+        return _subst_compat(s).format_map(lookup)
+    except KeyError as var:
+        raise ValueError(f"invalid variable {var}")
+
+
+def _subst_compat(s):
+    """
+    Replace shell/Perl-style variable substitution with
+    format-style. For compatibility.
+    """
+
+    def _subst(match):
+        return f'{{{match.group(1)}}}'
+
+    repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
+    if repl != s:
+        import warnings
+
+        warnings.warn(
+            "shell/Perl-style substitutions are deprecated",
+            DeprecationWarning,
+        )
+    return repl
+
+
+def grok_environment_error(exc: object, prefix: str = "error: ") -> str:
+    # Function kept for backward compatibility.
+    # Used to try clever things with EnvironmentErrors,
+    # but nowadays str(exception) produces good messages.
+    return prefix + str(exc)
+
+
+# Needed by 'split_quoted()'
+_wordchars_re = _squote_re = _dquote_re = None
+
+
+def _init_regex():
+    global _wordchars_re, _squote_re, _dquote_re
+    _wordchars_re = re.compile(rf'[^\\\'\"{string.whitespace} ]*')
+    _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
+    _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
+
+
+def split_quoted(s: str) -> list[str]:
+    """Split a string up according to Unix shell-like rules for quotes and
+    backslashes.  In short: words are delimited by spaces, as long as those
+    spaces are not escaped by a backslash, or inside a quoted string.
+    Single and double quotes are equivalent, and the quote characters can
+    be backslash-escaped.  The backslash is stripped from any two-character
+    escape sequence, leaving only the escaped character.  The quote
+    characters are stripped from any quoted string.  Returns a list of
+    words.
+    """
+
+    # This is a nice algorithm for splitting up a single string, since it
+    # doesn't require character-by-character examination.  It was a little
+    # bit of a brain-bender to get it working right, though...
+    if _wordchars_re is None:
+        _init_regex()
+
+    s = s.strip()
+    words = []
+    pos = 0
+
+    while s:
+        m = _wordchars_re.match(s, pos)
+        end = m.end()
+        if end == len(s):
+            words.append(s[:end])
+            break
+
+        if s[end] in string.whitespace:
+            # unescaped, unquoted whitespace: now
+            # we definitely have a word delimiter
+            words.append(s[:end])
+            s = s[end:].lstrip()
+            pos = 0
+
+        elif s[end] == '\\':
+            # preserve whatever is being escaped;
+            # will become part of the current word
+            s = s[:end] + s[end + 1 :]
+            pos = end + 1
+
+        else:
+            if s[end] == "'":  # slurp singly-quoted string
+                m = _squote_re.match(s, end)
+            elif s[end] == '"':  # slurp doubly-quoted string
+                m = _dquote_re.match(s, end)
+            else:
+                raise RuntimeError(f"this can't happen (bad char '{s[end]}')")
+
+            if m is None:
+                raise ValueError(f"bad string (mismatched {s[end]} quotes?)")
+
+            (beg, end) = m.span()
+            s = s[:beg] + s[beg + 1 : end - 1] + s[end:]
+            pos = m.end() - 2
+
+        if pos >= len(s):
+            words.append(s)
+            break
+
+    return words
+
+
+# split_quoted ()
+
+
+def execute(
+    func: Callable[[Unpack[_Ts]], object],
+    args: tuple[Unpack[_Ts]],
+    msg: object = None,
+    verbose: bool = False,
+) -> None:
+    """
+    Perform some action that affects the outside world (e.g. by
+    writing to the filesystem). Was previously used to deal with
+    "dry run" operations, but now runs unconditionally.
+    """
+    if msg is None:
+        msg = f"{func.__name__}{args!r}"
+        if msg[-2:] == ',)':  # correct for singleton tuple
+            msg = msg[0:-2] + ')'
+
+    log.info(msg)
+    func(*args)
+
+
+def strtobool(val: str) -> bool:
+    """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 True
+    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
+        return False
+    else:
+        raise ValueError(f"invalid truth value {val!r}")
+
+
+def byte_compile(  # noqa: C901
+    py_files: Iterable[str],
+    optimize: int = 0,
+    force: bool = False,
+    prefix: str | None = None,
+    base_dir: str | None = None,
+    verbose: bool = True,
+    direct: bool | None = None,
+) -> None:
+    """Byte-compile a collection of Python source files to .pyc
+    files in a __pycache__ subdirectory.  'py_files' is a list
+    of files to compile; any files that don't end in ".py" are silently
+    skipped.  'optimize' must be one of the following:
+      0 - don't optimize
+      1 - normal optimization (like "python -O")
+      2 - extra optimization (like "python -OO")
+    If 'force' is true, all files are recompiled regardless of
+    timestamps.
+
+    The source filename encoded in each bytecode file defaults to the
+    filenames listed in 'py_files'; you can modify these with 'prefix' and
+    'basedir'.  'prefix' is a string that will be stripped off of each
+    source filename, and 'base_dir' is a directory name that will be
+    prepended (after 'prefix' is stripped).  You can supply either or both
+    (or neither) of 'prefix' and 'base_dir', as you wish.
+
+    Byte-compilation is either done directly in this interpreter process
+    with the standard py_compile module, or indirectly by writing a
+    temporary script and executing it.  Normally, you should let
+    'byte_compile()' figure out to use direct compilation or not (see
+    the source for details).  The 'direct' flag is used by the script
+    generated in indirect mode; unless you know what you're doing, leave
+    it set to None.
+    """
+
+    # nothing is done if sys.dont_write_bytecode is True
+    if sys.dont_write_bytecode:
+        raise DistutilsByteCompileError('byte-compiling is disabled.')
+
+    # First, if the caller didn't force us into direct or indirect mode,
+    # figure out which mode we should be in.  We take a conservative
+    # approach: choose direct mode *only* if the current interpreter is
+    # in debug mode and optimize is 0.  If we're not in debug mode (-O
+    # or -OO), we don't know which level of optimization this
+    # interpreter is running with, so we can't do direct
+    # byte-compilation and be certain that it's the right thing.  Thus,
+    # always compile indirectly if the current interpreter is in either
+    # optimize mode, or if either optimization level was requested by
+    # the caller.
+    if direct is None:
+        direct = __debug__ and optimize == 0
+
+    # "Indirect" byte-compilation: write a temporary script and then
+    # run it with the appropriate flags.
+    if not direct:
+        (script_fd, script_name) = tempfile.mkstemp(".py")
+        log.info("writing byte-compilation script '%s'", script_name)
+        script = os.fdopen(script_fd, "w", encoding='utf-8')
+
+        with script:
+            script.write(
+                """\
+from distutils.util import byte_compile
+files = [
+"""
+            )
+
+            # XXX would be nice to write absolute filenames, just for
+            # safety's sake (script should be more robust in the face of
+            # chdir'ing before running it).  But this requires abspath'ing
+            # 'prefix' as well, and that breaks the hack in build_lib's
+            # 'byte_compile()' method that carefully tacks on a trailing
+            # slash (os.sep really) to make sure the prefix here is "just
+            # right".  This whole prefix business is rather delicate -- the
+            # problem is that it's really a directory, but I'm treating it
+            # as a dumb string, so trailing slashes and so forth matter.
+
+            script.write(",\n".join(map(repr, py_files)) + "]\n")
+            script.write(
+                f"""
+byte_compile(files, optimize={optimize!r}, force={force!r},
+         prefix={prefix!r}, base_dir={base_dir!r},
+         verbose={verbose!r},
+         direct=True)
+"""
+            )
+
+        cmd = [sys.executable]
+        cmd.extend(subprocess._optim_args_from_interpreter_flags())
+        cmd.append(script_name)
+        spawn(cmd)
+        execute(os.remove, (script_name,), f"removing {script_name}")
+
+    # "Direct" byte-compilation: use the py_compile module to compile
+    # right here, right now.  Note that the script generated in indirect
+    # mode simply calls 'byte_compile()' in direct mode, a weird sort of
+    # cross-process recursion.  Hey, it works!
+    else:
+        from py_compile import compile
+
+        for file in py_files:
+            if file[-3:] != ".py":
+                # This lets us be lazy and not filter filenames in
+                # the "install_lib" command.
+                continue
+
+            # Terminology from the py_compile module:
+            #   cfile - byte-compiled file
+            #   dfile - purported source filename (same as 'file' by default)
+            if optimize >= 0:
+                opt = '' if optimize == 0 else optimize
+                cfile = importlib.util.cache_from_source(file, optimization=opt)
+            else:
+                cfile = importlib.util.cache_from_source(file)
+            dfile = file
+            if prefix:
+                if file[: len(prefix)] != prefix:
+                    raise ValueError(
+                        f"invalid prefix: filename {file!r} doesn't start with {prefix!r}"
+                    )
+                dfile = dfile[len(prefix) :]
+            if base_dir:
+                dfile = os.path.join(base_dir, dfile)
+
+            cfile_base = os.path.basename(cfile)
+            if direct:
+                if force or newer(file, cfile):
+                    log.info("byte-compiling %s to %s", file, cfile_base)
+                    compile(file, cfile, dfile)
+                else:
+                    log.debug("skipping byte-compilation of %s to %s", file, cfile_base)
+
+
+def rfc822_escape(header: str) -> str:
+    """Return a version of the string escaped for inclusion in an
+    RFC-822 header, by ensuring there are 8 spaces space after each newline.
+    """
+    indent = 8 * " "
+    lines = header.splitlines(keepends=True)
+
+    # Emulate the behaviour of `str.split`
+    # (the terminal line break in `splitlines` does not result in an extra line):
+    ends_in_newline = lines and lines[-1].splitlines()[0] != lines[-1]
+    suffix = indent if ends_in_newline else ""
+
+    return indent.join(lines) + suffix
+
+
+def is_mingw() -> bool:
+    """Returns True if the current platform is mingw.
+
+    Python compiled with Mingw-w64 has sys.platform == 'win32' and
+    get_platform() starts with 'mingw'.
+    """
+    return sys.platform == 'win32' and get_platform().startswith('mingw')
+
+
+def is_freethreaded():
+    """Return True if the Python interpreter is built with free threading support."""
+    return bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/version.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/version.py
new file mode 100644
index 0000000..2223ee9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/version.py
@@ -0,0 +1,348 @@
+#
+# distutils/version.py
+#
+# Implements multiple version numbering conventions for the
+# Python Module Distribution Utilities.
+#
+# $Id$
+#
+
+"""Provides classes to represent module version numbers (one class for
+each style of version numbering).  There are currently two such classes
+implemented: StrictVersion and LooseVersion.
+
+Every version number class implements the following interface:
+  * the 'parse' method takes a string and parses it to some internal
+    representation; if the string is an invalid version number,
+    'parse' raises a ValueError exception
+  * the class constructor takes an optional string argument which,
+    if supplied, is passed to 'parse'
+  * __str__ reconstructs the string that was passed to 'parse' (or
+    an equivalent string -- ie. one that will generate an equivalent
+    version number instance)
+  * __repr__ generates Python code to recreate the version number instance
+  * _cmp compares the current instance with either another instance
+    of the same class or a string (which will be parsed to an instance
+    of the same class, thus must follow the same rules)
+"""
+
+import contextlib
+import re
+import warnings
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings(record=True) as ctx:
+        warnings.filterwarnings(
+            action='default',
+            category=DeprecationWarning,
+            message="distutils Version classes are deprecated.",
+        )
+        yield ctx
+
+
+class Version:
+    """Abstract base class for version numbering classes.  Just provides
+    constructor (__init__) and reproducer (__repr__), because those
+    seem to be the same for all version numbering classes; and route
+    rich comparisons to _cmp.
+    """
+
+    def __init__(self, vstring=None):
+        if vstring:
+            self.parse(vstring)
+        warnings.warn(
+            "distutils Version classes are deprecated. Use packaging.version instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+    def __repr__(self):
+        return f"{self.__class__.__name__} ('{self}')"
+
+    def __eq__(self, other):
+        c = self._cmp(other)
+        if c is NotImplemented:
+            return c
+        return c == 0
+
+    def __lt__(self, other):
+        c = self._cmp(other)
+        if c is NotImplemented:
+            return c
+        return c < 0
+
+    def __le__(self, other):
+        c = self._cmp(other)
+        if c is NotImplemented:
+            return c
+        return c <= 0
+
+    def __gt__(self, other):
+        c = self._cmp(other)
+        if c is NotImplemented:
+            return c
+        return c > 0
+
+    def __ge__(self, other):
+        c = self._cmp(other)
+        if c is NotImplemented:
+            return c
+        return c >= 0
+
+
+# Interface for version-number classes -- must be implemented
+# by the following classes (the concrete ones -- Version should
+# be treated as an abstract class).
+#    __init__ (string) - create and take same action as 'parse'
+#                        (string parameter is optional)
+#    parse (string)    - convert a string representation to whatever
+#                        internal representation is appropriate for
+#                        this style of version numbering
+#    __str__ (self)    - convert back to a string; should be very similar
+#                        (if not identical to) the string supplied to parse
+#    __repr__ (self)   - generate Python code to recreate
+#                        the instance
+#    _cmp (self, other) - compare two version numbers ('other' may
+#                        be an unparsed version string, or another
+#                        instance of your version class)
+
+
+class StrictVersion(Version):
+    """Version numbering for anal retentives and software idealists.
+    Implements the standard interface for version number classes as
+    described above.  A version number consists of two or three
+    dot-separated numeric components, with an optional "pre-release" tag
+    on the end.  The pre-release tag consists of the letter 'a' or 'b'
+    followed by a number.  If the numeric components of two version
+    numbers are equal, then one with a pre-release tag will always
+    be deemed earlier (lesser) than one without.
+
+    The following are valid version numbers (shown in the order that
+    would be obtained by sorting according to the supplied cmp function):
+
+        0.4       0.4.0  (these two are equivalent)
+        0.4.1
+        0.5a1
+        0.5b3
+        0.5
+        0.9.6
+        1.0
+        1.0.4a3
+        1.0.4b1
+        1.0.4
+
+    The following are examples of invalid version numbers:
+
+        1
+        2.7.2.2
+        1.3.a4
+        1.3pl1
+        1.3c4
+
+    The rationale for this version numbering system will be explained
+    in the distutils documentation.
+    """
+
+    version_re = re.compile(
+        r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE | re.ASCII
+    )
+
+    def parse(self, vstring):
+        match = self.version_re.match(vstring)
+        if not match:
+            raise ValueError(f"invalid version number '{vstring}'")
+
+        (major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6)
+
+        if patch:
+            self.version = tuple(map(int, [major, minor, patch]))
+        else:
+            self.version = tuple(map(int, [major, minor])) + (0,)
+
+        if prerelease:
+            self.prerelease = (prerelease[0], int(prerelease_num))
+        else:
+            self.prerelease = None
+
+    def __str__(self):
+        if self.version[2] == 0:
+            vstring = '.'.join(map(str, self.version[0:2]))
+        else:
+            vstring = '.'.join(map(str, self.version))
+
+        if self.prerelease:
+            vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
+
+        return vstring
+
+    def _cmp(self, other):
+        if isinstance(other, str):
+            with suppress_known_deprecation():
+                other = StrictVersion(other)
+        elif not isinstance(other, StrictVersion):
+            return NotImplemented
+
+        if self.version == other.version:
+            # versions match; pre-release drives the comparison
+            return self._cmp_prerelease(other)
+
+        return -1 if self.version < other.version else 1
+
+    def _cmp_prerelease(self, other):
+        """
+        case 1: self has prerelease, other doesn't; other is greater
+        case 2: self doesn't have prerelease, other does: self is greater
+        case 3: both or neither have prerelease: compare them!
+        """
+        if self.prerelease and not other.prerelease:
+            return -1
+        elif not self.prerelease and other.prerelease:
+            return 1
+
+        if self.prerelease == other.prerelease:
+            return 0
+        elif self.prerelease < other.prerelease:
+            return -1
+        else:
+            return 1
+
+
+# end class StrictVersion
+
+
+# The rules according to Greg Stein:
+# 1) a version number has 1 or more numbers separated by a period or by
+#    sequences of letters. If only periods, then these are compared
+#    left-to-right to determine an ordering.
+# 2) sequences of letters are part of the tuple for comparison and are
+#    compared lexicographically
+# 3) recognize the numeric components may have leading zeroes
+#
+# The LooseVersion class below implements these rules: a version number
+# string is split up into a tuple of integer and string components, and
+# comparison is a simple tuple comparison.  This means that version
+# numbers behave in a predictable and obvious way, but a way that might
+# not necessarily be how people *want* version numbers to behave.  There
+# wouldn't be a problem if people could stick to purely numeric version
+# numbers: just split on period and compare the numbers as tuples.
+# However, people insist on putting letters into their version numbers;
+# the most common purpose seems to be:
+#   - indicating a "pre-release" version
+#     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
+#   - indicating a post-release patch ('p', 'pl', 'patch')
+# but of course this can't cover all version number schemes, and there's
+# no way to know what a programmer means without asking him.
+#
+# The problem is what to do with letters (and other non-numeric
+# characters) in a version number.  The current implementation does the
+# obvious and predictable thing: keep them as strings and compare
+# lexically within a tuple comparison.  This has the desired effect if
+# an appended letter sequence implies something "post-release":
+# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
+#
+# However, if letters in a version number imply a pre-release version,
+# the "obvious" thing isn't correct.  Eg. you would expect that
+# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
+# implemented here, this just isn't so.
+#
+# Two possible solutions come to mind.  The first is to tie the
+# comparison algorithm to a particular set of semantic rules, as has
+# been done in the StrictVersion class above.  This works great as long
+# as everyone can go along with bondage and discipline.  Hopefully a
+# (large) subset of Python module programmers will agree that the
+# particular flavour of bondage and discipline provided by StrictVersion
+# provides enough benefit to be worth using, and will submit their
+# version numbering scheme to its domination.  The free-thinking
+# anarchists in the lot will never give in, though, and something needs
+# to be done to accommodate them.
+#
+# Perhaps a "moderately strict" version class could be implemented that
+# lets almost anything slide (syntactically), and makes some heuristic
+# assumptions about non-digits in version number strings.  This could
+# sink into special-case-hell, though; if I was as talented and
+# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
+# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
+# just as happy dealing with things like "2g6" and "1.13++".  I don't
+# think I'm smart enough to do it right though.
+#
+# In any case, I've coded the test suite for this module (see
+# ../test/test_version.py) specifically to fail on things like comparing
+# "1.2a2" and "1.2".  That's not because the *code* is doing anything
+# wrong, it's because the simple, obvious design doesn't match my
+# complicated, hairy expectations for real-world version numbers.  It
+# would be a snap to fix the test suite to say, "Yep, LooseVersion does
+# the Right Thing" (ie. the code matches the conception).  But I'd rather
+# have a conception that matches common notions about version numbers.
+
+
+class LooseVersion(Version):
+    """Version numbering for anarchists and software realists.
+    Implements the standard interface for version number classes as
+    described above.  A version number consists of a series of numbers,
+    separated by either periods or strings of letters.  When comparing
+    version numbers, the numeric components will be compared
+    numerically, and the alphabetic components lexically.  The following
+    are all valid version numbers, in no particular order:
+
+        1.5.1
+        1.5.2b2
+        161
+        3.10a
+        8.02
+        3.4j
+        1996.07.12
+        3.2.pl0
+        3.1.1.6
+        2g6
+        11g
+        0.960923
+        2.2beta29
+        1.13++
+        5.5.kw
+        2.0b1pl0
+
+    In fact, there is no such thing as an invalid version number under
+    this scheme; the rules for comparison are simple and predictable,
+    but may not always give the results you want (for some definition
+    of "want").
+    """
+
+    component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
+
+    def parse(self, vstring):
+        # I've given up on thinking I can reconstruct the version string
+        # from the parsed tuple -- so I just store the string here for
+        # use by __str__
+        self.vstring = vstring
+        components = [x for x in self.component_re.split(vstring) if x and x != '.']
+        for i, obj in enumerate(components):
+            try:
+                components[i] = int(obj)
+            except ValueError:
+                pass
+
+        self.version = components
+
+    def __str__(self):
+        return self.vstring
+
+    def __repr__(self):
+        return f"LooseVersion ('{self}')"
+
+    def _cmp(self, other):
+        if isinstance(other, str):
+            other = LooseVersion(other)
+        elif not isinstance(other, LooseVersion):
+            return NotImplemented
+
+        if self.version == other.version:
+            return 0
+        if self.version < other.version:
+            return -1
+        if self.version > other.version:
+            return 1
+
+
+# end class LooseVersion
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/versionpredicate.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/versionpredicate.py
new file mode 100644
index 0000000..fe31b0e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/versionpredicate.py
@@ -0,0 +1,175 @@
+"""Module for parsing and testing package version predicate strings."""
+
+import operator
+import re
+
+from . import version
+
+re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", re.ASCII)
+# (package) (rest)
+
+re_paren = re.compile(r"^\s*\((.*)\)\s*$")  # (list) inside of parentheses
+re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$")
+# (comp) (version)
+
+
+def splitUp(pred):
+    """Parse a single version comparison.
+
+    Return (comparison string, StrictVersion)
+    """
+    res = re_splitComparison.match(pred)
+    if not res:
+        raise ValueError(f"bad package restriction syntax: {pred!r}")
+    comp, verStr = res.groups()
+    with version.suppress_known_deprecation():
+        other = version.StrictVersion(verStr)
+    return (comp, other)
+
+
+compmap = {
+    "<": operator.lt,
+    "<=": operator.le,
+    "==": operator.eq,
+    ">": operator.gt,
+    ">=": operator.ge,
+    "!=": operator.ne,
+}
+
+
+class VersionPredicate:
+    """Parse and test package version predicates.
+
+    >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)')
+
+    The `name` attribute provides the full dotted name that is given::
+
+    >>> v.name
+    'pyepat.abc'
+
+    The str() of a `VersionPredicate` provides a normalized
+    human-readable version of the expression::
+
+    >>> print(v)
+    pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3)
+
+    The `satisfied_by()` method can be used to determine with a given
+    version number is included in the set described by the version
+    restrictions::
+
+    >>> v.satisfied_by('1.1')
+    True
+    >>> v.satisfied_by('1.4')
+    True
+    >>> v.satisfied_by('1.0')
+    False
+    >>> v.satisfied_by('4444.4')
+    False
+    >>> v.satisfied_by('1555.1b3')
+    False
+
+    `VersionPredicate` is flexible in accepting extra whitespace::
+
+    >>> v = VersionPredicate(' pat( ==  0.1  )  ')
+    >>> v.name
+    'pat'
+    >>> v.satisfied_by('0.1')
+    True
+    >>> v.satisfied_by('0.2')
+    False
+
+    If any version numbers passed in do not conform to the
+    restrictions of `StrictVersion`, a `ValueError` is raised::
+
+    >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)')
+    Traceback (most recent call last):
+      ...
+    ValueError: invalid version number '1.2zb3'
+
+    It the module or package name given does not conform to what's
+    allowed as a legal module or package name, `ValueError` is
+    raised::
+
+    >>> v = VersionPredicate('foo-bar')
+    Traceback (most recent call last):
+      ...
+    ValueError: expected parenthesized list: '-bar'
+
+    >>> v = VersionPredicate('foo bar (12.21)')
+    Traceback (most recent call last):
+      ...
+    ValueError: expected parenthesized list: 'bar (12.21)'
+
+    """
+
+    def __init__(self, versionPredicateStr):
+        """Parse a version predicate string."""
+        # Fields:
+        #    name:  package name
+        #    pred:  list of (comparison string, StrictVersion)
+
+        versionPredicateStr = versionPredicateStr.strip()
+        if not versionPredicateStr:
+            raise ValueError("empty package restriction")
+        match = re_validPackage.match(versionPredicateStr)
+        if not match:
+            raise ValueError(f"bad package name in {versionPredicateStr!r}")
+        self.name, paren = match.groups()
+        paren = paren.strip()
+        if paren:
+            match = re_paren.match(paren)
+            if not match:
+                raise ValueError(f"expected parenthesized list: {paren!r}")
+            str = match.groups()[0]
+            self.pred = [splitUp(aPred) for aPred in str.split(",")]
+            if not self.pred:
+                raise ValueError(f"empty parenthesized list in {versionPredicateStr!r}")
+        else:
+            self.pred = []
+
+    def __str__(self):
+        if self.pred:
+            seq = [cond + " " + str(ver) for cond, ver in self.pred]
+            return self.name + " (" + ", ".join(seq) + ")"
+        else:
+            return self.name
+
+    def satisfied_by(self, version):
+        """True if version is compatible with all the predicates in self.
+        The parameter version must be acceptable to the StrictVersion
+        constructor.  It may be either a string or StrictVersion.
+        """
+        for cond, ver in self.pred:
+            if not compmap[cond](version, ver):
+                return False
+        return True
+
+
+_provision_rx = None
+
+
+def split_provision(value):
+    """Return the name and optional version number of a provision.
+
+    The version number, if given, will be returned as a `StrictVersion`
+    instance, otherwise it will be `None`.
+
+    >>> split_provision('mypkg')
+    ('mypkg', None)
+    >>> split_provision(' mypkg( 1.2 ) ')
+    ('mypkg', StrictVersion ('1.2'))
+    """
+    global _provision_rx
+    if _provision_rx is None:
+        _provision_rx = re.compile(
+            r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", re.ASCII
+        )
+    value = value.strip()
+    m = _provision_rx.match(value)
+    if not m:
+        raise ValueError(f"illegal provides specification: {value!r}")
+    ver = m.group(2) or None
+    if ver:
+        with version.suppress_known_deprecation():
+            ver = version.StrictVersion(ver)
+    return m.group(1), ver
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/zosccompiler.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/zosccompiler.py
new file mode 100644
index 0000000..e49630a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/zosccompiler.py
@@ -0,0 +1,3 @@
+from .compilers.C import zos
+
+zOSCCompiler = zos.Compiler
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py b/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py
new file mode 100644
index 0000000..cd5dd2c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py
@@ -0,0 +1,94 @@
+import functools
+import itertools
+import operator
+
+from jaraco.functools import pass_none
+from jaraco.text import yield_lines
+from more_itertools import consume
+
+from ._importlib import metadata
+from ._itertools import ensure_unique
+from .errors import OptionError
+
+
+def ensure_valid(ep):
+    """
+    Exercise one of the dynamic properties to trigger
+    the pattern match.
+
+    This function is deprecated in favor of importlib_metadata 8.7 and
+    Python 3.14 importlib.metadata, which validates entry points on
+    construction.
+    """
+    try:
+        ep.extras
+    except (AttributeError, AssertionError) as ex:
+        # Why both? See https://github.com/python/importlib_metadata/issues/488
+        msg = (
+            f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: "
+            "https://packaging.python.org/en/latest/specifications/entry-points/"
+        )
+        raise OptionError(msg) from ex
+
+
+def load_group(value, group):
+    """
+    Given a value of an entry point or series of entry points,
+    return each as an EntryPoint.
+    """
+    # normalize to a single sequence of lines
+    lines = yield_lines(value)
+    text = f'[{group}]\n' + '\n'.join(lines)
+    return metadata.EntryPoints._from_text(text)
+
+
+def by_group_and_name(ep):
+    return ep.group, ep.name
+
+
+def validate(eps: metadata.EntryPoints):
+    """
+    Ensure entry points are unique by group and name and validate each.
+    """
+    consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name)))
+    return eps
+
+
+@functools.singledispatch
+def load(eps):
+    """
+    Given a Distribution.entry_points, produce EntryPoints.
+    """
+    groups = itertools.chain.from_iterable(
+        load_group(value, group) for group, value in eps.items()
+    )
+    return validate(metadata.EntryPoints(groups))
+
+
+@load.register(str)
+def _(eps):
+    r"""
+    >>> ep, = load('[console_scripts]\nfoo=bar')
+    >>> ep.group
+    'console_scripts'
+    >>> ep.name
+    'foo'
+    >>> ep.value
+    'bar'
+    """
+    return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps)))
+
+
+load.register(type(None), lambda x: x)
+
+
+@pass_none
+def render(eps: metadata.EntryPoints):
+    by_group = operator.attrgetter('group')
+    groups = itertools.groupby(sorted(eps, key=by_group), by_group)
+
+    return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups)
+
+
+def render_items(eps):
+    return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_imp.py b/.venv/lib/python3.12/site-packages/setuptools/_imp.py
new file mode 100644
index 0000000..f1d9f29
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_imp.py
@@ -0,0 +1,87 @@
+"""
+Re-implementation of find_module and get_frozen_object
+from the deprecated imp module.
+"""
+
+import importlib.machinery
+import importlib.util
+import os
+import tokenize
+from importlib.util import module_from_spec
+
+PY_SOURCE = 1
+PY_COMPILED = 2
+C_EXTENSION = 3
+C_BUILTIN = 6
+PY_FROZEN = 7
+
+
+def find_spec(module, paths):
+    finder = (
+        importlib.machinery.PathFinder().find_spec
+        if isinstance(paths, list)
+        else importlib.util.find_spec
+    )
+    return finder(module, paths)
+
+
+def find_module(module, paths=None):
+    """Just like 'imp.find_module()', but with package support"""
+    spec = find_spec(module, paths)
+    if spec is None:
+        raise ImportError(f"Can't find {module}")
+    if not spec.has_location and hasattr(spec, 'submodule_search_locations'):
+        spec = importlib.util.spec_from_loader('__init__.py', spec.loader)
+
+    kind = -1
+    file = None
+    static = isinstance(spec.loader, type)
+    if (
+        spec.origin == 'frozen'
+        or static
+        and issubclass(spec.loader, importlib.machinery.FrozenImporter)
+    ):
+        kind = PY_FROZEN
+        path = None  # imp compabilty
+        suffix = mode = ''  # imp compatibility
+    elif (
+        spec.origin == 'built-in'
+        or static
+        and issubclass(spec.loader, importlib.machinery.BuiltinImporter)
+    ):
+        kind = C_BUILTIN
+        path = None  # imp compabilty
+        suffix = mode = ''  # imp compatibility
+    elif spec.has_location:
+        path = spec.origin
+        suffix = os.path.splitext(path)[1]
+        mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb'
+
+        if suffix in importlib.machinery.SOURCE_SUFFIXES:
+            kind = PY_SOURCE
+            file = tokenize.open(path)
+        elif suffix in importlib.machinery.BYTECODE_SUFFIXES:
+            kind = PY_COMPILED
+            file = open(path, 'rb')
+        elif suffix in importlib.machinery.EXTENSION_SUFFIXES:
+            kind = C_EXTENSION
+
+    else:
+        path = None
+        suffix = mode = ''
+
+    return file, path, (suffix, mode, kind)
+
+
+def get_frozen_object(module, paths=None):
+    spec = find_spec(module, paths)
+    if not spec:
+        raise ImportError(f"Can't find {module}")
+    return spec.loader.get_code(module)
+
+
+def get_module(module, paths, info):
+    spec = find_spec(module, paths)
+    if not spec:
+        raise ImportError(f"Can't find {module}")
+    return module_from_spec(spec)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_importlib.py b/.venv/lib/python3.12/site-packages/setuptools/_importlib.py
new file mode 100644
index 0000000..ce0fd52
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_importlib.py
@@ -0,0 +1,9 @@
+import sys
+
+if sys.version_info < (3, 10):
+    import importlib_metadata as metadata  # pragma: no cover
+else:
+    import importlib.metadata as metadata  # noqa: F401
+
+
+import importlib.resources as resources  # noqa: F401
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_itertools.py b/.venv/lib/python3.12/site-packages/setuptools/_itertools.py
new file mode 100644
index 0000000..d6ca841
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_itertools.py
@@ -0,0 +1,23 @@
+from more_itertools import consume  # noqa: F401
+
+
+# copied from jaraco.itertools 6.1
+def ensure_unique(iterable, key=lambda x: x):
+    """
+    Wrap an iterable to raise a ValueError if non-unique values are encountered.
+
+    >>> list(ensure_unique('abc'))
+    ['a', 'b', 'c']
+    >>> consume(ensure_unique('abca'))
+    Traceback (most recent call last):
+    ...
+    ValueError: Duplicate element 'a' encountered.
+    """
+    seen = set()
+    seen_add = seen.add
+    for element in iterable:
+        k = key(element)
+        if k in seen:
+            raise ValueError(f"Duplicate element {element!r} encountered.")
+        seen_add(k)
+        yield element
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_normalization.py b/.venv/lib/python3.12/site-packages/setuptools/_normalization.py
new file mode 100644
index 0000000..6b8d4dd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_normalization.py
@@ -0,0 +1,180 @@
+"""
+Helpers for normalization as expected in wheel/sdist/module file names
+and core metadata
+"""
+
+import re
+from typing import TYPE_CHECKING
+
+import packaging
+
+# https://packaging.python.org/en/latest/specifications/core-metadata/#name
+_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE)
+_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9._-]+", re.IGNORECASE)
+_NON_ALPHANUMERIC = re.compile(r"[^A-Z0-9]+", re.IGNORECASE)
+_PEP440_FALLBACK = re.compile(
+    r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.IGNORECASE
+)
+
+
+def safe_identifier(name: str) -> str:
+    """Make a string safe to be used as Python identifier.
+    >>> safe_identifier("12abc")
+    '_12abc'
+    >>> safe_identifier("__editable__.myns.pkg-78.9.3_local")
+    '__editable___myns_pkg_78_9_3_local'
+    """
+    safe = re.sub(r'\W|^(?=\d)', '_', name)
+    assert safe.isidentifier()
+    return safe
+
+
+def safe_name(component: str) -> str:
+    """Escape a component used as a project name according to Core Metadata.
+    >>> safe_name("hello world")
+    'hello-world'
+    >>> safe_name("hello?world")
+    'hello-world'
+    >>> safe_name("hello_world")
+    'hello_world'
+    """
+    return _UNSAFE_NAME_CHARS.sub("-", component)
+
+
+def safe_version(version: str) -> str:
+    """Convert an arbitrary string into a valid version string.
+    Can still raise an ``InvalidVersion`` exception.
+    To avoid exceptions use ``best_effort_version``.
+    >>> safe_version("1988 12 25")
+    '1988.12.25'
+    >>> safe_version("v0.2.1")
+    '0.2.1'
+    >>> safe_version("v0.2?beta")
+    '0.2b0'
+    >>> safe_version("v0.2 beta")
+    '0.2b0'
+    >>> safe_version("ubuntu lts")
+    Traceback (most recent call last):
+    ...
+    packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts'
+    """
+    v = version.replace(' ', '.')
+    try:
+        return str(packaging.version.Version(v))
+    except packaging.version.InvalidVersion:
+        attempt = _UNSAFE_NAME_CHARS.sub("-", v)
+        return str(packaging.version.Version(attempt))
+
+
+def best_effort_version(version: str) -> str:
+    """Convert an arbitrary string into a version-like string.
+    Fallback when ``safe_version`` is not safe enough.
+    >>> best_effort_version("v0.2 beta")
+    '0.2b0'
+    >>> best_effort_version("ubuntu lts")
+    '0.dev0+sanitized.ubuntu.lts'
+    >>> best_effort_version("0.23ubuntu1")
+    '0.23.dev0+sanitized.ubuntu1'
+    >>> best_effort_version("0.23-")
+    '0.23.dev0+sanitized'
+    >>> best_effort_version("0.-_")
+    '0.dev0+sanitized'
+    >>> best_effort_version("42.+?1")
+    '42.dev0+sanitized.1'
+    """
+    try:
+        return safe_version(version)
+    except packaging.version.InvalidVersion:
+        v = version.replace(' ', '.')
+        match = _PEP440_FALLBACK.search(v)
+        if match:
+            safe = match["safe"]
+            rest = v[len(safe) :]
+        else:
+            safe = "0"
+            rest = version
+        safe_rest = _NON_ALPHANUMERIC.sub(".", rest).strip(".")
+        local = f"sanitized.{safe_rest}".strip(".")
+        return safe_version(f"{safe}.dev0+{local}")
+
+
+def safe_extra(extra: str) -> str:
+    """Normalize extra name according to PEP 685
+    >>> safe_extra("_FrIeNdLy-._.-bArD")
+    'friendly-bard'
+    >>> safe_extra("FrIeNdLy-._.-bArD__._-")
+    'friendly-bard'
+    """
+    return _NON_ALPHANUMERIC.sub("-", extra).strip("-").lower()
+
+
+def filename_component(value: str) -> str:
+    """Normalize each component of a filename (e.g. distribution/version part of wheel)
+    Note: ``value`` needs to be already normalized.
+    >>> filename_component("my-pkg")
+    'my_pkg'
+    """
+    return value.replace("-", "_").strip("_")
+
+
+def filename_component_broken(value: str) -> str:
+    """
+    Produce the incorrect filename component for compatibility.
+
+    See pypa/setuptools#4167 for detailed analysis.
+
+    TODO: replace this with filename_component after pip 24 is
+    nearly-ubiquitous.
+
+    >>> filename_component_broken('foo_bar-baz')
+    'foo-bar-baz'
+    """
+    return value.replace('_', '-')
+
+
+def safer_name(value: str) -> str:
+    """Like ``safe_name`` but can be used as filename component for wheel"""
+    # See bdist_wheel.safer_name
+    return (
+        # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
+        re
+        .sub(r"[-_.]+", "-", safe_name(value))
+        .lower()
+        # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
+        .replace("-", "_")
+    )
+
+
+def safer_best_effort_version(value: str) -> str:
+    """Like ``best_effort_version`` but can be used as filename component for wheel"""
+    # See bdist_wheel.safer_verion
+    # TODO: Replace with only safe_version in the future (no need for best effort)
+    return filename_component(best_effort_version(value))
+
+
+def _missing_canonicalize_license_expression(expression: str) -> str:
+    """
+    Defer import error to affect only users that actually use it
+    https://github.com/pypa/setuptools/issues/4894
+    >>> _missing_canonicalize_license_expression("a OR b")
+    Traceback (most recent call last):
+    ...
+    ImportError: ...Cannot import `packaging.licenses`...
+    """
+    raise ImportError(
+        "Cannot import `packaging.licenses`."
+        """
+        Setuptools>=77.0.0 requires "packaging>=24.2" to work properly.
+        Please make sure you have a suitable version installed.
+        """
+    )
+
+
+try:
+    from packaging.licenses import (
+        canonicalize_license_expression as _canonicalize_license_expression,
+    )
+except ImportError:  # pragma: nocover
+    if not TYPE_CHECKING:
+        # XXX: pyright is still upset even with # pyright: ignore[reportAssignmentType]
+        _canonicalize_license_expression = _missing_canonicalize_license_expression
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_path.py b/.venv/lib/python3.12/site-packages/setuptools/_path.py
new file mode 100644
index 0000000..2b78022
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_path.py
@@ -0,0 +1,93 @@
+from __future__ import annotations
+
+import contextlib
+import os
+import sys
+from typing import TYPE_CHECKING, TypeVar, Union
+
+from more_itertools import unique_everseen
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+StrPath: TypeAlias = Union[str, os.PathLike[str]]  #  Same as _typeshed.StrPath
+StrPathT = TypeVar("StrPathT", bound=Union[str, os.PathLike[str]])
+
+
+def ensure_directory(path):
+    """Ensure that the parent directory of `path` exists"""
+    dirname = os.path.dirname(path)
+    os.makedirs(dirname, exist_ok=True)
+
+
+def same_path(p1: StrPath, p2: StrPath) -> bool:
+    """Differs from os.path.samefile because it does not require paths to exist.
+    Purely string based (no comparison between i-nodes).
+    >>> same_path("a/b", "./a/b")
+    True
+    >>> same_path("a/b", "a/./b")
+    True
+    >>> same_path("a/b", "././a/b")
+    True
+    >>> same_path("a/b", "./a/b/c/..")
+    True
+    >>> same_path("a/b", "../a/b/c")
+    False
+    >>> same_path("a", "a/b")
+    False
+    """
+    return normpath(p1) == normpath(p2)
+
+
+def _cygwin_patch(filename: StrPath):  # 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 normpath(filename: StrPath) -> str:
+    """Normalize a file/dir name for comparison purposes."""
+    return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
+
+
+@contextlib.contextmanager
+def paths_on_pythonpath(paths):
+    """
+    Add the indicated paths to the head of the PYTHONPATH environment
+    variable so that subprocesses will also see the packages at
+    these paths.
+
+    Do this in a context that restores the value on exit.
+
+    >>> getfixture('monkeypatch').setenv('PYTHONPATH', 'anything')
+    >>> with paths_on_pythonpath(['foo', 'bar']):
+    ...     assert 'foo' in os.environ['PYTHONPATH']
+    ...     assert 'anything' in os.environ['PYTHONPATH']
+    >>> os.environ['PYTHONPATH']
+    'anything'
+
+    >>> getfixture('monkeypatch').delenv('PYTHONPATH')
+    >>> with paths_on_pythonpath(['foo', 'bar']):
+    ...     assert 'foo' in os.environ['PYTHONPATH']
+    >>> os.environ.get('PYTHONPATH')
+    """
+    nothing = object()
+    orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
+    current_pythonpath = os.environ.get('PYTHONPATH', '')
+    try:
+        prefix = os.pathsep.join(unique_everseen(paths))
+        to_join = filter(None, [prefix, current_pythonpath])
+        new_path = os.pathsep.join(to_join)
+        if new_path:
+            os.environ['PYTHONPATH'] = new_path
+        yield
+    finally:
+        if orig_pythonpath is nothing:
+            os.environ.pop('PYTHONPATH', None)
+        else:
+            os.environ['PYTHONPATH'] = orig_pythonpath
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_reqs.py b/.venv/lib/python3.12/site-packages/setuptools/_reqs.py
new file mode 100644
index 0000000..7be56cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_reqs.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+from collections.abc import Iterable, Iterator
+from functools import lru_cache
+from typing import TYPE_CHECKING, Callable, TypeVar, Union, overload
+
+import jaraco.text as text
+from packaging.requirements import Requirement
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+_T = TypeVar("_T")
+_StrOrIter: TypeAlias = Union[str, Iterable[str]]
+
+
+parse_req: Callable[[str], Requirement] = lru_cache()(Requirement)
+# Setuptools parses the same requirement many times
+# (e.g. first for validation than for normalisation),
+# so it might be worth to cache.
+
+
+def parse_strings(strs: _StrOrIter) -> Iterator[str]:
+    """
+    Yield requirement strings for each specification in `strs`.
+
+    `strs` must be a string, or a (possibly-nested) iterable thereof.
+    """
+    return text.join_continuation(map(text.drop_comment, text.yield_lines(strs)))
+
+
+# These overloads are only needed because of a mypy false-positive, pyright gets it right
+# https://github.com/python/mypy/issues/3737
+@overload
+def parse(strs: _StrOrIter) -> Iterator[Requirement]: ...
+@overload
+def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ...
+def parse(strs: _StrOrIter, parser: Callable[[str], _T] = parse_req) -> Iterator[_T]:  # type: ignore[assignment]
+    """
+    Parse requirements.
+    """
+    return map(parser, parse_strings(strs))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_scripts.py
new file mode 100644
index 0000000..88bf02f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_scripts.py
@@ -0,0 +1,361 @@
+from __future__ import annotations
+
+import os
+import re
+import shlex
+import shutil
+import struct
+import subprocess
+import sys
+import textwrap
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, TypedDict
+
+from ._importlib import metadata, resources
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+from .warnings import SetuptoolsWarning
+
+from distutils.command.build_scripts import first_line_re
+from distutils.util import get_platform
+
+
+class _SplitArgs(TypedDict, total=False):
+    comments: bool
+    posix: bool
+
+
+class CommandSpec(list):
+    """
+    A command spec for a #! header, specified as a list of arguments akin to
+    those passed to Popen.
+    """
+
+    options: list[str] = []
+    split_args = _SplitArgs()
+
+    @classmethod
+    def best(cls):
+        """
+        Choose the best CommandSpec class based on environmental conditions.
+        """
+        return cls
+
+    @classmethod
+    def _sys_executable(cls):
+        _default = os.path.normpath(sys.executable)
+        return os.environ.get('__PYVENV_LAUNCHER__', _default)
+
+    @classmethod
+    def from_param(cls, param: Self | str | Iterable[str] | None) -> Self:
+        """
+        Construct a CommandSpec from a parameter to build_scripts, which may
+        be None.
+        """
+        if isinstance(param, cls):
+            return param
+        if isinstance(param, str):
+            return cls.from_string(param)
+        if isinstance(param, Iterable):
+            return cls(param)
+        if param is None:
+            return cls.from_environment()
+        raise TypeError(f"Argument has an unsupported type {type(param)}")
+
+    @classmethod
+    def from_environment(cls):
+        return cls([cls._sys_executable()])
+
+    @classmethod
+    def from_string(cls, string: str) -> Self:
+        """
+        Construct a command spec from a simple string representing a command
+        line parseable by shlex.split.
+        """
+        items = shlex.split(string, **cls.split_args)
+        return cls(items)
+
+    def install_options(self, script_text: str):
+        self.options = shlex.split(self._extract_options(script_text))
+        cmdline = subprocess.list2cmdline(self)
+        if not isascii(cmdline):
+            self.options[:0] = ['-x']
+
+    @staticmethod
+    def _extract_options(orig_script):
+        """
+        Extract any options from the first line of the script.
+        """
+        first = (orig_script + '\n').splitlines()[0]
+        match = _first_line_re().match(first)
+        options = match.group(1) or '' if match else ''
+        return options.strip()
+
+    def as_header(self):
+        return self._render(self + list(self.options))
+
+    @staticmethod
+    def _strip_quotes(item):
+        _QUOTES = '"\''
+        for q in _QUOTES:
+            if item.startswith(q) and item.endswith(q):
+                return item[1:-1]
+        return item
+
+    @staticmethod
+    def _render(items):
+        cmdline = subprocess.list2cmdline(
+            CommandSpec._strip_quotes(item.strip()) for item in items
+        )
+        return '#!' + cmdline + '\n'
+
+
+class WindowsCommandSpec(CommandSpec):
+    split_args = _SplitArgs(posix=False)
+
+
+class ScriptWriter:
+    """
+    Encapsulates behavior around writing entry point scripts for console and
+    gui apps.
+    """
+
+    template = textwrap.dedent(
+        r"""
+        # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
+        import re
+        import sys
+
+        # for compatibility with easy_install; see #2198
+        __requires__ = %(spec)r
+
+        try:
+            from importlib.metadata import distribution
+        except ImportError:
+            try:
+                from importlib_metadata import distribution
+            except ImportError:
+                from pkg_resources import load_entry_point
+
+
+        def importlib_load_entry_point(spec, group, name):
+            dist_name, _, _ = spec.partition('==')
+            matches = (
+                entry_point
+                for entry_point in distribution(dist_name).entry_points
+                if entry_point.group == group and entry_point.name == name
+            )
+            return next(matches).load()
+
+
+        globals().setdefault('load_entry_point', importlib_load_entry_point)
+
+
+        if __name__ == '__main__':
+            sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
+            sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)())
+        """
+    ).lstrip()
+
+    command_spec_class = CommandSpec
+
+    @classmethod
+    def get_args(cls, dist, header=None):
+        """
+        Yield write_script() argument tuples for a distribution's
+        console_scripts and gui_scripts entry points.
+        """
+
+        # If distribution is not an importlib.metadata.Distribution, assume
+        # it's a pkg_resources.Distribution and transform it.
+        if not hasattr(dist, 'entry_points'):
+            SetuptoolsWarning.emit("Unsupported distribution encountered.")
+            dist = metadata.Distribution.at(dist.egg_info)
+
+        if header is None:
+            header = cls.get_header()
+        spec = f'{dist.name}=={dist.version}'
+        for type_ in 'console', 'gui':
+            group = f'{type_}_scripts'
+            for ep in dist.entry_points.select(group=group):
+                name = ep.name
+                cls._ensure_safe_name(ep.name)
+                script_text = cls.template % locals()
+                args = cls._get_script_args(type_, ep.name, header, script_text)
+                yield from args
+
+    @staticmethod
+    def _ensure_safe_name(name):
+        """
+        Prevent paths in *_scripts entry point names.
+        """
+        has_path_sep = re.search(r'[\\/]', name)
+        if has_path_sep:
+            raise ValueError("Path separators not allowed in script names")
+
+    @classmethod
+    def best(cls):
+        """
+        Select the best ScriptWriter for this environment.
+        """
+        if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'):
+            return WindowsScriptWriter.best()
+        else:
+            return cls
+
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        # Simply write the stub with no extension.
+        yield (name, header + script_text)
+
+    @classmethod
+    def get_header(
+        cls,
+        script_text: str = "",
+        executable: str | CommandSpec | Iterable[str] | None = None,
+    ) -> str:
+        """Create a #! line, getting options (if any) from script_text"""
+        cmd = cls.command_spec_class.best().from_param(executable)
+        cmd.install_options(script_text)
+        return cmd.as_header()
+
+
+class WindowsScriptWriter(ScriptWriter):
+    command_spec_class = WindowsCommandSpec
+
+    @classmethod
+    def best(cls):
+        """
+        Select the best ScriptWriter suitable for Windows
+        """
+        writer_lookup = dict(
+            executable=WindowsExecutableLauncherWriter,
+            natural=cls,
+        )
+        # for compatibility, use the executable launcher by default
+        launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable')
+        return writer_lookup[launcher]
+
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        "For Windows, add a .py extension"
+        ext = dict(console='.pya', gui='.pyw')[type_]
+        if ext not in os.environ['PATHEXT'].lower().split(';'):
+            msg = (
+                "{ext} not listed in PATHEXT; scripts will not be "
+                "recognized as executables."
+            ).format(**locals())
+            SetuptoolsWarning.emit(msg)
+        old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe']
+        old.remove(ext)
+        header = cls._adjust_header(type_, header)
+        blockers = [name + x for x in old]
+        yield name + ext, header + script_text, 't', blockers
+
+    @classmethod
+    def _adjust_header(cls, type_, orig_header):
+        """
+        Make sure 'pythonw' is used for gui and 'python' is used for
+        console (regardless of what sys.executable is).
+        """
+        pattern = 'pythonw.exe'
+        repl = 'python.exe'
+        if type_ == 'gui':
+            pattern, repl = repl, pattern
+        pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE)
+        new_header = pattern_ob.sub(string=orig_header, repl=repl)
+        return new_header if cls._use_header(new_header) else orig_header
+
+    @staticmethod
+    def _use_header(new_header):
+        """
+        Should _adjust_header use the replaced header?
+
+        On non-windows systems, always use. On
+        Windows systems, only use the replaced header if it resolves
+        to an executable on the system.
+        """
+        clean_header = new_header[2:-1].strip('"')
+        return sys.platform != 'win32' or shutil.which(clean_header)
+
+
+class WindowsExecutableLauncherWriter(WindowsScriptWriter):
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        """
+        For Windows, add a .py extension and an .exe launcher
+        """
+        if type_ == 'gui':
+            launcher_type = 'gui'
+            ext = '-script.pyw'
+            old = ['.pyw']
+        else:
+            launcher_type = 'cli'
+            ext = '-script.py'
+            old = ['.py', '.pyc', '.pyo']
+        hdr = cls._adjust_header(type_, header)
+        blockers = [name + x for x in old]
+        yield (name + ext, hdr + script_text, 't', blockers)
+        yield (
+            name + '.exe',
+            get_win_launcher(launcher_type),
+            'b',  # write in binary mode
+        )
+        if not is_64bit():
+            # install a manifest for the launcher to prevent Windows
+            # from detecting it as an installer (which it will for
+            #  launchers like easy_install.exe). Consider only
+            #  adding a manifest for launchers detected as installers.
+            #  See Distribute #143 for details.
+            m_name = name + '.exe.manifest'
+            yield (m_name, load_launcher_manifest(name), 't')
+
+
+def get_win_launcher(type):
+    """
+    Load the Windows launcher (executable) suitable for launching a script.
+
+    `type` should be either 'cli' or 'gui'
+
+    Returns the executable as a byte string.
+    """
+    launcher_fn = f'{type}.exe'
+    if is_64bit():
+        if get_platform() == "win-arm64":
+            launcher_fn = launcher_fn.replace(".", "-arm64.")
+        else:
+            launcher_fn = launcher_fn.replace(".", "-64.")
+    else:
+        launcher_fn = launcher_fn.replace(".", "-32.")
+    return resources.files('setuptools').joinpath(launcher_fn).read_bytes()
+
+
+def load_launcher_manifest(name):
+    res = resources.files(__name__).joinpath('launcher manifest.xml')
+    return res.read_text(encoding='utf-8') % vars()
+
+
+def _first_line_re():
+    """
+    Return a regular expression based on first_line_re suitable for matching
+    strings.
+    """
+    if isinstance(first_line_re.pattern, str):
+        return first_line_re
+
+    # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
+    return re.compile(first_line_re.pattern.decode())
+
+
+def is_64bit():
+    return struct.calcsize("P") == 8
+
+
+def isascii(s):
+    try:
+        s.encode('ascii')
+    except UnicodeError:
+        return False
+    return True
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_shutil.py b/.venv/lib/python3.12/site-packages/setuptools/_shutil.py
new file mode 100644
index 0000000..660459a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_shutil.py
@@ -0,0 +1,59 @@
+"""Convenience layer on top of stdlib's shutil and os"""
+
+import os
+import stat
+from typing import Callable, TypeVar
+
+from .compat import py311
+
+from distutils import log
+
+try:
+    from os import chmod  # pyright: ignore[reportAssignmentType]
+    # Losing type-safety w/ pyright, but that's ok
+except ImportError:  # pragma: no cover
+    # Jython compatibility
+    def chmod(*args: object, **kwargs: object) -> None:  # type: ignore[misc] # Mypy reuses the imported definition anyway
+        pass
+
+
+_T = TypeVar("_T")
+
+
+def attempt_chmod_verbose(path, mode):
+    log.debug("changing mode of %s to %o", path, mode)
+    try:
+        chmod(path, mode)
+    except OSError as e:  # pragma: no cover
+        log.debug("chmod failed: %s", e)
+
+
+# Must match shutil._OnExcCallback
+def _auto_chmod(
+    func: Callable[..., _T], arg: str, exc: BaseException
+) -> _T:  # pragma: no cover
+    """shutils onexc callback to automatically call chmod for certain functions."""
+    # Only retry for scenarios known to have an issue
+    if func in [os.unlink, os.remove] and os.name == 'nt':
+        attempt_chmod_verbose(arg, stat.S_IWRITE)
+        return func(arg)
+    raise exc
+
+
+def rmtree(path, ignore_errors=False, onexc=_auto_chmod):
+    """
+    Similar to ``shutil.rmtree`` but automatically executes ``chmod``
+    for well know Windows failure scenarios.
+    """
+    return py311.shutil_rmtree(path, ignore_errors, onexc)
+
+
+def rmdir(path, **opts):
+    if os.path.isdir(path):
+        rmtree(path, **opts)
+
+
+def current_umask():
+    tmp = os.umask(0o022)
+    os.umask(tmp)
+    return tmp
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_static.py b/.venv/lib/python3.12/site-packages/setuptools/_static.py
new file mode 100644
index 0000000..af35862
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_static.py
@@ -0,0 +1,188 @@
+from functools import wraps
+from typing import TypeVar
+
+import packaging.specifiers
+
+from .warnings import SetuptoolsDeprecationWarning
+
+
+class Static:
+    """
+    Wrapper for built-in object types that are allow setuptools to identify
+    static core metadata (in opposition to ``Dynamic``, as defined :pep:`643`).
+
+    The trick is to mark values with :class:`Static` when they come from
+    ``pyproject.toml`` or ``setup.cfg``, so if any plugin overwrite the value
+    with a built-in, setuptools will be able to recognise the change.
+
+    We inherit from built-in classes, so that we don't need to change the existing
+    code base to deal with the new types.
+    We also should strive for immutability objects to avoid changes after the
+    initial parsing.
+    """
+
+    _mutated_: bool = False  # TODO: Remove after deprecation warning is solved
+
+
+def _prevent_modification(target: type, method: str, copying: str) -> None:
+    """
+    Because setuptools is very flexible we cannot fully prevent
+    plugins and user customizations from modifying static values that were
+    parsed from config files.
+    But we can attempt to block "in-place" mutations and identify when they
+    were done.
+    """
+    fn = getattr(target, method, None)
+    if fn is None:
+        return
+
+    @wraps(fn)
+    def _replacement(self: Static, *args, **kwargs):
+        # TODO: After deprecation period raise NotImplementedError instead of warning
+        #       which obviated the existence and checks of the `_mutated_` attribute.
+        self._mutated_ = True
+        SetuptoolsDeprecationWarning.emit(
+            "Direct modification of value will be disallowed",
+            f"""
+            In an effort to implement PEP 643, direct/in-place changes of static values
+            that come from configuration files are deprecated.
+            If you need to modify this value, please first create a copy with {copying}
+            and make sure conform to all relevant standards when overriding setuptools
+            functionality (https://packaging.python.org/en/latest/specifications/).
+            """,
+            due_date=(2025, 10, 10),  # Initially introduced in 2024-09-06
+        )
+        return fn(self, *args, **kwargs)
+
+    _replacement.__doc__ = ""  # otherwise doctest may fail.
+    setattr(target, method, _replacement)
+
+
+class Str(str, Static):
+    pass
+
+
+class Tuple(tuple, Static):
+    pass
+
+
+class List(list, Static):
+    """
+    :meta private:
+    >>> x = List([1, 2, 3])
+    >>> is_static(x)
+    True
+    >>> x += [0]  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+    SetuptoolsDeprecationWarning: Direct modification ...
+    >>> is_static(x)  # no longer static after modification
+    False
+    >>> y = list(x)
+    >>> y.clear()
+    >>> y
+    []
+    >>> y == x
+    False
+    >>> is_static(List(y))
+    True
+    """
+
+
+# Make `List` immutable-ish
+# (certain places of setuptools/distutils issue a warn if we use tuple instead of list)
+for _method in (
+    '__delitem__',
+    '__iadd__',
+    '__setitem__',
+    'append',
+    'clear',
+    'extend',
+    'insert',
+    'remove',
+    'reverse',
+    'pop',
+):
+    _prevent_modification(List, _method, "`list(value)`")
+
+
+class Dict(dict, Static):
+    """
+    :meta private:
+    >>> x = Dict({'a': 1, 'b': 2})
+    >>> is_static(x)
+    True
+    >>> x['c'] = 0  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+    SetuptoolsDeprecationWarning: Direct modification ...
+    >>> x._mutated_
+    True
+    >>> is_static(x)  # no longer static after modification
+    False
+    >>> y = dict(x)
+    >>> y.popitem()
+    ('b', 2)
+    >>> y == x
+    False
+    >>> is_static(Dict(y))
+    True
+    """
+
+
+# Make `Dict` immutable-ish (we cannot inherit from types.MappingProxyType):
+for _method in (
+    '__delitem__',
+    '__ior__',
+    '__setitem__',
+    'clear',
+    'pop',
+    'popitem',
+    'setdefault',
+    'update',
+):
+    _prevent_modification(Dict, _method, "`dict(value)`")
+
+
+class SpecifierSet(packaging.specifiers.SpecifierSet, Static):
+    """Not exactly a built-in type but useful for ``requires-python``"""
+
+
+T = TypeVar("T")
+
+
+def noop(value: T) -> T:
+    """
+    >>> noop(42)
+    42
+    """
+    return value
+
+
+_CONVERSIONS = {str: Str, tuple: Tuple, list: List, dict: Dict}
+
+
+def attempt_conversion(value: T) -> T:
+    """
+    >>> is_static(attempt_conversion("hello"))
+    True
+    >>> is_static(object())
+    False
+    """
+    return _CONVERSIONS.get(type(value), noop)(value)  # type: ignore[call-overload]
+
+
+def is_static(value: object) -> bool:
+    """
+    >>> is_static(a := Dict({'a': 1}))
+    True
+    >>> is_static(dict(a))
+    False
+    >>> is_static(b := List([1, 2, 3]))
+    True
+    >>> is_static(list(b))
+    False
+    """
+    return isinstance(value, Static) and not value._mutated_
+
+
+EMPTY_LIST = List()
+EMPTY_DICT = Dict()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/.lock b/.venv/lib/python3.12/site-packages/setuptools/_vendor/.lock
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE
new file mode 100644
index 0000000..b49c3af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE
@@ -0,0 +1,166 @@
+GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. 
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA
new file mode 100644
index 0000000..32214fb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA
@@ -0,0 +1,420 @@
+Metadata-Version: 2.1
+Name: autocommand
+Version: 2.2.2
+Summary: A library to create a command-line program from a function
+Home-page: https://github.com/Lucretiel/autocommand
+Author: Nathan West
+License: LGPLv3
+Project-URL: Homepage, https://github.com/Lucretiel/autocommand
+Project-URL: Bug Tracker, https://github.com/Lucretiel/autocommand/issues
+Platform: any
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Topic :: Software Development
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.7
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+[![PyPI version](https://badge.fury.io/py/autocommand.svg)](https://badge.fury.io/py/autocommand)
+
+# autocommand
+
+A library to automatically generate and run simple argparse parsers from function signatures.
+
+## Installation
+
+Autocommand is installed via pip:
+
+```
+$ pip install autocommand
+```
+
+## Usage
+
+Autocommand turns a function into a command-line program. It converts the function's parameter signature into command-line arguments, and automatically runs the function if the module was called as `__main__`. In effect, it lets your create a smart main function.
+
+```python
+from autocommand import autocommand
+
+# This program takes exactly one argument and echos it.
+@autocommand(__name__)
+def echo(thing):
+    print(thing)
+```
+
+```
+$ python echo.py hello
+hello
+$ python echo.py -h
+usage: echo [-h] thing
+
+positional arguments:
+  thing
+
+optional arguments:
+  -h, --help  show this help message and exit
+$ python echo.py hello world  # too many arguments
+usage: echo.py [-h] thing
+echo.py: error: unrecognized arguments: world
+```
+
+As you can see, autocommand converts the signature of the function into an argument spec. When you run the file as a program, autocommand collects the command-line arguments and turns them into function arguments. The function is executed with these arguments, and then the program exits with the return value of the function, via `sys.exit`. Autocommand also automatically creates a usage message, which can be invoked with `-h` or `--help`, and automatically prints an error message when provided with invalid arguments.
+
+### Types
+
+You can use a type annotation to give an argument a type. Any type (or in fact any callable) that returns an object when given a string argument can be used, though there are a few special cases that are described later.
+
+```python
+@autocommand(__name__)
+def net_client(host, port: int):
+    ...
+```
+
+Autocommand will catch `TypeErrors` raised by the type during argument parsing, so you can supply a callable and do some basic argument validation as well.
+
+### Trailing Arguments
+
+You can add a `*args` parameter to your function to give it trailing arguments. The command will collect 0 or more trailing arguments and supply them to `args` as a tuple. If a type annotation is supplied, the type is applied to each argument.
+
+```python
+# Write the contents of each file, one by one
+@autocommand(__name__)
+def cat(*files):
+    for filename in files:
+        with open(filename) as file:
+            for line in file:
+                print(line.rstrip())
+```
+
+```
+$ python cat.py -h
+usage: ipython [-h] [file [file ...]]
+
+positional arguments:
+  file
+
+optional arguments:
+  -h, --help  show this help message and exit
+```
+
+### Options
+
+To create `--option` switches, just assign a default. Autocommand will automatically create `--long` and `-s`hort switches.
+
+```python
+@autocommand(__name__)
+def do_with_config(argument, config='~/foo.conf'):
+    pass
+```
+
+```
+$ python example.py -h
+usage: example.py [-h] [-c CONFIG] argument
+
+positional arguments:
+  argument
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -c CONFIG, --config CONFIG
+```
+
+The option's type is automatically deduced from the default, unless one is explicitly given in an annotation:
+
+```python
+@autocommand(__name__)
+def http_connect(host, port=80):
+    print('{}:{}'.format(host, port))
+```
+
+```
+$ python http.py -h
+usage: http.py [-h] [-p PORT] host
+
+positional arguments:
+  host
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -p PORT, --port PORT
+$ python http.py localhost
+localhost:80
+$ python http.py localhost -p 8080
+localhost:8080
+$ python http.py localhost -p blah
+usage: http.py [-h] [-p PORT] host
+http.py: error: argument -p/--port: invalid int value: 'blah'
+```
+
+#### None
+
+If an option is given a default value of `None`, it reads in a value as normal, but supplies `None` if the option isn't provided.
+
+#### Switches
+
+If an argument is given a default value of `True` or `False`, or
+given an explicit `bool` type, it becomes an option switch.
+
+```python
+    @autocommand(__name__)
+    def example(verbose=False, quiet=False):
+        pass
+```
+
+```
+$ python example.py -h
+usage: example.py [-h] [-v] [-q]
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose
+  -q, --quiet
+```
+
+Autocommand attempts to do the "correct thing" in these cases- if the default is `True`, then supplying the switch makes the argument `False`; if the type is `bool` and the default is some other `True` value, then supplying the switch makes the argument `False`, while not supplying the switch makes the argument the default value.
+
+Autocommand also supports the creation of switch inverters. Pass `add_nos=True` to `autocommand` to enable this.
+
+```
+    @autocommand(__name__, add_nos=True)
+    def example(verbose=False):
+        pass
+```
+
+```
+$ python example.py -h
+usage: ipython [-h] [-v] [--no-verbose]
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose
+  --no-verbose
+```
+
+Using the `--no-` version of a switch will pass the opposite value in as a function argument. If multiple switches are present, the last one takes precedence.
+
+#### Files
+
+If the default value is a file object, such as `sys.stdout`, then autocommand just looks for a string, for a file path. It doesn't do any special checking on the string, though (such as checking if the file exists); it's better to let the client decide how to handle errors in this case. Instead, it provides a special context manager called `smart_open`, which behaves exactly like `open` if a filename or other openable type is provided, but also lets you use already open files:
+
+```python
+from autocommand import autocommand, smart_open
+import sys
+
+# Write the contents of stdin, or a file, to stdout
+@autocommand(__name__)
+def write_out(infile=sys.stdin):
+    with smart_open(infile) as f:
+        for line in f:
+            print(line.rstrip())
+    # If a file was opened, it is closed here. If it was just stdin, it is untouched.
+```
+
+```
+$ echo "Hello World!" | python write_out.py | tee hello.txt
+Hello World!
+$ python write_out.py --infile hello.txt
+Hello World!
+```
+
+### Descriptions and docstrings
+
+The `autocommand` decorator accepts `description` and `epilog` kwargs, corresponding to the `description `_ and `epilog `_ of the `ArgumentParser`. If no description is given, but the decorated function has a docstring, then it is taken as the `description` for the `ArgumentParser`. You can also provide both the description and epilog in the docstring by splitting it into two sections with 4 or more - characters.
+
+```python
+@autocommand(__name__)
+def copy(infile=sys.stdin, outfile=sys.stdout):
+    '''
+    Copy an the contents of a file (or stdin) to another file (or stdout)
+    ----------
+    Some extra documentation in the epilog
+    '''
+    with smart_open(infile) as istr:
+        with smart_open(outfile, 'w') as ostr:
+            for line in istr:
+                ostr.write(line)
+```
+
+```
+$ python copy.py -h
+usage: copy.py [-h] [-i INFILE] [-o OUTFILE]
+
+Copy an the contents of a file (or stdin) to another file (or stdout)
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -i INFILE, --infile INFILE
+  -o OUTFILE, --outfile OUTFILE
+
+Some extra documentation in the epilog
+$ echo "Hello World" | python copy.py --outfile hello.txt
+$ python copy.py --infile hello.txt --outfile hello2.txt
+$ python copy.py --infile hello2.txt
+Hello World
+```
+
+### Parameter descriptions
+
+You can also attach description text to individual parameters in the annotation. To attach both a type and a description, supply them both in any order in a tuple
+
+```python
+@autocommand(__name__)
+def copy_net(
+    infile: 'The name of the file to send',
+    host: 'The host to send the file to',
+    port: (int, 'The port to connect to')):
+
+    '''
+    Copy a file over raw TCP to a remote destination.
+    '''
+    # Left as an exercise to the reader
+```
+
+### Decorators and wrappers
+
+Autocommand automatically follows wrapper chains created by `@functools.wraps`. This means that you can apply other wrapping decorators to your main function, and autocommand will still correctly detect the signature.
+
+```python
+from functools import wraps
+from autocommand import autocommand
+
+def print_yielded(func):
+    '''
+    Convert a generator into a function that prints all yielded elements
+    '''
+    @wraps(func)
+    def wrapper(*args, **kwargs):
+        for thing in func(*args, **kwargs):
+            print(thing)
+    return wrapper
+
+@autocommand(__name__,
+    description= 'Print all the values from START to STOP, inclusive, in steps of STEP',
+    epilog=      'STOP and STEP default to 1')
+@print_yielded
+def seq(stop, start=1, step=1):
+    for i in range(start, stop + 1, step):
+        yield i
+```
+
+```
+$ seq.py -h
+usage: seq.py [-h] [-s START] [-S STEP] stop
+
+Print all the values from START to STOP, inclusive, in steps of STEP
+
+positional arguments:
+  stop
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -s START, --start START
+  -S STEP, --step STEP
+
+STOP and STEP default to 1
+```
+
+Even though autocommand is being applied to the `wrapper` returned by `print_yielded`, it still retreives the signature of the underlying `seq` function to create the argument parsing.
+
+### Custom Parser
+
+While autocommand's automatic parser generator is a powerful convenience, it doesn't cover all of the different features that argparse provides. If you need these features, you can provide your own parser as a kwarg to `autocommand`:
+
+```python
+from argparse import ArgumentParser
+from autocommand import autocommand
+
+parser = ArgumentParser()
+# autocommand can't do optional positonal parameters
+parser.add_argument('arg', nargs='?')
+# or mutually exclusive options
+group = parser.add_mutually_exclusive_group()
+group.add_argument('-v', '--verbose', action='store_true')
+group.add_argument('-q', '--quiet', action='store_true')
+
+@autocommand(__name__, parser=parser)
+def main(arg, verbose, quiet):
+    print(arg, verbose, quiet)
+```
+
+```
+$ python parser.py -h
+usage: write_file.py [-h] [-v | -q] [arg]
+
+positional arguments:
+  arg
+
+optional arguments:
+  -h, --help     show this help message and exit
+  -v, --verbose
+  -q, --quiet
+$ python parser.py
+None False False
+$ python parser.py hello
+hello False False
+$ python parser.py -v
+None True False
+$ python parser.py -q
+None False True
+$ python parser.py -vq
+usage: parser.py [-h] [-v | -q] [arg]
+parser.py: error: argument -q/--quiet: not allowed with argument -v/--verbose
+```
+
+Any parser should work fine, so long as each of the parser's arguments has a corresponding parameter in the decorated main function. The order of parameters doesn't matter, as long as they are all present. Note that when using a custom parser, autocommand doesn't modify the parser or the retrieved arguments. This means that no description/epilog will be added, and the function's type annotations and defaults (if present) will be ignored.
+
+## Testing and Library use
+
+The decorated function is only called and exited from if the first argument to `autocommand` is `'__main__'` or `True`. If it is neither of these values, or no argument is given, then a new main function is created by the decorator. This function has the signature `main(argv=None)`, and is intended to be called with arguments as if via `main(sys.argv[1:])`. The function has the attributes `parser` and `main`, which are the generated `ArgumentParser` and the original main function that was decorated. This is to facilitate testing and library use of your main. Calling the function triggers a `parse_args()` with the supplied arguments, and returns the result of the main function. Note that, while it returns instead of calling `sys.exit`, the `parse_args()` function will raise a `SystemExit` in the event of a parsing error or `-h/--help` argument.
+
+```python
+    @autocommand()
+    def test_prog(arg1, arg2: int, quiet=False, verbose=False):
+        if not quiet:
+            print(arg1, arg2)
+            if verbose:
+                print("LOUD NOISES")
+
+        return 0
+
+    print(test_prog(['-v', 'hello', '80']))
+```
+
+```
+$ python test_prog.py
+hello 80
+LOUD NOISES
+0
+```
+
+If the function is called with no arguments, `sys.argv[1:]` is used. This is to allow the autocommand function to be used as a setuptools entry point.
+
+## Exceptions and limitations
+
+- There are a few possible exceptions that `autocommand` can raise. All of them derive from `autocommand.AutocommandError`.
+
+  - If an invalid annotation is given (that is, it isn't a `type`, `str`, `(type, str)`, or `(str, type)`, an `AnnotationError` is raised. The `type` may be any callable, as described in the `Types`_ section.
+  - If the function has a `**kwargs` parameter, a `KWargError` is raised.
+  - If, somehow, the function has a positional-only parameter, a `PositionalArgError` is raised. This means that the argument doesn't have a name, which is currently not possible with a plain `def` or `lambda`, though many built-in functions have this kind of parameter.
+
+- There are a few argparse features that are not supported by autocommand.
+
+  - It isn't possible to have an optional positional argument (as opposed to a `--option`). POSIX thinks this is bad form anyway.
+  - It isn't possible to have mutually exclusive arguments or options
+  - It isn't possible to have subcommands or subparsers, though I'm working on a few solutions involving classes or nested function definitions to allow this.
+
+## Development
+
+Autocommand cannot be important from the project root; this is to enforce separation of concerns and prevent accidental importing of `setup.py` or tests. To develop, install the project in editable mode:
+
+```
+$ python setup.py develop
+```
+
+This will create a link to the source files in the deployment directory, so that any source changes are reflected when it is imported.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD
new file mode 100644
index 0000000..8cd7450
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD
@@ -0,0 +1,13 @@
+autocommand-2.2.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+autocommand-2.2.2.dist-info/LICENSE,sha256=reeNBJgtaZctREqOFKlPh6IzTdOFXMgDSOqOJAqg3y0,7634
+autocommand-2.2.2.dist-info/METADATA,sha256=OADZuR3O6iBlpu1ieTgzYul6w4uOVrk0P0BO5TGGAJk,15006
+autocommand-2.2.2.dist-info/RECORD,,
+autocommand-2.2.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+autocommand-2.2.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+autocommand-2.2.2.dist-info/top_level.txt,sha256=AzfhgKKS8EdAwWUTSF8mgeVQbXOY9kokHB6kSqwwqu0,12
+autocommand/__init__.py,sha256=zko5Rnvolvb-UXjCx_2ArPTGBWwUK5QY4LIQIKYR7As,1037
+autocommand/autoasync.py,sha256=AMdyrxNS4pqWJfP_xuoOcImOHWD-qT7x06wmKN1Vp-U,5680
+autocommand/autocommand.py,sha256=hmkEmQ72HtL55gnURVjDOnsfYlGd5lLXbvT4KG496Qw,2505
+autocommand/automain.py,sha256=A2b8i754Mxc_DjU9WFr6vqYDWlhz0cn8miu8d8EsxV8,2076
+autocommand/autoparse.py,sha256=WVWmZJPcbzUKXP40raQw_0HD8qPJ2V9VG1eFFmmnFxw,11642
+autocommand/errors.py,sha256=7aa3roh9Herd6nIKpQHNWEslWE8oq7GiHYVUuRqORnA,886
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL
new file mode 100644
index 0000000..57e3d84
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.38.4)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt
new file mode 100644
index 0000000..dda5158
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+autocommand
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py
new file mode 100644
index 0000000..73fbfca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/__init__.py
@@ -0,0 +1,27 @@
+# Copyright 2014-2016 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+# flake8 flags all these imports as unused, hence the NOQAs everywhere.
+
+from .automain import automain  # NOQA
+from .autoparse import autoparse, smart_open  # NOQA
+from .autocommand import autocommand  # NOQA
+
+try:
+    from .autoasync import autoasync  # NOQA
+except ImportError:  # pragma: no cover
+    pass
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py
new file mode 100644
index 0000000..688f7e0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoasync.py
@@ -0,0 +1,142 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+from asyncio import get_event_loop, iscoroutine
+from functools import wraps
+from inspect import signature
+
+
+async def _run_forever_coro(coro, args, kwargs, loop):
+    '''
+    This helper function launches an async main function that was tagged with
+    forever=True. There are two possibilities:
+
+    - The function is a normal function, which handles initializing the event
+      loop, which is then run forever
+    - The function is a coroutine, which needs to be scheduled in the event
+      loop, which is then run forever
+      - There is also the possibility that the function is a normal function
+        wrapping a coroutine function
+
+    The function is therefore called unconditionally and scheduled in the event
+    loop if the return value is a coroutine object.
+
+    The reason this is a separate function is to make absolutely sure that all
+    the objects created are garbage collected after all is said and done; we
+    do this to ensure that any exceptions raised in the tasks are collected
+    ASAP.
+    '''
+
+    # Personal note: I consider this an antipattern, as it relies on the use of
+    # unowned resources. The setup function dumps some stuff into the event
+    # loop where it just whirls in the ether without a well defined owner or
+    # lifetime. For this reason, there's a good chance I'll remove the
+    # forever=True feature from autoasync at some point in the future.
+    thing = coro(*args, **kwargs)
+    if iscoroutine(thing):
+        await thing
+
+
+def autoasync(coro=None, *, loop=None, forever=False, pass_loop=False):
+    '''
+    Convert an asyncio coroutine into a function which, when called, is
+    evaluted in an event loop, and the return value returned. This is intented
+    to make it easy to write entry points into asyncio coroutines, which
+    otherwise need to be explictly evaluted with an event loop's
+    run_until_complete.
+
+    If `loop` is given, it is used as the event loop to run the coro in. If it
+    is None (the default), the loop is retreived using asyncio.get_event_loop.
+    This call is defered until the decorated function is called, so that
+    callers can install custom event loops or event loop policies after
+    @autoasync is applied.
+
+    If `forever` is True, the loop is run forever after the decorated coroutine
+    is finished. Use this for servers created with asyncio.start_server and the
+    like.
+
+    If `pass_loop` is True, the event loop object is passed into the coroutine
+    as the `loop` kwarg when the wrapper function is called. In this case, the
+    wrapper function's __signature__ is updated to remove this parameter, so
+    that autoparse can still be used on it without generating a parameter for
+    `loop`.
+
+    This coroutine can be called with ( @autoasync(...) ) or without
+    ( @autoasync ) arguments.
+
+    Examples:
+
+    @autoasync
+    def get_file(host, port):
+        reader, writer = yield from asyncio.open_connection(host, port)
+        data = reader.read()
+        sys.stdout.write(data.decode())
+
+    get_file(host, port)
+
+    @autoasync(forever=True, pass_loop=True)
+    def server(host, port, loop):
+        yield_from loop.create_server(Proto, host, port)
+
+    server('localhost', 8899)
+
+    '''
+    if coro is None:
+        return lambda c: autoasync(
+            c, loop=loop,
+            forever=forever,
+            pass_loop=pass_loop)
+
+    # The old and new signatures are required to correctly bind the loop
+    # parameter in 100% of cases, even if it's a positional parameter.
+    # NOTE: A future release will probably require the loop parameter to be
+    # a kwonly parameter.
+    if pass_loop:
+        old_sig = signature(coro)
+        new_sig = old_sig.replace(parameters=(
+            param for name, param in old_sig.parameters.items()
+            if name != "loop"))
+
+    @wraps(coro)
+    def autoasync_wrapper(*args, **kwargs):
+        # Defer the call to get_event_loop so that, if a custom policy is
+        # installed after the autoasync decorator, it is respected at call time
+        local_loop = get_event_loop() if loop is None else loop
+
+        # Inject the 'loop' argument. We have to use this signature binding to
+        # ensure it's injected in the correct place (positional, keyword, etc)
+        if pass_loop:
+            bound_args = old_sig.bind_partial()
+            bound_args.arguments.update(
+                loop=local_loop,
+                **new_sig.bind(*args, **kwargs).arguments)
+            args, kwargs = bound_args.args, bound_args.kwargs
+
+        if forever:
+            local_loop.create_task(_run_forever_coro(
+                coro, args, kwargs, local_loop
+            ))
+            local_loop.run_forever()
+        else:
+            return local_loop.run_until_complete(coro(*args, **kwargs))
+
+    # Attach the updated signature. This allows 'pass_loop' to be used with
+    # autoparse
+    if pass_loop:
+        autoasync_wrapper.__signature__ = new_sig
+
+    return autoasync_wrapper
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py
new file mode 100644
index 0000000..097e86d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autocommand.py
@@ -0,0 +1,70 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+from .autoparse import autoparse
+from .automain import automain
+try:
+    from .autoasync import autoasync
+except ImportError:  # pragma: no cover
+    pass
+
+
+def autocommand(
+        module, *,
+        description=None,
+        epilog=None,
+        add_nos=False,
+        parser=None,
+        loop=None,
+        forever=False,
+        pass_loop=False):
+
+    if callable(module):
+        raise TypeError('autocommand requires a module name argument')
+
+    def autocommand_decorator(func):
+        # Step 1: if requested, run it all in an asyncio event loop. autoasync
+        # patches the __signature__ of the decorated function, so that in the
+        # event that pass_loop is True, the `loop` parameter of the original
+        # function will *not* be interpreted as a command-line argument by
+        # autoparse
+        if loop is not None or forever or pass_loop:
+            func = autoasync(
+                func,
+                loop=None if loop is True else loop,
+                pass_loop=pass_loop,
+                forever=forever)
+
+        # Step 2: create parser. We do this second so that the arguments are
+        # parsed and passed *before* entering the asyncio event loop, if it
+        # exists. This simplifies the stack trace and ensures errors are
+        # reported earlier. It also ensures that errors raised during parsing &
+        # passing are still raised if `forever` is True.
+        func = autoparse(
+            func,
+            description=description,
+            epilog=epilog,
+            add_nos=add_nos,
+            parser=parser)
+
+        # Step 3: call the function automatically if __name__ == '__main__' (or
+        # if True was provided)
+        func = automain(module)(func)
+
+        return func
+
+    return autocommand_decorator
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py
new file mode 100644
index 0000000..6cc45db
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/automain.py
@@ -0,0 +1,59 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+import sys
+from .errors import AutocommandError
+
+
+class AutomainRequiresModuleError(AutocommandError, TypeError):
+    pass
+
+
+def automain(module, *, args=(), kwargs=None):
+    '''
+    This decorator automatically invokes a function if the module is being run
+    as the "__main__" module. Optionally, provide args or kwargs with which to
+    call the function. If `module` is "__main__", the function is called, and
+    the program is `sys.exit`ed with the return value. You can also pass `True`
+    to cause the function to be called unconditionally. If the function is not
+    called, it is returned unchanged by the decorator.
+
+    Usage:
+
+    @automain(__name__)  # Pass __name__ to check __name__=="__main__"
+    def main():
+        ...
+
+    If __name__ is "__main__" here, the main function is called, and then
+    sys.exit called with the return value.
+    '''
+
+    # Check that @automain(...) was called, rather than @automain
+    if callable(module):
+        raise AutomainRequiresModuleError(module)
+
+    if module == '__main__' or module is True:
+        if kwargs is None:
+            kwargs = {}
+
+        # Use a function definition instead of a lambda for a neater traceback
+        def automain_decorator(main):
+            sys.exit(main(*args, **kwargs))
+
+        return automain_decorator
+    else:
+        return lambda main: main
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py
new file mode 100644
index 0000000..0276a3f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/autoparse.py
@@ -0,0 +1,333 @@
+# Copyright 2014-2015 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+import sys
+from re import compile as compile_regex
+from inspect import signature, getdoc, Parameter
+from argparse import ArgumentParser
+from contextlib import contextmanager
+from functools import wraps
+from io import IOBase
+from autocommand.errors import AutocommandError
+
+
+_empty = Parameter.empty
+
+
+class AnnotationError(AutocommandError):
+    '''Annotation error: annotation must be a string, type, or tuple of both'''
+
+
+class PositionalArgError(AutocommandError):
+    '''
+    Postional Arg Error: autocommand can't handle postional-only parameters
+    '''
+
+
+class KWArgError(AutocommandError):
+    '''kwarg Error: autocommand can't handle a **kwargs parameter'''
+
+
+class DocstringError(AutocommandError):
+    '''Docstring error'''
+
+
+class TooManySplitsError(DocstringError):
+    '''
+    The docstring had too many ---- section splits. Currently we only support
+    using up to a single split, to split the docstring into description and
+    epilog parts.
+    '''
+
+
+def _get_type_description(annotation):
+    '''
+    Given an annotation, return the (type, description) for the parameter.
+    If you provide an annotation that is somehow both a string and a callable,
+    the behavior is undefined.
+    '''
+    if annotation is _empty:
+        return None, None
+    elif callable(annotation):
+        return annotation, None
+    elif isinstance(annotation, str):
+        return None, annotation
+    elif isinstance(annotation, tuple):
+        try:
+            arg1, arg2 = annotation
+        except ValueError as e:
+            raise AnnotationError(annotation) from e
+        else:
+            if callable(arg1) and isinstance(arg2, str):
+                return arg1, arg2
+            elif isinstance(arg1, str) and callable(arg2):
+                return arg2, arg1
+
+    raise AnnotationError(annotation)
+
+
+def _add_arguments(param, parser, used_char_args, add_nos):
+    '''
+    Add the argument(s) to an ArgumentParser (using add_argument) for a given
+    parameter. used_char_args is the set of -short options currently already in
+    use, and is updated (if necessary) by this function. If add_nos is True,
+    this will also add an inverse switch for all boolean options. For
+    instance, for the boolean parameter "verbose", this will create --verbose
+    and --no-verbose.
+    '''
+
+    # Impl note: This function is kept separate from make_parser because it's
+    # already very long and I wanted to separate out as much as possible into
+    # its own call scope, to prevent even the possibility of suble mutation
+    # bugs.
+    if param.kind is param.POSITIONAL_ONLY:
+        raise PositionalArgError(param)
+    elif param.kind is param.VAR_KEYWORD:
+        raise KWArgError(param)
+
+    # These are the kwargs for the add_argument function.
+    arg_spec = {}
+    is_option = False
+
+    # Get the type and default from the annotation.
+    arg_type, description = _get_type_description(param.annotation)
+
+    # Get the default value
+    default = param.default
+
+    # If there is no explicit type, and the default is present and not None,
+    # infer the type from the default.
+    if arg_type is None and default not in {_empty, None}:
+        arg_type = type(default)
+
+    # Add default. The presence of a default means this is an option, not an
+    # argument.
+    if default is not _empty:
+        arg_spec['default'] = default
+        is_option = True
+
+    # Add the type
+    if arg_type is not None:
+        # Special case for bool: make it just a --switch
+        if arg_type is bool:
+            if not default or default is _empty:
+                arg_spec['action'] = 'store_true'
+            else:
+                arg_spec['action'] = 'store_false'
+
+            # Switches are always options
+            is_option = True
+
+        # Special case for file types: make it a string type, for filename
+        elif isinstance(default, IOBase):
+            arg_spec['type'] = str
+
+        # TODO: special case for list type.
+        #   - How to specificy type of list members?
+        #       - param: [int]
+        #       - param: int =[]
+        #   - action='append' vs nargs='*'
+
+        else:
+            arg_spec['type'] = arg_type
+
+    # nargs: if the signature includes *args, collect them as trailing CLI
+    # arguments in a list. *args can't have a default value, so it can never be
+    # an option.
+    if param.kind is param.VAR_POSITIONAL:
+        # TODO: consider depluralizing metavar/name here.
+        arg_spec['nargs'] = '*'
+
+    # Add description.
+    if description is not None:
+        arg_spec['help'] = description
+
+    # Get the --flags
+    flags = []
+    name = param.name
+
+    if is_option:
+        # Add the first letter as a -short option.
+        for letter in name[0], name[0].swapcase():
+            if letter not in used_char_args:
+                used_char_args.add(letter)
+                flags.append('-{}'.format(letter))
+                break
+
+        # If the parameter is a --long option, or is a -short option that
+        # somehow failed to get a flag, add it.
+        if len(name) > 1 or not flags:
+            flags.append('--{}'.format(name))
+
+        arg_spec['dest'] = name
+    else:
+        flags.append(name)
+
+    parser.add_argument(*flags, **arg_spec)
+
+    # Create the --no- version for boolean switches
+    if add_nos and arg_type is bool:
+        parser.add_argument(
+            '--no-{}'.format(name),
+            action='store_const',
+            dest=name,
+            const=default if default is not _empty else False)
+
+
+def make_parser(func_sig, description, epilog, add_nos):
+    '''
+    Given the signature of a function, create an ArgumentParser
+    '''
+    parser = ArgumentParser(description=description, epilog=epilog)
+
+    used_char_args = {'h'}
+
+    # Arange the params so that single-character arguments are first. This
+    # esnures they don't have to get --long versions. sorted is stable, so the
+    # parameters will otherwise still be in relative order.
+    params = sorted(
+        func_sig.parameters.values(),
+        key=lambda param: len(param.name) > 1)
+
+    for param in params:
+        _add_arguments(param, parser, used_char_args, add_nos)
+
+    return parser
+
+
+_DOCSTRING_SPLIT = compile_regex(r'\n\s*-{4,}\s*\n')
+
+
+def parse_docstring(docstring):
+    '''
+    Given a docstring, parse it into a description and epilog part
+    '''
+    if docstring is None:
+        return '', ''
+
+    parts = _DOCSTRING_SPLIT.split(docstring)
+
+    if len(parts) == 1:
+        return docstring, ''
+    elif len(parts) == 2:
+        return parts[0], parts[1]
+    else:
+        raise TooManySplitsError()
+
+
+def autoparse(
+        func=None, *,
+        description=None,
+        epilog=None,
+        add_nos=False,
+        parser=None):
+    '''
+    This decorator converts a function that takes normal arguments into a
+    function which takes a single optional argument, argv, parses it using an
+    argparse.ArgumentParser, and calls the underlying function with the parsed
+    arguments. If it is not given, sys.argv[1:] is used. This is so that the
+    function can be used as a setuptools entry point, as well as a normal main
+    function. sys.argv[1:] is not evaluated until the function is called, to
+    allow injecting different arguments for testing.
+
+    It uses the argument signature of the function to create an
+    ArgumentParser. Parameters without defaults become positional parameters,
+    while parameters *with* defaults become --options. Use annotations to set
+    the type of the parameter.
+
+    The `desctiption` and `epilog` parameters corrospond to the same respective
+    argparse parameters. If no description is given, it defaults to the
+    decorated functions's docstring, if present.
+
+    If add_nos is True, every boolean option (that is, every parameter with a
+    default of True/False or a type of bool) will have a --no- version created
+    as well, which inverts the option. For instance, the --verbose option will
+    have a --no-verbose counterpart. These are not mutually exclusive-
+    whichever one appears last in the argument list will have precedence.
+
+    If a parser is given, it is used instead of one generated from the function
+    signature. In this case, no parser is created; instead, the given parser is
+    used to parse the argv argument. The parser's results' argument names must
+    match up with the parameter names of the decorated function.
+
+    The decorated function is attached to the result as the `func` attribute,
+    and the parser is attached as the `parser` attribute.
+    '''
+
+    # If @autoparse(...) is used instead of @autoparse
+    if func is None:
+        return lambda f: autoparse(
+            f, description=description,
+            epilog=epilog,
+            add_nos=add_nos,
+            parser=parser)
+
+    func_sig = signature(func)
+
+    docstr_description, docstr_epilog = parse_docstring(getdoc(func))
+
+    if parser is None:
+        parser = make_parser(
+            func_sig,
+            description or docstr_description,
+            epilog or docstr_epilog,
+            add_nos)
+
+    @wraps(func)
+    def autoparse_wrapper(argv=None):
+        if argv is None:
+            argv = sys.argv[1:]
+
+        # Get empty argument binding, to fill with parsed arguments. This
+        # object does all the heavy lifting of turning named arguments into
+        # into correctly bound *args and **kwargs.
+        parsed_args = func_sig.bind_partial()
+        parsed_args.arguments.update(vars(parser.parse_args(argv)))
+
+        return func(*parsed_args.args, **parsed_args.kwargs)
+
+    # TODO: attach an updated __signature__ to autoparse_wrapper, just in case.
+
+    # Attach the wrapped function and parser, and return the wrapper.
+    autoparse_wrapper.func = func
+    autoparse_wrapper.parser = parser
+    return autoparse_wrapper
+
+
+@contextmanager
+def smart_open(filename_or_file, *args, **kwargs):
+    '''
+    This context manager allows you to open a filename, if you want to default
+    some already-existing file object, like sys.stdout, which shouldn't be
+    closed at the end of the context. If the filename argument is a str, bytes,
+    or int, the file object is created via a call to open with the given *args
+    and **kwargs, sent to the context, and closed at the end of the context,
+    just like "with open(filename) as f:". If it isn't one of the openable
+    types, the object simply sent to the context unchanged, and left unclosed
+    at the end of the context. Example:
+
+        def work_with_file(name=sys.stdout):
+            with smart_open(name) as f:
+                # Works correctly if name is a str filename or sys.stdout
+                print("Some stuff", file=f)
+                # If it was a filename, f is closed at the end here.
+    '''
+    if isinstance(filename_or_file, (str, bytes, int)):
+        with open(filename_or_file, *args, **kwargs) as file:
+            yield file
+    else:
+        yield filename_or_file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py
new file mode 100644
index 0000000..2570607
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/autocommand/errors.py
@@ -0,0 +1,23 @@
+# Copyright 2014-2016 Nathan West
+#
+# This file is part of autocommand.
+#
+# autocommand 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 3 of the License, or
+# (at your option) any later version.
+#
+# autocommand 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 autocommand.  If not, see .
+
+
+class AutocommandError(Exception):
+    '''Base class for autocommand exceptions'''
+    pass
+
+# Individual modules will define errors specific to that module.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA
new file mode 100644
index 0000000..db0a2dc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA
@@ -0,0 +1,46 @@
+Metadata-Version: 2.1
+Name: backports.tarfile
+Version: 1.2.0
+Summary: Backport of CPython tarfile module
+Author-email: "Jason R. Coombs" 
+Project-URL: Homepage, https://github.com/jaraco/backports.tarfile
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx >=3.5 ; extra == 'docs'
+Requires-Dist: jaraco.packaging >=9.3 ; extra == 'docs'
+Requires-Dist: rst.linker >=1.9 ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'testing'
+Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler >=2.2 ; extra == 'testing'
+Requires-Dist: jaraco.test ; extra == 'testing'
+Requires-Dist: pytest !=8.0.* ; extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/backports.tarfile.svg
+   :target: https://pypi.org/project/backports.tarfile
+
+.. image:: https://img.shields.io/pypi/pyversions/backports.tarfile.svg
+
+.. image:: https://github.com/jaraco/backports.tarfile/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/jaraco/backports.tarfile/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. .. image:: https://readthedocs.org/projects/backportstarfile/badge/?version=latest
+..    :target: https://backportstarfile.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2024-informational
+   :target: https://blog.jaraco.com/skeleton
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD
new file mode 100644
index 0000000..c7d2c25
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD
@@ -0,0 +1,12 @@
+backports.tarfile-1.2.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+backports.tarfile-1.2.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+backports.tarfile-1.2.0.dist-info/METADATA,sha256=ghXFTq132dxaEIolxr3HK1mZqm9iyUmaRANZQSr6WlE,2020
+backports.tarfile-1.2.0.dist-info/RECORD,,
+backports.tarfile-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+backports.tarfile-1.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+backports.tarfile-1.2.0.dist-info/top_level.txt,sha256=cGjaLMOoBR1FK0ApojtzWVmViTtJ7JGIK_HwXiEsvtU,10
+backports/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
+backports/tarfile/__init__.py,sha256=Pwf2qUIfB0SolJPCKcx3vz3UEu_aids4g4sAfxy94qg,108491
+backports/tarfile/__main__.py,sha256=Yw2oGT1afrz2eBskzdPYL8ReB_3liApmhFkN2EbDmc4,59
+backports/tarfile/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+backports/tarfile/compat/py38.py,sha256=iYkyt_gvWjLzGUTJD9TuTfMMjOk-ersXZmRlvQYN2qE,568
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL
new file mode 100644
index 0000000..bab98d6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.43.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt
new file mode 100644
index 0000000..99d2be5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+backports
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/__init__.py
new file mode 100644
index 0000000..0d1f7ed
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/__init__.py
@@ -0,0 +1 @@
+__path__ = __import__('pkgutil').extend_path(__path__, __name__)  # type: ignore
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__init__.py
new file mode 100644
index 0000000..8c16881
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__init__.py
@@ -0,0 +1,2937 @@
+#-------------------------------------------------------------------
+# tarfile.py
+#-------------------------------------------------------------------
+# Copyright (C) 2002 Lars Gustaebel 
+# All rights reserved.
+#
+# 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.
+#
+"""Read from and write to tar format archives.
+"""
+
+version     = "0.9.0"
+__author__  = "Lars Gust\u00e4bel (lars@gustaebel.de)"
+__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend."
+
+#---------
+# Imports
+#---------
+from builtins import open as bltn_open
+import sys
+import os
+import io
+import shutil
+import stat
+import time
+import struct
+import copy
+import re
+
+from .compat.py38 import removesuffix
+
+try:
+    import pwd
+except ImportError:
+    pwd = None
+try:
+    import grp
+except ImportError:
+    grp = None
+
+# os.symlink on Windows prior to 6.0 raises NotImplementedError
+# OSError (winerror=1314) will be raised if the caller does not hold the
+# SeCreateSymbolicLinkPrivilege privilege
+symlink_exception = (AttributeError, NotImplementedError, OSError)
+
+# from tarfile import *
+__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError",
+           "CompressionError", "StreamError", "ExtractError", "HeaderError",
+           "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT",
+           "DEFAULT_FORMAT", "open","fully_trusted_filter", "data_filter",
+           "tar_filter", "FilterError", "AbsoluteLinkError",
+           "OutsideDestinationError", "SpecialFileError", "AbsolutePathError",
+           "LinkOutsideDestinationError"]
+
+
+#---------------------------------------------------------
+# tar constants
+#---------------------------------------------------------
+NUL = b"\0"                     # the null character
+BLOCKSIZE = 512                 # length of processing blocks
+RECORDSIZE = BLOCKSIZE * 20     # length of records
+GNU_MAGIC = b"ustar  \0"        # magic gnu tar string
+POSIX_MAGIC = b"ustar\x0000"    # magic posix tar string
+
+LENGTH_NAME = 100               # maximum length of a filename
+LENGTH_LINK = 100               # maximum length of a linkname
+LENGTH_PREFIX = 155             # maximum length of the prefix field
+
+REGTYPE = b"0"                  # regular file
+AREGTYPE = b"\0"                # regular file
+LNKTYPE = b"1"                  # link (inside tarfile)
+SYMTYPE = b"2"                  # symbolic link
+CHRTYPE = b"3"                  # character special device
+BLKTYPE = b"4"                  # block special device
+DIRTYPE = b"5"                  # directory
+FIFOTYPE = b"6"                 # fifo special device
+CONTTYPE = b"7"                 # contiguous file
+
+GNUTYPE_LONGNAME = b"L"         # GNU tar longname
+GNUTYPE_LONGLINK = b"K"         # GNU tar longlink
+GNUTYPE_SPARSE = b"S"           # GNU tar sparse file
+
+XHDTYPE = b"x"                  # POSIX.1-2001 extended header
+XGLTYPE = b"g"                  # POSIX.1-2001 global header
+SOLARIS_XHDTYPE = b"X"          # Solaris extended header
+
+USTAR_FORMAT = 0                # POSIX.1-1988 (ustar) format
+GNU_FORMAT = 1                  # GNU tar format
+PAX_FORMAT = 2                  # POSIX.1-2001 (pax) format
+DEFAULT_FORMAT = PAX_FORMAT
+
+#---------------------------------------------------------
+# tarfile constants
+#---------------------------------------------------------
+# File types that tarfile supports:
+SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE,
+                   SYMTYPE, DIRTYPE, FIFOTYPE,
+                   CONTTYPE, CHRTYPE, BLKTYPE,
+                   GNUTYPE_LONGNAME, GNUTYPE_LONGLINK,
+                   GNUTYPE_SPARSE)
+
+# File types that will be treated as a regular file.
+REGULAR_TYPES = (REGTYPE, AREGTYPE,
+                 CONTTYPE, GNUTYPE_SPARSE)
+
+# File types that are part of the GNU tar format.
+GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK,
+             GNUTYPE_SPARSE)
+
+# Fields from a pax header that override a TarInfo attribute.
+PAX_FIELDS = ("path", "linkpath", "size", "mtime",
+              "uid", "gid", "uname", "gname")
+
+# Fields from a pax header that are affected by hdrcharset.
+PAX_NAME_FIELDS = {"path", "linkpath", "uname", "gname"}
+
+# Fields in a pax header that are numbers, all other fields
+# are treated as strings.
+PAX_NUMBER_FIELDS = {
+    "atime": float,
+    "ctime": float,
+    "mtime": float,
+    "uid": int,
+    "gid": int,
+    "size": int
+}
+
+#---------------------------------------------------------
+# initialization
+#---------------------------------------------------------
+if os.name == "nt":
+    ENCODING = "utf-8"
+else:
+    ENCODING = sys.getfilesystemencoding()
+
+#---------------------------------------------------------
+# Some useful functions
+#---------------------------------------------------------
+
+def stn(s, length, encoding, errors):
+    """Convert a string to a null-terminated bytes object.
+    """
+    if s is None:
+        raise ValueError("metadata cannot contain None")
+    s = s.encode(encoding, errors)
+    return s[:length] + (length - len(s)) * NUL
+
+def nts(s, encoding, errors):
+    """Convert a null-terminated bytes object to a string.
+    """
+    p = s.find(b"\0")
+    if p != -1:
+        s = s[:p]
+    return s.decode(encoding, errors)
+
+def nti(s):
+    """Convert a number field to a python number.
+    """
+    # There are two possible encodings for a number field, see
+    # itn() below.
+    if s[0] in (0o200, 0o377):
+        n = 0
+        for i in range(len(s) - 1):
+            n <<= 8
+            n += s[i + 1]
+        if s[0] == 0o377:
+            n = -(256 ** (len(s) - 1) - n)
+    else:
+        try:
+            s = nts(s, "ascii", "strict")
+            n = int(s.strip() or "0", 8)
+        except ValueError:
+            raise InvalidHeaderError("invalid header")
+    return n
+
+def itn(n, digits=8, format=DEFAULT_FORMAT):
+    """Convert a python number to a number field.
+    """
+    # POSIX 1003.1-1988 requires numbers to be encoded as a string of
+    # octal digits followed by a null-byte, this allows values up to
+    # (8**(digits-1))-1. GNU tar allows storing numbers greater than
+    # that if necessary. A leading 0o200 or 0o377 byte indicate this
+    # particular encoding, the following digits-1 bytes are a big-endian
+    # base-256 representation. This allows values up to (256**(digits-1))-1.
+    # A 0o200 byte indicates a positive number, a 0o377 byte a negative
+    # number.
+    original_n = n
+    n = int(n)
+    if 0 <= n < 8 ** (digits - 1):
+        s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL
+    elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1):
+        if n >= 0:
+            s = bytearray([0o200])
+        else:
+            s = bytearray([0o377])
+            n = 256 ** digits + n
+
+        for i in range(digits - 1):
+            s.insert(1, n & 0o377)
+            n >>= 8
+    else:
+        raise ValueError("overflow in number field")
+
+    return s
+
+def calc_chksums(buf):
+    """Calculate the checksum for a member's header by summing up all
+       characters except for the chksum field which is treated as if
+       it was filled with spaces. According to the GNU tar sources,
+       some tars (Sun and NeXT) calculate chksum with signed char,
+       which will be different if there are chars in the buffer with
+       the high bit set. So we calculate two checksums, unsigned and
+       signed.
+    """
+    unsigned_chksum = 256 + sum(struct.unpack_from("148B8x356B", buf))
+    signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf))
+    return unsigned_chksum, signed_chksum
+
+def copyfileobj(src, dst, length=None, exception=OSError, bufsize=None):
+    """Copy length bytes from fileobj src to fileobj dst.
+       If length is None, copy the entire content.
+    """
+    bufsize = bufsize or 16 * 1024
+    if length == 0:
+        return
+    if length is None:
+        shutil.copyfileobj(src, dst, bufsize)
+        return
+
+    blocks, remainder = divmod(length, bufsize)
+    for b in range(blocks):
+        buf = src.read(bufsize)
+        if len(buf) < bufsize:
+            raise exception("unexpected end of data")
+        dst.write(buf)
+
+    if remainder != 0:
+        buf = src.read(remainder)
+        if len(buf) < remainder:
+            raise exception("unexpected end of data")
+        dst.write(buf)
+    return
+
+def _safe_print(s):
+    encoding = getattr(sys.stdout, 'encoding', None)
+    if encoding is not None:
+        s = s.encode(encoding, 'backslashreplace').decode(encoding)
+    print(s, end=' ')
+
+
+class TarError(Exception):
+    """Base exception."""
+    pass
+class ExtractError(TarError):
+    """General exception for extract errors."""
+    pass
+class ReadError(TarError):
+    """Exception for unreadable tar archives."""
+    pass
+class CompressionError(TarError):
+    """Exception for unavailable compression methods."""
+    pass
+class StreamError(TarError):
+    """Exception for unsupported operations on stream-like TarFiles."""
+    pass
+class HeaderError(TarError):
+    """Base exception for header errors."""
+    pass
+class EmptyHeaderError(HeaderError):
+    """Exception for empty headers."""
+    pass
+class TruncatedHeaderError(HeaderError):
+    """Exception for truncated headers."""
+    pass
+class EOFHeaderError(HeaderError):
+    """Exception for end of file headers."""
+    pass
+class InvalidHeaderError(HeaderError):
+    """Exception for invalid headers."""
+    pass
+class SubsequentHeaderError(HeaderError):
+    """Exception for missing and invalid extended headers."""
+    pass
+
+#---------------------------
+# internal stream interface
+#---------------------------
+class _LowLevelFile:
+    """Low-level file object. Supports reading and writing.
+       It is used instead of a regular file object for streaming
+       access.
+    """
+
+    def __init__(self, name, mode):
+        mode = {
+            "r": os.O_RDONLY,
+            "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
+        }[mode]
+        if hasattr(os, "O_BINARY"):
+            mode |= os.O_BINARY
+        self.fd = os.open(name, mode, 0o666)
+
+    def close(self):
+        os.close(self.fd)
+
+    def read(self, size):
+        return os.read(self.fd, size)
+
+    def write(self, s):
+        os.write(self.fd, s)
+
+class _Stream:
+    """Class that serves as an adapter between TarFile and
+       a stream-like object.  The stream-like object only
+       needs to have a read() or write() method that works with bytes,
+       and the method is accessed blockwise.
+       Use of gzip or bzip2 compression is possible.
+       A stream-like object could be for example: sys.stdin.buffer,
+       sys.stdout.buffer, a socket, a tape device etc.
+
+       _Stream is intended to be used only internally.
+    """
+
+    def __init__(self, name, mode, comptype, fileobj, bufsize,
+                 compresslevel):
+        """Construct a _Stream object.
+        """
+        self._extfileobj = True
+        if fileobj is None:
+            fileobj = _LowLevelFile(name, mode)
+            self._extfileobj = False
+
+        if comptype == '*':
+            # Enable transparent compression detection for the
+            # stream interface
+            fileobj = _StreamProxy(fileobj)
+            comptype = fileobj.getcomptype()
+
+        self.name     = name or ""
+        self.mode     = mode
+        self.comptype = comptype
+        self.fileobj  = fileobj
+        self.bufsize  = bufsize
+        self.buf      = b""
+        self.pos      = 0
+        self.closed   = False
+
+        try:
+            if comptype == "gz":
+                try:
+                    import zlib
+                except ImportError:
+                    raise CompressionError("zlib module is not available") from None
+                self.zlib = zlib
+                self.crc = zlib.crc32(b"")
+                if mode == "r":
+                    self.exception = zlib.error
+                    self._init_read_gz()
+                else:
+                    self._init_write_gz(compresslevel)
+
+            elif comptype == "bz2":
+                try:
+                    import bz2
+                except ImportError:
+                    raise CompressionError("bz2 module is not available") from None
+                if mode == "r":
+                    self.dbuf = b""
+                    self.cmp = bz2.BZ2Decompressor()
+                    self.exception = OSError
+                else:
+                    self.cmp = bz2.BZ2Compressor(compresslevel)
+
+            elif comptype == "xz":
+                try:
+                    import lzma
+                except ImportError:
+                    raise CompressionError("lzma module is not available") from None
+                if mode == "r":
+                    self.dbuf = b""
+                    self.cmp = lzma.LZMADecompressor()
+                    self.exception = lzma.LZMAError
+                else:
+                    self.cmp = lzma.LZMACompressor()
+
+            elif comptype != "tar":
+                raise CompressionError("unknown compression type %r" % comptype)
+
+        except:
+            if not self._extfileobj:
+                self.fileobj.close()
+            self.closed = True
+            raise
+
+    def __del__(self):
+        if hasattr(self, "closed") and not self.closed:
+            self.close()
+
+    def _init_write_gz(self, compresslevel):
+        """Initialize for writing with gzip compression.
+        """
+        self.cmp = self.zlib.compressobj(compresslevel,
+                                         self.zlib.DEFLATED,
+                                         -self.zlib.MAX_WBITS,
+                                         self.zlib.DEF_MEM_LEVEL,
+                                         0)
+        timestamp = struct.pack(" self.bufsize:
+            self.fileobj.write(self.buf[:self.bufsize])
+            self.buf = self.buf[self.bufsize:]
+
+    def close(self):
+        """Close the _Stream object. No operation should be
+           done on it afterwards.
+        """
+        if self.closed:
+            return
+
+        self.closed = True
+        try:
+            if self.mode == "w" and self.comptype != "tar":
+                self.buf += self.cmp.flush()
+
+            if self.mode == "w" and self.buf:
+                self.fileobj.write(self.buf)
+                self.buf = b""
+                if self.comptype == "gz":
+                    self.fileobj.write(struct.pack("= 0:
+            blocks, remainder = divmod(pos - self.pos, self.bufsize)
+            for i in range(blocks):
+                self.read(self.bufsize)
+            self.read(remainder)
+        else:
+            raise StreamError("seeking backwards is not allowed")
+        return self.pos
+
+    def read(self, size):
+        """Return the next size number of bytes from the stream."""
+        assert size is not None
+        buf = self._read(size)
+        self.pos += len(buf)
+        return buf
+
+    def _read(self, size):
+        """Return size bytes from the stream.
+        """
+        if self.comptype == "tar":
+            return self.__read(size)
+
+        c = len(self.dbuf)
+        t = [self.dbuf]
+        while c < size:
+            # Skip underlying buffer to avoid unaligned double buffering.
+            if self.buf:
+                buf = self.buf
+                self.buf = b""
+            else:
+                buf = self.fileobj.read(self.bufsize)
+                if not buf:
+                    break
+            try:
+                buf = self.cmp.decompress(buf)
+            except self.exception as e:
+                raise ReadError("invalid compressed data") from e
+            t.append(buf)
+            c += len(buf)
+        t = b"".join(t)
+        self.dbuf = t[size:]
+        return t[:size]
+
+    def __read(self, size):
+        """Return size bytes from stream. If internal buffer is empty,
+           read another block from the stream.
+        """
+        c = len(self.buf)
+        t = [self.buf]
+        while c < size:
+            buf = self.fileobj.read(self.bufsize)
+            if not buf:
+                break
+            t.append(buf)
+            c += len(buf)
+        t = b"".join(t)
+        self.buf = t[size:]
+        return t[:size]
+# class _Stream
+
+class _StreamProxy(object):
+    """Small proxy class that enables transparent compression
+       detection for the Stream interface (mode 'r|*').
+    """
+
+    def __init__(self, fileobj):
+        self.fileobj = fileobj
+        self.buf = self.fileobj.read(BLOCKSIZE)
+
+    def read(self, size):
+        self.read = self.fileobj.read
+        return self.buf
+
+    def getcomptype(self):
+        if self.buf.startswith(b"\x1f\x8b\x08"):
+            return "gz"
+        elif self.buf[0:3] == b"BZh" and self.buf[4:10] == b"1AY&SY":
+            return "bz2"
+        elif self.buf.startswith((b"\x5d\x00\x00\x80", b"\xfd7zXZ")):
+            return "xz"
+        else:
+            return "tar"
+
+    def close(self):
+        self.fileobj.close()
+# class StreamProxy
+
+#------------------------
+# Extraction file object
+#------------------------
+class _FileInFile(object):
+    """A thin wrapper around an existing file object that
+       provides a part of its data as an individual file
+       object.
+    """
+
+    def __init__(self, fileobj, offset, size, name, blockinfo=None):
+        self.fileobj = fileobj
+        self.offset = offset
+        self.size = size
+        self.position = 0
+        self.name = name
+        self.closed = False
+
+        if blockinfo is None:
+            blockinfo = [(0, size)]
+
+        # Construct a map with data and zero blocks.
+        self.map_index = 0
+        self.map = []
+        lastpos = 0
+        realpos = self.offset
+        for offset, size in blockinfo:
+            if offset > lastpos:
+                self.map.append((False, lastpos, offset, None))
+            self.map.append((True, offset, offset + size, realpos))
+            realpos += size
+            lastpos = offset + size
+        if lastpos < self.size:
+            self.map.append((False, lastpos, self.size, None))
+
+    def flush(self):
+        pass
+
+    @property
+    def mode(self):
+        return 'rb'
+
+    def readable(self):
+        return True
+
+    def writable(self):
+        return False
+
+    def seekable(self):
+        return self.fileobj.seekable()
+
+    def tell(self):
+        """Return the current file position.
+        """
+        return self.position
+
+    def seek(self, position, whence=io.SEEK_SET):
+        """Seek to a position in the file.
+        """
+        if whence == io.SEEK_SET:
+            self.position = min(max(position, 0), self.size)
+        elif whence == io.SEEK_CUR:
+            if position < 0:
+                self.position = max(self.position + position, 0)
+            else:
+                self.position = min(self.position + position, self.size)
+        elif whence == io.SEEK_END:
+            self.position = max(min(self.size + position, self.size), 0)
+        else:
+            raise ValueError("Invalid argument")
+        return self.position
+
+    def read(self, size=None):
+        """Read data from the file.
+        """
+        if size is None:
+            size = self.size - self.position
+        else:
+            size = min(size, self.size - self.position)
+
+        buf = b""
+        while size > 0:
+            while True:
+                data, start, stop, offset = self.map[self.map_index]
+                if start <= self.position < stop:
+                    break
+                else:
+                    self.map_index += 1
+                    if self.map_index == len(self.map):
+                        self.map_index = 0
+            length = min(size, stop - self.position)
+            if data:
+                self.fileobj.seek(offset + (self.position - start))
+                b = self.fileobj.read(length)
+                if len(b) != length:
+                    raise ReadError("unexpected end of data")
+                buf += b
+            else:
+                buf += NUL * length
+            size -= length
+            self.position += length
+        return buf
+
+    def readinto(self, b):
+        buf = self.read(len(b))
+        b[:len(buf)] = buf
+        return len(buf)
+
+    def close(self):
+        self.closed = True
+#class _FileInFile
+
+class ExFileObject(io.BufferedReader):
+
+    def __init__(self, tarfile, tarinfo):
+        fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data,
+                tarinfo.size, tarinfo.name, tarinfo.sparse)
+        super().__init__(fileobj)
+#class ExFileObject
+
+
+#-----------------------------
+# extraction filters (PEP 706)
+#-----------------------------
+
+class FilterError(TarError):
+    pass
+
+class AbsolutePathError(FilterError):
+    def __init__(self, tarinfo):
+        self.tarinfo = tarinfo
+        super().__init__(f'member {tarinfo.name!r} has an absolute path')
+
+class OutsideDestinationError(FilterError):
+    def __init__(self, tarinfo, path):
+        self.tarinfo = tarinfo
+        self._path = path
+        super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, '
+                         + 'which is outside the destination')
+
+class SpecialFileError(FilterError):
+    def __init__(self, tarinfo):
+        self.tarinfo = tarinfo
+        super().__init__(f'{tarinfo.name!r} is a special file')
+
+class AbsoluteLinkError(FilterError):
+    def __init__(self, tarinfo):
+        self.tarinfo = tarinfo
+        super().__init__(f'{tarinfo.name!r} is a link to an absolute path')
+
+class LinkOutsideDestinationError(FilterError):
+    def __init__(self, tarinfo, path):
+        self.tarinfo = tarinfo
+        self._path = path
+        super().__init__(f'{tarinfo.name!r} would link to {path!r}, '
+                         + 'which is outside the destination')
+
+def _get_filtered_attrs(member, dest_path, for_data=True):
+    new_attrs = {}
+    name = member.name
+    dest_path = os.path.realpath(dest_path)
+    # Strip leading / (tar's directory separator) from filenames.
+    # Include os.sep (target OS directory separator) as well.
+    if name.startswith(('/', os.sep)):
+        name = new_attrs['name'] = member.path.lstrip('/' + os.sep)
+    if os.path.isabs(name):
+        # Path is absolute even after stripping.
+        # For example, 'C:/foo' on Windows.
+        raise AbsolutePathError(member)
+    # Ensure we stay in the destination
+    target_path = os.path.realpath(os.path.join(dest_path, name))
+    if os.path.commonpath([target_path, dest_path]) != dest_path:
+        raise OutsideDestinationError(member, target_path)
+    # Limit permissions (no high bits, and go-w)
+    mode = member.mode
+    if mode is not None:
+        # Strip high bits & group/other write bits
+        mode = mode & 0o755
+        if for_data:
+            # For data, handle permissions & file types
+            if member.isreg() or member.islnk():
+                if not mode & 0o100:
+                    # Clear executable bits if not executable by user
+                    mode &= ~0o111
+                # Ensure owner can read & write
+                mode |= 0o600
+            elif member.isdir() or member.issym():
+                # Ignore mode for directories & symlinks
+                mode = None
+            else:
+                # Reject special files
+                raise SpecialFileError(member)
+        if mode != member.mode:
+            new_attrs['mode'] = mode
+    if for_data:
+        # Ignore ownership for 'data'
+        if member.uid is not None:
+            new_attrs['uid'] = None
+        if member.gid is not None:
+            new_attrs['gid'] = None
+        if member.uname is not None:
+            new_attrs['uname'] = None
+        if member.gname is not None:
+            new_attrs['gname'] = None
+        # Check link destination for 'data'
+        if member.islnk() or member.issym():
+            if os.path.isabs(member.linkname):
+                raise AbsoluteLinkError(member)
+            if member.issym():
+                target_path = os.path.join(dest_path,
+                                           os.path.dirname(name),
+                                           member.linkname)
+            else:
+                target_path = os.path.join(dest_path,
+                                           member.linkname)
+            target_path = os.path.realpath(target_path)
+            if os.path.commonpath([target_path, dest_path]) != dest_path:
+                raise LinkOutsideDestinationError(member, target_path)
+    return new_attrs
+
+def fully_trusted_filter(member, dest_path):
+    return member
+
+def tar_filter(member, dest_path):
+    new_attrs = _get_filtered_attrs(member, dest_path, False)
+    if new_attrs:
+        return member.replace(**new_attrs, deep=False)
+    return member
+
+def data_filter(member, dest_path):
+    new_attrs = _get_filtered_attrs(member, dest_path, True)
+    if new_attrs:
+        return member.replace(**new_attrs, deep=False)
+    return member
+
+_NAMED_FILTERS = {
+    "fully_trusted": fully_trusted_filter,
+    "tar": tar_filter,
+    "data": data_filter,
+}
+
+#------------------
+# Exported Classes
+#------------------
+
+# Sentinel for replace() defaults, meaning "don't change the attribute"
+_KEEP = object()
+
+class TarInfo(object):
+    """Informational class which holds the details about an
+       archive member given by a tar header block.
+       TarInfo objects are returned by TarFile.getmember(),
+       TarFile.getmembers() and TarFile.gettarinfo() and are
+       usually created internally.
+    """
+
+    __slots__ = dict(
+        name = 'Name of the archive member.',
+        mode = 'Permission bits.',
+        uid = 'User ID of the user who originally stored this member.',
+        gid = 'Group ID of the user who originally stored this member.',
+        size = 'Size in bytes.',
+        mtime = 'Time of last modification.',
+        chksum = 'Header checksum.',
+        type = ('File type. type is usually one of these constants: '
+                'REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, '
+                'CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE.'),
+        linkname = ('Name of the target file name, which is only present '
+                    'in TarInfo objects of type LNKTYPE and SYMTYPE.'),
+        uname = 'User name.',
+        gname = 'Group name.',
+        devmajor = 'Device major number.',
+        devminor = 'Device minor number.',
+        offset = 'The tar header starts here.',
+        offset_data = "The file's data starts here.",
+        pax_headers = ('A dictionary containing key-value pairs of an '
+                       'associated pax extended header.'),
+        sparse = 'Sparse member information.',
+        _tarfile = None,
+        _sparse_structs = None,
+        _link_target = None,
+        )
+
+    def __init__(self, name=""):
+        """Construct a TarInfo object. name is the optional name
+           of the member.
+        """
+        self.name = name        # member name
+        self.mode = 0o644       # file permissions
+        self.uid = 0            # user id
+        self.gid = 0            # group id
+        self.size = 0           # file size
+        self.mtime = 0          # modification time
+        self.chksum = 0         # header checksum
+        self.type = REGTYPE     # member type
+        self.linkname = ""      # link name
+        self.uname = ""         # user name
+        self.gname = ""         # group name
+        self.devmajor = 0       # device major number
+        self.devminor = 0       # device minor number
+
+        self.offset = 0         # the tar header starts here
+        self.offset_data = 0    # the file's data starts here
+
+        self.sparse = None      # sparse member information
+        self.pax_headers = {}   # pax header information
+
+    @property
+    def tarfile(self):
+        import warnings
+        warnings.warn(
+            'The undocumented "tarfile" attribute of TarInfo objects '
+            + 'is deprecated and will be removed in Python 3.16',
+            DeprecationWarning, stacklevel=2)
+        return self._tarfile
+
+    @tarfile.setter
+    def tarfile(self, tarfile):
+        import warnings
+        warnings.warn(
+            'The undocumented "tarfile" attribute of TarInfo objects '
+            + 'is deprecated and will be removed in Python 3.16',
+            DeprecationWarning, stacklevel=2)
+        self._tarfile = tarfile
+
+    @property
+    def path(self):
+        'In pax headers, "name" is called "path".'
+        return self.name
+
+    @path.setter
+    def path(self, name):
+        self.name = name
+
+    @property
+    def linkpath(self):
+        'In pax headers, "linkname" is called "linkpath".'
+        return self.linkname
+
+    @linkpath.setter
+    def linkpath(self, linkname):
+        self.linkname = linkname
+
+    def __repr__(self):
+        return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self))
+
+    def replace(self, *,
+                name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP,
+                uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP,
+                deep=True, _KEEP=_KEEP):
+        """Return a deep copy of self with the given attributes replaced.
+        """
+        if deep:
+            result = copy.deepcopy(self)
+        else:
+            result = copy.copy(self)
+        if name is not _KEEP:
+            result.name = name
+        if mtime is not _KEEP:
+            result.mtime = mtime
+        if mode is not _KEEP:
+            result.mode = mode
+        if linkname is not _KEEP:
+            result.linkname = linkname
+        if uid is not _KEEP:
+            result.uid = uid
+        if gid is not _KEEP:
+            result.gid = gid
+        if uname is not _KEEP:
+            result.uname = uname
+        if gname is not _KEEP:
+            result.gname = gname
+        return result
+
+    def get_info(self):
+        """Return the TarInfo's attributes as a dictionary.
+        """
+        if self.mode is None:
+            mode = None
+        else:
+            mode = self.mode & 0o7777
+        info = {
+            "name":     self.name,
+            "mode":     mode,
+            "uid":      self.uid,
+            "gid":      self.gid,
+            "size":     self.size,
+            "mtime":    self.mtime,
+            "chksum":   self.chksum,
+            "type":     self.type,
+            "linkname": self.linkname,
+            "uname":    self.uname,
+            "gname":    self.gname,
+            "devmajor": self.devmajor,
+            "devminor": self.devminor
+        }
+
+        if info["type"] == DIRTYPE and not info["name"].endswith("/"):
+            info["name"] += "/"
+
+        return info
+
+    def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"):
+        """Return a tar header as a string of 512 byte blocks.
+        """
+        info = self.get_info()
+        for name, value in info.items():
+            if value is None:
+                raise ValueError("%s may not be None" % name)
+
+        if format == USTAR_FORMAT:
+            return self.create_ustar_header(info, encoding, errors)
+        elif format == GNU_FORMAT:
+            return self.create_gnu_header(info, encoding, errors)
+        elif format == PAX_FORMAT:
+            return self.create_pax_header(info, encoding)
+        else:
+            raise ValueError("invalid format")
+
+    def create_ustar_header(self, info, encoding, errors):
+        """Return the object as a ustar header block.
+        """
+        info["magic"] = POSIX_MAGIC
+
+        if len(info["linkname"].encode(encoding, errors)) > LENGTH_LINK:
+            raise ValueError("linkname is too long")
+
+        if len(info["name"].encode(encoding, errors)) > LENGTH_NAME:
+            info["prefix"], info["name"] = self._posix_split_name(info["name"], encoding, errors)
+
+        return self._create_header(info, USTAR_FORMAT, encoding, errors)
+
+    def create_gnu_header(self, info, encoding, errors):
+        """Return the object as a GNU header block sequence.
+        """
+        info["magic"] = GNU_MAGIC
+
+        buf = b""
+        if len(info["linkname"].encode(encoding, errors)) > LENGTH_LINK:
+            buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors)
+
+        if len(info["name"].encode(encoding, errors)) > LENGTH_NAME:
+            buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors)
+
+        return buf + self._create_header(info, GNU_FORMAT, encoding, errors)
+
+    def create_pax_header(self, info, encoding):
+        """Return the object as a ustar header block. If it cannot be
+           represented this way, prepend a pax extended header sequence
+           with supplement information.
+        """
+        info["magic"] = POSIX_MAGIC
+        pax_headers = self.pax_headers.copy()
+
+        # Test string fields for values that exceed the field length or cannot
+        # be represented in ASCII encoding.
+        for name, hname, length in (
+                ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK),
+                ("uname", "uname", 32), ("gname", "gname", 32)):
+
+            if hname in pax_headers:
+                # The pax header has priority.
+                continue
+
+            # Try to encode the string as ASCII.
+            try:
+                info[name].encode("ascii", "strict")
+            except UnicodeEncodeError:
+                pax_headers[hname] = info[name]
+                continue
+
+            if len(info[name]) > length:
+                pax_headers[hname] = info[name]
+
+        # Test number fields for values that exceed the field limit or values
+        # that like to be stored as float.
+        for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)):
+            needs_pax = False
+
+            val = info[name]
+            val_is_float = isinstance(val, float)
+            val_int = round(val) if val_is_float else val
+            if not 0 <= val_int < 8 ** (digits - 1):
+                # Avoid overflow.
+                info[name] = 0
+                needs_pax = True
+            elif val_is_float:
+                # Put rounded value in ustar header, and full
+                # precision value in pax header.
+                info[name] = val_int
+                needs_pax = True
+
+            # The existing pax header has priority.
+            if needs_pax and name not in pax_headers:
+                pax_headers[name] = str(val)
+
+        # Create a pax extended header if necessary.
+        if pax_headers:
+            buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding)
+        else:
+            buf = b""
+
+        return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace")
+
+    @classmethod
+    def create_pax_global_header(cls, pax_headers):
+        """Return the object as a pax global header block sequence.
+        """
+        return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf-8")
+
+    def _posix_split_name(self, name, encoding, errors):
+        """Split a name longer than 100 chars into a prefix
+           and a name part.
+        """
+        components = name.split("/")
+        for i in range(1, len(components)):
+            prefix = "/".join(components[:i])
+            name = "/".join(components[i:])
+            if len(prefix.encode(encoding, errors)) <= LENGTH_PREFIX and \
+                    len(name.encode(encoding, errors)) <= LENGTH_NAME:
+                break
+        else:
+            raise ValueError("name is too long")
+
+        return prefix, name
+
+    @staticmethod
+    def _create_header(info, format, encoding, errors):
+        """Return a header block. info is a dictionary with file
+           information, format must be one of the *_FORMAT constants.
+        """
+        has_device_fields = info.get("type") in (CHRTYPE, BLKTYPE)
+        if has_device_fields:
+            devmajor = itn(info.get("devmajor", 0), 8, format)
+            devminor = itn(info.get("devminor", 0), 8, format)
+        else:
+            devmajor = stn("", 8, encoding, errors)
+            devminor = stn("", 8, encoding, errors)
+
+        # None values in metadata should cause ValueError.
+        # itn()/stn() do this for all fields except type.
+        filetype = info.get("type", REGTYPE)
+        if filetype is None:
+            raise ValueError("TarInfo.type must not be None")
+
+        parts = [
+            stn(info.get("name", ""), 100, encoding, errors),
+            itn(info.get("mode", 0) & 0o7777, 8, format),
+            itn(info.get("uid", 0), 8, format),
+            itn(info.get("gid", 0), 8, format),
+            itn(info.get("size", 0), 12, format),
+            itn(info.get("mtime", 0), 12, format),
+            b"        ", # checksum field
+            filetype,
+            stn(info.get("linkname", ""), 100, encoding, errors),
+            info.get("magic", POSIX_MAGIC),
+            stn(info.get("uname", ""), 32, encoding, errors),
+            stn(info.get("gname", ""), 32, encoding, errors),
+            devmajor,
+            devminor,
+            stn(info.get("prefix", ""), 155, encoding, errors)
+        ]
+
+        buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts))
+        chksum = calc_chksums(buf[-BLOCKSIZE:])[0]
+        buf = buf[:-364] + bytes("%06o\0" % chksum, "ascii") + buf[-357:]
+        return buf
+
+    @staticmethod
+    def _create_payload(payload):
+        """Return the string payload filled with zero bytes
+           up to the next 512 byte border.
+        """
+        blocks, remainder = divmod(len(payload), BLOCKSIZE)
+        if remainder > 0:
+            payload += (BLOCKSIZE - remainder) * NUL
+        return payload
+
+    @classmethod
+    def _create_gnu_long_header(cls, name, type, encoding, errors):
+        """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence
+           for name.
+        """
+        name = name.encode(encoding, errors) + NUL
+
+        info = {}
+        info["name"] = "././@LongLink"
+        info["type"] = type
+        info["size"] = len(name)
+        info["magic"] = GNU_MAGIC
+
+        # create extended header + name blocks.
+        return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \
+                cls._create_payload(name)
+
+    @classmethod
+    def _create_pax_generic_header(cls, pax_headers, type, encoding):
+        """Return a POSIX.1-2008 extended or global header sequence
+           that contains a list of keyword, value pairs. The values
+           must be strings.
+        """
+        # Check if one of the fields contains surrogate characters and thereby
+        # forces hdrcharset=BINARY, see _proc_pax() for more information.
+        binary = False
+        for keyword, value in pax_headers.items():
+            try:
+                value.encode("utf-8", "strict")
+            except UnicodeEncodeError:
+                binary = True
+                break
+
+        records = b""
+        if binary:
+            # Put the hdrcharset field at the beginning of the header.
+            records += b"21 hdrcharset=BINARY\n"
+
+        for keyword, value in pax_headers.items():
+            keyword = keyword.encode("utf-8")
+            if binary:
+                # Try to restore the original byte representation of 'value'.
+                # Needless to say, that the encoding must match the string.
+                value = value.encode(encoding, "surrogateescape")
+            else:
+                value = value.encode("utf-8")
+
+            l = len(keyword) + len(value) + 3   # ' ' + '=' + '\n'
+            n = p = 0
+            while True:
+                n = l + len(str(p))
+                if n == p:
+                    break
+                p = n
+            records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n"
+
+        # We use a hardcoded "././@PaxHeader" name like star does
+        # instead of the one that POSIX recommends.
+        info = {}
+        info["name"] = "././@PaxHeader"
+        info["type"] = type
+        info["size"] = len(records)
+        info["magic"] = POSIX_MAGIC
+
+        # Create pax header + record blocks.
+        return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \
+                cls._create_payload(records)
+
+    @classmethod
+    def frombuf(cls, buf, encoding, errors):
+        """Construct a TarInfo object from a 512 byte bytes object.
+        """
+        if len(buf) == 0:
+            raise EmptyHeaderError("empty header")
+        if len(buf) != BLOCKSIZE:
+            raise TruncatedHeaderError("truncated header")
+        if buf.count(NUL) == BLOCKSIZE:
+            raise EOFHeaderError("end of file header")
+
+        chksum = nti(buf[148:156])
+        if chksum not in calc_chksums(buf):
+            raise InvalidHeaderError("bad checksum")
+
+        obj = cls()
+        obj.name = nts(buf[0:100], encoding, errors)
+        obj.mode = nti(buf[100:108])
+        obj.uid = nti(buf[108:116])
+        obj.gid = nti(buf[116:124])
+        obj.size = nti(buf[124:136])
+        obj.mtime = nti(buf[136:148])
+        obj.chksum = chksum
+        obj.type = buf[156:157]
+        obj.linkname = nts(buf[157:257], encoding, errors)
+        obj.uname = nts(buf[265:297], encoding, errors)
+        obj.gname = nts(buf[297:329], encoding, errors)
+        obj.devmajor = nti(buf[329:337])
+        obj.devminor = nti(buf[337:345])
+        prefix = nts(buf[345:500], encoding, errors)
+
+        # Old V7 tar format represents a directory as a regular
+        # file with a trailing slash.
+        if obj.type == AREGTYPE and obj.name.endswith("/"):
+            obj.type = DIRTYPE
+
+        # The old GNU sparse format occupies some of the unused
+        # space in the buffer for up to 4 sparse structures.
+        # Save them for later processing in _proc_sparse().
+        if obj.type == GNUTYPE_SPARSE:
+            pos = 386
+            structs = []
+            for i in range(4):
+                try:
+                    offset = nti(buf[pos:pos + 12])
+                    numbytes = nti(buf[pos + 12:pos + 24])
+                except ValueError:
+                    break
+                structs.append((offset, numbytes))
+                pos += 24
+            isextended = bool(buf[482])
+            origsize = nti(buf[483:495])
+            obj._sparse_structs = (structs, isextended, origsize)
+
+        # Remove redundant slashes from directories.
+        if obj.isdir():
+            obj.name = obj.name.rstrip("/")
+
+        # Reconstruct a ustar longname.
+        if prefix and obj.type not in GNU_TYPES:
+            obj.name = prefix + "/" + obj.name
+        return obj
+
+    @classmethod
+    def fromtarfile(cls, tarfile):
+        """Return the next TarInfo object from TarFile object
+           tarfile.
+        """
+        buf = tarfile.fileobj.read(BLOCKSIZE)
+        obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
+        obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
+        return obj._proc_member(tarfile)
+
+    #--------------------------------------------------------------------------
+    # The following are methods that are called depending on the type of a
+    # member. The entry point is _proc_member() which can be overridden in a
+    # subclass to add custom _proc_*() methods. A _proc_*() method MUST
+    # implement the following
+    # operations:
+    # 1. Set self.offset_data to the position where the data blocks begin,
+    #    if there is data that follows.
+    # 2. Set tarfile.offset to the position where the next member's header will
+    #    begin.
+    # 3. Return self or another valid TarInfo object.
+    def _proc_member(self, tarfile):
+        """Choose the right processing method depending on
+           the type and call it.
+        """
+        if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK):
+            return self._proc_gnulong(tarfile)
+        elif self.type == GNUTYPE_SPARSE:
+            return self._proc_sparse(tarfile)
+        elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE):
+            return self._proc_pax(tarfile)
+        else:
+            return self._proc_builtin(tarfile)
+
+    def _proc_builtin(self, tarfile):
+        """Process a builtin type or an unknown type which
+           will be treated as a regular file.
+        """
+        self.offset_data = tarfile.fileobj.tell()
+        offset = self.offset_data
+        if self.isreg() or self.type not in SUPPORTED_TYPES:
+            # Skip the following data blocks.
+            offset += self._block(self.size)
+        tarfile.offset = offset
+
+        # Patch the TarInfo object with saved global
+        # header information.
+        self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors)
+
+        # Remove redundant slashes from directories. This is to be consistent
+        # with frombuf().
+        if self.isdir():
+            self.name = self.name.rstrip("/")
+
+        return self
+
+    def _proc_gnulong(self, tarfile):
+        """Process the blocks that hold a GNU longname
+           or longlink member.
+        """
+        buf = tarfile.fileobj.read(self._block(self.size))
+
+        # Fetch the next header and process it.
+        try:
+            next = self.fromtarfile(tarfile)
+        except HeaderError as e:
+            raise SubsequentHeaderError(str(e)) from None
+
+        # Patch the TarInfo object from the next header with
+        # the longname information.
+        next.offset = self.offset
+        if self.type == GNUTYPE_LONGNAME:
+            next.name = nts(buf, tarfile.encoding, tarfile.errors)
+        elif self.type == GNUTYPE_LONGLINK:
+            next.linkname = nts(buf, tarfile.encoding, tarfile.errors)
+
+        # Remove redundant slashes from directories. This is to be consistent
+        # with frombuf().
+        if next.isdir():
+            next.name = removesuffix(next.name, "/")
+
+        return next
+
+    def _proc_sparse(self, tarfile):
+        """Process a GNU sparse header plus extra headers.
+        """
+        # We already collected some sparse structures in frombuf().
+        structs, isextended, origsize = self._sparse_structs
+        del self._sparse_structs
+
+        # Collect sparse structures from extended header blocks.
+        while isextended:
+            buf = tarfile.fileobj.read(BLOCKSIZE)
+            pos = 0
+            for i in range(21):
+                try:
+                    offset = nti(buf[pos:pos + 12])
+                    numbytes = nti(buf[pos + 12:pos + 24])
+                except ValueError:
+                    break
+                if offset and numbytes:
+                    structs.append((offset, numbytes))
+                pos += 24
+            isextended = bool(buf[504])
+        self.sparse = structs
+
+        self.offset_data = tarfile.fileobj.tell()
+        tarfile.offset = self.offset_data + self._block(self.size)
+        self.size = origsize
+        return self
+
+    def _proc_pax(self, tarfile):
+        """Process an extended or global header as described in
+           POSIX.1-2008.
+        """
+        # Read the header information.
+        buf = tarfile.fileobj.read(self._block(self.size))
+
+        # A pax header stores supplemental information for either
+        # the following file (extended) or all following files
+        # (global).
+        if self.type == XGLTYPE:
+            pax_headers = tarfile.pax_headers
+        else:
+            pax_headers = tarfile.pax_headers.copy()
+
+        # Check if the pax header contains a hdrcharset field. This tells us
+        # the encoding of the path, linkpath, uname and gname fields. Normally,
+        # these fields are UTF-8 encoded but since POSIX.1-2008 tar
+        # implementations are allowed to store them as raw binary strings if
+        # the translation to UTF-8 fails.
+        match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf)
+        if match is not None:
+            pax_headers["hdrcharset"] = match.group(1).decode("utf-8")
+
+        # For the time being, we don't care about anything other than "BINARY".
+        # The only other value that is currently allowed by the standard is
+        # "ISO-IR 10646 2000 UTF-8" in other words UTF-8.
+        hdrcharset = pax_headers.get("hdrcharset")
+        if hdrcharset == "BINARY":
+            encoding = tarfile.encoding
+        else:
+            encoding = "utf-8"
+
+        # Parse pax header information. A record looks like that:
+        # "%d %s=%s\n" % (length, keyword, value). length is the size
+        # of the complete record including the length field itself and
+        # the newline. keyword and value are both UTF-8 encoded strings.
+        regex = re.compile(br"(\d+) ([^=]+)=")
+        pos = 0
+        while match := regex.match(buf, pos):
+            length, keyword = match.groups()
+            length = int(length)
+            if length == 0:
+                raise InvalidHeaderError("invalid header")
+            value = buf[match.end(2) + 1:match.start(1) + length - 1]
+
+            # Normally, we could just use "utf-8" as the encoding and "strict"
+            # as the error handler, but we better not take the risk. For
+            # example, GNU tar <= 1.23 is known to store filenames it cannot
+            # translate to UTF-8 as raw strings (unfortunately without a
+            # hdrcharset=BINARY header).
+            # We first try the strict standard encoding, and if that fails we
+            # fall back on the user's encoding and error handler.
+            keyword = self._decode_pax_field(keyword, "utf-8", "utf-8",
+                    tarfile.errors)
+            if keyword in PAX_NAME_FIELDS:
+                value = self._decode_pax_field(value, encoding, tarfile.encoding,
+                        tarfile.errors)
+            else:
+                value = self._decode_pax_field(value, "utf-8", "utf-8",
+                        tarfile.errors)
+
+            pax_headers[keyword] = value
+            pos += length
+
+        # Fetch the next header.
+        try:
+            next = self.fromtarfile(tarfile)
+        except HeaderError as e:
+            raise SubsequentHeaderError(str(e)) from None
+
+        # Process GNU sparse information.
+        if "GNU.sparse.map" in pax_headers:
+            # GNU extended sparse format version 0.1.
+            self._proc_gnusparse_01(next, pax_headers)
+
+        elif "GNU.sparse.size" in pax_headers:
+            # GNU extended sparse format version 0.0.
+            self._proc_gnusparse_00(next, pax_headers, buf)
+
+        elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0":
+            # GNU extended sparse format version 1.0.
+            self._proc_gnusparse_10(next, pax_headers, tarfile)
+
+        if self.type in (XHDTYPE, SOLARIS_XHDTYPE):
+            # Patch the TarInfo object with the extended header info.
+            next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors)
+            next.offset = self.offset
+
+            if "size" in pax_headers:
+                # If the extended header replaces the size field,
+                # we need to recalculate the offset where the next
+                # header starts.
+                offset = next.offset_data
+                if next.isreg() or next.type not in SUPPORTED_TYPES:
+                    offset += next._block(next.size)
+                tarfile.offset = offset
+
+        return next
+
+    def _proc_gnusparse_00(self, next, pax_headers, buf):
+        """Process a GNU tar extended sparse header, version 0.0.
+        """
+        offsets = []
+        for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf):
+            offsets.append(int(match.group(1)))
+        numbytes = []
+        for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf):
+            numbytes.append(int(match.group(1)))
+        next.sparse = list(zip(offsets, numbytes))
+
+    def _proc_gnusparse_01(self, next, pax_headers):
+        """Process a GNU tar extended sparse header, version 0.1.
+        """
+        sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")]
+        next.sparse = list(zip(sparse[::2], sparse[1::2]))
+
+    def _proc_gnusparse_10(self, next, pax_headers, tarfile):
+        """Process a GNU tar extended sparse header, version 1.0.
+        """
+        fields = None
+        sparse = []
+        buf = tarfile.fileobj.read(BLOCKSIZE)
+        fields, buf = buf.split(b"\n", 1)
+        fields = int(fields)
+        while len(sparse) < fields * 2:
+            if b"\n" not in buf:
+                buf += tarfile.fileobj.read(BLOCKSIZE)
+            number, buf = buf.split(b"\n", 1)
+            sparse.append(int(number))
+        next.offset_data = tarfile.fileobj.tell()
+        next.sparse = list(zip(sparse[::2], sparse[1::2]))
+
+    def _apply_pax_info(self, pax_headers, encoding, errors):
+        """Replace fields with supplemental information from a previous
+           pax extended or global header.
+        """
+        for keyword, value in pax_headers.items():
+            if keyword == "GNU.sparse.name":
+                setattr(self, "path", value)
+            elif keyword == "GNU.sparse.size":
+                setattr(self, "size", int(value))
+            elif keyword == "GNU.sparse.realsize":
+                setattr(self, "size", int(value))
+            elif keyword in PAX_FIELDS:
+                if keyword in PAX_NUMBER_FIELDS:
+                    try:
+                        value = PAX_NUMBER_FIELDS[keyword](value)
+                    except ValueError:
+                        value = 0
+                if keyword == "path":
+                    value = value.rstrip("/")
+                setattr(self, keyword, value)
+
+        self.pax_headers = pax_headers.copy()
+
+    def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors):
+        """Decode a single field from a pax record.
+        """
+        try:
+            return value.decode(encoding, "strict")
+        except UnicodeDecodeError:
+            return value.decode(fallback_encoding, fallback_errors)
+
+    def _block(self, count):
+        """Round up a byte count by BLOCKSIZE and return it,
+           e.g. _block(834) => 1024.
+        """
+        blocks, remainder = divmod(count, BLOCKSIZE)
+        if remainder:
+            blocks += 1
+        return blocks * BLOCKSIZE
+
+    def isreg(self):
+        'Return True if the Tarinfo object is a regular file.'
+        return self.type in REGULAR_TYPES
+
+    def isfile(self):
+        'Return True if the Tarinfo object is a regular file.'
+        return self.isreg()
+
+    def isdir(self):
+        'Return True if it is a directory.'
+        return self.type == DIRTYPE
+
+    def issym(self):
+        'Return True if it is a symbolic link.'
+        return self.type == SYMTYPE
+
+    def islnk(self):
+        'Return True if it is a hard link.'
+        return self.type == LNKTYPE
+
+    def ischr(self):
+        'Return True if it is a character device.'
+        return self.type == CHRTYPE
+
+    def isblk(self):
+        'Return True if it is a block device.'
+        return self.type == BLKTYPE
+
+    def isfifo(self):
+        'Return True if it is a FIFO.'
+        return self.type == FIFOTYPE
+
+    def issparse(self):
+        return self.sparse is not None
+
+    def isdev(self):
+        'Return True if it is one of character device, block device or FIFO.'
+        return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE)
+# class TarInfo
+
+class TarFile(object):
+    """The TarFile Class provides an interface to tar archives.
+    """
+
+    debug = 0                   # May be set from 0 (no msgs) to 3 (all msgs)
+
+    dereference = False         # If true, add content of linked file to the
+                                # tar file, else the link.
+
+    ignore_zeros = False        # If true, skips empty or invalid blocks and
+                                # continues processing.
+
+    errorlevel = 1              # If 0, fatal errors only appear in debug
+                                # messages (if debug >= 0). If > 0, errors
+                                # are passed to the caller as exceptions.
+
+    format = DEFAULT_FORMAT     # The format to use when creating an archive.
+
+    encoding = ENCODING         # Encoding for 8-bit character strings.
+
+    errors = None               # Error handler for unicode conversion.
+
+    tarinfo = TarInfo           # The default TarInfo class to use.
+
+    fileobject = ExFileObject   # The file-object for extractfile().
+
+    extraction_filter = None    # The default filter for extraction.
+
+    def __init__(self, name=None, mode="r", fileobj=None, format=None,
+            tarinfo=None, dereference=None, ignore_zeros=None, encoding=None,
+            errors="surrogateescape", pax_headers=None, debug=None,
+            errorlevel=None, copybufsize=None, stream=False):
+        """Open an (uncompressed) tar archive 'name'. 'mode' is either 'r' to
+           read from an existing archive, 'a' to append data to an existing
+           file or 'w' to create a new file overwriting an existing one. 'mode'
+           defaults to 'r'.
+           If 'fileobj' is given, it is used for reading or writing data. If it
+           can be determined, 'mode' is overridden by 'fileobj's mode.
+           'fileobj' is not closed, when TarFile is closed.
+        """
+        modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"}
+        if mode not in modes:
+            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
+        self.mode = mode
+        self._mode = modes[mode]
+
+        if not fileobj:
+            if self.mode == "a" and not os.path.exists(name):
+                # Create nonexistent files in append mode.
+                self.mode = "w"
+                self._mode = "wb"
+            fileobj = bltn_open(name, self._mode)
+            self._extfileobj = False
+        else:
+            if (name is None and hasattr(fileobj, "name") and
+                isinstance(fileobj.name, (str, bytes))):
+                name = fileobj.name
+            if hasattr(fileobj, "mode"):
+                self._mode = fileobj.mode
+            self._extfileobj = True
+        self.name = os.path.abspath(name) if name else None
+        self.fileobj = fileobj
+
+        self.stream = stream
+
+        # Init attributes.
+        if format is not None:
+            self.format = format
+        if tarinfo is not None:
+            self.tarinfo = tarinfo
+        if dereference is not None:
+            self.dereference = dereference
+        if ignore_zeros is not None:
+            self.ignore_zeros = ignore_zeros
+        if encoding is not None:
+            self.encoding = encoding
+        self.errors = errors
+
+        if pax_headers is not None and self.format == PAX_FORMAT:
+            self.pax_headers = pax_headers
+        else:
+            self.pax_headers = {}
+
+        if debug is not None:
+            self.debug = debug
+        if errorlevel is not None:
+            self.errorlevel = errorlevel
+
+        # Init datastructures.
+        self.copybufsize = copybufsize
+        self.closed = False
+        self.members = []       # list of members as TarInfo objects
+        self._loaded = False    # flag if all members have been read
+        self.offset = self.fileobj.tell()
+                                # current position in the archive file
+        self.inodes = {}        # dictionary caching the inodes of
+                                # archive members already added
+
+        try:
+            if self.mode == "r":
+                self.firstmember = None
+                self.firstmember = self.next()
+
+            if self.mode == "a":
+                # Move to the end of the archive,
+                # before the first empty block.
+                while True:
+                    self.fileobj.seek(self.offset)
+                    try:
+                        tarinfo = self.tarinfo.fromtarfile(self)
+                        self.members.append(tarinfo)
+                    except EOFHeaderError:
+                        self.fileobj.seek(self.offset)
+                        break
+                    except HeaderError as e:
+                        raise ReadError(str(e)) from None
+
+            if self.mode in ("a", "w", "x"):
+                self._loaded = True
+
+                if self.pax_headers:
+                    buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
+                    self.fileobj.write(buf)
+                    self.offset += len(buf)
+        except:
+            if not self._extfileobj:
+                self.fileobj.close()
+            self.closed = True
+            raise
+
+    #--------------------------------------------------------------------------
+    # Below are the classmethods which act as alternate constructors to the
+    # TarFile class. The open() method is the only one that is needed for
+    # public use; it is the "super"-constructor and is able to select an
+    # adequate "sub"-constructor for a particular compression using the mapping
+    # from OPEN_METH.
+    #
+    # This concept allows one to subclass TarFile without losing the comfort of
+    # the super-constructor. A sub-constructor is registered and made available
+    # by adding it to the mapping in OPEN_METH.
+
+    @classmethod
+    def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs):
+        r"""Open a tar archive for reading, writing or appending. Return
+           an appropriate TarFile class.
+
+           mode:
+           'r' or 'r:\*' open for reading with transparent compression
+           'r:'         open for reading exclusively uncompressed
+           'r:gz'       open for reading with gzip compression
+           'r:bz2'      open for reading with bzip2 compression
+           'r:xz'       open for reading with lzma compression
+           'a' or 'a:'  open for appending, creating the file if necessary
+           'w' or 'w:'  open for writing without compression
+           'w:gz'       open for writing with gzip compression
+           'w:bz2'      open for writing with bzip2 compression
+           'w:xz'       open for writing with lzma compression
+
+           'x' or 'x:'  create a tarfile exclusively without compression, raise
+                        an exception if the file is already created
+           'x:gz'       create a gzip compressed tarfile, raise an exception
+                        if the file is already created
+           'x:bz2'      create a bzip2 compressed tarfile, raise an exception
+                        if the file is already created
+           'x:xz'       create an lzma compressed tarfile, raise an exception
+                        if the file is already created
+
+           'r|\*'        open a stream of tar blocks with transparent compression
+           'r|'         open an uncompressed stream of tar blocks for reading
+           'r|gz'       open a gzip compressed stream of tar blocks
+           'r|bz2'      open a bzip2 compressed stream of tar blocks
+           'r|xz'       open an lzma compressed stream of tar blocks
+           'w|'         open an uncompressed stream for writing
+           'w|gz'       open a gzip compressed stream for writing
+           'w|bz2'      open a bzip2 compressed stream for writing
+           'w|xz'       open an lzma compressed stream for writing
+        """
+
+        if not name and not fileobj:
+            raise ValueError("nothing to open")
+
+        if mode in ("r", "r:*"):
+            # Find out which *open() is appropriate for opening the file.
+            def not_compressed(comptype):
+                return cls.OPEN_METH[comptype] == 'taropen'
+            error_msgs = []
+            for comptype in sorted(cls.OPEN_METH, key=not_compressed):
+                func = getattr(cls, cls.OPEN_METH[comptype])
+                if fileobj is not None:
+                    saved_pos = fileobj.tell()
+                try:
+                    return func(name, "r", fileobj, **kwargs)
+                except (ReadError, CompressionError) as e:
+                    error_msgs.append(f'- method {comptype}: {e!r}')
+                    if fileobj is not None:
+                        fileobj.seek(saved_pos)
+                    continue
+            error_msgs_summary = '\n'.join(error_msgs)
+            raise ReadError(f"file could not be opened successfully:\n{error_msgs_summary}")
+
+        elif ":" in mode:
+            filemode, comptype = mode.split(":", 1)
+            filemode = filemode or "r"
+            comptype = comptype or "tar"
+
+            # Select the *open() function according to
+            # given compression.
+            if comptype in cls.OPEN_METH:
+                func = getattr(cls, cls.OPEN_METH[comptype])
+            else:
+                raise CompressionError("unknown compression type %r" % comptype)
+            return func(name, filemode, fileobj, **kwargs)
+
+        elif "|" in mode:
+            filemode, comptype = mode.split("|", 1)
+            filemode = filemode or "r"
+            comptype = comptype or "tar"
+
+            if filemode not in ("r", "w"):
+                raise ValueError("mode must be 'r' or 'w'")
+
+            compresslevel = kwargs.pop("compresslevel", 9)
+            stream = _Stream(name, filemode, comptype, fileobj, bufsize,
+                             compresslevel)
+            try:
+                t = cls(name, filemode, stream, **kwargs)
+            except:
+                stream.close()
+                raise
+            t._extfileobj = False
+            return t
+
+        elif mode in ("a", "w", "x"):
+            return cls.taropen(name, mode, fileobj, **kwargs)
+
+        raise ValueError("undiscernible mode")
+
+    @classmethod
+    def taropen(cls, name, mode="r", fileobj=None, **kwargs):
+        """Open uncompressed tar archive name for reading or writing.
+        """
+        if mode not in ("r", "a", "w", "x"):
+            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
+        return cls(name, mode, fileobj, **kwargs)
+
+    @classmethod
+    def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
+        """Open gzip compressed tar archive name for reading or writing.
+           Appending is not allowed.
+        """
+        if mode not in ("r", "w", "x"):
+            raise ValueError("mode must be 'r', 'w' or 'x'")
+
+        try:
+            from gzip import GzipFile
+        except ImportError:
+            raise CompressionError("gzip module is not available") from None
+
+        try:
+            fileobj = GzipFile(name, mode + "b", compresslevel, fileobj)
+        except OSError as e:
+            if fileobj is not None and mode == 'r':
+                raise ReadError("not a gzip file") from e
+            raise
+
+        try:
+            t = cls.taropen(name, mode, fileobj, **kwargs)
+        except OSError as e:
+            fileobj.close()
+            if mode == 'r':
+                raise ReadError("not a gzip file") from e
+            raise
+        except:
+            fileobj.close()
+            raise
+        t._extfileobj = False
+        return t
+
+    @classmethod
+    def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
+        """Open bzip2 compressed tar archive name for reading or writing.
+           Appending is not allowed.
+        """
+        if mode not in ("r", "w", "x"):
+            raise ValueError("mode must be 'r', 'w' or 'x'")
+
+        try:
+            from bz2 import BZ2File
+        except ImportError:
+            raise CompressionError("bz2 module is not available") from None
+
+        fileobj = BZ2File(fileobj or name, mode, compresslevel=compresslevel)
+
+        try:
+            t = cls.taropen(name, mode, fileobj, **kwargs)
+        except (OSError, EOFError) as e:
+            fileobj.close()
+            if mode == 'r':
+                raise ReadError("not a bzip2 file") from e
+            raise
+        except:
+            fileobj.close()
+            raise
+        t._extfileobj = False
+        return t
+
+    @classmethod
+    def xzopen(cls, name, mode="r", fileobj=None, preset=None, **kwargs):
+        """Open lzma compressed tar archive name for reading or writing.
+           Appending is not allowed.
+        """
+        if mode not in ("r", "w", "x"):
+            raise ValueError("mode must be 'r', 'w' or 'x'")
+
+        try:
+            from lzma import LZMAFile, LZMAError
+        except ImportError:
+            raise CompressionError("lzma module is not available") from None
+
+        fileobj = LZMAFile(fileobj or name, mode, preset=preset)
+
+        try:
+            t = cls.taropen(name, mode, fileobj, **kwargs)
+        except (LZMAError, EOFError) as e:
+            fileobj.close()
+            if mode == 'r':
+                raise ReadError("not an lzma file") from e
+            raise
+        except:
+            fileobj.close()
+            raise
+        t._extfileobj = False
+        return t
+
+    # All *open() methods are registered here.
+    OPEN_METH = {
+        "tar": "taropen",   # uncompressed tar
+        "gz":  "gzopen",    # gzip compressed tar
+        "bz2": "bz2open",   # bzip2 compressed tar
+        "xz":  "xzopen"     # lzma compressed tar
+    }
+
+    #--------------------------------------------------------------------------
+    # The public methods which TarFile provides:
+
+    def close(self):
+        """Close the TarFile. In write-mode, two finishing zero blocks are
+           appended to the archive.
+        """
+        if self.closed:
+            return
+
+        self.closed = True
+        try:
+            if self.mode in ("a", "w", "x"):
+                self.fileobj.write(NUL * (BLOCKSIZE * 2))
+                self.offset += (BLOCKSIZE * 2)
+                # fill up the end with zero-blocks
+                # (like option -b20 for tar does)
+                blocks, remainder = divmod(self.offset, RECORDSIZE)
+                if remainder > 0:
+                    self.fileobj.write(NUL * (RECORDSIZE - remainder))
+        finally:
+            if not self._extfileobj:
+                self.fileobj.close()
+
+    def getmember(self, name):
+        """Return a TarInfo object for member 'name'. If 'name' can not be
+           found in the archive, KeyError is raised. If a member occurs more
+           than once in the archive, its last occurrence is assumed to be the
+           most up-to-date version.
+        """
+        tarinfo = self._getmember(name.rstrip('/'))
+        if tarinfo is None:
+            raise KeyError("filename %r not found" % name)
+        return tarinfo
+
+    def getmembers(self):
+        """Return the members of the archive as a list of TarInfo objects. The
+           list has the same order as the members in the archive.
+        """
+        self._check()
+        if not self._loaded:    # if we want to obtain a list of
+            self._load()        # all members, we first have to
+                                # scan the whole archive.
+        return self.members
+
+    def getnames(self):
+        """Return the members of the archive as a list of their names. It has
+           the same order as the list returned by getmembers().
+        """
+        return [tarinfo.name for tarinfo in self.getmembers()]
+
+    def gettarinfo(self, name=None, arcname=None, fileobj=None):
+        """Create a TarInfo object from the result of os.stat or equivalent
+           on an existing file. The file is either named by 'name', or
+           specified as a file object 'fileobj' with a file descriptor. If
+           given, 'arcname' specifies an alternative name for the file in the
+           archive, otherwise, the name is taken from the 'name' attribute of
+           'fileobj', or the 'name' argument. The name should be a text
+           string.
+        """
+        self._check("awx")
+
+        # When fileobj is given, replace name by
+        # fileobj's real name.
+        if fileobj is not None:
+            name = fileobj.name
+
+        # Building the name of the member in the archive.
+        # Backward slashes are converted to forward slashes,
+        # Absolute paths are turned to relative paths.
+        if arcname is None:
+            arcname = name
+        drv, arcname = os.path.splitdrive(arcname)
+        arcname = arcname.replace(os.sep, "/")
+        arcname = arcname.lstrip("/")
+
+        # Now, fill the TarInfo object with
+        # information specific for the file.
+        tarinfo = self.tarinfo()
+        tarinfo._tarfile = self  # To be removed in 3.16.
+
+        # Use os.stat or os.lstat, depending on if symlinks shall be resolved.
+        if fileobj is None:
+            if not self.dereference:
+                statres = os.lstat(name)
+            else:
+                statres = os.stat(name)
+        else:
+            statres = os.fstat(fileobj.fileno())
+        linkname = ""
+
+        stmd = statres.st_mode
+        if stat.S_ISREG(stmd):
+            inode = (statres.st_ino, statres.st_dev)
+            if not self.dereference and statres.st_nlink > 1 and \
+                    inode in self.inodes and arcname != self.inodes[inode]:
+                # Is it a hardlink to an already
+                # archived file?
+                type = LNKTYPE
+                linkname = self.inodes[inode]
+            else:
+                # The inode is added only if its valid.
+                # For win32 it is always 0.
+                type = REGTYPE
+                if inode[0]:
+                    self.inodes[inode] = arcname
+        elif stat.S_ISDIR(stmd):
+            type = DIRTYPE
+        elif stat.S_ISFIFO(stmd):
+            type = FIFOTYPE
+        elif stat.S_ISLNK(stmd):
+            type = SYMTYPE
+            linkname = os.readlink(name)
+        elif stat.S_ISCHR(stmd):
+            type = CHRTYPE
+        elif stat.S_ISBLK(stmd):
+            type = BLKTYPE
+        else:
+            return None
+
+        # Fill the TarInfo object with all
+        # information we can get.
+        tarinfo.name = arcname
+        tarinfo.mode = stmd
+        tarinfo.uid = statres.st_uid
+        tarinfo.gid = statres.st_gid
+        if type == REGTYPE:
+            tarinfo.size = statres.st_size
+        else:
+            tarinfo.size = 0
+        tarinfo.mtime = statres.st_mtime
+        tarinfo.type = type
+        tarinfo.linkname = linkname
+        if pwd:
+            try:
+                tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0]
+            except KeyError:
+                pass
+        if grp:
+            try:
+                tarinfo.gname = grp.getgrgid(tarinfo.gid)[0]
+            except KeyError:
+                pass
+
+        if type in (CHRTYPE, BLKTYPE):
+            if hasattr(os, "major") and hasattr(os, "minor"):
+                tarinfo.devmajor = os.major(statres.st_rdev)
+                tarinfo.devminor = os.minor(statres.st_rdev)
+        return tarinfo
+
+    def list(self, verbose=True, *, members=None):
+        """Print a table of contents to sys.stdout. If 'verbose' is False, only
+           the names of the members are printed. If it is True, an 'ls -l'-like
+           output is produced. 'members' is optional and must be a subset of the
+           list returned by getmembers().
+        """
+        # Convert tarinfo type to stat type.
+        type2mode = {REGTYPE: stat.S_IFREG, SYMTYPE: stat.S_IFLNK,
+                     FIFOTYPE: stat.S_IFIFO, CHRTYPE: stat.S_IFCHR,
+                     DIRTYPE: stat.S_IFDIR, BLKTYPE: stat.S_IFBLK}
+        self._check()
+
+        if members is None:
+            members = self
+        for tarinfo in members:
+            if verbose:
+                if tarinfo.mode is None:
+                    _safe_print("??????????")
+                else:
+                    modetype = type2mode.get(tarinfo.type, 0)
+                    _safe_print(stat.filemode(modetype | tarinfo.mode))
+                _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid,
+                                       tarinfo.gname or tarinfo.gid))
+                if tarinfo.ischr() or tarinfo.isblk():
+                    _safe_print("%10s" %
+                            ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor)))
+                else:
+                    _safe_print("%10d" % tarinfo.size)
+                if tarinfo.mtime is None:
+                    _safe_print("????-??-?? ??:??:??")
+                else:
+                    _safe_print("%d-%02d-%02d %02d:%02d:%02d" \
+                                % time.localtime(tarinfo.mtime)[:6])
+
+            _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else ""))
+
+            if verbose:
+                if tarinfo.issym():
+                    _safe_print("-> " + tarinfo.linkname)
+                if tarinfo.islnk():
+                    _safe_print("link to " + tarinfo.linkname)
+            print()
+
+    def add(self, name, arcname=None, recursive=True, *, filter=None):
+        """Add the file 'name' to the archive. 'name' may be any type of file
+           (directory, fifo, symbolic link, etc.). If given, 'arcname'
+           specifies an alternative name for the file in the archive.
+           Directories are added recursively by default. This can be avoided by
+           setting 'recursive' to False. 'filter' is a function
+           that expects a TarInfo object argument and returns the changed
+           TarInfo object, if it returns None the TarInfo object will be
+           excluded from the archive.
+        """
+        self._check("awx")
+
+        if arcname is None:
+            arcname = name
+
+        # Skip if somebody tries to archive the archive...
+        if self.name is not None and os.path.abspath(name) == self.name:
+            self._dbg(2, "tarfile: Skipped %r" % name)
+            return
+
+        self._dbg(1, name)
+
+        # Create a TarInfo object from the file.
+        tarinfo = self.gettarinfo(name, arcname)
+
+        if tarinfo is None:
+            self._dbg(1, "tarfile: Unsupported type %r" % name)
+            return
+
+        # Change or exclude the TarInfo object.
+        if filter is not None:
+            tarinfo = filter(tarinfo)
+            if tarinfo is None:
+                self._dbg(2, "tarfile: Excluded %r" % name)
+                return
+
+        # Append the tar header and data to the archive.
+        if tarinfo.isreg():
+            with bltn_open(name, "rb") as f:
+                self.addfile(tarinfo, f)
+
+        elif tarinfo.isdir():
+            self.addfile(tarinfo)
+            if recursive:
+                for f in sorted(os.listdir(name)):
+                    self.add(os.path.join(name, f), os.path.join(arcname, f),
+                            recursive, filter=filter)
+
+        else:
+            self.addfile(tarinfo)
+
+    def addfile(self, tarinfo, fileobj=None):
+        """Add the TarInfo object 'tarinfo' to the archive. If 'tarinfo' represents
+           a non zero-size regular file, the 'fileobj' argument should be a binary file,
+           and tarinfo.size bytes are read from it and added to the archive.
+           You can create TarInfo objects directly, or by using gettarinfo().
+        """
+        self._check("awx")
+
+        if fileobj is None and tarinfo.isreg() and tarinfo.size != 0:
+            raise ValueError("fileobj not provided for non zero-size regular file")
+
+        tarinfo = copy.copy(tarinfo)
+
+        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
+        self.fileobj.write(buf)
+        self.offset += len(buf)
+        bufsize=self.copybufsize
+        # If there's data to follow, append it.
+        if fileobj is not None:
+            copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize)
+            blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
+            if remainder > 0:
+                self.fileobj.write(NUL * (BLOCKSIZE - remainder))
+                blocks += 1
+            self.offset += blocks * BLOCKSIZE
+
+        self.members.append(tarinfo)
+
+    def _get_filter_function(self, filter):
+        if filter is None:
+            filter = self.extraction_filter
+            if filter is None:
+                import warnings
+                warnings.warn(
+                    'Python 3.14 will, by default, filter extracted tar '
+                    + 'archives and reject files or modify their metadata. '
+                    + 'Use the filter argument to control this behavior.',
+                    DeprecationWarning, stacklevel=3)
+                return fully_trusted_filter
+            if isinstance(filter, str):
+                raise TypeError(
+                    'String names are not supported for '
+                    + 'TarFile.extraction_filter. Use a function such as '
+                    + 'tarfile.data_filter directly.')
+            return filter
+        if callable(filter):
+            return filter
+        try:
+            return _NAMED_FILTERS[filter]
+        except KeyError:
+            raise ValueError(f"filter {filter!r} not found") from None
+
+    def extractall(self, path=".", members=None, *, numeric_owner=False,
+                   filter=None):
+        """Extract all members from the archive to the current working
+           directory and set owner, modification time and permissions on
+           directories afterwards. 'path' specifies a different directory
+           to extract to. 'members' is optional and must be a subset of the
+           list returned by getmembers(). If 'numeric_owner' is True, only
+           the numbers for user/group names are used and not the names.
+
+           The 'filter' function will be called on each member just
+           before extraction.
+           It can return a changed TarInfo or None to skip the member.
+           String names of common filters are accepted.
+        """
+        directories = []
+
+        filter_function = self._get_filter_function(filter)
+        if members is None:
+            members = self
+
+        for member in members:
+            tarinfo = self._get_extract_tarinfo(member, filter_function, path)
+            if tarinfo is None:
+                continue
+            if tarinfo.isdir():
+                # For directories, delay setting attributes until later,
+                # since permissions can interfere with extraction and
+                # extracting contents can reset mtime.
+                directories.append(tarinfo)
+            self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(),
+                              numeric_owner=numeric_owner)
+
+        # Reverse sort directories.
+        directories.sort(key=lambda a: a.name, reverse=True)
+
+        # Set correct owner, mtime and filemode on directories.
+        for tarinfo in directories:
+            dirpath = os.path.join(path, tarinfo.name)
+            try:
+                self.chown(tarinfo, dirpath, numeric_owner=numeric_owner)
+                self.utime(tarinfo, dirpath)
+                self.chmod(tarinfo, dirpath)
+            except ExtractError as e:
+                self._handle_nonfatal_error(e)
+
+    def extract(self, member, path="", set_attrs=True, *, numeric_owner=False,
+                filter=None):
+        """Extract a member from the archive to the current working directory,
+           using its full name. Its file information is extracted as accurately
+           as possible. 'member' may be a filename or a TarInfo object. You can
+           specify a different directory using 'path'. File attributes (owner,
+           mtime, mode) are set unless 'set_attrs' is False. If 'numeric_owner'
+           is True, only the numbers for user/group names are used and not
+           the names.
+
+           The 'filter' function will be called before extraction.
+           It can return a changed TarInfo or None to skip the member.
+           String names of common filters are accepted.
+        """
+        filter_function = self._get_filter_function(filter)
+        tarinfo = self._get_extract_tarinfo(member, filter_function, path)
+        if tarinfo is not None:
+            self._extract_one(tarinfo, path, set_attrs, numeric_owner)
+
+    def _get_extract_tarinfo(self, member, filter_function, path):
+        """Get filtered TarInfo (or None) from member, which might be a str"""
+        if isinstance(member, str):
+            tarinfo = self.getmember(member)
+        else:
+            tarinfo = member
+
+        unfiltered = tarinfo
+        try:
+            tarinfo = filter_function(tarinfo, path)
+        except (OSError, FilterError) as e:
+            self._handle_fatal_error(e)
+        except ExtractError as e:
+            self._handle_nonfatal_error(e)
+        if tarinfo is None:
+            self._dbg(2, "tarfile: Excluded %r" % unfiltered.name)
+            return None
+        # Prepare the link target for makelink().
+        if tarinfo.islnk():
+            tarinfo = copy.copy(tarinfo)
+            tarinfo._link_target = os.path.join(path, tarinfo.linkname)
+        return tarinfo
+
+    def _extract_one(self, tarinfo, path, set_attrs, numeric_owner):
+        """Extract from filtered tarinfo to disk"""
+        self._check("r")
+
+        try:
+            self._extract_member(tarinfo, os.path.join(path, tarinfo.name),
+                                 set_attrs=set_attrs,
+                                 numeric_owner=numeric_owner)
+        except OSError as e:
+            self._handle_fatal_error(e)
+        except ExtractError as e:
+            self._handle_nonfatal_error(e)
+
+    def _handle_nonfatal_error(self, e):
+        """Handle non-fatal error (ExtractError) according to errorlevel"""
+        if self.errorlevel > 1:
+            raise
+        else:
+            self._dbg(1, "tarfile: %s" % e)
+
+    def _handle_fatal_error(self, e):
+        """Handle "fatal" error according to self.errorlevel"""
+        if self.errorlevel > 0:
+            raise
+        elif isinstance(e, OSError):
+            if e.filename is None:
+                self._dbg(1, "tarfile: %s" % e.strerror)
+            else:
+                self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename))
+        else:
+            self._dbg(1, "tarfile: %s %s" % (type(e).__name__, e))
+
+    def extractfile(self, member):
+        """Extract a member from the archive as a file object. 'member' may be
+           a filename or a TarInfo object. If 'member' is a regular file or
+           a link, an io.BufferedReader object is returned. For all other
+           existing members, None is returned. If 'member' does not appear
+           in the archive, KeyError is raised.
+        """
+        self._check("r")
+
+        if isinstance(member, str):
+            tarinfo = self.getmember(member)
+        else:
+            tarinfo = member
+
+        if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES:
+            # Members with unknown types are treated as regular files.
+            return self.fileobject(self, tarinfo)
+
+        elif tarinfo.islnk() or tarinfo.issym():
+            if isinstance(self.fileobj, _Stream):
+                # A small but ugly workaround for the case that someone tries
+                # to extract a (sym)link as a file-object from a non-seekable
+                # stream of tar blocks.
+                raise StreamError("cannot extract (sym)link as file object")
+            else:
+                # A (sym)link's file object is its target's file object.
+                return self.extractfile(self._find_link_target(tarinfo))
+        else:
+            # If there's no data associated with the member (directory, chrdev,
+            # blkdev, etc.), return None instead of a file object.
+            return None
+
+    def _extract_member(self, tarinfo, targetpath, set_attrs=True,
+                        numeric_owner=False):
+        """Extract the TarInfo object tarinfo to a physical
+           file called targetpath.
+        """
+        # Fetch the TarInfo object for the given name
+        # and build the destination pathname, replacing
+        # forward slashes to platform specific separators.
+        targetpath = targetpath.rstrip("/")
+        targetpath = targetpath.replace("/", os.sep)
+
+        # Create all upper directories.
+        upperdirs = os.path.dirname(targetpath)
+        if upperdirs and not os.path.exists(upperdirs):
+            # Create directories that are not part of the archive with
+            # default permissions.
+            os.makedirs(upperdirs, exist_ok=True)
+
+        if tarinfo.islnk() or tarinfo.issym():
+            self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname))
+        else:
+            self._dbg(1, tarinfo.name)
+
+        if tarinfo.isreg():
+            self.makefile(tarinfo, targetpath)
+        elif tarinfo.isdir():
+            self.makedir(tarinfo, targetpath)
+        elif tarinfo.isfifo():
+            self.makefifo(tarinfo, targetpath)
+        elif tarinfo.ischr() or tarinfo.isblk():
+            self.makedev(tarinfo, targetpath)
+        elif tarinfo.islnk() or tarinfo.issym():
+            self.makelink(tarinfo, targetpath)
+        elif tarinfo.type not in SUPPORTED_TYPES:
+            self.makeunknown(tarinfo, targetpath)
+        else:
+            self.makefile(tarinfo, targetpath)
+
+        if set_attrs:
+            self.chown(tarinfo, targetpath, numeric_owner)
+            if not tarinfo.issym():
+                self.chmod(tarinfo, targetpath)
+                self.utime(tarinfo, targetpath)
+
+    #--------------------------------------------------------------------------
+    # Below are the different file methods. They are called via
+    # _extract_member() when extract() is called. They can be replaced in a
+    # subclass to implement other functionality.
+
+    def makedir(self, tarinfo, targetpath):
+        """Make a directory called targetpath.
+        """
+        try:
+            if tarinfo.mode is None:
+                # Use the system's default mode
+                os.mkdir(targetpath)
+            else:
+                # Use a safe mode for the directory, the real mode is set
+                # later in _extract_member().
+                os.mkdir(targetpath, 0o700)
+        except FileExistsError:
+            if not os.path.isdir(targetpath):
+                raise
+
+    def makefile(self, tarinfo, targetpath):
+        """Make a file called targetpath.
+        """
+        source = self.fileobj
+        source.seek(tarinfo.offset_data)
+        bufsize = self.copybufsize
+        with bltn_open(targetpath, "wb") as target:
+            if tarinfo.sparse is not None:
+                for offset, size in tarinfo.sparse:
+                    target.seek(offset)
+                    copyfileobj(source, target, size, ReadError, bufsize)
+                target.seek(tarinfo.size)
+                target.truncate()
+            else:
+                copyfileobj(source, target, tarinfo.size, ReadError, bufsize)
+
+    def makeunknown(self, tarinfo, targetpath):
+        """Make a file from a TarInfo object with an unknown type
+           at targetpath.
+        """
+        self.makefile(tarinfo, targetpath)
+        self._dbg(1, "tarfile: Unknown file type %r, " \
+                     "extracted as regular file." % tarinfo.type)
+
+    def makefifo(self, tarinfo, targetpath):
+        """Make a fifo called targetpath.
+        """
+        if hasattr(os, "mkfifo"):
+            os.mkfifo(targetpath)
+        else:
+            raise ExtractError("fifo not supported by system")
+
+    def makedev(self, tarinfo, targetpath):
+        """Make a character or block device called targetpath.
+        """
+        if not hasattr(os, "mknod") or not hasattr(os, "makedev"):
+            raise ExtractError("special devices not supported by system")
+
+        mode = tarinfo.mode
+        if mode is None:
+            # Use mknod's default
+            mode = 0o600
+        if tarinfo.isblk():
+            mode |= stat.S_IFBLK
+        else:
+            mode |= stat.S_IFCHR
+
+        os.mknod(targetpath, mode,
+                 os.makedev(tarinfo.devmajor, tarinfo.devminor))
+
+    def makelink(self, tarinfo, targetpath):
+        """Make a (symbolic) link called targetpath. If it cannot be created
+          (platform limitation), we try to make a copy of the referenced file
+          instead of a link.
+        """
+        try:
+            # For systems that support symbolic and hard links.
+            if tarinfo.issym():
+                if os.path.lexists(targetpath):
+                    # Avoid FileExistsError on following os.symlink.
+                    os.unlink(targetpath)
+                os.symlink(tarinfo.linkname, targetpath)
+            else:
+                if os.path.exists(tarinfo._link_target):
+                    os.link(tarinfo._link_target, targetpath)
+                else:
+                    self._extract_member(self._find_link_target(tarinfo),
+                                         targetpath)
+        except symlink_exception:
+            try:
+                self._extract_member(self._find_link_target(tarinfo),
+                                     targetpath)
+            except KeyError:
+                raise ExtractError("unable to resolve link inside archive") from None
+
+    def chown(self, tarinfo, targetpath, numeric_owner):
+        """Set owner of targetpath according to tarinfo. If numeric_owner
+           is True, use .gid/.uid instead of .gname/.uname. If numeric_owner
+           is False, fall back to .gid/.uid when the search based on name
+           fails.
+        """
+        if hasattr(os, "geteuid") and os.geteuid() == 0:
+            # We have to be root to do so.
+            g = tarinfo.gid
+            u = tarinfo.uid
+            if not numeric_owner:
+                try:
+                    if grp and tarinfo.gname:
+                        g = grp.getgrnam(tarinfo.gname)[2]
+                except KeyError:
+                    pass
+                try:
+                    if pwd and tarinfo.uname:
+                        u = pwd.getpwnam(tarinfo.uname)[2]
+                except KeyError:
+                    pass
+            if g is None:
+                g = -1
+            if u is None:
+                u = -1
+            try:
+                if tarinfo.issym() and hasattr(os, "lchown"):
+                    os.lchown(targetpath, u, g)
+                else:
+                    os.chown(targetpath, u, g)
+            except (OSError, OverflowError) as e:
+                # OverflowError can be raised if an ID doesn't fit in 'id_t'
+                raise ExtractError("could not change owner") from e
+
+    def chmod(self, tarinfo, targetpath):
+        """Set file permissions of targetpath according to tarinfo.
+        """
+        if tarinfo.mode is None:
+            return
+        try:
+            os.chmod(targetpath, tarinfo.mode)
+        except OSError as e:
+            raise ExtractError("could not change mode") from e
+
+    def utime(self, tarinfo, targetpath):
+        """Set modification time of targetpath according to tarinfo.
+        """
+        mtime = tarinfo.mtime
+        if mtime is None:
+            return
+        if not hasattr(os, 'utime'):
+            return
+        try:
+            os.utime(targetpath, (mtime, mtime))
+        except OSError as e:
+            raise ExtractError("could not change modification time") from e
+
+    #--------------------------------------------------------------------------
+    def next(self):
+        """Return the next member of the archive as a TarInfo object, when
+           TarFile is opened for reading. Return None if there is no more
+           available.
+        """
+        self._check("ra")
+        if self.firstmember is not None:
+            m = self.firstmember
+            self.firstmember = None
+            return m
+
+        # Advance the file pointer.
+        if self.offset != self.fileobj.tell():
+            if self.offset == 0:
+                return None
+            self.fileobj.seek(self.offset - 1)
+            if not self.fileobj.read(1):
+                raise ReadError("unexpected end of data")
+
+        # Read the next block.
+        tarinfo = None
+        while True:
+            try:
+                tarinfo = self.tarinfo.fromtarfile(self)
+            except EOFHeaderError as e:
+                if self.ignore_zeros:
+                    self._dbg(2, "0x%X: %s" % (self.offset, e))
+                    self.offset += BLOCKSIZE
+                    continue
+            except InvalidHeaderError as e:
+                if self.ignore_zeros:
+                    self._dbg(2, "0x%X: %s" % (self.offset, e))
+                    self.offset += BLOCKSIZE
+                    continue
+                elif self.offset == 0:
+                    raise ReadError(str(e)) from None
+            except EmptyHeaderError:
+                if self.offset == 0:
+                    raise ReadError("empty file") from None
+            except TruncatedHeaderError as e:
+                if self.offset == 0:
+                    raise ReadError(str(e)) from None
+            except SubsequentHeaderError as e:
+                raise ReadError(str(e)) from None
+            except Exception as e:
+                try:
+                    import zlib
+                    if isinstance(e, zlib.error):
+                        raise ReadError(f'zlib error: {e}') from None
+                    else:
+                        raise e
+                except ImportError:
+                    raise e
+            break
+
+        if tarinfo is not None:
+            # if streaming the file we do not want to cache the tarinfo
+            if not self.stream:
+                self.members.append(tarinfo)
+        else:
+            self._loaded = True
+
+        return tarinfo
+
+    #--------------------------------------------------------------------------
+    # Little helper methods:
+
+    def _getmember(self, name, tarinfo=None, normalize=False):
+        """Find an archive member by name from bottom to top.
+           If tarinfo is given, it is used as the starting point.
+        """
+        # Ensure that all members have been loaded.
+        members = self.getmembers()
+
+        # Limit the member search list up to tarinfo.
+        skipping = False
+        if tarinfo is not None:
+            try:
+                index = members.index(tarinfo)
+            except ValueError:
+                # The given starting point might be a (modified) copy.
+                # We'll later skip members until we find an equivalent.
+                skipping = True
+            else:
+                # Happy fast path
+                members = members[:index]
+
+        if normalize:
+            name = os.path.normpath(name)
+
+        for member in reversed(members):
+            if skipping:
+                if tarinfo.offset == member.offset:
+                    skipping = False
+                continue
+            if normalize:
+                member_name = os.path.normpath(member.name)
+            else:
+                member_name = member.name
+
+            if name == member_name:
+                return member
+
+        if skipping:
+            # Starting point was not found
+            raise ValueError(tarinfo)
+
+    def _load(self):
+        """Read through the entire archive file and look for readable
+           members. This should not run if the file is set to stream.
+        """
+        if not self.stream:
+            while self.next() is not None:
+                pass
+            self._loaded = True
+
+    def _check(self, mode=None):
+        """Check if TarFile is still open, and if the operation's mode
+           corresponds to TarFile's mode.
+        """
+        if self.closed:
+            raise OSError("%s is closed" % self.__class__.__name__)
+        if mode is not None and self.mode not in mode:
+            raise OSError("bad operation for mode %r" % self.mode)
+
+    def _find_link_target(self, tarinfo):
+        """Find the target member of a symlink or hardlink member in the
+           archive.
+        """
+        if tarinfo.issym():
+            # Always search the entire archive.
+            linkname = "/".join(filter(None, (os.path.dirname(tarinfo.name), tarinfo.linkname)))
+            limit = None
+        else:
+            # Search the archive before the link, because a hard link is
+            # just a reference to an already archived file.
+            linkname = tarinfo.linkname
+            limit = tarinfo
+
+        member = self._getmember(linkname, tarinfo=limit, normalize=True)
+        if member is None:
+            raise KeyError("linkname %r not found" % linkname)
+        return member
+
+    def __iter__(self):
+        """Provide an iterator object.
+        """
+        if self._loaded:
+            yield from self.members
+            return
+
+        # Yield items using TarFile's next() method.
+        # When all members have been read, set TarFile as _loaded.
+        index = 0
+        # Fix for SF #1100429: Under rare circumstances it can
+        # happen that getmembers() is called during iteration,
+        # which will have already exhausted the next() method.
+        if self.firstmember is not None:
+            tarinfo = self.next()
+            index += 1
+            yield tarinfo
+
+        while True:
+            if index < len(self.members):
+                tarinfo = self.members[index]
+            elif not self._loaded:
+                tarinfo = self.next()
+                if not tarinfo:
+                    self._loaded = True
+                    return
+            else:
+                return
+            index += 1
+            yield tarinfo
+
+    def _dbg(self, level, msg):
+        """Write debugging output to sys.stderr.
+        """
+        if level <= self.debug:
+            print(msg, file=sys.stderr)
+
+    def __enter__(self):
+        self._check()
+        return self
+
+    def __exit__(self, type, value, traceback):
+        if type is None:
+            self.close()
+        else:
+            # An exception occurred. We must not call close() because
+            # it would try to write end-of-archive blocks and padding.
+            if not self._extfileobj:
+                self.fileobj.close()
+            self.closed = True
+
+#--------------------
+# exported functions
+#--------------------
+
+def is_tarfile(name):
+    """Return True if name points to a tar archive that we
+       are able to handle, else return False.
+
+       'name' should be a string, file, or file-like object.
+    """
+    try:
+        if hasattr(name, "read"):
+            pos = name.tell()
+            t = open(fileobj=name)
+            name.seek(pos)
+        else:
+            t = open(name)
+        t.close()
+        return True
+    except TarError:
+        return False
+
+open = TarFile.open
+
+
+def main():
+    import argparse
+
+    description = 'A simple command-line interface for tarfile module.'
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument('-v', '--verbose', action='store_true', default=False,
+                        help='Verbose output')
+    parser.add_argument('--filter', metavar='',
+                        choices=_NAMED_FILTERS,
+                        help='Filter for extraction')
+
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument('-l', '--list', metavar='',
+                       help='Show listing of a tarfile')
+    group.add_argument('-e', '--extract', nargs='+',
+                       metavar=('', ''),
+                       help='Extract tarfile into target dir')
+    group.add_argument('-c', '--create', nargs='+',
+                       metavar=('', ''),
+                       help='Create tarfile from sources')
+    group.add_argument('-t', '--test', metavar='',
+                       help='Test if a tarfile is valid')
+
+    args = parser.parse_args()
+
+    if args.filter and args.extract is None:
+        parser.exit(1, '--filter is only valid for extraction\n')
+
+    if args.test is not None:
+        src = args.test
+        if is_tarfile(src):
+            with open(src, 'r') as tar:
+                tar.getmembers()
+                print(tar.getmembers(), file=sys.stderr)
+            if args.verbose:
+                print('{!r} is a tar archive.'.format(src))
+        else:
+            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
+
+    elif args.list is not None:
+        src = args.list
+        if is_tarfile(src):
+            with TarFile.open(src, 'r:*') as tf:
+                tf.list(verbose=args.verbose)
+        else:
+            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
+
+    elif args.extract is not None:
+        if len(args.extract) == 1:
+            src = args.extract[0]
+            curdir = os.curdir
+        elif len(args.extract) == 2:
+            src, curdir = args.extract
+        else:
+            parser.exit(1, parser.format_help())
+
+        if is_tarfile(src):
+            with TarFile.open(src, 'r:*') as tf:
+                tf.extractall(path=curdir, filter=args.filter)
+            if args.verbose:
+                if curdir == '.':
+                    msg = '{!r} file is extracted.'.format(src)
+                else:
+                    msg = ('{!r} file is extracted '
+                           'into {!r} directory.').format(src, curdir)
+                print(msg)
+        else:
+            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
+
+    elif args.create is not None:
+        tar_name = args.create.pop(0)
+        _, ext = os.path.splitext(tar_name)
+        compressions = {
+            # gz
+            '.gz': 'gz',
+            '.tgz': 'gz',
+            # xz
+            '.xz': 'xz',
+            '.txz': 'xz',
+            # bz2
+            '.bz2': 'bz2',
+            '.tbz': 'bz2',
+            '.tbz2': 'bz2',
+            '.tb2': 'bz2',
+        }
+        tar_mode = 'w:' + compressions[ext] if ext in compressions else 'w'
+        tar_files = args.create
+
+        with TarFile.open(tar_name, tar_mode) as tf:
+            for file_name in tar_files:
+                tf.add(file_name)
+
+        if args.verbose:
+            print('{!r} file created.'.format(tar_name))
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__main__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__main__.py
new file mode 100644
index 0000000..daf5509
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/__main__.py
@@ -0,0 +1,5 @@
+from . import main
+
+
+if __name__ == '__main__':
+    main()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/py38.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/py38.py
new file mode 100644
index 0000000..20fbbfc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/backports/tarfile/compat/py38.py
@@ -0,0 +1,24 @@
+import sys
+
+
+if sys.version_info < (3, 9):
+
+    def removesuffix(self, suffix):
+        # suffix='' should not call self[:-0].
+        if suffix and self.endswith(suffix):
+            return self[: -len(suffix)]
+        else:
+            return self[:]
+
+    def removeprefix(self, prefix):
+        if self.startswith(prefix):
+            return self[len(prefix) :]
+        else:
+            return self[:]
+else:
+
+    def removesuffix(self, suffix):
+        return self.removesuffix(suffix)
+
+    def removeprefix(self, prefix):
+        return self.removeprefix(prefix)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/METADATA
new file mode 100644
index 0000000..9d894b4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/METADATA
@@ -0,0 +1,133 @@
+Metadata-Version: 2.4
+Name: importlib_metadata
+Version: 8.7.1
+Summary: Read metadata from Python packages
+Author-email: "Jason R. Coombs" 
+License-Expression: Apache-2.0
+Project-URL: Source, https://github.com/python/importlib_metadata
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: zipp>=3.20
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: packaging; extra == "test"
+Requires-Dist: pyfakefs; extra == "test"
+Requires-Dist: flufl.flake8; extra == "test"
+Requires-Dist: pytest-perf>=0.9.2; extra == "test"
+Requires-Dist: jaraco.test>=5.4; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
+Provides-Extra: perf
+Requires-Dist: ipython; extra == "perf"
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
+Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
+Provides-Extra: cover
+Requires-Dist: pytest-cov; extra == "cover"
+Provides-Extra: enabler
+Requires-Dist: pytest-enabler>=3.4; extra == "enabler"
+Provides-Extra: type
+Requires-Dist: pytest-mypy>=1.0.1; extra == "type"
+Requires-Dist: mypy<1.19; platform_python_implementation == "PyPy" and extra == "type"
+Dynamic: license-file
+
+.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
+   :target: https://pypi.org/project/importlib_metadata
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
+
+.. image:: https://github.com/python/importlib_metadata/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest
+   :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2025-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata
+   :target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme
+
+Library to access the metadata for a Python package.
+
+This package supplies third-party access to the functionality of
+`importlib.metadata `_
+including improvements added to subsequent Python versions.
+
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+   :header-rows: 1
+
+   * - importlib_metadata
+     - stdlib
+   * - 7.0
+     - 3.13
+   * - 6.5
+     - 3.12
+   * - 4.13
+     - 3.11
+   * - 4.6
+     - 3.10
+   * - 1.4
+     - 3.8
+
+
+Usage
+=====
+
+See the `online documentation `_
+for usage details.
+
+`Finder authors
+`_ can
+also add support for custom package installers.  See the above documentation
+for details.
+
+
+Caveats
+=======
+
+This project primarily supports third-party packages installed by PyPA
+tools (or other conforming packages). It does not support:
+
+- Packages in the stdlib.
+- Packages installed without metadata.
+
+Project details
+===============
+
+ * Project home: https://github.com/python/importlib_metadata
+ * Report bugs at: https://github.com/python/importlib_metadata/issues
+ * Code hosting: https://github.com/python/importlib_metadata
+ * Documentation: https://importlib-metadata.readthedocs.io/
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more `_.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/RECORD
new file mode 100644
index 0000000..78aa761
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/RECORD
@@ -0,0 +1,21 @@
+importlib_metadata-8.7.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+importlib_metadata-8.7.1.dist-info/METADATA,sha256=o-OLnuQyYonUhkcE8w4pnudp4jCc6fSnXw3hpQrQo1Y,4670
+importlib_metadata-8.7.1.dist-info/RECORD,,
+importlib_metadata-8.7.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_metadata-8.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+importlib_metadata-8.7.1.dist-info/licenses/LICENSE,sha256=RYUC4S2Xu_ZEOGBqIARKqF6wX7CoqAe7NdvsJT_R_AQ,10278
+importlib_metadata-8.7.1.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
+importlib_metadata/__init__.py,sha256=u7Ew4-UkpzNY-ka6k-WRkDhQZS1akkLMfWs2eEnUmGo,37734
+importlib_metadata/_adapters.py,sha256=r5i8XLrKT6xmrpoREZhZrfczOYDmrVZeJBW5u0HzIGU,3797
+importlib_metadata/_collections.py,sha256=CxAhzlF3g1rwu_fMiB53JtRQiUFh0RgiMpoOvmK_ocg,760
+importlib_metadata/_compat.py,sha256=VC5ZDLlT-BcshauCShdFJvMNLntJJfZzNK1meGa-enw,1313
+importlib_metadata/_functools.py,sha256=0pA2OoiVK6wnsGq8HvVIzgdkvLiZ0nfnfw7IsndjoHk,3510
+importlib_metadata/_itertools.py,sha256=nMvp9SfHAQ_JYwK4L2i64lr3GRXGlYlikGTVzWbys_E,5351
+importlib_metadata/_meta.py,sha256=EtHyiJ5kGzWFDfKyQ2XQp6Vu113CeadKW1Vf6aGc1B4,1765
+importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+importlib_metadata/_typing.py,sha256=EQKhhsEgz_Sa-FnePI-faC72rNOOQwopjA1i5pG8FDU,367
+importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
+importlib_metadata/compat/py39.py,sha256=J3W7PUVRPNYMmcvT12RF8ndBU9e8_T0Ac4U87Bsrq70,1187
+importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
+importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/WHEEL
new file mode 100644
index 0000000..e7fa31b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.9.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..5c1d8bb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE
@@ -0,0 +1,73 @@
+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 2025 [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/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/top_level.txt
new file mode 100644
index 0000000..bbb0754
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+importlib_metadata
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/__init__.py
new file mode 100644
index 0000000..508b02e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/__init__.py
@@ -0,0 +1,1191 @@
+"""
+APIs exposing metadata from third-party Python packages.
+
+This codebase is shared between importlib.metadata in the stdlib
+and importlib_metadata in PyPI. See
+https://github.com/python/importlib_metadata/wiki/Development-Methodology
+for more detail.
+"""
+
+from __future__ import annotations
+
+import abc
+import collections
+import email
+import functools
+import itertools
+import operator
+import os
+import pathlib
+import posixpath
+import re
+import sys
+import textwrap
+import types
+from collections.abc import Iterable, Mapping
+from contextlib import suppress
+from importlib import import_module
+from importlib.abc import MetaPathFinder
+from itertools import starmap
+from typing import Any
+
+from . import _meta
+from ._collections import FreezableDefaultDict, Pair
+from ._compat import (
+    NullFinder,
+    install,
+)
+from ._functools import method_cache, noop, pass_none, passthrough
+from ._itertools import always_iterable, bucket, unique_everseen
+from ._meta import PackageMetadata, SimplePath
+from ._typing import md_none
+from .compat import py39, py311
+
+__all__ = [
+    'Distribution',
+    'DistributionFinder',
+    'PackageMetadata',
+    'PackageNotFoundError',
+    'SimplePath',
+    'distribution',
+    'distributions',
+    'entry_points',
+    'files',
+    'metadata',
+    'packages_distributions',
+    'requires',
+    'version',
+]
+
+
+class PackageNotFoundError(ModuleNotFoundError):
+    """The package was not found."""
+
+    def __str__(self) -> str:
+        return f"No package metadata was found for {self.name}"
+
+    @property
+    def name(self) -> str:  # type: ignore[override] # make readonly
+        (name,) = self.args
+        return name
+
+
+class Sectioned:
+    """
+    A simple entry point config parser for performance
+
+    >>> for item in Sectioned.read(Sectioned._sample):
+    ...     print(item)
+    Pair(name='sec1', value='# comments ignored')
+    Pair(name='sec1', value='a = 1')
+    Pair(name='sec1', value='b = 2')
+    Pair(name='sec2', value='a = 2')
+
+    >>> res = Sectioned.section_pairs(Sectioned._sample)
+    >>> item = next(res)
+    >>> item.name
+    'sec1'
+    >>> item.value
+    Pair(name='a', value='1')
+    >>> item = next(res)
+    >>> item.value
+    Pair(name='b', value='2')
+    >>> item = next(res)
+    >>> item.name
+    'sec2'
+    >>> item.value
+    Pair(name='a', value='2')
+    >>> list(res)
+    []
+    """
+
+    _sample = textwrap.dedent(
+        """
+        [sec1]
+        # comments ignored
+        a = 1
+        b = 2
+
+        [sec2]
+        a = 2
+        """
+    ).lstrip()
+
+    @classmethod
+    def section_pairs(cls, text):
+        return (
+            section._replace(value=Pair.parse(section.value))
+            for section in cls.read(text, filter_=cls.valid)
+            if section.name is not None
+        )
+
+    @staticmethod
+    def read(text, filter_=None):
+        lines = filter(filter_, map(str.strip, text.splitlines()))
+        name = None
+        for value in lines:
+            section_match = value.startswith('[') and value.endswith(']')
+            if section_match:
+                name = value.strip('[]')
+                continue
+            yield Pair(name, value)
+
+    @staticmethod
+    def valid(line: str):
+        return line and not line.startswith('#')
+
+
+class _EntryPointMatch(types.SimpleNamespace):
+    module: str
+    attr: str
+    extras: str
+
+
+class EntryPoint:
+    """An entry point as defined by Python packaging conventions.
+
+    See `the packaging docs on entry points
+    `_
+    for more information.
+
+    >>> ep = EntryPoint(
+    ...     name=None, group=None, value='package.module:attr [extra1, extra2]')
+    >>> ep.module
+    'package.module'
+    >>> ep.attr
+    'attr'
+    >>> ep.extras
+    ['extra1', 'extra2']
+
+    If the value package or module are not valid identifiers, a
+    ValueError is raised on access.
+
+    >>> EntryPoint(name=None, group=None, value='invalid-name').module
+    Traceback (most recent call last):
+    ...
+    ValueError: ('Invalid object reference...invalid-name...
+    >>> EntryPoint(name=None, group=None, value='invalid-name').attr
+    Traceback (most recent call last):
+    ...
+    ValueError: ('Invalid object reference...invalid-name...
+    >>> EntryPoint(name=None, group=None, value='invalid-name').extras
+    Traceback (most recent call last):
+    ...
+    ValueError: ('Invalid object reference...invalid-name...
+
+    The same thing happens on construction.
+
+    >>> EntryPoint(name=None, group=None, value='invalid-name')
+    Traceback (most recent call last):
+    ...
+    ValueError: ('Invalid object reference...invalid-name...
+
+    """
+
+    pattern = re.compile(
+        r'(?P[\w.]+)\s*'
+        r'(:\s*(?P[\w.]+)\s*)?'
+        r'((?P\[.*\])\s*)?$'
+    )
+    """
+    A regular expression describing the syntax for an entry point,
+    which might look like:
+
+        - module
+        - package.module
+        - package.module:attribute
+        - package.module:object.attribute
+        - package.module:attr [extra1, extra2]
+
+    Other combinations are possible as well.
+
+    The expression is lenient about whitespace around the ':',
+    following the attr, and following any extras.
+    """
+
+    name: str
+    value: str
+    group: str
+
+    dist: Distribution | None = None
+
+    def __init__(self, name: str, value: str, group: str) -> None:
+        vars(self).update(name=name, value=value, group=group)
+        self.module
+
+    def load(self) -> Any:
+        """Load the entry point from its definition. If only a module
+        is indicated by the value, return that module. Otherwise,
+        return the named object.
+        """
+        module = import_module(self.module)
+        attrs = filter(None, (self.attr or '').split('.'))
+        return functools.reduce(getattr, attrs, module)
+
+    @property
+    def module(self) -> str:
+        return self._match.module
+
+    @property
+    def attr(self) -> str:
+        return self._match.attr
+
+    @property
+    def extras(self) -> list[str]:
+        return re.findall(r'\w+', self._match.extras or '')
+
+    @functools.cached_property
+    def _match(self) -> _EntryPointMatch:
+        match = self.pattern.match(self.value)
+        if not match:
+            raise ValueError(
+                'Invalid object reference. '
+                'See https://packaging.python.org'
+                '/en/latest/specifications/entry-points/#data-model',
+                self.value,
+            )
+        return _EntryPointMatch(**match.groupdict())
+
+    def _for(self, dist):
+        vars(self).update(dist=dist)
+        return self
+
+    def matches(self, **params):
+        """
+        EntryPoint matches the given parameters.
+
+        >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]')
+        >>> ep.matches(group='foo')
+        True
+        >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]')
+        True
+        >>> ep.matches(group='foo', name='other')
+        False
+        >>> ep.matches()
+        True
+        >>> ep.matches(extras=['extra1', 'extra2'])
+        True
+        >>> ep.matches(module='bing')
+        True
+        >>> ep.matches(attr='bong')
+        True
+        """
+        self._disallow_dist(params)
+        attrs = (getattr(self, param) for param in params)
+        return all(map(operator.eq, params.values(), attrs))
+
+    @staticmethod
+    def _disallow_dist(params):
+        """
+        Querying by dist is not allowed (dist objects are not comparable).
+        >>> EntryPoint(name='fan', value='fav', group='fag').matches(dist='foo')
+        Traceback (most recent call last):
+        ...
+        ValueError: "dist" is not suitable for matching...
+        """
+        if "dist" in params:
+            raise ValueError(
+                '"dist" is not suitable for matching. '
+                "Instead, use Distribution.entry_points.select() on a "
+                "located distribution."
+            )
+
+    def _key(self):
+        return self.name, self.value, self.group
+
+    def __lt__(self, other):
+        return self._key() < other._key()
+
+    def __eq__(self, other):
+        return self._key() == other._key()
+
+    def __setattr__(self, name, value):
+        raise AttributeError("EntryPoint objects are immutable.")
+
+    def __repr__(self):
+        return (
+            f'EntryPoint(name={self.name!r}, value={self.value!r}, '
+            f'group={self.group!r})'
+        )
+
+    def __hash__(self) -> int:
+        return hash(self._key())
+
+
+class EntryPoints(tuple):
+    """
+    An immutable collection of selectable EntryPoint objects.
+    """
+
+    __slots__ = ()
+
+    def __getitem__(self, name: str) -> EntryPoint:  # type: ignore[override] # Work with str instead of int
+        """
+        Get the EntryPoint in self matching name.
+        """
+        try:
+            return next(iter(self.select(name=name)))
+        except StopIteration:
+            raise KeyError(name)
+
+    def __repr__(self):
+        """
+        Repr with classname and tuple constructor to
+        signal that we deviate from regular tuple behavior.
+        """
+        return '%s(%r)' % (self.__class__.__name__, tuple(self))
+
+    def select(self, **params) -> EntryPoints:
+        """
+        Select entry points from self that match the
+        given parameters (typically group and/or name).
+        """
+        return EntryPoints(ep for ep in self if py39.ep_matches(ep, **params))
+
+    @property
+    def names(self) -> set[str]:
+        """
+        Return the set of all names of all entry points.
+        """
+        return {ep.name for ep in self}
+
+    @property
+    def groups(self) -> set[str]:
+        """
+        Return the set of all groups of all entry points.
+        """
+        return {ep.group for ep in self}
+
+    @classmethod
+    def _from_text_for(cls, text, dist):
+        return cls(ep._for(dist) for ep in cls._from_text(text))
+
+    @staticmethod
+    def _from_text(text):
+        return (
+            EntryPoint(name=item.value.name, value=item.value.value, group=item.name)
+            for item in Sectioned.section_pairs(text or '')
+        )
+
+
+class PackagePath(pathlib.PurePosixPath):
+    """A reference to a path in a package"""
+
+    hash: FileHash | None
+    size: int
+    dist: Distribution
+
+    def read_text(self, encoding: str = 'utf-8') -> str:
+        return self.locate().read_text(encoding=encoding)
+
+    def read_binary(self) -> bytes:
+        return self.locate().read_bytes()
+
+    def locate(self) -> SimplePath:
+        """Return a path-like object for this path"""
+        return self.dist.locate_file(self)
+
+
+class FileHash:
+    def __init__(self, spec: str) -> None:
+        self.mode, _, self.value = spec.partition('=')
+
+    def __repr__(self) -> str:
+        return f''
+
+
+class Distribution(metaclass=abc.ABCMeta):
+    """
+    An abstract Python distribution package.
+
+    Custom providers may derive from this class and define
+    the abstract methods to provide a concrete implementation
+    for their environment. Some providers may opt to override
+    the default implementation of some properties to bypass
+    the file-reading mechanism.
+    """
+
+    @abc.abstractmethod
+    def read_text(self, filename) -> str | None:
+        """Attempt to load metadata file given by the name.
+
+        Python distribution metadata is organized by blobs of text
+        typically represented as "files" in the metadata directory
+        (e.g. package-1.0.dist-info). These files include things
+        like:
+
+        - METADATA: The distribution metadata including fields
+          like Name and Version and Description.
+        - entry_points.txt: A series of entry points as defined in
+          `the entry points spec `_.
+        - RECORD: A record of files according to
+          `this recording spec `_.
+
+        A package may provide any set of files, including those
+        not listed here or none at all.
+
+        :param filename: The name of the file in the distribution info.
+        :return: The text if found, otherwise None.
+        """
+
+    @abc.abstractmethod
+    def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
+        """
+        Given a path to a file in this distribution, return a SimplePath
+        to it.
+
+        This method is used by callers of ``Distribution.files()`` to
+        locate files within the distribution. If it's possible for a
+        Distribution to represent files in the distribution as
+        ``SimplePath`` objects, it should implement this method
+        to resolve such objects.
+
+        Some Distribution providers may elect not to resolve SimplePath
+        objects within the distribution by raising a
+        NotImplementedError, but consumers of such a Distribution would
+        be unable to invoke ``Distribution.files()``.
+        """
+
+    @classmethod
+    def from_name(cls, name: str) -> Distribution:
+        """Return the Distribution for the given package name.
+
+        :param name: The name of the distribution package to search for.
+        :return: The Distribution instance (or subclass thereof) for the named
+            package, if found.
+        :raises PackageNotFoundError: When the named package's distribution
+            metadata cannot be found.
+        :raises ValueError: When an invalid value is supplied for name.
+        """
+        if not name:
+            raise ValueError("A distribution name is required.")
+        try:
+            return next(iter(cls._prefer_valid(cls.discover(name=name))))
+        except StopIteration:
+            raise PackageNotFoundError(name)
+
+    @classmethod
+    def discover(
+        cls, *, context: DistributionFinder.Context | None = None, **kwargs
+    ) -> Iterable[Distribution]:
+        """Return an iterable of Distribution objects for all packages.
+
+        Pass a ``context`` or pass keyword arguments for constructing
+        a context.
+
+        :context: A ``DistributionFinder.Context`` object.
+        :return: Iterable of Distribution objects for packages matching
+          the context.
+        """
+        if context and kwargs:
+            raise ValueError("cannot accept context and kwargs")
+        context = context or DistributionFinder.Context(**kwargs)
+        return itertools.chain.from_iterable(
+            resolver(context) for resolver in cls._discover_resolvers()
+        )
+
+    @staticmethod
+    def _prefer_valid(dists: Iterable[Distribution]) -> Iterable[Distribution]:
+        """
+        Prefer (move to the front) distributions that have metadata.
+
+        Ref python/importlib_resources#489.
+        """
+        buckets = bucket(dists, lambda dist: bool(dist.metadata))
+        return itertools.chain(buckets[True], buckets[False])
+
+    @staticmethod
+    def at(path: str | os.PathLike[str]) -> Distribution:
+        """Return a Distribution for the indicated metadata path.
+
+        :param path: a string or path-like object
+        :return: a concrete Distribution instance for the path
+        """
+        return PathDistribution(pathlib.Path(path))
+
+    @staticmethod
+    def _discover_resolvers():
+        """Search the meta_path for resolvers (MetadataPathFinders)."""
+        declared = (
+            getattr(finder, 'find_distributions', None) for finder in sys.meta_path
+        )
+        return filter(None, declared)
+
+    @property
+    def metadata(self) -> _meta.PackageMetadata | None:
+        """Return the parsed metadata for this Distribution.
+
+        The returned object will have keys that name the various bits of
+        metadata per the
+        `Core metadata specifications `_.
+
+        Custom providers may provide the METADATA file or override this
+        property.
+        """
+
+        text = (
+            self.read_text('METADATA')
+            or self.read_text('PKG-INFO')
+            # This last clause is here to support old egg-info files.  Its
+            # effect is to just end up using the PathDistribution's self._path
+            # (which points to the egg-info file) attribute unchanged.
+            or self.read_text('')
+        )
+        return self._assemble_message(text)
+
+    @staticmethod
+    @pass_none
+    def _assemble_message(text: str) -> _meta.PackageMetadata:
+        # deferred for performance (python/cpython#109829)
+        from . import _adapters
+
+        return _adapters.Message(email.message_from_string(text))
+
+    @property
+    def name(self) -> str:
+        """Return the 'Name' metadata for the distribution package."""
+        return md_none(self.metadata)['Name']
+
+    @property
+    def _normalized_name(self):
+        """Return a normalized version of the name."""
+        return Prepared.normalize(self.name)
+
+    @property
+    def version(self) -> str:
+        """Return the 'Version' metadata for the distribution package."""
+        return md_none(self.metadata)['Version']
+
+    @property
+    def entry_points(self) -> EntryPoints:
+        """
+        Return EntryPoints for this distribution.
+
+        Custom providers may provide the ``entry_points.txt`` file
+        or override this property.
+        """
+        return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
+
+    @property
+    def files(self) -> list[PackagePath] | None:
+        """Files in this distribution.
+
+        :return: List of PackagePath for this distribution or None
+
+        Result is `None` if the metadata file that enumerates files
+        (i.e. RECORD for dist-info, or installed-files.txt or
+        SOURCES.txt for egg-info) is missing.
+        Result may be empty if the metadata exists but is empty.
+
+        Custom providers are recommended to provide a "RECORD" file (in
+        ``read_text``) or override this property to allow for callers to be
+        able to resolve filenames provided by the package.
+        """
+
+        def make_file(name, hash=None, size_str=None):
+            result = PackagePath(name)
+            result.hash = FileHash(hash) if hash else None
+            result.size = int(size_str) if size_str else None
+            result.dist = self
+            return result
+
+        @pass_none
+        def make_files(lines):
+            # Delay csv import, since Distribution.files is not as widely used
+            # as other parts of importlib.metadata
+            import csv
+
+            return starmap(make_file, csv.reader(lines))
+
+        @pass_none
+        def skip_missing_files(package_paths):
+            return list(filter(lambda path: path.locate().exists(), package_paths))
+
+        return skip_missing_files(
+            make_files(
+                self._read_files_distinfo()
+                or self._read_files_egginfo_installed()
+                or self._read_files_egginfo_sources()
+            )
+        )
+
+    def _read_files_distinfo(self):
+        """
+        Read the lines of RECORD.
+        """
+        text = self.read_text('RECORD')
+        return text and text.splitlines()
+
+    def _read_files_egginfo_installed(self):
+        """
+        Read installed-files.txt and return lines in a similar
+        CSV-parsable format as RECORD: each file must be placed
+        relative to the site-packages directory and must also be
+        quoted (since file names can contain literal commas).
+
+        This file is written when the package is installed by pip,
+        but it might not be written for other installation methods.
+        Assume the file is accurate if it exists.
+        """
+        text = self.read_text('installed-files.txt')
+        # Prepend the .egg-info/ subdir to the lines in this file.
+        # But this subdir is only available from PathDistribution's
+        # self._path.
+        subdir = getattr(self, '_path', None)
+        if not text or not subdir:
+            return
+
+        paths = (
+            py311
+            .relative_fix((subdir / name).resolve())
+            .relative_to(self.locate_file('').resolve(), walk_up=True)
+            .as_posix()
+            for name in text.splitlines()
+        )
+        return map('"{}"'.format, paths)
+
+    def _read_files_egginfo_sources(self):
+        """
+        Read SOURCES.txt and return lines in a similar CSV-parsable
+        format as RECORD: each file name must be quoted (since it
+        might contain literal commas).
+
+        Note that SOURCES.txt is not a reliable source for what
+        files are installed by a package. This file is generated
+        for a source archive, and the files that are present
+        there (e.g. setup.py) may not correctly reflect the files
+        that are present after the package has been installed.
+        """
+        text = self.read_text('SOURCES.txt')
+        return text and map('"{}"'.format, text.splitlines())
+
+    @property
+    def requires(self) -> list[str] | None:
+        """Generated requirements specified for this Distribution"""
+        reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
+        return reqs and list(reqs)
+
+    def _read_dist_info_reqs(self):
+        return self.metadata.get_all('Requires-Dist')
+
+    def _read_egg_info_reqs(self):
+        source = self.read_text('requires.txt')
+        return pass_none(self._deps_from_requires_text)(source)
+
+    @classmethod
+    def _deps_from_requires_text(cls, source):
+        return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source))
+
+    @staticmethod
+    def _convert_egg_info_reqs_to_simple_reqs(sections):
+        """
+        Historically, setuptools would solicit and store 'extra'
+        requirements, including those with environment markers,
+        in separate sections. More modern tools expect each
+        dependency to be defined separately, with any relevant
+        extras and environment markers attached directly to that
+        requirement. This method converts the former to the
+        latter. See _test_deps_from_requires_text for an example.
+        """
+
+        def make_condition(name):
+            return name and f'extra == "{name}"'
+
+        def quoted_marker(section):
+            section = section or ''
+            extra, sep, markers = section.partition(':')
+            if extra and markers:
+                markers = f'({markers})'
+            conditions = list(filter(None, [markers, make_condition(extra)]))
+            return '; ' + ' and '.join(conditions) if conditions else ''
+
+        def url_req_space(req):
+            """
+            PEP 508 requires a space between the url_spec and the quoted_marker.
+            Ref python/importlib_metadata#357.
+            """
+            # '@' is uniquely indicative of a url_req.
+            return ' ' * ('@' in req)
+
+        for section in sections:
+            space = url_req_space(section.value)
+            yield section.value + space + quoted_marker(section.name)
+
+    @property
+    def origin(self):
+        return self._load_json('direct_url.json')
+
+    def _load_json(self, filename):
+        # Deferred for performance (python/importlib_metadata#503)
+        import json
+
+        return pass_none(json.loads)(
+            self.read_text(filename),
+            object_hook=lambda data: types.SimpleNamespace(**data),
+        )
+
+
+class DistributionFinder(MetaPathFinder):
+    """
+    A MetaPathFinder capable of discovering installed distributions.
+
+    Custom providers should implement this interface in order to
+    supply metadata.
+    """
+
+    class Context:
+        """
+        Keyword arguments presented by the caller to
+        ``distributions()`` or ``Distribution.discover()``
+        to narrow the scope of a search for distributions
+        in all DistributionFinders.
+
+        Each DistributionFinder may expect any parameters
+        and should attempt to honor the canonical
+        parameters defined below when appropriate.
+
+        This mechanism gives a custom provider a means to
+        solicit additional details from the caller beyond
+        "name" and "path" when searching distributions.
+        For example, imagine a provider that exposes suites
+        of packages in either a "public" or "private" ``realm``.
+        A caller may wish to query only for distributions in
+        a particular realm and could call
+        ``distributions(realm="private")`` to signal to the
+        custom provider to only include distributions from that
+        realm.
+        """
+
+        name = None
+        """
+        Specific name for which a distribution finder should match.
+        A name of ``None`` matches all distributions.
+        """
+
+        def __init__(self, **kwargs):
+            vars(self).update(kwargs)
+
+        @property
+        def path(self) -> list[str]:
+            """
+            The sequence of directory path that a distribution finder
+            should search.
+
+            Typically refers to Python installed package paths such as
+            "site-packages" directories and defaults to ``sys.path``.
+            """
+            return vars(self).get('path', sys.path)
+
+    @abc.abstractmethod
+    def find_distributions(self, context=Context()) -> Iterable[Distribution]:
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching the ``context``,
+        a DistributionFinder.Context instance.
+        """
+
+
+@passthrough
+def _clear_after_fork(cached):
+    """Ensure ``func`` clears cached state after ``fork`` when supported.
+
+    ``FastPath`` caches zip-backed ``pathlib.Path`` objects that retain a
+    reference to the parent's open ``ZipFile`` handle. Re-using a cached
+    instance in a forked child can therefore resurrect invalid file pointers
+    and trigger ``BadZipFile``/``OSError`` failures (python/importlib_metadata#520).
+    Registering ``cache_clear`` with ``os.register_at_fork`` keeps each process
+    on its own cache.
+    """
+    getattr(os, 'register_at_fork', noop)(after_in_child=cached.cache_clear)
+
+
+class FastPath:
+    """
+    Micro-optimized class for searching a root for children.
+
+    Root is a path on the file system that may contain metadata
+    directories either as natural directories or within a zip file.
+
+    >>> FastPath('').children()
+    ['...']
+
+    FastPath objects are cached and recycled for any given root.
+
+    >>> FastPath('foobar') is FastPath('foobar')
+    True
+    """
+
+    @_clear_after_fork  # type: ignore[misc]
+    @functools.lru_cache()
+    def __new__(cls, root):
+        return super().__new__(cls)
+
+    def __init__(self, root):
+        self.root = root
+
+    def joinpath(self, child):
+        return pathlib.Path(self.root, child)
+
+    def children(self):
+        with suppress(Exception):
+            return os.listdir(self.root or '.')
+        with suppress(Exception):
+            return self.zip_children()
+        return []
+
+    def zip_children(self):
+        # deferred for performance (python/importlib_metadata#502)
+        from zipp.compat.overlay import zipfile
+
+        zip_path = zipfile.Path(self.root)
+        names = zip_path.root.namelist()
+        self.joinpath = zip_path.joinpath
+
+        return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names)
+
+    def search(self, name):
+        return self.lookup(self.mtime).search(name)
+
+    @property
+    def mtime(self):
+        with suppress(OSError):
+            return os.stat(self.root).st_mtime
+        self.lookup.cache_clear()
+
+    @method_cache
+    def lookup(self, mtime):
+        return Lookup(self)
+
+
+class Lookup:
+    """
+    A micro-optimized class for searching a (fast) path for metadata.
+    """
+
+    def __init__(self, path: FastPath):
+        """
+        Calculate all of the children representing metadata.
+
+        From the children in the path, calculate early all of the
+        children that appear to represent metadata (infos) or legacy
+        metadata (eggs).
+        """
+
+        base = os.path.basename(path.root).lower()
+        base_is_egg = base.endswith(".egg")
+        self.infos = FreezableDefaultDict(list)
+        self.eggs = FreezableDefaultDict(list)
+
+        for child in path.children():
+            low = child.lower()
+            if low.endswith((".dist-info", ".egg-info")):
+                # rpartition is faster than splitext and suitable for this purpose.
+                name = low.rpartition(".")[0].partition("-")[0]
+                normalized = Prepared.normalize(name)
+                self.infos[normalized].append(path.joinpath(child))
+            elif base_is_egg and low == "egg-info":
+                name = base.rpartition(".")[0].partition("-")[0]
+                legacy_normalized = Prepared.legacy_normalize(name)
+                self.eggs[legacy_normalized].append(path.joinpath(child))
+
+        self.infos.freeze()
+        self.eggs.freeze()
+
+    def search(self, prepared: Prepared):
+        """
+        Yield all infos and eggs matching the Prepared query.
+        """
+        infos = (
+            self.infos[prepared.normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.infos.values())
+        )
+        eggs = (
+            self.eggs[prepared.legacy_normalized]
+            if prepared
+            else itertools.chain.from_iterable(self.eggs.values())
+        )
+        return itertools.chain(infos, eggs)
+
+
+class Prepared:
+    """
+    A prepared search query for metadata on a possibly-named package.
+
+    Pre-calculates the normalization to prevent repeated operations.
+
+    >>> none = Prepared(None)
+    >>> none.normalized
+    >>> none.legacy_normalized
+    >>> bool(none)
+    False
+    >>> sample = Prepared('Sample__Pkg-name.foo')
+    >>> sample.normalized
+    'sample_pkg_name_foo'
+    >>> sample.legacy_normalized
+    'sample__pkg_name.foo'
+    >>> bool(sample)
+    True
+    """
+
+    normalized = None
+    legacy_normalized = None
+
+    def __init__(self, name: str | None):
+        self.name = name
+        if name is None:
+            return
+        self.normalized = self.normalize(name)
+        self.legacy_normalized = self.legacy_normalize(name)
+
+    @staticmethod
+    def normalize(name):
+        """
+        PEP 503 normalization plus dashes as underscores.
+        """
+        return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_')
+
+    @staticmethod
+    def legacy_normalize(name):
+        """
+        Normalize the package name as found in the convention in
+        older packaging tools versions and specs.
+        """
+        return name.lower().replace('-', '_')
+
+    def __bool__(self):
+        return bool(self.name)
+
+
+@install
+class MetadataPathFinder(NullFinder, DistributionFinder):
+    """A degenerate finder for distribution packages on the file system.
+
+    This finder supplies only a find_distributions() method for versions
+    of Python that do not have a PathFinder find_distributions().
+    """
+
+    @classmethod
+    def find_distributions(
+        cls, context=DistributionFinder.Context()
+    ) -> Iterable[PathDistribution]:
+        """
+        Find distributions.
+
+        Return an iterable of all Distribution instances capable of
+        loading the metadata for packages matching ``context.name``
+        (or all names if ``None`` indicated) along the paths in the list
+        of directories ``context.path``.
+        """
+        found = cls._search_paths(context.name, context.path)
+        return map(PathDistribution, found)
+
+    @classmethod
+    def _search_paths(cls, name, paths):
+        """Find metadata directories in paths heuristically."""
+        prepared = Prepared(name)
+        return itertools.chain.from_iterable(
+            path.search(prepared) for path in map(FastPath, paths)
+        )
+
+    @classmethod
+    def invalidate_caches(cls) -> None:
+        FastPath.__new__.cache_clear()
+
+
+class PathDistribution(Distribution):
+    def __init__(self, path: SimplePath) -> None:
+        """Construct a distribution.
+
+        :param path: SimplePath indicating the metadata directory.
+        """
+        self._path = path
+
+    def read_text(self, filename: str | os.PathLike[str]) -> str | None:
+        with suppress(
+            FileNotFoundError,
+            IsADirectoryError,
+            KeyError,
+            NotADirectoryError,
+            PermissionError,
+        ):
+            return self._path.joinpath(filename).read_text(encoding='utf-8')
+
+        return None
+
+    read_text.__doc__ = Distribution.read_text.__doc__
+
+    def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
+        return self._path.parent / path
+
+    @property
+    def _normalized_name(self):
+        """
+        Performance optimization: where possible, resolve the
+        normalized name from the file system path.
+        """
+        stem = os.path.basename(str(self._path))
+        return (
+            pass_none(Prepared.normalize)(self._name_from_stem(stem))
+            or super()._normalized_name
+        )
+
+    @staticmethod
+    def _name_from_stem(stem):
+        """
+        >>> PathDistribution._name_from_stem('foo-3.0.egg-info')
+        'foo'
+        >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info')
+        'CherryPy'
+        >>> PathDistribution._name_from_stem('face.egg-info')
+        'face'
+        >>> PathDistribution._name_from_stem('foo.bar')
+        """
+        filename, ext = os.path.splitext(stem)
+        if ext not in ('.dist-info', '.egg-info'):
+            return
+        name, sep, rest = filename.partition('-')
+        return name
+
+
+def distribution(distribution_name: str) -> Distribution:
+    """Get the ``Distribution`` instance for the named package.
+
+    :param distribution_name: The name of the distribution package as a string.
+    :return: A ``Distribution`` instance (or subclass thereof).
+    """
+    return Distribution.from_name(distribution_name)
+
+
+def distributions(**kwargs) -> Iterable[Distribution]:
+    """Get all ``Distribution`` instances in the current environment.
+
+    :return: An iterable of ``Distribution`` instances.
+    """
+    return Distribution.discover(**kwargs)
+
+
+def metadata(distribution_name: str) -> _meta.PackageMetadata | None:
+    """Get the metadata for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: A PackageMetadata containing the parsed metadata.
+    """
+    return Distribution.from_name(distribution_name).metadata
+
+
+def version(distribution_name: str) -> str:
+    """Get the version string for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: The version string for the package as defined in the package's
+        "Version" metadata key.
+    """
+    return distribution(distribution_name).version
+
+
+_unique = functools.partial(
+    unique_everseen,
+    key=py39.normalized_name,
+)
+"""
+Wrapper for ``distributions`` to return unique distributions by name.
+"""
+
+
+def entry_points(**params) -> EntryPoints:
+    """Return EntryPoint objects for all installed packages.
+
+    Pass selection parameters (group or name) to filter the
+    result to entry points matching those properties (see
+    EntryPoints.select()).
+
+    :return: EntryPoints for all installed packages.
+    """
+    eps = itertools.chain.from_iterable(
+        dist.entry_points for dist in _unique(distributions())
+    )
+    return EntryPoints(eps).select(**params)
+
+
+def files(distribution_name: str) -> list[PackagePath] | None:
+    """Return a list of files for the named package.
+
+    :param distribution_name: The name of the distribution package to query.
+    :return: List of files composing the distribution.
+    """
+    return distribution(distribution_name).files
+
+
+def requires(distribution_name: str) -> list[str] | None:
+    """
+    Return a list of requirements for the named package.
+
+    :return: An iterable of requirements, suitable for
+        packaging.requirement.Requirement.
+    """
+    return distribution(distribution_name).requires
+
+
+def packages_distributions() -> Mapping[str, list[str]]:
+    """
+    Return a mapping of top-level packages to their
+    distributions.
+
+    >>> import collections.abc
+    >>> pkgs = packages_distributions()
+    >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values())
+    True
+    """
+    pkg_to_dist = collections.defaultdict(list)
+    for dist in distributions():
+        for pkg in _top_level_declared(dist) or _top_level_inferred(dist):
+            pkg_to_dist[pkg].append(md_none(dist.metadata)['Name'])
+    return dict(pkg_to_dist)
+
+
+def _top_level_declared(dist):
+    return (dist.read_text('top_level.txt') or '').split()
+
+
+def _topmost(name: PackagePath) -> str | None:
+    """
+    Return the top-most parent as long as there is a parent.
+    """
+    top, *rest = name.parts
+    return top if rest else None
+
+
+def _get_toplevel_name(name: PackagePath) -> str:
+    """
+    Infer a possibly importable module name from a name presumed on
+    sys.path.
+
+    >>> _get_toplevel_name(PackagePath('foo.py'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo.pyc'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo/__init__.py'))
+    'foo'
+    >>> _get_toplevel_name(PackagePath('foo.pth'))
+    'foo.pth'
+    >>> _get_toplevel_name(PackagePath('foo.dist-info'))
+    'foo.dist-info'
+    """
+    # Defer import of inspect for performance (python/cpython#118761)
+    import inspect
+
+    return _topmost(name) or inspect.getmodulename(name) or str(name)
+
+
+def _top_level_inferred(dist):
+    opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))
+
+    def importable_name(name):
+        return '.' not in name
+
+    return filter(importable_name, opt_names)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py
new file mode 100644
index 0000000..dede395
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py
@@ -0,0 +1,136 @@
+import email.message
+import email.policy
+import re
+import textwrap
+
+from ._text import FoldedCase
+
+
+class RawPolicy(email.policy.EmailPolicy):
+    def fold(self, name, value):
+        folded = self.linesep.join(
+            textwrap
+            .indent(value, prefix=' ' * 8, predicate=lambda line: True)
+            .lstrip()
+            .splitlines()
+        )
+        return f'{name}: {folded}{self.linesep}'
+
+
+class Message(email.message.Message):
+    r"""
+    Specialized Message subclass to handle metadata naturally.
+
+    Reads values that may have newlines in them and converts the
+    payload to the Description.
+
+    >>> msg_text = textwrap.dedent('''
+    ...     Name: Foo
+    ...     Version: 3.0
+    ...     License: blah
+    ...             de-blah
+    ...     
+    ...     First line of description.
+    ...     Second line of description.
+    ...     
+    ...     Fourth line!
+    ...     ''').lstrip().replace('', '')
+    >>> msg = Message(email.message_from_string(msg_text))
+    >>> msg['Description']
+    'First line of description.\nSecond line of description.\n\nFourth line!\n'
+
+    Message should render even if values contain newlines.
+
+    >>> print(msg)
+    Name: Foo
+    Version: 3.0
+    License: blah
+            de-blah
+    Description: First line of description.
+            Second line of description.
+    
+            Fourth line!
+    
+    
+    """
+
+    multiple_use_keys = set(
+        map(
+            FoldedCase,
+            [
+                'Classifier',
+                'Obsoletes-Dist',
+                'Platform',
+                'Project-URL',
+                'Provides-Dist',
+                'Provides-Extra',
+                'Requires-Dist',
+                'Requires-External',
+                'Supported-Platform',
+                'Dynamic',
+            ],
+        )
+    )
+    """
+    Keys that may be indicated multiple times per PEP 566.
+    """
+
+    def __new__(cls, orig: email.message.Message):
+        res = super().__new__(cls)
+        vars(res).update(vars(orig))
+        return res
+
+    def __init__(self, *args, **kwargs):
+        self._headers = self._repair_headers()
+
+    # suppress spurious error from mypy
+    def __iter__(self):
+        return super().__iter__()
+
+    def __getitem__(self, item):
+        """
+        Override parent behavior to typical dict behavior.
+
+        ``email.message.Message`` will emit None values for missing
+        keys. Typical mappings, including this ``Message``, will raise
+        a key error for missing keys.
+
+        Ref python/importlib_metadata#371.
+        """
+        res = super().__getitem__(item)
+        if res is None:
+            raise KeyError(item)
+        return res
+
+    def _repair_headers(self):
+        def redent(value):
+            "Correct for RFC822 indentation"
+            indent = ' ' * 8
+            if not value or '\n' + indent not in value:
+                return value
+            return textwrap.dedent(indent + value)
+
+        headers = [(key, redent(value)) for key, value in vars(self)['_headers']]
+        if self._payload:
+            headers.append(('Description', self.get_payload()))
+            self.set_payload('')
+        return headers
+
+    def as_string(self):
+        return super().as_string(policy=RawPolicy())
+
+    @property
+    def json(self):
+        """
+        Convert PackageMetadata to a JSON-compatible format
+        per PEP 0566.
+        """
+
+        def transform(key):
+            value = self.get_all(key) if key in self.multiple_use_keys else self[key]
+            if key == 'Keywords':
+                value = re.split(r'\s+', value)
+            tk = key.lower().replace('-', '_')
+            return tk, value
+
+        return dict(map(transform, map(FoldedCase, self)))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_collections.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_collections.py
new file mode 100644
index 0000000..fc5045d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_collections.py
@@ -0,0 +1,34 @@
+import collections
+import typing
+
+
+# from jaraco.collections 3.3
+class FreezableDefaultDict(collections.defaultdict):
+    """
+    Often it is desirable to prevent the mutation of
+    a default dict after its initial construction, such
+    as to prevent mutation during iteration.
+
+    >>> dd = FreezableDefaultDict(list)
+    >>> dd[0].append('1')
+    >>> dd.freeze()
+    >>> dd[1]
+    []
+    >>> len(dd)
+    1
+    """
+
+    def __missing__(self, key):
+        return getattr(self, '_frozen', super().__missing__)(key)
+
+    def freeze(self):
+        self._frozen = lambda key: self.default_factory()
+
+
+class Pair(typing.NamedTuple):
+    name: str
+    value: str
+
+    @classmethod
+    def parse(cls, text):
+        return cls(*map(str.strip, text.split("=", 1)))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_compat.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_compat.py
new file mode 100644
index 0000000..01356d6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_compat.py
@@ -0,0 +1,56 @@
+import platform
+import sys
+
+__all__ = ['install', 'NullFinder']
+
+
+def install(cls):
+    """
+    Class decorator for installation on sys.meta_path.
+
+    Adds the backport DistributionFinder to sys.meta_path and
+    attempts to disable the finder functionality of the stdlib
+    DistributionFinder.
+    """
+    sys.meta_path.append(cls())
+    disable_stdlib_finder()
+    return cls
+
+
+def disable_stdlib_finder():
+    """
+    Give the backport primacy for discovering path-based distributions
+    by monkey-patching the stdlib O_O.
+
+    See #91 for more background for rationale on this sketchy
+    behavior.
+    """
+
+    def matches(finder):
+        return getattr(
+            finder, '__module__', None
+        ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions')
+
+    for finder in filter(matches, sys.meta_path):  # pragma: nocover
+        del finder.find_distributions
+
+
+class NullFinder:
+    """
+    A "Finder" (aka "MetaPathFinder") that never finds any modules,
+    but may find distributions.
+    """
+
+    @staticmethod
+    def find_spec(*args, **kwargs):
+        return None
+
+
+def pypy_partial(val):
+    """
+    Adjust for variable stacklevel on partial under PyPy.
+
+    Workaround for #327.
+    """
+    is_pypy = platform.python_implementation() == 'PyPy'
+    return val + is_pypy
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_functools.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_functools.py
new file mode 100644
index 0000000..b1fd04a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_functools.py
@@ -0,0 +1,135 @@
+import functools
+import types
+from typing import Callable, TypeVar
+
+
+# from jaraco.functools 3.3
+def method_cache(method, cache_wrapper=None):
+    """
+    Wrap lru_cache to support storing the cache data in the object instances.
+
+    Abstracts the common paradigm where the method explicitly saves an
+    underscore-prefixed protected property on first call and returns that
+    subsequently.
+
+    >>> class MyClass:
+    ...     calls = 0
+    ...
+    ...     @method_cache
+    ...     def method(self, value):
+    ...         self.calls += 1
+    ...         return value
+
+    >>> a = MyClass()
+    >>> a.method(3)
+    3
+    >>> for x in range(75):
+    ...     res = a.method(x)
+    >>> a.calls
+    75
+
+    Note that the apparent behavior will be exactly like that of lru_cache
+    except that the cache is stored on each instance, so values in one
+    instance will not flush values from another, and when an instance is
+    deleted, so are the cached values for that instance.
+
+    >>> b = MyClass()
+    >>> for x in range(35):
+    ...     res = b.method(x)
+    >>> b.calls
+    35
+    >>> a.method(0)
+    0
+    >>> a.calls
+    75
+
+    Note that if method had been decorated with ``functools.lru_cache()``,
+    a.calls would have been 76 (due to the cached value of 0 having been
+    flushed by the 'b' instance).
+
+    Clear the cache with ``.cache_clear()``
+
+    >>> a.method.cache_clear()
+
+    Same for a method that hasn't yet been called.
+
+    >>> c = MyClass()
+    >>> c.method.cache_clear()
+
+    Another cache wrapper may be supplied:
+
+    >>> cache = functools.lru_cache(maxsize=2)
+    >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
+    >>> a = MyClass()
+    >>> a.method2()
+    3
+
+    Caution - do not subsequently wrap the method with another decorator, such
+    as ``@property``, which changes the semantics of the function.
+
+    See also
+    http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
+    for another implementation and additional justification.
+    """
+    cache_wrapper = cache_wrapper or functools.lru_cache()
+
+    def wrapper(self, *args, **kwargs):
+        # it's the first call, replace the method with a cached, bound method
+        bound_method = types.MethodType(method, self)
+        cached_method = cache_wrapper(bound_method)
+        setattr(self, method.__name__, cached_method)
+        return cached_method(*args, **kwargs)
+
+    # Support cache clear even before cache has been created.
+    wrapper.cache_clear = lambda: None
+
+    return wrapper
+
+
+# From jaraco.functools 3.3
+def pass_none(func):
+    """
+    Wrap func so it's not called if its first param is None
+
+    >>> print_text = pass_none(print)
+    >>> print_text('text')
+    text
+    >>> print_text(None)
+    """
+
+    @functools.wraps(func)
+    def wrapper(param, *args, **kwargs):
+        if param is not None:
+            return func(param, *args, **kwargs)
+
+    return wrapper
+
+
+# From jaraco.functools 4.4
+def noop(*args, **kwargs):
+    """
+    A no-operation function that does nothing.
+
+    >>> noop(1, 2, three=3)
+    """
+
+
+_T = TypeVar('_T')
+
+
+# From jaraco.functools 4.4
+def passthrough(func: Callable[..., object]) -> Callable[[_T], _T]:
+    """
+    Wrap the function to always return the first parameter.
+
+    >>> passthrough(print)('3')
+    3
+    '3'
+    """
+
+    @functools.wraps(func)
+    def wrapper(first: _T, *args, **kwargs) -> _T:
+        func(first, *args, **kwargs)
+        return first
+
+    return wrapper  # type: ignore[return-value]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py
new file mode 100644
index 0000000..79d3719
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py
@@ -0,0 +1,171 @@
+from collections import defaultdict, deque
+from itertools import filterfalse
+
+
+def unique_everseen(iterable, key=None):
+    "List unique elements, preserving order. Remember all elements ever seen."
+    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+    # unique_everseen('ABBCcAD', str.lower) --> A B C D
+    seen = set()
+    seen_add = seen.add
+    if key is None:
+        for element in filterfalse(seen.__contains__, iterable):
+            seen_add(element)
+            yield element
+    else:
+        for element in iterable:
+            k = key(element)
+            if k not in seen:
+                seen_add(k)
+                yield element
+
+
+# copied from more_itertools 8.8
+def always_iterable(obj, base_type=(str, bytes)):
+    """If *obj* is iterable, return an iterator over its items::
+
+        >>> obj = (1, 2, 3)
+        >>> list(always_iterable(obj))
+        [1, 2, 3]
+
+    If *obj* is not iterable, return a one-item iterable containing *obj*::
+
+        >>> obj = 1
+        >>> list(always_iterable(obj))
+        [1]
+
+    If *obj* is ``None``, return an empty iterable:
+
+        >>> obj = None
+        >>> list(always_iterable(None))
+        []
+
+    By default, binary and text strings are not considered iterable::
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj))
+        ['foo']
+
+    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
+    returns ``True`` won't be considered iterable.
+
+        >>> obj = {'a': 1}
+        >>> list(always_iterable(obj))  # Iterate over the dict's keys
+        ['a']
+        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
+        [{'a': 1}]
+
+    Set *base_type* to ``None`` to avoid any special handling and treat objects
+    Python considers iterable as iterable:
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj, base_type=None))
+        ['f', 'o', 'o']
+    """
+    if obj is None:
+        return iter(())
+
+    if (base_type is not None) and isinstance(obj, base_type):
+        return iter((obj,))
+
+    try:
+        return iter(obj)
+    except TypeError:
+        return iter((obj,))
+
+
+# Copied from more_itertools 10.3
+class bucket:
+    """Wrap *iterable* and return an object that buckets the iterable into
+    child iterables based on a *key* function.
+
+        >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3']
+        >>> s = bucket(iterable, key=lambda x: x[0])  # Bucket by 1st character
+        >>> sorted(list(s))  # Get the keys
+        ['a', 'b', 'c']
+        >>> a_iterable = s['a']
+        >>> next(a_iterable)
+        'a1'
+        >>> next(a_iterable)
+        'a2'
+        >>> list(s['b'])
+        ['b1', 'b2', 'b3']
+
+    The original iterable will be advanced and its items will be cached until
+    they are used by the child iterables. This may require significant storage.
+
+    By default, attempting to select a bucket to which no items belong  will
+    exhaust the iterable and cache all values.
+    If you specify a *validator* function, selected buckets will instead be
+    checked against it.
+
+        >>> from itertools import count
+        >>> it = count(1, 2)  # Infinite sequence of odd numbers
+        >>> key = lambda x: x % 10  # Bucket by last digit
+        >>> validator = lambda x: x in {1, 3, 5, 7, 9}  # Odd digits only
+        >>> s = bucket(it, key=key, validator=validator)
+        >>> 2 in s
+        False
+        >>> list(s[2])
+        []
+
+    """
+
+    def __init__(self, iterable, key, validator=None):
+        self._it = iter(iterable)
+        self._key = key
+        self._cache = defaultdict(deque)
+        self._validator = validator or (lambda x: True)
+
+    def __contains__(self, value):
+        if not self._validator(value):
+            return False
+
+        try:
+            item = next(self[value])
+        except StopIteration:
+            return False
+        else:
+            self._cache[value].appendleft(item)
+
+        return True
+
+    def _get_values(self, value):
+        """
+        Helper to yield items from the parent iterator that match *value*.
+        Items that don't match are stored in the local cache as they
+        are encountered.
+        """
+        while True:
+            # If we've cached some items that match the target value, emit
+            # the first one and evict it from the cache.
+            if self._cache[value]:
+                yield self._cache[value].popleft()
+            # Otherwise we need to advance the parent iterator to search for
+            # a matching item, caching the rest.
+            else:
+                while True:
+                    try:
+                        item = next(self._it)
+                    except StopIteration:
+                        return
+                    item_value = self._key(item)
+                    if item_value == value:
+                        yield item
+                        break
+                    elif self._validator(item_value):
+                        self._cache[item_value].append(item)
+
+    def __iter__(self):
+        for item in self._it:
+            item_value = self._key(item)
+            if self._validator(item_value):
+                self._cache[item_value].append(item)
+
+        yield from self._cache.keys()
+
+    def __getitem__(self, value):
+        if not self._validator(value):
+            return iter(())
+
+        return self._get_values(value)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_meta.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_meta.py
new file mode 100644
index 0000000..0c20eff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_meta.py
@@ -0,0 +1,71 @@
+from __future__ import annotations
+
+import os
+from collections.abc import Iterator
+from typing import (
+    Any,
+    Protocol,
+    TypeVar,
+    overload,
+)
+
+_T = TypeVar("_T")
+
+
+class PackageMetadata(Protocol):
+    def __len__(self) -> int: ...  # pragma: no cover
+
+    def __contains__(self, item: str) -> bool: ...  # pragma: no cover
+
+    def __getitem__(self, key: str) -> str: ...  # pragma: no cover
+
+    def __iter__(self) -> Iterator[str]: ...  # pragma: no cover
+
+    @overload
+    def get(
+        self, name: str, failobj: None = None
+    ) -> str | None: ...  # pragma: no cover
+
+    @overload
+    def get(self, name: str, failobj: _T) -> str | _T: ...  # pragma: no cover
+
+    # overload per python/importlib_metadata#435
+    @overload
+    def get_all(
+        self, name: str, failobj: None = None
+    ) -> list[Any] | None: ...  # pragma: no cover
+
+    @overload
+    def get_all(self, name: str, failobj: _T) -> list[Any] | _T:
+        """
+        Return all values associated with a possibly multi-valued key.
+        """
+
+    @property
+    def json(self) -> dict[str, str | list[str]]:
+        """
+        A JSON-compatible form of the metadata.
+        """
+
+
+class SimplePath(Protocol):
+    """
+    A minimal subset of pathlib.Path required by Distribution.
+    """
+
+    def joinpath(
+        self, other: str | os.PathLike[str]
+    ) -> SimplePath: ...  # pragma: no cover
+
+    def __truediv__(
+        self, other: str | os.PathLike[str]
+    ) -> SimplePath: ...  # pragma: no cover
+
+    @property
+    def parent(self) -> SimplePath: ...  # pragma: no cover
+
+    def read_text(self, encoding=None) -> str: ...  # pragma: no cover
+
+    def read_bytes(self) -> bytes: ...  # pragma: no cover
+
+    def exists(self) -> bool: ...  # pragma: no cover
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_text.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_text.py
new file mode 100644
index 0000000..c88cfbb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_text.py
@@ -0,0 +1,99 @@
+import re
+
+from ._functools import method_cache
+
+
+# from jaraco.text 3.5
+class FoldedCase(str):
+    """
+    A case insensitive string class; behaves just like str
+    except compares equal when the only variation is case.
+
+    >>> s = FoldedCase('hello world')
+
+    >>> s == 'Hello World'
+    True
+
+    >>> 'Hello World' == s
+    True
+
+    >>> s != 'Hello World'
+    False
+
+    >>> s.index('O')
+    4
+
+    >>> s.split('O')
+    ['hell', ' w', 'rld']
+
+    >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
+    ['alpha', 'Beta', 'GAMMA']
+
+    Sequence membership is straightforward.
+
+    >>> "Hello World" in [s]
+    True
+    >>> s in ["Hello World"]
+    True
+
+    You may test for set inclusion, but candidate and elements
+    must both be folded.
+
+    >>> FoldedCase("Hello World") in {s}
+    True
+    >>> s in {FoldedCase("Hello World")}
+    True
+
+    String inclusion works as long as the FoldedCase object
+    is on the right.
+
+    >>> "hello" in FoldedCase("Hello World")
+    True
+
+    But not if the FoldedCase object is on the left:
+
+    >>> FoldedCase('hello') in 'Hello World'
+    False
+
+    In that case, use in_:
+
+    >>> FoldedCase('hello').in_('Hello World')
+    True
+
+    >>> FoldedCase('hello') > FoldedCase('Hello')
+    False
+    """
+
+    def __lt__(self, other):
+        return self.lower() < other.lower()
+
+    def __gt__(self, other):
+        return self.lower() > other.lower()
+
+    def __eq__(self, other):
+        return self.lower() == other.lower()
+
+    def __ne__(self, other):
+        return self.lower() != other.lower()
+
+    def __hash__(self):
+        return hash(self.lower())
+
+    def __contains__(self, other):
+        return super().lower().__contains__(other.lower())
+
+    def in_(self, other):
+        "Does self appear in other?"
+        return self in FoldedCase(other)
+
+    # cache lower since it's likely to be called frequently.
+    @method_cache
+    def lower(self):
+        return super().lower()
+
+    def index(self, sub):
+        return self.lower().index(sub.lower())
+
+    def split(self, splitter=' ', maxsplit=0):
+        pattern = re.compile(re.escape(splitter), re.I)
+        return pattern.split(self, maxsplit)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_typing.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_typing.py
new file mode 100644
index 0000000..32b1d2b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/_typing.py
@@ -0,0 +1,15 @@
+import functools
+import typing
+
+from ._meta import PackageMetadata
+
+md_none = functools.partial(typing.cast, PackageMetadata)
+"""
+Suppress type errors for optional metadata.
+
+Although Distribution.metadata can return None when metadata is corrupt
+and thus None, allow callers to assume it's not None and crash if
+that's the case.
+
+# python/importlib_metadata#493
+"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py311.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py311.py
new file mode 100644
index 0000000..3a53274
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py311.py
@@ -0,0 +1,22 @@
+import os
+import pathlib
+import sys
+import types
+
+
+def wrap(path):  # pragma: no cover
+    """
+    Workaround for https://github.com/python/cpython/issues/84538
+    to add backward compatibility for walk_up=True.
+    An example affected package is dask-labextension, which uses
+    jupyter-packaging to install JupyterLab javascript files outside
+    of site-packages.
+    """
+
+    def relative_to(root, *, walk_up=False):
+        return pathlib.Path(os.path.relpath(path, root))
+
+    return types.SimpleNamespace(relative_to=relative_to)
+
+
+relative_fix = wrap if sys.version_info < (3, 12) else lambda x: x
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py39.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py39.py
new file mode 100644
index 0000000..3eb9c01
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/compat/py39.py
@@ -0,0 +1,42 @@
+"""
+Compatibility layer with Python 3.8/3.9
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:  # pragma: no cover
+    # Prevent circular imports on runtime.
+    from .. import Distribution, EntryPoint
+else:
+    Distribution = EntryPoint = Any
+
+from .._typing import md_none
+
+
+def normalized_name(dist: Distribution) -> str | None:
+    """
+    Honor name normalization for distributions that don't provide ``_normalized_name``.
+    """
+    try:
+        return dist._normalized_name
+    except AttributeError:
+        from .. import Prepared  # -> delay to prevent circular imports.
+
+        return Prepared.normalize(
+            getattr(dist, "name", None) or md_none(dist.metadata)['Name']
+        )
+
+
+def ep_matches(ep: EntryPoint, **params) -> bool:
+    """
+    Workaround for ``EntryPoint`` objects without the ``matches`` method.
+    """
+    try:
+        return ep.matches(**params)
+    except AttributeError:
+        from .. import EntryPoint  # -> delay to prevent circular imports.
+
+        # Reconstruct the EntryPoint object to make sure it is compatible.
+        return EntryPoint(ep.name, ep.value, ep.group).matches(**params)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/diagnose.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/diagnose.py
new file mode 100644
index 0000000..e405471
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/diagnose.py
@@ -0,0 +1,21 @@
+import sys
+
+from . import Distribution
+
+
+def inspect(path):
+    print("Inspecting", path)
+    dists = list(Distribution.discover(path=[path]))
+    if not dists:
+        return
+    print("Found", len(dists), "packages:", end=' ')
+    print(', '.join(dist.name for dist in dists))
+
+
+def run():
+    for path in sys.path:
+        inspect(path)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/importlib_metadata/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/METADATA
new file mode 100644
index 0000000..797b9da
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/METADATA
@@ -0,0 +1,96 @@
+Metadata-Version: 2.1
+Name: jaraco.text
+Version: 4.0.0
+Summary: Module for text manipulation
+Author-email: "Jason R. Coombs" 
+Project-URL: Source, https://github.com/jaraco/jaraco.text
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: jaraco.functools
+Requires-Dist: jaraco.context >=4.1
+Requires-Dist: autocommand
+Requires-Dist: more-itertools
+Requires-Dist: importlib-resources ; python_version < "3.9"
+Provides-Extra: doc
+Requires-Dist: sphinx >=3.5 ; extra == 'doc'
+Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc'
+Requires-Dist: rst.linker >=1.9 ; extra == 'doc'
+Requires-Dist: furo ; extra == 'doc'
+Requires-Dist: sphinx-lint ; extra == 'doc'
+Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'doc'
+Provides-Extra: inflect
+Requires-Dist: inflect ; extra == 'inflect'
+Provides-Extra: test
+Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test'
+Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'test'
+Requires-Dist: pytest-cov ; extra == 'test'
+Requires-Dist: pytest-mypy ; extra == 'test'
+Requires-Dist: pytest-enabler >=2.2 ; extra == 'test'
+Requires-Dist: pathlib2 ; (python_version < "3.10") and extra == 'test'
+Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'test'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.text.svg
+   :target: https://pypi.org/project/jaraco.text
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.text.svg
+
+.. image:: https://github.com/jaraco/jaraco.text/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest
+   :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2024-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.text
+   :target: https://tidelift.com/subscription/pkg/pypi-jaraco.text?utm_source=pypi-jaraco.text&utm_medium=readme
+
+
+This package provides handy routines for dealing with text, such as
+wrapping, substitution, trimming, stripping, prefix and suffix removal,
+line continuation, indentation, comment processing, identifier processing,
+values parsing, case insensitive comparison, and more. See the docs
+(linked in the badge above) for the detailed documentation and examples.
+
+Layouts
+=======
+
+One of the features of this package is the layouts module, which
+provides a simple example of translating keystrokes from one keyboard
+layout to another::
+
+    echo qwerty | python -m jaraco.text.to-dvorak
+    ',.pyf
+    echo  "',.pyf" | python -m jaraco.text.to-qwerty
+    qwerty
+
+Newline Reporting
+=================
+
+Need to know what newlines appear in a file?
+
+::
+
+    $ python -m jaraco.text.show-newlines README.rst
+    newline is '\n'
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more `_.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/RECORD
new file mode 100644
index 0000000..af65a9d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/RECORD
@@ -0,0 +1,14 @@
+jaraco.text-4.0.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+jaraco.text-4.0.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+jaraco.text-4.0.0.dist-info/METADATA,sha256=XC_QkBLJVPE5sQYkl41TNaZUw0AUzQb29GbKaD28nFY,3731
+jaraco.text-4.0.0.dist-info/RECORD,,
+jaraco.text-4.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+jaraco.text-4.0.0.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
+jaraco.text-4.0.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335
+jaraco/text/__init__.py,sha256=lazNYXo8IhOR1bFigLAyGiiQao6jtO3KGWh8bZZPx3c,16762
+jaraco/text/layouts.py,sha256=HTC8aSTLZ7uXipyOXapRMC158juecjK6RVwitfmZ9_w,643
+jaraco/text/show-newlines.py,sha256=jT0vp4gLhG20hX2lTB-zKo_i3NgKzj79yRAdz4eMzIM,903
+jaraco/text/strip-prefix.py,sha256=NfVXV8JVNo6nqcuYASfMV7_y4Eo8zMQqlCOGvAnRIVw,412
+jaraco/text/to-dvorak.py,sha256=36nPPsiifwv6RfpAb--3zpgbIx8ohnnI1aR29IJTO9s,118
+jaraco/text/to-qwerty.py,sha256=IQoFY9v7vLTEybcput4KBYm_5GR35pmtgZ_xyrmdTgI,118
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/WHEEL
new file mode 100644
index 0000000..ecaf39f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (71.1.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/top_level.txt
new file mode 100644
index 0000000..f6205a5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/__init__.py
new file mode 100644
index 0000000..41ad609
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/__init__.py
@@ -0,0 +1,367 @@
+from __future__ import annotations
+
+import contextlib
+import errno
+import functools
+import operator
+import os
+import platform
+import shutil
+import stat
+import subprocess
+import sys
+import tempfile
+import urllib.request
+from collections.abc import Iterator
+
+if sys.version_info < (3, 12):
+    from backports import tarfile
+else:
+    import tarfile
+
+
+@contextlib.contextmanager
+def pushd(dir: str | os.PathLike) -> Iterator[str | os.PathLike]:
+    """
+    >>> tmp_path = getfixture('tmp_path')
+    >>> with pushd(tmp_path):
+    ...     assert os.getcwd() == os.fspath(tmp_path)
+    >>> assert os.getcwd() != os.fspath(tmp_path)
+    """
+
+    orig = os.getcwd()
+    os.chdir(dir)
+    try:
+        yield dir
+    finally:
+        os.chdir(orig)
+
+
+@contextlib.contextmanager
+def tarball(
+    url, target_dir: str | os.PathLike | None = None
+) -> Iterator[str | os.PathLike]:
+    """
+    Get a URL to a tarball, download, extract, yield, then clean up.
+
+    Assumes everything in the tarball is prefixed with a common
+    directory. That common path is stripped and the contents
+    are extracted to ``target_dir``, similar to passing
+    ``-C {target} --strip-components 1`` to the ``tar`` command.
+
+    Uses the streaming protocol to extract the contents from a
+    stream in a single pass without loading the whole file into
+    memory.
+
+    >>> import urllib.request
+    >>> url = getfixture('tarfile_served')
+    >>> target = getfixture('tmp_path') / 'out'
+    >>> tb = tarball(url, target_dir=target)
+    >>> import pathlib
+    >>> with tb as extracted:
+    ...     contents = pathlib.Path(extracted, 'contents.txt').read_text(encoding='utf-8')
+    >>> assert not os.path.exists(extracted)
+
+    If the target is not specified, contents are extracted to a
+    directory relative to the current working directory named after
+    the name of the file as extracted from the URL.
+
+    >>> target = getfixture('tmp_path')
+    >>> with pushd(target), tarball(url):
+    ...     target.joinpath('served').is_dir()
+    True
+    """
+    if target_dir is None:
+        target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '')
+    os.mkdir(target_dir)
+    try:
+        req = urllib.request.urlopen(url)
+        with tarfile.open(fileobj=req, mode='r|*') as tf:
+            tf.extractall(path=target_dir, filter=_default_filter)
+        yield target_dir
+    finally:
+        shutil.rmtree(target_dir)
+
+
+def _compose_tarfile_filters(*filters):
+    def compose_two(f1, f2):
+        return lambda member, path: f1(f2(member, path), path)
+
+    return functools.reduce(compose_two, filters, lambda member, path: member)
+
+
+def strip_first_component(
+    member: tarfile.TarInfo,
+    path,
+) -> tarfile.TarInfo:
+    _, member.name = member.name.split('/', 1)
+    return member
+
+
+_default_filter = _compose_tarfile_filters(tarfile.data_filter, strip_first_component)
+
+
+def _compose(*cmgrs):
+    """
+    Compose any number of dependent context managers into a single one.
+
+    The last, innermost context manager may take arbitrary arguments, but
+    each successive context manager should accept the result from the
+    previous as a single parameter.
+
+    Like :func:`jaraco.functools.compose`, behavior works from right to
+    left, so the context manager should be indicated from outermost to
+    innermost.
+
+    Example, to create a context manager to change to a temporary
+    directory:
+
+    >>> temp_dir_as_cwd = _compose(pushd, temp_dir)
+    >>> with temp_dir_as_cwd() as dir:
+    ...     assert os.path.samefile(os.getcwd(), dir)
+    """
+
+    def compose_two(inner, outer):
+        def composed(*args, **kwargs):
+            with inner(*args, **kwargs) as saved, outer(saved) as res:
+                yield res
+
+        return contextlib.contextmanager(composed)
+
+    return functools.reduce(compose_two, reversed(cmgrs))
+
+
+tarball_cwd = _compose(pushd, tarball)
+"""
+A tarball context with the current working directory pointing to the contents.
+"""
+
+
+def remove_readonly(func, path, exc_info):
+    """
+    Add support for removing read-only files on Windows.
+    """
+    _, exc, _ = exc_info
+    if func in (os.rmdir, os.remove, os.unlink) and exc.errno == errno.EACCES:
+        # change the file to be readable,writable,executable: 0777
+        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
+        # retry
+        func(path)
+    else:
+        raise
+
+
+def robust_remover():
+    return (
+        functools.partial(shutil.rmtree, onerror=remove_readonly)
+        if platform.system() == 'Windows'
+        else shutil.rmtree
+    )
+
+
+@contextlib.contextmanager
+def temp_dir(remover=shutil.rmtree):
+    """
+    Create a temporary directory context. Pass a custom remover
+    to override the removal behavior.
+
+    >>> import pathlib
+    >>> with temp_dir() as the_dir:
+    ...     assert os.path.isdir(the_dir)
+    >>> assert not os.path.exists(the_dir)
+    """
+    temp_dir = tempfile.mkdtemp()
+    try:
+        yield temp_dir
+    finally:
+        remover(temp_dir)
+
+
+robust_temp_dir = functools.partial(temp_dir, remover=robust_remover())
+
+
+@contextlib.contextmanager
+def repo_context(
+    url, branch: str | None = None, quiet: bool = True, dest_ctx=robust_temp_dir
+):
+    """
+    Check out the repo indicated by url.
+
+    If dest_ctx is supplied, it should be a context manager
+    to yield the target directory for the check out.
+
+    >>> getfixture('ensure_git')
+    >>> getfixture('needs_internet')
+    >>> repo = repo_context('https://github.com/jaraco/jaraco.context')
+    >>> with repo as dest:
+    ...     listing = os.listdir(dest)
+    >>> 'README.rst' in listing
+    True
+    """
+    exe = 'git' if 'git' in url else 'hg'
+    with dest_ctx() as repo_dir:
+        cmd = [exe, 'clone', url, repo_dir]
+        cmd.extend(['--branch', branch] * bool(branch))
+        stream = subprocess.DEVNULL if quiet else None
+        subprocess.check_call(cmd, stdout=stream, stderr=stream)
+        yield repo_dir
+
+
+class ExceptionTrap:
+    """
+    A context manager that will catch certain exceptions and provide an
+    indication they occurred.
+
+    >>> with ExceptionTrap() as trap:
+    ...     raise Exception()
+    >>> bool(trap)
+    True
+
+    >>> with ExceptionTrap() as trap:
+    ...     pass
+    >>> bool(trap)
+    False
+
+    >>> with ExceptionTrap(ValueError) as trap:
+    ...     raise ValueError("1 + 1 is not 3")
+    >>> bool(trap)
+    True
+    >>> trap.value
+    ValueError('1 + 1 is not 3')
+    >>> trap.tb
+    
+
+    >>> with ExceptionTrap(ValueError) as trap:
+    ...     raise Exception()
+    Traceback (most recent call last):
+    ...
+    Exception
+
+    >>> bool(trap)
+    False
+    """
+
+    exc_info = None, None, None
+
+    def __init__(self, exceptions=(Exception,)):
+        self.exceptions = exceptions
+
+    def __enter__(self):
+        return self
+
+    @property
+    def type(self):
+        return self.exc_info[0]
+
+    @property
+    def value(self):
+        return self.exc_info[1]
+
+    @property
+    def tb(self):
+        return self.exc_info[2]
+
+    def __exit__(self, *exc_info):
+        type = exc_info[0]
+        matches = type and issubclass(type, self.exceptions)
+        if matches:
+            self.exc_info = exc_info
+        return matches
+
+    def __bool__(self):
+        return bool(self.type)
+
+    def raises(self, func, *, _test=bool):
+        """
+        Wrap func and replace the result with the truth
+        value of the trap (True if an exception occurred).
+
+        First, give the decorator an alias to support Python 3.8
+        Syntax.
+
+        >>> raises = ExceptionTrap(ValueError).raises
+
+        Now decorate a function that always fails.
+
+        >>> @raises
+        ... def fail():
+        ...     raise ValueError('failed')
+        >>> fail()
+        True
+        """
+
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            with ExceptionTrap(self.exceptions) as trap:
+                func(*args, **kwargs)
+            return _test(trap)
+
+        return wrapper
+
+    def passes(self, func):
+        """
+        Wrap func and replace the result with the truth
+        value of the trap (True if no exception).
+
+        First, give the decorator an alias to support Python 3.8
+        Syntax.
+
+        >>> passes = ExceptionTrap(ValueError).passes
+
+        Now decorate a function that always fails.
+
+        >>> @passes
+        ... def fail():
+        ...     raise ValueError('failed')
+
+        >>> fail()
+        False
+        """
+        return self.raises(func, _test=operator.not_)
+
+
+class suppress(contextlib.suppress, contextlib.ContextDecorator):
+    """
+    A version of contextlib.suppress with decorator support.
+
+    >>> @suppress(KeyError)
+    ... def key_error():
+    ...     {}['']
+    >>> key_error()
+    """
+
+
+class on_interrupt(contextlib.ContextDecorator):
+    """
+    Replace a KeyboardInterrupt with SystemExit(1).
+
+    Useful in conjunction with console entry point functions.
+
+    >>> def do_interrupt():
+    ...     raise KeyboardInterrupt()
+    >>> on_interrupt('error')(do_interrupt)()
+    Traceback (most recent call last):
+    ...
+    SystemExit: 1
+    >>> on_interrupt('error', code=255)(do_interrupt)()
+    Traceback (most recent call last):
+    ...
+    SystemExit: 255
+    >>> on_interrupt('suppress')(do_interrupt)()
+    >>> with __import__('pytest').raises(KeyboardInterrupt):
+    ...     on_interrupt('ignore')(do_interrupt)()
+    """
+
+    def __init__(self, action='error', /, code=1):
+        self.action = action
+        self.code = code
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exctype, excinst, exctb):
+        if exctype is not KeyboardInterrupt or self.action == 'ignore':
+            return
+        elif self.action == 'error':
+            raise SystemExit(self.code) from excinst
+        return self.action == 'suppress'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/context/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.py
new file mode 100644
index 0000000..df32e2e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.py
@@ -0,0 +1,722 @@
+from __future__ import annotations
+
+import collections.abc
+import functools
+import inspect
+import itertools
+import operator
+import time
+import types
+import warnings
+from typing import Callable, TypeVar
+
+import more_itertools
+
+
+def compose(*funcs):
+    """
+    Compose any number of unary functions into a single unary function.
+
+    Comparable to
+    `function composition `_
+    in mathematics:
+
+    ``h = g ∘ f`` implies ``h(x) = g(f(x))``.
+
+    In Python, ``h = compose(g, f)``.
+
+    >>> import textwrap
+    >>> expected = str.strip(textwrap.dedent(compose.__doc__))
+    >>> strip_and_dedent = compose(str.strip, textwrap.dedent)
+    >>> strip_and_dedent(compose.__doc__) == expected
+    True
+
+    Compose also allows the innermost function to take arbitrary arguments.
+
+    >>> round_three = lambda x: round(x, ndigits=3)
+    >>> f = compose(round_three, int.__truediv__)
+    >>> [f(3*x, x+1) for x in range(1,10)]
+    [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7]
+    """
+
+    def compose_two(f1, f2):
+        return lambda *args, **kwargs: f1(f2(*args, **kwargs))
+
+    return functools.reduce(compose_two, funcs)
+
+
+def once(func):
+    """
+    Decorate func so it's only ever called the first time.
+
+    This decorator can ensure that an expensive or non-idempotent function
+    will not be expensive on subsequent calls and is idempotent.
+
+    >>> add_three = once(lambda a: a+3)
+    >>> add_three(3)
+    6
+    >>> add_three(9)
+    6
+    >>> add_three('12')
+    6
+
+    To reset the stored value, simply clear the property ``saved_result``.
+
+    >>> del add_three.saved_result
+    >>> add_three(9)
+    12
+    >>> add_three(8)
+    12
+
+    Or invoke 'reset()' on it.
+
+    >>> add_three.reset()
+    >>> add_three(-3)
+    0
+    >>> add_three(0)
+    0
+    """
+
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        if not hasattr(wrapper, 'saved_result'):
+            wrapper.saved_result = func(*args, **kwargs)
+        return wrapper.saved_result
+
+    wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result')
+    return wrapper
+
+
+def method_cache(method, cache_wrapper=functools.lru_cache()):
+    """
+    Wrap lru_cache to support storing the cache data in the object instances.
+
+    Abstracts the common paradigm where the method explicitly saves an
+    underscore-prefixed protected property on first call and returns that
+    subsequently.
+
+    >>> class MyClass:
+    ...     calls = 0
+    ...
+    ...     @method_cache
+    ...     def method(self, value):
+    ...         self.calls += 1
+    ...         return value
+
+    >>> a = MyClass()
+    >>> a.method(3)
+    3
+    >>> for x in range(75):
+    ...     res = a.method(x)
+    >>> a.calls
+    75
+
+    Note that the apparent behavior will be exactly like that of lru_cache
+    except that the cache is stored on each instance, so values in one
+    instance will not flush values from another, and when an instance is
+    deleted, so are the cached values for that instance.
+
+    >>> b = MyClass()
+    >>> for x in range(35):
+    ...     res = b.method(x)
+    >>> b.calls
+    35
+    >>> a.method(0)
+    0
+    >>> a.calls
+    75
+
+    Note that if method had been decorated with ``functools.lru_cache()``,
+    a.calls would have been 76 (due to the cached value of 0 having been
+    flushed by the 'b' instance).
+
+    Clear the cache with ``.cache_clear()``
+
+    >>> a.method.cache_clear()
+
+    Same for a method that hasn't yet been called.
+
+    >>> c = MyClass()
+    >>> c.method.cache_clear()
+
+    Another cache wrapper may be supplied:
+
+    >>> cache = functools.lru_cache(maxsize=2)
+    >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache)
+    >>> a = MyClass()
+    >>> a.method2()
+    3
+
+    Caution - do not subsequently wrap the method with another decorator, such
+    as ``@property``, which changes the semantics of the function.
+
+    See also
+    http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/
+    for another implementation and additional justification.
+    """
+
+    def wrapper(self, *args, **kwargs):
+        # it's the first call, replace the method with a cached, bound method
+        bound_method = types.MethodType(method, self)
+        cached_method = cache_wrapper(bound_method)
+        setattr(self, method.__name__, cached_method)
+        return cached_method(*args, **kwargs)
+
+    # Support cache clear even before cache has been created.
+    wrapper.cache_clear = lambda: None
+
+    return _special_method_cache(method, cache_wrapper) or wrapper
+
+
+def _special_method_cache(method, cache_wrapper):
+    """
+    Because Python treats special methods differently, it's not
+    possible to use instance attributes to implement the cached
+    methods.
+
+    Instead, install the wrapper method under a different name
+    and return a simple proxy to that wrapper.
+
+    https://github.com/jaraco/jaraco.functools/issues/5
+    """
+    name = method.__name__
+    special_names = '__getattr__', '__getitem__'
+
+    if name not in special_names:
+        return None
+
+    wrapper_name = '__cached' + name
+
+    def proxy(self, /, *args, **kwargs):
+        if wrapper_name not in vars(self):
+            bound = types.MethodType(method, self)
+            cache = cache_wrapper(bound)
+            setattr(self, wrapper_name, cache)
+        else:
+            cache = getattr(self, wrapper_name)
+        return cache(*args, **kwargs)
+
+    return proxy
+
+
+def apply(transform):
+    """
+    Decorate a function with a transform function that is
+    invoked on results returned from the decorated function.
+
+    >>> @apply(reversed)
+    ... def get_numbers(start):
+    ...     "doc for get_numbers"
+    ...     return range(start, start+3)
+    >>> list(get_numbers(4))
+    [6, 5, 4]
+    >>> get_numbers.__doc__
+    'doc for get_numbers'
+    """
+
+    def wrap(func):
+        return functools.wraps(func)(compose(transform, func))
+
+    return wrap
+
+
+def result_invoke(action):
+    r"""
+    Decorate a function with an action function that is
+    invoked on the results returned from the decorated
+    function (for its side effect), then return the original
+    result.
+
+    >>> @result_invoke(print)
+    ... def add_two(a, b):
+    ...     return a + b
+    >>> x = add_two(2, 3)
+    5
+    >>> x
+    5
+    """
+
+    def wrap(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            result = func(*args, **kwargs)
+            action(result)
+            return result
+
+        return wrapper
+
+    return wrap
+
+
+def invoke(f, /, *args, **kwargs):
+    """
+    Call a function for its side effect after initialization.
+
+    The benefit of using the decorator instead of simply invoking a function
+    after defining it is that it makes explicit the author's intent for the
+    function to be called immediately. Whereas if one simply calls the
+    function immediately, it's less obvious if that was intentional or
+    incidental. It also avoids repeating the name - the two actions, defining
+    the function and calling it immediately are modeled separately, but linked
+    by the decorator construct.
+
+    The benefit of having a function construct (opposed to just invoking some
+    behavior inline) is to serve as a scope in which the behavior occurs. It
+    avoids polluting the global namespace with local variables, provides an
+    anchor on which to attach documentation (docstring), keeps the behavior
+    logically separated (instead of conceptually separated or not separated at
+    all), and provides potential to re-use the behavior for testing or other
+    purposes.
+
+    This function is named as a pithy way to communicate, "call this function
+    primarily for its side effect", or "while defining this function, also
+    take it aside and call it". It exists because there's no Python construct
+    for "define and call" (nor should there be, as decorators serve this need
+    just fine). The behavior happens immediately and synchronously.
+
+    >>> @invoke
+    ... def func(): print("called")
+    called
+    >>> func()
+    called
+
+    Use functools.partial to pass parameters to the initial call
+
+    >>> @functools.partial(invoke, name='bingo')
+    ... def func(name): print('called with', name)
+    called with bingo
+    """
+    f(*args, **kwargs)
+    return f
+
+
+_T = TypeVar('_T')
+
+
+def passthrough(func: Callable[..., object]) -> Callable[[_T], _T]:
+    """
+    Wrap the function to always return the first parameter.
+
+    >>> passthrough(print)('3')
+    3
+    '3'
+    """
+
+    @functools.wraps(func)
+    def wrapper(first: _T, *args, **kwargs) -> _T:
+        func(first, *args, **kwargs)
+        return first
+
+    return wrapper
+
+
+class Throttler:
+    """Rate-limit a function (or other callable)."""
+
+    def __init__(self, func, max_rate=float('Inf')):
+        if isinstance(func, Throttler):
+            func = func.func
+        self.func = func
+        self.max_rate = max_rate
+        self.reset()
+
+    def reset(self):
+        self.last_called = 0
+
+    def __call__(self, *args, **kwargs):
+        self._wait()
+        return self.func(*args, **kwargs)
+
+    def _wait(self):
+        """Ensure at least 1/max_rate seconds from last call."""
+        elapsed = time.time() - self.last_called
+        must_wait = 1 / self.max_rate - elapsed
+        time.sleep(max(0, must_wait))
+        self.last_called = time.time()
+
+    def __get__(self, obj, owner=None):
+        return first_invoke(self._wait, functools.partial(self.func, obj))
+
+
+def first_invoke(func1, func2):
+    """
+    Return a function that when invoked will invoke func1 without
+    any parameters (for its side effect) and then invoke func2
+    with whatever parameters were passed, returning its result.
+    """
+
+    def wrapper(*args, **kwargs):
+        func1()
+        return func2(*args, **kwargs)
+
+    return wrapper
+
+
+method_caller = first_invoke(
+    lambda: warnings.warn(
+        '`jaraco.functools.method_caller` is deprecated, '
+        'use `operator.methodcaller` instead',
+        DeprecationWarning,
+        stacklevel=3,
+    ),
+    operator.methodcaller,
+)
+
+
+def retry_call(func, cleanup=lambda: None, retries=0, trap=()):
+    """
+    Given a callable func, trap the indicated exceptions
+    for up to 'retries' times, invoking cleanup on the
+    exception. On the final attempt, allow any exceptions
+    to propagate.
+    """
+    attempts = itertools.count() if retries == float('inf') else range(retries)
+    for _ in attempts:
+        try:
+            return func()
+        except trap:
+            cleanup()
+
+    return func()
+
+
+def retry(*r_args, **r_kwargs):
+    """
+    Decorator wrapper for retry_call. Accepts arguments to retry_call
+    except func and then returns a decorator for the decorated function.
+
+    Ex:
+
+    >>> @retry(retries=3)
+    ... def my_func(a, b):
+    ...     "this is my funk"
+    ...     print(a, b)
+    >>> my_func.__doc__
+    'this is my funk'
+    """
+
+    def decorate(func):
+        @functools.wraps(func)
+        def wrapper(*f_args, **f_kwargs):
+            bound = functools.partial(func, *f_args, **f_kwargs)
+            return retry_call(bound, *r_args, **r_kwargs)
+
+        return wrapper
+
+    return decorate
+
+
+def print_yielded(func):
+    """
+    Convert a generator into a function that prints all yielded elements.
+
+    >>> @print_yielded
+    ... def x():
+    ...     yield 3; yield None
+    >>> x()
+    3
+    None
+    """
+    print_all = functools.partial(map, print)
+    print_results = compose(more_itertools.consume, print_all, func)
+    return functools.wraps(func)(print_results)
+
+
+def pass_none(func):
+    """
+    Wrap func so it's not called if its first param is None.
+
+    >>> print_text = pass_none(print)
+    >>> print_text('text')
+    text
+    >>> print_text(None)
+    """
+
+    @functools.wraps(func)
+    def wrapper(param, /, *args, **kwargs):
+        if param is not None:
+            return func(param, *args, **kwargs)
+        return None
+
+    return wrapper
+
+
+def none_as(value, replacement=None):
+    """
+    >>> none_as(None, 'foo')
+    'foo'
+    >>> none_as('bar', 'foo')
+    'bar'
+    """
+    return replacement if value is None else value
+
+
+def assign_params(func, namespace):
+    """
+    Assign parameters from namespace where func solicits.
+
+    >>> def func(x, y=3):
+    ...     print(x, y)
+    >>> assigned = assign_params(func, dict(x=2, z=4))
+    >>> assigned()
+    2 3
+
+    The usual errors are raised if a function doesn't receive
+    its required parameters:
+
+    >>> assigned = assign_params(func, dict(y=3, z=4))
+    >>> assigned()
+    Traceback (most recent call last):
+    TypeError: func() ...argument...
+
+    It even works on methods:
+
+    >>> class Handler:
+    ...     def meth(self, arg):
+    ...         print(arg)
+    >>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))()
+    crystal
+    """
+    sig = inspect.signature(func)
+    params = sig.parameters.keys()
+    call_ns = {k: namespace[k] for k in params if k in namespace}
+    return functools.partial(func, **call_ns)
+
+
+def save_method_args(method):
+    """
+    Wrap a method such that when it is called, the args and kwargs are
+    saved on the method.
+
+    >>> class MyClass:
+    ...     @save_method_args
+    ...     def method(self, a, b):
+    ...         print(a, b)
+    >>> my_ob = MyClass()
+    >>> my_ob.method(1, 2)
+    1 2
+    >>> my_ob._saved_method.args
+    (1, 2)
+    >>> my_ob._saved_method.kwargs
+    {}
+    >>> my_ob.method(a=3, b='foo')
+    3 foo
+    >>> my_ob._saved_method.args
+    ()
+    >>> my_ob._saved_method.kwargs == dict(a=3, b='foo')
+    True
+
+    The arguments are stored on the instance, allowing for
+    different instance to save different args.
+
+    >>> your_ob = MyClass()
+    >>> your_ob.method({str('x'): 3}, b=[4])
+    {'x': 3} [4]
+    >>> your_ob._saved_method.args
+    ({'x': 3},)
+    >>> my_ob._saved_method.args
+    ()
+    """
+    args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs')  # noqa: PYI024 # Internal; stubs used for typing
+
+    @functools.wraps(method)
+    def wrapper(self, /, *args, **kwargs):
+        attr_name = '_saved_' + method.__name__
+        attr = args_and_kwargs(args, kwargs)
+        setattr(self, attr_name, attr)
+        return method(self, *args, **kwargs)
+
+    return wrapper
+
+
+def except_(*exceptions, replace=None, use=None):
+    """
+    Replace the indicated exceptions, if raised, with the indicated
+    literal replacement or evaluated expression (if present).
+
+    >>> safe_int = except_(ValueError)(int)
+    >>> safe_int('five')
+    >>> safe_int('5')
+    5
+
+    Specify a literal replacement with ``replace``.
+
+    >>> safe_int_r = except_(ValueError, replace=0)(int)
+    >>> safe_int_r('five')
+    0
+
+    Provide an expression to ``use`` to pass through particular parameters.
+
+    >>> safe_int_pt = except_(ValueError, use='args[0]')(int)
+    >>> safe_int_pt('five')
+    'five'
+
+    """
+
+    def decorate(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            try:
+                return func(*args, **kwargs)
+            except exceptions:
+                try:
+                    return eval(use)
+                except TypeError:
+                    return replace
+
+        return wrapper
+
+    return decorate
+
+
+def identity(x):
+    """
+    Return the argument.
+
+    >>> o = object()
+    >>> identity(o) is o
+    True
+    """
+    return x
+
+
+def bypass_when(check, *, _op=identity):
+    """
+    Decorate a function to return its parameter when ``check``.
+
+    >>> bypassed = []  # False
+
+    >>> @bypass_when(bypassed)
+    ... def double(x):
+    ...     return x * 2
+    >>> double(2)
+    4
+    >>> bypassed[:] = [object()]  # True
+    >>> double(2)
+    2
+    """
+
+    def decorate(func):
+        @functools.wraps(func)
+        def wrapper(param, /):
+            return param if _op(check) else func(param)
+
+        return wrapper
+
+    return decorate
+
+
+def bypass_unless(check):
+    """
+    Decorate a function to return its parameter unless ``check``.
+
+    >>> enabled = [object()]  # True
+
+    >>> @bypass_unless(enabled)
+    ... def double(x):
+    ...     return x * 2
+    >>> double(2)
+    4
+    >>> del enabled[:]  # False
+    >>> double(2)
+    2
+    """
+    return bypass_when(check, _op=operator.not_)
+
+
+@functools.singledispatch
+def _splat_inner(args, func):
+    """Splat args to func."""
+    return func(*args)
+
+
+@_splat_inner.register
+def _(args: collections.abc.Mapping, func):
+    """Splat kargs to func as kwargs."""
+    return func(**args)
+
+
+def splat(func):
+    """
+    Wrap func to expect its parameters to be passed positionally in a tuple.
+
+    Has a similar effect to that of ``itertools.starmap`` over
+    simple ``map``.
+
+    >>> pairs = [(-1, 1), (0, 2)]
+    >>> more_itertools.consume(itertools.starmap(print, pairs))
+    -1 1
+    0 2
+    >>> more_itertools.consume(map(splat(print), pairs))
+    -1 1
+    0 2
+
+    The approach generalizes to other iterators that don't have a "star"
+    equivalent, such as a "starfilter".
+
+    >>> list(filter(splat(operator.add), pairs))
+    [(0, 2)]
+
+    Splat also accepts a mapping argument.
+
+    >>> def is_nice(msg, code):
+    ...     return "smile" in msg or code == 0
+    >>> msgs = [
+    ...     dict(msg='smile!', code=20),
+    ...     dict(msg='error :(', code=1),
+    ...     dict(msg='unknown', code=0),
+    ... ]
+    >>> for msg in filter(splat(is_nice), msgs):
+    ...     print(msg)
+    {'msg': 'smile!', 'code': 20}
+    {'msg': 'unknown', 'code': 0}
+    """
+    return functools.wraps(func)(functools.partial(_splat_inner, func=func))
+
+
+_T = TypeVar('_T')
+
+
+def chainable(method: Callable[[_T, ...], None]) -> Callable[[_T, ...], _T]:
+    """
+    Wrap an instance method to always return self.
+
+
+    >>> class Dingus:
+    ...     @chainable
+    ...     def set_attr(self, name, val):
+    ...         setattr(self, name, val)
+    >>> d = Dingus().set_attr('a', 'eh!')
+    >>> d.a
+    'eh!'
+    >>> d2 = Dingus().set_attr('a', 'eh!').set_attr('b', 'bee!')
+    >>> d2.a + d2.b
+    'eh!bee!'
+
+    Enforces that the return value is null.
+
+    >>> class BorkedDingus:
+    ...     @chainable
+    ...     def set_attr(self, name, val):
+    ...         setattr(self, name, val)
+    ...         return len(name)
+    >>> BorkedDingus().set_attr('a', 'eh!')
+    Traceback (most recent call last):
+    ...
+    AssertionError
+    """
+
+    @functools.wraps(method)
+    def wrapper(self, *args, **kwargs):
+        assert method(self, *args, **kwargs) is None
+        return self
+
+    return wrapper
+
+
+def noop(*args, **kwargs):
+    """
+    A no-operation function that does nothing.
+
+    >>> noop(1, 2, three=3)
+    """
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi
new file mode 100644
index 0000000..6f834bf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi
@@ -0,0 +1,123 @@
+from collections.abc import Callable, Hashable, Iterator
+from functools import partial
+from operator import methodcaller
+from typing import (
+    Any,
+    Generic,
+    Protocol,
+    TypeVar,
+    overload,
+)
+
+from typing_extensions import Concatenate, ParamSpec, TypeVarTuple, Unpack
+
+_P = ParamSpec('_P')
+_R = TypeVar('_R')
+_T = TypeVar('_T')
+_Ts = TypeVarTuple('_Ts')
+_R1 = TypeVar('_R1')
+_R2 = TypeVar('_R2')
+_V = TypeVar('_V')
+_S = TypeVar('_S')
+_R_co = TypeVar('_R_co', covariant=True)
+
+class _OnceCallable(Protocol[_P, _R]):
+    saved_result: _R
+    reset: Callable[[], None]
+    def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...
+
+class _ProxyMethodCacheWrapper(Protocol[_R_co]):
+    cache_clear: Callable[[], None]
+    def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ...
+
+class _MethodCacheWrapper(Protocol[_R_co]):
+    def cache_clear(self) -> None: ...
+    def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ...
+
+# `compose()` overloads below will cover most use cases.
+
+@overload
+def compose(
+    __func1: Callable[[_R], _T],
+    __func2: Callable[_P, _R],
+    /,
+) -> Callable[_P, _T]: ...
+@overload
+def compose(
+    __func1: Callable[[_R], _T],
+    __func2: Callable[[_R1], _R],
+    __func3: Callable[_P, _R1],
+    /,
+) -> Callable[_P, _T]: ...
+@overload
+def compose(
+    __func1: Callable[[_R], _T],
+    __func2: Callable[[_R2], _R],
+    __func3: Callable[[_R1], _R2],
+    __func4: Callable[_P, _R1],
+    /,
+) -> Callable[_P, _T]: ...
+def once(func: Callable[_P, _R]) -> _OnceCallable[_P, _R]: ...
+def method_cache(
+    method: Callable[..., _R],
+    cache_wrapper: Callable[[Callable[..., _R]], _MethodCacheWrapper[_R]] = ...,
+) -> _MethodCacheWrapper[_R] | _ProxyMethodCacheWrapper[_R]: ...
+def apply(
+    transform: Callable[[_R], _T],
+) -> Callable[[Callable[_P, _R]], Callable[_P, _T]]: ...
+def result_invoke(
+    action: Callable[[_R], Any],
+) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ...
+def invoke(
+    f: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs
+) -> Callable[_P, _R]: ...
+
+class Throttler(Generic[_R]):
+    last_called: float
+    func: Callable[..., _R]
+    max_rate: float
+    def __init__(
+        self, func: Callable[..., _R] | Throttler[_R], max_rate: float = ...
+    ) -> None: ...
+    def reset(self) -> None: ...
+    def __call__(self, *args: Any, **kwargs: Any) -> _R: ...
+    def __get__(self, obj: Any, owner: type[Any] | None = ...) -> Callable[..., _R]: ...
+
+def first_invoke(
+    func1: Callable[..., Any], func2: Callable[_P, _R]
+) -> Callable[_P, _R]: ...
+
+method_caller: Callable[..., methodcaller]
+
+def retry_call(
+    func: Callable[..., _R],
+    cleanup: Callable[..., None] = ...,
+    retries: float = ...,
+    trap: type[BaseException] | tuple[type[BaseException], ...] = ...,
+) -> _R: ...
+def retry(
+    cleanup: Callable[..., None] = ...,
+    retries: float = ...,
+    trap: type[BaseException] | tuple[type[BaseException], ...] = ...,
+) -> Callable[[Callable[..., _R]], Callable[..., _R]]: ...
+def print_yielded(func: Callable[_P, Iterator[Any]]) -> Callable[_P, None]: ...
+def pass_none(
+    func: Callable[Concatenate[_T, _P], _R],
+) -> Callable[Concatenate[_T, _P], _R]: ...
+def assign_params(
+    func: Callable[..., _R], namespace: dict[str, Any]
+) -> partial[_R]: ...
+def save_method_args(
+    method: Callable[Concatenate[_S, _P], _R],
+) -> Callable[Concatenate[_S, _P], _R]: ...
+def except_(
+    *exceptions: type[BaseException], replace: Any = ..., use: Any = ...
+) -> Callable[[Callable[_P, Any]], Callable[_P, Any]]: ...
+def identity(x: _T) -> _T: ...
+def bypass_when(
+    check: _V, *, _op: Callable[[_V], Any] = ...
+) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ...
+def bypass_unless(
+    check: Any,
+) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ...
+def splat(func: Callable[[Unpack[_Ts]], _R]) -> Callable[[tuple[Unpack[_Ts]]], _R]: ...
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/functools/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/Lorem ipsum.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/Lorem ipsum.txt
new file mode 100644
index 0000000..986f944
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/Lorem ipsum.txt	
@@ -0,0 +1,2 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/__init__.py
new file mode 100644
index 0000000..8567200
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/__init__.py
@@ -0,0 +1,647 @@
+import functools
+import itertools
+import re
+import textwrap
+
+from typing import Iterable
+
+try:
+    from importlib.resources import files  # type: ignore
+except ImportError:  # pragma: nocover
+    from importlib_resources import files  # type: ignore
+
+from jaraco.context import ExceptionTrap
+from jaraco.functools import compose, method_cache
+
+
+def substitution(old, new):
+    """
+    Return a function that will perform a substitution on a string
+    """
+    return lambda s: s.replace(old, new)
+
+
+def multi_substitution(*substitutions):
+    """
+    Take a sequence of pairs specifying substitutions, and create
+    a function that performs those substitutions.
+
+    >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo')
+    'baz'
+    """
+    substitutions = itertools.starmap(substitution, substitutions)
+    # compose function applies last function first, so reverse the
+    #  substitutions to get the expected order.
+    substitutions = reversed(tuple(substitutions))
+    return compose(*substitutions)
+
+
+class FoldedCase(str):
+    """
+    A case insensitive string class; behaves just like str
+    except compares equal when the only variation is case.
+
+    >>> s = FoldedCase('hello world')
+
+    >>> s == 'Hello World'
+    True
+
+    >>> 'Hello World' == s
+    True
+
+    >>> s != 'Hello World'
+    False
+
+    >>> s.index('O')
+    4
+
+    >>> s.split('O')
+    ['hell', ' w', 'rld']
+
+    >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta']))
+    ['alpha', 'Beta', 'GAMMA']
+
+    Sequence membership is straightforward.
+
+    >>> "Hello World" in [s]
+    True
+    >>> s in ["Hello World"]
+    True
+
+    Allows testing for set inclusion, but candidate and elements
+    must both be folded.
+
+    >>> FoldedCase("Hello World") in {s}
+    True
+    >>> s in {FoldedCase("Hello World")}
+    True
+
+    String inclusion works as long as the FoldedCase object
+    is on the right.
+
+    >>> "hello" in FoldedCase("Hello World")
+    True
+
+    But not if the FoldedCase object is on the left:
+
+    >>> FoldedCase('hello') in 'Hello World'
+    False
+
+    In that case, use ``in_``:
+
+    >>> FoldedCase('hello').in_('Hello World')
+    True
+
+    >>> FoldedCase('hello') > FoldedCase('Hello')
+    False
+
+    >>> FoldedCase('ß') == FoldedCase('ss')
+    True
+    """
+
+    def __lt__(self, other):
+        return self.casefold() < other.casefold()
+
+    def __gt__(self, other):
+        return self.casefold() > other.casefold()
+
+    def __eq__(self, other):
+        return self.casefold() == other.casefold()
+
+    def __ne__(self, other):
+        return self.casefold() != other.casefold()
+
+    def __hash__(self):
+        return hash(self.casefold())
+
+    def __contains__(self, other):
+        return super().casefold().__contains__(other.casefold())
+
+    def in_(self, other):
+        "Does self appear in other?"
+        return self in FoldedCase(other)
+
+    # cache casefold since it's likely to be called frequently.
+    @method_cache
+    def casefold(self):
+        return super().casefold()
+
+    def index(self, sub):
+        return self.casefold().index(sub.casefold())
+
+    def split(self, splitter=' ', maxsplit=0):
+        pattern = re.compile(re.escape(splitter), re.I)
+        return pattern.split(self, maxsplit)
+
+
+# Python 3.8 compatibility
+_unicode_trap = ExceptionTrap(UnicodeDecodeError)
+
+
+@_unicode_trap.passes
+def is_decodable(value):
+    r"""
+    Return True if the supplied value is decodable (using the default
+    encoding).
+
+    >>> is_decodable(b'\xff')
+    False
+    >>> is_decodable(b'\x32')
+    True
+    """
+    value.decode()
+
+
+def is_binary(value):
+    r"""
+    Return True if the value appears to be binary (that is, it's a byte
+    string and isn't decodable).
+
+    >>> is_binary(b'\xff')
+    True
+    >>> is_binary('\xff')
+    False
+    """
+    return isinstance(value, bytes) and not is_decodable(value)
+
+
+def trim(s):
+    r"""
+    Trim something like a docstring to remove the whitespace that
+    is common due to indentation and formatting.
+
+    >>> trim("\n\tfoo = bar\n\t\tbar = baz\n")
+    'foo = bar\n\tbar = baz'
+    """
+    return textwrap.dedent(s).strip()
+
+
+def wrap(s):
+    """
+    Wrap lines of text, retaining existing newlines as
+    paragraph markers.
+
+    >>> print(wrap(lorem_ipsum))
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+    eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+    minim veniam, quis nostrud exercitation ullamco laboris nisi ut
+    aliquip ex ea commodo consequat. Duis aute irure dolor in
+    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+    culpa qui officia deserunt mollit anim id est laborum.
+    
+    Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam
+    varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus
+    magna felis sollicitudin mauris. Integer in mauris eu nibh euismod
+    gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis
+    risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue,
+    eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas
+    fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla
+    a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis,
+    neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing
+    sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque
+    nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus
+    quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis,
+    molestie eu, feugiat in, orci. In hac habitasse platea dictumst.
+    """
+    paragraphs = s.splitlines()
+    wrapped = ('\n'.join(textwrap.wrap(para)) for para in paragraphs)
+    return '\n\n'.join(wrapped)
+
+
+def unwrap(s):
+    r"""
+    Given a multi-line string, return an unwrapped version.
+
+    >>> wrapped = wrap(lorem_ipsum)
+    >>> wrapped.count('\n')
+    20
+    >>> unwrapped = unwrap(wrapped)
+    >>> unwrapped.count('\n')
+    1
+    >>> print(unwrapped)
+    Lorem ipsum dolor sit amet, consectetur adipiscing ...
+    Curabitur pretium tincidunt lacus. Nulla gravida orci ...
+
+    """
+    paragraphs = re.split(r'\n\n+', s)
+    cleaned = (para.replace('\n', ' ') for para in paragraphs)
+    return '\n'.join(cleaned)
+
+
+lorem_ipsum: str = (
+    files(__name__).joinpath('Lorem ipsum.txt').read_text(encoding='utf-8')
+)
+
+
+class Splitter:
+    """object that will split a string with the given arguments for each call
+
+    >>> s = Splitter(',')
+    >>> s('hello, world, this is your, master calling')
+    ['hello', ' world', ' this is your', ' master calling']
+    """
+
+    def __init__(self, *args):
+        self.args = args
+
+    def __call__(self, s):
+        return s.split(*self.args)
+
+
+def indent(string, prefix=' ' * 4):
+    """
+    >>> indent('foo')
+    '    foo'
+    """
+    return prefix + string
+
+
+class WordSet(tuple):
+    """
+    Given an identifier, return the words that identifier represents,
+    whether in camel case, underscore-separated, etc.
+
+    >>> WordSet.parse("camelCase")
+    ('camel', 'Case')
+
+    >>> WordSet.parse("under_sep")
+    ('under', 'sep')
+
+    Acronyms should be retained
+
+    >>> WordSet.parse("firstSNL")
+    ('first', 'SNL')
+
+    >>> WordSet.parse("you_and_I")
+    ('you', 'and', 'I')
+
+    >>> WordSet.parse("A simple test")
+    ('A', 'simple', 'test')
+
+    Multiple caps should not interfere with the first cap of another word.
+
+    >>> WordSet.parse("myABCClass")
+    ('my', 'ABC', 'Class')
+
+    The result is a WordSet, providing access to other forms.
+
+    >>> WordSet.parse("myABCClass").underscore_separated()
+    'my_ABC_Class'
+
+    >>> WordSet.parse('a-command').camel_case()
+    'ACommand'
+
+    >>> WordSet.parse('someIdentifier').lowered().space_separated()
+    'some identifier'
+
+    Slices of the result should return another WordSet.
+
+    >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated()
+    'out_of_context'
+
+    >>> WordSet.from_class_name(WordSet()).lowered().space_separated()
+    'word set'
+
+    >>> example = WordSet.parse('figured it out')
+    >>> example.headless_camel_case()
+    'figuredItOut'
+    >>> example.dash_separated()
+    'figured-it-out'
+
+    """
+
+    _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))')
+
+    def capitalized(self):
+        return WordSet(word.capitalize() for word in self)
+
+    def lowered(self):
+        return WordSet(word.lower() for word in self)
+
+    def camel_case(self):
+        return ''.join(self.capitalized())
+
+    def headless_camel_case(self):
+        words = iter(self)
+        first = next(words).lower()
+        new_words = itertools.chain((first,), WordSet(words).camel_case())
+        return ''.join(new_words)
+
+    def underscore_separated(self):
+        return '_'.join(self)
+
+    def dash_separated(self):
+        return '-'.join(self)
+
+    def space_separated(self):
+        return ' '.join(self)
+
+    def trim_right(self, item):
+        """
+        Remove the item from the end of the set.
+
+        >>> WordSet.parse('foo bar').trim_right('foo')
+        ('foo', 'bar')
+        >>> WordSet.parse('foo bar').trim_right('bar')
+        ('foo',)
+        >>> WordSet.parse('').trim_right('bar')
+        ()
+        """
+        return self[:-1] if self and self[-1] == item else self
+
+    def trim_left(self, item):
+        """
+        Remove the item from the beginning of the set.
+
+        >>> WordSet.parse('foo bar').trim_left('foo')
+        ('bar',)
+        >>> WordSet.parse('foo bar').trim_left('bar')
+        ('foo', 'bar')
+        >>> WordSet.parse('').trim_left('bar')
+        ()
+        """
+        return self[1:] if self and self[0] == item else self
+
+    def trim(self, item):
+        """
+        >>> WordSet.parse('foo bar').trim('foo')
+        ('bar',)
+        """
+        return self.trim_left(item).trim_right(item)
+
+    def __getitem__(self, item):
+        result = super().__getitem__(item)
+        if isinstance(item, slice):
+            result = WordSet(result)
+        return result
+
+    @classmethod
+    def parse(cls, identifier):
+        matches = cls._pattern.finditer(identifier)
+        return WordSet(match.group(0) for match in matches)
+
+    @classmethod
+    def from_class_name(cls, subject):
+        return cls.parse(subject.__class__.__name__)
+
+
+# for backward compatibility
+words = WordSet.parse
+
+
+def simple_html_strip(s):
+    r"""
+    Remove HTML from the string `s`.
+
+    >>> str(simple_html_strip(''))
+    ''
+
+    >>> print(simple_html_strip('A stormy day in paradise'))
+    A stormy day in paradise
+
+    >>> print(simple_html_strip('Somebody  tell the truth.'))
+    Somebody  tell the truth.
+
+    >>> print(simple_html_strip('What about
\nmultiple lines?')) + What about + multiple lines? + """ + html_stripper = re.compile('()|(<[^>]*>)|([^<]+)', re.DOTALL) + texts = (match.group(3) or '' for match in html_stripper.finditer(s)) + return ''.join(texts) + + +class SeparatedValues(str): + """ + A string separated by a separator. Overrides __iter__ for getting + the values. + + >>> list(SeparatedValues('a,b,c')) + ['a', 'b', 'c'] + + Whitespace is stripped and empty values are discarded. + + >>> list(SeparatedValues(' a, b , c, ')) + ['a', 'b', 'c'] + """ + + separator = ',' + + def __iter__(self): + parts = self.split(self.separator) + return filter(None, (part.strip() for part in parts)) + + +class Stripper: + r""" + Given a series of lines, find the common prefix and strip it from them. + + >>> lines = [ + ... 'abcdefg\n', + ... 'abc\n', + ... 'abcde\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix + 'abc' + >>> list(res.lines) + ['defg\n', '\n', 'de\n'] + + If no prefix is common, nothing should be stripped. + + >>> lines = [ + ... 'abcd\n', + ... '1234\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix = '' + >>> list(res.lines) + ['abcd\n', '1234\n'] + """ + + def __init__(self, prefix, lines): + self.prefix = prefix + self.lines = map(self, lines) + + @classmethod + def strip_prefix(cls, lines): + prefix_lines, lines = itertools.tee(lines) + prefix = functools.reduce(cls.common_prefix, prefix_lines) + return cls(prefix, lines) + + def __call__(self, line): + if not self.prefix: + return line + null, prefix, rest = line.partition(self.prefix) + return rest + + @staticmethod + def common_prefix(s1, s2): + """ + Return the common prefix of two lines. + """ + index = min(len(s1), len(s2)) + while s1[:index] != s2[:index]: + index -= 1 + return s1[:index] + + +def remove_prefix(text, prefix): + """ + Remove the prefix from the text if it exists. + + >>> remove_prefix('underwhelming performance', 'underwhelming ') + 'performance' + + >>> remove_prefix('something special', 'sample') + 'something special' + """ + null, prefix, rest = text.rpartition(prefix) + return rest + + +def remove_suffix(text, suffix): + """ + Remove the suffix from the text if it exists. + + >>> remove_suffix('name.git', '.git') + 'name' + + >>> remove_suffix('something special', 'sample') + 'something special' + """ + rest, suffix, null = text.partition(suffix) + return rest + + +def normalize_newlines(text): + r""" + Replace alternate newlines with the canonical newline. + + >>> normalize_newlines('Lorem Ipsum\u2029') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\r\n') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\x85') + 'Lorem Ipsum\n' + """ + newlines = ['\r\n', '\r', '\n', '\u0085', '\u2028', '\u2029'] + pattern = '|'.join(newlines) + return re.sub(pattern, '\n', text) + + +def _nonblank(str): + return str and not str.startswith('#') + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return clean(text.splitlines()) + + +def clean(lines: Iterable[str]): + """ + Yield non-blank, non-comment elements from lines. + """ + return filter(_nonblank, map(str.strip, lines)) + + +def drop_comment(line): + """ + Drop comments. + + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' + """ + return line.partition(' #')[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. + + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith('\\'): + try: + item = item[:-2].strip() + next(lines) + except StopIteration: + return + yield item + + +def read_newlines(filename, limit=1024): + r""" + >>> tmp_path = getfixture('tmp_path') + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\n', newline='', encoding='utf-8') + >>> read_newlines(filename) + '\n' + >>> _ = filename.write_text('foo\r\n', newline='', encoding='utf-8') + >>> read_newlines(filename) + '\r\n' + >>> _ = filename.write_text('foo\r\nbar\nbing\r', newline='', encoding='utf-8') + >>> read_newlines(filename) + ('\r', '\n', '\r\n') + """ + with open(filename, encoding='utf-8') as fp: + fp.read(limit) + return fp.newlines + + +def lines_from(input): + """ + Generate lines from a :class:`importlib.resources.abc.Traversable` path. + + >>> lines = lines_from(files(__name__).joinpath('Lorem ipsum.txt')) + >>> next(lines) + 'Lorem ipsum...' + >>> next(lines) + 'Curabitur pretium...' + """ + with input.open(encoding='utf-8') as stream: + yield from stream diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/layouts.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/layouts.py new file mode 100644 index 0000000..9636f0f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/layouts.py @@ -0,0 +1,25 @@ +qwerty = "-=qwertyuiop[]asdfghjkl;'zxcvbnm,./_+QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" +dvorak = "[]',.pyfgcrl/=aoeuidhtns-;qjkxbmwvz{}\"<>PYFGCRL?+AOEUIDHTNS_:QJKXBMWVZ" + + +to_dvorak = str.maketrans(qwerty, dvorak) +to_qwerty = str.maketrans(dvorak, qwerty) + + +def translate(input, translation): + """ + >>> translate('dvorak', to_dvorak) + 'ekrpat' + >>> translate('qwerty', to_qwerty) + 'x,dokt' + """ + return input.translate(translation) + + +def _translate_stream(stream, translation): + """ + >>> import io + >>> _translate_stream(io.StringIO('foo'), to_dvorak) + urr + """ + print(translate(stream.read(), translation)) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/show-newlines.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/show-newlines.py new file mode 100644 index 0000000..ef4cc54 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/show-newlines.py @@ -0,0 +1,32 @@ +import autocommand +import inflect +from more_itertools import always_iterable + +import jaraco.text + + +def report_newlines(filename): + r""" + Report the newlines in the indicated file. + + >>> tmp_path = getfixture('tmp_path') + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\nbar\n', newline='', encoding='utf-8') + >>> report_newlines(filename) + newline is '\n' + >>> filename = tmp_path / 'out.txt' + >>> _ = filename.write_text('foo\nbar\r\n', newline='', encoding='utf-8') + >>> report_newlines(filename) + newlines are ('\n', '\r\n') + """ + newlines = jaraco.text.read_newlines(filename) + count = len(tuple(always_iterable(newlines))) + engine = inflect.engine() + print( + engine.plural_noun("newline", count), + engine.plural_verb("is", count), + repr(newlines), + ) + + +autocommand.autocommand(__name__)(report_newlines) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/strip-prefix.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/strip-prefix.py new file mode 100644 index 0000000..761717a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/strip-prefix.py @@ -0,0 +1,21 @@ +import sys + +import autocommand + +from jaraco.text import Stripper + + +def strip_prefix(): + r""" + Strip any common prefix from stdin. + + >>> import io, pytest + >>> getfixture('monkeypatch').setattr('sys.stdin', io.StringIO('abcdef\nabc123')) + >>> strip_prefix() + def + 123 + """ + sys.stdout.writelines(Stripper.strip_prefix(sys.stdin).lines) + + +autocommand.autocommand(__name__)(strip_prefix) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-dvorak.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-dvorak.py new file mode 100644 index 0000000..14c8981 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-dvorak.py @@ -0,0 +1,5 @@ +import sys + +from . import layouts + +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_dvorak) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-qwerty.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-qwerty.py new file mode 100644 index 0000000..23596fd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco/text/to-qwerty.py @@ -0,0 +1,5 @@ +import sys + +from . import layouts + +__name__ == '__main__' and layouts._translate_stream(sys.stdin, layouts.to_qwerty) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..5c69047 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/METADATA new file mode 100644 index 0000000..8fb5e53 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/METADATA @@ -0,0 +1,82 @@ +Metadata-Version: 2.4 +Name: jaraco.context +Version: 6.1.0 +Summary: Useful decorators and context managers +Author-email: "Jason R. Coombs" +License-Expression: MIT +Project-URL: Source, https://github.com/jaraco/jaraco.context +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: backports.tarfile; python_version < "3.12" +Provides-Extra: test +Requires-Dist: pytest!=8.1.*,>=6; extra == "test" +Requires-Dist: jaraco.test>=5.6.0; extra == "test" +Requires-Dist: portend; extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx>=3.5; extra == "doc" +Requires-Dist: jaraco.packaging>=9.3; extra == "doc" +Requires-Dist: rst.linker>=1.9; extra == "doc" +Requires-Dist: furo; extra == "doc" +Requires-Dist: sphinx-lint; extra == "doc" +Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=3.4; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy>=1.0.1; extra == "type" +Requires-Dist: mypy<1.19; platform_python_implementation == "PyPy" and extra == "type" +Dynamic: license-file + +.. image:: https://img.shields.io/pypi/v/jaraco.context.svg + :target: https://pypi.org/project/jaraco.context + +.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg + +.. image:: https://github.com/jaraco/jaraco.context/actions/workflows/main.yml/badge.svg + :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + +.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest + :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2025-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/jaraco.context + :target: https://tidelift.com/subscription/pkg/pypi-jaraco.context?utm_source=pypi-jaraco.context&utm_medium=readme + + +Highlights +========== + +See the docs linked from the badge above for the full details, but here are some features that may be of interest. + +- ``ExceptionTrap`` provides a general-purpose wrapper for trapping exceptions and then acting on the outcome. Includes ``passes`` and ``raises`` decorators to replace the result of a wrapped function by a boolean indicating the outcome of the exception trap. See `this keyring commit `_ for an example of it in production. +- ``suppress`` simply enables ``contextlib.suppress`` as a decorator. +- ``on_interrupt`` is a decorator used by CLI entry points to affect the handling of a ``KeyboardInterrupt``. Inspired by `Lucretiel/autocommand#18 `_. +- ``pushd`` is similar to pytest's ``monkeypatch.chdir`` or path's `default context `_, changes the current working directory for the duration of the context. +- ``tarball`` will download a tarball, extract it, change directory, yield, then clean up after. Convenient when working with web assets. +- ``null`` is there for those times when one code branch needs a context and the other doesn't; this null context provides symmetry across those branches. + + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/RECORD new file mode 100644 index 0000000..e5169f6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/RECORD @@ -0,0 +1,9 @@ +jaraco/context/__init__.py,sha256=br1ydYGo1Xr_Pu1anuEdd-QrjUiz_EY5L_5E4C03L4w,9809 +jaraco/context/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jaraco_context-6.1.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +jaraco_context-6.1.0.dist-info/METADATA,sha256=BDXr_FIFXFqZdO0gwXG2RUOD6vnbsVCIFLp62XxZ1xI,4270 +jaraco_context-6.1.0.dist-info/RECORD,, +jaraco_context-6.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jaraco_context-6.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +jaraco_context-6.1.0.dist-info/licenses/LICENSE,sha256=l1WhhRlmbl8PTK49qtPXASvK5IpgCzEjfXXp_hNOZoM,1076 +jaraco_context-6.1.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/WHEEL new file mode 100644 index 0000000..e7fa31b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..c891f41 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..f6205a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +jaraco diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/INSTALLER new file mode 100644 index 0000000..5c69047 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/METADATA new file mode 100644 index 0000000..f2150dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/METADATA @@ -0,0 +1,69 @@ +Metadata-Version: 2.4 +Name: jaraco.functools +Version: 4.4.0 +Summary: Functools like those found in stdlib +Author-email: "Jason R. Coombs" +License-Expression: MIT +Project-URL: Source, https://github.com/jaraco/jaraco.functools +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: more_itertools +Provides-Extra: test +Requires-Dist: pytest!=8.1.*,>=6; extra == "test" +Requires-Dist: jaraco.classes; extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx>=3.5; extra == "doc" +Requires-Dist: jaraco.packaging>=9.3; extra == "doc" +Requires-Dist: rst.linker>=1.9; extra == "doc" +Requires-Dist: furo; extra == "doc" +Requires-Dist: sphinx-lint; extra == "doc" +Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=3.4; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy>=1.0.1; extra == "type" +Requires-Dist: mypy<1.19; platform_python_implementation == "PyPy" and extra == "type" +Dynamic: license-file + +.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg + :target: https://pypi.org/project/jaraco.functools + +.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg + +.. image:: https://github.com/jaraco/jaraco.functools/actions/workflows/main.yml/badge.svg + :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + +.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest + :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2025-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/jaraco.functools + :target: https://tidelift.com/subscription/pkg/pypi-jaraco.functools?utm_source=pypi-jaraco.functools&utm_medium=readme + +Additional functools in the spirit of stdlib's functools. + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/RECORD new file mode 100644 index 0000000..3516c9f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/RECORD @@ -0,0 +1,10 @@ +jaraco/functools/__init__.py,sha256=ZJx9cMs2Nvk2xGUl8OjVGkpjdOaNlSzJrN4dGglgX2g,18599 +jaraco/functools/__init__.pyi,sha256=K4DcbnYIHE5QlMxqf9-cVp-WhycrhuTao4J7O7TMq4Y,3907 +jaraco/functools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jaraco_functools-4.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +jaraco_functools-4.4.0.dist-info/METADATA,sha256=LnnajcNGmSSr46yLIqP-tWkqeb-fR7vIa2U11hhkGEk,2960 +jaraco_functools-4.4.0.dist-info/RECORD,, +jaraco_functools-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jaraco_functools-4.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +jaraco_functools-4.4.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076 +jaraco_functools-4.4.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/WHEEL new file mode 100644 index 0000000..e7fa31b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..f60bd57 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2025 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..f6205a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +jaraco diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..5c69047 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/METADATA new file mode 100644 index 0000000..bb7a3db --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/METADATA @@ -0,0 +1,283 @@ +Metadata-Version: 2.4 +Name: more-itertools +Version: 10.8.0 +Summary: More routines for operating on iterables, beyond itertools +Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked +Author-email: Erik Rose +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-Expression: MIT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +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 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries +License-File: LICENSE +Project-URL: Documentation, https://more-itertools.readthedocs.io/en/stable/ +Project-URL: Homepage, https://github.com/more-itertools/more-itertools + +============== +More Itertools +============== + +.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest + :target: https://more-itertools.readthedocs.io/en/stable/ + +Python's ``itertools`` library is a gem - you can compose elegant solutions +for a variety of problems with the functions it provides. In ``more-itertools`` +we collect additional building blocks, recipes, and routines for working with +Python iterables. + ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Grouping | `chunked `_, | +| | `ichunked `_, | +| | `chunked_even `_, | +| | `sliced `_, | +| | `constrained_batches `_, | +| | `distribute `_, | +| | `divide `_, | +| | `split_at `_, | +| | `split_before `_, | +| | `split_after `_, | +| | `split_into `_, | +| | `split_when `_, | +| | `bucket `_, | +| | `unzip `_, | +| | `batched `_, | +| | `grouper `_, | +| | `partition `_, | +| | `transpose `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Lookahead and lookback | `spy `_, | +| | `peekable `_, | +| | `seekable `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Windowing | `windowed `_, | +| | `substrings `_, | +| | `substrings_indexes `_, | +| | `stagger `_, | +| | `windowed_complete `_, | +| | `pairwise `_, | +| | `triplewise `_, | +| | `sliding_window `_, | +| | `subslices `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Augmenting | `count_cycle `_, | +| | `intersperse `_, | +| | `padded `_, | +| | `repeat_each `_, | +| | `mark_ends `_, | +| | `repeat_last `_, | +| | `adjacent `_, | +| | `groupby_transform `_, | +| | `pad_none `_, | +| | `ncycles `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combining | `collapse `_, | +| | `sort_together `_, | +| | `interleave `_, | +| | `interleave_longest `_, | +| | `interleave_evenly `_, | +| | `interleave_randomly `_, | +| | `zip_offset `_, | +| | `zip_equal `_, | +| | `zip_broadcast `_, | +| | `flatten `_, | +| | `roundrobin `_, | +| | `prepend `_, | +| | `value_chain `_, | +| | `partial_product `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Summarizing | `ilen `_, | +| | `unique_to_each `_, | +| | `sample `_, | +| | `consecutive_groups `_, | +| | `run_length `_, | +| | `map_reduce `_, | +| | `join_mappings `_, | +| | `exactly_n `_, | +| | `is_sorted `_, | +| | `all_equal `_, | +| | `all_unique `_, | +| | `argmin `_, | +| | `argmax `_, | +| | `minmax `_, | +| | `first_true `_, | +| | `quantify `_, | +| | `iequals `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Selecting | `islice_extended `_, | +| | `first `_, | +| | `last `_, | +| | `one `_, | +| | `only `_, | +| | `strictly_n `_, | +| | `strip `_, | +| | `lstrip `_, | +| | `rstrip `_, | +| | `filter_except `_, | +| | `map_except `_, | +| | `filter_map `_, | +| | `iter_suppress `_, | +| | `nth_or_last `_, | +| | `extract `_, | +| | `unique_in_window `_, | +| | `before_and_after `_, | +| | `nth `_, | +| | `take `_, | +| | `tail `_, | +| | `unique_everseen `_, | +| | `unique_justseen `_, | +| | `unique `_, | +| | `duplicates_everseen `_, | +| | `duplicates_justseen `_, | +| | `classify_unique `_, | +| | `longest_common_prefix `_, | +| | `takewhile_inclusive `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Math | `dft `_, | +| | `idft `_, | +| | `convolve `_, | +| | `dotproduct `_, | +| | `matmul `_, | +| | `polynomial_from_roots `_, | +| | `polynomial_derivative `_, | +| | `polynomial_eval `_, | +| | `sum_of_squares `_, | +| | `running_median `_, | +| | `totient `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Integer math | `factor `_, | +| | `is_prime `_, | +| | `multinomial `_, | +| | `nth_prime `_, | +| | `sieve `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Combinatorics | `circular_shifts `_, | +| | `derangements `_, | +| | `gray_product `_, | +| | `outer_product `_, | +| | `partitions `_, | +| | `set_partitions `_, | +| | `powerset `_, | +| | `powerset_of_sets `_ | +| +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | `distinct_combinations `_, | +| | `distinct_permutations `_ | +| +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | `combination_index `_, | +| | `combination_with_replacement_index `_, | +| | `permutation_index `_, | +| | `product_index `_ | +| +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | `nth_combination `_, | +| | `nth_combination_with_replacement `_, | +| | `nth_permutation `_, | +| | `nth_product `_ | +| +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | `random_combination `_, | +| | `random_combination_with_replacement `_, | +| | `random_permutation `_, | +| | `random_product `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Wrapping | `always_iterable `_, | +| | `always_reversible `_, | +| | `countable `_, | +| | `consumer `_, | +| | `with_iter `_, | +| | `iter_except `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Others | `locate `_, | +| | `rlocate `_, | +| | `replace `_, | +| | `numeric_range `_, | +| | `side_effect `_, | +| | `iterate `_, | +| | `loops `_, | +| | `difference `_, | +| | `make_decorator `_, | +| | `SequenceView `_, | +| | `time_limited `_, | +| | `map_if `_, | +| | `iter_index `_, | +| | `consume `_, | +| | `tabulate `_, | +| | `repeatfunc `_, | +| | `reshape `_, | +| | `doublestarmap `_ | ++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +Getting started +=============== + +To get started, install the library with `pip `_: + +.. code-block:: shell + + pip install more-itertools + +The recipes from the `itertools docs `_ +are included in the top-level package: + +.. code-block:: python + + >>> from more_itertools import flatten + >>> iterable = [(0, 1), (2, 3)] + >>> list(flatten(iterable)) + [0, 1, 2, 3] + +Several new recipes are available as well: + +.. code-block:: python + + >>> from more_itertools import chunked + >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8] + >>> list(chunked(iterable, 3)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + >>> from more_itertools import spy + >>> iterable = (x * x for x in range(1, 6)) + >>> head, iterable = spy(iterable, n=3) + >>> list(head) + [1, 4, 9] + >>> list(iterable) + [1, 4, 9, 16, 25] + + + +For the full listing of functions, see the `API documentation `_. + + +Links elsewhere +=============== + +Blog posts about ``more-itertools``: + +* `Yo, I heard you like decorators `__ +* `Tour of Python Itertools `__ (`Alternate `__) +* `Real-World Python More Itertools `_ + + +Development +=========== + +``more-itertools`` is maintained by `@erikrose `_ +and `@bbayles `_, with help from `many others `_. +If you have a problem or suggestion, please file a bug or pull request in this +repository. Thanks for contributing! + + +Version History +=============== + +The version history can be found in `documentation `_. + diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/RECORD new file mode 100644 index 0000000..61ef7d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/RECORD @@ -0,0 +1,13 @@ +more_itertools-10.8.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +more_itertools-10.8.0.dist-info/METADATA,sha256=arNRUUWr5YsGfwh8hnYxz0z11lP-2BuWQu4SCGw5BLg,39413 +more_itertools-10.8.0.dist-info/RECORD,, +more_itertools-10.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +more_itertools-10.8.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +more_itertools-10.8.0.dist-info/licenses/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053 +more_itertools/__init__.py,sha256=5F7E_zpoGcEBW_T_3WE0WYYt8j-gJodIuiBcOJxrOv8,149 +more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43 +more_itertools/more.py,sha256=mNPKKu5UI7lRL460vgm0QTCWFiGMVCMosSPxVSdibos,163690 +more_itertools/more.pyi,sha256=fpEgNX3O66wY5cnT-s5VYDKNUpAcaCyU3iP84It3OOM,27119 +more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +more_itertools/recipes.py,sha256=Ma-kuBNZDFhaQDbIJgRmnrG86WzaupbOyUV3v8je3xw,41811 +more_itertools/recipes.pyi,sha256=LNRwN-OL3nkMfQAqx-PPc1fBaetUObb_Z6mdePyzh1c,6226 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..0a523be --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Erik Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.py new file mode 100644 index 0000000..24216c5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.py @@ -0,0 +1,6 @@ +"""More routines for operating on iterables, beyond itertools""" + +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '10.8.0' diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.pyi b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.pyi new file mode 100644 index 0000000..96f6e36 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.py new file mode 100644 index 0000000..bf50195 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.py @@ -0,0 +1,5303 @@ +import math +import warnings + +from collections import Counter, defaultdict, deque, abc +from collections.abc import Sequence +from contextlib import suppress +from functools import cached_property, partial, reduce, wraps +from heapq import heapify, heapreplace +from itertools import ( + chain, + combinations, + compress, + count, + cycle, + dropwhile, + groupby, + islice, + permutations, + repeat, + starmap, + takewhile, + tee, + zip_longest, + product, +) +from math import comb, e, exp, factorial, floor, fsum, log, log1p, perm, tau +from math import ceil +from queue import Empty, Queue +from random import random, randrange, shuffle, uniform +from operator import ( + attrgetter, + is_not, + itemgetter, + lt, + mul, + neg, + sub, + gt, +) +from sys import hexversion, maxsize +from time import monotonic + +from .recipes import ( + _marker, + _zip_equal, + UnequalIterablesError, + consume, + first_true, + flatten, + is_prime, + nth, + powerset, + sieve, + take, + unique_everseen, + all_equal, + batched, +) + +__all__ = [ + 'AbortThread', + 'SequenceView', + 'UnequalIterablesError', + 'adjacent', + 'all_unique', + 'always_iterable', + 'always_reversible', + 'argmax', + 'argmin', + 'bucket', + 'callback_iter', + 'chunked', + 'chunked_even', + 'circular_shifts', + 'collapse', + 'combination_index', + 'combination_with_replacement_index', + 'consecutive_groups', + 'constrained_batches', + 'consumer', + 'count_cycle', + 'countable', + 'derangements', + 'dft', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'doublestarmap', + 'duplicates_everseen', + 'duplicates_justseen', + 'classify_unique', + 'exactly_n', + 'extract', + 'filter_except', + 'filter_map', + 'first', + 'gray_product', + 'groupby_transform', + 'ichunked', + 'iequals', + 'idft', + 'ilen', + 'interleave', + 'interleave_evenly', + 'interleave_longest', + 'interleave_randomly', + 'intersperse', + 'is_sorted', + 'islice_extended', + 'iterate', + 'iter_suppress', + 'join_mappings', + 'last', + 'locate', + 'longest_common_prefix', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_if', + 'map_reduce', + 'mark_ends', + 'minmax', + 'nth_or_last', + 'nth_permutation', + 'nth_prime', + 'nth_product', + 'nth_combination_with_replacement', + 'numeric_range', + 'one', + 'only', + 'outer_product', + 'padded', + 'partial_product', + 'partitions', + 'peekable', + 'permutation_index', + 'powerset_of_sets', + 'product_index', + 'raise_', + 'repeat_each', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'set_partitions', + 'side_effect', + 'sliced', + 'sort_together', + 'split_after', + 'split_at', + 'split_before', + 'split_into', + 'split_when', + 'spy', + 'stagger', + 'strip', + 'strictly_n', + 'substrings', + 'substrings_indexes', + 'takewhile_inclusive', + 'time_limited', + 'unique_in_window', + 'unique_to_each', + 'unzip', + 'value_chain', + 'windowed', + 'windowed_complete', + 'with_iter', + 'zip_broadcast', + 'zip_equal', + 'zip_offset', +] + +# math.sumprod is available for Python 3.12+ +try: + from math import sumprod as _fsumprod + +except ImportError: # pragma: no cover + # Extended precision algorithms from T. J. Dekker, + # "A Floating-Point Technique for Extending the Available Precision" + # https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf + # Formulas: (5.5) (5.6) and (5.8). Code: mul12() + + def dl_split(x: float): + "Split a float into two half-precision components." + t = x * 134217729.0 # Veltkamp constant = 2.0 ** 27 + 1 + hi = t - (t - x) + lo = x - hi + return hi, lo + + def dl_mul(x, y): + "Lossless multiplication." + xx_hi, xx_lo = dl_split(x) + yy_hi, yy_lo = dl_split(y) + p = xx_hi * yy_hi + q = xx_hi * yy_lo + xx_lo * yy_hi + z = p + q + zz = p - z + q + xx_lo * yy_lo + return z, zz + + def _fsumprod(p, q): + return fsum(chain.from_iterable(map(dl_mul, p, q))) + + +def chunked(iterable, n, strict=False): + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + By the default, the last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + If the length of *iterable* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + list is yielded. + + """ + iterator = iter(partial(take, n, iter(iterable)), []) + if strict: + if n is None: + raise ValueError('n must not be None when using strict mode.') + + def ret(): + for chunk in iterator: + if len(chunk) != n: + raise ValueError('iterable is not divisible by n.') + yield chunk + + return ret() + else: + return iterator + + +def first(iterable, default=_marker): + """Return the first item of *iterable*, or *default* if *iterable* is + empty. + + >>> first([0, 1, 2, 3]) + 0 + >>> first([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + + :func:`first` is useful when you have a generator of expensive-to-retrieve + values and want any arbitrary one. It is marginally shorter than + ``next(iter(iterable), default)``. + + """ + for item in iterable: + return item + if default is _marker: + raise ValueError( + 'first() was called on an empty iterable, ' + 'and no default value was provided.' + ) + return default + + +def last(iterable, default=_marker): + """Return the last item of *iterable*, or *default* if *iterable* is + empty. + + >>> last([0, 1, 2, 3]) + 3 + >>> last([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + try: + if isinstance(iterable, Sequence): + return iterable[-1] + # Work around https://bugs.python.org/issue38525 + if getattr(iterable, '__reversed__', None): + return next(reversed(iterable)) + return deque(iterable, maxlen=1)[-1] + except (IndexError, TypeError, StopIteration): + if default is _marker: + raise ValueError( + 'last() was called on an empty iterable, ' + 'and no default value was provided.' + ) + return default + + +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: + """Wrap an iterator to allow lookahead and prepending elements. + + Call :meth:`peek` on the result to get the value that will be returned + by :func:`next`. This won't advance the iterator: + + >>> p = peekable(['a', 'b']) + >>> p.peek() + 'a' + >>> next(p) + 'a' + + Pass :meth:`peek` a default value to return that instead of raising + ``StopIteration`` when the iterator is exhausted. + + >>> p = peekable([]) + >>> p.peek('hi') + 'hi' + + peekables also offer a :meth:`prepend` method, which "inserts" items + at the head of the iterable: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> p.peek() + 11 + >>> list(p) + [11, 12, 1, 2, 3] + + peekables can be indexed. Index 0 is the item that will be returned by + :func:`next`, index 1 is the item after that, and so on: + The values up to the given index will be cached. + + >>> p = peekable(['a', 'b', 'c', 'd']) + >>> p[0] + 'a' + >>> p[1] + 'b' + >>> next(p) + 'a' + + Negative indexes are supported, but be aware that they will cache the + remaining items in the source iterator, which may require significant + storage. + + To check whether a peekable is exhausted, check its truth value: + + >>> p = peekable(['a', 'b']) + >>> if p: # peekable has items + ... list(p) + ['a', 'b'] + >>> if not p: # peekable is exhausted + ... list(p) + [] + + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self._cache = deque() + + def __iter__(self): + return self + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + """Return the item that will be next returned from ``next()``. + + Return ``default`` if there are no items left. If ``default`` is not + provided, raise ``StopIteration``. + + """ + if not self._cache: + try: + self._cache.append(next(self._it)) + except StopIteration: + if default is _marker: + raise + return default + return self._cache[0] + + def prepend(self, *items): + """Stack up items to be the next ones returned from ``next()`` or + ``self.peek()``. The items will be returned in + first in, first out order:: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> list(p) + [11, 12, 1, 2, 3] + + It is possible, by prepending items, to "resurrect" a peekable that + previously raised ``StopIteration``. + + >>> p = peekable([]) + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + >>> p.prepend(1) + >>> next(p) + 1 + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + + """ + self._cache.extendleft(reversed(items)) + + def __next__(self): + if self._cache: + return self._cache.popleft() + + return next(self._it) + + def _get_slice(self, index): + # Normalize the slice's arguments + step = 1 if (index.step is None) else index.step + if step > 0: + start = 0 if (index.start is None) else index.start + stop = maxsize if (index.stop is None) else index.stop + elif step < 0: + start = -1 if (index.start is None) else index.start + stop = (-maxsize - 1) if (index.stop is None) else index.stop + else: + raise ValueError('slice step cannot be zero') + + # If either the start or stop index is negative, we'll need to cache + # the rest of the iterable in order to slice from the right side. + if (start < 0) or (stop < 0): + self._cache.extend(self._it) + # Otherwise we'll need to find the rightmost index and cache to that + # point. + else: + n = min(max(start, stop) + 1, maxsize) + cache_len = len(self._cache) + if n >= cache_len: + self._cache.extend(islice(self._it, n - cache_len)) + + return list(self._cache)[index] + + def __getitem__(self, index): + if isinstance(index, slice): + return self._get_slice(index) + + cache_len = len(self._cache) + if index < 0: + self._cache.extend(self._it) + elif index >= cache_len: + self._cache.extend(islice(self._it, index + 1 - cache_len)) + + return self._cache[index] + + +def consumer(func): + """Decorator that automatically advances a PEP-342-style "reverse iterator" + to its first yield point so you don't have to call ``next()`` on it + manually. + + >>> @consumer + ... def tally(): + ... i = 0 + ... while True: + ... print('Thing number %s is %s.' % (i, (yield))) + ... i += 1 + ... + >>> t = tally() + >>> t.send('red') + Thing number 0 is red. + >>> t.send('fish') + Thing number 1 is fish. + + Without the decorator, you would have to call ``next(t)`` before + ``t.send()`` could be used. + + """ + + @wraps(func) + def wrapper(*args, **kwargs): + gen = func(*args, **kwargs) + next(gen) + return gen + + return wrapper + + +def ilen(iterable): + """Return the number of items in *iterable*. + + For example, there are 168 prime numbers below 1,000: + + >>> ilen(sieve(1000)) + 168 + + Equivalent to, but faster than:: + + def ilen(iterable): + count = 0 + for _ in iterable: + count += 1 + return count + + This fully consumes the iterable, so handle with care. + + """ + # This is the "most beautiful of the fast variants" of this function. + # If you think you can improve on it, please ensure that your version + # is both 10x faster and 10x more beautiful. + return sum(compress(repeat(1), zip(iterable))) + + +def iterate(func, start): + """Return ``start``, ``func(start)``, ``func(func(start))``, ... + + Produces an infinite iterator. To add a stopping condition, + use :func:`take`, ``takewhile``, or :func:`takewhile_inclusive`:. + + >>> take(10, iterate(lambda x: 2*x, 1)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + >>> collatz = lambda x: 3*x + 1 if x%2==1 else x // 2 + >>> list(takewhile_inclusive(lambda x: x!=1, iterate(collatz, 10))) + [10, 5, 16, 8, 4, 2, 1] + + """ + with suppress(StopIteration): + while True: + yield start + start = func(start) + + +def with_iter(context_manager): + """Wrap an iterable in a ``with`` statement, so it closes once exhausted. + + For example, this will close the file when the iterator is exhausted:: + + upper_lines = (line.upper() for line in with_iter(open('foo'))) + + Any context manager which returns an iterable is a candidate for + ``with_iter``. + + """ + with context_manager as iterable: + yield from iterable + + +def one(iterable, too_short=None, too_long=None): + """Return the first item from *iterable*, which is expected to contain only + that item. Raise an exception if *iterable* is empty or has more than one + item. + + :func:`one` is useful for ensuring that an iterable contains only one item. + For example, it can be used to retrieve the result of a database query + that is expected to return a single row. + + If *iterable* is empty, ``ValueError`` will be raised. You may specify a + different exception with the *too_short* keyword: + + >>> it = [] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too few items in iterable (expected 1)' + >>> too_short = IndexError('too few items') + >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + IndexError: too few items + + Similarly, if *iterable* contains more than one item, ``ValueError`` will + be raised. You may specify a different exception with the *too_long* + keyword: + + >>> it = ['too', 'many'] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. + >>> too_long = RuntimeError + >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + Note that :func:`one` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. + + """ + iterator = iter(iterable) + for first in iterator: + for second in iterator: + msg = ( + f'Expected exactly one item in iterable, but got {first!r}, ' + f'{second!r}, and perhaps more.' + ) + raise too_long or ValueError(msg) + return first + raise too_short or ValueError('too few items in iterable (expected 1)') + + +def raise_(exception, *args): + raise exception(*args) + + +def strictly_n(iterable, n, too_short=None, too_long=None): + """Validate that *iterable* has exactly *n* items and return them if + it does. If it has fewer than *n* items, call function *too_short* + with the actual number of items. If it has more than *n* items, call function + *too_long* with the number ``n + 1``. + + >>> iterable = ['a', 'b', 'c', 'd'] + >>> n = 4 + >>> list(strictly_n(iterable, n)) + ['a', 'b', 'c', 'd'] + + Note that the returned iterable must be consumed in order for the check to + be made. + + By default, *too_short* and *too_long* are functions that raise + ``ValueError``. + + >>> list(strictly_n('ab', 3)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too few items in iterable (got 2) + + >>> list(strictly_n('abc', 2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (got at least 3) + + You can instead supply functions that do something else. + *too_short* will be called with the number of items in *iterable*. + *too_long* will be called with `n + 1`. + + >>> def too_short(item_count): + ... raise RuntimeError + >>> it = strictly_n('abcd', 6, too_short=too_short) + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + >>> def too_long(item_count): + ... print('The boss is going to hear about this') + >>> it = strictly_n('abcdef', 4, too_long=too_long) + >>> list(it) + The boss is going to hear about this + ['a', 'b', 'c', 'd'] + + """ + if too_short is None: + too_short = lambda item_count: raise_( + ValueError, + f'Too few items in iterable (got {item_count})', + ) + + if too_long is None: + too_long = lambda item_count: raise_( + ValueError, + f'Too many items in iterable (got at least {item_count})', + ) + + it = iter(iterable) + + sent = 0 + for item in islice(it, n): + yield item + sent += 1 + + if sent < n: + too_short(sent) + return + + for item in it: + too_long(n + 1) + return + + +def distinct_permutations(iterable, r=None): + """Yield successive distinct permutations of the elements in *iterable*. + + >>> sorted(distinct_permutations([1, 0, 1])) + [(0, 1, 1), (1, 0, 1), (1, 1, 0)] + + Equivalent to yielding from ``set(permutations(iterable))``, except + duplicates are not generated and thrown away. For larger input sequences + this is much more efficient. + + Duplicate permutations arise when there are duplicated elements in the + input iterable. The number of items returned is + `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of + items input, and each `x_i` is the count of a distinct item in the input + sequence. The function :func:`multinomial` computes this directly. + + If *r* is given, only the *r*-length permutations are yielded. + + >>> sorted(distinct_permutations([1, 0, 1], r=2)) + [(0, 1), (1, 0), (1, 1)] + >>> sorted(distinct_permutations(range(3), r=2)) + [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + + *iterable* need not be sortable, but note that using equal (``x == y``) + but non-identical (``id(x) != id(y)``) elements may produce surprising + behavior. For example, ``1`` and ``True`` are equal but non-identical: + + >>> list(distinct_permutations([1, True, '3'])) # doctest: +SKIP + [ + (1, True, '3'), + (1, '3', True), + ('3', 1, True) + ] + >>> list(distinct_permutations([1, 2, '3'])) # doctest: +SKIP + [ + (1, 2, '3'), + (1, '3', 2), + (2, 1, '3'), + (2, '3', 1), + ('3', 1, 2), + ('3', 2, 1) + ] + """ + + # Algorithm: https://w.wiki/Qai + def _full(A): + while True: + # Yield the permutation we have + yield tuple(A) + + # Find the largest index i such that A[i] < A[i + 1] + for i in range(size - 2, -1, -1): + if A[i] < A[i + 1]: + break + # If no such index exists, this permutation is the last one + else: + return + + # Find the largest index j greater than j such that A[i] < A[j] + for j in range(size - 1, i, -1): + if A[i] < A[j]: + break + + # Swap the value of A[i] with that of A[j], then reverse the + # sequence from A[i + 1] to form the new permutation + A[i], A[j] = A[j], A[i] + A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] + + # Algorithm: modified from the above + def _partial(A, r): + # Split A into the first r items and the last r items + head, tail = A[:r], A[r:] + right_head_indexes = range(r - 1, -1, -1) + left_tail_indexes = range(len(tail)) + + while True: + # Yield the permutation we have + yield tuple(head) + + # Starting from the right, find the first index of the head with + # value smaller than the maximum value of the tail - call it i. + pivot = tail[-1] + for i in right_head_indexes: + if head[i] < pivot: + break + pivot = head[i] + else: + return + + # Starting from the left, find the first value of the tail + # with a value greater than head[i] and swap. + for j in left_tail_indexes: + if tail[j] > head[i]: + head[i], tail[j] = tail[j], head[i] + break + # If we didn't find one, start from the right and find the first + # index of the head with a value greater than head[i] and swap. + else: + for j in right_head_indexes: + if head[j] > head[i]: + head[i], head[j] = head[j], head[i] + break + + # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] + tail += head[: i - r : -1] # head[i + 1:][::-1] + i += 1 + head[i:], tail[:] = tail[: r - i], tail[r - i :] + + items = list(iterable) + + try: + items.sort() + sortable = True + except TypeError: + sortable = False + + indices_dict = defaultdict(list) + + for item in items: + indices_dict[items.index(item)].append(item) + + indices = [items.index(item) for item in items] + indices.sort() + + equivalent_items = {k: cycle(v) for k, v in indices_dict.items()} + + def permuted_items(permuted_indices): + return tuple( + next(equivalent_items[index]) for index in permuted_indices + ) + + size = len(items) + if r is None: + r = size + + # functools.partial(_partial, ... ) + algorithm = _full if (r == size) else partial(_partial, r=r) + + if 0 < r <= size: + if sortable: + return algorithm(items) + else: + return ( + permuted_items(permuted_indices) + for permuted_indices in algorithm(indices) + ) + + return iter(() if r else ((),)) + + +def derangements(iterable, r=None): + """Yield successive derangements of the elements in *iterable*. + + A derangement is a permutation in which no element appears at its original + index. In other words, a derangement is a permutation that has no fixed points. + + Suppose Alice, Bob, Carol, and Dave are playing Secret Santa. + The code below outputs all of the different ways to assign gift recipients + such that nobody is assigned to himself or herself: + + >>> for d in derangements(['Alice', 'Bob', 'Carol', 'Dave']): + ... print(', '.join(d)) + Bob, Alice, Dave, Carol + Bob, Carol, Dave, Alice + Bob, Dave, Alice, Carol + Carol, Alice, Dave, Bob + Carol, Dave, Alice, Bob + Carol, Dave, Bob, Alice + Dave, Alice, Bob, Carol + Dave, Carol, Alice, Bob + Dave, Carol, Bob, Alice + + If *r* is given, only the *r*-length derangements are yielded. + + >>> sorted(derangements(range(3), 2)) + [(1, 0), (1, 2), (2, 0)] + >>> sorted(derangements([0, 2, 3], 2)) + [(2, 0), (2, 3), (3, 0)] + + Elements are treated as unique based on their position, not on their value. + + Consider the Secret Santa example with two *different* people who have + the *same* name. Then there are two valid gift assignments even though + it might appear that a person is assigned to themselves: + + >>> names = ['Alice', 'Bob', 'Bob'] + >>> list(derangements(names)) + [('Bob', 'Bob', 'Alice'), ('Bob', 'Alice', 'Bob')] + + To avoid confusion, make the inputs distinct: + + >>> deduped = [f'{name}{index}' for index, name in enumerate(names)] + >>> list(derangements(deduped)) + [('Bob1', 'Bob2', 'Alice0'), ('Bob2', 'Alice0', 'Bob1')] + + The number of derangements of a set of size *n* is known as the + "subfactorial of n". For n > 0, the subfactorial is: + ``round(math.factorial(n) / math.e)``. + + References: + + * Article: https://www.numberanalytics.com/blog/ultimate-guide-to-derangements-in-combinatorics + * Sizes: https://oeis.org/A000166 + """ + xs = tuple(iterable) + ys = tuple(range(len(xs))) + return compress( + permutations(xs, r=r), + map(all, map(map, repeat(is_not), repeat(ys), permutations(ys, r=r))), + ) + + +def intersperse(e, iterable, n=1): + """Intersperse filler element *e* among the items in *iterable*, leaving + *n* items between each filler element. + + >>> list(intersperse('!', [1, 2, 3, 4, 5])) + [1, '!', 2, '!', 3, '!', 4, '!', 5] + + >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) + [1, 2, None, 3, 4, None, 5] + + """ + if n == 0: + raise ValueError('n must be > 0') + elif n == 1: + # interleave(repeat(e), iterable) -> e, x_0, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, x_1, e, x_2... + return islice(interleave(repeat(e), iterable), 1, None) + else: + # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... + # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... + # flatten(...) -> x_0, x_1, e, x_2, x_3... + filler = repeat([e]) + chunks = chunked(iterable, n) + return flatten(islice(interleave(filler, chunks), 1, None)) + + +def unique_to_each(*iterables): + """Return the elements from each of the input iterables that aren't in the + other input iterables. + + For example, suppose you have a set of packages, each with a set of + dependencies:: + + {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} + + If you remove one package, which dependencies can also be removed? + + If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not + associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for + ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: + + >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) + [['A'], ['C'], ['D']] + + If there are duplicates in one input iterable that aren't in the others + they will be duplicated in the output. Input order is preserved:: + + >>> unique_to_each("mississippi", "missouri") + [['p', 'p'], ['o', 'u', 'r']] + + It is assumed that the elements of each iterable are hashable. + + """ + pool = [list(it) for it in iterables] + counts = Counter(chain.from_iterable(map(set, pool))) + uniques = {element for element in counts if counts[element] == 1} + return [list(filter(uniques.__contains__, it)) for it in pool] + + +def windowed(seq, n, fillvalue=None, step=1): + """Return a sliding window of width *n* over the given iterable. + + >>> all_windows = windowed([1, 2, 3, 4, 5], 3) + >>> list(all_windows) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + When the window is larger than the iterable, *fillvalue* is used in place + of missing values: + + >>> list(windowed([1, 2, 3], 4)) + [(1, 2, 3, None)] + + Each window will advance in increments of *step*: + + >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) + [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] + """ + if n < 0: + raise ValueError('n must be >= 0') + if n == 0: + yield () + return + if step < 1: + raise ValueError('step must be >= 1') + + iterator = iter(seq) + + # Generate first window + window = deque(islice(iterator, n), maxlen=n) + + # Deal with the first window not being full + if not window: + return + if len(window) < n: + yield tuple(window) + ((fillvalue,) * (n - len(window))) + return + yield tuple(window) + + # Create the filler for the next windows. The padding ensures + # we have just enough elements to fill the last window. + padding = (fillvalue,) * (n - 1 if step >= n else step - 1) + filler = map(window.append, chain(iterator, padding)) + + # Generate the rest of the windows + for _ in islice(filler, step - 1, None, step): + yield tuple(window) + + +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iterable: + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: + """Wrap *iterable* and return an object that buckets the iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + return iter(self._cache) + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) + + +def spy(iterable, n=1): + """Return a 2-tuple with a list containing the first *n* elements of + *iterable*, and an iterator with the same items as *iterable*. + This allows you to "look ahead" at the items in the iterable without + advancing it. + + There is one item in the list by default: + + >>> iterable = 'abcdefg' + >>> head, iterable = spy(iterable) + >>> head + ['a'] + >>> list(iterable) + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + You may use unpacking to retrieve items instead of lists: + + >>> (head,), iterable = spy('abcdefg') + >>> head + 'a' + >>> (first, second), iterable = spy('abcdefg', 2) + >>> first + 'a' + >>> second + 'b' + + The number of items requested can be larger than the number of items in + the iterable: + + >>> iterable = [1, 2, 3, 4, 5] + >>> head, iterable = spy(iterable, 10) + >>> head + [1, 2, 3, 4, 5] + >>> list(iterable) + [1, 2, 3, 4, 5] + + """ + p, q = tee(iterable) + return take(n, q), p + + +def interleave(*iterables): + """Return a new iterable yielding from each iterable in turn, + until the shortest is exhausted. + + >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7] + + For a version that doesn't terminate after the shortest iterable is + exhausted, see :func:`interleave_longest`. + + """ + return chain.from_iterable(zip(*iterables)) + + +def interleave_longest(*iterables): + """Return a new iterable yielding from each iterable in turn, + skipping any that are exhausted. + + >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7, 3, 8] + + This function produces the same output as :func:`roundrobin`, but may + perform better for some inputs (in particular when the number of iterables + is large). + + """ + for xs in zip_longest(*iterables, fillvalue=_marker): + for x in xs: + if x is not _marker: + yield x + + +def interleave_evenly(iterables, lengths=None): + """ + Interleave multiple iterables so that their elements are evenly distributed + throughout the output sequence. + + >>> iterables = [1, 2, 3, 4, 5], ['a', 'b'] + >>> list(interleave_evenly(iterables)) + [1, 2, 'a', 3, 4, 'b', 5] + + >>> iterables = [[1, 2, 3], [4, 5], [6, 7, 8]] + >>> list(interleave_evenly(iterables)) + [1, 6, 4, 2, 7, 3, 8, 5] + + This function requires iterables of known length. Iterables without + ``__len__()`` can be used by manually specifying lengths with *lengths*: + + >>> from itertools import combinations, repeat + >>> iterables = [combinations(range(4), 2), ['a', 'b', 'c']] + >>> lengths = [4 * (4 - 1) // 2, 3] + >>> list(interleave_evenly(iterables, lengths=lengths)) + [(0, 1), (0, 2), 'a', (0, 3), (1, 2), 'b', (1, 3), (2, 3), 'c'] + + Based on Bresenham's algorithm. + """ + if lengths is None: + try: + lengths = [len(it) for it in iterables] + except TypeError: + raise ValueError( + 'Iterable lengths could not be determined automatically. ' + 'Specify them with the lengths keyword.' + ) + elif len(iterables) != len(lengths): + raise ValueError('Mismatching number of iterables and lengths.') + + dims = len(lengths) + + # sort iterables by length, descending + lengths_permute = sorted( + range(dims), key=lambda i: lengths[i], reverse=True + ) + lengths_desc = [lengths[i] for i in lengths_permute] + iters_desc = [iter(iterables[i]) for i in lengths_permute] + + # the longest iterable is the primary one (Bresenham: the longest + # distance along an axis) + delta_primary, deltas_secondary = lengths_desc[0], lengths_desc[1:] + iter_primary, iters_secondary = iters_desc[0], iters_desc[1:] + errors = [delta_primary // dims] * len(deltas_secondary) + + to_yield = sum(lengths) + while to_yield: + yield next(iter_primary) + to_yield -= 1 + # update errors for each secondary iterable + errors = [e - delta for e, delta in zip(errors, deltas_secondary)] + + # those iterables for which the error is negative are yielded + # ("diagonal step" in Bresenham) + for i, e_ in enumerate(errors): + if e_ < 0: + yield next(iters_secondary[i]) + to_yield -= 1 + errors[i] += delta_primary + + +def interleave_randomly(*iterables): + """Repeatedly select one of the input *iterables* at random and yield the next + item from it. + + >>> iterables = [1, 2, 3], 'abc', (True, False, None) + >>> list(interleave_randomly(*iterables)) # doctest: +SKIP + ['a', 'b', 1, 'c', True, False, None, 2, 3] + + The relative order of the items in each input iterable will preserved. Note the + sequences of items with this property are not equally likely to be generated. + + """ + iterators = [iter(e) for e in iterables] + while iterators: + idx = randrange(len(iterators)) + try: + yield next(iterators[idx]) + except StopIteration: + # equivalent to `list.pop` but slightly faster + iterators[idx] = iterators[-1] + del iterators[-1] + + +def collapse(iterable, base_type=None, levels=None): + """Flatten an iterable with multiple levels of nesting (e.g., a list of + lists of tuples) into non-iterable types. + + >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] + >>> list(collapse(iterable)) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and + will not be collapsed. + + To avoid collapsing other types, specify *base_type*: + + >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] + >>> list(collapse(iterable, base_type=tuple)) + ['ab', ('cd', 'ef'), 'gh', 'ij'] + + Specify *levels* to stop flattening after a certain level: + + >>> iterable = [('a', ['b']), ('c', ['d'])] + >>> list(collapse(iterable)) # Fully flattened + ['a', 'b', 'c', 'd'] + >>> list(collapse(iterable, levels=1)) # Only one level flattened + ['a', ['b'], 'c', ['d']] + + """ + stack = deque() + # Add our first node group, treat the iterable as a single node + stack.appendleft((0, repeat(iterable, 1))) + + while stack: + node_group = stack.popleft() + level, nodes = node_group + + # Check if beyond max level + if levels is not None and level > levels: + yield from nodes + continue + + for node in nodes: + # Check if done iterating + if isinstance(node, (str, bytes)) or ( + (base_type is not None) and isinstance(node, base_type) + ): + yield node + # Otherwise try to create child nodes + else: + try: + tree = iter(node) + except TypeError: + yield node + else: + # Save our current location + stack.appendleft(node_group) + # Append the new child node + stack.appendleft((level + 1, tree)) + # Break to process child node + break + + +def side_effect(func, iterable, chunk_size=None, before=None, after=None): + """Invoke *func* on each item in *iterable* (or on each *chunk_size* group + of items) before yielding the item. + + `func` must be a function that takes a single argument. Its return value + will be discarded. + + *before* and *after* are optional functions that take no arguments. They + will be executed before iteration starts and after it ends, respectively. + + `side_effect` can be used for logging, updating progress bars, or anything + that is not functionally "pure." + + Emitting a status message: + + >>> from more_itertools import consume + >>> func = lambda item: print('Received {}'.format(item)) + >>> consume(side_effect(func, range(2))) + Received 0 + Received 1 + + Operating on chunks of items: + + >>> pair_sums = [] + >>> func = lambda chunk: pair_sums.append(sum(chunk)) + >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) + [0, 1, 2, 3, 4, 5] + >>> list(pair_sums) + [1, 5, 9] + + Writing to a file-like object: + + >>> from io import StringIO + >>> from more_itertools import consume + >>> f = StringIO() + >>> func = lambda x: print(x, file=f) + >>> before = lambda: print(u'HEADER', file=f) + >>> after = f.close + >>> it = [u'a', u'b', u'c'] + >>> consume(side_effect(func, it, before=before, after=after)) + >>> f.closed + True + + """ + try: + if before is not None: + before() + + if chunk_size is None: + for item in iterable: + func(item) + yield item + else: + for chunk in chunked(iterable, chunk_size): + func(chunk) + yield from chunk + finally: + if after is not None: + after() + + +def sliced(seq, n, strict=False): + """Yield slices of length *n* from the sequence *seq*. + + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] + + By the default, the last yielded slice will have fewer than *n* elements + if the length of *seq* is not divisible by *n*: + + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + If the length of *seq* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + slice is yielded. + + This function will only work for iterables that support slicing. + For non-sliceable iterables, see :func:`chunked`. + + """ + iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) + if strict: + + def ret(): + for _slice in iterator: + if len(_slice) != n: + raise ValueError("seq is not divisible by n.") + yield _slice + + return ret() + else: + return iterator + + +def split_at(iterable, pred, maxsplit=-1, keep_separator=False): + """Yield lists of items from *iterable*, where each list is delimited by + an item where callable *pred* returns ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b')) + [['a'], ['c', 'd', 'c'], ['a']] + + >>> list(split_at(range(10), lambda n: n % 2 == 1)) + [[0], [2], [4], [6], [8], []] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) + [[0], [2], [4, 5, 6, 7, 8, 9]] + + By default, the delimiting items are not included in the output. + To include them, set *keep_separator* to ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) + [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item): + yield buf + if keep_separator: + yield [item] + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + else: + buf.append(item) + yield buf + + +def split_before(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: + + >>> list(split_before('OneTwo', lambda s: s.isupper())) + [['O', 'n', 'e'], ['T', 'w', 'o']] + + >>> list(split_before(range(10), lambda n: n % 3 == 0)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield [item, *it] + return + buf = [] + maxsplit -= 1 + buf.append(item) + if buf: + yield buf + + +def split_after(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends with an + item where callable *pred* returns ``True``: + + >>> list(split_after('one1two2', lambda s: s.isdigit())) + [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] + + >>> list(split_after(range(10), lambda n: n % 3 == 0)) + [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + buf.append(item) + if pred(item) and buf: + yield buf + if maxsplit == 1: + buf = list(it) + if buf: + yield buf + return + buf = [] + maxsplit -= 1 + if buf: + yield buf + + +def split_when(iterable, pred, maxsplit=-1): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], + ... lambda x, y: x > y, maxsplit=2)) + [[1, 2, 3, 3], [2, 5], [2, 4, 2]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + if maxsplit == 1: + yield [next_item, *it] + return + buf = [] + maxsplit -= 1 + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns the *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that + :func:`itertools.slice` does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + +def padded(iterable, fillvalue=None, n=None, next_multiple=False): + """Yield the elements from *iterable*, followed by *fillvalue*, such that + at least *n* items are emitted. + + >>> list(padded([1, 2, 3], '?', 5)) + [1, 2, 3, '?', '?'] + + If *next_multiple* is ``True``, *fillvalue* will be emitted until the + number of items emitted is a multiple of *n*: + + >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) + [1, 2, 3, 4, None, None] + + If *n* is ``None``, *fillvalue* will be emitted indefinitely. + + To create an *iterable* of exactly size *n*, you can truncate with + :func:`islice`. + + >>> list(islice(padded([1, 2, 3], '?'), 5)) + [1, 2, 3, '?', '?'] + >>> list(islice(padded([1, 2, 3, 4, 5, 6, 7, 8], '?'), 5)) + [1, 2, 3, 4, 5] + + """ + iterator = iter(iterable) + iterator_with_repeat = chain(iterator, repeat(fillvalue)) + + if n is None: + return iterator_with_repeat + elif n < 1: + raise ValueError('n must be at least 1') + elif next_multiple: + + def slice_generator(): + for first in iterator: + yield (first,) + yield islice(iterator_with_repeat, n - 1) + + # While elements exist produce slices of size n + return chain.from_iterable(slice_generator()) + else: + # Ensure the first batch is at least size n then iterate + return chain(islice(iterator_with_repeat, n), iterator) + + +def repeat_each(iterable, n=2): + """Repeat each element in *iterable* *n* times. + + >>> list(repeat_each('ABC', 3)) + ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'] + """ + return chain.from_iterable(map(repeat, iterable, repeat(n))) + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + +def distribute(n, iterable): + """Distribute the items from *iterable* among *n* smaller iterables. + + >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 3, 5] + >>> list(group_2) + [2, 4, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 4, 7], [2, 5], [3, 6]] + + If the length of *iterable* is smaller than *n*, then the last returned + iterables will be empty: + + >>> children = distribute(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function uses :func:`itertools.tee` and may require significant + storage. + + If you need the order items in the smaller iterables to match the + original iterable, see :func:`divide`. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + children = tee(iterable, n) + return [islice(it, index, None, n) for index, it in enumerate(children)] + + +def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): + """Yield tuples whose elements are offset from *iterable*. + The amount by which the `i`-th item in each tuple is offset is given by + the `i`-th item in *offsets*. + + >>> list(stagger([0, 1, 2, 3])) + [(None, 0, 1), (0, 1, 2), (1, 2, 3)] + >>> list(stagger(range(8), offsets=(0, 2, 4))) + [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] + + By default, the sequence will end when the final element of a tuple is the + last item in the iterable. To continue until the first element of a tuple + is the last item in the iterable, set *longest* to ``True``:: + + >>> list(stagger([0, 1, 2, 3], longest=True)) + [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + children = tee(iterable, len(offsets)) + + return zip_offset( + *children, offsets=offsets, longest=longest, fillvalue=fillvalue + ) + + +def zip_equal(*iterables): + """``zip`` the input *iterables* together but raise + ``UnequalIterablesError`` if they aren't all the same length. + + >>> it_1 = range(3) + >>> it_2 = iter('abc') + >>> list(zip_equal(it_1, it_2)) + [(0, 'a'), (1, 'b'), (2, 'c')] + + >>> it_1 = range(3) + >>> it_2 = iter('abcd') + >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + more_itertools.more.UnequalIterablesError: Iterables have different + lengths + + """ + if hexversion >= 0x30A00A6: + warnings.warn( + ( + 'zip_equal will be removed in a future version of ' + 'more-itertools. Use the builtin zip function with ' + 'strict=True instead.' + ), + DeprecationWarning, + ) + + return _zip_equal(*iterables) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): + """``zip`` the input *iterables* together, but offset the `i`-th iterable + by the `i`-th item in *offsets*. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] + + This can be used as a lightweight alternative to SciPy or pandas to analyze + data sets in which some series have a lead or lag relationship. + + By default, the sequence will end when the shortest iterable is exhausted. + To continue until the longest iterable is exhausted, set *longest* to + ``True``. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + if len(iterables) != len(offsets): + raise ValueError("Number of iterables and offsets didn't match") + + staggered = [] + for it, n in zip(iterables, offsets): + if n < 0: + staggered.append(chain(repeat(fillvalue, -n), it)) + elif n > 0: + staggered.append(islice(it, n, None)) + else: + staggered.append(it) + + if longest: + return zip_longest(*staggered, fillvalue=fillvalue) + + return zip(*staggered) + + +def sort_together( + iterables, key_list=(0,), key=None, reverse=False, strict=False +): + """Return the input iterables sorted together, with *key_list* as the + priority for sorting. All iterables are trimmed to the length of the + shortest one. + + This can be used like the sorting function in a spreadsheet. If each + iterable represents a column of data, the key list determines which + columns are used for sorting. + + By default, all iterables are sorted using the ``0``-th iterable:: + + >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] + >>> sort_together(iterables) + [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] + + Set a different key list to sort according to another iterable. + Specifying multiple keys dictates how ties are broken:: + + >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] + >>> sort_together(iterables, key_list=(1, 2)) + [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + + To sort by a function of the elements of the iterable, pass a *key* + function. Its arguments are the elements of the iterables corresponding to + the key list:: + + >>> names = ('a', 'b', 'c') + >>> lengths = (1, 2, 3) + >>> widths = (5, 2, 1) + >>> def area(length, width): + ... return length * width + >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) + [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] + + Set *reverse* to ``True`` to sort in descending order. + + >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) + [(3, 2, 1), ('a', 'b', 'c')] + + If the *strict* keyword argument is ``True``, then + ``UnequalIterablesError`` will be raised if any of the iterables have + different lengths. + + """ + if key is None: + # if there is no key function, the key argument to sorted is an + # itemgetter + key_argument = itemgetter(*key_list) + else: + # if there is a key function, call it with the items at the offsets + # specified by the key function as arguments + key_list = list(key_list) + if len(key_list) == 1: + # if key_list contains a single item, pass the item at that offset + # as the only argument to the key function + key_offset = key_list[0] + key_argument = lambda zipped_items: key(zipped_items[key_offset]) + else: + # if key_list contains multiple items, use itemgetter to return a + # tuple of items, which we pass as *args to the key function + get_key_items = itemgetter(*key_list) + key_argument = lambda zipped_items: key( + *get_key_items(zipped_items) + ) + + zipper = zip_equal if strict else zip + return list( + zipper(*sorted(zipper(*iterables), key=key_argument, reverse=reverse)) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iterable) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + # If we have an iterable like iter([(1, 2, 3), (4, 5), (6,)]), + # the second unzipped iterable fails at the third tuple since + # it tries to access (6,)[1]. + # Same with the third unzipped iterable and the second tuple. + # To support these "improperly zipped" iterables, we suppress + # the IndexError, which just stops the unzipped iterables at + # first length mismatch. + return tuple( + iter_suppress(map(itemgetter(i), it), IndexError) + for i, it in enumerate(iterables) + ) + + +def divide(n, iterable): + """Divide the elements from *iterable* into *n* parts, maintaining + order. + + >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 2, 3] + >>> list(group_2) + [4, 5, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 2, 3], [4, 5], [6, 7]] + + If the length of the iterable is smaller than n, then the last returned + iterables will be empty: + + >>> children = divide(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function will exhaust the iterable before returning. + If order is not important, see :func:`distribute`, which does not first + pull the iterable into memory. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + + q, r = divmod(len(seq), n) + + ret = [] + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q + ret.append(iter(seq[start:stop])) + + return ret + + +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +def adjacent(predicate, iterable, distance=1): + """Return an iterable over `(bool, item)` tuples where the `item` is + drawn from *iterable* and the `bool` indicates whether + that item satisfies the *predicate* or is adjacent to an item that does. + + For example, to find whether items are adjacent to a ``3``:: + + >>> list(adjacent(lambda x: x == 3, range(6))) + [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] + + Set *distance* to change what counts as adjacent. For example, to find + whether items are two places away from a ``3``: + + >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) + [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] + + This is useful for contextualizing the results of a search function. + For example, a code comparison tool might want to identify lines that + have changed, but also surrounding lines to give the viewer of the diff + context. + + The predicate function will only be called once for each item in the + iterable. + + See also :func:`groupby_transform`, which can be used with this function + to group ranges of items with the same `bool` value. + + """ + # Allow distance=0 mainly for testing that it reproduces results with map() + if distance < 0: + raise ValueError('distance must be at least 0') + + i1, i2 = tee(iterable) + padding = [False] * distance + selected = chain(padding, map(predicate, i1), padding) + adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) + return zip(adjacent_to_selected, i2) + + +def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): + """An extension of :func:`itertools.groupby` that can apply transformations + to the grouped data. + + * *keyfunc* is a function computing a key value for each item in *iterable* + * *valuefunc* is a function that transforms the individual items from + *iterable* after grouping + * *reducefunc* is a function that transforms each group of items + + >>> iterable = 'aAAbBBcCC' + >>> keyfunc = lambda k: k.upper() + >>> valuefunc = lambda v: v.lower() + >>> reducefunc = lambda g: ''.join(g) + >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) + [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] + + Each optional argument defaults to an identity function if not specified. + + :func:`groupby_transform` is useful when grouping elements of an iterable + using a separate iterable as the key. To do this, :func:`zip` the iterables + and pass a *keyfunc* that extracts the first element and a *valuefunc* + that extracts the second element:: + + >>> from operator import itemgetter + >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] + >>> values = 'abcdefghi' + >>> iterable = zip(keys, values) + >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) + >>> [(k, ''.join(g)) for k, g in grouper] + [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] + + Note that the order of items in the iterable is significant. + Only adjacent items are grouped together, so if you don't want any + duplicate groups, you should sort the iterable by the key function. + + """ + ret = groupby(iterable, keyfunc) + if valuefunc: + ret = ((k, map(valuefunc, g)) for k, g in ret) + if reducefunc: + ret = ((k, reducefunc(g)) for k, g in ret) + + return ret + + +class numeric_range(abc.Sequence, abc.Hashable): + """An extension of the built-in ``range()`` function whose arguments can + be any orderable numeric type. + + With only *stop* specified, *start* defaults to ``0`` and *step* + defaults to ``1``. The output items will match the type of *stop*: + + >>> list(numeric_range(3.5)) + [0.0, 1.0, 2.0, 3.0] + + With only *start* and *stop* specified, *step* defaults to ``1``. The + output items will match the type of *start*: + + >>> from decimal import Decimal + >>> start = Decimal('2.1') + >>> stop = Decimal('5.1') + >>> list(numeric_range(start, stop)) + [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] + + With *start*, *stop*, and *step* specified the output items will match + the type of ``start + step``: + + >>> from fractions import Fraction + >>> start = Fraction(1, 2) # Start at 1/2 + >>> stop = Fraction(5, 2) # End at 5/2 + >>> step = Fraction(1, 2) # Count by 1/2 + >>> list(numeric_range(start, stop, step)) + [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] + + If *step* is zero, ``ValueError`` is raised. Negative steps are supported: + + >>> list(numeric_range(3, -1, -1.0)) + [3.0, 2.0, 1.0, 0.0] + + Be aware of the limitations of floating-point numbers; the representation + of the yielded numbers may be surprising. + + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: + + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = iter(numeric_range(start, stop, step)) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + + _EMPTY_HASH = hash(range(0, 0)) + + def __init__(self, *args): + argc = len(args) + if argc == 1: + (self._stop,) = args + self._start = type(self._stop)(0) + self._step = type(self._stop - self._start)(1) + elif argc == 2: + self._start, self._stop = args + self._step = type(self._stop - self._start)(1) + elif argc == 3: + self._start, self._stop, self._step = args + elif argc == 0: + raise TypeError( + f'numeric_range expected at least 1 argument, got {argc}' + ) + else: + raise TypeError( + f'numeric_range expected at most 3 arguments, got {argc}' + ) + + self._zero = type(self._step)(0) + if self._step == self._zero: + raise ValueError('numeric_range() arg 3 must not be zero') + self._growing = self._step > self._zero + + def __bool__(self): + if self._growing: + return self._start < self._stop + else: + return self._start > self._stop + + def __contains__(self, elem): + if self._growing: + if self._start <= elem < self._stop: + return (elem - self._start) % self._step == self._zero + else: + if self._start >= elem > self._stop: + return (self._start - elem) % (-self._step) == self._zero + + return False + + def __eq__(self, other): + if isinstance(other, numeric_range): + empty_self = not bool(self) + empty_other = not bool(other) + if empty_self or empty_other: + return empty_self and empty_other # True if both empty + else: + return ( + self._start == other._start + and self._step == other._step + and self._get_by_index(-1) == other._get_by_index(-1) + ) + else: + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self._get_by_index(key) + elif isinstance(key, slice): + step = self._step if key.step is None else key.step * self._step + + if key.start is None or key.start <= -self._len: + start = self._start + elif key.start >= self._len: + start = self._stop + else: # -self._len < key.start < self._len + start = self._get_by_index(key.start) + + if key.stop is None or key.stop >= self._len: + stop = self._stop + elif key.stop <= -self._len: + stop = self._start + else: # -self._len < key.stop < self._len + stop = self._get_by_index(key.stop) + + return numeric_range(start, stop, step) + else: + raise TypeError( + 'numeric range indices must be ' + f'integers or slices, not {type(key).__name__}' + ) + + def __hash__(self): + if self: + return hash((self._start, self._get_by_index(-1), self._step)) + else: + return self._EMPTY_HASH + + def __iter__(self): + values = (self._start + (n * self._step) for n in count()) + if self._growing: + return takewhile(partial(gt, self._stop), values) + else: + return takewhile(partial(lt, self._stop), values) + + def __len__(self): + return self._len + + @cached_property + def _len(self): + if self._growing: + start = self._start + stop = self._stop + step = self._step + else: + start = self._stop + stop = self._start + step = -self._step + distance = stop - start + if distance <= self._zero: + return 0 + else: # distance > 0 and step > 0: regular euclidean division + q, r = divmod(distance, step) + return int(q) + int(r != self._zero) + + def __reduce__(self): + return numeric_range, (self._start, self._stop, self._step) + + def __repr__(self): + if self._step == 1: + return f"numeric_range({self._start!r}, {self._stop!r})" + return ( + f"numeric_range({self._start!r}, {self._stop!r}, {self._step!r})" + ) + + def __reversed__(self): + return iter( + numeric_range( + self._get_by_index(-1), self._start - self._step, -self._step + ) + ) + + def count(self, value): + return int(value in self) + + def index(self, value): + if self._growing: + if self._start <= value < self._stop: + q, r = divmod(value - self._start, self._step) + if r == self._zero: + return int(q) + else: + if self._start >= value > self._stop: + q, r = divmod(self._start - value, -self._step) + if r == self._zero: + return int(q) + + raise ValueError(f"{value} is not in numeric range") + + def _get_by_index(self, i): + if i < 0: + i += self._len + if i < 0 or i >= self._len: + raise IndexError("numeric range object index out of range") + return self._start + i * self._step + + +def count_cycle(iterable, n=None): + """Cycle through the items from *iterable* up to *n* times, yielding + the number of completed cycles along with each item. If *n* is omitted the + process repeats indefinitely. + + >>> list(count_cycle('AB', 3)) + [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] + + """ + seq = tuple(iterable) + if not seq: + return iter(()) + counter = count() if n is None else range(n) + return zip(repeat_each(counter, len(seq)), cycle(seq)) + + +def mark_ends(iterable): + """Yield 3-tuples of the form ``(is_first, is_last, item)``. + + >>> list(mark_ends('ABC')) + [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] + + Use this when looping over an iterable to take special action on its first + and/or last items: + + >>> iterable = ['Header', 100, 200, 'Footer'] + >>> total = 0 + >>> for is_first, is_last, item in mark_ends(iterable): + ... if is_first: + ... continue # Skip the header + ... if is_last: + ... continue # Skip the footer + ... total += item + >>> print(total) + 300 + """ + it = iter(iterable) + for a in it: + first = True + for b in it: + yield first, False, a + a = b + first = False + yield first, True, a + + +def locate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(locate([0, 1, 1, 0, 1, 0, 0])) + [1, 2, 4] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item. + + >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) + [1, 3] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(locate(iterable, pred=pred, window_size=3)) + [1, 5, 9] + + Use with :func:`seekable` to find indexes and then retrieve the associated + items: + + >>> from itertools import count + >>> from more_itertools import seekable + >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) + >>> it = seekable(source) + >>> pred = lambda x: x > 100 + >>> indexes = locate(it, pred=pred) + >>> i = next(indexes) + >>> it.seek(i) + >>> next(it) + 106 + + """ + if window_size is None: + return compress(count(), map(pred, iterable)) + + if window_size < 1: + raise ValueError('window size must be at least 1') + + it = windowed(iterable, window_size, fillvalue=_marker) + return compress(count(), starmap(pred, it)) + + +def longest_common_prefix(iterables): + """Yield elements of the longest common prefix among given *iterables*. + + >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf'])) + 'ab' + + """ + return (c[0] for c in takewhile(all_equal, zip(*iterables))) + + +def lstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the beginning + for which *pred* returns ``True``. + + For example, to remove a set of items from the start of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(lstrip(iterable, pred)) + [1, 2, None, 3, False, None] + + This function is analogous to to :func:`str.lstrip`, and is essentially + an wrapper for :func:`itertools.dropwhile`. + + """ + return dropwhile(pred, iterable) + + +def rstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the end + for which *pred* returns ``True``. + + For example, to remove a set of items from the end of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(rstrip(iterable, pred)) + [None, False, None, 1, 2, None, 3] + + This function is analogous to :func:`str.rstrip`. + + """ + cache = [] + cache_append = cache.append + cache_clear = cache.clear + for x in iterable: + if pred(x): + cache_append(x) + else: + yield from cache + cache_clear() + yield x + + +def strip(iterable, pred): + """Yield the items from *iterable*, but strip any from the + beginning and end for which *pred* returns ``True``. + + For example, to remove a set of items from both ends of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(strip(iterable, pred)) + [1, 2, None, 3] + + This function is analogous to :func:`str.strip`. + + """ + return rstrip(lstrip(iterable, pred), pred) + + +class islice_extended: + """An extension of :func:`itertools.islice` that supports negative values + for *stop*, *start*, and *step*. + + >>> iterator = iter('abcdefgh') + >>> list(islice_extended(iterator, -4, -1)) + ['e', 'f', 'g'] + + Slices with negative values require some caching of *iterable*, but this + function takes care to minimize the amount of memory required. + + For example, you can use a negative step with an infinite iterator: + + >>> from itertools import count + >>> list(islice_extended(count(), 110, 99, -2)) + [110, 108, 106, 104, 102, 100] + + You can also use slice notation directly: + + >>> iterator = map(str, count()) + >>> it = islice_extended(iterator)[10:20:2] + >>> list(it) + ['10', '12', '14', '16', '18'] + + """ + + def __init__(self, iterable, *args): + it = iter(iterable) + if args: + self._iterator = _islice_helper(it, slice(*args)) + else: + self._iterator = it + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + def __getitem__(self, key): + if isinstance(key, slice): + return islice_extended(_islice_helper(self._iterator, key)) + + raise TypeError('islice_extended.__getitem__ argument must be a slice') + + +def _islice_helper(it, s): + start = s.start + stop = s.stop + if s.step == 0: + raise ValueError('step argument must be a non-zero integer or None.') + step = s.step or 1 + + if step > 0: + start = 0 if (start is None) else start + + if start < 0: + # Consume all but the last -start items + cache = deque(enumerate(it, 1), maxlen=-start) + len_iter = cache[-1][0] if cache else 0 + + # Adjust start to be positive + i = max(len_iter + start, 0) + + # Adjust stop to be positive + if stop is None: + j = len_iter + elif stop >= 0: + j = min(stop, len_iter) + else: + j = max(len_iter + stop, 0) + + # Slice the cache + n = j - i + if n <= 0: + return + + for index in range(n): + if index % step == 0: + # pop and yield the item. + # We don't want to use an intermediate variable + # it would extend the lifetime of the current item + yield cache.popleft()[1] + else: + # just pop and discard the item + cache.popleft() + elif (stop is not None) and (stop < 0): + # Advance to the start position + next(islice(it, start, start), None) + + # When stop is negative, we have to carry -stop items while + # iterating + cache = deque(islice(it, -stop), maxlen=-stop) + + for index, item in enumerate(it): + if index % step == 0: + # pop and yield the item. + # We don't want to use an intermediate variable + # it would extend the lifetime of the current item + yield cache.popleft() + else: + # just pop and discard the item + cache.popleft() + cache.append(item) + else: + # When both start and stop are positive we have the normal case + yield from islice(it, start, stop, step) + else: + start = -1 if (start is None) else start + + if (stop is not None) and (stop < 0): + # Consume all but the last items + n = -stop - 1 + cache = deque(enumerate(it, 1), maxlen=n) + len_iter = cache[-1][0] if cache else 0 + + # If start and stop are both negative they are comparable and + # we can just slice. Otherwise we can adjust start to be negative + # and then slice. + if start < 0: + i, j = start, stop + else: + i, j = min(start - len_iter, -1), None + + for index, item in list(cache)[i:j:step]: + yield item + else: + # Advance to the stop position + if stop is not None: + m = stop + 1 + next(islice(it, m, m), None) + + # stop is positive, so if start is negative they are not comparable + # and we need the rest of the items. + if start < 0: + i = start + n = None + # stop is None and start is positive, so we just need items up to + # the start index. + elif stop is None: + i = None + n = start + 1 + # Both stop and start are positive, so they are comparable. + else: + i = None + n = start - stop + if n <= 0: + return + + cache = list(islice(it, n)) + + yield from cache[i::step] + + +def always_reversible(iterable): + """An extension of :func:`reversed` that supports all iterables, not + just those which implement the ``Reversible`` or ``Sequence`` protocols. + + >>> print(*always_reversible(x for x in range(3))) + 2 1 0 + + If the iterable is already reversible, this function returns the + result of :func:`reversed()`. If the iterable is not reversible, + this function will cache the remaining items in the iterable and + yield them in reverse order, which may require significant storage. + """ + try: + return reversed(iterable) + except TypeError: + return reversed(list(iterable)) + + +def consecutive_groups(iterable, ordering=None): + """Yield groups of consecutive items using :func:`itertools.groupby`. + The *ordering* function determines whether two items are adjacent by + returning their position. + + By default, the ordering function is the identity function. This is + suitable for finding runs of numbers: + + >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] + >>> for group in consecutive_groups(iterable): + ... print(list(group)) + [1] + [10, 11, 12] + [20] + [30, 31, 32, 33] + [40] + + To find runs of adjacent letters, apply :func:`ord` function + to convert letters to ordinals. + + >>> iterable = 'abcdfgilmnop' + >>> ordering = ord + >>> for group in consecutive_groups(iterable, ordering): + ... print(list(group)) + ['a', 'b', 'c', 'd'] + ['f', 'g'] + ['i'] + ['l', 'm', 'n', 'o', 'p'] + + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + + """ + if ordering is None: + key = lambda x: x[0] - x[1] + else: + key = lambda x: x[0] - ordering(x[1]) + + for k, g in groupby(enumerate(iterable), key=key): + yield map(itemgetter(1), g) + + +def difference(iterable, func=sub, *, initial=None): + """This function is the inverse of :func:`itertools.accumulate`. By default + it will compute the first difference of *iterable* using + :func:`operator.sub`: + + >>> from itertools import accumulate + >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + *func* defaults to :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + If the *initial* keyword is set, the first element will be skipped when + computing successive differences. + + >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) + >>> list(difference(it, initial=10)) + [1, 2, 3] + + """ + a, b = tee(iterable) + try: + first = [next(b)] + except StopIteration: + return iter([]) + + if initial is not None: + first = [] + + return chain(first, map(func, b, a)) + + +class SequenceView(Sequence): + """Return a read-only view of the sequence object *target*. + + :class:`SequenceView` objects are analogous to Python's built-in + "dictionary view" types. They provide a dynamic view of a sequence's items, + meaning that when the sequence updates, so does the view. + + >>> seq = ['0', '1', '2'] + >>> view = SequenceView(seq) + >>> view + SequenceView(['0', '1', '2']) + >>> seq.append('3') + >>> view + SequenceView(['0', '1', '2', '3']) + + Sequence views support indexing, slicing, and length queries. They act + like the underlying sequence, except they don't allow assignment: + + >>> view[1] + '1' + >>> view[1:-1] + ['1', '2'] + >>> len(view) + 4 + + Sequence views are useful as an alternative to copying, as they don't + require (much) extra storage. + + """ + + def __init__(self, target): + if not isinstance(target, Sequence): + raise TypeError + self._target = target + + def __getitem__(self, index): + return self._target[index] + + def __len__(self): + return len(self._target) + + def __repr__(self): + return f'{self.__class__.__name__}({self._target!r})' + + +class seekable: + """Wrap an iterator to allow for seeking backward and forward. This + progressively caches the items in the source iterable so they can be + re-visited. + + Call :meth:`seek` with an index to seek to that position in the source + iterable. + + To "reset" an iterator, seek to ``0``: + + >>> from itertools import count + >>> it = seekable((str(n) for n in count())) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.seek(0) + >>> next(it), next(it), next(it) + ('0', '1', '2') + + You can also seek forward: + + >>> it = seekable((str(n) for n in range(20))) + >>> it.seek(10) + >>> next(it) + '10' + >>> it.seek(20) # Seeking past the end of the source isn't a problem + >>> list(it) + [] + >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it) + '0' + + Call :meth:`relative_seek` to seek relative to the source iterator's + current position. + + >>> it = seekable((str(n) for n in range(20))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.relative_seek(2) + >>> next(it) + '5' + >>> it.relative_seek(-3) # Source is at '6', we move back to '3' + >>> next(it) + '3' + >>> it.relative_seek(-3) # Source is at '4', we move back to '1' + >>> next(it) + '1' + + + Call :meth:`peek` to look ahead one item without advancing the iterator: + + >>> it = seekable('1234') + >>> it.peek() + '1' + >>> list(it) + ['1', '2', '3', '4'] + >>> it.peek(default='empty') + 'empty' + + Before the iterator is at its end, calling :func:`bool` on it will return + ``True``. After it will return ``False``: + + >>> it = seekable('5678') + >>> bool(it) + True + >>> list(it) + ['5', '6', '7', '8'] + >>> bool(it) + False + + You may view the contents of the cache with the :meth:`elements` method. + That returns a :class:`SequenceView`, a view that updates automatically: + + >>> it = seekable((str(n) for n in range(10))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> elements = it.elements() + >>> elements + SequenceView(['0', '1', '2']) + >>> next(it) + '3' + >>> elements + SequenceView(['0', '1', '2', '3']) + + By default, the cache grows as the source iterable progresses, so beware of + wrapping very large or infinite iterables. Supply *maxlen* to limit the + size of the cache (this of course limits how far back you can seek). + + >>> from itertools import count + >>> it = seekable((str(n) for n in count()), maxlen=2) + >>> next(it), next(it), next(it), next(it) + ('0', '1', '2', '3') + >>> list(it.elements()) + ['2', '3'] + >>> it.seek(0) + >>> next(it), next(it), next(it), next(it) + ('2', '3', '4', '5') + >>> next(it) + '6' + + """ + + def __init__(self, iterable, maxlen=None): + self._source = iter(iterable) + if maxlen is None: + self._cache = [] + else: + self._cache = deque([], maxlen) + self._index = None + + def __iter__(self): + return self + + def __next__(self): + if self._index is not None: + try: + item = self._cache[self._index] + except IndexError: + self._index = None + else: + self._index += 1 + return item + + item = next(self._source) + self._cache.append(item) + return item + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + try: + peeked = next(self) + except StopIteration: + if default is _marker: + raise + return default + if self._index is None: + self._index = len(self._cache) + self._index -= 1 + return peeked + + def elements(self): + return SequenceView(self._cache) + + def seek(self, index): + self._index = index + remainder = index - len(self._cache) + if remainder > 0: + consume(self, remainder) + + def relative_seek(self, count): + if self._index is None: + self._index = len(self._cache) + + self.seek(max(self._index + count, 0)) + + +class run_length: + """ + :func:`run_length.encode` compresses an iterable with run-length encoding. + It yields groups of repeated items with the count of how many times they + were repeated: + + >>> uncompressed = 'abbcccdddd' + >>> list(run_length.encode(uncompressed)) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + + :func:`run_length.decode` decompresses an iterable that was previously + compressed with run-length encoding. It yields the items of the + decompressed iterable: + + >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> list(run_length.decode(compressed)) + ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] + + """ + + @staticmethod + def encode(iterable): + return ((k, ilen(g)) for k, g in groupby(iterable)) + + @staticmethod + def decode(iterable): + return chain.from_iterable(starmap(repeat, iterable)) + + +def exactly_n(iterable, n, predicate=bool): + """Return ``True`` if exactly ``n`` items in the iterable are ``True`` + according to the *predicate* function. + + >>> exactly_n([True, True, False], 2) + True + >>> exactly_n([True, True, False], 1) + False + >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) + True + + The iterable will be advanced until ``n + 1`` truthy items are encountered, + so avoid calling it on infinite iterables. + + """ + return ilen(islice(filter(predicate, iterable), n + 1)) == n + + +def circular_shifts(iterable, steps=1): + """Yield the circular shifts of *iterable*. + + >>> list(circular_shifts(range(4))) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + + Set *steps* to the number of places to rotate to the left + (or to the right if negative). Defaults to 1. + + >>> list(circular_shifts(range(4), 2)) + [(0, 1, 2, 3), (2, 3, 0, 1)] + + >>> list(circular_shifts(range(4), -1)) + [(0, 1, 2, 3), (3, 0, 1, 2), (2, 3, 0, 1), (1, 2, 3, 0)] + + """ + buffer = deque(iterable) + if steps == 0: + raise ValueError('Steps should be a non-zero integer') + + buffer.rotate(steps) + steps = -steps + n = len(buffer) + n //= math.gcd(n, steps) + + for _ in repeat(None, n): + buffer.rotate(steps) + yield tuple(buffer) + + +def make_decorator(wrapping_func, result_index=0): + """Return a decorator version of *wrapping_func*, which is a function that + modifies an iterable. *result_index* is the position in that function's + signature where the iterable goes. + + This lets you use itertools on the "production end," i.e. at function + definition. This can augment what the function returns without changing the + function's code. + + For example, to produce a decorator version of :func:`chunked`: + + >>> from more_itertools import chunked + >>> chunker = make_decorator(chunked, result_index=0) + >>> @chunker(3) + ... def iter_range(n): + ... return iter(range(n)) + ... + >>> list(iter_range(9)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + To only allow truthy items to be returned: + + >>> truth_serum = make_decorator(filter, result_index=1) + >>> @truth_serum(bool) + ... def boolean_test(): + ... return [0, 1, '', ' ', False, True] + ... + >>> list(boolean_test()) + [1, ' ', True] + + The :func:`peekable` and :func:`seekable` wrappers make for practical + decorators: + + >>> from more_itertools import peekable + >>> peekable_function = make_decorator(peekable) + >>> @peekable_function() + ... def str_range(*args): + ... return (str(x) for x in range(*args)) + ... + >>> it = str_range(1, 20, 2) + >>> next(it), next(it), next(it) + ('1', '3', '5') + >>> it.peek() + '7' + >>> next(it) + '7' + + """ + + # See https://sites.google.com/site/bbayles/index/decorator_factory for + # notes on how this works. + def decorator(*wrapping_args, **wrapping_kwargs): + def outer_wrapper(f): + def inner_wrapper(*args, **kwargs): + result = f(*args, **kwargs) + wrapping_args_ = list(wrapping_args) + wrapping_args_.insert(result_index, result) + return wrapping_func(*wrapping_args_, **wrapping_kwargs) + + return inner_wrapper + + return outer_wrapper + + return decorator + + +def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): + """Return a dictionary that maps the items in *iterable* to categories + defined by *keyfunc*, transforms them with *valuefunc*, and + then summarizes them by category with *reducefunc*. + + *valuefunc* defaults to the identity function if it is unspecified. + If *reducefunc* is unspecified, no summarization takes place: + + >>> keyfunc = lambda x: x.upper() + >>> result = map_reduce('abbccc', keyfunc) + >>> sorted(result.items()) + [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] + + Specifying *valuefunc* transforms the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> result = map_reduce('abbccc', keyfunc, valuefunc) + >>> sorted(result.items()) + [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] + + Specifying *reducefunc* summarizes the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> reducefunc = sum + >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) + >>> sorted(result.items()) + [('A', 1), ('B', 2), ('C', 3)] + + You may want to filter the input iterable before applying the map/reduce + procedure: + + >>> all_items = range(30) + >>> items = [x for x in all_items if 10 <= x <= 20] # Filter + >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 + >>> categories = map_reduce(items, keyfunc=keyfunc) + >>> sorted(categories.items()) + [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] + >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) + >>> sorted(summaries.items()) + [(0, 90), (1, 75)] + + Note that all items in the iterable are gathered into a list before the + summarization step, which may require significant storage. + + The returned object is a :obj:`collections.defaultdict` with the + ``default_factory`` set to ``None``, such that it behaves like a normal + dictionary. + + """ + + ret = defaultdict(list) + + if valuefunc is None: + for item in iterable: + key = keyfunc(item) + ret[key].append(item) + + else: + for item in iterable: + key = keyfunc(item) + value = valuefunc(item) + ret[key].append(value) + + if reducefunc is not None: + for key, value_list in ret.items(): + ret[key] = reducefunc(value_list) + + ret.default_factory = None + return ret + + +def rlocate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``, starting from the right and moving left. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 + [4, 2, 1] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item: + + >>> iterator = iter('abcb') + >>> pred = lambda x: x == 'b' + >>> list(rlocate(iterator, pred)) + [3, 1] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(rlocate(iterable, pred=pred, window_size=3)) + [9, 5, 1] + + Beware, this function won't return anything for infinite iterables. + If *iterable* is reversible, ``rlocate`` will reverse it and search from + the right. Otherwise, it will search from the left and return the results + in reverse order. + + See :func:`locate` to for other example applications. + + """ + if window_size is None: + try: + len_iter = len(iterable) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) + except TypeError: + pass + + return reversed(list(locate(iterable, pred, window_size))) + + +def replace(iterable, pred, substitutes, count=None, window_size=1): + """Yield the items from *iterable*, replacing the items for which *pred* + returns ``True`` with the items from the iterable *substitutes*. + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] + >>> pred = lambda x: x == 0 + >>> substitutes = (2, 3) + >>> list(replace(iterable, pred, substitutes)) + [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] + + If *count* is given, the number of replacements will be limited: + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] + >>> pred = lambda x: x == 0 + >>> substitutes = [None] + >>> list(replace(iterable, pred, substitutes, count=2)) + [1, 1, None, 1, 1, None, 1, 1, 0] + + Use *window_size* to control the number of items passed as arguments to + *pred*. This allows for locating and replacing subsequences. + + >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] + >>> window_size = 3 + >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred + >>> substitutes = [3, 4] # Splice in these items + >>> list(replace(iterable, pred, substitutes, window_size=window_size)) + [3, 4, 5, 3, 4, 5] + + """ + if window_size < 1: + raise ValueError('window_size must be at least 1') + + # Save the substitutes iterable, since it's used more than once + substitutes = tuple(substitutes) + + # Add padding such that the number of windows matches the length of the + # iterable + it = chain(iterable, repeat(_marker, window_size - 1)) + windows = windowed(it, window_size) + + n = 0 + for w in windows: + # If the current window matches our predicate (and we haven't hit + # our maximum number of replacements), splice in the substitutes + # and then consume the following windows that overlap with this one. + # For example, if the iterable is (0, 1, 2, 3, 4...) + # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... + # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) + if pred(*w): + if (count is None) or (n < count): + n += 1 + yield from substitutes + consume(windows, window_size - 1) + continue + + # If there was no match (or we've reached the replacement limit), + # yield the first item from the window. + if w and (w[0] is not _marker): + yield w[0] + + +def partitions(iterable): + """Yield all possible order-preserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None, min_size=None, max_size=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + if *min_size* and/or *max_size* are given, the minimum and/or maximum size + per block in partition is set. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, min_size=2): + ... print([''.join(p) for p in part]) + ['abc'] + >>> for part in set_partitions(iterable, max_size=2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + min_size = min_size if min_size is not None else 0 + max_size = max_size if max_size is not None else n + if min_size > max_size: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from filter( + lambda z: all(min_size <= len(bk) <= max_size for bk in z), + set_partitions_helper(L, k), + ) + else: + yield from filter( + lambda z: all(min_size <= len(bk) <= max_size for bk in z), + set_partitions_helper(L, k), + ) + + +class time_limited: + """ + Yield items from *iterable* until *limit_seconds* have passed. + If the time limit expires before all items have been yielded, the + ``timed_out`` parameter will be set to ``True``. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = time_limited(0.1, generator()) + >>> list(iterable) + [1, 2] + >>> iterable.timed_out + True + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + As a special case, when *limit_seconds* is zero, the iterator never + returns anything. + + """ + + def __init__(self, limit_seconds, iterable): + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + self.limit_seconds = limit_seconds + self._iterator = iter(iterable) + self._start_time = monotonic() + self.timed_out = False + + def __iter__(self): + return self + + def __next__(self): + if self.limit_seconds == 0: + self.timed_out = True + raise StopIteration + item = next(self._iterator) + if monotonic() - self._start_time > self.limit_seconds: + self.timed_out = True + raise StopIteration + + return item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + + """ + iterator = iter(iterable) + for first in iterator: + for second in iterator: + msg = ( + f'Expected exactly one item in iterable, but got {first!r}, ' + f'{second!r}, and perhaps more.' + ) + raise too_long or ValueError(msg) + return first + return default + + +def _ichunk(iterator, n): + cache = deque() + chunk = islice(iterator, n) + + def generator(): + with suppress(StopIteration): + while True: + if cache: + yield cache.popleft() + else: + yield next(chunk) + + def materialize_next(n=1): + # if n not specified materialize everything + if n is None: + cache.extend(chunk) + return len(cache) + + to_cache = n - len(cache) + + # materialize up to n + if to_cache > 0: + cache.extend(islice(chunk, to_cache)) + + # return number materialized up to n + return min(n, len(cache)) + + return (generator(), materialize_next) + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + iterator = iter(iterable) + while True: + # Create new chunk + chunk, materialize_next = _ichunk(iterator, n) + + # Check to see whether we're at the end of the source iterable + if not materialize_next(): + return + + yield chunk + + # Fill previous chunk's cache + materialize_next(None) + + +def iequals(*iterables): + """Return ``True`` if all given *iterables* are equal to each other, + which means that they contain the same elements in the same order. + + The function is useful for comparing iterables of different data types + or iterables that do not support equality checks. + + >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc")) + True + + >>> iequals("abc", "acb") + False + + Not to be confused with :func:`all_equal`, which checks whether all + elements of iterable are equal to each other. + + """ + return all(map(all_equal, zip_longest(*iterables, fillvalue=object()))) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + return + pool = tuple(iterable) + generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] + current_combo = [None] * r + level = 0 + while generators: + try: + cur_idx, p = next(generators[-1]) + except StopIteration: + generators.pop() + level -= 1 + continue + current_combo[level] = p + if level + 1 == r: + yield tuple(current_combo) + else: + generators.append( + unique_everseen( + enumerate(pool[cur_idx + 1 :], cur_idx + 1), + key=itemgetter(1), + ) + ) + level += 1 + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def map_if(iterable, pred, func, func_else=None): + """Evaluate each item from *iterable* using *pred*. If the result is + equivalent to ``True``, transform the item with *func* and yield it. + Otherwise, transform the item with *func_else* and yield it. + + *pred*, *func*, and *func_else* should each be functions that accept + one argument. By default, *func_else* is the identity function. + + >>> from math import sqrt + >>> iterable = list(range(-5, 5)) + >>> iterable + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] + >>> list(map_if(iterable, lambda x: x > 3, lambda x: 'toobig')) + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig'] + >>> list(map_if(iterable, lambda x: x >= 0, + ... lambda x: f'{sqrt(x):.2f}', lambda x: None)) + [None, None, None, None, None, '0.00', '1.00', '1.41', '1.73', '2.00'] + """ + + if func_else is None: + for item in iterable: + yield func(item) if pred(item) else item + + else: + for item in iterable: + yield func(item) if pred(item) else func_else(item) + + +def _sample_unweighted(iterator, k, strict): + # Algorithm L in the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + reservoir = list(islice(iterator, k)) + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + W = 1.0 + + with suppress(StopIteration): + while True: + W *= random() ** (1 / k) + skip = floor(log(random()) / log1p(-W)) + element = next(islice(iterator, skip, None)) + reservoir[randrange(k)] = element + + shuffle(reservoir) + return reservoir + + +def _sample_weighted(iterator, k, weights, strict): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterator)) + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterator): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + ret = [element for weight_key, element in reservoir] + shuffle(ret) + return ret + + +def _sample_counted(population, k, counts, strict): + element = None + remaining = 0 + + def feed(i): + # Advance *i* steps ahead and consume an element + nonlocal element, remaining + + while i + 1 > remaining: + i = i - remaining + element = next(population) + remaining = next(counts) + remaining -= i + 1 + return element + + with suppress(StopIteration): + reservoir = [] + for _ in range(k): + reservoir.append(feed(0)) + + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + + with suppress(StopIteration): + W = 1.0 + while True: + W *= random() ** (1 / k) + skip = floor(log(random()) / log1p(-W)) + element = feed(skip) + reservoir[randrange(k)] = element + + shuffle(reservoir) + return reservoir + + +def sample(iterable, k, weights=None, *, counts=None, strict=False): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. + + Similar to :func:`random.sample`, but works on inputs that aren't + indexable (such as sets and dictionaries) and on inputs where the + size isn't known in advance (such as generators). + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + For iterables with repeated elements, you may supply *counts* to + indicate the repeats. + + >>> iterable = ['a', 'b'] + >>> counts = [3, 4] # Equivalent to 'a', 'a', 'a', 'b', 'b', 'b', 'b' + >>> sample(iterable, k=3, counts=counts) # doctest: +SKIP + ['a', 'a', 'b'] + + An iterable with *weights* may be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + Weighted selections are made without replacement. + After an element is selected, it is removed from the pool and the + relative weights of the other elements increase (this + does not match the behavior of :func:`random.sample`'s *counts* + parameter). Note that *weights* may not be used with *counts*. + + If the length of *iterable* is less than *k*, + ``ValueError`` is raised if *strict* is ``True`` and + all elements are returned (in shuffled order) if *strict* is ``False``. + + By default, the `Algorithm L `__ reservoir sampling + technique is used. When *weights* are provided, + `Algorithm A-ExpJ `__ is used instead. + + Notes on reproducibility: + + * The algorithms rely on inexact floating-point functions provided + by the underlying math library (e.g. ``log``, ``log1p``, and ``pow``). + Those functions can `produce slightly different results + `_ on + different builds. Accordingly, selections can vary across builds + even for the same seed. + + * The algorithms loop over the input and make selections based on + ordinal position, so selections from unordered collections (such as + sets) won't reproduce across sessions on the same platform using the + same seed. For example, this won't reproduce:: + + >> seed(8675309) + >> sample(set('abcdefghijklmnopqrstuvwxyz'), 10) + ['c', 'p', 'e', 'w', 's', 'a', 'j', 'd', 'n', 't'] + + """ + iterator = iter(iterable) + + if k < 0: + raise ValueError('k must be non-negative') + + if k == 0: + return [] + + if weights is not None and counts is not None: + raise TypeError('weights and counts are mutually exclusive') + + elif weights is not None: + weights = iter(weights) + return _sample_weighted(iterator, k, weights, strict) + + elif counts is not None: + counts = iter(counts) + return _sample_counted(iterator, k, counts, strict) + + else: + return _sample_unweighted(iterator, k, strict) + + +def is_sorted(iterable, key=None, reverse=False, strict=False): + """Returns ``True`` if the items of iterable are in sorted order, and + ``False`` otherwise. *key* and *reverse* have the same meaning that they do + in the built-in :func:`sorted` function. + + >>> is_sorted(['1', '2', '3', '4', '5'], key=int) + True + >>> is_sorted([5, 4, 3, 1, 2], reverse=True) + False + + If *strict*, tests for strict sorting, that is, returns ``False`` if equal + elements are found: + + >>> is_sorted([1, 2, 2]) + True + >>> is_sorted([1, 2, 2], strict=True) + False + + The function returns ``False`` after encountering the first out-of-order + item, which means it may produce results that differ from the built-in + :func:`sorted` function for objects with unusual comparison dynamics + (like ``math.nan``). If there are no out-of-order items, the iterable is + exhausted. + """ + it = iterable if (key is None) else map(key, iterable) + a, b = tee(it) + next(b, None) + if reverse: + b, a = a, b + return all(map(lt, a, b)) if strict else not any(map(lt, b, a)) + + +class AbortThread(BaseException): + pass + + +class callback_iter: + """Convert a function that uses callbacks to an iterator. + + Let *func* be a function that takes a `callback` keyword argument. + For example: + + >>> def func(callback=None): + ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: + ... if callback: + ... callback(i, c) + ... return 4 + + + Use ``with callback_iter(func)`` to get an iterator over the parameters + that are delivered to the callback. + + >>> with callback_iter(func) as it: + ... for args, kwargs in it: + ... print(args) + (1, 'a') + (2, 'b') + (3, 'c') + + The function will be called in a background thread. The ``done`` property + indicates whether it has completed execution. + + >>> it.done + True + + If it completes successfully, its return value will be available + in the ``result`` property. + + >>> it.result + 4 + + Notes: + + * If the function uses some keyword argument besides ``callback``, supply + *callback_kwd*. + * If it finished executing, but raised an exception, accessing the + ``result`` property will raise the same exception. + * If it hasn't finished executing, accessing the ``result`` + property from within the ``with`` block will raise ``RuntimeError``. + * If it hasn't finished executing, accessing the ``result`` property from + outside the ``with`` block will raise a + ``more_itertools.AbortThread`` exception. + * Provide *wait_seconds* to adjust how frequently the it is polled for + output. + + """ + + def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): + self._func = func + self._callback_kwd = callback_kwd + self._aborted = False + self._future = None + self._wait_seconds = wait_seconds + # Lazily import concurrent.future + self._executor = __import__( + 'concurrent.futures' + ).futures.ThreadPoolExecutor(max_workers=1) + self._iterator = self._reader() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._aborted = True + self._executor.shutdown() + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + @property + def done(self): + if self._future is None: + return False + return self._future.done() + + @property + def result(self): + if not self.done: + raise RuntimeError('Function has not yet completed') + + return self._future.result() + + def _reader(self): + q = Queue() + + def callback(*args, **kwargs): + if self._aborted: + raise AbortThread('canceled by user') + + q.put((args, kwargs)) + + self._future = self._executor.submit( + self._func, **{self._callback_kwd: callback} + ) + + while True: + try: + item = q.get(timeout=self._wait_seconds) + except Empty: + pass + else: + q.task_done() + yield item + + if self._future.done(): + break + + remaining = [] + while True: + try: + item = q.get_nowait() + except Empty: + break + else: + q.task_done() + remaining.append(item) + q.join() + yield from remaining + + +def windowed_complete(iterable, n): + """ + Yield ``(beginning, middle, end)`` tuples, where: + + * Each ``middle`` has *n* items from *iterable* + * Each ``beginning`` has the items before the ones in ``middle`` + * Each ``end`` has the items after the ones in ``middle`` + + >>> iterable = range(7) + >>> n = 3 + >>> for beginning, middle, end in windowed_complete(iterable, n): + ... print(beginning, middle, end) + () (0, 1, 2) (3, 4, 5, 6) + (0,) (1, 2, 3) (4, 5, 6) + (0, 1) (2, 3, 4) (5, 6) + (0, 1, 2) (3, 4, 5) (6,) + (0, 1, 2, 3) (4, 5, 6) () + + Note that *n* must be at least 0 and most equal to the length of + *iterable*. + + This function will exhaust the iterable and may require significant + storage. + """ + if n < 0: + raise ValueError('n must be >= 0') + + seq = tuple(iterable) + size = len(seq) + + if n > size: + raise ValueError('n must be <= len(seq)') + + for i in range(size - n + 1): + beginning = seq[:i] + middle = seq[i : i + n] + end = seq[i + n :] + yield beginning, middle, end + + +def all_unique(iterable, key=None): + """ + Returns ``True`` if all the elements of *iterable* are unique (no two + elements are equal). + + >>> all_unique('ABCB') + False + + If a *key* function is specified, it will be used to make comparisons. + + >>> all_unique('ABCb') + True + >>> all_unique('ABCb', str.lower) + False + + The function returns as soon as the first non-unique element is + encountered. Iterables with a mix of hashable and unhashable items can + be used, but the function will be slower for unhashable items. + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + for element in map(key, iterable) if key else iterable: + try: + if element in seenset: + return False + seenset_add(element) + except TypeError: + if element in seenlist: + return False + seenlist_add(element) + return True + + +def nth_product(index, *args): + """Equivalent to ``list(product(*args))[index]``. + + The products of *args* can be ordered lexicographically. + :func:`nth_product` computes the product at sort position *index* without + computing the previous products. + + >>> nth_product(8, range(2), range(2), range(2), range(2)) + (1, 0, 0, 0) + + ``IndexError`` will be raised if the given *index* is invalid. + """ + pools = list(map(tuple, reversed(args))) + ns = list(map(len, pools)) + + c = reduce(mul, ns) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + result = [] + for pool, n in zip(pools, ns): + result.append(pool[index % n]) + index //= n + + return tuple(reversed(result)) + + +def nth_permutation(iterable, r, index): + """Equivalent to ``list(permutations(iterable, r))[index]``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`nth_permutation` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences. + + >>> nth_permutation('ghijk', 2, 5) + ('h', 'i') + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = list(iterable) + n = len(pool) + + if r is None or r == n: + r, c = n, factorial(n) + elif not 0 <= r < n: + raise ValueError + else: + c = perm(n, r) + assert c > 0 # factorial(n)>0, and r>> nth_combination_with_replacement(range(5), 3, 5) + (0, 1, 1) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = comb(n + r - 1, r) + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + i = 0 + while r: + r -= 1 + while n >= 0: + num_combs = comb(n + r - 1, r) + if index < num_combs: + break + n -= 1 + i += 1 + index -= num_combs + result.append(pool[i]) + + return tuple(result) + + +def value_chain(*args): + """Yield all arguments passed to the function in the same order in which + they were passed. If an argument itself is iterable then iterate over its + values. + + >>> list(value_chain(1, 2, 3, [4, 5, 6])) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and are emitted + as-is: + + >>> list(value_chain('12', '34', ['56', '78'])) + ['12', '34', '56', '78'] + + Pre- or postpend a single element to an iterable: + + >>> list(value_chain(1, [2, 3, 4, 5, 6])) + [1, 2, 3, 4, 5, 6] + >>> list(value_chain([1, 2, 3, 4, 5], 6)) + [1, 2, 3, 4, 5, 6] + + Multiple levels of nesting are not flattened. + + """ + for value in args: + if isinstance(value, (str, bytes)): + yield value + continue + try: + yield from value + except TypeError: + yield value + + +def product_index(element, *args): + """Equivalent to ``list(product(*args)).index(element)`` + + The products of *args* can be ordered lexicographically. + :func:`product_index` computes the first index of *element* without + computing the previous products. + + >>> product_index([8, 2], range(10), range(5)) + 42 + + ``ValueError`` will be raised if the given *element* isn't in the product + of *args*. + """ + index = 0 + + for x, pool in zip_longest(element, args, fillvalue=_marker): + if x is _marker or pool is _marker: + raise ValueError('element is not a product of args') + + pool = tuple(pool) + index = index * len(pool) + pool.index(x) + + return index + + +def combination_index(element, iterable): + """Equivalent to ``list(combinations(iterable, r)).index(element)`` + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`combination_index` computes the index of the + first *element*, without computing the previous combinations. + + >>> combination_index('adf', 'abcdefg') + 10 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations of *iterable*. + """ + element = enumerate(element) + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = enumerate(iterable) + for n, x in pool: + if x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + else: + raise ValueError('element is not a combination of iterable') + + n, _ = last(pool, default=(n, None)) + + # Python versions below 3.8 don't have math.comb + index = 1 + for i, j in enumerate(reversed(indexes), start=1): + j = n - j + if i <= j: + index += comb(j, i) + + return comb(n + 1, k + 1) - index + + +def combination_with_replacement_index(element, iterable): + """Equivalent to + ``list(combinations_with_replacement(iterable, r)).index(element)`` + + The subsequences with repetition of *iterable* that are of length *r* can + be ordered lexicographically. :func:`combination_with_replacement_index` + computes the index of the first *element*, without computing the previous + combinations with replacement. + + >>> combination_with_replacement_index('adf', 'abcdefg') + 20 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations with replacement of *iterable*. + """ + element = tuple(element) + l = len(element) + element = enumerate(element) + + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = tuple(iterable) + for n, x in enumerate(pool): + while x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + if y is None: + break + else: + raise ValueError( + 'element is not a combination with replacement of iterable' + ) + + n = len(pool) + occupations = [0] * n + for p in indexes: + occupations[p] += 1 + + index = 0 + cumulative_sum = 0 + for k in range(1, n): + cumulative_sum += occupations[k - 1] + j = l + n - 1 - k - cumulative_sum + i = n - k + if i <= j: + index += comb(j, i) + + return index + + +def permutation_index(element, iterable): + """Equivalent to ``list(permutations(iterable, r)).index(element)``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`permutation_index` + computes the index of the first *element* directly, without computing + the previous permutations. + + >>> permutation_index([1, 3, 2], range(5)) + 19 + + ``ValueError`` will be raised if the given *element* isn't one of the + permutations of *iterable*. + """ + index = 0 + pool = list(iterable) + for i, x in zip(range(len(pool), -1, -1), element): + r = pool.index(x) + index = index * i + r + del pool[r] + + return index + + +class countable: + """Wrap *iterable* and keep a count of how many items have been consumed. + + The ``items_seen`` attribute starts at ``0`` and increments as the iterable + is consumed: + + >>> iterable = map(str, range(10)) + >>> it = countable(iterable) + >>> it.items_seen + 0 + >>> next(it), next(it) + ('0', '1') + >>> list(it) + ['2', '3', '4', '5', '6', '7', '8', '9'] + >>> it.items_seen + 10 + """ + + def __init__(self, iterable): + self._iterator = iter(iterable) + self.items_seen = 0 + + def __iter__(self): + return self + + def __next__(self): + item = next(self._iterator) + self.items_seen += 1 + + return item + + +def chunked_even(iterable, n): + """Break *iterable* into lists of approximately length *n*. + Items are distributed such the lengths of the lists differ by at most + 1 item. + + >>> iterable = [1, 2, 3, 4, 5, 6, 7] + >>> n = 3 + >>> list(chunked_even(iterable, n)) # List lengths: 3, 2, 2 + [[1, 2, 3], [4, 5], [6, 7]] + >>> list(chunked(iterable, n)) # List lengths: 3, 3, 1 + [[1, 2, 3], [4, 5, 6], [7]] + + """ + iterator = iter(iterable) + + # Initialize a buffer to process the chunks while keeping + # some back to fill any underfilled chunks + min_buffer = (n - 1) * (n - 2) + buffer = list(islice(iterator, min_buffer)) + + # Append items until we have a completed chunk + for _ in islice(map(buffer.append, iterator), n, None, n): + yield buffer[:n] + del buffer[:n] + + # Check if any chunks need addition processing + if not buffer: + return + length = len(buffer) + + # Chunks are either size `full_size <= n` or `partial_size = full_size - 1` + q, r = divmod(length, n) + num_lists = q + (1 if r > 0 else 0) + q, r = divmod(length, num_lists) + full_size = q + (1 if r > 0 else 0) + partial_size = full_size - 1 + num_full = length - partial_size * num_lists + + # Yield chunks of full size + partial_start_idx = num_full * full_size + if full_size > 0: + for i in range(0, partial_start_idx, full_size): + yield buffer[i : i + full_size] + + # Yield chunks of partial size + if partial_size > 0: + for i in range(partial_start_idx, length, partial_size): + yield buffer[i : i + partial_size] + + +def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False): + """A version of :func:`zip` that "broadcasts" any scalar + (i.e., non-iterable) items into output tuples. + + >>> iterable_1 = [1, 2, 3] + >>> iterable_2 = ['a', 'b', 'c'] + >>> scalar = '_' + >>> list(zip_broadcast(iterable_1, iterable_2, scalar)) + [(1, 'a', '_'), (2, 'b', '_'), (3, 'c', '_')] + + The *scalar_types* keyword argument determines what types are considered + scalar. It is set to ``(str, bytes)`` by default. Set it to ``None`` to + treat strings and byte strings as iterable: + + >>> list(zip_broadcast('abc', 0, 'xyz', scalar_types=None)) + [('a', 0, 'x'), ('b', 0, 'y'), ('c', 0, 'z')] + + If the *strict* keyword argument is ``True``, then + ``UnequalIterablesError`` will be raised if any of the iterables have + different lengths. + """ + + def is_scalar(obj): + if scalar_types and isinstance(obj, scalar_types): + return True + try: + iter(obj) + except TypeError: + return True + else: + return False + + size = len(objects) + if not size: + return + + new_item = [None] * size + iterables, iterable_positions = [], [] + for i, obj in enumerate(objects): + if is_scalar(obj): + new_item[i] = obj + else: + iterables.append(iter(obj)) + iterable_positions.append(i) + + if not iterables: + yield tuple(objects) + return + + zipper = _zip_equal if strict else zip + for item in zipper(*iterables): + for i, new_item[i] in zip(iterable_positions, item): + pass + yield tuple(new_item) + + +def unique_in_window(iterable, n, key=None): + """Yield the items from *iterable* that haven't been seen recently. + *n* is the size of the lookback window. + + >>> iterable = [0, 1, 0, 2, 3, 0] + >>> n = 3 + >>> list(unique_in_window(iterable, n)) + [0, 1, 2, 3, 0] + + The *key* function, if provided, will be used to determine uniqueness: + + >>> list(unique_in_window('abAcda', 3, key=lambda x: x.lower())) + ['a', 'b', 'c', 'd', 'a'] + + The items in *iterable* must be hashable. + + """ + if n <= 0: + raise ValueError('n must be greater than 0') + + window = deque(maxlen=n) + counts = defaultdict(int) + use_key = key is not None + + for item in iterable: + if len(window) == n: + to_discard = window[0] + if counts[to_discard] == 1: + del counts[to_discard] + else: + counts[to_discard] -= 1 + + k = key(item) if use_key else item + if k not in counts: + yield item + counts[k] += 1 + window.append(k) + + +def duplicates_everseen(iterable, key=None): + """Yield duplicate elements after their first appearance. + + >>> list(duplicates_everseen('mississippi')) + ['s', 'i', 's', 's', 'i', 'p', 'i'] + >>> list(duplicates_everseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'A', 'a', 'a'] + + This function is analogous to :func:`unique_everseen` and is subject to + the same performance considerations. + + """ + seen_set = set() + seen_list = [] + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seen_set: + seen_set.add(k) + else: + yield element + except TypeError: + if k not in seen_list: + seen_list.append(k) + else: + yield element + + +def duplicates_justseen(iterable, key=None): + """Yields serially-duplicate elements after their first appearance. + + >>> list(duplicates_justseen('mississippi')) + ['s', 's', 'p'] + >>> list(duplicates_justseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'a', 'a'] + + This function is analogous to :func:`unique_justseen`. + + """ + return flatten(g for _, g in groupby(iterable, key) for _ in g) + + +def classify_unique(iterable, key=None): + """Classify each element in terms of its uniqueness. + + For each element in the input iterable, return a 3-tuple consisting of: + + 1. The element itself + 2. ``False`` if the element is equal to the one preceding it in the input, + ``True`` otherwise (i.e. the equivalent of :func:`unique_justseen`) + 3. ``False`` if this element has been seen anywhere in the input before, + ``True`` otherwise (i.e. the equivalent of :func:`unique_everseen`) + + >>> list(classify_unique('otto')) # doctest: +NORMALIZE_WHITESPACE + [('o', True, True), + ('t', True, True), + ('t', False, False), + ('o', True, False)] + + This function is analogous to :func:`unique_everseen` and is subject to + the same performance considerations. + + """ + seen_set = set() + seen_list = [] + use_key = key is not None + previous = None + + for i, element in enumerate(iterable): + k = key(element) if use_key else element + is_unique_justseen = not i or previous != k + previous = k + is_unique_everseen = False + try: + if k not in seen_set: + seen_set.add(k) + is_unique_everseen = True + except TypeError: + if k not in seen_list: + seen_list.append(k) + is_unique_everseen = True + yield element, is_unique_justseen, is_unique_everseen + + +def minmax(iterable_or_value, *others, key=None, default=_marker): + """Returns both the smallest and largest items from an iterable + or from two or more arguments. + + >>> minmax([3, 1, 5]) + (1, 5) + + >>> minmax(4, 2, 6) + (2, 6) + + If a *key* function is provided, it will be used to transform the input + items for comparison. + + >>> minmax([5, 30], key=str) # '30' sorts before '5' + (30, 5) + + If a *default* value is provided, it will be returned if there are no + input items. + + >>> minmax([], default=(0, 0)) + (0, 0) + + Otherwise ``ValueError`` is raised. + + This function makes a single pass over the input elements and takes care to + minimize the number of comparisons made during processing. + + Note that unlike the builtin ``max`` function, which always returns the first + item with the maximum value, this function may return another item when there are + ties. + + This function is based on the + `recipe `__ by + Raymond Hettinger. + """ + iterable = (iterable_or_value, *others) if others else iterable_or_value + + it = iter(iterable) + + try: + lo = hi = next(it) + except StopIteration as exc: + if default is _marker: + raise ValueError( + '`minmax()` argument is an empty iterable. ' + 'Provide a `default` value to suppress this error.' + ) from exc + return default + + # Different branches depending on the presence of key. This saves a lot + # of unimportant copies which would slow the "key=None" branch + # significantly down. + if key is None: + for x, y in zip_longest(it, it, fillvalue=lo): + if y < x: + x, y = y, x + if x < lo: + lo = x + if hi < y: + hi = y + + else: + lo_key = hi_key = key(lo) + + for x, y in zip_longest(it, it, fillvalue=lo): + x_key, y_key = key(x), key(y) + + if y_key < x_key: + x, y, x_key, y_key = y, x, y_key, x_key + if x_key < lo_key: + lo, lo_key = x, x_key + if hi_key < y_key: + hi, hi_key = y, y_key + + return lo, hi + + +def constrained_batches( + iterable, max_size, max_count=None, get_len=len, strict=True +): + """Yield batches of items from *iterable* with a combined size limited by + *max_size*. + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10)) + [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')] + + If a *max_count* is supplied, the number of items per batch is also + limited: + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10, max_count = 2)) + [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)] + + If a *get_len* function is supplied, use that instead of :func:`len` to + determine item size. + + If *strict* is ``True``, raise ``ValueError`` if any single item is bigger + than *max_size*. Otherwise, allow single items to exceed *max_size*. + """ + if max_size <= 0: + raise ValueError('maximum size must be greater than zero') + + batch = [] + batch_size = 0 + batch_count = 0 + for item in iterable: + item_len = get_len(item) + if strict and item_len > max_size: + raise ValueError('item size exceeds maximum size') + + reached_count = batch_count == max_count + reached_size = item_len + batch_size > max_size + if batch_count and (reached_size or reached_count): + yield tuple(batch) + batch.clear() + batch_size = 0 + batch_count = 0 + + batch.append(item) + batch_size += item_len + batch_count += 1 + + if batch: + yield tuple(batch) + + +def gray_product(*iterables): + """Like :func:`itertools.product`, but return tuples in an order such + that only one element in the generated tuple changes from one iteration + to the next. + + >>> list(gray_product('AB','CD')) + [('A', 'C'), ('B', 'C'), ('B', 'D'), ('A', 'D')] + + This function consumes all of the input iterables before producing output. + If any of the input iterables have fewer than two items, ``ValueError`` + is raised. + + For information on the algorithm, see + `this section `__ + of Donald Knuth's *The Art of Computer Programming*. + """ + all_iterables = tuple(tuple(x) for x in iterables) + iterable_count = len(all_iterables) + for iterable in all_iterables: + if len(iterable) < 2: + raise ValueError("each iterable must have two or more items") + + # This is based on "Algorithm H" from section 7.2.1.1, page 20. + # a holds the indexes of the source iterables for the n-tuple to be yielded + # f is the array of "focus pointers" + # o is the array of "directions" + a = [0] * iterable_count + f = list(range(iterable_count + 1)) + o = [1] * iterable_count + while True: + yield tuple(all_iterables[i][a[i]] for i in range(iterable_count)) + j = f[0] + f[0] = 0 + if j == iterable_count: + break + a[j] = a[j] + o[j] + if a[j] == 0 or a[j] == len(all_iterables[j]) - 1: + o[j] = -o[j] + f[j] = f[j + 1] + f[j + 1] = j + 1 + + +def partial_product(*iterables): + """Yields tuples containing one item from each iterator, with subsequent + tuples changing a single item at a time by advancing each iterator until it + is exhausted. This sequence guarantees every value in each iterable is + output at least once without generating all possible combinations. + + This may be useful, for example, when testing an expensive function. + + >>> list(partial_product('AB', 'C', 'DEF')) + [('A', 'C', 'D'), ('B', 'C', 'D'), ('B', 'C', 'E'), ('B', 'C', 'F')] + """ + + iterators = list(map(iter, iterables)) + + try: + prod = [next(it) for it in iterators] + except StopIteration: + return + yield tuple(prod) + + for i, it in enumerate(iterators): + for prod[i] in it: + yield tuple(prod) + + +def takewhile_inclusive(predicate, iterable): + """A variant of :func:`takewhile` that yields one additional element. + + >>> list(takewhile_inclusive(lambda x: x < 5, [1, 4, 6, 4, 1])) + [1, 4, 6] + + :func:`takewhile` would return ``[1, 4]``. + """ + for x in iterable: + yield x + if not predicate(x): + break + + +def outer_product(func, xs, ys, *args, **kwargs): + """A generalized outer product that applies a binary function to all + pairs of items. Returns a 2D matrix with ``len(xs)`` rows and ``len(ys)`` + columns. + Also accepts ``*args`` and ``**kwargs`` that are passed to ``func``. + + Multiplication table: + + >>> list(outer_product(mul, range(1, 4), range(1, 6))) + [(1, 2, 3, 4, 5), (2, 4, 6, 8, 10), (3, 6, 9, 12, 15)] + + Cross tabulation: + + >>> xs = ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'] + >>> ys = ['X', 'X', 'X', 'Y', 'Z', 'Z', 'Y', 'Y', 'Z', 'Z'] + >>> pair_counts = Counter(zip(xs, ys)) + >>> count_rows = lambda x, y: pair_counts[x, y] + >>> list(outer_product(count_rows, sorted(set(xs)), sorted(set(ys)))) + [(2, 3, 0), (1, 0, 4)] + + Usage with ``*args`` and ``**kwargs``: + + >>> animals = ['cat', 'wolf', 'mouse'] + >>> list(outer_product(min, animals, animals, key=len)) + [('cat', 'cat', 'cat'), ('cat', 'wolf', 'wolf'), ('cat', 'wolf', 'mouse')] + """ + ys = tuple(ys) + return batched( + starmap(lambda x, y: func(x, y, *args, **kwargs), product(xs, ys)), + n=len(ys), + ) + + +def iter_suppress(iterable, *exceptions): + """Yield each of the items from *iterable*. If the iteration raises one of + the specified *exceptions*, that exception will be suppressed and iteration + will stop. + + >>> from itertools import chain + >>> def breaks_at_five(x): + ... while True: + ... if x >= 5: + ... raise RuntimeError + ... yield x + ... x += 1 + >>> it_1 = iter_suppress(breaks_at_five(1), RuntimeError) + >>> it_2 = iter_suppress(breaks_at_five(2), RuntimeError) + >>> list(chain(it_1, it_2)) + [1, 2, 3, 4, 2, 3, 4] + """ + try: + yield from iterable + except exceptions: + return + + +def filter_map(func, iterable): + """Apply *func* to every element of *iterable*, yielding only those which + are not ``None``. + + >>> elems = ['1', 'a', '2', 'b', '3'] + >>> list(filter_map(lambda s: int(s) if s.isnumeric() else None, elems)) + [1, 2, 3] + """ + for x in iterable: + y = func(x) + if y is not None: + yield y + + +def powerset_of_sets(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset_of_sets([1, 2, 3])) # doctest: +SKIP + [set(), {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}] + >>> list(powerset_of_sets([1, 1, 0])) # doctest: +SKIP + [set(), {1}, {0}, {0, 1}] + + :func:`powerset_of_sets` takes care to minimize the number + of hash operations performed. + """ + sets = tuple(dict.fromkeys(map(frozenset, zip(iterable)))) + return chain.from_iterable( + starmap(set().union, combinations(sets, r)) + for r in range(len(sets) + 1) + ) + + +def join_mappings(**field_to_map): + """ + Joins multiple mappings together using their common keys. + + >>> user_scores = {'elliot': 50, 'claris': 60} + >>> user_times = {'elliot': 30, 'claris': 40} + >>> join_mappings(score=user_scores, time=user_times) + {'elliot': {'score': 50, 'time': 30}, 'claris': {'score': 60, 'time': 40}} + """ + ret = defaultdict(dict) + + for field_name, mapping in field_to_map.items(): + for key, value in mapping.items(): + ret[key][field_name] = value + + return dict(ret) + + +def _complex_sumprod(v1, v2): + """High precision sumprod() for complex numbers. + Used by :func:`dft` and :func:`idft`. + """ + + real = attrgetter('real') + imag = attrgetter('imag') + r1 = chain(map(real, v1), map(neg, map(imag, v1))) + r2 = chain(map(real, v2), map(imag, v2)) + i1 = chain(map(real, v1), map(imag, v1)) + i2 = chain(map(imag, v2), map(real, v2)) + return complex(_fsumprod(r1, r2), _fsumprod(i1, i2)) + + +def dft(xarr): + """Discrete Fourier Transform. *xarr* is a sequence of complex numbers. + Yields the components of the corresponding transformed output vector. + + >>> import cmath + >>> xarr = [1, 2-1j, -1j, -1+2j] # time domain + >>> Xarr = [2, -2-2j, -2j, 4+4j] # frequency domain + >>> magnitudes, phases = zip(*map(cmath.polar, Xarr)) + >>> all(map(cmath.isclose, dft(xarr), Xarr)) + True + + Inputs are restricted to numeric types that can add and multiply + with a complex number. This includes int, float, complex, and + Fraction, but excludes Decimal. + + See :func:`idft` for the inverse Discrete Fourier Transform. + """ + N = len(xarr) + roots_of_unity = [e ** (n / N * tau * -1j) for n in range(N)] + for k in range(N): + coeffs = [roots_of_unity[k * n % N] for n in range(N)] + yield _complex_sumprod(xarr, coeffs) + + +def idft(Xarr): + """Inverse Discrete Fourier Transform. *Xarr* is a sequence of + complex numbers. Yields the components of the corresponding + inverse-transformed output vector. + + >>> import cmath + >>> xarr = [1, 2-1j, -1j, -1+2j] # time domain + >>> Xarr = [2, -2-2j, -2j, 4+4j] # frequency domain + >>> all(map(cmath.isclose, idft(Xarr), xarr)) + True + + Inputs are restricted to numeric types that can add and multiply + with a complex number. This includes int, float, complex, and + Fraction, but excludes Decimal. + + See :func:`dft` for the Discrete Fourier Transform. + """ + N = len(Xarr) + roots_of_unity = [e ** (n / N * tau * 1j) for n in range(N)] + for k in range(N): + coeffs = [roots_of_unity[k * n % N] for n in range(N)] + yield _complex_sumprod(Xarr, coeffs) / N + + +def doublestarmap(func, iterable): + """Apply *func* to every item of *iterable* by dictionary unpacking + the item into *func*. + + The difference between :func:`itertools.starmap` and :func:`doublestarmap` + parallels the distinction between ``func(*a)`` and ``func(**a)``. + + >>> iterable = [{'a': 1, 'b': 2}, {'a': 40, 'b': 60}] + >>> list(doublestarmap(lambda a, b: a + b, iterable)) + [3, 100] + + ``TypeError`` will be raised if *func*'s signature doesn't match the + mapping contained in *iterable* or if *iterable* does not contain mappings. + """ + for item in iterable: + yield func(**item) + + +def _nth_prime_bounds(n): + """Bounds for the nth prime (counting from 1): lb < p_n < ub.""" + # At and above 688,383, the lb/ub spread is under 0.003 * p_n. + + if n < 1: + raise ValueError + + if n < 6: + return (n, 2.25 * n) + + # https://en.wikipedia.org/wiki/Prime-counting_function#Inequalities + upper_bound = n * log(n * log(n)) + lower_bound = upper_bound - n + if n >= 688_383: + upper_bound -= n * (1.0 - (log(log(n)) - 2.0) / log(n)) + + return lower_bound, upper_bound + + +def nth_prime(n, *, approximate=False): + """Return the nth prime (counting from 0). + + >>> nth_prime(0) + 2 + >>> nth_prime(100) + 547 + + If *approximate* is set to True, will return a prime close + to the nth prime. The estimation is much faster than computing + an exact result. + + >>> nth_prime(200_000_000, approximate=True) # Exact result is 4222234763 + 4217820427 + + """ + lb, ub = _nth_prime_bounds(n + 1) + + if not approximate or n <= 1_000_000: + return nth(sieve(ceil(ub)), n) + + # Search from the midpoint and return the first odd prime + odd = floor((lb + ub) / 2) | 1 + return first_true(count(odd, step=2), pred=is_prime) + + +def argmin(iterable, *, key=None): + """ + Index of the first occurrence of a minimum value in an iterable. + + >>> argmin('efghabcdijkl') + 4 + >>> argmin([3, 2, 1, 0, 4, 2, 1, 0]) + 3 + + For example, look up a label corresponding to the position + of a value that minimizes a cost function:: + + >>> def cost(x): + ... "Days for a wound to heal given a subject's age." + ... return x**2 - 20*x + 150 + ... + >>> labels = ['homer', 'marge', 'bart', 'lisa', 'maggie'] + >>> ages = [ 35, 30, 10, 9, 1 ] + + # Fastest healing family member + >>> labels[argmin(ages, key=cost)] + 'bart' + + # Age with fastest healing + >>> min(ages, key=cost) + 10 + + """ + if key is not None: + iterable = map(key, iterable) + return min(enumerate(iterable), key=itemgetter(1))[0] + + +def argmax(iterable, *, key=None): + """ + Index of the first occurrence of a maximum value in an iterable. + + >>> argmax('abcdefghabcd') + 7 + >>> argmax([0, 1, 2, 3, 3, 2, 1, 0]) + 3 + + For example, identify the best machine learning model:: + + >>> models = ['svm', 'random forest', 'knn', 'naïve bayes'] + >>> accuracy = [ 68, 61, 84, 72 ] + + # Most accurate model + >>> models[argmax(accuracy)] + 'knn' + + # Best accuracy + >>> max(accuracy) + 84 + + """ + if key is not None: + iterable = map(key, iterable) + return max(enumerate(iterable), key=itemgetter(1))[0] + + +def extract(iterable, indices): + """Yield values at the specified indices. + + Example: + + >>> data = 'abcdefghijklmnopqrstuvwxyz' + >>> list(extract(data, [7, 4, 11, 11, 14])) + ['h', 'e', 'l', 'l', 'o'] + + The *iterable* is consumed lazily and can be infinite. + The *indices* are consumed immediately and must be finite. + + Raises ``IndexError`` if an index lies beyond the iterable. + Raises ``ValueError`` for negative indices. + """ + + iterator = iter(iterable) + index_and_position = sorted(zip(indices, count())) + + if index_and_position and index_and_position[0][0] < 0: + raise ValueError('Indices must be non-negative') + + buffer = {} + iterator_position = -1 + next_to_emit = 0 + + for index, order in index_and_position: + advance = index - iterator_position + if advance: + try: + value = next(islice(iterator, advance - 1, None)) + except StopIteration: + raise IndexError(index) + iterator_position = index + + buffer[order] = value + + while next_to_emit in buffer: + yield buffer.pop(next_to_emit) + next_to_emit += 1 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.pyi b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.pyi new file mode 100644 index 0000000..b5e33f8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/more.pyi @@ -0,0 +1,949 @@ +"""Stubs for more_itertools.more""" + +from __future__ import annotations + +import sys +import types + +from collections.abc import ( + Container, + Hashable, + Iterable, + Iterator, + Mapping, + Reversible, + Sequence, + Sized, +) +from contextlib import AbstractContextManager +from typing import ( + Any, + Callable, + Generic, + TypeVar, + overload, + type_check_only, +) +from typing_extensions import Protocol + +__all__ = [ + 'AbortThread', + 'SequenceView', + 'UnequalIterablesError', + 'adjacent', + 'all_unique', + 'always_iterable', + 'always_reversible', + 'argmax', + 'argmin', + 'bucket', + 'callback_iter', + 'chunked', + 'chunked_even', + 'circular_shifts', + 'collapse', + 'combination_index', + 'combination_with_replacement_index', + 'consecutive_groups', + 'constrained_batches', + 'consumer', + 'count_cycle', + 'countable', + 'derangements', + 'dft', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'doublestarmap', + 'duplicates_everseen', + 'duplicates_justseen', + 'classify_unique', + 'exactly_n', + 'extract', + 'filter_except', + 'filter_map', + 'first', + 'gray_product', + 'groupby_transform', + 'ichunked', + 'iequals', + 'idft', + 'ilen', + 'interleave', + 'interleave_evenly', + 'interleave_longest', + 'interleave_randomly', + 'intersperse', + 'is_sorted', + 'islice_extended', + 'iterate', + 'iter_suppress', + 'join_mappings', + 'last', + 'locate', + 'longest_common_prefix', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_if', + 'map_reduce', + 'mark_ends', + 'minmax', + 'nth_or_last', + 'nth_permutation', + 'nth_prime', + 'nth_product', + 'nth_combination_with_replacement', + 'numeric_range', + 'one', + 'only', + 'outer_product', + 'padded', + 'partial_product', + 'partitions', + 'peekable', + 'permutation_index', + 'powerset_of_sets', + 'product_index', + 'raise_', + 'repeat_each', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'set_partitions', + 'side_effect', + 'sliced', + 'sort_together', + 'split_after', + 'split_at', + 'split_before', + 'split_into', + 'split_when', + 'spy', + 'stagger', + 'strip', + 'strictly_n', + 'substrings', + 'substrings_indexes', + 'takewhile_inclusive', + 'time_limited', + 'unique_in_window', + 'unique_to_each', + 'unzip', + 'value_chain', + 'windowed', + 'windowed_complete', + 'with_iter', + 'zip_broadcast', + 'zip_equal', + 'zip_offset', +] + +# Type and type variable definitions +_T = TypeVar('_T') +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_T3 = TypeVar('_T3') +_T4 = TypeVar('_T4') +_T5 = TypeVar('_T5') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[Any]]) +_Raisable = BaseException | type[BaseException] + +# The type of isinstance's second argument (from typeshed builtins) +if sys.version_info >= (3, 10): + _ClassInfo = type | types.UnionType | tuple[_ClassInfo, ...] +else: + _ClassInfo = type | tuple[_ClassInfo, ...] + +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +@type_check_only +class _SupportsSlicing(Protocol[_T_co]): + def __getitem__(self, __k: slice) -> _T_co: ... + +def chunked( + iterable: Iterable[_T], n: int | None, strict: bool = ... +) -> Iterator[list[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> _T | _U: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> _T | _U: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> _T | _U: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> list[_T]: ... + +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[_T]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: AbstractContextManager[Iterable[_T]], +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: _Raisable | None = ..., + too_long: _Raisable | None = ..., +) -> _T: ... +def raise_(exception: _Raisable, *args: Any) -> None: ... +def strictly_n( + iterable: Iterable[_T], + n: int, + too_short: _GenFn | None = ..., + too_long: _GenFn | None = ..., +) -> list[_T]: ... +def distinct_permutations( + iterable: Iterable[_T], r: int | None = ... +) -> Iterator[tuple[_T, ...]]: ... +def derangements( + iterable: Iterable[_T], r: int | None = None +) -> Iterator[tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[_T | _U]: ... +def unique_to_each(*iterables: Iterable[_T]) -> list[list[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[tuple[_T | _U, ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Callable[[_U], object] | None = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __iter__(self) -> Iterator[_U]: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> tuple[list[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_evenly( + iterables: list[Iterable[_T]], lengths: list[int] | None = ... +) -> Iterator[_T]: ... +def interleave_randomly(*iterables: Iterable[_T]) -> Iterable[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: _ClassInfo | None = ..., + levels: int | None = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[list[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., +) -> Iterator[_T]: ... +def sliced( + seq: _SupportsSlicing[_T], n: int, strict: bool = ... +) -> Iterator[_T]: ... +def split_at( + iterable: Iterable[_T], + pred: Callable[[_T], object], + maxsplit: int = ..., + keep_separator: bool = ..., +) -> Iterator[list[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[list[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[list[_T]]: ... +def split_when( + iterable: Iterable[_T], + pred: Callable[[_T, _T], object], + maxsplit: int = ..., +) -> Iterator[list[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[int | None] +) -> Iterator[list[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: int | None = ..., + next_multiple: bool = ..., +) -> Iterator[_T | None]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: int | None = ..., + next_multiple: bool = ..., +) -> Iterator[_T | _U]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last(iterable: Iterable[_T], default: _U) -> Iterator[_T | _U]: ... +def distribute(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[tuple[_T | _U, ...]]: ... + +class UnequalIterablesError(ValueError): + def __init__(self, details: tuple[int, int, int] | None = ...) -> None: ... + +# zip_equal +@overload +def zip_equal(__iter1: Iterable[_T1]) -> Iterator[tuple[_T1]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], __iter2: Iterable[_T2] +) -> Iterator[tuple[_T1, _T2]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3] +) -> Iterator[tuple[_T1, _T2, _T3]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], +) -> Iterator[tuple[_T1, _T2, _T3, _T4]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + __iter3: Iterable[_T3], + __iter4: Iterable[_T4], + __iter5: Iterable[_T5], +) -> Iterator[tuple[_T1, _T2, _T3, _T4, _T5]]: ... +@overload +def zip_equal( + __iter1: Iterable[Any], + __iter2: Iterable[Any], + __iter3: Iterable[Any], + __iter4: Iterable[Any], + __iter5: Iterable[Any], + __iter6: Iterable[Any], + *iterables: Iterable[Any], +) -> Iterator[tuple[Any, ...]]: ... + +# zip_offset +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T1 | None]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T1 | None, _T2 | None]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T1 | _U]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T1 | _U, _T2 | _U]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T | _U, ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + key: Callable[..., Any] | None = ..., + reverse: bool = ..., + strict: bool = ..., +) -> list[tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... +def always_iterable( + obj: object, + base_type: _ClassInfo | None = ..., +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[tuple[bool, _T]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None = None, + valuefunc: None = None, + reducefunc: None = None, +) -> Iterator[tuple[_T, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: None, +) -> Iterator[tuple[_U, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterator[tuple[_T, Iterator[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterator[tuple[_U, Iterator[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterator[tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterator[tuple[_U, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterator[_V]], _W], +) -> Iterator[tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterator[_V]], _W], +) -> Iterator[tuple[_U, _W]]: ... + +class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): + @overload + def __init__(self, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... + def __bool__(self) -> bool: ... + def __contains__(self, elem: object) -> bool: ... + def __eq__(self, other: object) -> bool: ... + @overload + def __getitem__(self, key: int) -> _T: ... + @overload + def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __len__(self) -> int: ... + def __reduce__( + self, + ) -> tuple[type[numeric_range[_T, _U]], tuple[_T, _T, _U]]: ... + def __repr__(self) -> str: ... + def __reversed__(self) -> Iterator[_T]: ... + def count(self, value: _T) -> int: ... + def index(self, value: _T) -> int: ... # type: ignore + +def count_cycle( + iterable: Iterable[_T], n: int | None = ... +) -> Iterable[tuple[int, _T]]: ... +def mark_ends( + iterable: Iterable[_T], +) -> Iterable[tuple[bool, bool, _T]]: ... +def locate( + iterable: Iterable[_T], + pred: Callable[..., Any] = ..., + window_size: int | None = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... + +class islice_extended(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T], *args: int | None) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + def __getitem__(self, index: slice) -> islice_extended[_T]: ... + +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: None | Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ..., +) -> Iterator[_T | _U]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], maxlen: int | None = ... + ) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> _T | _U: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + def relative_seek(self, count: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts( + iterable: Iterable[_T], steps: int = 1 +) -> list[tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> dict[_U, list[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> dict[_U, list[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[list[_T]], _W] = ..., +) -> dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[list[_V]], _W], +) -> dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: int | None = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: int | None = ..., + window_size: int = ..., +) -> Iterator[_T | _U]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[list[list[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], + k: int | None = ..., + min_size: int | None = ..., + max_size: int | None = ..., +) -> Iterator[list[list[_T]]]: ... + +class time_limited(Generic[_T], Iterator[_T]): + def __init__( + self, limit_seconds: float, iterable: Iterable[_T] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + +@overload +def only( + iterable: Iterable[_T], *, too_long: _Raisable | None = ... +) -> _T | None: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: _Raisable | None = ... +) -> _T | _U: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: type[BaseException], +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: type[BaseException], +) -> Iterator[_U]: ... +def map_if( + iterable: Iterable[Any], + pred: Callable[[Any], bool], + func: Callable[[Any], Any], + func_else: Callable[[Any], Any] | None = ..., +) -> Iterator[Any]: ... +def _sample_unweighted( + iterator: Iterator[_T], k: int, strict: bool +) -> list[_T]: ... +def _sample_counted( + population: Iterator[_T], k: int, counts: Iterable[int], strict: bool +) -> list[_T]: ... +def _sample_weighted( + iterator: Iterator[_T], k: int, weights: Iterator[float], strict: bool +) -> list[_T]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Iterable[float] | None = ..., + *, + counts: Iterable[int] | None = ..., + strict: bool = False, +) -> list[_T]: ... +def is_sorted( + iterable: Iterable[_T], + key: Callable[[_T], _U] | None = ..., + reverse: bool = False, + strict: bool = False, +) -> bool: ... + +class AbortThread(BaseException): + pass + +class callback_iter(Generic[_T], Iterator[_T]): + def __init__( + self, + func: Callable[..., Any], + callback_kwd: str = ..., + wait_seconds: float = ..., + ) -> None: ... + def __enter__(self) -> callback_iter[_T]: ... + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: types.TracebackType | None, + ) -> bool | None: ... + def __iter__(self) -> callback_iter[_T]: ... + def __next__(self) -> _T: ... + def _reader(self) -> Iterator[_T]: ... + @property + def done(self) -> bool: ... + @property + def result(self) -> Any: ... + +def windowed_complete( + iterable: Iterable[_T], n: int +) -> Iterator[tuple[tuple[_T, ...], tuple[_T, ...], tuple[_T, ...]]]: ... +def all_unique( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> bool: ... +def nth_product(index: int, *args: Iterable[_T]) -> tuple[_T, ...]: ... +def nth_combination_with_replacement( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def nth_permutation( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def value_chain(*args: _T | Iterable[_T]) -> Iterable[_T]: ... +def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... +def combination_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def combination_with_replacement_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def permutation_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def repeat_each(iterable: Iterable[_T], n: int = ...) -> Iterator[_T]: ... + +class countable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> countable[_T]: ... + def __next__(self) -> _T: ... + items_seen: int + +def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[list[_T]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + __obj5: _T | Iterable[_T], + *, + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +@overload +def zip_broadcast( + __obj1: _T | Iterable[_T], + __obj2: _T | Iterable[_T], + __obj3: _T | Iterable[_T], + __obj4: _T | Iterable[_T], + __obj5: _T | Iterable[_T], + __obj6: _T | Iterable[_T], + *objects: _T | Iterable[_T], + scalar_types: _ClassInfo | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +def unique_in_window( + iterable: Iterable[_T], n: int, key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def duplicates_everseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def duplicates_justseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def classify_unique( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[tuple[_T, bool, bool]]: ... + +class _SupportsLessThan(Protocol): + def __lt__(self, __other: Any) -> bool: ... + +_SupportsLessThanT = TypeVar("_SupportsLessThanT", bound=_SupportsLessThan) + +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], *, key: None = None +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], *, key: Callable[[_T], _SupportsLessThan] +) -> tuple[_T, _T]: ... +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], + *, + key: None = None, + default: _U, +) -> _U | tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], + *, + key: Callable[[_T], _SupportsLessThan], + default: _U, +) -> _U | tuple[_T, _T]: ... +@overload +def minmax( + iterable_or_value: _SupportsLessThanT, + __other: _SupportsLessThanT, + *others: _SupportsLessThanT, +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: _T, + __other: _T, + *others: _T, + key: Callable[[_T], _SupportsLessThan], +) -> tuple[_T, _T]: ... +def longest_common_prefix( + iterables: Iterable[Iterable[_T]], +) -> Iterator[_T]: ... +def iequals(*iterables: Iterable[Any]) -> bool: ... +def constrained_batches( + iterable: Iterable[_T], + max_size: int, + max_count: int | None = ..., + get_len: Callable[[_T], object] = ..., + strict: bool = ..., +) -> Iterator[tuple[_T]]: ... +def gray_product(*iterables: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def partial_product(*iterables: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def takewhile_inclusive( + predicate: Callable[[_T], bool], iterable: Iterable[_T] +) -> Iterator[_T]: ... +def outer_product( + func: Callable[[_T, _U], _V], + xs: Iterable[_T], + ys: Iterable[_U], + *args: Any, + **kwargs: Any, +) -> Iterator[tuple[_V, ...]]: ... +def iter_suppress( + iterable: Iterable[_T], + *exceptions: type[BaseException], +) -> Iterator[_T]: ... +def filter_map( + func: Callable[[_T], _V | None], + iterable: Iterable[_T], +) -> Iterator[_V]: ... +def powerset_of_sets(iterable: Iterable[_T]) -> Iterator[set[_T]]: ... +def join_mappings( + **field_to_map: Mapping[_T, _V], +) -> dict[_T, dict[str, _V]]: ... +def doublestarmap( + func: Callable[..., _T], + iterable: Iterable[Mapping[str, Any]], +) -> Iterator[_T]: ... +def dft(xarr: Sequence[complex]) -> Iterator[complex]: ... +def idft(Xarr: Sequence[complex]) -> Iterator[complex]: ... +def _nth_prime_ub(n: int) -> float: ... +def nth_prime(n: int, *, approximate: bool = ...) -> int: ... +def argmin( + iterable: Iterable[_T], *, key: Callable[[_T], _U] | None = ... +) -> int: ... +def argmax( + iterable: Iterable[_T], *, key: Callable[[_T], _U] | None = ... +) -> int: ... +def extract( + iterable: Iterable[_T], indices: Iterable[int] +) -> Iterator[_T]: ... diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.py new file mode 100644 index 0000000..dacf614 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.py @@ -0,0 +1,1471 @@ +"""Imported from the recipes section of the itertools documentation. + +All functions taken from the recipes section of the itertools library docs +[1]_. +Some backward-compatible usability improvements have been made. + +.. [1] http://docs.python.org/library/itertools.html#recipes + +""" + +import random + +from bisect import bisect_left, insort +from collections import deque +from contextlib import suppress +from functools import lru_cache, partial, reduce +from heapq import heappush, heappushpop +from itertools import ( + accumulate, + chain, + combinations, + compress, + count, + cycle, + groupby, + islice, + product, + repeat, + starmap, + takewhile, + tee, + zip_longest, +) +from math import prod, comb, isqrt, gcd +from operator import mul, not_, itemgetter, getitem, index +from random import randrange, sample, choice +from sys import hexversion + +__all__ = [ + 'all_equal', + 'batched', + 'before_and_after', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'factor', + 'flatten', + 'grouper', + 'is_prime', + 'iter_except', + 'iter_index', + 'loops', + 'matmul', + 'multinomial', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'polynomial_eval', + 'polynomial_from_roots', + 'polynomial_derivative', + 'powerset', + 'prepend', + 'quantify', + 'reshape', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'running_median', + 'sieve', + 'sliding_window', + 'subslices', + 'sum_of_squares', + 'tabulate', + 'tail', + 'take', + 'totient', + 'transpose', + 'triplewise', + 'unique', + 'unique_everseen', + 'unique_justseen', +] + +_marker = object() + + +# zip with strict is available for Python 3.10+ +try: + zip(strict=True) +except TypeError: # pragma: no cover + _zip_strict = zip +else: # pragma: no cover + _zip_strict = partial(zip, strict=True) + + +# math.sumprod is available for Python 3.12+ +try: + from math import sumprod as _sumprod +except ImportError: # pragma: no cover + _sumprod = lambda x, y: dotproduct(x, y) + + +# heapq max-heap functions are available for Python 3.14+ +try: + from heapq import heappush_max, heappushpop_max +except ImportError: # pragma: no cover + _max_heap_available = False +else: # pragma: no cover + _max_heap_available = True + + +def take(n, iterable): + """Return first *n* items of the *iterable* as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +def tabulate(function, start=0): + """Return an iterator over the results of ``func(start)``, + ``func(start + 1)``, ``func(start + 2)``... + + *func* should be a function that accepts one integer argument. + + If *start* is not specified it defaults to 0. It will be incremented each + time the iterator is advanced. + + >>> square = lambda x: x ** 2 + >>> iterator = tabulate(square, -3) + >>> take(4, iterator) + [9, 4, 1, 0] + + """ + return map(function, count(start)) + + +def tail(n, iterable): + """Return an iterator over the last *n* items of *iterable*. + + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] + + """ + try: + size = len(iterable) + except TypeError: + return iter(deque(iterable, maxlen=n)) + else: + return islice(iterable, max(0, size - n), None) + + +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' + + """ + return next(islice(iterable, n, None), default) + + +def all_equal(iterable, key=None): + """ + Returns ``True`` if all the elements are equal to each other. + + >>> all_equal('aaaa') + True + >>> all_equal('aaab') + False + + A function that accepts a single argument and returns a transformed version + of each input item can be specified with *key*: + + >>> all_equal('AaaA', key=str.casefold) + True + >>> all_equal([1, 2, 3], key=lambda x: x < 10) + True + + """ + iterator = groupby(iterable, key) + for first in iterator: + for second in iterator: + return False + return True + return True + + +def quantify(iterable, pred=bool): + """Return the how many times the predicate is true. + + >>> quantify([True, False, True]) + 2 + + """ + return sum(map(pred, iterable)) + + +def pad_none(iterable): + """Returns the sequence of elements and then returns ``None`` indefinitely. + + >>> take(5, pad_none(range(3))) + [0, 1, 2, None, None] + + Useful for emulating the behavior of the built-in :func:`map` function. + + See also :func:`padded`. + + """ + return chain(iterable, repeat(None)) + + +padnone = pad_none + + +def ncycles(iterable, n): + """Returns the sequence elements *n* times + + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] + + """ + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def dotproduct(vec1, vec2): + """Returns the dot product of the two iterables. + + >>> dotproduct([10, 15, 12], [0.65, 0.80, 1.25]) + 33.5 + >>> 10 * 0.65 + 15 * 0.80 + 12 * 1.25 + 33.5 + + In Python 3.12 and later, use ``math.sumprod()`` instead. + """ + return sum(map(mul, vec1, vec2)) + + +def flatten(listOfLists): + """Return an iterator flattening one level of nesting in a list of lists. + + >>> list(flatten([[0, 1], [2, 3]])) + [0, 1, 2, 3] + + See also :func:`collapse`, which can flatten multiple levels of nesting. + + """ + return chain.from_iterable(listOfLists) + + +def repeatfunc(func, times=None, *args): + """Call *func* with *args* repeatedly, returning an iterable over the + results. + + If *times* is specified, the iterable will terminate after that many + repetitions: + + >>> from operator import add + >>> times = 4 + >>> args = 3, 5 + >>> list(repeatfunc(add, times, *args)) + [8, 8, 8, 8] + + If *times* is ``None`` the iterable will not terminate: + + >>> from random import randrange + >>> times = None + >>> args = 1, 11 + >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP + [2, 4, 8, 1, 8, 4] + + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def _pairwise(iterable): + """Returns an iterator of paired items, overlapping, from the original + + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. + + """ + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + +try: + from itertools import pairwise as itertools_pairwise +except ImportError: # pragma: no cover + pairwise = _pairwise +else: # pragma: no cover + + def pairwise(iterable): + return itertools_pairwise(iterable) + + pairwise.__doc__ = _pairwise.__doc__ + + +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def _zip_equal(*iterables): + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + raise UnequalIterablesError(details=(first_size, i, size)) + # All sizes are equal, we can use the built-in zip. + return zip(*iterables) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def grouper(iterable, n, incomplete='fill', fillvalue=None): + """Group elements from *iterable* into fixed-length groups of length *n*. + + >>> list(grouper('ABCDEF', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + The keyword arguments *incomplete* and *fillvalue* control what happens for + iterables whose length is not a multiple of *n*. + + When *incomplete* is `'fill'`, the last group will contain instances of + *fillvalue*. + + >>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + When *incomplete* is `'ignore'`, the last group will not be emitted. + + >>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised. + + >>> iterator = grouper('ABCDEFG', 3, incomplete='strict') + >>> list(iterator) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnequalIterablesError + + """ + iterators = [iter(iterable)] * n + if incomplete == 'fill': + return zip_longest(*iterators, fillvalue=fillvalue) + if incomplete == 'strict': + return _zip_equal(*iterators) + if incomplete == 'ignore': + return zip(*iterators) + else: + raise ValueError('Expected fill, strict, or ignore') + + +def roundrobin(*iterables): + """Visit input iterables in a cycle until each is exhausted. + + >>> list(roundrobin('ABC', 'D', 'EF')) + ['A', 'D', 'E', 'B', 'F', 'C'] + + This function produces the same output as :func:`interleave_longest`, but + may perform better for some inputs (in particular when the number of + iterables is small). + + """ + # Algorithm credited to George Sakkis + iterators = map(iter, iterables) + for num_active in range(len(iterables), 0, -1): + iterators = cycle(islice(iterators, num_active)) + yield from map(next, iterators) + + +def partition(pred, iterable): + """ + Returns a 2-tuple of iterables derived from the input iterable. + The first yields the items that have ``pred(item) == False``. + The second yields the items that have ``pred(item) == True``. + + >>> is_odd = lambda x: x % 2 != 0 + >>> iterable = range(10) + >>> even_items, odd_items = partition(is_odd, iterable) + >>> list(even_items), list(odd_items) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + + """ + if pred is None: + pred = bool + + t1, t2, p = tee(iterable, 3) + p1, p2 = tee(map(pred, p)) + return (compress(t1, map(not_, p1)), compress(t2, p2)) + + +def powerset(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset([1, 2, 3])) + [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + + For a variant that efficiently yields actual :class:`set` instances, see + :func:`powerset_of_sets`. + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def unique_everseen(iterable, key=None): + """ + Yield unique elements, preserving order. + + >>> list(unique_everseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D'] + >>> list(unique_everseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'D'] + + Sequences with a mix of hashable and unhashable items can be used. + The function will be slower (i.e., `O(n^2)`) for unhashable items. + + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similarly, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element + + +def unique_justseen(iterable, key=None): + """Yields elements in order, ignoring serial duplicates + + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] + + """ + if key is None: + return map(itemgetter(0), groupby(iterable)) + + return map(next, map(itemgetter(1), groupby(iterable, key))) + + +def unique(iterable, key=None, reverse=False): + """Yields unique elements in sorted order. + + >>> list(unique([[1, 2], [3, 4], [1, 2]])) + [[1, 2], [3, 4]] + + *key* and *reverse* are passed to :func:`sorted`. + + >>> list(unique('ABBcCAD', str.casefold)) + ['A', 'B', 'c', 'D'] + >>> list(unique('ABBcCAD', str.casefold, reverse=True)) + ['D', 'c', 'B', 'A'] + + The elements in *iterable* need not be hashable, but they must be + comparable for sorting to work. + """ + sequenced = sorted(iterable, key=key, reverse=reverse) + return unique_justseen(sequenced, key=key) + + +def iter_except(func, exception, first=None): + """Yields results from a function repeatedly until an exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel + to end the loop. + + >>> l = [0, 1, 2] + >>> list(iter_except(l.pop, IndexError)) + [2, 1, 0] + + Multiple exceptions can be specified as a stopping condition: + + >>> l = [1, 2, 3, '...', 4, 5, 6] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [7, 6, 5] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [4, 3, 2] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [] + + """ + with suppress(exception): + if first is not None: + yield first() + while True: + yield func() + + +def first_true(iterable, default=None, pred=None): + """ + Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which + ``pred(item) == True`` . + + >>> first_true(range(10)) + 1 + >>> first_true(range(10), pred=lambda x: x > 5) + 6 + >>> first_true(range(10), default='missing', pred=lambda x: x > 9) + 'missing' + + """ + return next(filter(pred, iterable), default) + + +def random_product(*args, repeat=1): + """Draw an item at random from each of the input iterables. + + >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP + ('c', 3, 'Z') + + If *repeat* is provided as a keyword argument, that many items will be + drawn from each iterable. + + >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP + ('a', 2, 'd', 3) + + This equivalent to taking a random selection from + ``itertools.product(*args, repeat=repeat)``. + + """ + pools = [tuple(pool) for pool in args] * repeat + return tuple(choice(pool) for pool in pools) + + +def random_permutation(iterable, r=None): + """Return a random *r* length permutation of the elements in *iterable*. + + If *r* is not specified or is ``None``, then *r* defaults to the length of + *iterable*. + + >>> random_permutation(range(5)) # doctest:+SKIP + (3, 4, 0, 1, 2) + + This equivalent to taking a random selection from + ``itertools.permutations(iterable, r)``. + + """ + pool = tuple(iterable) + r = len(pool) if r is None else r + return tuple(sample(pool, r)) + + +def random_combination(iterable, r): + """Return a random *r* length subsequence of the elements in *iterable*. + + >>> random_combination(range(5), 3) # doctest:+SKIP + (2, 3, 4) + + This equivalent to taking a random selection from + ``itertools.combinations(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(sample(range(n), r)) + return tuple(pool[i] for i in indices) + + +def random_combination_with_replacement(iterable, r): + """Return a random *r* length subsequence of elements in *iterable*, + allowing individual elements to be repeated. + + >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP + (0, 0, 1, 2, 2) + + This equivalent to taking a random selection from + ``itertools.combinations_with_replacement(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(randrange(n) for i in range(r)) + return tuple(pool[i] for i in indices) + + +def nth_combination(iterable, r, index): + """Equivalent to ``list(combinations(iterable, r))[index]``. + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`nth_combination` computes the subsequence at + sort position *index* directly, without computing the previous + subsequences. + + >>> nth_combination(range(5), 3, 5) + (0, 3, 4) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = 1 + k = min(r, n - r) + for i in range(1, k + 1): + c = c * (n - k + i) // i + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + + return tuple(result) + + +def prepend(value, iterator): + """Yield *value*, followed by the elements in *iterator*. + + >>> value = '0' + >>> iterator = ['1', '2', '3'] + >>> list(prepend(value, iterator)) + ['0', '1', '2', '3'] + + To prepend multiple values, see :func:`itertools.chain` + or :func:`value_chain`. + + """ + return chain([value], iterator) + + +def convolve(signal, kernel): + """Discrete linear convolution of two iterables. + Equivalent to polynomial multiplication. + + For example, multiplying ``(x² -x - 20)`` by ``(x - 3)`` + gives ``(x³ -4x² -17x + 60)``. + + >>> list(convolve([1, -1, -20], [1, -3])) + [1, -4, -17, 60] + + Examples of popular kinds of kernels: + + * The kernel ``[0.25, 0.25, 0.25, 0.25]`` computes a moving average. + For image data, this blurs the image and reduces noise. + * The kernel ``[1/2, 0, -1/2]`` estimates the first derivative of + a function evaluated at evenly spaced inputs. + * The kernel ``[1, -2, 1]`` estimates the second derivative of a + function evaluated at evenly spaced inputs. + + Convolutions are mathematically commutative; however, the inputs are + evaluated differently. The signal is consumed lazily and can be + infinite. The kernel is fully consumed before the calculations begin. + + Supports all numeric types: int, float, complex, Decimal, Fraction. + + References: + + * Article: https://betterexplained.com/articles/intuitive-convolution/ + * Video by 3Blue1Brown: https://www.youtube.com/watch?v=KuXjwB4LzSA + + """ + # This implementation comes from an older version of the itertools + # documentation. While the newer implementation is a bit clearer, + # this one was kept because the inlined window logic is faster + # and it avoids an unnecessary deque-to-tuple conversion. + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n - 1)): + window.append(x) + yield _sumprod(kernel, window) + + +def before_and_after(predicate, it): + """A variant of :func:`takewhile` that allows complete access to the + remainder of the iterator. + + >>> it = iter('ABCdEfGhI') + >>> all_upper, remainder = before_and_after(str.isupper, it) + >>> ''.join(all_upper) + 'ABC' + >>> ''.join(remainder) # takewhile() would lose the 'd' + 'dEfGhI' + + Note that the first iterator must be fully consumed before the second + iterator can generate valid results. + """ + trues, after = tee(it) + trues = compress(takewhile(predicate, trues), zip(after)) + return trues, after + + +def triplewise(iterable): + """Return overlapping triplets from *iterable*. + + >>> list(triplewise('ABCDE')) + [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E')] + + """ + # This deviates from the itertools documentation recipe - see + # https://github.com/more-itertools/more-itertools/issues/889 + t1, t2, t3 = tee(iterable, 3) + next(t3, None) + next(t3, None) + next(t2, None) + return zip(t1, t2, t3) + + +def _sliding_window_islice(iterable, n): + # Fast path for small, non-zero values of n. + iterators = tee(iterable, n) + for i, iterator in enumerate(iterators): + next(islice(iterator, i, i), None) + return zip(*iterators) + + +def _sliding_window_deque(iterable, n): + # Normal path for other values of n. + iterator = iter(iterable) + window = deque(islice(iterator, n - 1), maxlen=n) + for x in iterator: + window.append(x) + yield tuple(window) + + +def sliding_window(iterable, n): + """Return a sliding window of width *n* over *iterable*. + + >>> list(sliding_window(range(6), 4)) + [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)] + + If *iterable* has fewer than *n* items, then nothing is yielded: + + >>> list(sliding_window(range(3), 4)) + [] + + For a variant with more features, see :func:`windowed`. + """ + if n > 20: + return _sliding_window_deque(iterable, n) + elif n > 2: + return _sliding_window_islice(iterable, n) + elif n == 2: + return pairwise(iterable) + elif n == 1: + return zip(iterable) + else: + raise ValueError(f'n should be at least one, not {n}') + + +def subslices(iterable): + """Return all contiguous non-empty subslices of *iterable*. + + >>> list(subslices('ABC')) + [['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']] + + This is similar to :func:`substrings`, but emits items in a different + order. + """ + seq = list(iterable) + slices = starmap(slice, combinations(range(len(seq) + 1), 2)) + return map(getitem, repeat(seq), slices) + + +def polynomial_from_roots(roots): + """Compute a polynomial's coefficients from its roots. + + >>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3) + >>> polynomial_from_roots(roots) # x³ - 4 x² - 17 x + 60 + [1, -4, -17, 60] + + Note that polynomial coefficients are specified in descending power order. + + Supports all numeric types: int, float, complex, Decimal, Fraction. + """ + + # This recipe differs from the one in itertools docs in that it + # applies list() after each call to convolve(). This avoids + # hitting stack limits with nested generators. + + poly = [1] + for root in roots: + poly = list(convolve(poly, (1, -root))) + return poly + + +def iter_index(iterable, value, start=0, stop=None): + """Yield the index of each place in *iterable* that *value* occurs, + beginning with index *start* and ending before index *stop*. + + + >>> list(iter_index('AABCADEAF', 'A')) + [0, 1, 4, 7] + >>> list(iter_index('AABCADEAF', 'A', 1)) # start index is inclusive + [1, 4, 7] + >>> list(iter_index('AABCADEAF', 'A', 1, 7)) # stop index is not inclusive + [1, 4] + + The behavior for non-scalar *values* matches the built-in Python types. + + >>> list(iter_index('ABCDABCD', 'AB')) + [0, 4] + >>> list(iter_index([0, 1, 2, 3, 0, 1, 2, 3], [0, 1])) + [] + >>> list(iter_index([[0, 1], [2, 3], [0, 1], [2, 3]], [0, 1])) + [0, 2] + + See :func:`locate` for a more general means of finding the indexes + associated with particular values. + + """ + seq_index = getattr(iterable, 'index', None) + if seq_index is None: + # Slow path for general iterables + iterator = islice(iterable, start, stop) + for i, element in enumerate(iterator, start): + if element is value or element == value: + yield i + else: + # Fast path for sequences + stop = len(iterable) if stop is None else stop + i = start - 1 + with suppress(ValueError): + while True: + yield (i := seq_index(value, i + 1, stop)) + + +def sieve(n): + """Yield the primes less than n. + + >>> list(sieve(30)) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + + """ + # This implementation comes from an older version of the itertools + # documentation. The newer implementation is easier to read but is + # less lazy. + if n > 2: + yield 2 + start = 3 + data = bytearray((0, 1)) * (n // 2) + for p in iter_index(data, 1, start, stop=isqrt(n) + 1): + yield from iter_index(data, 1, start, p * p) + data[p * p : n : p + p] = bytes(len(range(p * p, n, p + p))) + start = p * p + yield from iter_index(data, 1, start) + + +def _batched(iterable, n, *, strict=False): # pragma: no cover + """Batch data into tuples of length *n*. If the number of items in + *iterable* is not divisible by *n*: + * The last batch will be shorter if *strict* is ``False``. + * :exc:`ValueError` will be raised if *strict* is ``True``. + + >>> list(batched('ABCDEFG', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)] + + On Python 3.13 and above, this is an alias for :func:`itertools.batched`. + """ + if n < 1: + raise ValueError('n must be at least one') + iterator = iter(iterable) + while batch := tuple(islice(iterator, n)): + if strict and len(batch) != n: + raise ValueError('batched(): incomplete batch') + yield batch + + +if hexversion >= 0x30D00A2: # pragma: no cover + from itertools import batched as itertools_batched + + def batched(iterable, n, *, strict=False): + return itertools_batched(iterable, n, strict=strict) + + batched.__doc__ = _batched.__doc__ +else: # pragma: no cover + batched = _batched + + +def transpose(it): + """Swap the rows and columns of the input matrix. + + >>> list(transpose([(1, 2, 3), (11, 22, 33)])) + [(1, 11), (2, 22), (3, 33)] + + The caller should ensure that the dimensions of the input are compatible. + If the input is empty, no output will be produced. + """ + return _zip_strict(*it) + + +def _is_scalar(value, stringlike=(str, bytes)): + "Scalars are bytes, strings, and non-iterables." + try: + iter(value) + except TypeError: + return True + return isinstance(value, stringlike) + + +def _flatten_tensor(tensor): + "Depth-first iterator over scalars in a tensor." + iterator = iter(tensor) + while True: + try: + value = next(iterator) + except StopIteration: + return iterator + iterator = chain((value,), iterator) + if _is_scalar(value): + return iterator + iterator = chain.from_iterable(iterator) + + +def reshape(matrix, shape): + """Change the shape of a *matrix*. + + If *shape* is an integer, the matrix must be two dimensional + and the shape is interpreted as the desired number of columns: + + >>> matrix = [(0, 1), (2, 3), (4, 5)] + >>> cols = 3 + >>> list(reshape(matrix, cols)) + [(0, 1, 2), (3, 4, 5)] + + If *shape* is a tuple (or other iterable), the input matrix can have + any number of dimensions. It will first be flattened and then rebuilt + to the desired shape which can also be multidimensional: + + >>> matrix = [(0, 1), (2, 3), (4, 5)] # Start with a 3 x 2 matrix + + >>> list(reshape(matrix, (2, 3))) # Make a 2 x 3 matrix + [(0, 1, 2), (3, 4, 5)] + + >>> list(reshape(matrix, (6,))) # Make a vector of length six + [0, 1, 2, 3, 4, 5] + + >>> list(reshape(matrix, (2, 1, 3, 1))) # Make 2 x 1 x 3 x 1 tensor + [(((0,), (1,), (2,)),), (((3,), (4,), (5,)),)] + + Each dimension is assumed to be uniform, either all arrays or all scalars. + Flattening stops when the first value in a dimension is a scalar. + Scalars are bytes, strings, and non-iterables. + The reshape iterator stops when the requested shape is complete + or when the input is exhausted, whichever comes first. + + """ + if isinstance(shape, int): + return batched(chain.from_iterable(matrix), shape) + first_dim, *dims = shape + scalar_stream = _flatten_tensor(matrix) + reshaped = reduce(batched, reversed(dims), scalar_stream) + return islice(reshaped, first_dim) + + +def matmul(m1, m2): + """Multiply two matrices. + + >>> list(matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)])) + [(49, 80), (41, 60)] + + The caller should ensure that the dimensions of the input matrices are + compatible with each other. + + Supports all numeric types: int, float, complex, Decimal, Fraction. + """ + n = len(m2[0]) + return batched(starmap(_sumprod, product(m1, transpose(m2))), n) + + +def _factor_pollard(n): + # Return a factor of n using Pollard's rho algorithm. + # Efficient when n is odd and composite. + for b in range(1, n): + x = y = 2 + d = 1 + while d == 1: + x = (x * x + b) % n + y = (y * y + b) % n + y = (y * y + b) % n + d = gcd(x - y, n) + if d != n: + return d + raise ValueError('prime or under 5') # pragma: no cover + + +_primes_below_211 = tuple(sieve(211)) + + +def factor(n): + """Yield the prime factors of n. + + >>> list(factor(360)) + [2, 2, 2, 3, 3, 5] + + Finds small factors with trial division. Larger factors are + either verified as prime with ``is_prime`` or split into + smaller factors with Pollard's rho algorithm. + """ + + # Corner case reduction + if n < 2: + return + + # Trial division reduction + for prime in _primes_below_211: + while not n % prime: + yield prime + n //= prime + + # Pollard's rho reduction + primes = [] + todo = [n] if n > 1 else [] + for n in todo: + if n < 211**2 or is_prime(n): + primes.append(n) + else: + fact = _factor_pollard(n) + todo += (fact, n // fact) + yield from sorted(primes) + + +def polynomial_eval(coefficients, x): + """Evaluate a polynomial at a specific value. + + Computes with better numeric stability than Horner's method. + + Evaluate ``x^3 - 4 * x^2 - 17 * x + 60`` at ``x = 2.5``: + + >>> coefficients = [1, -4, -17, 60] + >>> x = 2.5 + >>> polynomial_eval(coefficients, x) + 8.125 + + Note that polynomial coefficients are specified in descending power order. + + Supports all numeric types: int, float, complex, Decimal, Fraction. + """ + n = len(coefficients) + if n == 0: + return type(x)(0) + powers = map(pow, repeat(x), reversed(range(n))) + return _sumprod(coefficients, powers) + + +def sum_of_squares(it): + """Return the sum of the squares of the input values. + + >>> sum_of_squares([10, 20, 30]) + 1400 + + Supports all numeric types: int, float, complex, Decimal, Fraction. + """ + return _sumprod(*tee(it)) + + +def polynomial_derivative(coefficients): + """Compute the first derivative of a polynomial. + + Evaluate the derivative of ``x³ - 4 x² - 17 x + 60``: + + >>> coefficients = [1, -4, -17, 60] + >>> derivative_coefficients = polynomial_derivative(coefficients) + >>> derivative_coefficients + [3, -8, -17] + + Note that polynomial coefficients are specified in descending power order. + + Supports all numeric types: int, float, complex, Decimal, Fraction. + """ + n = len(coefficients) + powers = reversed(range(1, n)) + return list(map(mul, coefficients, powers)) + + +def totient(n): + """Return the count of natural numbers up to *n* that are coprime with *n*. + + Euler's totient function φ(n) gives the number of totatives. + Totative are integers k in the range 1 ≤ k ≤ n such that gcd(n, k) = 1. + + >>> n = 9 + >>> totient(n) + 6 + + >>> totatives = [x for x in range(1, n) if gcd(n, x) == 1] + >>> totatives + [1, 2, 4, 5, 7, 8] + >>> len(totatives) + 6 + + Reference: https://en.wikipedia.org/wiki/Euler%27s_totient_function + + """ + for prime in set(factor(n)): + n -= n // prime + return n + + +# Miller–Rabin primality test: https://oeis.org/A014233 +_perfect_tests = [ + (2047, (2,)), + (9080191, (31, 73)), + (4759123141, (2, 7, 61)), + (1122004669633, (2, 13, 23, 1662803)), + (2152302898747, (2, 3, 5, 7, 11)), + (3474749660383, (2, 3, 5, 7, 11, 13)), + (18446744073709551616, (2, 325, 9375, 28178, 450775, 9780504, 1795265022)), + ( + 3317044064679887385961981, + (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41), + ), +] + + +@lru_cache +def _shift_to_odd(n): + 'Return s, d such that 2**s * d == n' + s = ((n - 1) ^ n).bit_length() - 1 + d = n >> s + assert (1 << s) * d == n and d & 1 and s >= 0 + return s, d + + +def _strong_probable_prime(n, base): + assert (n > 2) and (n & 1) and (2 <= base < n) + + s, d = _shift_to_odd(n - 1) + + x = pow(base, d, n) + if x == 1 or x == n - 1: + return True + + for _ in range(s - 1): + x = x * x % n + if x == n - 1: + return True + + return False + + +# Separate instance of Random() that doesn't share state +# with the default user instance of Random(). +_private_randrange = random.Random().randrange + + +def is_prime(n): + """Return ``True`` if *n* is prime and ``False`` otherwise. + + Basic examples: + + >>> is_prime(37) + True + >>> is_prime(3 * 13) + False + >>> is_prime(18_446_744_073_709_551_557) + True + + Find the next prime over one billion: + + >>> next(filter(is_prime, count(10**9))) + 1000000007 + + Generate random primes up to 200 bits and up to 60 decimal digits: + + >>> from random import seed, randrange, getrandbits + >>> seed(18675309) + + >>> next(filter(is_prime, map(getrandbits, repeat(200)))) + 893303929355758292373272075469392561129886005037663238028407 + + >>> next(filter(is_prime, map(randrange, repeat(10**60)))) + 269638077304026462407872868003560484232362454342414618963649 + + This function is exact for values of *n* below 10**24. For larger inputs, + the probabilistic Miller-Rabin primality test has a less than 1 in 2**128 + chance of a false positive. + """ + + if n < 17: + return n in {2, 3, 5, 7, 11, 13} + + if not (n & 1 and n % 3 and n % 5 and n % 7 and n % 11 and n % 13): + return False + + for limit, bases in _perfect_tests: + if n < limit: + break + else: + bases = (_private_randrange(2, n - 1) for i in range(64)) + + return all(_strong_probable_prime(n, base) for base in bases) + + +def loops(n): + """Returns an iterable with *n* elements for efficient looping. + Like ``range(n)`` but doesn't create integers. + + >>> i = 0 + >>> for _ in loops(5): + ... i += 1 + >>> i + 5 + + """ + return repeat(None, n) + + +def multinomial(*counts): + """Number of distinct arrangements of a multiset. + + The expression ``multinomial(3, 4, 2)`` has several equivalent + interpretations: + + * In the expansion of ``(a + b + c)⁹``, the coefficient of the + ``a³b⁴c²`` term is 1260. + + * There are 1260 distinct ways to arrange 9 balls consisting of 3 reds, 4 + greens, and 2 blues. + + * There are 1260 unique ways to place 9 distinct objects into three bins + with sizes 3, 4, and 2. + + The :func:`multinomial` function computes the length of + :func:`distinct_permutations`. For example, there are 83,160 distinct + anagrams of the word "abracadabra": + + >>> from more_itertools import distinct_permutations, ilen + >>> ilen(distinct_permutations('abracadabra')) + 83160 + + This can be computed directly from the letter counts, 5a 2b 2r 1c 1d: + + >>> from collections import Counter + >>> list(Counter('abracadabra').values()) + [5, 2, 2, 1, 1] + >>> multinomial(5, 2, 2, 1, 1) + 83160 + + A binomial coefficient is a special case of multinomial where there are + only two categories. For example, the number of ways to arrange 12 balls + with 5 reds and 7 blues is ``multinomial(5, 7)`` or ``math.comb(12, 5)``. + + Likewise, factorial is a special case of multinomial where + the multiplicities are all just 1 so that + ``multinomial(1, 1, 1, 1, 1, 1, 1) == math.factorial(7)``. + + Reference: https://en.wikipedia.org/wiki/Multinomial_theorem + + """ + return prod(map(comb, accumulate(counts), counts)) + + +def _running_median_minheap_and_maxheap(iterator): # pragma: no cover + "Non-windowed running_median() for Python 3.14+" + + read = iterator.__next__ + lo = [] # max-heap + hi = [] # min-heap (same size as or one smaller than lo) + + with suppress(StopIteration): + while True: + heappush_max(lo, heappushpop(hi, read())) + yield lo[0] + + heappush(hi, heappushpop_max(lo, read())) + yield (lo[0] + hi[0]) / 2 + + +def _running_median_minheap_only(iterator): # pragma: no cover + "Backport of non-windowed running_median() for Python 3.13 and prior." + + read = iterator.__next__ + lo = [] # max-heap (actually a minheap with negated values) + hi = [] # min-heap (same size as or one smaller than lo) + + with suppress(StopIteration): + while True: + heappush(lo, -heappushpop(hi, read())) + yield -lo[0] + + heappush(hi, -heappushpop(lo, -read())) + yield (hi[0] - lo[0]) / 2 + + +def _running_median_windowed(iterator, maxlen): + "Yield median of values in a sliding window." + + window = deque() + ordered = [] + + for x in iterator: + window.append(x) + insort(ordered, x) + + if len(ordered) > maxlen: + i = bisect_left(ordered, window.popleft()) + del ordered[i] + + n = len(ordered) + m = n // 2 + yield ordered[m] if n & 1 else (ordered[m - 1] + ordered[m]) / 2 + + +def running_median(iterable, *, maxlen=None): + """Cumulative median of values seen so far or values in a sliding window. + + Set *maxlen* to a positive integer to specify the maximum size + of the sliding window. The default of *None* is equivalent to + an unbounded window. + + For example: + + >>> list(running_median([5.0, 9.0, 4.0, 12.0, 8.0, 9.0])) + [5.0, 7.0, 5.0, 7.0, 8.0, 8.5] + >>> list(running_median([5.0, 9.0, 4.0, 12.0, 8.0, 9.0], maxlen=3)) + [5.0, 7.0, 5.0, 9.0, 8.0, 9.0] + + Supports numeric types such as int, float, Decimal, and Fraction, + but not complex numbers which are unorderable. + + On version Python 3.13 and prior, max-heaps are simulated with + negative values. The negation causes Decimal inputs to apply context + rounding, making the results slightly different than that obtained + by statistics.median(). + """ + + iterator = iter(iterable) + + if maxlen is not None: + maxlen = index(maxlen) + if maxlen <= 0: + raise ValueError('Window size should be positive') + return _running_median_windowed(iterator, maxlen) + + if not _max_heap_available: + return _running_median_minheap_only(iterator) # pragma: no cover + + return _running_median_minheap_and_maxheap(iterator) # pragma: no cover diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.pyi b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.pyi new file mode 100644 index 0000000..de3d0a1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/more_itertools/recipes.pyi @@ -0,0 +1,205 @@ +"""Stubs for more_itertools.recipes""" + +from __future__ import annotations + +from collections.abc import Iterable, Iterator, Sequence +from decimal import Decimal +from fractions import Fraction +from typing import ( + Any, + Callable, + TypeVar, + overload, +) + +__all__ = [ + 'all_equal', + 'batched', + 'before_and_after', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'factor', + 'flatten', + 'grouper', + 'is_prime', + 'iter_except', + 'iter_index', + 'loops', + 'matmul', + 'multinomial', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'polynomial_eval', + 'polynomial_from_roots', + 'polynomial_derivative', + 'powerset', + 'prepend', + 'quantify', + 'reshape', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'running_median', + 'sieve', + 'sliding_window', + 'subslices', + 'sum_of_squares', + 'tabulate', + 'tail', + 'take', + 'totient', + 'transpose', + 'triplewise', + 'unique', + 'unique_everseen', + 'unique_justseen', +] + +# Type and type variable definitions +_T = TypeVar('_T') +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_U = TypeVar('_U') +_NumberT = TypeVar("_NumberT", float, Decimal, Fraction) + +def take(n: int, iterable: Iterable[_T]) -> list[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[_T], n: int | None = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> _T | None: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... +def all_equal( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[_T | None]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[_T | None]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[_T1], vec2: Iterable[_T2]) -> Any: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: int | None = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T]]: ... +def grouper( + iterable: Iterable[_T], + n: int, + incomplete: str = ..., + fillvalue: _U = ..., +) -> Iterator[tuple[_T | _U, ...]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Callable[[_T], object] | None, iterable: Iterable[_T] +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Callable[[_T], object] | None = ... +) -> Iterator[_T]: ... +def unique( + iterable: Iterable[_T], + key: Callable[[_T], object] | None = ..., + reverse: bool = False, +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: type[BaseException] | tuple[type[BaseException], ...], + first: None = ..., +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: type[BaseException] | tuple[type[BaseException], ...], + first: Callable[[], _U], +) -> Iterator[_T | _U]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Callable[[_T], object] | None = ... +) -> _T | None: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Callable[[_T], object] | None = ..., +) -> _T | _U: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: int | None = ... +) -> tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[_T | _U]: ... +def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... +def before_and_after( + predicate: Callable[[_T], bool], it: Iterable[_T] +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def triplewise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T, _T]]: ... +def sliding_window( + iterable: Iterable[_T], n: int +) -> Iterator[tuple[_T, ...]]: ... +def subslices(iterable: Iterable[_T]) -> Iterator[list[_T]]: ... +def polynomial_from_roots(roots: Sequence[_T]) -> list[_T]: ... +def iter_index( + iterable: Iterable[_T], + value: Any, + start: int | None = ..., + stop: int | None = ..., +) -> Iterator[int]: ... +def sieve(n: int) -> Iterator[int]: ... +def _batched( + iterable: Iterable[_T], n: int, *, strict: bool = False +) -> Iterator[tuple[_T, ...]]: ... + +batched = _batched + +def transpose( + it: Iterable[Iterable[_T]], +) -> Iterator[tuple[_T, ...]]: ... +@overload +def reshape( + matrix: Iterable[Iterable[_T]], shape: int +) -> Iterator[tuple[_T, ...]]: ... +@overload +def reshape(matrix: Iterable[Any], shape: Iterable[int]) -> Iterator[Any]: ... +def matmul(m1: Sequence[_T], m2: Sequence[_T]) -> Iterator[tuple[_T]]: ... +def _factor_trial(n: int) -> Iterator[int]: ... +def _factor_pollard(n: int) -> int: ... +def factor(n: int) -> Iterator[int]: ... +def polynomial_eval(coefficients: Sequence[_T], x: _U) -> _U: ... +def sum_of_squares(it: Iterable[_T]) -> _T: ... +def polynomial_derivative(coefficients: Sequence[_T]) -> list[_T]: ... +def totient(n: int) -> int: ... +def _shift_to_odd(n: int) -> tuple[int, int]: ... +def _strong_probable_prime(n: int, base: int) -> bool: ... +def is_prime(n: int) -> bool: ... +def loops(n: int) -> Iterator[None]: ... +def multinomial(*counts: int) -> int: ... +def running_median( + iterable: Iterable[_NumberT], *, maxlen: int | None = ... +) -> Iterator[_NumberT]: ... diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/INSTALLER new file mode 100644 index 0000000..5c69047 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/METADATA new file mode 100644 index 0000000..3200e60 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/METADATA @@ -0,0 +1,107 @@ +Metadata-Version: 2.4 +Name: packaging +Version: 26.0 +Summary: Core utilities for Python packages +Author-email: Donald Stufft +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-Expression: Apache-2.0 OR BSD-2-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Project-URL: Documentation, https://packaging.pypa.io/ +Project-URL: Source, https://github.com/pypa/packaging + +packaging +========= + +.. start-intro + +Reusable core utilities for various Python Packaging +`interoperability specifications `_. + +This library provides utilities that implement the interoperability +specifications which have clearly one correct behaviour (eg: :pep:`440`) +or benefit greatly from having a single shared implementation (eg: :pep:`425`). + +.. end-intro + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, metadata, lockfiles, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Requirements +- Tags +- Metadata +- Lockfiles +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +The ``packaging`` library uses calendar-based versioning (``YY.N``). + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join ``#pypa`` on Freenode to ask questions or get involved. + + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/RECORD new file mode 100644 index 0000000..38cdeaf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/RECORD @@ -0,0 +1,26 @@ +packaging-26.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +packaging-26.0.dist-info/METADATA,sha256=M2K7fWom2iliuo2qpHhc0LrKwhq6kIoRlcyPWVgKJlo,3309 +packaging-26.0.dist-info/RECORD,, +packaging-26.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging-26.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +packaging-26.0.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-26.0.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-26.0.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging/__init__.py,sha256=y4lVbpeBzCGk-IPDw5BGBZ_b0P3ukEEJZAbGYc6Ey8c,494 +packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211 +packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559 +packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707 +packaging/_parser.py,sha256=U_DajsEx2VoC_F46fSVV3hDKNCWoQYkPkasO3dld0ig,10518 +packaging/_structures.py,sha256=Hn49Ta8zV9Wo8GiCL8Nl2ARZY983Un3pruZGVNldPwE,1514 +packaging/_tokenizer.py,sha256=M8EwNIdXeL9NMFuFrQtiOKwjka_xFx8KjRQnfE8O_z8,5421 +packaging/licenses/__init__.py,sha256=TwXLHZCXwSgdFwRLPxW602T6mSieunSFHM6fp8pgW78,5819 +packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122 +packaging/markers.py,sha256=ZX-cLvW1S3cZcEc0fHI4z7zSx5U2T19yMpDP_mE-CYw,12771 +packaging/metadata.py,sha256=CWVZpN_HfoYMSSDuCP7igOvGgqA9AOmpW8f3qTisfnc,39360 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/pylock.py,sha256=-R1uNfJ4PaLto7Mg62YsGOHgvskuiIEqPwxOywl42Jk,22537 +packaging/requirements.py,sha256=PMCAWD8aNMnVD-6uZMedhBuAVX2573eZ4yPBLXmz04I,2870 +packaging/specifiers.py,sha256=EPNPimY_zFivthv1vdjZYz5IqkKGsnKR2yKh-EVyvZw,40797 +packaging/tags.py,sha256=cXLV1pJD3UtJlDg7Wz3zrfdQhRZqr8jumSAKKAAd2xE,22856 +packaging/utils.py,sha256=N4c6oZzFJy6klTZ3AnkNz7sSkJesuFWPp68LA3B5dAo,5040 +packaging/version.py,sha256=7XWlL2IDYLwDYC0ht6cFEhapLwLWbmyo4rb7sEFj0x8,23272 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..6f62d44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,177 @@ + + 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 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000..42ce7b7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/__init__.py new file mode 100644 index 0000000..21695a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/__init__.py @@ -0,0 +1,15 @@ +# 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. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "26.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = f"2014 {__author__}" diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_elffile.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_elffile.py new file mode 100644 index 0000000..497b064 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +from __future__ import annotations + +import enum +import os +import struct +from typing import IO + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error as e: + raise ELFInvalid("unable to parse identification") from e + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + 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, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError as e: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})" + ) from e + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> str | None: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_manylinux.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_manylinux.py new file mode 100644 index 0000000..0e79e8a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_manylinux.py @@ -0,0 +1,262 @@ +from __future__ import annotations + +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import Generator, Iterator, NamedTuple, Sequence + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + +_ALLOWED_ARCHS = { + "x86_64", + "aarch64", + "ppc64", + "ppc64le", + "s390x", + "loongarch64", + "riscv64", +} + + +# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` +# as the type for `path` until then. +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: + if "armv7l" in archs: + return _is_linux_armhf(executable) + if "i686" in archs: + return _is_linux_i686(executable) + return any(arch in _ALLOWED_ARCHS for arch in archs) + + +# 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() -> str | None: + """ + 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: + # Should be a string like "glibc 2.17". + version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + 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() -> str | None: + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes # noqa: PLC0415 + 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() -> str | None: + """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) -> _GLibCVersion: + """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( + f"Expected glibc version with 2 components major.minor, got: {version_str}", + RuntimeWarning, + stacklevel=2, + ) + return _GLibCVersion(-1, -1) + return _GLibCVersion(int(m.group("major")), int(m.group("minor"))) + + +@functools.lru_cache +def _get_glibc_version() -> _GLibCVersion: + version_str = _glibc_version_string() + if version_str is None: + return _GLibCVersion(-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(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: PLC0415 + 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) and hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12) and hasattr( + _manylinux, "manylinux2010_compatible" + ): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17) and hasattr( + _manylinux, "manylinux2014_compatible" + ): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + _GLibCVersion(2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + _GLibCVersion(2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + _GLibCVersion(2, 5): "manylinux1", +} + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate manylinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and 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 manylinux-compatible. + + :returns: An iterator of compatible manylinux tags. + """ + if not _have_compatible_abi(sys.executable, archs): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if set(archs) & {"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 arch in archs: + 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) + if _is_compatible(arch, glibc_version): + yield "manylinux_{}_{}_{}".format(*glibc_version, arch) + + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version): + yield f"{legacy_tag}_{arch}" diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_musllinux.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_musllinux.py new file mode 100644 index 0000000..4e8116a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_musllinux.py @@ -0,0 +1,85 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +from __future__ import annotations + +import functools +import re +import subprocess +import sys +from typing import Iterator, NamedTuple, Sequence + +from ._elffile import ELFFile + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> _MuslVersion | None: + 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) -> _MuslVersion | None: + """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 + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and 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 arch in archs: + 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/python3.12/site-packages/setuptools/_vendor/packaging/_parser.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_parser.py new file mode 100644 index 0000000..f6c1f5c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_parser.py @@ -0,0 +1,365 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains EBNF-inspired grammar representing +the implementation. +""" + +from __future__ import annotations + +import ast +from typing import List, Literal, NamedTuple, Sequence, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + __slots__ = ("value",) + + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({self.value!r})>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +class Value(Node): + __slots__ = () + + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +MarkerLogical = Literal["and", "or"] +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]] +MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: list[str] + specifier: str + marker: MarkerList | None + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> tuple[str, str, MarkerList | None]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=url_start, + expected="semicolon (after URL and whitespace)", + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + expected=( + "comma (within version specifier), semicolon (after version specifier)" + if specifier + else "semicolon (after name with no version specifier)" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, expected: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected {expected} or end", + span_start=span_start, + span_end=None, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> list[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens( + "LEFT_BRACKET", + "RIGHT_BRACKET", + around="extras", + ): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> list[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: list[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="version specifier", + ): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + span_start = tokenizer.position + parsed_specifiers += tokenizer.read().text + if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + tokenizer.raise_syntax_error( + ".* suffix can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position + 1, + ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: + retval = _parse_marker(tokenizer) + tokenizer.expect("END", expected="end of marker expression") + return retval + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="marker expression", + ): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503 + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if env_var in ("platform_python_implementation", "python_implementation"): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_structures.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_structures.py new file mode 100644 index 0000000..225e2ee --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_structures.py @@ -0,0 +1,69 @@ +# 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 + + +@typing.final +class InfinityType: + __slots__ = () + + 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() + + +@typing.final +class NegativeInfinityType: + __slots__ = () + + 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/python3.12/site-packages/setuptools/_vendor/packaging/_tokenizer.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_tokenizer.py new file mode 100644 index 0000000..e6d20dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/_tokenizer.py @@ -0,0 +1,193 @@ +from __future__ import annotations + +import contextlib +import re +from dataclasses import dataclass +from typing import Generator, Mapping, NoReturn + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return f"{self.message}\n {self.source}\n {marker}" + + +DEFAULT_RULES: dict[str, re.Pattern[str]] = { + "LEFT_PARENTHESIS": re.compile(r"\("), + "RIGHT_PARENTHESIS": re.compile(r"\)"), + "LEFT_BRACKET": re.compile(r"\["), + "RIGHT_BRACKET": re.compile(r"\]"), + "SEMICOLON": re.compile(r";"), + "COMMA": re.compile(r","), + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"), + "BOOLOP": re.compile(r"\b(or|and)\b"), + "IN": re.compile(r"\bin\b"), + "NOT": re.compile(r"\bnot\b"), + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extras? + |dependency_groups + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": re.compile(r"\@"), + "URL": re.compile(r"[^ \t]+"), + "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"), + "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"), + "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"), + "WS": re.compile(r"[ \t]+"), + "END": re.compile(r"$"), +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: Mapping[str, re.Pattern[str]], + ) -> None: + self.source = source + self.rules = rules + self.next_token: Token | None = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert self.next_token is None, ( + f"Cannot check for {name!r}, already have {self.next_token!r}" + ) + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: int | None = None, + span_end: int | None = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens( + self, open_token: str, close_token: str, *, around: str + ) -> Generator[None, None, None]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected matching {close_token} for {open_token}, after {around}", + span_start=open_position, + ) + + self.read() diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/__init__.py new file mode 100644 index 0000000..335b275 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/__init__.py @@ -0,0 +1,147 @@ +####################################################################################### +# +# Adapted from: +# https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py +# +# MIT License +# +# Copyright (c) 2017-present Ofek Lev +# +# 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. +# +# +# With additional allowance of arbitrary `LicenseRef-` identifiers, not just +# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`. +# +####################################################################################### +from __future__ import annotations + +import re +from typing import NewType, cast + +from ._spdx import EXCEPTIONS, LICENSES + +__all__ = [ + "InvalidLicenseExpression", + "NormalizedLicenseExpression", + "canonicalize_license_expression", +] + +license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$") + +NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) + + +class InvalidLicenseExpression(ValueError): + """Raised when a license-expression string is invalid + + >>> canonicalize_license_expression("invalid") + Traceback (most recent call last): + ... + packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid' + """ + + +def canonicalize_license_expression( + raw_license_expression: str, +) -> NormalizedLicenseExpression: + if not raw_license_expression: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + + # Pad any parentheses so tokenization can be achieved by merely splitting on + # whitespace. + license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") + licenseref_prefix = "LicenseRef-" + license_refs = { + ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] + for ref in license_expression.split() + if ref.lower().startswith(licenseref_prefix.lower()) + } + + # Normalize to lower case so we can look up licenses/exceptions + # and so boolean operators are Python-compatible. + license_expression = license_expression.lower() + + tokens = license_expression.split() + + # Rather than implementing a parenthesis/boolean logic parser, create an + # expression that Python can parse. Everything that is not involved with the + # grammar itself is replaced with the placeholder `False` and the resultant + # expression should become a valid Python expression. + python_tokens = [] + for token in tokens: + if token not in {"or", "and", "with", "(", ")"}: + python_tokens.append("False") + elif token == "with": + python_tokens.append("or") + elif ( + token == "(" + and python_tokens + and python_tokens[-1] not in {"or", "and", "("} + ) or (token == ")" and python_tokens and python_tokens[-1] == "("): + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + else: + python_tokens.append(token) + + python_expression = " ".join(python_tokens) + try: + compile(python_expression, "", "eval") + except SyntaxError: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) from None + + # Take a final pass to check for unknown licenses/exceptions. + normalized_tokens = [] + for token in tokens: + if token in {"or", "and", "with", "(", ")"}: + normalized_tokens.append(token.upper()) + continue + + if normalized_tokens and normalized_tokens[-1] == "WITH": + if token not in EXCEPTIONS: + message = f"Unknown license exception: {token!r}" + raise InvalidLicenseExpression(message) + + normalized_tokens.append(EXCEPTIONS[token]["id"]) + else: + if token.endswith("+"): + final_token = token[:-1] + suffix = "+" + else: + final_token = token + suffix = "" + + if final_token.startswith("licenseref-"): + if not license_ref_allowed.match(final_token): + message = f"Invalid licenseref: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(license_refs[final_token] + suffix) + else: + if final_token not in LICENSES: + message = f"Unknown license: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(LICENSES[final_token]["id"] + suffix) + + normalized_expression = " ".join(normalized_tokens) + + return cast( + "NormalizedLicenseExpression", + normalized_expression.replace("( ", "(").replace(" )", ")"), + ) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/_spdx.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/_spdx.py new file mode 100644 index 0000000..a277af2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/licenses/_spdx.py @@ -0,0 +1,799 @@ + +from __future__ import annotations + +from typing import TypedDict + +class SPDXLicense(TypedDict): + id: str + deprecated: bool + +class SPDXException(TypedDict): + id: str + deprecated: bool + + +VERSION = '3.27.0' + +LICENSES: dict[str, SPDXLicense] = { + '0bsd': {'id': '0BSD', 'deprecated': False}, + '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False}, + 'aal': {'id': 'AAL', 'deprecated': False}, + 'abstyles': {'id': 'Abstyles', 'deprecated': False}, + 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False}, + 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False}, + 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False}, + 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False}, + 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False}, + 'adsl': {'id': 'ADSL', 'deprecated': False}, + 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False}, + 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False}, + 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False}, + 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False}, + 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False}, + 'afmparse': {'id': 'Afmparse', 'deprecated': False}, + 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True}, + 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False}, + 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False}, + 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True}, + 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False}, + 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False}, + 'aladdin': {'id': 'Aladdin', 'deprecated': False}, + 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False}, + 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False}, + 'aml': {'id': 'AML', 'deprecated': False}, + 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False}, + 'ampas': {'id': 'AMPAS', 'deprecated': False}, + 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False}, + 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False}, + 'any-osi': {'id': 'any-OSI', 'deprecated': False}, + 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False}, + 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False}, + 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False}, + 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False}, + 'apafml': {'id': 'APAFML', 'deprecated': False}, + 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False}, + 'app-s2p': {'id': 'App-s2p', 'deprecated': False}, + 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False}, + 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False}, + 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False}, + 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False}, + 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False}, + 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False}, + 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False}, + 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False}, + 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False}, + 'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False}, + 'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False}, + 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False}, + 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False}, + 'baekmuk': {'id': 'Baekmuk', 'deprecated': False}, + 'bahyph': {'id': 'Bahyph', 'deprecated': False}, + 'barr': {'id': 'Barr', 'deprecated': False}, + 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False}, + 'beerware': {'id': 'Beerware', 'deprecated': False}, + 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False}, + 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False}, + 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False}, + 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False}, + 'blessing': {'id': 'blessing', 'deprecated': False}, + 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False}, + 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False}, + 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False}, + 'borceux': {'id': 'Borceux', 'deprecated': False}, + 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False}, + 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False}, + 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False}, + 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False}, + 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False}, + 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False}, + 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True}, + 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True}, + 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False}, + 'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False}, + 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False}, + 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False}, + 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False}, + 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False}, + 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False}, + 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False}, + 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False}, + 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False}, + 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False}, + 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False}, + 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False}, + 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False}, + 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False}, + 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False}, + 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False}, + 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False}, + 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False}, + 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False}, + 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False}, + 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False}, + 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False}, + 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False}, + 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False}, + 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False}, + 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False}, + 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False}, + 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False}, + 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True}, + 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False}, + 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False}, + 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False}, + 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False}, + 'caldera': {'id': 'Caldera', 'deprecated': False}, + 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False}, + 'catharon': {'id': 'Catharon', 'deprecated': False}, + 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False}, + 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False}, + 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False}, + 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False}, + 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False}, + 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False}, + 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False}, + 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False}, + 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False}, + 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False}, + 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False}, + 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False}, + 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False}, + 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False}, + 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False}, + 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False}, + 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False}, + 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False}, + 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False}, + 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False}, + 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False}, + 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False}, + 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False}, + 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False}, + 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False}, + 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False}, + 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False}, + 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False}, + 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False}, + 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False}, + 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False}, + 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False}, + 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False}, + 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False}, + 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False}, + 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False}, + 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False}, + 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False}, + 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False}, + 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False}, + 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False}, + 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False}, + 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False}, + 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False}, + 'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False}, + 'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False}, + 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False}, + 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False}, + 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False}, + 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False}, + 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False}, + 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False}, + 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False}, + 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False}, + 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False}, + 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False}, + 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False}, + 'cecill-b': {'id': 'CECILL-B', 'deprecated': False}, + 'cecill-c': {'id': 'CECILL-C', 'deprecated': False}, + 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False}, + 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False}, + 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False}, + 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False}, + 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False}, + 'cfitsio': {'id': 'CFITSIO', 'deprecated': False}, + 'check-cvs': {'id': 'check-cvs', 'deprecated': False}, + 'checkmk': {'id': 'checkmk', 'deprecated': False}, + 'clartistic': {'id': 'ClArtistic', 'deprecated': False}, + 'clips': {'id': 'Clips', 'deprecated': False}, + 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False}, + 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False}, + 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False}, + 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False}, + 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False}, + 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False}, + 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False}, + 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False}, + 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False}, + 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False}, + 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False}, + 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False}, + 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False}, + 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False}, + 'cronyx': {'id': 'Cronyx', 'deprecated': False}, + 'crossword': {'id': 'Crossword', 'deprecated': False}, + 'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False}, + 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False}, + 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False}, + 'cube': {'id': 'Cube', 'deprecated': False}, + 'curl': {'id': 'curl', 'deprecated': False}, + 'cve-tou': {'id': 'cve-tou', 'deprecated': False}, + 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False}, + 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False}, + 'diffmark': {'id': 'diffmark', 'deprecated': False}, + 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False}, + 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False}, + 'doc': {'id': 'DOC', 'deprecated': False}, + 'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False}, + 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False}, + 'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False}, + 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False}, + 'dotseqn': {'id': 'Dotseqn', 'deprecated': False}, + 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False}, + 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False}, + 'dsdp': {'id': 'DSDP', 'deprecated': False}, + 'dtoa': {'id': 'dtoa', 'deprecated': False}, + 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False}, + 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False}, + 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False}, + 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True}, + 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False}, + 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False}, + 'egenix': {'id': 'eGenix', 'deprecated': False}, + 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False}, + 'entessa': {'id': 'Entessa', 'deprecated': False}, + 'epics': {'id': 'EPICS', 'deprecated': False}, + 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False}, + 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False}, + 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False}, + 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False}, + 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False}, + 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False}, + 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False}, + 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False}, + 'eurosym': {'id': 'Eurosym', 'deprecated': False}, + 'fair': {'id': 'Fair', 'deprecated': False}, + 'fbm': {'id': 'FBM', 'deprecated': False}, + 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False}, + 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False}, + 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False}, + 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False}, + 'freeimage': {'id': 'FreeImage', 'deprecated': False}, + 'fsfap': {'id': 'FSFAP', 'deprecated': False}, + 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False}, + 'fsful': {'id': 'FSFUL', 'deprecated': False}, + 'fsfullr': {'id': 'FSFULLR', 'deprecated': False}, + 'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False}, + 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False}, + 'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False}, + 'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False}, + 'ftl': {'id': 'FTL', 'deprecated': False}, + 'furuseth': {'id': 'Furuseth', 'deprecated': False}, + 'fwlw': {'id': 'fwlw', 'deprecated': False}, + 'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False}, + 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False}, + 'gd': {'id': 'GD', 'deprecated': False}, + 'generic-xts': {'id': 'generic-xts', 'deprecated': False}, + 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True}, + 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False}, + 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False}, + 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False}, + 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False}, + 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True}, + 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False}, + 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False}, + 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False}, + 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False}, + 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True}, + 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False}, + 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False}, + 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False}, + 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False}, + 'giftware': {'id': 'Giftware', 'deprecated': False}, + 'gl2ps': {'id': 'GL2PS', 'deprecated': False}, + 'glide': {'id': 'Glide', 'deprecated': False}, + 'glulxe': {'id': 'Glulxe', 'deprecated': False}, + 'glwtpl': {'id': 'GLWTPL', 'deprecated': False}, + 'gnuplot': {'id': 'gnuplot', 'deprecated': False}, + 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True}, + 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True}, + 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False}, + 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False}, + 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True}, + 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True}, + 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False}, + 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False}, + 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True}, + 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True}, + 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True}, + 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True}, + 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True}, + 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True}, + 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False}, + 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False}, + 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True}, + 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False}, + 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False}, + 'gtkbook': {'id': 'gtkbook', 'deprecated': False}, + 'gutmann': {'id': 'Gutmann', 'deprecated': False}, + 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, + 'hdf5': {'id': 'HDF5', 'deprecated': False}, + 'hdparm': {'id': 'hdparm', 'deprecated': False}, + 'hidapi': {'id': 'HIDAPI', 'deprecated': False}, + 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False}, + 'hp-1986': {'id': 'HP-1986', 'deprecated': False}, + 'hp-1989': {'id': 'HP-1989', 'deprecated': False}, + 'hpnd': {'id': 'HPND', 'deprecated': False}, + 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False}, + 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False}, + 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False}, + 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False}, + 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False}, + 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, + 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False}, + 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, + 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, + 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False}, + 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, + 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, + 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False}, + 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False}, + 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False}, + 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False}, + 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False}, + 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False}, + 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False}, + 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False}, + 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False}, + 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False}, + 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False}, + 'icu': {'id': 'ICU', 'deprecated': False}, + 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False}, + 'ijg': {'id': 'IJG', 'deprecated': False}, + 'ijg-short': {'id': 'IJG-short', 'deprecated': False}, + 'imagemagick': {'id': 'ImageMagick', 'deprecated': False}, + 'imatix': {'id': 'iMatix', 'deprecated': False}, + 'imlib2': {'id': 'Imlib2', 'deprecated': False}, + 'info-zip': {'id': 'Info-ZIP', 'deprecated': False}, + 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False}, + 'innosetup': {'id': 'InnoSetup', 'deprecated': False}, + 'intel': {'id': 'Intel', 'deprecated': False}, + 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False}, + 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False}, + 'ipa': {'id': 'IPA', 'deprecated': False}, + 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False}, + 'isc': {'id': 'ISC', 'deprecated': False}, + 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False}, + 'jam': {'id': 'Jam', 'deprecated': False}, + 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False}, + 'jove': {'id': 'jove', 'deprecated': False}, + 'jpl-image': {'id': 'JPL-image', 'deprecated': False}, + 'jpnic': {'id': 'JPNIC', 'deprecated': False}, + 'json': {'id': 'JSON', 'deprecated': False}, + 'kastrup': {'id': 'Kastrup', 'deprecated': False}, + 'kazlib': {'id': 'Kazlib', 'deprecated': False}, + 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False}, + 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False}, + 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False}, + 'latex2e': {'id': 'Latex2e', 'deprecated': False}, + 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False}, + 'leptonica': {'id': 'Leptonica', 'deprecated': False}, + 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True}, + 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True}, + 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False}, + 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False}, + 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True}, + 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True}, + 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False}, + 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False}, + 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True}, + 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True}, + 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False}, + 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False}, + 'lgpllr': {'id': 'LGPLLR', 'deprecated': False}, + 'libpng': {'id': 'Libpng', 'deprecated': False}, + 'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False}, + 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False}, + 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False}, + 'libtiff': {'id': 'libtiff', 'deprecated': False}, + 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False}, + 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False}, + 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False}, + 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False}, + 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False}, + 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False}, + 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False}, + 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False}, + 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False}, + 'loop': {'id': 'LOOP', 'deprecated': False}, + 'lpd-document': {'id': 'LPD-document', 'deprecated': False}, + 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False}, + 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False}, + 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False}, + 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False}, + 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False}, + 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False}, + 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False}, + 'lsof': {'id': 'lsof', 'deprecated': False}, + 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False}, + 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False}, + 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False}, + 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False}, + 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False}, + 'magaz': {'id': 'magaz', 'deprecated': False}, + 'mailprio': {'id': 'mailprio', 'deprecated': False}, + 'makeindex': {'id': 'MakeIndex', 'deprecated': False}, + 'man2html': {'id': 'man2html', 'deprecated': False}, + 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False}, + 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False}, + 'metamail': {'id': 'metamail', 'deprecated': False}, + 'minpack': {'id': 'Minpack', 'deprecated': False}, + 'mips': {'id': 'MIPS', 'deprecated': False}, + 'miros': {'id': 'MirOS', 'deprecated': False}, + 'mit': {'id': 'MIT', 'deprecated': False}, + 'mit-0': {'id': 'MIT-0', 'deprecated': False}, + 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False}, + 'mit-click': {'id': 'MIT-Click', 'deprecated': False}, + 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False}, + 'mit-enna': {'id': 'MIT-enna', 'deprecated': False}, + 'mit-feh': {'id': 'MIT-feh', 'deprecated': False}, + 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False}, + 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False}, + 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False}, + 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False}, + 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False}, + 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False}, + 'mitnfa': {'id': 'MITNFA', 'deprecated': False}, + 'mmixware': {'id': 'MMIXware', 'deprecated': False}, + 'motosoto': {'id': 'Motosoto', 'deprecated': False}, + 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False}, + 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False}, + 'mpich2': {'id': 'mpich2', 'deprecated': False}, + 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False}, + 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False}, + 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False}, + 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False}, + 'mplus': {'id': 'mplus', 'deprecated': False}, + 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False}, + 'ms-pl': {'id': 'MS-PL', 'deprecated': False}, + 'ms-rl': {'id': 'MS-RL', 'deprecated': False}, + 'mtll': {'id': 'MTLL', 'deprecated': False}, + 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False}, + 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False}, + 'multics': {'id': 'Multics', 'deprecated': False}, + 'mup': {'id': 'Mup', 'deprecated': False}, + 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False}, + 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False}, + 'naumen': {'id': 'Naumen', 'deprecated': False}, + 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False}, + 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False}, + 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, + 'ncl': {'id': 'NCL', 'deprecated': False}, + 'ncsa': {'id': 'NCSA', 'deprecated': False}, + 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True}, + 'netcdf': {'id': 'NetCDF', 'deprecated': False}, + 'newsletr': {'id': 'Newsletr', 'deprecated': False}, + 'ngpl': {'id': 'NGPL', 'deprecated': False}, + 'ngrep': {'id': 'ngrep', 'deprecated': False}, + 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False}, + 'nist-pd': {'id': 'NIST-PD', 'deprecated': False}, + 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False}, + 'nist-software': {'id': 'NIST-Software', 'deprecated': False}, + 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False}, + 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False}, + 'nlpl': {'id': 'NLPL', 'deprecated': False}, + 'nokia': {'id': 'Nokia', 'deprecated': False}, + 'nosl': {'id': 'NOSL', 'deprecated': False}, + 'noweb': {'id': 'Noweb', 'deprecated': False}, + 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False}, + 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False}, + 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False}, + 'nrl': {'id': 'NRL', 'deprecated': False}, + 'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False}, + 'ntp': {'id': 'NTP', 'deprecated': False}, + 'ntp-0': {'id': 'NTP-0', 'deprecated': False}, + 'nunit': {'id': 'Nunit', 'deprecated': True}, + 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False}, + 'oar': {'id': 'OAR', 'deprecated': False}, + 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False}, + 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False}, + 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False}, + 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False}, + 'offis': {'id': 'OFFIS', 'deprecated': False}, + 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False}, + 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False}, + 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False}, + 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False}, + 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False}, + 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False}, + 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False}, + 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False}, + 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False}, + 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False}, + 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False}, + 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False}, + 'ogtsl': {'id': 'OGTSL', 'deprecated': False}, + 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False}, + 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False}, + 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False}, + 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False}, + 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False}, + 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False}, + 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False}, + 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False}, + 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False}, + 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False}, + 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False}, + 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False}, + 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False}, + 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False}, + 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False}, + 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False}, + 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False}, + 'oml': {'id': 'OML', 'deprecated': False}, + 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False}, + 'openssl': {'id': 'OpenSSL', 'deprecated': False}, + 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False}, + 'openvision': {'id': 'OpenVision', 'deprecated': False}, + 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False}, + 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False}, + 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False}, + 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False}, + 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False}, + 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False}, + 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False}, + 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False}, + 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False}, + 'padl': {'id': 'PADL', 'deprecated': False}, + 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False}, + 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False}, + 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False}, + 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False}, + 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False}, + 'pixar': {'id': 'Pixar', 'deprecated': False}, + 'pkgconf': {'id': 'pkgconf', 'deprecated': False}, + 'plexus': {'id': 'Plexus', 'deprecated': False}, + 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False}, + 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False}, + 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False}, + 'postgresql': {'id': 'PostgreSQL', 'deprecated': False}, + 'ppl': {'id': 'PPL', 'deprecated': False}, + 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False}, + 'psfrag': {'id': 'psfrag', 'deprecated': False}, + 'psutils': {'id': 'psutils', 'deprecated': False}, + 'python-2.0': {'id': 'Python-2.0', 'deprecated': False}, + 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False}, + 'python-ldap': {'id': 'python-ldap', 'deprecated': False}, + 'qhull': {'id': 'Qhull', 'deprecated': False}, + 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False}, + 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False}, + 'radvd': {'id': 'radvd', 'deprecated': False}, + 'rdisc': {'id': 'Rdisc', 'deprecated': False}, + 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False}, + 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False}, + 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False}, + 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False}, + 'rsa-md': {'id': 'RSA-MD', 'deprecated': False}, + 'rscpl': {'id': 'RSCPL', 'deprecated': False}, + 'ruby': {'id': 'Ruby', 'deprecated': False}, + 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False}, + 'sax-pd': {'id': 'SAX-PD', 'deprecated': False}, + 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False}, + 'saxpath': {'id': 'Saxpath', 'deprecated': False}, + 'scea': {'id': 'SCEA', 'deprecated': False}, + 'schemereport': {'id': 'SchemeReport', 'deprecated': False}, + 'sendmail': {'id': 'Sendmail', 'deprecated': False}, + 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False}, + 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False}, + 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False}, + 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False}, + 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False}, + 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False}, + 'sgp4': {'id': 'SGP4', 'deprecated': False}, + 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False}, + 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False}, + 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False}, + 'sissl': {'id': 'SISSL', 'deprecated': False}, + 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False}, + 'sl': {'id': 'SL', 'deprecated': False}, + 'sleepycat': {'id': 'Sleepycat', 'deprecated': False}, + 'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False}, + 'smlnj': {'id': 'SMLNJ', 'deprecated': False}, + 'smppl': {'id': 'SMPPL', 'deprecated': False}, + 'snia': {'id': 'SNIA', 'deprecated': False}, + 'snprintf': {'id': 'snprintf', 'deprecated': False}, + 'sofa': {'id': 'SOFA', 'deprecated': False}, + 'softsurfer': {'id': 'softSurfer', 'deprecated': False}, + 'soundex': {'id': 'Soundex', 'deprecated': False}, + 'spencer-86': {'id': 'Spencer-86', 'deprecated': False}, + 'spencer-94': {'id': 'Spencer-94', 'deprecated': False}, + 'spencer-99': {'id': 'Spencer-99', 'deprecated': False}, + 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False}, + 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False}, + 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False}, + 'ssh-short': {'id': 'SSH-short', 'deprecated': False}, + 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False}, + 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False}, + 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True}, + 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False}, + 'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False}, + 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False}, + 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False}, + 'sunpro': {'id': 'SunPro', 'deprecated': False}, + 'swl': {'id': 'SWL', 'deprecated': False}, + 'swrule': {'id': 'swrule', 'deprecated': False}, + 'symlinks': {'id': 'Symlinks', 'deprecated': False}, + 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False}, + 'tcl': {'id': 'TCL', 'deprecated': False}, + 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False}, + 'termreadkey': {'id': 'TermReadKey', 'deprecated': False}, + 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False}, + 'thirdeye': {'id': 'ThirdEye', 'deprecated': False}, + 'threeparttable': {'id': 'threeparttable', 'deprecated': False}, + 'tmate': {'id': 'TMate', 'deprecated': False}, + 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False}, + 'tosl': {'id': 'TOSL', 'deprecated': False}, + 'tpdl': {'id': 'TPDL', 'deprecated': False}, + 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False}, + 'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False}, + 'ttwl': {'id': 'TTWL', 'deprecated': False}, + 'ttyp0': {'id': 'TTYP0', 'deprecated': False}, + 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False}, + 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False}, + 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False}, + 'ucar': {'id': 'UCAR', 'deprecated': False}, + 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False}, + 'ulem': {'id': 'ulem', 'deprecated': False}, + 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False}, + 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False}, + 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False}, + 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False}, + 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False}, + 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False}, + 'unlicense': {'id': 'Unlicense', 'deprecated': False}, + 'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False}, + 'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False}, + 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False}, + 'urt-rle': {'id': 'URT-RLE', 'deprecated': False}, + 'vim': {'id': 'Vim', 'deprecated': False}, + 'vostrom': {'id': 'VOSTROM', 'deprecated': False}, + 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False}, + 'w3c': {'id': 'W3C', 'deprecated': False}, + 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False}, + 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False}, + 'w3m': {'id': 'w3m', 'deprecated': False}, + 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False}, + 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False}, + 'wsuipa': {'id': 'Wsuipa', 'deprecated': False}, + 'wtfpl': {'id': 'WTFPL', 'deprecated': False}, + 'wwl': {'id': 'wwl', 'deprecated': False}, + 'wxwindows': {'id': 'wxWindows', 'deprecated': True}, + 'x11': {'id': 'X11', 'deprecated': False}, + 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False}, + 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False}, + 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False}, + 'xerox': {'id': 'Xerox', 'deprecated': False}, + 'xfig': {'id': 'Xfig', 'deprecated': False}, + 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False}, + 'xinetd': {'id': 'xinetd', 'deprecated': False}, + 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False}, + 'xlock': {'id': 'xlock', 'deprecated': False}, + 'xnet': {'id': 'Xnet', 'deprecated': False}, + 'xpp': {'id': 'xpp', 'deprecated': False}, + 'xskat': {'id': 'XSkat', 'deprecated': False}, + 'xzoom': {'id': 'xzoom', 'deprecated': False}, + 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False}, + 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False}, + 'zed': {'id': 'Zed', 'deprecated': False}, + 'zeeff': {'id': 'Zeeff', 'deprecated': False}, + 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False}, + 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False}, + 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False}, + 'zlib': {'id': 'Zlib', 'deprecated': False}, + 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False}, + 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False}, + 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False}, + 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False}, +} + +EXCEPTIONS: dict[str, SPDXException] = { + '389-exception': {'id': '389-exception', 'deprecated': False}, + 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False}, + 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False}, + 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False}, + 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False}, + 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False}, + 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False}, + 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False}, + 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False}, + 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False}, + 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False}, + 'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False}, + 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False}, + 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False}, + 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False}, + 'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False}, + 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False}, + 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False}, + 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False}, + 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False}, + 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False}, + 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False}, + 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False}, + 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False}, + 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False}, + 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False}, + 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False}, + 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False}, + 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False}, + 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False}, + 'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False}, + 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False}, + 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False}, + 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False}, + 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False}, + 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False}, + 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False}, + 'harbour-exception': {'id': 'harbour-exception', 'deprecated': False}, + 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False}, + 'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False}, + 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False}, + 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False}, + 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False}, + 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False}, + 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False}, + 'llgpl': {'id': 'LLGPL', 'deprecated': False}, + 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False}, + 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False}, + 'mif-exception': {'id': 'mif-exception', 'deprecated': False}, + 'mxml-exception': {'id': 'mxml-exception', 'deprecated': False}, + 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True}, + 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False}, + 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False}, + 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False}, + 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False}, + 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False}, + 'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False}, + 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False}, + 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False}, + 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False}, + 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False}, + 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False}, + 'romic-exception': {'id': 'romic-exception', 'deprecated': False}, + 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False}, + 'sane-exception': {'id': 'SANE-exception', 'deprecated': False}, + 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False}, + 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False}, + 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False}, + 'swi-exception': {'id': 'SWI-exception', 'deprecated': False}, + 'swift-exception': {'id': 'Swift-exception', 'deprecated': False}, + 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False}, + 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False}, + 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False}, + 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False}, + 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False}, + 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False}, + 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False}, +} diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/markers.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/markers.py new file mode 100644 index 0000000..ca3706f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/markers.py @@ -0,0 +1,388 @@ +# 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 operator +import os +import platform +import sys +from typing import AbstractSet, Callable, Literal, Mapping, TypedDict, Union, cast + +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable +from ._parser import parse_marker as _parse_marker +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "Environment", + "EvaluateContext", + "InvalidMarker", + "Marker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "default_environment", +] + +Operator = Callable[[str, Union[str, AbstractSet[str]]], bool] +EvaluateContext = Literal["metadata", "lock_file", "requirement"] +MARKERS_ALLOWING_SET = {"extras", "dependency_groups"} +MARKERS_REQUIRING_VERSION = { + "implementation_version", + "platform_release", + "python_full_version", + "python_version", +} + + +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 Environment(TypedDict): + implementation_name: str + """The implementation's identifier, e.g. ``'cpython'``.""" + + implementation_version: str + """ + The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or + ``'7.3.13'`` for PyPy3.10 v7.3.13. + """ + + os_name: str + """ + The value of :py:data:`os.name`. The name of the operating system dependent module + imported, e.g. ``'posix'``. + """ + + platform_machine: str + """ + Returns the machine type, e.g. ``'i386'``. + + An empty string if the value cannot be determined. + """ + + platform_release: str + """ + The system's release, e.g. ``'2.2.0'`` or ``'NT'``. + + An empty string if the value cannot be determined. + """ + + platform_system: str + """ + The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``. + + An empty string if the value cannot be determined. + """ + + platform_version: str + """ + The system's release version, e.g. ``'#3 on degas'``. + + An empty string if the value cannot be determined. + """ + + python_full_version: str + """ + The Python version as string ``'major.minor.patchlevel'``. + + Note that unlike the Python :py:data:`sys.version`, this value will always include + the patchlevel (it defaults to 0). + """ + + platform_python_implementation: str + """ + A string identifying the Python implementation, e.g. ``'CPython'``. + """ + + python_version: str + """The Python version as string ``'major.minor'``.""" + + sys_platform: str + """ + This string contains a platform identifier that can be used to append + platform-specific components to :py:data:`sys.path`, for instance. + + For Unix systems, except on Linux and AIX, this is the lowercased OS name as + returned by ``uname -s`` with the first part of the version as returned by + ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python + was built. + """ + + +def _normalize_extras( + result: MarkerList | MarkerAtom | str, +) -> MarkerList | MarkerAtom | str: + if not isinstance(result, tuple): + return result + + lhs, op, rhs = result + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + return lhs, op, rhs + + +def _normalize_extra_values(results: MarkerList) -> MarkerList: + """ + Normalize extra values. + """ + + return [_normalize_extras(r) for r in results] + + +def _format_marker( + marker: list[str] | MarkerAtom | str, first: bool | None = 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, + "<": lambda _lhs, _rhs: False, + "<=": operator.eq, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.eq, + ">": lambda _lhs, _rhs: False, +} + + +def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool: + op_str = op.serialize() + if key in MARKERS_REQUIRING_VERSION: + try: + spec = Specifier(f"{op_str}{rhs}") + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Operator | None = _operators.get(op_str) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize( + lhs: str, rhs: str | AbstractSet[str], key: str +) -> tuple[str, str | AbstractSet[str]]: + # PEP 685 - Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + assert isinstance(rhs, str), "extra value must be a string" + # Both sides are normalized at this point already + return (lhs, rhs) + if key in MARKERS_ALLOWING_SET: + if isinstance(rhs, str): # pragma: no cover + return (canonicalize_name(lhs), canonicalize_name(rhs)) + else: + return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs}) + + # other environment markers don't have such standards + return lhs, rhs + + +def _evaluate_markers( + markers: MarkerList, environment: dict[str, str | AbstractSet[str]] +) -> bool: + groups: list[list[bool]] = [[]] + + for marker in markers: + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + assert isinstance(lhs_value, str), "lhs must be a string" + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key)) + elif marker == "or": + groups.append([]) + elif marker == "and": + pass + else: # pragma: nocover + raise TypeError(f"Unexpected marker {marker!r}") + + return any(all(item) for item in groups) + + +def format_full_version(info: sys._version_info) -> str: + version = f"{info.major}.{info.minor}.{info.micro}" + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment() -> Environment: + 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: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + + # If this fails and throws an error, the repr still expects _markers to + # be defined. + self._markers: MarkerList = [] + + try: + self._markers = _normalize_extra_values(_parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def evaluate( + self, + environment: Mapping[str, str | AbstractSet[str]] | None = None, + context: EvaluateContext = "metadata", + ) -> 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 *context* parameter specifies what + context the markers are being evaluated for, which influences what markers + are considered valid. Acceptable values are "metadata" (for core metadata; + default), "lock_file", and "requirement" (i.e. all other situations). + + The environment is determined from the current Python process. + """ + current_environment = cast( + "dict[str, str | AbstractSet[str]]", default_environment() + ) + if context == "lock_file": + current_environment.update( + extras=frozenset(), dependency_groups=frozenset() + ) + elif context == "metadata": + current_environment["extra"] = "" + + if environment is not None: + current_environment.update(environment) + if "extra" in current_environment: + # The API used to allow setting extra to None. We need to handle + # this case for backwards compatibility. Also skip running + # normalize name if extra is empty. + extra = cast("str | None", current_environment["extra"]) + current_environment["extra"] = canonicalize_name(extra) if extra else "" + + return _evaluate_markers( + self._markers, _repair_python_full_version(current_environment) + ) + + +def _repair_python_full_version( + env: dict[str, str | AbstractSet[str]], +) -> dict[str, str | AbstractSet[str]]: + """ + Work around platform.python_version() returning something that is not PEP 440 + compliant for non-tagged Python builds. + """ + python_full_version = cast("str", env["python_full_version"]) + if python_full_version.endswith("+"): + env["python_full_version"] = f"{python_full_version}local" + return env diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/metadata.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/metadata.py new file mode 100644 index 0000000..253f6b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/metadata.py @@ -0,0 +1,978 @@ +from __future__ import annotations + +import email.feedparser +import email.header +import email.message +import email.parser +import email.policy +import keyword +import pathlib +import sys +import typing +from typing import ( + Any, + Callable, + Generic, + Literal, + TypedDict, + cast, +) + +from . import licenses, requirements, specifiers, utils +from . import version as version_module + +if typing.TYPE_CHECKING: + from .licenses import NormalizedLicenseExpression + +T = typing.TypeVar("T") + + +if sys.version_info >= (3, 11): # pragma: no cover + ExceptionGroup = ExceptionGroup # noqa: F821 +else: # pragma: no cover + + class ExceptionGroup(Exception): + """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. + + If :external:exc:`ExceptionGroup` is already defined by Python itself, + that version is used instead. + """ + + message: str + exceptions: list[Exception] + + def __init__(self, message: str, exceptions: list[Exception]) -> None: + self.message = message + self.exceptions = exceptions + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + + +class InvalidMetadata(ValueError): + """A metadata field contains invalid data.""" + + field: str + """The name of the field that contains invalid data.""" + + def __init__(self, field: str, message: str) -> None: + self.field = field + super().__init__(message) + + +# The RawMetadata class attempts to make as few assumptions about the underlying +# serialization formats as possible. The idea is that as long as a serialization +# formats offer some very basic primitives in *some* way then we can support +# serializing to and from that format. +class RawMetadata(TypedDict, total=False): + """A dictionary of raw core metadata. + + Each field in core metadata maps to a key of this dictionary (when data is + provided). The key is lower-case and underscores are used instead of dashes + compared to the equivalent core metadata field. Any core metadata field that + can be specified multiple times or can hold multiple values in a single + field have a key with a plural name. See :class:`Metadata` whose attributes + match the keys of this dictionary. + + Core metadata fields that can be specified multiple times are stored as a + list or dict depending on which is appropriate for the field. Any fields + which hold multiple values in a single field are stored as a list. + + """ + + # Metadata 1.0 - PEP 241 + metadata_version: str + name: str + version: str + platforms: list[str] + summary: str + description: str + keywords: list[str] + home_page: str + author: str + author_email: str + license: str + + # Metadata 1.1 - PEP 314 + supported_platforms: list[str] + download_url: str + classifiers: list[str] + requires: list[str] + provides: list[str] + obsoletes: list[str] + + # Metadata 1.2 - PEP 345 + maintainer: str + maintainer_email: str + requires_dist: list[str] + provides_dist: list[str] + obsoletes_dist: list[str] + requires_python: str + requires_external: list[str] + project_urls: dict[str, str] + + # Metadata 2.0 + # PEP 426 attempted to completely revamp the metadata format + # but got stuck without ever being able to build consensus on + # it and ultimately ended up withdrawn. + # + # However, a number of tools had started emitting METADATA with + # `2.0` Metadata-Version, so for historical reasons, this version + # was skipped. + + # Metadata 2.1 - PEP 566 + description_content_type: str + provides_extra: list[str] + + # Metadata 2.2 - PEP 643 + dynamic: list[str] + + # Metadata 2.3 - PEP 685 + # No new fields were added in PEP 685, just some edge case were + # tightened up to provide better interoperability. + + # Metadata 2.4 - PEP 639 + license_expression: str + license_files: list[str] + + # Metadata 2.5 - PEP 794 + import_names: list[str] + import_namespaces: list[str] + + +# 'keywords' is special as it's a string in the core metadata spec, but we +# represent it as a list. +_STRING_FIELDS = { + "author", + "author_email", + "description", + "description_content_type", + "download_url", + "home_page", + "license", + "license_expression", + "maintainer", + "maintainer_email", + "metadata_version", + "name", + "requires_python", + "summary", + "version", +} + +_LIST_FIELDS = { + "classifiers", + "dynamic", + "license_files", + "obsoletes", + "obsoletes_dist", + "platforms", + "provides", + "provides_dist", + "provides_extra", + "requires", + "requires_dist", + "requires_external", + "supported_platforms", + "import_names", + "import_namespaces", +} + +_DICT_FIELDS = { + "project_urls", +} + + +def _parse_keywords(data: str) -> list[str]: + """Split a string of comma-separated keywords into a list of keywords.""" + return [k.strip() for k in data.split(",")] + + +def _parse_project_urls(data: list[str]) -> dict[str, str]: + """Parse a list of label/URL string pairings separated by a comma.""" + urls = {} + for pair in data: + # Our logic is slightly tricky here as we want to try and do + # *something* reasonable with malformed data. + # + # The main thing that we have to worry about, is data that does + # not have a ',' at all to split the label from the Value. There + # isn't a singular right answer here, and we will fail validation + # later on (if the caller is validating) so it doesn't *really* + # matter, but since the missing value has to be an empty str + # and our return value is dict[str, str], if we let the key + # be the missing value, then they'd have multiple '' values that + # overwrite each other in a accumulating dict. + # + # The other potential issue is that it's possible to have the + # same label multiple times in the metadata, with no solid "right" + # answer with what to do in that case. As such, we'll do the only + # thing we can, which is treat the field as unparsable and add it + # to our list of unparsed fields. + # + # TODO: The spec doesn't say anything about if the keys should be + # considered case sensitive or not... logically they should + # be case-preserving and case-insensitive, but doing that + # would open up more cases where we might have duplicate + # entries. + label, _, url = (s.strip() for s in pair.partition(",")) + + if label in urls: + # The label already exists in our set of urls, so this field + # is unparsable, and we can just add the whole thing to our + # unparsable data and stop processing it. + raise KeyError("duplicate labels in project urls") + urls[label] = url + + return urls + + +def _get_payload(msg: email.message.Message, source: bytes | str) -> str: + """Get the body of the message.""" + # If our source is a str, then our caller has managed encodings for us, + # and we don't need to deal with it. + if isinstance(source, str): + payload = msg.get_payload() + assert isinstance(payload, str) + return payload + # If our source is a bytes, then we're managing the encoding and we need + # to deal with it. + else: + bpayload = msg.get_payload(decode=True) + assert isinstance(bpayload, bytes) + try: + return bpayload.decode("utf8", "strict") + except UnicodeDecodeError as exc: + raise ValueError("payload in an invalid encoding") from exc + + +# The various parse_FORMAT functions here are intended to be as lenient as +# possible in their parsing, while still returning a correctly typed +# RawMetadata. +# +# To aid in this, we also generally want to do as little touching of the +# data as possible, except where there are possibly some historic holdovers +# that make valid data awkward to work with. +# +# While this is a lower level, intermediate format than our ``Metadata`` +# class, some light touch ups can make a massive difference in usability. + +# Map METADATA fields to RawMetadata. +_EMAIL_TO_RAW_MAPPING = { + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "description", + "description-content-type": "description_content_type", + "download-url": "download_url", + "dynamic": "dynamic", + "home-page": "home_page", + "import-name": "import_names", + "import-namespace": "import_namespaces", + "keywords": "keywords", + "license": "license", + "license-expression": "license_expression", + "license-file": "license_files", + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "metadata-version": "metadata_version", + "name": "name", + "obsoletes": "obsoletes", + "obsoletes-dist": "obsoletes_dist", + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + "provides-dist": "provides_dist", + "provides-extra": "provides_extra", + "requires": "requires", + "requires-dist": "requires_dist", + "requires-external": "requires_external", + "requires-python": "requires_python", + "summary": "summary", + "supported-platform": "supported_platforms", + "version": "version", +} +_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} + + +# This class is for writing RFC822 messages +class RFC822Policy(email.policy.EmailPolicy): + """ + This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse`` + implementation that handles multi-line values, and some nice defaults. + """ + + utf8 = True + mangle_from_ = False + max_line_length = 0 + + def header_store_parse(self, name: str, value: str) -> tuple[str, str]: + size = len(name) + 2 + value = value.replace("\n", "\n" + " " * size) + return (name, value) + + +# This class is for writing RFC822 messages +class RFC822Message(email.message.EmailMessage): + """ + This is :class:`email.message.EmailMessage` with two small changes: it defaults to + our `RFC822Policy`, and it correctly writes unicode when being called + with `bytes()`. + """ + + def __init__(self) -> None: + super().__init__(policy=RFC822Policy()) + + def as_bytes( + self, unixfrom: bool = False, policy: email.policy.Policy | None = None + ) -> bytes: + """ + Return the bytes representation of the message. + + This handles unicode encoding. + """ + return self.as_string(unixfrom, policy=policy).encode("utf-8") + + +def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: + """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). + + This function returns a two-item tuple of dicts. The first dict is of + recognized fields from the core metadata specification. Fields that can be + parsed and translated into Python's built-in types are converted + appropriately. All other fields are left as-is. Fields that are allowed to + appear multiple times are stored as lists. + + The second dict contains all other fields from the metadata. This includes + any unrecognized fields. It also includes any fields which are expected to + be parsed into a built-in type but were not formatted appropriately. Finally, + any fields that are expected to appear only once but are repeated are + included in this dict. + + """ + raw: dict[str, str | list[str] | dict[str, str]] = {} + unparsed: dict[str, list[str]] = {} + + if isinstance(data, str): + parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) + else: + parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) + + # We have to wrap parsed.keys() in a set, because in the case of multiple + # values for a key (a list), the key will appear multiple times in the + # list of keys, but we're avoiding that by using get_all(). + for name_with_case in frozenset(parsed.keys()): + # Header names in RFC are case insensitive, so we'll normalize to all + # lower case to make comparisons easier. + name = name_with_case.lower() + + # We use get_all() here, even for fields that aren't multiple use, + # because otherwise someone could have e.g. two Name fields, and we + # would just silently ignore it rather than doing something about it. + headers = parsed.get_all(name) or [] + + # The way the email module works when parsing bytes is that it + # unconditionally decodes the bytes as ascii using the surrogateescape + # handler. When you pull that data back out (such as with get_all() ), + # it looks to see if the str has any surrogate escapes, and if it does + # it wraps it in a Header object instead of returning the string. + # + # As such, we'll look for those Header objects, and fix up the encoding. + value = [] + # Flag if we have run into any issues processing the headers, thus + # signalling that the data belongs in 'unparsed'. + valid_encoding = True + for h in headers: + # It's unclear if this can return more types than just a Header or + # a str, so we'll just assert here to make sure. + assert isinstance(h, (email.header.Header, str)) + + # If it's a header object, we need to do our little dance to get + # the real data out of it. In cases where there is invalid data + # we're going to end up with mojibake, but there's no obvious, good + # way around that without reimplementing parts of the Header object + # ourselves. + # + # That should be fine since, if mojibacked happens, this key is + # going into the unparsed dict anyways. + if isinstance(h, email.header.Header): + # The Header object stores it's data as chunks, and each chunk + # can be independently encoded, so we'll need to check each + # of them. + chunks: list[tuple[bytes, str | None]] = [] + for binary, _encoding in email.header.decode_header(h): + try: + binary.decode("utf8", "strict") + except UnicodeDecodeError: + # Enable mojibake. + encoding = "latin1" + valid_encoding = False + else: + encoding = "utf8" + chunks.append((binary, encoding)) + + # Turn our chunks back into a Header object, then let that + # Header object do the right thing to turn them into a + # string for us. + value.append(str(email.header.make_header(chunks))) + # This is already a string, so just add it. + else: + value.append(h) + + # We've processed all of our values to get them into a list of str, + # but we may have mojibake data, in which case this is an unparsed + # field. + if not valid_encoding: + unparsed[name] = value + continue + + raw_name = _EMAIL_TO_RAW_MAPPING.get(name) + if raw_name is None: + # This is a bit of a weird situation, we've encountered a key that + # we don't know what it means, so we don't know whether it's meant + # to be a list or not. + # + # Since we can't really tell one way or another, we'll just leave it + # as a list, even though it may be a single item list, because that's + # what makes the most sense for email headers. + unparsed[name] = value + continue + + # If this is one of our string fields, then we'll check to see if our + # value is a list of a single item. If it is then we'll assume that + # it was emitted as a single string, and unwrap the str from inside + # the list. + # + # If it's any other kind of data, then we haven't the faintest clue + # what we should parse it as, and we have to just add it to our list + # of unparsed stuff. + if raw_name in _STRING_FIELDS and len(value) == 1: + raw[raw_name] = value[0] + # If this is import_names, we need to special case the empty field + # case, which converts to an empty list instead of None. We can't let + # the empty case slip through, as it will fail validation. + elif raw_name == "import_names" and value == [""]: + raw[raw_name] = [] + # If this is one of our list of string fields, then we can just assign + # the value, since email *only* has strings, and our get_all() call + # above ensures that this is a list. + elif raw_name in _LIST_FIELDS: + raw[raw_name] = value + # Special Case: Keywords + # The keywords field is implemented in the metadata spec as a str, + # but it conceptually is a list of strings, and is serialized using + # ", ".join(keywords), so we'll do some light data massaging to turn + # this into what it logically is. + elif raw_name == "keywords" and len(value) == 1: + raw[raw_name] = _parse_keywords(value[0]) + # Special Case: Project-URL + # The project urls is implemented in the metadata spec as a list of + # specially-formatted strings that represent a key and a value, which + # is fundamentally a mapping, however the email format doesn't support + # mappings in a sane way, so it was crammed into a list of strings + # instead. + # + # We will do a little light data massaging to turn this into a map as + # it logically should be. + elif raw_name == "project_urls": + try: + raw[raw_name] = _parse_project_urls(value) + except KeyError: + unparsed[name] = value + # Nothing that we've done has managed to parse this, so it'll just + # throw it in our unparsable data and move on. + else: + unparsed[name] = value + + # We need to support getting the Description from the message payload in + # addition to getting it from the the headers. This does mean, though, there + # is the possibility of it being set both ways, in which case we put both + # in 'unparsed' since we don't know which is right. + try: + payload = _get_payload(parsed, data) + except ValueError: + unparsed.setdefault("description", []).append( + parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload] + ) + else: + if payload: + # Check to see if we've already got a description, if so then both + # it, and this body move to unparsable. + if "description" in raw: + description_header = cast("str", raw.pop("description")) + unparsed.setdefault("description", []).extend( + [description_header, payload] + ) + elif "description" in unparsed: + unparsed["description"].append(payload) + else: + raw["description"] = payload + + # We need to cast our `raw` to a metadata, because a TypedDict only support + # literal key names, but we're computing our key names on purpose, but the + # way this function is implemented, our `TypedDict` can only have valid key + # names. + return cast("RawMetadata", raw), unparsed + + +_NOT_FOUND = object() + + +# Keep the two values in sync. +_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] +_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] + +_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) + + +class _Validator(Generic[T]): + """Validate a metadata field. + + All _process_*() methods correspond to a core metadata field. The method is + called with the field's raw value. If the raw value is valid it is returned + in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). + If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause + as appropriate). + """ + + name: str + raw_name: str + added: _MetadataVersion + + def __init__( + self, + *, + added: _MetadataVersion = "1.0", + ) -> None: + self.added = added + + def __set_name__(self, _owner: Metadata, name: str) -> None: + self.name = name + self.raw_name = _RAW_TO_EMAIL_MAPPING[name] + + def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T: + # With Python 3.8, the caching can be replaced with functools.cached_property(). + # No need to check the cache as attribute lookup will resolve into the + # instance's __dict__ before __get__ is called. + cache = instance.__dict__ + value = instance._raw.get(self.name) + + # To make the _process_* methods easier, we'll check if the value is None + # and if this field is NOT a required attribute, and if both of those + # things are true, we'll skip the the converter. This will mean that the + # converters never have to deal with the None union. + if self.name in _REQUIRED_ATTRS or value is not None: + try: + converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + except AttributeError: + pass + else: + value = converter(value) + + cache[self.name] = value + try: + del instance._raw[self.name] # type: ignore[misc] + except KeyError: + pass + + return cast("T", value) + + def _invalid_metadata( + self, msg: str, cause: Exception | None = None + ) -> InvalidMetadata: + exc = InvalidMetadata( + self.raw_name, msg.format_map({"field": repr(self.raw_name)}) + ) + exc.__cause__ = cause + return exc + + def _process_metadata_version(self, value: str) -> _MetadataVersion: + # Implicitly makes Metadata-Version required. + if value not in _VALID_METADATA_VERSIONS: + raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + return cast("_MetadataVersion", value) + + def _process_name(self, value: str) -> str: + if not value: + raise self._invalid_metadata("{field} is a required field") + # Validate the name as a side-effect. + try: + utils.canonicalize_name(value, validate=True) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return value + + def _process_version(self, value: str) -> version_module.Version: + if not value: + raise self._invalid_metadata("{field} is a required field") + try: + return version_module.parse(value) + except version_module.InvalidVersion as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_summary(self, value: str) -> str: + """Check the field contains no newlines.""" + if "\n" in value: + raise self._invalid_metadata("{field} must be a single line") + return value + + def _process_description_content_type(self, value: str) -> str: + content_types = {"text/plain", "text/x-rst", "text/markdown"} + message = email.message.EmailMessage() + message["content-type"] = value + + content_type, parameters = ( + # Defaults to `text/plain` if parsing failed. + message.get_content_type().lower(), + message["content-type"].params, + ) + # Check if content-type is valid or defaulted to `text/plain` and thus was + # not parseable. + if content_type not in content_types or content_type not in value.lower(): + raise self._invalid_metadata( + f"{{field}} must be one of {list(content_types)}, not {value!r}" + ) + + charset = parameters.get("charset", "UTF-8") + if charset != "UTF-8": + raise self._invalid_metadata( + f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" + ) + + markdown_variants = {"GFM", "CommonMark"} + variant = parameters.get("variant", "GFM") # Use an acceptable default. + if content_type == "text/markdown" and variant not in markdown_variants: + raise self._invalid_metadata( + f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " + f"not {variant!r}", + ) + return value + + def _process_dynamic(self, value: list[str]) -> list[str]: + for dynamic_field in map(str.lower, value): + if dynamic_field in {"name", "version", "metadata-version"}: + raise self._invalid_metadata( + f"{dynamic_field!r} is not allowed as a dynamic field" + ) + elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: + raise self._invalid_metadata( + f"{dynamic_field!r} is not a valid dynamic field" + ) + return list(map(str.lower, value)) + + def _process_provides_extra( + self, + value: list[str], + ) -> list[utils.NormalizedName]: + normalized_names = [] + try: + for name in value: + normalized_names.append(utils.canonicalize_name(name, validate=True)) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return normalized_names + + def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: + try: + return specifiers.SpecifierSet(value) + except specifiers.InvalidSpecifier as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_requires_dist( + self, + value: list[str], + ) -> list[requirements.Requirement]: + reqs = [] + try: + for req in value: + reqs.append(requirements.Requirement(req)) + except requirements.InvalidRequirement as exc: + raise self._invalid_metadata( + f"{req!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return reqs + + def _process_license_expression(self, value: str) -> NormalizedLicenseExpression: + try: + return licenses.canonicalize_license_expression(value) + except ValueError as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_license_files(self, value: list[str]) -> list[str]: + paths = [] + for path in value: + if ".." in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, " + "parent directory indicators are not allowed" + ) + if "*" in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be resolved" + ) + if ( + pathlib.PurePosixPath(path).is_absolute() + or pathlib.PureWindowsPath(path).is_absolute() + ): + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be relative" + ) + if pathlib.PureWindowsPath(path).as_posix() != path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must use '/' delimiter" + ) + paths.append(path) + return paths + + def _process_import_names(self, value: list[str]) -> list[str]: + for import_name in value: + name, semicolon, private = import_name.partition(";") + name = name.rstrip() + for identifier in name.split("."): + if not identifier.isidentifier(): + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}; " + f"{identifier!r} is not a valid identifier" + ) + elif keyword.iskeyword(identifier): + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}; " + f"{identifier!r} is a keyword" + ) + if semicolon and private.lstrip() != "private": + raise self._invalid_metadata( + f"{import_name!r} is invalid for {{field}}; " + "the only valid option is 'private'" + ) + return value + + _process_import_namespaces = _process_import_names + + +class Metadata: + """Representation of distribution metadata. + + Compared to :class:`RawMetadata`, this class provides objects representing + metadata fields instead of only using built-in types. Any invalid metadata + will cause :exc:`InvalidMetadata` to be raised (with a + :py:attr:`~BaseException.__cause__` attribute as appropriate). + """ + + _raw: RawMetadata + + @classmethod + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: + """Create an instance from :class:`RawMetadata`. + + If *validate* is true, all metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + ins = cls() + ins._raw = data.copy() # Mutations occur due to caching enriched values. + + if validate: + exceptions: list[Exception] = [] + try: + metadata_version = ins.metadata_version + metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) + except InvalidMetadata as metadata_version_exc: + exceptions.append(metadata_version_exc) + metadata_version = None + + # Make sure to check for the fields that are present, the required + # fields (so their absence can be reported). + fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS + # Remove fields that have already been checked. + fields_to_check -= {"metadata_version"} + + for key in fields_to_check: + try: + if metadata_version: + # Can't use getattr() as that triggers descriptor protocol which + # will fail due to no value for the instance argument. + try: + field_metadata_version = cls.__dict__[key].added + except KeyError: + exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + exceptions.append(exc) + continue + field_age = _VALID_METADATA_VERSIONS.index( + field_metadata_version + ) + if field_age > metadata_age: + field = _RAW_TO_EMAIL_MAPPING[key] + exc = InvalidMetadata( + field, + f"{field} introduced in metadata version " + f"{field_metadata_version}, not {metadata_version}", + ) + exceptions.append(exc) + continue + getattr(ins, key) + except InvalidMetadata as exc: + exceptions.append(exc) + + if exceptions: + raise ExceptionGroup("invalid metadata", exceptions) + + return ins + + @classmethod + def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: + """Parse metadata from email headers. + + If *validate* is true, the metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + raw, unparsed = parse_email(data) + + if validate: + exceptions: list[Exception] = [] + for unparsed_key in unparsed: + if unparsed_key in _EMAIL_TO_RAW_MAPPING: + message = f"{unparsed_key!r} has invalid data" + else: + message = f"unrecognized field: {unparsed_key!r}" + exceptions.append(InvalidMetadata(unparsed_key, message)) + + if exceptions: + raise ExceptionGroup("unparsed", exceptions) + + try: + return cls.from_raw(raw, validate=validate) + except ExceptionGroup as exc_group: + raise ExceptionGroup( + "invalid or unparsed metadata", exc_group.exceptions + ) from None + + metadata_version: _Validator[_MetadataVersion] = _Validator() + """:external:ref:`core-metadata-metadata-version` + (required; validated to be a valid metadata version)""" + # `name` is not normalized/typed to NormalizedName so as to provide access to + # the original/raw name. + name: _Validator[str] = _Validator() + """:external:ref:`core-metadata-name` + (required; validated using :func:`~packaging.utils.canonicalize_name` and its + *validate* parameter)""" + version: _Validator[version_module.Version] = _Validator() + """:external:ref:`core-metadata-version` (required)""" + dynamic: _Validator[list[str] | None] = _Validator( + added="2.2", + ) + """:external:ref:`core-metadata-dynamic` + (validated against core metadata field names and lowercased)""" + platforms: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-platform`""" + supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-supported-platform`""" + summary: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" + description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body + """:external:ref:`core-metadata-description`""" + description_content_type: _Validator[str | None] = _Validator(added="2.1") + """:external:ref:`core-metadata-description-content-type` (validated)""" + keywords: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-keywords`""" + home_page: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-home-page`""" + download_url: _Validator[str | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-download-url`""" + author: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author`""" + author_email: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author-email`""" + maintainer: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer`""" + maintainer_email: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer-email`""" + license: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-license`""" + license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator( + added="2.4" + ) + """:external:ref:`core-metadata-license-expression`""" + license_files: _Validator[list[str] | None] = _Validator(added="2.4") + """:external:ref:`core-metadata-license-file`""" + classifiers: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-classifier`""" + requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-dist`""" + requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-python`""" + # Because `Requires-External` allows for non-PEP 440 version specifiers, we + # don't do any processing on the values. + requires_external: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-requires-external`""" + project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-project-url`""" + # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation + # regardless of metadata version. + provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator( + added="2.1", + ) + """:external:ref:`core-metadata-provides-extra`""" + provides_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-provides-dist`""" + obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-obsoletes-dist`""" + import_names: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-name`""" + import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-namespace`""" + requires: _Validator[list[str] | None] = _Validator(added="1.1") + """``Requires`` (deprecated)""" + provides: _Validator[list[str] | None] = _Validator(added="1.1") + """``Provides`` (deprecated)""" + obsoletes: _Validator[list[str] | None] = _Validator(added="1.1") + """``Obsoletes`` (deprecated)""" + + def as_rfc822(self) -> RFC822Message: + """ + Return an RFC822 message with the metadata. + """ + message = RFC822Message() + self._write_metadata(message) + return message + + def _write_metadata(self, message: RFC822Message) -> None: + """ + Return an RFC822 message with the metadata. + """ + for name, validator in self.__class__.__dict__.items(): + if isinstance(validator, _Validator) and name != "description": + value = getattr(self, name) + email_name = _RAW_TO_EMAIL_MAPPING[name] + if value is not None: + if email_name == "project-url": + for label, url in value.items(): + message[email_name] = f"{label}, {url}" + elif email_name == "keywords": + message[email_name] = ",".join(value) + elif email_name == "import-name" and value == []: + message[email_name] = "" + elif isinstance(value, list): + for item in value: + message[email_name] = str(item) + else: + message[email_name] = str(value) + + # The description is a special case because it is in the body of the message. + if self.description is not None: + message.set_payload(self.description) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/pylock.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/pylock.py new file mode 100644 index 0000000..a564f15 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/pylock.py @@ -0,0 +1,635 @@ +from __future__ import annotations + +import dataclasses +import logging +import re +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from datetime import datetime +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Protocol, + TypeVar, +) + +from .markers import Marker +from .specifiers import SpecifierSet +from .utils import NormalizedName, is_normalized_name +from .version import Version + +if TYPE_CHECKING: # pragma: no cover + from pathlib import Path + + from typing_extensions import Self + +_logger = logging.getLogger(__name__) + +__all__ = [ + "Package", + "PackageArchive", + "PackageDirectory", + "PackageSdist", + "PackageVcs", + "PackageWheel", + "Pylock", + "PylockUnsupportedVersionError", + "PylockValidationError", + "is_valid_pylock_path", +] + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") + + +class _FromMappingProtocol(Protocol): # pragma: no cover + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... + + +_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) + + +_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$") + + +def is_valid_pylock_path(path: Path) -> bool: + """Check if the given path is a valid pylock file path.""" + return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name)) + + +def _toml_key(key: str) -> str: + return key.replace("_", "-") + + +def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401 + if isinstance(value, (Version, Marker, SpecifierSet)): + return str(value) + if isinstance(value, Sequence) and key == "environments": + return [str(v) for v in value] + return value + + +def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: + return { + _toml_key(key): _toml_value(key, value) + for key, value in data + if value is not None + } + + +def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: + """Get a value from the dictionary and verify it's the expected type.""" + if (value := d.get(key)) is None: + return None + if not isinstance(value, expected_type): + raise PylockValidationError( + f"Unexpected type {type(value).__name__} " + f"(expected {expected_type.__name__})", + context=key, + ) + return value + + +def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: + """Get a required value from the dictionary and verify it's the expected type.""" + if (value := _get(d, expected_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence( + d: Mapping[str, Any], expected_item_type: type[_T], key: str +) -> Sequence[_T] | None: + """Get a list value from the dictionary and verify it's the expected items type.""" + if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract] + return None + if isinstance(value, (str, bytes)): + # special case: str and bytes are Sequences, but we want to reject it + raise PylockValidationError( + f"Unexpected type {type(value).__name__} (expected Sequence)", + context=key, + ) + for i, item in enumerate(value): + if not isinstance(item, expected_item_type): + raise PylockValidationError( + f"Unexpected type {type(item).__name__} " + f"(expected {expected_item_type.__name__})", + context=f"{key}[{i}]", + ) + return value + + +def _get_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2 | None: + """Get a value from the dictionary, verify it's the expected type, + and convert to the target type. + + This assumes the target_type constructor accepts the value. + """ + if (value := _get(d, expected_type, key)) is None: + return None + try: + return target_type(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_required_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2: + """Get a required value from the dict, verify it's the expected type, + and convert to the target type.""" + if (value := _get_as(d, expected_type, target_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence_as( + d: Mapping[str, Any], + expected_item_type: type[_T], + target_item_type: Callable[[_T], _T2], + key: str, +) -> list[_T2] | None: + """Get list value from dictionary and verify expected items type.""" + if (value := _get_sequence(d, expected_item_type, key)) is None: + return None + result = [] + try: + for item in value: + typed_item = target_item_type(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_object( + d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str +) -> _FromMappingProtocolT | None: + """Get a dictionary value from the dictionary and convert it to a dataclass.""" + if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + try: + return target_type._from_dict(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> list[_FromMappingProtocolT] | None: + """Get a list value from the dictionary and convert its items to a dataclass.""" + if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + result: list[_FromMappingProtocolT] = [] + try: + for item in value: + typed_item = target_item_type._from_dict(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_required_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> Sequence[_FromMappingProtocolT]: + """Get a required list value from the dictionary and convert its items to a + dataclass.""" + if (result := _get_sequence_of_objects(d, target_item_type, key)) is None: + raise _PylockRequiredKeyError(key) + return result + + +def _validate_normalized_name(name: str) -> NormalizedName: + """Validate that a string is a NormalizedName.""" + if not is_normalized_name(name): + raise PylockValidationError(f"Name {name!r} is not normalized") + return NormalizedName(name) + + +def _validate_path_url(path: str | None, url: str | None) -> None: + if not path and not url: + raise PylockValidationError("path or url must be provided") + + +def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]: + if not hashes: + raise PylockValidationError("At least one hash must be provided") + if not all(isinstance(hash_val, str) for hash_val in hashes.values()): + raise PylockValidationError("Hash values must be strings") + return hashes + + +class PylockValidationError(Exception): + """Raised when when input data is not spec-compliant.""" + + context: str | None = None + message: str + + def __init__( + self, + cause: str | Exception, + *, + context: str | None = None, + ) -> None: + if isinstance(cause, PylockValidationError): + if cause.context: + self.context = ( + f"{context}.{cause.context}" if context else cause.context + ) + else: + self.context = context + self.message = cause.message + else: + self.context = context + self.message = str(cause) + + def __str__(self) -> str: + if self.context: + return f"{self.message} in {self.context!r}" + return self.message + + +class _PylockRequiredKeyError(PylockValidationError): + def __init__(self, key: str) -> None: + super().__init__("Missing required value", context=key) + + +class PylockUnsupportedVersionError(PylockValidationError): + """Raised when encountering an unsupported `lock_version`.""" + + +@dataclass(frozen=True, init=False) +class PackageVcs: + type: str + url: str | None = None + path: str | None = None + requested_revision: str | None = None + commit_id: str # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + type: str, + url: str | None = None, + path: str | None = None, + requested_revision: str | None = None, + commit_id: str, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "type", type) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "requested_revision", requested_revision) + object.__setattr__(self, "commit_id", commit_id) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_vcs = cls( + type=_get_required(d, str, "type"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + requested_revision=_get(d, str, "requested-revision"), + commit_id=_get_required(d, str, "commit-id"), + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_vcs.path, package_vcs.url) + return package_vcs + + +@dataclass(frozen=True, init=False) +class PackageDirectory: + path: str + editable: bool | None = None + subdirectory: str | None = None + + def __init__( + self, + *, + path: str, + editable: bool | None = None, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "path", path) + object.__setattr__(self, "editable", editable) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + return cls( + path=_get_required(d, str, "path"), + editable=_get(d, bool, "editable"), + subdirectory=_get(d, str, "subdirectory"), + ) + + +@dataclass(frozen=True, init=False) +class PackageArchive: + url: str | None = None + path: str | None = None + size: int | None = None + upload_time: datetime | None = None + hashes: Mapping[str, str] # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + url: str | None = None, + path: str | None = None, + size: int | None = None, + upload_time: datetime | None = None, + hashes: Mapping[str, str], + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "hashes", hashes) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_archive = cls( + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + upload_time=_get(d, datetime, "upload-time"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_archive.path, package_archive.url) + return package_archive + + +@dataclass(frozen=True, init=False) +class PackageSdist: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_sdist = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_sdist.path, package_sdist.url) + return package_sdist + + +@dataclass(frozen=True, init=False) +class PackageWheel: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_wheel = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_wheel.path, package_wheel.url) + return package_wheel + + +@dataclass(frozen=True, init=False) +class Package: + name: NormalizedName + version: Version | None = None + marker: Marker | None = None + requires_python: SpecifierSet | None = None + dependencies: Sequence[Mapping[str, Any]] | None = None + vcs: PackageVcs | None = None + directory: PackageDirectory | None = None + archive: PackageArchive | None = None + index: str | None = None + sdist: PackageSdist | None = None + wheels: Sequence[PackageWheel] | None = None + attestation_identities: Sequence[Mapping[str, Any]] | None = None + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + name: NormalizedName, + version: Version | None = None, + marker: Marker | None = None, + requires_python: SpecifierSet | None = None, + dependencies: Sequence[Mapping[str, Any]] | None = None, + vcs: PackageVcs | None = None, + directory: PackageDirectory | None = None, + archive: PackageArchive | None = None, + index: str | None = None, + sdist: PackageSdist | None = None, + wheels: Sequence[PackageWheel] | None = None, + attestation_identities: Sequence[Mapping[str, Any]] | None = None, + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "version", version) + object.__setattr__(self, "marker", marker) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "dependencies", dependencies) + object.__setattr__(self, "vcs", vcs) + object.__setattr__(self, "directory", directory) + object.__setattr__(self, "archive", archive) + object.__setattr__(self, "index", index) + object.__setattr__(self, "sdist", sdist) + object.__setattr__(self, "wheels", wheels) + object.__setattr__(self, "attestation_identities", attestation_identities) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package = cls( + name=_get_required_as(d, str, _validate_normalized_name, "name"), + version=_get_as(d, str, Version, "version"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract] + marker=_get_as(d, str, Marker, "marker"), + vcs=_get_object(d, PackageVcs, "vcs"), + directory=_get_object(d, PackageDirectory, "directory"), + archive=_get_object(d, PackageArchive, "archive"), + index=_get(d, str, "index"), + sdist=_get_object(d, PackageSdist, "sdist"), + wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"), + attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract] + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + distributions = bool(package.sdist) + len(package.wheels or []) + direct_urls = ( + bool(package.vcs) + bool(package.directory) + bool(package.archive) + ) + if distributions > 0 and direct_urls > 0: + raise PylockValidationError( + "None of vcs, directory, archive must be set if sdist or wheels are set" + ) + if distributions == 0 and direct_urls != 1: + raise PylockValidationError( + "Exactly one of vcs, directory, archive must be set " + "if sdist and wheels are not set" + ) + try: + for i, attestation_identity in enumerate( # noqa: B007 + package.attestation_identities or [] + ): + _get_required(attestation_identity, str, "kind") + except Exception as e: + raise PylockValidationError( + e, context=f"attestation-identities[{i}]" + ) from e + return package + + @property + def is_direct(self) -> bool: + return not (self.sdist or self.wheels) + + +@dataclass(frozen=True, init=False) +class Pylock: + """A class representing a pylock file.""" + + lock_version: Version + environments: Sequence[Marker] | None = None + requires_python: SpecifierSet | None = None + extras: Sequence[NormalizedName] | None = None + dependency_groups: Sequence[str] | None = None + default_groups: Sequence[str] | None = None + created_by: str # type: ignore[misc] + packages: Sequence[Package] # type: ignore[misc] + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + lock_version: Version, + environments: Sequence[Marker] | None = None, + requires_python: SpecifierSet | None = None, + extras: Sequence[NormalizedName] | None = None, + dependency_groups: Sequence[str] | None = None, + default_groups: Sequence[str] | None = None, + created_by: str, + packages: Sequence[Package], + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "lock_version", lock_version) + object.__setattr__(self, "environments", environments) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "extras", extras) + object.__setattr__(self, "dependency_groups", dependency_groups) + object.__setattr__(self, "default_groups", default_groups) + object.__setattr__(self, "created_by", created_by) + object.__setattr__(self, "packages", packages) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + pylock = cls( + lock_version=_get_required_as(d, str, Version, "lock-version"), + environments=_get_sequence_as(d, str, Marker, "environments"), + extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"), + dependency_groups=_get_sequence(d, str, "dependency-groups"), + default_groups=_get_sequence(d, str, "default-groups"), + created_by=_get_required(d, str, "created-by"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + packages=_get_required_sequence_of_objects(d, Package, "packages"), + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + if not Version("1") <= pylock.lock_version < Version("2"): + raise PylockUnsupportedVersionError( + f"pylock version {pylock.lock_version} is not supported" + ) + if pylock.lock_version > Version("1.0"): + _logger.warning( + "pylock minor version %s is not supported", pylock.lock_version + ) + return pylock + + @classmethod + def from_dict(cls, d: Mapping[str, Any], /) -> Self: + """Create and validate a Pylock instance from a TOML dictionary. + + Raises :class:`PylockValidationError` if the input data is not + spec-compliant. + """ + return cls._from_dict(d) + + def to_dict(self) -> Mapping[str, Any]: + """Convert the Pylock instance to a TOML dictionary.""" + return dataclasses.asdict(self, dict_factory=_toml_dict_factory) + + def validate(self) -> None: + """Validate the Pylock instance against the specification. + + Raises :class:`PylockValidationError` otherwise.""" + self.from_dict(self.to_dict()) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/requirements.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/requirements.py new file mode 100644 index 0000000..3079be6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/requirements.py @@ -0,0 +1,86 @@ +# 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 Iterator + +from ._parser import parse_requirement as _parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet +from .utils import canonicalize_name + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +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: + parsed = _parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + self.url: str | None = parsed.url or None + self.extras: set[str] = set(parsed.extras or []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Marker | None = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def _iter_parts(self, name: str) -> Iterator[str]: + yield name + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + yield f"[{formatted_extras}]" + + if self.specifier: + yield str(self.specifier) + + if self.url: + yield f" @ {self.url}" + if self.marker: + yield " " + + if self.marker: + yield f"; {self.marker}" + + def __str__(self) -> str: + return "".join(self._iter_parts(self.name)) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def __hash__(self) -> int: + return hash(tuple(self._iter_parts(canonicalize_name(self.name)))) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + canonicalize_name(self.name) == canonicalize_name(other.name) + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/specifiers.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..5d26b0d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/specifiers.py @@ -0,0 +1,1068 @@ +# 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. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +from __future__ import annotations + +import abc +import itertools +import re +from typing import Callable, Final, Iterable, Iterator, TypeVar, Union + +from .utils import canonicalize_version +from .version import InvalidVersion, Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + + +def _coerce_version(version: UnparsedVersion) -> Version | None: + if not isinstance(version, Version): + try: + version = Version(version) + except InvalidVersion: + return None + return version + + +def _public_version(version: Version) -> Version: + return version.__replace__(local=None) + + +def _base_version(version: Version) -> Version: + return version.__replace__(pre=None, post=None, dev=None, local=None) + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + __slots__ = () + __match_args__ = ("_str",) + + @property + def _str(self) -> str: + """Internal property for match_args""" + return str(self) + + @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. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> bool | None: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter # noqa: B027 + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: bool | None = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None + ) -> Iterator[UnparsedVersionVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ + + __slots__ = ("_prereleases", "_spec", "_spec_version") + + _operator_regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" + (?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;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # 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 + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # 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 + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [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", + } + + def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.fullmatch(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: {spec!r}") + + 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 + + # Specifier version cache + self._spec_version: tuple[str, Version] | None = None + + def _get_spec_version(self, version: str) -> Version | None: + """One element cache, as only one spec Version is needed per Specifier.""" + if self._spec_version is not None and self._spec_version[0] == version: + return self._spec_version[1] + + version_specifier = _coerce_version(version) + if version_specifier is None: + return None + + self._spec_version = (version, version_specifier) + return version_specifier + + def _require_spec_version(self, version: str) -> Version: + """Get spec version, asserting it's valid (not for === operator). + + This method should only be called for operators where version + strings are guaranteed to be valid PEP 440 versions (not ===). + """ + spec_version = self._get_spec_version(version) + assert spec_version is not None + return spec_version + + @property + def prereleases(self) -> bool | None: + # 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 + + # Only the "!=" operator does not imply prereleases when + # the version in the specifier is a prerelease. + operator, version_str = self._spec + if operator != "!=": + # The == specifier with trailing .* cannot include prereleases + # e.g. "==1.0a1.*" is not valid. + if operator == "==" and version_str.endswith(".*"): + return False + + # "===" can have arbitrary string versions, so we cannot parse + # those, we take prereleases as unknown (None) for those. + version = self._get_spec_version(version_str) + if version is None: + return None + + # For all other operators, use the check if spec Version + # object implies pre-releases. + if version.is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + 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: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> tuple[str, str]: + operator, version = self._spec + if operator == "===" or version.endswith(".*"): + return operator, version + + spec_version = self._require_spec_version(version) + + canonical_version = canonicalize_version( + spec_version, strip_trailing_zero=(operator != "~=") + ) + + return operator, canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + 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 _compare_compatible(self, prospective: Version, 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 = _version_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 + ) + + def _compare_equal(self, prospective: Version, 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. + normalized_prospective = canonicalize_version( + _public_version(prospective), strip_trailing_zero=False + ) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) + # Split the spec out by bangs and dots, and pretend that there is + # an implicit dot in between a release segment and a pre-release segment. + split_spec = _version_split(normalized_spec) + + # Split the prospective version out by bangs and dots, and pretend + # that there is an implicit dot in between a release segment and + # a pre-release segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) + + # 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 = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_spec + else: + # Convert our spec string into a Version + spec_version = self._require_spec_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 = _public_version(prospective) + + return prospective == spec_version + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, 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 _public_version(prospective) <= self._require_spec_version(spec) + + def _compare_greater_than_equal(self, prospective: Version, 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 _public_version(prospective) >= self._require_spec_version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = self._require_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 + and _base_version(prospective) == _base_version(spec) + ): + 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 + + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = self._require_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 + and _base_version(prospective) == _base_version(spec) + ): + 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 and _base_version( + prospective + ) == _base_version(spec): + 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 | str, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: str | Version) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + True + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it will follow the recommendation from + :pep:`440` and match prereleases, as there are no other versions. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + """ + + return bool(list(self.filter([item], prereleases=prereleases))) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + prereleases_versions = [] + found_non_prereleases = False + + # Determine if to include prereleases by default + include_prereleases = ( + prereleases if prereleases is not None else self.prereleases + ) + + # Get the matching operator + operator_callable = self._get_operator(self.operator) + + # Filter versions + for version in iterable: + parsed_version = _coerce_version(version) + if parsed_version is None: + # === operator can match arbitrary (non-version) strings + if self.operator == "===" and self._compare_arbitrary( + version, self.version + ): + yield version + elif operator_callable(parsed_version, self.version): + # If it's not a prerelease or prereleases are allowed, yield it directly + if not parsed_version.is_prerelease or include_prereleases: + found_non_prereleases = True + yield version + # Otherwise collect prereleases for potential later use + elif prereleases is None and self._prereleases is not False: + prereleases_versions.append(version) + + # If no non-prereleases were found and prereleases weren't + # explicitly forbidden, yield the collected prereleases + if ( + not found_non_prereleases + and prereleases is None + and self._prereleases is not False + ): + yield from prereleases_versions + + +_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)") + + +def _version_split(version: str) -> list[str]: + """Split version into components. + + The split components are intended for version comparison. The logic does + not attempt to retain the original version string, so joining the + components back with :func:`_version_join` may not produce the original + version string. + """ + result: list[str] = [] + + epoch, _, rest = version.rpartition("!") + result.append(epoch or "0") + + for item in rest.split("."): + match = _prefix_regex.fullmatch(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _version_join(components: list[str]) -> str: + """Join split version components into a version string. + + This function assumes the input came from :func:`_version_split`, where the + first component must be the epoch (either empty or numeric), and all other + components numeric. + """ + epoch, *rest = components + return f"{epoch}!{'.'.join(rest)}" + + +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.from_iterable(left_split)), + list(itertools.chain.from_iterable(right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + + __slots__ = ("_prereleases", "_specs") + + def __init__( + self, + specifiers: str | Iterable[Specifier] = "", + prereleases: bool | None = None, + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + May also be an iterable of ``Specifier`` instances, which will be used + as is. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + if isinstance(specifiers, str): + # Split on `,` to break each individual specifier into its own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Make each individual specifier a Specifier and save in a frozen set + # for later. + self._specs = frozenset(map(Specifier, split_specifiers)) + else: + # Save the supplied specifiers in a frozen set. + self._specs = frozenset(specifiers) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + @property + def prereleases(self) -> bool | None: + # 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. + if any(s.prereleases for s in self._specs): + return True + + return None + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: SpecifierSet | str) -> SpecifierSet: + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + 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 + ) or 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: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: bool | None = None, + installed: bool | None = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases, as there are no other versions. + :param installed: + Whether or not the item is installed. If set to ``True``, it will + accept prerelease versions even if the specifier does not allow them. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + version = _coerce_version(item) + + if version is not None and installed and version.is_prerelease: + prereleases = True + + check_item = item if version is None else version + return bool(list(self.filter([check_item], prereleases=prereleases))) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # 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 and self.prereleases is not 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: + # When prereleases is None, we need to let all versions through + # the individual filters, then decide about prereleases at the end + # based on whether any non-prereleases matched ALL specs. + for spec in self._specs: + iterable = spec.filter( + iterable, prereleases=True if prereleases is None else prereleases + ) + + if prereleases is not None: + # If we have a forced prereleases value, + # we can immediately return the iterator. + return iter(iterable) + else: + # Handle empty SpecifierSet cases where prereleases is not None. + if prereleases is True: + return iter(iterable) + + if prereleases is False: + return ( + item + for item in iterable + if (version := _coerce_version(item)) is None + or not version.is_prerelease + ) + + # Finally if prereleases is None, apply PEP 440 logic: + # exclude prereleases unless there are no final releases that matched. + filtered_items: list[UnparsedVersionVar] = [] + found_prereleases: list[UnparsedVersionVar] = [] + found_final_release = False + + for item in iterable: + parsed_version = _coerce_version(item) + # Arbitrary strings are always included as it is not + # possible to determine if they are prereleases, + # and they have already passed all specifiers. + if parsed_version is None: + filtered_items.append(item) + found_prereleases.append(item) + elif parsed_version.is_prerelease: + found_prereleases.append(item) + else: + filtered_items.append(item) + found_final_release = True + + return iter(filtered_items if found_final_release else found_prereleases) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/tags.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/tags.py new file mode 100644 index 0000000..5ef27c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/tags.py @@ -0,0 +1,651 @@ +# 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 logging +import platform +import re +import struct +import subprocess +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Any, + Iterable, + Iterator, + Sequence, + Tuple, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +AppleVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = struct.calcsize("P") == 4 + + +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__ = ["_abi", "_hash", "_interpreter", "_platform"] + + 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 __setstate__(self, state: tuple[None, dict[str, Any]]) -> None: + # The cached _hash is wrong when unpickling. + _, slots = state + for k, v in slots.items(): + setattr(self, k, v) + self._hash = hash((self._interpreter, self._abi, self._platform)) + + +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) -> int | str | None: + value: int | str | None = 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("-", "_").replace(" ", "_") + + +def _is_threaded_cpython(abis: list[str]) -> bool: + """ + Determine if the ABI corresponds to a threaded (`--disable-gil`) build. + + The threaded builds are indicated by a "t" in the abiflags. + """ + if len(abis) == 0: + return False + # expect e.g., cp313 + m = re.match(r"cp\d+(.*)", abis[0]) + if not m: + return False + abiflags = m.group(1) + return "t" in abiflags + + +def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`) + builds do not support abi3. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading + + +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]) + threading = 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, 13) and _get_config_var("Py_GIL_DISABLED", warn): + threading = "t" + 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}{threading}") + abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + return abis + + +def cpython_tags( + python_version: PythonVersion | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = 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: + abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: # noqa: PERF203 + pass + + platforms = list(platforms or platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + threading = _is_threaded_cpython(abis) + use_abi3 = _abi3_applies(python_version, threading) + if use_abi3: + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if use_abi3: + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + version = _version_nodot((python_version[0], minor_version)) + interpreter = f"cp{version}" + yield Tag(interpreter, "abi3", platform_) + + +def _generic_abi() -> list[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: str | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = 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 = f"{interp_name}{interp_version}" + abis = _generic_abi() if abis is None else list(abis) + platforms = list(platforms or platform_tags()) + 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: PythonVersion | None = None, + interpreter: str | None = None, + platforms: Iterable[str] | None = 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: AppleVersion, 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: AppleVersion | None = None, arch: str | None = 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("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + text=True, + ).stdout + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) + + if arch is None: + arch = _mac_arch(cpu_arch) + + if (10, 0) <= 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. + major_version = 10 + for minor_version in range(version[1], -1, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{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. + minor_version = 0 + for major_version in range(version[0], 10, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{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. + major_version = 10 + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + else: + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_format = "universal2" + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + +def ios_platforms( + version: AppleVersion | None = None, multiarch: str | None = None +) -> Iterator[str]: + """ + Yields the platform tags for an iOS system. + + :param version: A two-item tuple specifying the iOS version to generate + platform tags for. Defaults to the current iOS version. + :param multiarch: The CPU architecture+ABI to generate platform tags for - + (the value used by `sys.implementation._multiarch` e.g., + `arm64_iphoneos` or `x84_64_iphonesimulator`). Defaults to the current + multiarch value. + """ + if version is None: + # if iOS is the current platform, ios_ver *must* be defined. However, + # it won't exist for CPython versions before 3.13, which causes a mypy + # error. + _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore] + version = cast("AppleVersion", tuple(map(int, release.split(".")[:2]))) + + if multiarch is None: + multiarch = sys.implementation._multiarch + multiarch = multiarch.replace("-", "_") + + ios_platform_template = "ios_{major}_{minor}_{multiarch}" + + # Consider any iOS major.minor version from the version requested, down to + # 12.0. 12.0 is the first iOS version that is known to have enough features + # to support CPython. Consider every possible minor release up to X.9. There + # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra + # candidates that won't ever match doesn't really hurt, and it saves us from + # having to keep an explicit list of known iOS versions in the code. Return + # the results descending order of version number. + + # If the requested major version is less than 12, there won't be any matches. + if version[0] < 12: + return + + # Consider the actual X.Y version that was requested. + yield ios_platform_template.format( + major=version[0], minor=version[1], multiarch=multiarch + ) + + # Consider every minor version from X.0 to the minor version prior to the + # version requested by the platform. + for minor in range(version[1] - 1, -1, -1): + yield ios_platform_template.format( + major=version[0], minor=minor, multiarch=multiarch + ) + + for major in range(version[0] - 1, 11, -1): + for minor in range(9, -1, -1): + yield ios_platform_template.format( + major=major, minor=minor, multiarch=multiarch + ) + + +def android_platforms( + api_level: int | None = None, abi: str | None = None +) -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on + non-Android platforms, the ``api_level`` and ``abi`` arguments are required. + + :param int api_level: The maximum `API level + `__ to return. Defaults + to the current system's version, as returned by ``platform.android_ver``. + :param str abi: The `Android ABI `__, + e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by + ``sysconfig.get_platform``. Hyphens and periods will be replaced with + underscores. + """ + if platform.system() != "Android" and (api_level is None or abi is None): + raise TypeError( + "on non-Android platforms, the api_level and abi arguments are required" + ) + + if api_level is None: + # Python 3.13 was the first version to return platform.system() == "Android", + # and also the first version to define platform.android_ver(). + api_level = platform.android_ver().api_level # type: ignore[attr-defined] + + if abi is None: + abi = sysconfig.get_platform().split("-")[-1] + abi = _normalize_string(abi) + + # 16 is the minimum API level known to have enough features to support CPython + # without major patching. Yield every API level from the maximum down to the + # minimum, inclusive. + min_api_level = 16 + for ver in range(api_level, min_api_level - 1, -1): + yield f"android_{ver}_{abi}" + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if not linux.startswith("linux_"): + # we should never be here, just yield the sysconfig one and return + yield linux + return + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv8l" + _, arch = linux.split("_", 1) + archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + yield from _manylinux.platform_tags(archs) + yield from _musllinux.platform_tags(archs) + for arch in archs: + yield f"linux_{arch}" + + +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() == "iOS": + return ios_platforms() + elif platform.system() == "Android": + return android_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + """ + 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) + return str(version) if version else _version_nodot(sys.version_info[:2]) + + +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": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/utils.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/utils.py new file mode 100644 index 0000000..c41c813 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/utils.py @@ -0,0 +1,158 @@ +# 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 re +from typing import NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version, _TrimmedRelease + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidName(ValueError): + """ + An invalid distribution name; users should refer to the packaging user guide. + """ + + +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. + """ + + +# Core metadata spec for `Name` +_validate_regex = re.compile(r"[A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9]", re.IGNORECASE) +_normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: + if validate and not _validate_regex.fullmatch(name): + raise InvalidName(f"name is invalid: {name!r}") + # Ensure all ``.`` and ``_`` are ``-`` + # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503 + # Much faster than re, and even faster than str.translate + value = name.lower().replace("_", "-").replace(".", "-") + # Condense repeats (faster than regex) + while "--" in value: + value = value.replace("--", "-") + return cast("NormalizedName", value) + + +def is_normalized_name(name: str) -> bool: + return _normalized_regex.fullmatch(name) is not None + + +def canonicalize_version( + version: Version | str, *, strip_trailing_zero: bool = True +) -> str: + """ + Return a canonical form of a version as a string. + + >>> canonicalize_version('1.0.1') + '1.0.1' + + Per PEP 625, versions may have multiple canonical forms, differing + only by trailing zeros. + + >>> canonicalize_version('1.0.0') + '1' + >>> canonicalize_version('1.0.0', strip_trailing_zero=False) + '1.0.0' + + Invalid versions are returned unaltered. + + >>> canonicalize_version('foo bar baz') + 'foo bar baz' + """ + if isinstance(version, str): + try: + version = Version(version) + except InvalidVersion: + return str(version) + return str(_TrimmedRelease(version) if strip_trailing_zero else version) + + +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!r}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename!r}" + ) + + 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!r}") + name = canonicalize_name(name_part) + + try: + version = Version(parts[1]) + except InvalidVersion as e: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid version): {filename!r}" + ) from e + + 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!r}" + ) + 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!r}" + ) + + # 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!r}") + + name = canonicalize_name(name_part) + + try: + version = Version(version_part) + except InvalidVersion as e: + raise InvalidSdistFilename( + f"Invalid sdist filename (invalid version): {filename!r}" + ) from e + + return (name, version) diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/version.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/version.py new file mode 100644 index 0000000..1206c46 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/packaging/version.py @@ -0,0 +1,792 @@ +# 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. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" + +from __future__ import annotations + +import re +import sys +import typing +from typing import ( + Any, + Callable, + Literal, + NamedTuple, + SupportsInt, + Tuple, + TypedDict, + Union, +) + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +if typing.TYPE_CHECKING: + from typing_extensions import Self, Unpack + +if sys.version_info >= (3, 13): # pragma: no cover + from warnings import deprecated as _deprecated +elif typing.TYPE_CHECKING: + from typing_extensions import deprecated as _deprecated +else: # pragma: no cover + import functools + import warnings + + def _deprecated(message: str) -> object: + def decorator(func: object) -> object: + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + warnings.warn( + message, + category=DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return decorator + + +_LETTER_NORMALIZATION = { + "alpha": "a", + "beta": "b", + "c": "rc", + "pre": "rc", + "preview": "rc", + "rev": "post", + "r": "post", +} + +__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "parse"] + +LocalType = Tuple[Union[int, str], ...] + +CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, Tuple[str, int]] +CmpLocalType = Union[ + NegativeInfinityType, + Tuple[Union[Tuple[int, str], Tuple[NegativeInfinityType, Union[int, str]]], ...], +] +CmpKey = Tuple[ + int, + Tuple[int, ...], + CmpPrePostDevType, + CmpPrePostDevType, + CmpPrePostDevType, + CmpLocalType, +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + + +class _VersionReplace(TypedDict, total=False): + epoch: int | None + release: tuple[int, ...] | None + pre: tuple[Literal["a", "b", "rc"], int] | None + post: int | None + dev: int | None + local: str | None + + +def parse(version: str) -> Version: + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + __slots__ = () + + # This can also be a normal member (see the packaging_legacy package); + # we are just requiring it to be readable. Actually defining a property + # has runtime effect on subclasses, so it's typing only. + if typing.TYPE_CHECKING: + + @property + def _key(self) -> tuple[Any, ...]: ... + + 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 + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse + +# Note that ++ doesn't behave identically on CPython and PyPy, so not using it here +_VERSION_PATTERN = r""" + v?+ # optional leading v + (?: + (?:(?P[0-9]+)!)?+ # epoch + (?P[0-9]+(?:\.[0-9]+)*+) # release segment + (?P
                                          # pre-release
+            [._-]?+
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [._-]?
+                (?Ppost|rev|r)
+                [._-]?
+                (?P[0-9]+)?
+            )
+        )?+
+        (?P                                          # dev release
+            [._-]?+
+            (?Pdev)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+    )
+    (?:\+
+        (?P                                        # local version
+            [a-z0-9]+
+            (?:[._-][a-z0-9]+)*+
+        )
+    )?+
+"""
+
+_VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
+
+# Possessive qualifiers were added in Python 3.11.
+# CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
+# Older PyPy also had a bug.
+VERSION_PATTERN = (
+    _VERSION_PATTERN_OLD
+    if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
+    or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
+    or sys.version_info < (3, 11)
+    else _VERSION_PATTERN
+)
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+:meta hide-value:
+"""
+
+
+# Validation pattern for local version in replace()
+_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE)
+
+
+def _validate_epoch(value: object, /) -> int:
+    epoch = value or 0
+    if isinstance(epoch, int) and epoch >= 0:
+        return epoch
+    msg = f"epoch must be non-negative integer, got {epoch}"
+    raise InvalidVersion(msg)
+
+
+def _validate_release(value: object, /) -> tuple[int, ...]:
+    release = (0,) if value is None else value
+    if (
+        isinstance(release, tuple)
+        and len(release) > 0
+        and all(isinstance(i, int) and i >= 0 for i in release)
+    ):
+        return release
+    msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
+    raise InvalidVersion(msg)
+
+
+def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
+    if value is None:
+        return value
+    if (
+        isinstance(value, tuple)
+        and len(value) == 2
+        and value[0] in ("a", "b", "rc")
+        and isinstance(value[1], int)
+        and value[1] >= 0
+    ):
+        return value
+    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("post", value)
+    msg = f"post must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("dev", value)
+    msg = f"dev must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_local(value: object, /) -> LocalType | None:
+    if value is None:
+        return value
+    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
+        return _parse_local_version(value)
+    msg = f"local must be a valid version string, got {value!r}"
+    raise InvalidVersion(msg)
+
+
+# Backward compatibility for internals before 26.0. Do not use.
+class _Version(NamedTuple):
+    epoch: int
+    release: tuple[int, ...]
+    dev: tuple[str, int] | None
+    pre: tuple[str, int] | None
+    post: tuple[str, int] | None
+    local: LocalType | None
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+    """
+
+    __slots__ = ("_dev", "_epoch", "_key_cache", "_local", "_post", "_pre", "_release")
+    __match_args__ = ("_str",)
+
+    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
+
+    _epoch: int
+    _release: tuple[int, ...]
+    _dev: tuple[str, int] | None
+    _pre: tuple[str, int] | None
+    _post: tuple[str, int] | None
+    _local: LocalType | None
+
+    _key_cache: CmpKey | None
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+        # Validate the version and parse it into pieces
+        match = self._regex.fullmatch(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: {version!r}")
+        self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
+        self._release = tuple(map(int, match.group("release").split(".")))
+        self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n"))
+        self._post = _parse_letter_version(
+            match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+        )
+        self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n"))
+        self._local = _parse_local_version(match.group("local"))
+
+        # Key which will be used for sorting
+        self._key_cache = None
+
+    def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
+        epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
+        release = (
+            _validate_release(kwargs["release"])
+            if "release" in kwargs
+            else self._release
+        )
+        pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
+        post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
+        dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
+        local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
+
+        if (
+            epoch == self._epoch
+            and release == self._release
+            and pre == self._pre
+            and post == self._post
+            and dev == self._dev
+            and local == self._local
+        ):
+            return self
+
+        new_version = self.__class__.__new__(self.__class__)
+        new_version._key_cache = None
+        new_version._epoch = epoch
+        new_version._release = release
+        new_version._pre = pre
+        new_version._post = post
+        new_version._dev = dev
+        new_version._local = local
+
+        return new_version
+
+    @property
+    def _key(self) -> CmpKey:
+        if self._key_cache is None:
+            self._key_cache = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        return self._key_cache
+
+    @property
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self) -> _Version:
+        return _Version(
+            self._epoch, self._release, self._dev, self._pre, self._post, self._local
+        )
+
+    @_version.setter
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self, value: _Version) -> None:
+        self._epoch = value.epoch
+        self._release = value.release
+        self._dev = value.dev
+        self._pre = value.pre
+        self._post = value.post
+        self._local = value.local
+        self._key_cache = None
+
+    def __repr__(self) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f""
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be round-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        # This is a hot function, so not calling self.base_version
+        version = ".".join(map(str, self.release))
+
+        # Epoch
+        if self.epoch:
+            version = f"{self.epoch}!{version}"
+
+        # Pre-release
+        if self.pre is not None:
+            version += "".join(map(str, self.pre))
+
+        # Post-release
+        if self.post is not None:
+            version += f".post{self.post}"
+
+        # Development release
+        if self.dev is not None:
+            version += f".dev{self.dev}"
+
+        # Local version segment
+        if self.local is not None:
+            version += f"+{self.local}"
+
+        return version
+
+    @property
+    def _str(self) -> str:
+        """Internal property for match_args"""
+        return str(self)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._epoch
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._release
+
+    @property
+    def pre(self) -> tuple[str, int] | None:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._pre
+
+    @property
+    def post(self) -> int | None:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._post[1] if self._post else None
+
+    @property
+    def dev(self) -> int | None:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._dev[1] if self._dev else None
+
+    @property
+    def local(self) -> str | None:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._local:
+            return ".".join(str(x) for x in self._local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").public
+        '1!1.2.3.dev1'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        release_segment = ".".join(map(str, self.release))
+        return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+class _TrimmedRelease(Version):
+    __slots__ = ()
+
+    def __init__(self, version: str | Version) -> None:
+        if isinstance(version, Version):
+            self._epoch = version._epoch
+            self._release = version._release
+            self._dev = version._dev
+            self._pre = version._pre
+            self._post = version._post
+            self._local = version._local
+            self._key_cache = version._key_cache
+            return
+        super().__init__(version)  # pragma: no cover
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """
+        Release segment without any trailing zeros.
+
+        >>> _TrimmedRelease('1.0.0').release
+        (1,)
+        >>> _TrimmedRelease('0.0').release
+        (0,)
+        """
+        # This leaves one 0.
+        rel = super().release
+        len_release = len(rel)
+        i = len_release
+        while i > 1 and rel[i - 1] == 0:
+            i -= 1
+        return rel if i == len_release else rel[:i]
+
+
+def _parse_letter_version(
+    letter: str | None, number: str | bytes | SupportsInt | None
+) -> tuple[str, int] | None:
+    if letter:
+        # 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.
+        letter = _LETTER_NORMALIZATION.get(letter, letter)
+
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        return letter, int(number or 0)
+
+    if 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)
+        return "post", int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local: str | None) -> LocalType | None:
+    """
+    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: tuple[str, int] | None,
+    post: tuple[str, int] | None,
+    dev: tuple[str, int] | None,
+    local: LocalType | None,
+) -> CmpKey:
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. We will use this for our sorting key.
+    len_release = len(release)
+    i = len_release
+    while i and release[i - 1] == 0:
+        i -= 1
+    _release = release if i == len_release else release[:i]
+
+    # 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: CmpPrePostDevType = 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: CmpPrePostDevType = NegativeInfinity
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev: CmpPrePostDevType = Infinity
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local: CmpLocalType = 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/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/METADATA
new file mode 100644
index 0000000..6b0908f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/METADATA
@@ -0,0 +1,350 @@
+Metadata-Version: 2.4
+Name: platformdirs
+Version: 4.4.0
+Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`.
+Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases
+Project-URL: Documentation, https://platformdirs.readthedocs.io
+Project-URL: Homepage, https://github.com/tox-dev/platformdirs
+Project-URL: Source, https://github.com/tox-dev/platformdirs
+Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues
+Maintainer-email: Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt 
+License-Expression: MIT
+License-File: LICENSE
+Keywords: appdirs,application,cache,directory,log,user
+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 :: Only
+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 :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.9
+Provides-Extra: docs
+Requires-Dist: furo>=2024.8.6; extra == 'docs'
+Requires-Dist: proselint>=0.14; extra == 'docs'
+Requires-Dist: sphinx-autodoc-typehints>=3; extra == 'docs'
+Requires-Dist: sphinx>=8.1.3; extra == 'docs'
+Provides-Extra: test
+Requires-Dist: appdirs==1.4.4; extra == 'test'
+Requires-Dist: covdefaults>=2.3; extra == 'test'
+Requires-Dist: pytest-cov>=6; extra == 'test'
+Requires-Dist: pytest-mock>=3.14; extra == 'test'
+Requires-Dist: pytest>=8.3.4; extra == 'test'
+Provides-Extra: type
+Requires-Dist: mypy>=1.14.1; extra == 'type'
+Description-Content-Type: text/x-rst
+
+The problem
+===========
+
+.. image:: https://badge.fury.io/py/platformdirs.svg
+   :target: https://badge.fury.io/py/platformdirs
+.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg
+   :target: https://pypi.python.org/pypi/platformdirs/
+.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg
+   :target: https://github.com/platformdirs/platformdirs/actions
+.. image:: https://static.pepy.tech/badge/platformdirs/month
+   :target: https://pepy.tech/project/platformdirs
+
+When writing desktop application, finding the right location to store user data
+and configuration varies per platform. Even for single-platform apps, there
+may by plenty of nuances in figuring out the right location.
+
+For example, if running on macOS, you should use::
+
+    ~/Library/Application Support/
+
+If on Windows (at least English Win) that should be::
+
+    C:\Documents and Settings\\Application Data\Local Settings\\
+
+or possibly::
+
+    C:\Documents and Settings\\Application Data\\
+
+for `roaming profiles `_ but that is another story.
+
+On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be::
+
+    ~/.local/share/
+
+.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
+
+``platformdirs`` to the rescue
+==============================
+
+This kind of thing is what the ``platformdirs`` package is for.
+``platformdirs`` will help you choose an appropriate:
+
+- user data dir (``user_data_dir``)
+- user config dir (``user_config_dir``)
+- user cache dir (``user_cache_dir``)
+- site data dir (``site_data_dir``)
+- site config dir (``site_config_dir``)
+- user log dir (``user_log_dir``)
+- user documents dir (``user_documents_dir``)
+- user downloads dir (``user_downloads_dir``)
+- user pictures dir (``user_pictures_dir``)
+- user videos dir (``user_videos_dir``)
+- user music dir (``user_music_dir``)
+- user desktop dir (``user_desktop_dir``)
+- user runtime dir (``user_runtime_dir``)
+
+And also:
+
+- Is slightly opinionated on the directory names used. Look for "OPINION" in
+  documentation and code for when an opinion is being applied.
+
+Example output
+==============
+
+On macOS:
+
+.. code-block:: pycon
+
+    >>> from platformdirs import *
+    >>> appname = "SuperApp"
+    >>> appauthor = "Acme"
+    >>> user_data_dir(appname, appauthor)
+    '/Users/trentm/Library/Application Support/SuperApp'
+    >>> user_config_dir(appname, appauthor)
+    '/Users/trentm/Library/Application Support/SuperApp'
+    >>> user_cache_dir(appname, appauthor)
+    '/Users/trentm/Library/Caches/SuperApp'
+    >>> site_data_dir(appname, appauthor)
+    '/Library/Application Support/SuperApp'
+    >>> site_config_dir(appname, appauthor)
+    '/Library/Application Support/SuperApp'
+    >>> user_log_dir(appname, appauthor)
+    '/Users/trentm/Library/Logs/SuperApp'
+    >>> user_documents_dir()
+    '/Users/trentm/Documents'
+    >>> user_downloads_dir()
+    '/Users/trentm/Downloads'
+    >>> user_pictures_dir()
+    '/Users/trentm/Pictures'
+    >>> user_videos_dir()
+    '/Users/trentm/Movies'
+    >>> user_music_dir()
+    '/Users/trentm/Music'
+    >>> user_desktop_dir()
+    '/Users/trentm/Desktop'
+    >>> user_runtime_dir(appname, appauthor)
+    '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
+
+On Windows:
+
+.. code-block:: pycon
+
+    >>> from platformdirs import *
+    >>> appname = "SuperApp"
+    >>> appauthor = "Acme"
+    >>> user_data_dir(appname, appauthor)
+    'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
+    >>> user_data_dir(appname, appauthor, roaming=True)
+    'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
+    >>> user_config_dir(appname, appauthor)
+    'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
+    >>> user_cache_dir(appname, appauthor)
+    'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
+    >>> site_data_dir(appname, appauthor)
+    'C:\\ProgramData\\Acme\\SuperApp'
+    >>> site_config_dir(appname, appauthor)
+    'C:\\ProgramData\\Acme\\SuperApp'
+    >>> user_log_dir(appname, appauthor)
+    'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
+    >>> user_documents_dir()
+    'C:\\Users\\trentm\\Documents'
+    >>> user_downloads_dir()
+    'C:\\Users\\trentm\\Downloads'
+    >>> user_pictures_dir()
+    'C:\\Users\\trentm\\Pictures'
+    >>> user_videos_dir()
+    'C:\\Users\\trentm\\Videos'
+    >>> user_music_dir()
+    'C:\\Users\\trentm\\Music'
+    >>> user_desktop_dir()
+    'C:\\Users\\trentm\\Desktop'
+    >>> user_runtime_dir(appname, appauthor)
+    'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp'
+
+On Linux:
+
+.. code-block:: pycon
+
+    >>> from platformdirs import *
+    >>> appname = "SuperApp"
+    >>> appauthor = "Acme"
+    >>> user_data_dir(appname, appauthor)
+    '/home/trentm/.local/share/SuperApp'
+    >>> user_config_dir(appname)
+    '/home/trentm/.config/SuperApp'
+    >>> user_cache_dir(appname, appauthor)
+    '/home/trentm/.cache/SuperApp'
+    >>> site_data_dir(appname, appauthor)
+    '/usr/local/share/SuperApp'
+    >>> site_data_dir(appname, appauthor, multipath=True)
+    '/usr/local/share/SuperApp:/usr/share/SuperApp'
+    >>> site_config_dir(appname)
+    '/etc/xdg/SuperApp'
+    >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc"
+    >>> site_config_dir(appname, multipath=True)
+    '/etc/SuperApp:/usr/local/etc/SuperApp'
+    >>> user_log_dir(appname, appauthor)
+    '/home/trentm/.local/state/SuperApp/log'
+    >>> user_documents_dir()
+    '/home/trentm/Documents'
+    >>> user_downloads_dir()
+    '/home/trentm/Downloads'
+    >>> user_pictures_dir()
+    '/home/trentm/Pictures'
+    >>> user_videos_dir()
+    '/home/trentm/Videos'
+    >>> user_music_dir()
+    '/home/trentm/Music'
+    >>> user_desktop_dir()
+    '/home/trentm/Desktop'
+    >>> user_runtime_dir(appname, appauthor)
+    '/run/user/{os.getuid()}/SuperApp'
+
+On Android::
+
+    >>> from platformdirs import *
+    >>> appname = "SuperApp"
+    >>> appauthor = "Acme"
+    >>> user_data_dir(appname, appauthor)
+    '/data/data/com.myApp/files/SuperApp'
+    >>> user_config_dir(appname)
+    '/data/data/com.myApp/shared_prefs/SuperApp'
+    >>> user_cache_dir(appname, appauthor)
+    '/data/data/com.myApp/cache/SuperApp'
+    >>> site_data_dir(appname, appauthor)
+    '/data/data/com.myApp/files/SuperApp'
+    >>> site_config_dir(appname)
+    '/data/data/com.myApp/shared_prefs/SuperApp'
+    >>> user_log_dir(appname, appauthor)
+    '/data/data/com.myApp/cache/SuperApp/log'
+    >>> user_documents_dir()
+    '/storage/emulated/0/Documents'
+    >>> user_downloads_dir()
+    '/storage/emulated/0/Downloads'
+    >>> user_pictures_dir()
+    '/storage/emulated/0/Pictures'
+    >>> user_videos_dir()
+    '/storage/emulated/0/DCIM/Camera'
+    >>> user_music_dir()
+    '/storage/emulated/0/Music'
+    >>> user_desktop_dir()
+    '/storage/emulated/0/Desktop'
+    >>> user_runtime_dir(appname, appauthor)
+    '/data/data/com.myApp/cache/SuperApp/tmp'
+
+Note: Some android apps like Termux and Pydroid are used as shells. These
+apps are used by the end user to emulate Linux environment. Presence of
+``SHELL`` environment variable is used by Platformdirs to differentiate
+between general android apps and android apps used as shells. Shell android
+apps also support ``XDG_*`` environment variables.
+
+
+``PlatformDirs`` for convenience
+================================
+
+.. code-block:: pycon
+
+    >>> from platformdirs import PlatformDirs
+    >>> dirs = PlatformDirs("SuperApp", "Acme")
+    >>> dirs.user_data_dir
+    '/Users/trentm/Library/Application Support/SuperApp'
+    >>> dirs.user_config_dir
+    '/Users/trentm/Library/Application Support/SuperApp'
+    >>> dirs.user_cache_dir
+    '/Users/trentm/Library/Caches/SuperApp'
+    >>> dirs.site_data_dir
+    '/Library/Application Support/SuperApp'
+    >>> dirs.site_config_dir
+    '/Library/Application Support/SuperApp'
+    >>> dirs.user_cache_dir
+    '/Users/trentm/Library/Caches/SuperApp'
+    >>> dirs.user_log_dir
+    '/Users/trentm/Library/Logs/SuperApp'
+    >>> dirs.user_documents_dir
+    '/Users/trentm/Documents'
+    >>> dirs.user_downloads_dir
+    '/Users/trentm/Downloads'
+    >>> dirs.user_pictures_dir
+    '/Users/trentm/Pictures'
+    >>> dirs.user_videos_dir
+    '/Users/trentm/Movies'
+    >>> dirs.user_music_dir
+    '/Users/trentm/Music'
+    >>> dirs.user_desktop_dir
+    '/Users/trentm/Desktop'
+    >>> dirs.user_runtime_dir
+    '/Users/trentm/Library/Caches/TemporaryItems/SuperApp'
+
+Per-version isolation
+=====================
+
+If you have multiple versions of your app in use that you want to be
+able to run side-by-side, then you may want version-isolation for these
+dirs::
+
+    >>> from platformdirs import PlatformDirs
+    >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0")
+    >>> dirs.user_data_dir
+    '/Users/trentm/Library/Application Support/SuperApp/1.0'
+    >>> dirs.user_config_dir
+    '/Users/trentm/Library/Application Support/SuperApp/1.0'
+    >>> dirs.user_cache_dir
+    '/Users/trentm/Library/Caches/SuperApp/1.0'
+    >>> dirs.site_data_dir
+    '/Library/Application Support/SuperApp/1.0'
+    >>> dirs.site_config_dir
+    '/Library/Application Support/SuperApp/1.0'
+    >>> dirs.user_log_dir
+    '/Users/trentm/Library/Logs/SuperApp/1.0'
+    >>> dirs.user_documents_dir
+    '/Users/trentm/Documents'
+    >>> dirs.user_downloads_dir
+    '/Users/trentm/Downloads'
+    >>> dirs.user_pictures_dir
+    '/Users/trentm/Pictures'
+    >>> dirs.user_videos_dir
+    '/Users/trentm/Movies'
+    >>> dirs.user_music_dir
+    '/Users/trentm/Music'
+    >>> dirs.user_desktop_dir
+    '/Users/trentm/Desktop'
+    >>> dirs.user_runtime_dir
+    '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0'
+
+Be wary of using this for configuration files though; you'll need to handle
+migrating configuration files manually.
+
+Why this Fork?
+==============
+
+This repository is a friendly fork of the wonderful work started by
+`ActiveState `_ who created
+``appdirs``, this package's ancestor.
+
+Maintaining an open source project is no easy task, particularly
+from within an organization, and the Python community is indebted
+to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for
+creating an incredibly useful simple module, as evidenced by the wide
+number of users it has attracted over the years.
+
+Nonetheless, given the number of long-standing open issues
+and pull requests, and no clear path towards `ensuring
+that maintenance of the package would continue or grow
+`_, this fork was
+created.
+
+Contributions are most welcome.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/RECORD
new file mode 100644
index 0000000..09572f1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/RECORD
@@ -0,0 +1,15 @@
+platformdirs-4.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+platformdirs-4.4.0.dist-info/METADATA,sha256=u8UhbV9Md7-8VyJyZNUuZrzN5xzPeedeGmBG0CnTAiM,12831
+platformdirs-4.4.0.dist-info/RECORD,,
+platformdirs-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+platformdirs-4.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
+platformdirs-4.4.0.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
+platformdirs/__init__.py,sha256=iORRy6_lZ9tXLvO0W6fJPn8QV7F532ivl-f2WGmabBc,22284
+platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493
+platformdirs/android.py,sha256=r0DshVBf-RO1jXJGX8C4Til7F1XWt-bkdWMgmvEiaYg,9013
+platformdirs/api.py,sha256=wPHOlwOsfz2oqQZ6A2FcCu5kEAj-JondzoNOHYFQ0h8,9281
+platformdirs/macos.py,sha256=0XoOgin1NK7Qki7iskD-oS8xKxw6bXgoKEgdqpCRAFQ,6322
+platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+platformdirs/unix.py,sha256=WZmkUA--L3JNRGmz32s35YfoD3ica6xKIPdCV_HhLcs,10458
+platformdirs/version.py,sha256=i31fi3nNO19D2FdSx8aldD7IFLSqm2YrAo6SmkV0FLM,704
+platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/WHEEL
new file mode 100644
index 0000000..12228d4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.27.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..f35fed9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2010-202x The platformdirs developers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__init__.py
new file mode 100644
index 0000000..02daa59
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__init__.py
@@ -0,0 +1,631 @@
+"""
+Utilities for determining application-specific dirs.
+
+See  for details and usage.
+
+"""
+
+from __future__ import annotations
+
+import os
+import sys
+from typing import TYPE_CHECKING
+
+from .api import PlatformDirsABC
+from .version import __version__
+from .version import __version_tuple__ as __version_info__
+
+if TYPE_CHECKING:
+    from pathlib import Path
+    from typing import Literal
+
+if sys.platform == "win32":
+    from platformdirs.windows import Windows as _Result
+elif sys.platform == "darwin":
+    from platformdirs.macos import MacOS as _Result
+else:
+    from platformdirs.unix import Unix as _Result
+
+
+def _set_platform_dir_class() -> type[PlatformDirsABC]:
+    if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
+        if os.getenv("SHELL") or os.getenv("PREFIX"):
+            return _Result
+
+        from platformdirs.android import _android_folder  # noqa: PLC0415
+
+        if _android_folder() is not None:
+            from platformdirs.android import Android  # noqa: PLC0415
+
+            return Android  # return to avoid redefinition of a result
+
+    return _Result
+
+
+if TYPE_CHECKING:
+    # Work around mypy issue: https://github.com/python/mypy/issues/10962
+    PlatformDirs = _Result
+else:
+    PlatformDirs = _set_platform_dir_class()  #: Currently active platform
+AppDirs = PlatformDirs  #: Backwards compatibility with appdirs
+
+
+def user_data_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: data directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_data_dir
+
+
+def site_data_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: data directory shared by users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_data_dir
+
+
+def user_config_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: config directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_config_dir
+
+
+def site_config_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: config directory shared by the users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_config_dir
+
+
+def user_cache_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: cache directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_cache_dir
+
+
+def site_cache_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: cache directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_cache_dir
+
+
+def user_state_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: state directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_state_dir
+
+
+def user_log_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: log directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_log_dir
+
+
+def user_documents_dir() -> str:
+    """:returns: documents directory tied to the user"""
+    return PlatformDirs().user_documents_dir
+
+
+def user_downloads_dir() -> str:
+    """:returns: downloads directory tied to the user"""
+    return PlatformDirs().user_downloads_dir
+
+
+def user_pictures_dir() -> str:
+    """:returns: pictures directory tied to the user"""
+    return PlatformDirs().user_pictures_dir
+
+
+def user_videos_dir() -> str:
+    """:returns: videos directory tied to the user"""
+    return PlatformDirs().user_videos_dir
+
+
+def user_music_dir() -> str:
+    """:returns: music directory tied to the user"""
+    return PlatformDirs().user_music_dir
+
+
+def user_desktop_dir() -> str:
+    """:returns: desktop directory tied to the user"""
+    return PlatformDirs().user_desktop_dir
+
+
+def user_runtime_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: runtime directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_runtime_dir
+
+
+def site_runtime_dir(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: runtime directory shared by users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_runtime_dir
+
+
+def user_data_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: data path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_data_path
+
+
+def site_data_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: data path shared by users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_data_path
+
+
+def user_config_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: config path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_config_path
+
+
+def site_config_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    multipath: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: config path shared by the users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        multipath=multipath,
+        ensure_exists=ensure_exists,
+    ).site_config_path
+
+
+def site_cache_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: cache directory tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_cache_path
+
+
+def user_cache_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: cache path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_cache_path
+
+
+def user_state_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    roaming: bool = False,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: state path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        roaming=roaming,
+        ensure_exists=ensure_exists,
+    ).user_state_path
+
+
+def user_log_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: log path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_log_path
+
+
+def user_documents_path() -> Path:
+    """:returns: documents a path tied to the user"""
+    return PlatformDirs().user_documents_path
+
+
+def user_downloads_path() -> Path:
+    """:returns: downloads path tied to the user"""
+    return PlatformDirs().user_downloads_path
+
+
+def user_pictures_path() -> Path:
+    """:returns: pictures path tied to the user"""
+    return PlatformDirs().user_pictures_path
+
+
+def user_videos_path() -> Path:
+    """:returns: videos path tied to the user"""
+    return PlatformDirs().user_videos_path
+
+
+def user_music_path() -> Path:
+    """:returns: music path tied to the user"""
+    return PlatformDirs().user_music_path
+
+
+def user_desktop_path() -> Path:
+    """:returns: desktop path tied to the user"""
+    return PlatformDirs().user_desktop_path
+
+
+def user_runtime_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: runtime path tied to the user
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).user_runtime_path
+
+
+def site_runtime_path(
+    appname: str | None = None,
+    appauthor: str | Literal[False] | None = None,
+    version: str | None = None,
+    opinion: bool = True,  # noqa: FBT001, FBT002
+    ensure_exists: bool = False,  # noqa: FBT001, FBT002
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :param ensure_exists: See `ensure_exists `.
+    :returns: runtime path shared by users
+    """
+    return PlatformDirs(
+        appname=appname,
+        appauthor=appauthor,
+        version=version,
+        opinion=opinion,
+        ensure_exists=ensure_exists,
+    ).site_runtime_path
+
+
+__all__ = [
+    "AppDirs",
+    "PlatformDirs",
+    "PlatformDirsABC",
+    "__version__",
+    "__version_info__",
+    "site_cache_dir",
+    "site_cache_path",
+    "site_config_dir",
+    "site_config_path",
+    "site_data_dir",
+    "site_data_path",
+    "site_runtime_dir",
+    "site_runtime_path",
+    "user_cache_dir",
+    "user_cache_path",
+    "user_config_dir",
+    "user_config_path",
+    "user_data_dir",
+    "user_data_path",
+    "user_desktop_dir",
+    "user_desktop_path",
+    "user_documents_dir",
+    "user_documents_path",
+    "user_downloads_dir",
+    "user_downloads_path",
+    "user_log_dir",
+    "user_log_path",
+    "user_music_dir",
+    "user_music_path",
+    "user_pictures_dir",
+    "user_pictures_path",
+    "user_runtime_dir",
+    "user_runtime_path",
+    "user_state_dir",
+    "user_state_path",
+    "user_videos_dir",
+    "user_videos_path",
+]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__main__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__main__.py
new file mode 100644
index 0000000..922c521
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/__main__.py
@@ -0,0 +1,55 @@
+"""Main entry point."""
+
+from __future__ import annotations
+
+from platformdirs import PlatformDirs, __version__
+
+PROPS = (
+    "user_data_dir",
+    "user_config_dir",
+    "user_cache_dir",
+    "user_state_dir",
+    "user_log_dir",
+    "user_documents_dir",
+    "user_downloads_dir",
+    "user_pictures_dir",
+    "user_videos_dir",
+    "user_music_dir",
+    "user_runtime_dir",
+    "site_data_dir",
+    "site_config_dir",
+    "site_cache_dir",
+    "site_runtime_dir",
+)
+
+
+def main() -> None:
+    """Run the main entry point."""
+    app_name = "MyApp"
+    app_author = "MyCompany"
+
+    print(f"-- platformdirs {__version__} --")  # noqa: T201
+
+    print("-- app dirs (with optional 'version')")  # noqa: T201
+    dirs = PlatformDirs(app_name, app_author, version="1.0")
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (without optional 'version')")  # noqa: T201
+    dirs = PlatformDirs(app_name, app_author)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (without optional 'appauthor')")  # noqa: T201
+    dirs = PlatformDirs(app_name)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+    print("\n-- app dirs (with disabled 'appauthor')")  # noqa: T201
+    dirs = PlatformDirs(app_name, appauthor=False)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")  # noqa: T201
+
+
+if __name__ == "__main__":
+    main()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/android.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/android.py
new file mode 100644
index 0000000..92efc85
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/android.py
@@ -0,0 +1,249 @@
+"""Android."""
+
+from __future__ import annotations
+
+import os
+import re
+import sys
+from functools import lru_cache
+from typing import TYPE_CHECKING, cast
+
+from .api import PlatformDirsABC
+
+
+class Android(PlatformDirsABC):
+    """
+    Follows the guidance `from here `_.
+
+    Makes use of the `appname `, `version
+    `, `ensure_exists `.
+
+    """
+
+    @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.,``/data/user///cache/``"""
+        return self._append_app_name_and_version(cast("str", _android_folder()), "cache")
+
+    @property
+    def site_cache_dir(self) -> str:
+        """:return: cache directory shared by users, same as `user_cache_dir`"""
+        return self.user_cache_dir
+
+    @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")  # noqa: PTH118
+        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_downloads_dir(self) -> str:
+        """:return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``"""
+        return _android_downloads_folder()
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``"""
+        return _android_pictures_folder()
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``"""
+        return _android_videos_folder()
+
+    @property
+    def user_music_dir(self) -> str:
+        """:return: music directory tied to the user e.g. ``/storage/emulated/0/Music``"""
+        return _android_music_folder()
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:return: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``"""
+        return "/storage/emulated/0/Desktop"
+
+    @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")  # noqa: PTH118
+        return path
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:return: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+
+@lru_cache(maxsize=1)
+def _android_folder() -> str | None:  # noqa: C901
+    """:return: base folder for the Android OS or None if it cannot be found"""
+    result: str | None = None
+    # type checker isn't happy with our "import android", just don't do this when type checking see
+    # https://stackoverflow.com/a/61394121
+    if not TYPE_CHECKING:
+        try:
+            # First try to get a path to android app using python4android (if available)...
+            from android import mActivity  # noqa: PLC0415
+
+            context = cast("android.content.Context", mActivity.getApplicationContext())  # noqa: F821
+            result = context.getFilesDir().getParentFile().getAbsolutePath()
+        except Exception:  # noqa: BLE001
+            result = None
+    if result is None:
+        try:
+            # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful
+            # result...
+            from jnius import autoclass  # noqa: PLC0415
+
+            context = autoclass("android.content.Context")
+            result = context.getFilesDir().getParentFile().getAbsolutePath()
+        except Exception:  # noqa: BLE001
+            result = None
+    if result is None:
+        # and if that fails, too, find an android folder looking at path on the sys.path
+        # warning: only works for apps installed under /data, not adopted storage etc.
+        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
+    if result is None:
+        # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into
+        # account
+        pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(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  # noqa: PLC0415
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        documents_dir = "/storage/emulated/0/Documents"
+
+    return documents_dir
+
+
+@lru_cache(maxsize=1)
+def _android_downloads_folder() -> str:
+    """:return: downloads folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        downloads_dir = "/storage/emulated/0/Downloads"
+
+    return downloads_dir
+
+
+@lru_cache(maxsize=1)
+def _android_pictures_folder() -> str:
+    """:return: pictures folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        pictures_dir = "/storage/emulated/0/Pictures"
+
+    return pictures_dir
+
+
+@lru_cache(maxsize=1)
+def _android_videos_folder() -> str:
+    """:return: videos folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        videos_dir = "/storage/emulated/0/DCIM/Camera"
+
+    return videos_dir
+
+
+@lru_cache(maxsize=1)
+def _android_music_folder() -> str:
+    """:return: music folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass  # noqa: PLC0415
+
+        context = autoclass("android.content.Context")
+        environment = autoclass("android.os.Environment")
+        music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath()
+    except Exception:  # noqa: BLE001
+        music_dir = "/storage/emulated/0/Music"
+
+    return music_dir
+
+
+__all__ = [
+    "Android",
+]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/api.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/api.py
new file mode 100644
index 0000000..251600e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/api.py
@@ -0,0 +1,299 @@
+"""Base API."""
+
+from __future__ import annotations
+
+import os
+from abc import ABC, abstractmethod
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+    from typing import Literal
+
+
+class PlatformDirsABC(ABC):  # noqa: PLR0904
+    """Abstract base class for platform directories."""
+
+    def __init__(  # noqa: PLR0913, PLR0917
+        self,
+        appname: str | None = None,
+        appauthor: str | Literal[False] | None = None,
+        version: str | None = None,
+        roaming: bool = False,  # noqa: FBT001, FBT002
+        multipath: bool = False,  # noqa: FBT001, FBT002
+        opinion: bool = True,  # noqa: FBT001, FBT002
+        ensure_exists: bool = False,  # noqa: FBT001, FBT002
+    ) -> None:
+        """
+        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`.
+        :param ensure_exists: See `ensure_exists`.
+
+        """
+        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 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.
+        self.ensure_exists = ensure_exists
+        """
+        Optionally create the directory (and any missing parents) upon access if it does not exist.
+
+        By default, no directories are created.
+
+        """
+
+    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)
+        path = os.path.join(base[0], *params)  # noqa: PTH118
+        self._optionally_create_directory(path)
+        return path
+
+    def _optionally_create_directory(self, path: str) -> None:
+        if self.ensure_exists:
+            Path(path).mkdir(parents=True, exist_ok=True)
+
+    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.partition(os.pathsep)[0]
+        return Path(directory)
+
+    @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 site_cache_dir(self) -> str:
+        """:return: cache directory shared by users"""
+
+    @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_downloads_dir(self) -> str:
+        """:return: downloads directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_pictures_dir(self) -> str:
+        """:return: pictures directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_videos_dir(self) -> str:
+        """:return: videos directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_music_dir(self) -> str:
+        """:return: music directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_desktop_dir(self) -> str:
+        """:return: desktop directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_runtime_dir(self) -> str:
+        """:return: runtime directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_runtime_dir(self) -> str:
+        """:return: runtime directory shared by users"""
+
+    @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 site_cache_path(self) -> Path:
+        """:return: cache path shared by users"""
+        return Path(self.site_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 a path tied to the user"""
+        return Path(self.user_documents_dir)
+
+    @property
+    def user_downloads_path(self) -> Path:
+        """:return: downloads path tied to the user"""
+        return Path(self.user_downloads_dir)
+
+    @property
+    def user_pictures_path(self) -> Path:
+        """:return: pictures path tied to the user"""
+        return Path(self.user_pictures_dir)
+
+    @property
+    def user_videos_path(self) -> Path:
+        """:return: videos path tied to the user"""
+        return Path(self.user_videos_dir)
+
+    @property
+    def user_music_path(self) -> Path:
+        """:return: music path tied to the user"""
+        return Path(self.user_music_dir)
+
+    @property
+    def user_desktop_path(self) -> Path:
+        """:return: desktop path tied to the user"""
+        return Path(self.user_desktop_dir)
+
+    @property
+    def user_runtime_path(self) -> Path:
+        """:return: runtime path tied to the user"""
+        return Path(self.user_runtime_dir)
+
+    @property
+    def site_runtime_path(self) -> Path:
+        """:return: runtime path shared by users"""
+        return Path(self.site_runtime_dir)
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        yield self.user_config_dir
+        yield self.site_config_dir
+
+    def iter_data_dirs(self) -> Iterator[str]:
+        """:yield: all user and site data directories."""
+        yield self.user_data_dir
+        yield self.site_data_dir
+
+    def iter_cache_dirs(self) -> Iterator[str]:
+        """:yield: all user and site cache directories."""
+        yield self.user_cache_dir
+        yield self.site_cache_dir
+
+    def iter_runtime_dirs(self) -> Iterator[str]:
+        """:yield: all user and site runtime directories."""
+        yield self.user_runtime_dir
+        yield self.site_runtime_dir
+
+    def iter_config_paths(self) -> Iterator[Path]:
+        """:yield: all user and site configuration paths."""
+        for path in self.iter_config_dirs():
+            yield Path(path)
+
+    def iter_data_paths(self) -> Iterator[Path]:
+        """:yield: all user and site data paths."""
+        for path in self.iter_data_dirs():
+            yield Path(path)
+
+    def iter_cache_paths(self) -> Iterator[Path]:
+        """:yield: all user and site cache paths."""
+        for path in self.iter_cache_dirs():
+            yield Path(path)
+
+    def iter_runtime_paths(self) -> Iterator[Path]:
+        """:yield: all user and site runtime paths."""
+        for path in self.iter_runtime_dirs():
+            yield Path(path)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/macos.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/macos.py
new file mode 100644
index 0000000..30ab368
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/macos.py
@@ -0,0 +1,146 @@
+"""macOS."""
+
+from __future__ import annotations
+
+import os.path
+import sys
+from typing import TYPE_CHECKING
+
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from pathlib import Path
+
+
+class MacOS(PlatformDirsABC):
+    """
+    Platform directories for the macOS operating system.
+
+    Follows the guidance from
+    `Apple documentation `_.
+    Makes use of the `appname `,
+    `version `,
+    `ensure_exists `.
+
+    """
+
+    @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"))  # noqa: PTH111
+
+    @property
+    def site_data_dir(self) -> str:
+        """
+        :return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``.
+          If we're using a Python binary managed by `Homebrew `_, the directory
+          will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``.
+          If `multipath ` is enabled, and we're in Homebrew,
+          the response is a multi-path string separated by ":", e.g.
+          ``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version``
+        """
+        is_homebrew = "/opt/python" in sys.prefix
+        homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
+        path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else []
+        path_list.append(self._append_app_name_and_version("/Library/Application Support"))
+        if self.multipath:
+            return os.pathsep.join(path_list)
+        return path_list[0]
+
+    @property
+    def site_data_path(self) -> Path:
+        """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_data_dir)
+
+    @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, e.g. ``~/Library/Caches/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))  # noqa: PTH111
+
+    @property
+    def site_cache_dir(self) -> str:
+        """
+        :return: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``.
+          If we're using a Python binary managed by `Homebrew `_, the directory
+          will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``.
+          If `multipath ` is enabled, and we're in Homebrew,
+          the response is a multi-path string separated by ":", e.g.
+          ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version``
+        """
+        is_homebrew = "/opt/python" in sys.prefix
+        homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else ""
+        path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/var/cache")] if is_homebrew else []
+        path_list.append(self._append_app_name_and_version("/Library/Caches"))
+        if self.multipath:
+            return os.pathsep.join(path_list)
+        return path_list[0]
+
+    @property
+    def site_cache_path(self) -> Path:
+        """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_cache_dir)
+
+    @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"))  # noqa: PTH111
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:return: documents directory tied to the user, e.g. ``~/Documents``"""
+        return os.path.expanduser("~/Documents")  # noqa: PTH111
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:return: downloads directory tied to the user, e.g. ``~/Downloads``"""
+        return os.path.expanduser("~/Downloads")  # noqa: PTH111
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:return: pictures directory tied to the user, e.g. ``~/Pictures``"""
+        return os.path.expanduser("~/Pictures")  # noqa: PTH111
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:return: videos directory tied to the user, e.g. ``~/Movies``"""
+        return os.path.expanduser("~/Movies")  # noqa: PTH111
+
+    @property
+    def user_music_dir(self) -> str:
+        """:return: music directory tied to the user, e.g. ``~/Music``"""
+        return os.path.expanduser("~/Music")  # noqa: PTH111
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:return: desktop directory tied to the user, e.g. ``~/Desktop``"""
+        return os.path.expanduser("~/Desktop")  # noqa: PTH111
+
+    @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"))  # noqa: PTH111
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:return: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+
+__all__ = [
+    "MacOS",
+]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/unix.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/unix.py
new file mode 100644
index 0000000..fc75d8d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/unix.py
@@ -0,0 +1,272 @@
+"""Unix."""
+
+from __future__ import annotations
+
+import os
+import sys
+from configparser import ConfigParser
+from pathlib import Path
+from typing import TYPE_CHECKING, NoReturn
+
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from collections.abc import Iterator
+
+if sys.platform == "win32":
+
+    def getuid() -> NoReturn:
+        msg = "should only be used on Unix"
+        raise RuntimeError(msg)
+
+else:
+    from os import getuid
+
+
+class Unix(PlatformDirsABC):  # noqa: PLR0904
+    """
+    On Unix/Linux, we follow the `XDG Basedir Spec `_.
+
+    The spec allows overriding directories with environment variables. The examples shown are the default values,
+    alongside the name of the environment variable that overrides them. Makes use of the `appname
+    `, `version `, `multipath
+    `, `opinion `, `ensure_exists
+    `.
+
+    """
+
+    @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")  # noqa: PTH111
+        return self._append_app_name_and_version(path)
+
+    @property
+    def _site_data_dirs(self) -> list[str]:
+        path = os.environ.get("XDG_DATA_DIRS", "")
+        if not path.strip():
+            path = f"/usr/local/share{os.pathsep}/usr/share"
+        return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)]
+
+    @property
+    def site_data_dir(self) -> str:
+        """
+        :return: data directories shared by users (if `multipath ` is
+         enabled and ``XDG_DATA_DIRS`` 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
+        dirs = self._site_data_dirs
+        if not self.multipath:
+            return dirs[0]
+        return os.pathsep.join(dirs)
+
+    @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")  # noqa: PTH111
+        return self._append_app_name_and_version(path)
+
+    @property
+    def _site_config_dirs(self) -> list[str]:
+        path = os.environ.get("XDG_CONFIG_DIRS", "")
+        if not path.strip():
+            path = "/etc/xdg"
+        return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)]
+
+    @property
+    def site_config_dir(self) -> str:
+        """
+        :return: config directories shared by users (if `multipath `
+         is enabled and ``XDG_CONFIG_DIRS`` 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
+        dirs = self._site_config_dirs
+        if not self.multipath:
+            return dirs[0]
+        return os.pathsep.join(dirs)
+
+    @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")  # noqa: PTH111
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_cache_dir(self) -> str:
+        """:return: cache directory shared by users, e.g. ``/var/cache/$appname/$version``"""
+        return self._append_app_name_and_version("/var/cache")
+
+    @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")  # noqa: PTH111
+        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_state_dir` if not opinionated else ``log`` in it"""
+        path = self.user_state_dir
+        if self.opinion:
+            path = os.path.join(path, "log")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        return path
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:return: documents directory tied to the user, e.g. ``~/Documents``"""
+        return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
+
+    @property
+    def user_downloads_dir(self) -> str:
+        """:return: downloads directory tied to the user, e.g. ``~/Downloads``"""
+        return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads")
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:return: pictures directory tied to the user, e.g. ``~/Pictures``"""
+        return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures")
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:return: videos directory tied to the user, e.g. ``~/Videos``"""
+        return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos")
+
+    @property
+    def user_music_dir(self) -> str:
+        """:return: music directory tied to the user, e.g. ``~/Music``"""
+        return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music")
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:return: desktop directory tied to the user, e.g. ``~/Desktop``"""
+        return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop")
+
+    @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``.
+
+         For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if
+         exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR``
+         is not set.
+        """
+        path = os.environ.get("XDG_RUNTIME_DIR", "")
+        if not path.strip():
+            if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
+                path = f"/var/run/user/{getuid()}"
+                if not Path(path).exists():
+                    path = f"/tmp/runtime-{getuid()}"  # noqa: S108
+            else:
+                path = f"/run/user/{getuid()}"
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """
+        :return: runtime directory shared by users, e.g. ``/run/$appname/$version`` or \
+        ``$XDG_RUNTIME_DIR/$appname/$version``.
+
+        Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will
+        fall back to paths associated to the root user instead of a regular logged-in user if it's not set.
+
+        If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir`
+        instead.
+
+        For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set.
+        """
+        path = os.environ.get("XDG_RUNTIME_DIR", "")
+        if not path.strip():
+            if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
+                path = "/var/run"
+            else:
+                path = "/run"
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_data_path(self) -> Path:
+        """:return: data path shared by users. Only return the 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, returns the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_config_dir)
+
+    @property
+    def site_cache_path(self) -> Path:
+        """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_cache_dir)
+
+    def iter_config_dirs(self) -> Iterator[str]:
+        """:yield: all user and site configuration directories."""
+        yield self.user_config_dir
+        yield from self._site_config_dirs
+
+    def iter_data_dirs(self) -> Iterator[str]:
+        """:yield: all user and site data directories."""
+        yield self.user_data_dir
+        yield from self._site_data_dirs
+
+
+def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
+    media_dir = _get_user_dirs_folder(env_var)
+    if media_dir is None:
+        media_dir = os.environ.get(env_var, "").strip()
+        if not media_dir:
+            media_dir = os.path.expanduser(fallback_tilde_path)  # noqa: PTH111
+
+    return media_dir
+
+
+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 = Path(Unix().user_config_dir) / "user-dirs.dirs"
+    if user_dirs_config_path.exists():
+        parser = ConfigParser()
+
+        with user_dirs_config_path.open() 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
+        return path.replace("$HOME", os.path.expanduser("~"))  # noqa: PTH111
+
+    return None
+
+
+__all__ = [
+    "Unix",
+]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/version.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/version.py
new file mode 100644
index 0000000..b945147
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/version.py
@@ -0,0 +1,34 @@
+# file generated by setuptools-scm
+# don't change, don't track in version control
+
+__all__ = [
+    "__version__",
+    "__version_tuple__",
+    "version",
+    "version_tuple",
+    "__commit_id__",
+    "commit_id",
+]
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+    from typing import Tuple
+    from typing import Union
+
+    VERSION_TUPLE = Tuple[Union[int, str], ...]
+    COMMIT_ID = Union[str, None]
+else:
+    VERSION_TUPLE = object
+    COMMIT_ID = object
+
+version: str
+__version__: str
+__version_tuple__: VERSION_TUPLE
+version_tuple: VERSION_TUPLE
+commit_id: COMMIT_ID
+__commit_id__: COMMIT_ID
+
+__version__ = version = '4.4.0'
+__version_tuple__ = version_tuple = (4, 4, 0)
+
+__commit_id__ = commit_id = None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/windows.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/windows.py
new file mode 100644
index 0000000..d7bc960
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/platformdirs/windows.py
@@ -0,0 +1,272 @@
+"""Windows."""
+
+from __future__ import annotations
+
+import os
+import sys
+from functools import lru_cache
+from typing import TYPE_CHECKING
+
+from .api import PlatformDirsABC
+
+if TYPE_CHECKING:
+    from collections.abc import Callable
+
+
+class Windows(PlatformDirsABC):
+    """
+    `MSDN on where to store app data files `_.
+
+    Makes use of the `appname `, `appauthor
+    `, `version `, `roaming
+    `, `opinion `, `ensure_exists
+    `.
+
+    """
+
+    @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)
+        path = os.path.join(path, *params)  # noqa: PTH118
+        self._optionally_create_directory(path)
+        return path
+
+    @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 site_cache_dir(self) -> str:
+        """:return: cache directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname\\Cache\\$version``"""
+        path = os.path.normpath(get_win_folder("CSIDL_COMMON_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")  # noqa: PTH118
+            self._optionally_create_directory(path)
+        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_downloads_dir(self) -> str:
+        """:return: downloads directory tied to the user e.g. ``%USERPROFILE%\\Downloads``"""
+        return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS"))
+
+    @property
+    def user_pictures_dir(self) -> str:
+        """:return: pictures directory tied to the user e.g. ``%USERPROFILE%\\Pictures``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYPICTURES"))
+
+    @property
+    def user_videos_dir(self) -> str:
+        """:return: videos directory tied to the user e.g. ``%USERPROFILE%\\Videos``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYVIDEO"))
+
+    @property
+    def user_music_dir(self) -> str:
+        """:return: music directory tied to the user e.g. ``%USERPROFILE%\\Music``"""
+        return os.path.normpath(get_win_folder("CSIDL_MYMUSIC"))
+
+    @property
+    def user_desktop_dir(self) -> str:
+        """:return: desktop directory tied to the user, e.g. ``%USERPROFILE%\\Desktop``"""
+        return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY"))
+
+    @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"))  # noqa: PTH118
+        return self._append_parts(path)
+
+    @property
+    def site_runtime_dir(self) -> str:
+        """:return: runtime directory shared by users, same as `user_runtime_dir`"""
+        return self.user_runtime_dir
+
+
+def get_win_folder_from_env_vars(csidl_name: str) -> str:
+    """Get folder from environment variables."""
+    result = get_win_folder_if_csidl_name_not_env_var(csidl_name)
+    if result is not None:
+        return result
+
+    env_var_name = {
+        "CSIDL_APPDATA": "APPDATA",
+        "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
+        "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
+    }.get(csidl_name)
+    if env_var_name is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+    result = os.environ.get(env_var_name)
+    if result is None:
+        msg = f"Unset environment variable: {env_var_name}"
+        raise ValueError(msg)
+    return result
+
+
+def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None:
+    """Get a folder for a CSIDL name that does not exist as an environment variable."""
+    if csidl_name == "CSIDL_PERSONAL":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_DOWNLOADS":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYPICTURES":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYVIDEO":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos")  # noqa: PTH118
+
+    if csidl_name == "CSIDL_MYMUSIC":
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music")  # noqa: PTH118
+    return None
+
+
+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 these 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",
+        "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}",
+        "CSIDL_MYPICTURES": "My Pictures",
+        "CSIDL_MYVIDEO": "My Video",
+        "CSIDL_MYMUSIC": "My Music",
+    }.get(csidl_name)
+    if shell_folder_name is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+    if sys.platform != "win32":  # only needed for mypy type checker to know that this code runs only on Windows
+        raise NotImplementedError
+    import winreg  # noqa: PLC0415
+
+    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."""
+    # There is no 'CSIDL_DOWNLOADS'.
+    # Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead.
+    # https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
+
+    import ctypes  # noqa: PLC0415
+
+    csidl_const = {
+        "CSIDL_APPDATA": 26,
+        "CSIDL_COMMON_APPDATA": 35,
+        "CSIDL_LOCAL_APPDATA": 28,
+        "CSIDL_PERSONAL": 5,
+        "CSIDL_MYPICTURES": 39,
+        "CSIDL_MYVIDEO": 14,
+        "CSIDL_MYMUSIC": 13,
+        "CSIDL_DOWNLOADS": 40,
+        "CSIDL_DESKTOPDIRECTORY": 16,
+    }.get(csidl_name)
+    if csidl_const is None:
+        msg = f"Unknown CSIDL name: {csidl_name}"
+        raise ValueError(msg)
+
+    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 high-bit chars.
+    if any(ord(c) > 255 for c in buf):  # noqa: PLR2004
+        buf2 = ctypes.create_unicode_buffer(1024)
+        if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
+            buf = buf2
+
+    if csidl_name == "CSIDL_DOWNLOADS":
+        return os.path.join(buf.value, "Downloads")  # noqa: PTH118
+
+    return buf.value
+
+
+def _pick_get_win_folder() -> Callable[[str], str]:
+    try:
+        import ctypes  # noqa: PLC0415
+    except ImportError:
+        pass
+    else:
+        if hasattr(ctypes, "windll"):
+            return get_win_folder_via_ctypes
+    try:
+        import winreg  # noqa: PLC0415, 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/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/METADATA
new file mode 100644
index 0000000..ffe8e40
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/METADATA
@@ -0,0 +1,270 @@
+Metadata-Version: 2.4
+Name: tomli
+Version: 2.4.0
+Summary: A lil' TOML parser
+Keywords: toml
+Author-email: Taneli Hukkinen 
+Requires-Python: >=3.8
+Description-Content-Type: text/markdown
+License-Expression: MIT
+Classifier: Operating System :: MacOS
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Typing :: Typed
+License-File: LICENSE
+Project-URL: Changelog, https://github.com/hukkin/tomli/blob/master/CHANGELOG.md
+Project-URL: Homepage, https://github.com/hukkin/tomli
+
+[![Build Status](https://github.com/hukkin/tomli/actions/workflows/tests.yaml/badge.svg?branch=master)](https://github.com/hukkin/tomli/actions?query=workflow%3ATests+branch%3Amaster+event%3Apush)
+[![codecov.io](https://codecov.io/gh/hukkin/tomli/branch/master/graph/badge.svg)](https://codecov.io/gh/hukkin/tomli)
+[![PyPI version](https://img.shields.io/pypi/v/tomli)](https://pypi.org/project/tomli)
+
+# Tomli
+
+> A lil' TOML parser
+
+**Table of Contents** *generated with [mdformat-toc](https://github.com/hukkin/mdformat-toc)*
+
+
+
+- [Intro](#intro)
+- [Installation](#installation)
+- [Usage](#usage)
+  - [Parse a TOML string](#parse-a-toml-string)
+  - [Parse a TOML file](#parse-a-toml-file)
+  - [Handle invalid TOML](#handle-invalid-toml)
+  - [Construct `decimal.Decimal`s from TOML floats](#construct-decimaldecimals-from-toml-floats)
+  - [Building a `tomli`/`tomllib` compatibility layer](#building-a-tomlitomllib-compatibility-layer)
+- [FAQ](#faq)
+  - [Why this parser?](#why-this-parser)
+  - [Is comment preserving round-trip parsing supported?](#is-comment-preserving-round-trip-parsing-supported)
+  - [Is there a `dumps`, `write` or `encode` function?](#is-there-a-dumps-write-or-encode-function)
+  - [How do TOML types map into Python types?](#how-do-toml-types-map-into-python-types)
+- [Performance](#performance)
+  - [Pure Python](#pure-python)
+  - [Mypyc generated wheel](#mypyc-generated-wheel)
+
+
+
+## Intro
+
+Tomli is a Python library for parsing [TOML](https://toml.io).
+Version 2.4.0 and later are compatible with [TOML v1.1.0](https://toml.io/en/v1.1.0).
+Older versions are [TOML v1.0.0](https://toml.io/en/v1.0.0) compatible.
+
+A version of Tomli, the `tomllib` module,
+was added to the standard library in Python 3.11
+via [PEP 680](https://www.python.org/dev/peps/pep-0680/).
+Tomli continues to provide a backport on PyPI for Python versions
+where the standard library module is not available
+and that have not yet reached their end-of-life.
+
+Tomli uses [mypyc](https://github.com/mypyc/mypyc)
+to generate binary wheels for most of the widely used platforms,
+so Python 3.11+ users may prefer it over `tomllib` for improved performance.
+Pure Python wheels are available on any platform and should perform the same as `tomllib`.
+
+## Installation
+
+```bash
+pip install tomli
+```
+
+## Usage
+
+### Parse a TOML string
+
+```python
+import tomli
+
+toml_str = """
+[[players]]
+name = "Lehtinen"
+number = 26
+
+[[players]]
+name = "Numminen"
+number = 27
+"""
+
+toml_dict = tomli.loads(toml_str)
+assert toml_dict == {
+    "players": [{"name": "Lehtinen", "number": 26}, {"name": "Numminen", "number": 27}]
+}
+```
+
+### Parse a TOML file
+
+```python
+import tomli
+
+with open("path_to_file/conf.toml", "rb") as f:
+    toml_dict = tomli.load(f)
+```
+
+The file must be opened in binary mode (with the `"rb"` flag).
+Binary mode will enforce decoding the file as UTF-8 with universal newlines disabled,
+both of which are required to correctly parse TOML.
+
+### Handle invalid TOML
+
+```python
+import tomli
+
+try:
+    toml_dict = tomli.loads("]] this is invalid TOML [[")
+except tomli.TOMLDecodeError:
+    print("Yep, definitely not valid.")
+```
+
+Note that error messages are considered informational only.
+They should not be assumed to stay constant across Tomli versions.
+
+### Construct `decimal.Decimal`s from TOML floats
+
+```python
+from decimal import Decimal
+import tomli
+
+toml_dict = tomli.loads("precision-matters = 0.982492", parse_float=Decimal)
+assert isinstance(toml_dict["precision-matters"], Decimal)
+assert toml_dict["precision-matters"] == Decimal("0.982492")
+```
+
+Note that `decimal.Decimal` can be replaced with another callable that converts a TOML float from string to a Python type.
+The `decimal.Decimal` is, however, a practical choice for use cases where float inaccuracies can not be tolerated.
+
+Illegal types are `dict` and `list`, and their subtypes.
+A `ValueError` will be raised if `parse_float` produces illegal types.
+
+### Building a `tomli`/`tomllib` compatibility layer
+
+Python versions 3.11+ ship with a version of Tomli:
+the `tomllib` standard library module.
+To build code that uses the standard library if available,
+but still works seamlessly with Python 3.6+,
+do the following.
+
+Instead of a hard Tomli dependency, use the following
+[dependency specifier](https://packaging.python.org/en/latest/specifications/dependency-specifiers/)
+to only require Tomli when the standard library module is not available:
+
+```
+tomli >= 1.1.0 ; python_version < "3.11"
+```
+
+Then, in your code, import a TOML parser using the following fallback mechanism:
+
+```python
+import sys
+
+if sys.version_info >= (3, 11):
+    import tomllib
+else:
+    import tomli as tomllib
+
+tomllib.loads("['This parses fine with Python 3.6+']")
+```
+
+## FAQ
+
+### Why this parser?
+
+- it's lil'
+- pure Python with zero dependencies
+- the fastest pure Python parser [\*](#pure-python):
+  18x as fast as [tomlkit](https://pypi.org/project/tomlkit/),
+  2.1x as fast as [toml](https://pypi.org/project/toml/)
+- outputs [basic data types](#how-do-toml-types-map-into-python-types) only
+- 100% spec compliant: passes all tests in
+  [toml-lang/toml-test](https://github.com/toml-lang/toml-test)
+  test suite
+- thoroughly tested: 100% branch coverage
+
+### Is comment preserving round-trip parsing supported?
+
+No.
+
+The `tomli.loads` function returns a plain `dict` that is populated with builtin types and types from the standard library only.
+Preserving comments requires a custom type to be returned so will not be supported,
+at least not by the `tomli.loads` and `tomli.load` functions.
+
+Look into [TOML Kit](https://github.com/sdispater/tomlkit) if preservation of style is what you need.
+
+### Is there a `dumps`, `write` or `encode` function?
+
+[Tomli-W](https://github.com/hukkin/tomli-w) is the write-only counterpart of Tomli, providing `dump` and `dumps` functions.
+
+The core library does not include write capability, as most TOML use cases are read-only, and Tomli intends to be minimal.
+
+### How do TOML types map into Python types?
+
+| TOML type        | Python type         | Details                                                      |
+| ---------------- | ------------------- | ------------------------------------------------------------ |
+| Document Root    | `dict`              |                                                              |
+| Key              | `str`               |                                                              |
+| String           | `str`               |                                                              |
+| Integer          | `int`               |                                                              |
+| Float            | `float`             |                                                              |
+| Boolean          | `bool`              |                                                              |
+| Offset Date-Time | `datetime.datetime` | `tzinfo` attribute set to an instance of `datetime.timezone` |
+| Local Date-Time  | `datetime.datetime` | `tzinfo` attribute set to `None`                             |
+| Local Date       | `datetime.date`     |                                                              |
+| Local Time       | `datetime.time`     |                                                              |
+| Array            | `list`              |                                                              |
+| Table            | `dict`              |                                                              |
+| Inline Table     | `dict`              |                                                              |
+
+## Performance
+
+The `benchmark/` folder in this repository contains a performance benchmark for comparing the various Python TOML parsers.
+
+Below are the results for commit [0724e2a](https://github.com/hukkin/tomli/tree/0724e2ab1858da7f5e05a9bffdb24c33589d951c).
+
+### Pure Python
+
+```console
+foo@bar:~/dev/tomli$ python --version
+Python 3.12.7
+foo@bar:~/dev/tomli$ pip freeze
+attrs==21.4.0
+click==8.1.7
+pytomlpp==1.0.13
+qtoml==0.3.1
+rtoml==0.11.0
+toml==0.10.2
+tomli @ file:///home/foo/dev/tomli
+tomlkit==0.13.2
+foo@bar:~/dev/tomli$ python benchmark/run.py
+Parsing data.toml 5000 times:
+------------------------------------------------------
+    parser |  exec time | performance (more is better)
+-----------+------------+-----------------------------
+     rtoml |    0.647 s | baseline (100%)
+  pytomlpp |    0.891 s | 72.62%
+     tomli |     3.14 s | 20.56%
+      toml |     6.69 s | 9.67%
+     qtoml |     8.27 s | 7.82%
+   tomlkit |     56.1 s | 1.15%
+```
+
+### Mypyc generated wheel
+
+```console
+foo@bar:~/dev/tomli$ python benchmark/run.py
+Parsing data.toml 5000 times:
+------------------------------------------------------
+    parser |  exec time | performance (more is better)
+-----------+------------+-----------------------------
+     rtoml |    0.668 s | baseline (100%)
+  pytomlpp |    0.893 s | 74.81%
+     tomli |     1.96 s | 34.18%
+      toml |     6.64 s | 10.07%
+     qtoml |     8.26 s | 8.09%
+   tomlkit |     52.9 s | 1.26%
+```
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/RECORD
new file mode 100644
index 0000000..d2415a3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/RECORD
@@ -0,0 +1,11 @@
+tomli-2.4.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+tomli-2.4.0.dist-info/METADATA,sha256=9awKH4-6kItGRs1lUwnpGq2Wm2eHYWrFccpGKjgy_84,10567
+tomli-2.4.0.dist-info/RECORD,,
+tomli-2.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+tomli-2.4.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+tomli-2.4.0.dist-info/licenses/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+tomli/__init__.py,sha256=ahtDjGJA2M_wWVvGpzx4YJtWxrWBx6qE-GH5-UYoECA,314
+tomli/_parser.py,sha256=txeATLE3zHyZ-ushXtYfrZ3LoIs7JzQF2W2KL1gwJPg,25958
+tomli/_re.py,sha256=oSNZ_ilFI6chEuQ01YRSoUydBQr_okF_mSdHTkFmv90,3396
+tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
+tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/WHEEL
new file mode 100644
index 0000000..d8b9936
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: flit 3.12.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..e859590
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Taneli Hukkinen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/__init__.py
new file mode 100644
index 0000000..55699b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/__init__.py
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+__all__ = ("loads", "load", "TOMLDecodeError")
+__version__ = "2.4.0"  # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT
+
+from ._parser import TOMLDecodeError, load, loads
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_parser.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_parser.py
new file mode 100644
index 0000000..3038891
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_parser.py
@@ -0,0 +1,782 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from __future__ import annotations
+
+import sys
+from types import MappingProxyType
+
+from ._re import (
+    RE_DATETIME,
+    RE_LOCALTIME,
+    RE_NUMBER,
+    match_to_datetime,
+    match_to_localtime,
+    match_to_number,
+)
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+    from collections.abc import Iterable
+    from typing import IO, Any, Final
+
+    from ._types import Key, ParseFloat, Pos
+
+# Inline tables/arrays are implemented using recursion. Pathologically
+# nested documents cause pure Python to raise RecursionError (which is OK),
+# but mypyc binary wheels will crash unrecoverably (not OK). According to
+# mypyc docs this will be fixed in the future:
+# https://mypyc.readthedocs.io/en/latest/differences_from_python.html#stack-overflows
+# Before mypyc's fix is in, recursion needs to be limited by this library.
+# Choosing `sys.getrecursionlimit()` as maximum inline table/array nesting
+# level, as it allows more nesting than pure Python, but still seems a far
+# lower number than where mypyc binaries crash.
+MAX_INLINE_NESTING: Final = sys.getrecursionlimit()
+
+ASCII_CTRL: Final = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
+
+# Neither of these sets include quotation mark or backslash. They are
+# currently handled as separate cases in the parser functions.
+ILLEGAL_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t")
+ILLEGAL_MULTILINE_BASIC_STR_CHARS: Final = ASCII_CTRL - frozenset("\t\n")
+
+ILLEGAL_LITERAL_STR_CHARS: Final = ILLEGAL_BASIC_STR_CHARS
+ILLEGAL_MULTILINE_LITERAL_STR_CHARS: Final = ILLEGAL_MULTILINE_BASIC_STR_CHARS
+
+ILLEGAL_COMMENT_CHARS: Final = ILLEGAL_BASIC_STR_CHARS
+
+TOML_WS: Final = frozenset(" \t")
+TOML_WS_AND_NEWLINE: Final = TOML_WS | frozenset("\n")
+BARE_KEY_CHARS: Final = frozenset(
+    "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "-_"
+)
+KEY_INITIAL_CHARS: Final = BARE_KEY_CHARS | frozenset("\"'")
+HEXDIGIT_CHARS: Final = frozenset("abcdef" "ABCDEF" "0123456789")
+
+BASIC_STR_ESCAPE_REPLACEMENTS: Final = MappingProxyType(
+    {
+        "\\b": "\u0008",  # backspace
+        "\\t": "\u0009",  # tab
+        "\\n": "\u000a",  # linefeed
+        "\\f": "\u000c",  # form feed
+        "\\r": "\u000d",  # carriage return
+        "\\e": "\u001b",  # escape
+        '\\"': "\u0022",  # quote
+        "\\\\": "\u005c",  # backslash
+    }
+)
+
+
+class DEPRECATED_DEFAULT:
+    """Sentinel to be used as default arg during deprecation
+    period of TOMLDecodeError's free-form arguments."""
+
+
+class TOMLDecodeError(ValueError):
+    """An error raised if a document is not valid TOML.
+
+    Adds the following attributes to ValueError:
+    msg: The unformatted error message
+    doc: The TOML document being parsed
+    pos: The index of doc where parsing failed
+    lineno: The line corresponding to pos
+    colno: The column corresponding to pos
+    """
+
+    def __init__(
+        self,
+        msg: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT,
+        doc: str | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT,
+        pos: Pos | type[DEPRECATED_DEFAULT] = DEPRECATED_DEFAULT,
+        *args: Any,
+    ):
+        if (
+            args
+            or not isinstance(msg, str)
+            or not isinstance(doc, str)
+            or not isinstance(pos, int)
+        ):
+            import warnings
+
+            warnings.warn(
+                "Free-form arguments for TOMLDecodeError are deprecated. "
+                "Please set 'msg' (str), 'doc' (str) and 'pos' (int) arguments only.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            if pos is not DEPRECATED_DEFAULT:
+                args = pos, *args
+            if doc is not DEPRECATED_DEFAULT:
+                args = doc, *args
+            if msg is not DEPRECATED_DEFAULT:
+                args = msg, *args
+            ValueError.__init__(self, *args)
+            return
+
+        lineno = doc.count("\n", 0, pos) + 1
+        if lineno == 1:
+            colno = pos + 1
+        else:
+            colno = pos - doc.rindex("\n", 0, pos)
+
+        if pos >= len(doc):
+            coord_repr = "end of document"
+        else:
+            coord_repr = f"line {lineno}, column {colno}"
+        errmsg = f"{msg} (at {coord_repr})"
+        ValueError.__init__(self, errmsg)
+
+        self.msg = msg
+        self.doc = doc
+        self.pos = pos
+        self.lineno = lineno
+        self.colno = colno
+
+
+def load(__fp: IO[bytes], *, parse_float: ParseFloat = float) -> dict[str, Any]:
+    """Parse TOML from a binary file object."""
+    b = __fp.read()
+    try:
+        s = b.decode()
+    except AttributeError:
+        raise TypeError(
+            "File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`"
+        ) from None
+    return loads(s, parse_float=parse_float)
+
+
+def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]:
+    """Parse TOML from a string."""
+
+    # The spec allows converting "\r\n" to "\n", even in string
+    # literals. Let's do so to simplify parsing.
+    try:
+        src = __s.replace("\r\n", "\n")
+    except (AttributeError, TypeError):
+        raise TypeError(
+            f"Expected str object, not '{type(__s).__qualname__}'"
+        ) from None
+    pos = 0
+    out = Output()
+    header: Key = ()
+    parse_float = make_safe_parse_float(parse_float)
+
+    # Parse one statement at a time
+    # (typically means one line in TOML source)
+    while True:
+        # 1. Skip line leading whitespace
+        pos = skip_chars(src, pos, TOML_WS)
+
+        # 2. Parse rules. Expect one of the following:
+        #    - end of file
+        #    - end of line
+        #    - comment
+        #    - key/value pair
+        #    - append dict to list (and move to its namespace)
+        #    - create dict (and move to its namespace)
+        # Skip trailing whitespace when applicable.
+        try:
+            char = src[pos]
+        except IndexError:
+            break
+        if char == "\n":
+            pos += 1
+            continue
+        if char in KEY_INITIAL_CHARS:
+            pos = key_value_rule(src, pos, out, header, parse_float)
+            pos = skip_chars(src, pos, TOML_WS)
+        elif char == "[":
+            try:
+                second_char: str | None = src[pos + 1]
+            except IndexError:
+                second_char = None
+            out.flags.finalize_pending()
+            if second_char == "[":
+                pos, header = create_list_rule(src, pos, out)
+            else:
+                pos, header = create_dict_rule(src, pos, out)
+            pos = skip_chars(src, pos, TOML_WS)
+        elif char != "#":
+            raise TOMLDecodeError("Invalid statement", src, pos)
+
+        # 3. Skip comment
+        pos = skip_comment(src, pos)
+
+        # 4. Expect end of line or end of file
+        try:
+            char = src[pos]
+        except IndexError:
+            break
+        if char != "\n":
+            raise TOMLDecodeError(
+                "Expected newline or end of document after a statement", src, pos
+            )
+        pos += 1
+
+    return out.data.dict
+
+
+class Flags:
+    """Flags that map to parsed keys/namespaces."""
+
+    # Marks an immutable namespace (inline array or inline table).
+    FROZEN: Final = 0
+    # Marks a nest that has been explicitly created and can no longer
+    # be opened using the "[table]" syntax.
+    EXPLICIT_NEST: Final = 1
+
+    def __init__(self) -> None:
+        self._flags: dict[str, dict[Any, Any]] = {}
+        self._pending_flags: set[tuple[Key, int]] = set()
+
+    def add_pending(self, key: Key, flag: int) -> None:
+        self._pending_flags.add((key, flag))
+
+    def finalize_pending(self) -> None:
+        for key, flag in self._pending_flags:
+            self.set(key, flag, recursive=False)
+        self._pending_flags.clear()
+
+    def unset_all(self, key: Key) -> None:
+        cont = self._flags
+        for k in key[:-1]:
+            if k not in cont:
+                return
+            cont = cont[k]["nested"]
+        cont.pop(key[-1], None)
+
+    def set(self, key: Key, flag: int, *, recursive: bool) -> None:  # noqa: A003
+        cont = self._flags
+        key_parent, key_stem = key[:-1], key[-1]
+        for k in key_parent:
+            if k not in cont:
+                cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}}
+            cont = cont[k]["nested"]
+        if key_stem not in cont:
+            cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}}
+        cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag)
+
+    def is_(self, key: Key, flag: int) -> bool:
+        if not key:
+            return False  # document root has no flags
+        cont = self._flags
+        for k in key[:-1]:
+            if k not in cont:
+                return False
+            inner_cont = cont[k]
+            if flag in inner_cont["recursive_flags"]:
+                return True
+            cont = inner_cont["nested"]
+        key_stem = key[-1]
+        if key_stem in cont:
+            inner_cont = cont[key_stem]
+            return flag in inner_cont["flags"] or flag in inner_cont["recursive_flags"]
+        return False
+
+
+class NestedDict:
+    def __init__(self) -> None:
+        # The parsed content of the TOML document
+        self.dict: dict[str, Any] = {}
+
+    def get_or_create_nest(
+        self,
+        key: Key,
+        *,
+        access_lists: bool = True,
+    ) -> dict[str, Any]:
+        cont: Any = self.dict
+        for k in key:
+            if k not in cont:
+                cont[k] = {}
+            cont = cont[k]
+            if access_lists and isinstance(cont, list):
+                cont = cont[-1]
+            if not isinstance(cont, dict):
+                raise KeyError("There is no nest behind this key")
+        return cont  # type: ignore[no-any-return]
+
+    def append_nest_to_list(self, key: Key) -> None:
+        cont = self.get_or_create_nest(key[:-1])
+        last_key = key[-1]
+        if last_key in cont:
+            list_ = cont[last_key]
+            if not isinstance(list_, list):
+                raise KeyError("An object other than list found behind this key")
+            list_.append({})
+        else:
+            cont[last_key] = [{}]
+
+
+class Output:
+    def __init__(self) -> None:
+        self.data = NestedDict()
+        self.flags = Flags()
+
+
+def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:
+    try:
+        while src[pos] in chars:
+            pos += 1
+    except IndexError:
+        pass
+    return pos
+
+
+def skip_until(
+    src: str,
+    pos: Pos,
+    expect: str,
+    *,
+    error_on: frozenset[str],
+    error_on_eof: bool,
+) -> Pos:
+    try:
+        new_pos = src.index(expect, pos)
+    except ValueError:
+        new_pos = len(src)
+        if error_on_eof:
+            raise TOMLDecodeError(f"Expected {expect!r}", src, new_pos) from None
+
+    if not error_on.isdisjoint(src[pos:new_pos]):
+        while src[pos] not in error_on:
+            pos += 1
+        raise TOMLDecodeError(f"Found invalid character {src[pos]!r}", src, pos)
+    return new_pos
+
+
+def skip_comment(src: str, pos: Pos) -> Pos:
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char == "#":
+        return skip_until(
+            src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False
+        )
+    return pos
+
+
+def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:
+    while True:
+        pos_before_skip = pos
+        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
+        pos = skip_comment(src, pos)
+        if pos == pos_before_skip:
+            return pos
+
+
+def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:
+    pos += 1  # Skip "["
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, key = parse_key(src, pos)
+
+    if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):
+        raise TOMLDecodeError(f"Cannot declare {key} twice", src, pos)
+    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
+    try:
+        out.data.get_or_create_nest(key)
+    except KeyError:
+        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None
+
+    if not src.startswith("]", pos):
+        raise TOMLDecodeError(
+            "Expected ']' at the end of a table declaration", src, pos
+        )
+    return pos + 1, key
+
+
+def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:
+    pos += 2  # Skip "[["
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, key = parse_key(src, pos)
+
+    if out.flags.is_(key, Flags.FROZEN):
+        raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos)
+    # Free the namespace now that it points to another empty list item...
+    out.flags.unset_all(key)
+    # ...but this key precisely is still prohibited from table declaration
+    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
+    try:
+        out.data.append_nest_to_list(key)
+    except KeyError:
+        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None
+
+    if not src.startswith("]]", pos):
+        raise TOMLDecodeError(
+            "Expected ']]' at the end of an array declaration", src, pos
+        )
+    return pos + 2, key
+
+
+def key_value_rule(
+    src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat
+) -> Pos:
+    pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl=0)
+    key_parent, key_stem = key[:-1], key[-1]
+    abs_key_parent = header + key_parent
+
+    relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))
+    for cont_key in relative_path_cont_keys:
+        # Check that dotted key syntax does not redefine an existing table
+        if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):
+            raise TOMLDecodeError(f"Cannot redefine namespace {cont_key}", src, pos)
+        # Containers in the relative path can't be opened with the table syntax or
+        # dotted key/value syntax in following table sections.
+        out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)
+
+    if out.flags.is_(abs_key_parent, Flags.FROZEN):
+        raise TOMLDecodeError(
+            f"Cannot mutate immutable namespace {abs_key_parent}", src, pos
+        )
+
+    try:
+        nest = out.data.get_or_create_nest(abs_key_parent)
+    except KeyError:
+        raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None
+    if key_stem in nest:
+        raise TOMLDecodeError("Cannot overwrite a value", src, pos)
+    # Mark inline table and array namespaces recursively immutable
+    if isinstance(value, (dict, list)):
+        out.flags.set(header + key, Flags.FROZEN, recursive=True)
+    nest[key_stem] = value
+    return pos
+
+
+def parse_key_value_pair(
+    src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int
+) -> tuple[Pos, Key, Any]:
+    pos, key = parse_key(src, pos)
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char != "=":
+        raise TOMLDecodeError("Expected '=' after a key in a key/value pair", src, pos)
+    pos += 1
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, value = parse_value(src, pos, parse_float, nest_lvl)
+    return pos, key, value
+
+
+def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:
+    pos, key_part = parse_key_part(src, pos)
+    key: Key = (key_part,)
+    pos = skip_chars(src, pos, TOML_WS)
+    while True:
+        try:
+            char: str | None = src[pos]
+        except IndexError:
+            char = None
+        if char != ".":
+            return pos, key
+        pos += 1
+        pos = skip_chars(src, pos, TOML_WS)
+        pos, key_part = parse_key_part(src, pos)
+        key += (key_part,)
+        pos = skip_chars(src, pos, TOML_WS)
+
+
+def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char in BARE_KEY_CHARS:
+        start_pos = pos
+        pos = skip_chars(src, pos, BARE_KEY_CHARS)
+        return pos, src[start_pos:pos]
+    if char == "'":
+        return parse_literal_str(src, pos)
+    if char == '"':
+        return parse_one_line_basic_str(src, pos)
+    raise TOMLDecodeError("Invalid initial character for a key part", src, pos)
+
+
+def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:
+    pos += 1
+    return parse_basic_str(src, pos, multiline=False)
+
+
+def parse_array(
+    src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int
+) -> tuple[Pos, list[Any]]:
+    pos += 1
+    array: list[Any] = []
+
+    pos = skip_comments_and_array_ws(src, pos)
+    if src.startswith("]", pos):
+        return pos + 1, array
+    while True:
+        pos, val = parse_value(src, pos, parse_float, nest_lvl)
+        array.append(val)
+        pos = skip_comments_and_array_ws(src, pos)
+
+        c = src[pos : pos + 1]
+        if c == "]":
+            return pos + 1, array
+        if c != ",":
+            raise TOMLDecodeError("Unclosed array", src, pos)
+        pos += 1
+
+        pos = skip_comments_and_array_ws(src, pos)
+        if src.startswith("]", pos):
+            return pos + 1, array
+
+
+def parse_inline_table(
+    src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int
+) -> tuple[Pos, dict[str, Any]]:
+    pos += 1
+    nested_dict = NestedDict()
+    flags = Flags()
+
+    pos = skip_comments_and_array_ws(src, pos)
+    if src.startswith("}", pos):
+        return pos + 1, nested_dict.dict
+    while True:
+        pos, key, value = parse_key_value_pair(src, pos, parse_float, nest_lvl)
+        key_parent, key_stem = key[:-1], key[-1]
+        if flags.is_(key, Flags.FROZEN):
+            raise TOMLDecodeError(f"Cannot mutate immutable namespace {key}", src, pos)
+        try:
+            nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)
+        except KeyError:
+            raise TOMLDecodeError("Cannot overwrite a value", src, pos) from None
+        if key_stem in nest:
+            raise TOMLDecodeError(f"Duplicate inline table key {key_stem!r}", src, pos)
+        nest[key_stem] = value
+        pos = skip_comments_and_array_ws(src, pos)
+        c = src[pos : pos + 1]
+        if c == "}":
+            return pos + 1, nested_dict.dict
+        if c != ",":
+            raise TOMLDecodeError("Unclosed inline table", src, pos)
+        pos += 1
+        pos = skip_comments_and_array_ws(src, pos)
+        if src.startswith("}", pos):
+            return pos + 1, nested_dict.dict
+        if isinstance(value, (dict, list)):
+            flags.set(key, Flags.FROZEN, recursive=True)
+
+
+def parse_basic_str_escape(
+    src: str, pos: Pos, *, multiline: bool = False
+) -> tuple[Pos, str]:
+    escape_id = src[pos : pos + 2]
+    pos += 2
+    if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}:
+        # Skip whitespace until next non-whitespace character or end of
+        # the doc. Error if non-whitespace is found before newline.
+        if escape_id != "\\\n":
+            pos = skip_chars(src, pos, TOML_WS)
+            try:
+                char = src[pos]
+            except IndexError:
+                return pos, ""
+            if char != "\n":
+                raise TOMLDecodeError("Unescaped '\\' in a string", src, pos)
+            pos += 1
+        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
+        return pos, ""
+    if escape_id == "\\x":
+        return parse_hex_char(src, pos, 2)
+    if escape_id == "\\u":
+        return parse_hex_char(src, pos, 4)
+    if escape_id == "\\U":
+        return parse_hex_char(src, pos, 8)
+    try:
+        return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]
+    except KeyError:
+        raise TOMLDecodeError("Unescaped '\\' in a string", src, pos) from None
+
+
+def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:
+    return parse_basic_str_escape(src, pos, multiline=True)
+
+
+def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:
+    hex_str = src[pos : pos + hex_len]
+    if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):
+        raise TOMLDecodeError("Invalid hex value", src, pos)
+    pos += hex_len
+    hex_int = int(hex_str, 16)
+    if not is_unicode_scalar_value(hex_int):
+        raise TOMLDecodeError(
+            "Escaped character is not a Unicode scalar value", src, pos
+        )
+    return pos, chr(hex_int)
+
+
+def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:
+    pos += 1  # Skip starting apostrophe
+    start_pos = pos
+    pos = skip_until(
+        src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True
+    )
+    return pos + 1, src[start_pos:pos]  # Skip ending apostrophe
+
+
+def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:
+    pos += 3
+    if src.startswith("\n", pos):
+        pos += 1
+
+    if literal:
+        delim = "'"
+        end_pos = skip_until(
+            src,
+            pos,
+            "'''",
+            error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,
+            error_on_eof=True,
+        )
+        result = src[pos:end_pos]
+        pos = end_pos + 3
+    else:
+        delim = '"'
+        pos, result = parse_basic_str(src, pos, multiline=True)
+
+    # Add at maximum two extra apostrophes/quotes if the end sequence
+    # is 4 or 5 chars long instead of just 3.
+    if not src.startswith(delim, pos):
+        return pos, result
+    pos += 1
+    if not src.startswith(delim, pos):
+        return pos, result + delim
+    pos += 1
+    return pos, result + (delim * 2)
+
+
+def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:
+    if multiline:
+        error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS
+        parse_escapes = parse_basic_str_escape_multiline
+    else:
+        error_on = ILLEGAL_BASIC_STR_CHARS
+        parse_escapes = parse_basic_str_escape
+    result = ""
+    start_pos = pos
+    while True:
+        try:
+            char = src[pos]
+        except IndexError:
+            raise TOMLDecodeError("Unterminated string", src, pos) from None
+        if char == '"':
+            if not multiline:
+                return pos + 1, result + src[start_pos:pos]
+            if src.startswith('"""', pos):
+                return pos + 3, result + src[start_pos:pos]
+            pos += 1
+            continue
+        if char == "\\":
+            result += src[start_pos:pos]
+            pos, parsed_escape = parse_escapes(src, pos)
+            result += parsed_escape
+            start_pos = pos
+            continue
+        if char in error_on:
+            raise TOMLDecodeError(f"Illegal character {char!r}", src, pos)
+        pos += 1
+
+
+def parse_value(
+    src: str, pos: Pos, parse_float: ParseFloat, nest_lvl: int
+) -> tuple[Pos, Any]:
+    if nest_lvl > MAX_INLINE_NESTING:
+        # Pure Python should have raised RecursionError already.
+        # This ensures mypyc binaries eventually do the same.
+        raise RecursionError(  # pragma: no cover
+            "TOML inline arrays/tables are nested more than the allowed"
+            f" {MAX_INLINE_NESTING} levels"
+        )
+
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+
+    # IMPORTANT: order conditions based on speed of checking and likelihood
+
+    # Basic strings
+    if char == '"':
+        if src.startswith('"""', pos):
+            return parse_multiline_str(src, pos, literal=False)
+        return parse_one_line_basic_str(src, pos)
+
+    # Literal strings
+    if char == "'":
+        if src.startswith("'''", pos):
+            return parse_multiline_str(src, pos, literal=True)
+        return parse_literal_str(src, pos)
+
+    # Booleans
+    if char == "t":
+        if src.startswith("true", pos):
+            return pos + 4, True
+    if char == "f":
+        if src.startswith("false", pos):
+            return pos + 5, False
+
+    # Arrays
+    if char == "[":
+        return parse_array(src, pos, parse_float, nest_lvl + 1)
+
+    # Inline tables
+    if char == "{":
+        return parse_inline_table(src, pos, parse_float, nest_lvl + 1)
+
+    # Dates and times
+    datetime_match = RE_DATETIME.match(src, pos)
+    if datetime_match:
+        try:
+            datetime_obj = match_to_datetime(datetime_match)
+        except ValueError as e:
+            raise TOMLDecodeError("Invalid date or datetime", src, pos) from e
+        return datetime_match.end(), datetime_obj
+    localtime_match = RE_LOCALTIME.match(src, pos)
+    if localtime_match:
+        return localtime_match.end(), match_to_localtime(localtime_match)
+
+    # Integers and "normal" floats.
+    # The regex will greedily match any type starting with a decimal
+    # char, so needs to be located after handling of dates and times.
+    number_match = RE_NUMBER.match(src, pos)
+    if number_match:
+        return number_match.end(), match_to_number(number_match, parse_float)
+
+    # Special floats
+    first_three = src[pos : pos + 3]
+    if first_three in {"inf", "nan"}:
+        return pos + 3, parse_float(first_three)
+    first_four = src[pos : pos + 4]
+    if first_four in {"-inf", "+inf", "-nan", "+nan"}:
+        return pos + 4, parse_float(first_four)
+
+    raise TOMLDecodeError("Invalid value", src, pos)
+
+
+def is_unicode_scalar_value(codepoint: int) -> bool:
+    return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)
+
+
+def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:
+    """A decorator to make `parse_float` safe.
+
+    `parse_float` must not return dicts or lists, because these types
+    would be mixed with parsed TOML tables and arrays, thus confusing
+    the parser. The returned decorated callable raises `ValueError`
+    instead of returning illegal types.
+    """
+    # The default `float` callable never returns illegal types. Optimize it.
+    if parse_float is float:
+        return float
+
+    def safe_parse_float(float_str: str) -> Any:
+        float_value = parse_float(float_str)
+        if isinstance(float_value, (dict, list)):
+            raise ValueError("parse_float must not return dicts or lists")
+        return float_value
+
+    return safe_parse_float
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_re.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_re.py
new file mode 100644
index 0000000..fc374ed
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_re.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from __future__ import annotations
+
+from datetime import date, datetime, time, timedelta, timezone, tzinfo
+from functools import lru_cache
+import re
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+    from typing import Any, Final
+
+    from ._types import ParseFloat
+
+_TIME_RE_STR: Final = r"""
+([01][0-9]|2[0-3])             # hours
+:([0-5][0-9])                  # minutes
+(?:
+    :([0-5][0-9])              # optional seconds
+    (?:\.([0-9]{1,6})[0-9]*)?  # optional fractions of a second
+)?
+"""
+
+RE_NUMBER: Final = re.compile(
+    r"""
+0
+(?:
+    x[0-9A-Fa-f](?:_?[0-9A-Fa-f])*   # hex
+    |
+    b[01](?:_?[01])*                 # bin
+    |
+    o[0-7](?:_?[0-7])*               # oct
+)
+|
+[+-]?(?:0|[1-9](?:_?[0-9])*)         # dec, integer part
+(?P
+    (?:\.[0-9](?:_?[0-9])*)?         # optional fractional part
+    (?:[eE][+-]?[0-9](?:_?[0-9])*)?  # optional exponent part
+)
+""",
+    flags=re.VERBOSE,
+)
+RE_LOCALTIME: Final = re.compile(_TIME_RE_STR, flags=re.VERBOSE)
+RE_DATETIME: Final = re.compile(
+    rf"""
+([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])  # date, e.g. 1988-10-27
+(?:
+    [Tt ]
+    {_TIME_RE_STR}
+    (?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))?  # optional time offset
+)?
+""",
+    flags=re.VERBOSE,
+)
+
+
+def match_to_datetime(match: re.Match[str]) -> datetime | date:
+    """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`.
+
+    Raises ValueError if the match does not correspond to a valid date
+    or datetime.
+    """
+    (
+        year_str,
+        month_str,
+        day_str,
+        hour_str,
+        minute_str,
+        sec_str,
+        micros_str,
+        zulu_time,
+        offset_sign_str,
+        offset_hour_str,
+        offset_minute_str,
+    ) = match.groups()
+    year, month, day = int(year_str), int(month_str), int(day_str)
+    if hour_str is None:
+        return date(year, month, day)
+    hour, minute = int(hour_str), int(minute_str)
+    sec = int(sec_str) if sec_str else 0
+    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
+    if offset_sign_str:
+        tz: tzinfo | None = cached_tz(
+            offset_hour_str, offset_minute_str, offset_sign_str
+        )
+    elif zulu_time:
+        tz = timezone.utc
+    else:  # local date-time
+        tz = None
+    return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz)
+
+
+# No need to limit cache size. This is only ever called on input
+# that matched RE_DATETIME, so there is an implicit bound of
+# 24 (hours) * 60 (minutes) * 2 (offset direction) = 2880.
+@lru_cache(maxsize=None)
+def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone:
+    sign = 1 if sign_str == "+" else -1
+    return timezone(
+        timedelta(
+            hours=sign * int(hour_str),
+            minutes=sign * int(minute_str),
+        )
+    )
+
+
+def match_to_localtime(match: re.Match[str]) -> time:
+    hour_str, minute_str, sec_str, micros_str = match.groups()
+    sec = int(sec_str) if sec_str else 0
+    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
+    return time(int(hour_str), int(minute_str), sec, micros)
+
+
+def match_to_number(match: re.Match[str], parse_float: ParseFloat) -> Any:
+    if match.group("floatpart"):
+        return parse_float(match.group())
+    return int(match.group(), 0)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_types.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_types.py
new file mode 100644
index 0000000..d949412
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/_types.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from typing import Any, Callable, Tuple
+
+# Type annotations
+ParseFloat = Callable[[str], Any]
+Key = Tuple[str, ...]
+Pos = int
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/py.typed b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/py.typed
new file mode 100644
index 0000000..7632ecf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/tomli/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/METADATA
new file mode 100644
index 0000000..77936a9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/METADATA
@@ -0,0 +1,69 @@
+Metadata-Version: 2.4
+Name: wheel
+Version: 0.46.3
+Summary: Command line tool for manipulating wheel files
+Keywords: wheel,packaging
+Author-email: Daniel Holth 
+Maintainer-email: Alex Grönholm 
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-Expression: MIT
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3 :: Only
+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 :: 3.14
+License-File: LICENSE.txt
+Requires-Dist: packaging >= 24.0
+Requires-Dist: pytest >= 6.0.0 ; extra == "test"
+Requires-Dist: setuptools >= 77 ; extra == "test"
+Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html
+Project-URL: Documentation, https://wheel.readthedocs.io/
+Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues
+Project-URL: Source, https://github.com/pypa/wheel
+Provides-Extra: test
+
+wheel
+=====
+
+This is a command line tool for manipulating Python wheel files, as defined in
+`PEP 427`_. It contains the following functionality:
+
+* Convert ``.egg`` archives into ``.whl``
+* Unpack wheel archives
+* Repack wheel archives
+* Add or remove tags in existing wheel archives
+
+.. _PEP 427: https://www.python.org/dev/peps/pep-0427/
+
+Historical note
+---------------
+
+This project used to contain the implementation of the setuptools_ ``bdist_wheel``
+command, but as of setuptools v70.1, it no longer needs ``wheel`` installed for that to
+work. Thus, you should install this **only** if you intend to use the ``wheel`` command
+line tool!
+
+.. _setuptools: https://pypi.org/project/setuptools/
+
+Documentation
+-------------
+
+The documentation_ can be found on Read The Docs.
+
+.. _documentation: https://wheel.readthedocs.io/
+
+Code of Conduct
+---------------
+
+Everyone interacting in the wheel project's codebases, issue trackers, chat
+rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
+
+.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/RECORD
new file mode 100644
index 0000000..5890933
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/RECORD
@@ -0,0 +1,22 @@
+bin/wheel,sha256=OLjFs9G7MQ-f_mhuW2aLGpngsyphW_v5FIoyN88Hfk0,339
+wheel-0.46.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+wheel-0.46.3.dist-info/METADATA,sha256=IpEKqXyonLzCCgGeJ_4xNgt5KaS9ZsoNMQ-ZpE9szTU,2410
+wheel-0.46.3.dist-info/RECORD,,
+wheel-0.46.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+wheel-0.46.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
+wheel-0.46.3.dist-info/entry_points.txt,sha256=JJdtSAGTvMLbIkTVZUAMvGKO39FtWfCVF8mp_NH6e4g,110
+wheel-0.46.3.dist-info/licenses/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
+wheel/__init__.py,sha256=UweKvhe4SyP7zFyDoYo8BOuwTA6q3-_WpMmY2NNO54c,59
+wheel/__main__.py,sha256=_83wl9tyGU2cHiqfudpubGHdRL5uonPXnzeznznkxzs,512
+wheel/_bdist_wheel.py,sha256=bpmNa7_s-CYFkVgXf9ENAYTiJ01XBhRW4pxH1T8XYsI,21729
+wheel/_commands/__init__.py,sha256=fCRAQZNDyj2JLrufdgPsBlaRS_t_j_aBUMpXj09KZ4E,4432
+wheel/_commands/convert.py,sha256=0wSJMU0m-6LY16Om8Wmmloy-hJWFZeOmI8hT-2Z7Qms,12743
+wheel/_commands/pack.py,sha256=o3iwjfRHl7N9ul-M2kHbewLJZnqBLAWf0tzUCwoiTMw,3078
+wheel/_commands/tags.py,sha256=Rv2ySVb8-qX3osKp3uJgxcIMXkjt43XUD0-zvC6KvnY,4775
+wheel/_commands/unpack.py,sha256=AjDSS23XYyCSFfifnMutinrpPv-DK_2wbNHkKAUFwgM,1016
+wheel/_metadata.py,sha256=BP5jC9uC1hyicp7nL4FJ2LYixNFpEJIV_uMDY1KBZBg,6188
+wheel/_setuptools_logging.py,sha256=-5KC-lne0ilOUWIDfOkqapUWGMFZhuKYDIavIZiB5kM,781
+wheel/bdist_wheel.py,sha256=HrzYiSzMkh5ohAAhlQnYBS1p8qbr85X6F59xqxd9kBg,1102
+wheel/macosx_libfile.py,sha256=pL0wm88jRMl_4ASgGlNg_mz69Zmv5xm8JSkjLdwyvIQ,16712
+wheel/metadata.py,sha256=GknOO7JJiZMlcEe_fiD7nqnDTTLd0sX_-IgipM4L3-4,757
+wheel/wheelfile.py,sha256=m_g_7TNsEp-j-xnvSr5yDLEFb1nhyObueq9Q5_1_lBA,8720
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/WHEEL
new file mode 100644
index 0000000..d8b9936
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: flit 3.12.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/entry_points.txt
new file mode 100644
index 0000000..dcebd58
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/entry_points.txt
@@ -0,0 +1,6 @@
+[console_scripts]
+wheel=wheel._commands:main
+
+[distutils.commands]
+bdist_wheel=wheel.bdist_wheel:bdist_wheel
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt
new file mode 100644
index 0000000..a31470f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2012 Daniel Holth  and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__init__.py
new file mode 100644
index 0000000..b5b076f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__init__.py
@@ -0,0 +1,3 @@
+from __future__ import annotations
+
+__version__ = "0.46.3"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__main__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__main__.py
new file mode 100644
index 0000000..3c7de5f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/__main__.py
@@ -0,0 +1,25 @@
+"""
+Wheel command line tool (enables the ``python -m wheel`` syntax)
+"""
+
+from __future__ import annotations
+
+import sys
+from typing import NoReturn
+
+
+def main() -> NoReturn:  # needed for console script
+    if __package__ == "":
+        # To be able to run 'python wheel-0.9.whl/wheel':
+        import os.path
+
+        path = os.path.dirname(os.path.dirname(__file__))
+        sys.path[0:0] = [path]
+
+    from ._commands import main as cli_main
+
+    sys.exit(cli_main())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_bdist_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_bdist_wheel.py
new file mode 100644
index 0000000..575fbfb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_bdist_wheel.py
@@ -0,0 +1,616 @@
+"""
+Create a wheel (.whl) distribution.
+
+A wheel is a built archive format.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+import re
+import shutil
+import stat
+import struct
+import sys
+import sysconfig
+import warnings
+from collections.abc import Iterable, Sequence
+from email.generator import BytesGenerator, Generator
+from email.policy import EmailPolicy
+from glob import iglob
+from shutil import rmtree
+from typing import TYPE_CHECKING, Callable, Literal, cast
+from zipfile import ZIP_DEFLATED, ZIP_STORED
+
+import setuptools
+from packaging import tags
+from packaging import version as _packaging_version
+from setuptools import Command
+
+from . import __version__ as wheel_version
+from ._metadata import pkginfo_to_metadata
+from .wheelfile import WheelFile
+
+if TYPE_CHECKING:
+    import types
+
+# ensure Python logging is configured
+try:
+    __import__("setuptools.logging")
+except ImportError:
+    # setuptools < ??
+    from . import _setuptools_logging
+
+    _setuptools_logging.configure()
+
+log = logging.getLogger("wheel")
+
+
+def safe_name(name: str) -> str:
+    """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: str) -> str:
+    """
+    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)
+
+
+setuptools_major_version = int(setuptools.__version__.split(".")[0])
+
+PY_LIMITED_API_PATTERN = r"cp3\d"
+
+
+def _is_32bit_interpreter() -> bool:
+    return struct.calcsize("P") == 4
+
+
+def python_tag() -> str:
+    return f"py{sys.version_info[0]}"
+
+
+def get_platform(archive_root: str | None) -> str:
+    """Return our platform name 'win32', 'linux_x86_64'"""
+    result = sysconfig.get_platform()
+    if result.startswith("macosx") and archive_root is not None:
+        from .macosx_libfile import calculate_macosx_platform_tag
+
+        result = calculate_macosx_platform_tag(archive_root, result)
+    elif _is_32bit_interpreter():
+        if result == "linux-x86_64":
+            # pip pull request #3497
+            result = "linux-i686"
+        elif result == "linux-aarch64":
+            # packaging pull request #234
+            # TODO armv8l, packaging pull request #690 => this did not land
+            # in pip/packaging yet
+            result = "linux-armv7l"
+
+    return result.replace("-", "_")
+
+
+def get_flag(
+    var: str, fallback: bool, expected: bool = True, warn: bool = True
+) -> bool:
+    """Use a fallback value for determining SOABI flags if the needed config
+    var is unset or unavailable."""
+    val = sysconfig.get_config_var(var)
+    if val is None:
+        if warn:
+            warnings.warn(
+                f"Config variable '{var}' is unset, Python ABI tag may be incorrect",
+                RuntimeWarning,
+                stacklevel=2,
+            )
+        return fallback
+    return val == expected
+
+
+def get_abi_tag() -> str | None:
+    """Return the ABI tag based on SOABI (if available) or emulate SOABI (PyPy2)."""
+    soabi: str = sysconfig.get_config_var("SOABI")
+    impl = tags.interpreter_name()
+    if not soabi and impl in ("cp", "pp") and hasattr(sys, "maxunicode"):
+        d = ""
+        m = ""
+        u = ""
+        if get_flag("Py_DEBUG", hasattr(sys, "gettotalrefcount"), warn=(impl == "cp")):
+            d = "d"
+
+        if get_flag(
+            "WITH_PYMALLOC",
+            impl == "cp",
+            warn=(impl == "cp" and sys.version_info < (3, 8)),
+        ) and sys.version_info < (3, 8):
+            m = "m"
+
+        abi = f"{impl}{tags.interpreter_version()}{d}{m}{u}"
+    elif soabi and impl == "cp" and soabi.startswith("cpython"):
+        # non-Windows
+        abi = "cp" + soabi.split("-")[1]
+    elif soabi and impl == "cp" and soabi.startswith("cp"):
+        # Windows
+        abi = soabi.split("-")[0]
+    elif soabi and impl == "pp":
+        # we want something like pypy36-pp73
+        abi = "-".join(soabi.split("-")[:2])
+        abi = abi.replace(".", "_").replace("-", "_")
+    elif soabi and impl == "graalpy":
+        abi = "-".join(soabi.split("-")[:3])
+        abi = abi.replace(".", "_").replace("-", "_")
+    elif soabi:
+        abi = soabi.replace(".", "_").replace("-", "_")
+    else:
+        abi = None
+
+    return abi
+
+
+def safer_name(name: str) -> str:
+    return safe_name(name).replace("-", "_")
+
+
+def safer_version(version: str) -> str:
+    return safe_version(version).replace("-", "_")
+
+
+def remove_readonly(
+    func: Callable[..., object],
+    path: str,
+    excinfo: tuple[type[Exception], Exception, types.TracebackType],
+) -> None:
+    remove_readonly_exc(func, path, excinfo[1])
+
+
+def remove_readonly_exc(func: Callable[..., object], path: str, exc: Exception) -> None:
+    os.chmod(path, stat.S_IWRITE)
+    func(path)
+
+
+class bdist_wheel(Command):
+    description = "create a wheel distribution"
+
+    supported_compressions = {
+        "stored": ZIP_STORED,
+        "deflated": ZIP_DEFLATED,
+    }
+
+    user_options = [
+        ("bdist-dir=", "b", "temporary directory for creating the distribution"),
+        (
+            "plat-name=",
+            "p",
+            "platform name to embed in generated filenames "
+            f"(default: {get_platform(None)})",
+        ),
+        (
+            "keep-temp",
+            "k",
+            "keep the pseudo-installation tree around after "
+            "creating the distribution archive",
+        ),
+        ("dist-dir=", "d", "directory to put final built distributions in"),
+        ("skip-build", None, "skip rebuilding everything (for testing/debugging)"),
+        (
+            "relative",
+            None,
+            "build the archive using relative paths (default: false)",
+        ),
+        (
+            "owner=",
+            "u",
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            "group=",
+            "g",
+            "Group name used when creating a tar file [default: current group]",
+        ),
+        ("universal", None, "make a universal wheel (default: false)"),
+        (
+            "compression=",
+            None,
+            "zipfile compression (one of: {}) (default: 'deflated')".format(
+                ", ".join(supported_compressions)
+            ),
+        ),
+        (
+            "python-tag=",
+            None,
+            f"Python implementation compatibility tag (default: '{python_tag()}')",
+        ),
+        (
+            "build-number=",
+            None,
+            "Build number for this particular version. "
+            "As specified in PEP-0427, this must start with a digit. "
+            "[default: None]",
+        ),
+        (
+            "py-limited-api=",
+            None,
+            "Python tag (cp32|cp33|cpNN) for abi3 wheel tag (default: false)",
+        ),
+    ]
+
+    boolean_options = ["keep-temp", "skip-build", "relative", "universal"]
+
+    def initialize_options(self):
+        self.bdist_dir: str = None
+        self.data_dir = None
+        self.plat_name: str | None = None
+        self.plat_tag = None
+        self.format = "zip"
+        self.keep_temp = False
+        self.dist_dir: str | None = None
+        self.egginfo_dir = None
+        self.root_is_pure: bool | None = None
+        self.skip_build = None
+        self.relative = False
+        self.owner = None
+        self.group = None
+        self.universal: bool = False
+        self.compression: str | int = "deflated"
+        self.python_tag: str = python_tag()
+        self.build_number: str | None = None
+        self.py_limited_api: str | Literal[False] = False
+        self.plat_name_supplied = False
+
+    def finalize_options(self):
+        if self.bdist_dir is None:
+            bdist_base = self.get_finalized_command("bdist").bdist_base
+            self.bdist_dir = os.path.join(bdist_base, "wheel")
+
+        egg_info = self.distribution.get_command_obj("egg_info")
+        egg_info.ensure_finalized()  # needed for correct `wheel_dist_name`
+
+        self.data_dir = self.wheel_dist_name + ".data"
+        self.plat_name_supplied = self.plat_name is not None
+
+        try:
+            self.compression = self.supported_compressions[self.compression]
+        except KeyError:
+            raise ValueError(f"Unsupported compression: {self.compression}") from None
+
+        need_options = ("dist_dir", "plat_name", "skip_build")
+
+        self.set_undefined_options("bdist", *zip(need_options, need_options))
+
+        self.root_is_pure = not (
+            self.distribution.has_ext_modules() or self.distribution.has_c_libraries()
+        )
+
+        if self.py_limited_api and not re.match(
+            PY_LIMITED_API_PATTERN, self.py_limited_api
+        ):
+            raise ValueError(f"py-limited-api must match '{PY_LIMITED_API_PATTERN}'")
+
+        # Support legacy [wheel] section for setting universal
+        wheel = self.distribution.get_option_dict("wheel")
+        if "universal" in wheel:
+            # please don't define this in your global configs
+            log.warning(
+                "The [wheel] section is deprecated. Use [bdist_wheel] instead.",
+            )
+            val = wheel["universal"][1].strip()
+            if val.lower() in ("1", "true", "yes"):
+                self.universal = True
+
+        if self.build_number is not None and not self.build_number[:1].isdigit():
+            raise ValueError("Build tag (build-number) must start with a digit.")
+
+    @property
+    def wheel_dist_name(self):
+        """Return distribution full name with - replaced with _"""
+        components = (
+            safer_name(self.distribution.get_name()),
+            safer_version(self.distribution.get_version()),
+        )
+        if self.build_number:
+            components += (self.build_number,)
+        return "-".join(components)
+
+    def get_tag(self) -> tuple[str, str, str]:
+        # bdist sets self.plat_name if unset, we should only use it for purepy
+        # wheels if the user supplied it.
+        if self.plat_name_supplied:
+            plat_name = cast(str, self.plat_name)
+        elif self.root_is_pure:
+            plat_name = "any"
+        else:
+            # macosx contains system version in platform name so need special handle
+            if self.plat_name and not self.plat_name.startswith("macosx"):
+                plat_name = self.plat_name
+            else:
+                # on macosx always limit the platform name to comply with any
+                # c-extension modules in bdist_dir, since the user can specify
+                # a higher MACOSX_DEPLOYMENT_TARGET via tools like CMake
+
+                # on other platforms, and on macosx if there are no c-extension
+                # modules, use the default platform name.
+                plat_name = get_platform(self.bdist_dir)
+
+            if _is_32bit_interpreter():
+                if plat_name in ("linux-x86_64", "linux_x86_64"):
+                    plat_name = "linux_i686"
+                if plat_name in ("linux-aarch64", "linux_aarch64"):
+                    # TODO armv8l, packaging pull request #690 => this did not land
+                    # in pip/packaging yet
+                    plat_name = "linux_armv7l"
+
+        plat_name = (
+            plat_name.lower().replace("-", "_").replace(".", "_").replace(" ", "_")
+        )
+
+        if self.root_is_pure:
+            if self.universal:
+                impl = "py2.py3"
+            else:
+                impl = self.python_tag
+            tag = (impl, "none", plat_name)
+        else:
+            impl_name = tags.interpreter_name()
+            impl_ver = tags.interpreter_version()
+            impl = impl_name + impl_ver
+            # We don't work on CPython 3.1, 3.0.
+            if self.py_limited_api and (impl_name + impl_ver).startswith("cp3"):
+                impl = self.py_limited_api
+                abi_tag = "abi3"
+            else:
+                abi_tag = str(get_abi_tag()).lower()
+            tag = (impl, abi_tag, plat_name)
+            # issue gh-374: allow overriding plat_name
+            supported_tags = [
+                (t.interpreter, t.abi, plat_name) for t in tags.sys_tags()
+            ]
+            assert tag in supported_tags, (
+                f"would build wheel with unsupported tag {tag}"
+            )
+        return tag
+
+    def run(self):
+        build_scripts = self.reinitialize_command("build_scripts")
+        build_scripts.executable = "python"
+        build_scripts.force = True
+
+        build_ext = self.reinitialize_command("build_ext")
+        build_ext.inplace = False
+
+        if not self.skip_build:
+            self.run_command("build")
+
+        install = self.reinitialize_command("install", reinit_subcommands=True)
+        install.root = self.bdist_dir
+        install.compile = False
+        install.skip_build = self.skip_build
+        install.warn_dir = False
+
+        # A wheel without setuptools scripts is more cross-platform.
+        # Use the (undocumented) `no_ep` option to setuptools'
+        # install_scripts command to avoid creating entry point scripts.
+        install_scripts = self.reinitialize_command("install_scripts")
+        install_scripts.no_ep = True
+
+        # Use a custom scheme for the archive, because we have to decide
+        # at installation time which scheme to use.
+        for key in ("headers", "scripts", "data", "purelib", "platlib"):
+            setattr(install, "install_" + key, os.path.join(self.data_dir, key))
+
+        basedir_observed = ""
+
+        if os.name == "nt":
+            # win32 barfs if any of these are ''; could be '.'?
+            # (distutils.command.install:change_roots bug)
+            basedir_observed = os.path.normpath(os.path.join(self.data_dir, ".."))
+            self.install_libbase = self.install_lib = basedir_observed
+
+        setattr(
+            install,
+            "install_purelib" if self.root_is_pure else "install_platlib",
+            basedir_observed,
+        )
+
+        log.info(f"installing to {self.bdist_dir}")
+
+        self.run_command("install")
+
+        impl_tag, abi_tag, plat_tag = self.get_tag()
+        archive_basename = f"{self.wheel_dist_name}-{impl_tag}-{abi_tag}-{plat_tag}"
+        if not self.relative:
+            archive_root = self.bdist_dir
+        else:
+            archive_root = os.path.join(
+                self.bdist_dir, self._ensure_relative(install.install_base)
+            )
+
+        self.set_undefined_options("install_egg_info", ("target", "egginfo_dir"))
+        distinfo_dirname = (
+            f"{safer_name(self.distribution.get_name())}-"
+            f"{safer_version(self.distribution.get_version())}.dist-info"
+        )
+        distinfo_dir = os.path.join(self.bdist_dir, distinfo_dirname)
+        self.egg2dist(self.egginfo_dir, distinfo_dir)
+
+        self.write_wheelfile(distinfo_dir)
+
+        # Make the archive
+        if not os.path.exists(self.dist_dir):
+            os.makedirs(self.dist_dir)
+
+        wheel_path = os.path.join(self.dist_dir, archive_basename + ".whl")
+        with WheelFile(wheel_path, "w", self.compression) as wf:
+            wf.write_files(archive_root)
+
+        # Add to 'Distribution.dist_files' so that the "upload" command works
+        getattr(self.distribution, "dist_files", []).append(
+            (
+                "bdist_wheel",
+                "{}.{}".format(*sys.version_info[:2]),  # like 3.7
+                wheel_path,
+            )
+        )
+
+        if not self.keep_temp:
+            log.info(f"removing {self.bdist_dir}")
+            if not self.dry_run:
+                if sys.version_info < (3, 12):
+                    rmtree(self.bdist_dir, onerror=remove_readonly)
+                else:
+                    rmtree(self.bdist_dir, onexc=remove_readonly_exc)
+
+    def write_wheelfile(
+        self, wheelfile_base: str, generator: str = f"bdist_wheel ({wheel_version})"
+    ):
+        from email.message import Message
+
+        msg = Message()
+        msg["Wheel-Version"] = "1.0"  # of the spec
+        msg["Generator"] = generator
+        msg["Root-Is-Purelib"] = str(self.root_is_pure).lower()
+        if self.build_number is not None:
+            msg["Build"] = self.build_number
+
+        # Doesn't work for bdist_wininst
+        impl_tag, abi_tag, plat_tag = self.get_tag()
+        for impl in impl_tag.split("."):
+            for abi in abi_tag.split("."):
+                for plat in plat_tag.split("."):
+                    msg["Tag"] = "-".join((impl, abi, plat))
+
+        wheelfile_path = os.path.join(wheelfile_base, "WHEEL")
+        log.info(f"creating {wheelfile_path}")
+        with open(wheelfile_path, "wb") as f:
+            BytesGenerator(f, maxheaderlen=0).flatten(msg)
+
+    def _ensure_relative(self, path: str) -> str:
+        # copied from dir_util, deleted
+        drive, path = os.path.splitdrive(path)
+        if path[0:1] == os.sep:
+            path = drive + path[1:]
+        return path
+
+    @property
+    def license_paths(self) -> Iterable[str]:
+        if setuptools_major_version >= 57:
+            # Setuptools has resolved any patterns to actual file names
+            return self.distribution.metadata.license_files or ()
+
+        files: set[str] = set()
+        metadata = self.distribution.get_option_dict("metadata")
+        if setuptools_major_version >= 42:
+            # Setuptools recognizes the license_files option but does not do globbing
+            patterns = cast(Sequence[str], self.distribution.metadata.license_files)
+        else:
+            # Prior to those, wheel is entirely responsible for handling license files
+            if "license_files" in metadata:
+                patterns = metadata["license_files"][1].split()
+            else:
+                patterns = ()
+
+        if "license_file" in metadata:
+            warnings.warn(
+                'The "license_file" option is deprecated. Use "license_files" instead.',
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            files.add(metadata["license_file"][1])
+
+        if not files and not patterns and not isinstance(patterns, list):
+            patterns = ("LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*")
+
+        for pattern in patterns:
+            for path in iglob(pattern):
+                if path.endswith("~"):
+                    log.debug(
+                        f'ignoring license file "{path}" as it looks like a backup'
+                    )
+                    continue
+
+                if path not in files and os.path.isfile(path):
+                    log.info(
+                        f'adding license file "{path}" (matched pattern "{pattern}")'
+                    )
+                    files.add(path)
+
+        return files
+
+    def egg2dist(self, egginfo_path: str, distinfo_path: str):
+        """Convert an .egg-info directory into a .dist-info directory"""
+
+        def adios(p: str) -> None:
+            """Appropriately delete directory, file or link."""
+            if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p):
+                shutil.rmtree(p)
+            elif os.path.exists(p):
+                os.unlink(p)
+
+        adios(distinfo_path)
+
+        if not os.path.exists(egginfo_path):
+            # There is no egg-info. This is probably because the egg-info
+            # file/directory is not named matching the distribution name used
+            # to name the archive file. Check for this case and report
+            # accordingly.
+            import glob
+
+            pat = os.path.join(os.path.dirname(egginfo_path), "*.egg-info")
+            possible = glob.glob(pat)
+            err = f"Egg metadata expected at {egginfo_path} but not found"
+            if possible:
+                alt = os.path.basename(possible[0])
+                err += f" ({alt} found - possible misnamed archive file?)"
+
+            raise ValueError(err)
+
+        if os.path.isfile(egginfo_path):
+            # .egg-info is a single file
+            pkg_info = pkginfo_to_metadata(egginfo_path, egginfo_path)
+            os.mkdir(distinfo_path)
+        else:
+            # .egg-info is a directory
+            pkginfo_path = os.path.join(egginfo_path, "PKG-INFO")
+            pkg_info = pkginfo_to_metadata(egginfo_path, pkginfo_path)
+
+            # ignore common egg metadata that is useless to wheel
+            shutil.copytree(
+                egginfo_path,
+                distinfo_path,
+                ignore=lambda x, y: {
+                    "PKG-INFO",
+                    "requires.txt",
+                    "SOURCES.txt",
+                    "not-zip-safe",
+                },
+            )
+
+            # delete dependency_links if it is only whitespace
+            dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt")
+            with open(dependency_links_path, encoding="utf-8") as dependency_links_file:
+                dependency_links = dependency_links_file.read().strip()
+            if not dependency_links:
+                adios(dependency_links_path)
+
+        pkg_info_path = os.path.join(distinfo_path, "METADATA")
+        serialization_policy = EmailPolicy(
+            utf8=True,
+            mangle_from_=False,
+            max_line_length=0,
+        )
+        with open(pkg_info_path, "w", encoding="utf-8") as out:
+            Generator(out, policy=serialization_policy).flatten(pkg_info)
+
+        for license_path in self.license_paths:
+            filename = os.path.basename(license_path)
+            shutil.copy(license_path, os.path.join(distinfo_path, filename))
+
+        adios(egginfo_path)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/__init__.py
new file mode 100644
index 0000000..42f1d7e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/__init__.py
@@ -0,0 +1,153 @@
+"""
+Wheel command-line utility.
+"""
+
+from __future__ import annotations
+
+import argparse
+import os
+import sys
+from argparse import ArgumentTypeError
+
+from ..wheelfile import WheelError
+
+
+def unpack_f(args: argparse.Namespace) -> None:
+    from .unpack import unpack
+
+    unpack(args.wheelfile, args.dest)
+
+
+def pack_f(args: argparse.Namespace) -> None:
+    from .pack import pack
+
+    pack(args.directory, args.dest_dir, args.build_number)
+
+
+def convert_f(args: argparse.Namespace) -> None:
+    from .convert import convert
+
+    convert(args.files, args.dest_dir, args.verbose)
+
+
+def tags_f(args: argparse.Namespace) -> None:
+    from .tags import tags
+
+    names = (
+        tags(
+            wheel,
+            args.python_tag,
+            args.abi_tag,
+            args.platform_tag,
+            args.build,
+            args.remove,
+        )
+        for wheel in args.wheel
+    )
+
+    for name in names:
+        print(name)
+
+
+def version_f(args: argparse.Namespace) -> None:
+    from .. import __version__
+
+    print(f"wheel {__version__}")
+
+
+def parse_build_tag(build_tag: str) -> str:
+    if build_tag and not build_tag[0].isdigit():
+        raise ArgumentTypeError("build tag must begin with a digit")
+    elif "-" in build_tag:
+        raise ArgumentTypeError("invalid character ('-') in build tag")
+
+    return build_tag
+
+
+TAGS_HELP = """\
+Make a new wheel with given tags. Any tags unspecified will remain the same.
+Starting the tags with a "+" will append to the existing tags. Starting with a
+"-" will remove a tag (use --option=-TAG syntax). Multiple tags can be
+separated by ".". The original file will remain unless --remove is given.  The
+output filename(s) will be displayed on stdout for further processing.
+"""
+
+
+def parser() -> argparse.ArgumentParser:
+    p = argparse.ArgumentParser()
+    s = p.add_subparsers(help="commands")
+
+    unpack_parser = s.add_parser("unpack", help="Unpack wheel")
+    unpack_parser.add_argument(
+        "--dest", "-d", help="Destination directory", default="."
+    )
+    unpack_parser.add_argument("wheelfile", help="Wheel file")
+    unpack_parser.set_defaults(func=unpack_f)
+
+    repack_parser = s.add_parser("pack", help="Repack wheel")
+    repack_parser.add_argument("directory", help="Root directory of the unpacked wheel")
+    repack_parser.add_argument(
+        "--dest-dir",
+        "-d",
+        default=os.path.curdir,
+        help="Directory to store the wheel (default %(default)s)",
+    )
+    repack_parser.add_argument(
+        "--build-number", help="Build tag to use in the wheel name"
+    )
+    repack_parser.set_defaults(func=pack_f)
+
+    convert_parser = s.add_parser("convert", help="Convert egg or wininst to wheel")
+    convert_parser.add_argument("files", nargs="*", help="Files to convert")
+    convert_parser.add_argument(
+        "--dest-dir",
+        "-d",
+        default=os.path.curdir,
+        help="Directory to store wheels (default %(default)s)",
+    )
+    convert_parser.add_argument("--verbose", "-v", action="store_true")
+    convert_parser.set_defaults(func=convert_f)
+
+    tags_parser = s.add_parser(
+        "tags", help="Add or replace the tags on a wheel", description=TAGS_HELP
+    )
+    tags_parser.add_argument("wheel", nargs="*", help="Existing wheel(s) to retag")
+    tags_parser.add_argument(
+        "--remove",
+        action="store_true",
+        help="Remove the original files, keeping only the renamed ones",
+    )
+    tags_parser.add_argument(
+        "--python-tag", metavar="TAG", help="Specify an interpreter tag(s)"
+    )
+    tags_parser.add_argument("--abi-tag", metavar="TAG", help="Specify an ABI tag(s)")
+    tags_parser.add_argument(
+        "--platform-tag", metavar="TAG", help="Specify a platform tag(s)"
+    )
+    tags_parser.add_argument(
+        "--build", type=parse_build_tag, metavar="BUILD", help="Specify a build tag"
+    )
+    tags_parser.set_defaults(func=tags_f)
+
+    version_parser = s.add_parser("version", help="Print version and exit")
+    version_parser.set_defaults(func=version_f)
+
+    help_parser = s.add_parser("help", help="Show this help")
+    help_parser.set_defaults(func=lambda args: p.print_help())
+
+    return p
+
+
+def main() -> int:
+    p = parser()
+    args = p.parse_args()
+    if not hasattr(args, "func"):
+        p.print_help()
+    else:
+        try:
+            args.func(args)
+            return 0
+        except WheelError as e:
+            print(e, file=sys.stderr)
+
+    return 1
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/convert.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/convert.py
new file mode 100644
index 0000000..cafd12c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/convert.py
@@ -0,0 +1,337 @@
+from __future__ import annotations
+
+import os.path
+import re
+from abc import ABCMeta, abstractmethod
+from collections import defaultdict
+from collections.abc import Iterator
+from email.message import Message
+from email.parser import Parser
+from email.policy import EmailPolicy
+from glob import iglob
+from pathlib import Path
+from textwrap import dedent
+from zipfile import ZipFile
+
+from packaging.tags import parse_tag
+
+from .. import __version__
+from .._metadata import generate_requirements
+from ..wheelfile import WheelFile
+
+egg_filename_re = re.compile(
+    r"""
+    (?P.+?)-(?P.+?)
+    (-(?Ppy\d\.\d+)
+     (-(?P.+?))?
+    )?.egg$""",
+    re.VERBOSE,
+)
+egg_info_re = re.compile(
+    r"""
+    ^(?P.+?)-(?P.+?)
+    (-(?Ppy\d\.\d+)
+    )?.egg-info/""",
+    re.VERBOSE,
+)
+wininst_re = re.compile(
+    r"\.(?Pwin32|win-amd64)(?:-(?Ppy\d\.\d))?\.exe$"
+)
+pyd_re = re.compile(r"\.(?P[a-z0-9]+)-(?Pwin32|win_amd64)\.pyd$")
+serialization_policy = EmailPolicy(
+    utf8=True,
+    mangle_from_=False,
+    max_line_length=0,
+)
+GENERATOR = f"wheel {__version__}"
+
+
+def convert_requires(requires: str, metadata: Message) -> None:
+    extra: str | None = None
+    requirements: dict[str | None, list[str]] = defaultdict(list)
+    for line in requires.splitlines():
+        line = line.strip()
+        if not line:
+            continue
+
+        if line.startswith("[") and line.endswith("]"):
+            extra = line[1:-1]
+            continue
+
+        requirements[extra].append(line)
+
+    for key, value in generate_requirements(requirements):
+        metadata.add_header(key, value)
+
+
+def convert_pkg_info(pkginfo: str, metadata: Message) -> None:
+    parsed_message = Parser().parsestr(pkginfo)
+    for key, value in parsed_message.items():
+        key_lower = key.lower()
+        if value == "UNKNOWN":
+            continue
+
+        if key_lower == "description":
+            description_lines = value.splitlines()
+            if description_lines:
+                value = "\n".join(
+                    (
+                        description_lines[0].lstrip(),
+                        dedent("\n".join(description_lines[1:])),
+                        "\n",
+                    )
+                )
+            else:
+                value = "\n"
+
+            metadata.set_payload(value)
+        elif key_lower == "home-page":
+            metadata.add_header("Project-URL", f"Homepage, {value}")
+        elif key_lower == "download-url":
+            metadata.add_header("Project-URL", f"Download, {value}")
+        else:
+            metadata.add_header(key, value)
+
+    metadata.replace_header("Metadata-Version", "2.4")
+
+
+def normalize(name: str) -> str:
+    return re.sub(r"[-_.]+", "-", name).lower().replace("-", "_")
+
+
+class ConvertSource(metaclass=ABCMeta):
+    name: str
+    version: str
+    pyver: str = "py2.py3"
+    abi: str = "none"
+    platform: str = "any"
+    metadata: Message
+
+    @property
+    def dist_info_dir(self) -> str:
+        return f"{self.name}-{self.version}.dist-info"
+
+    @abstractmethod
+    def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+        pass
+
+
+class EggFileSource(ConvertSource):
+    def __init__(self, path: Path):
+        if not (match := egg_filename_re.match(path.name)):
+            raise ValueError(f"Invalid egg file name: {path.name}")
+
+        # Binary wheels are assumed to be for CPython
+        self.path = path
+        self.name = normalize(match.group("name"))
+        self.version = match.group("ver")
+        if pyver := match.group("pyver"):
+            self.pyver = pyver.replace(".", "")
+            if arch := match.group("arch"):
+                self.abi = self.pyver.replace("py", "cp")
+                self.platform = normalize(arch)
+
+        self.metadata = Message()
+
+    def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+        with ZipFile(self.path, "r") as zip_file:
+            for filename in sorted(zip_file.namelist()):
+                # Skip pure directory entries
+                if filename.endswith("/"):
+                    continue
+
+                # Handle files in the egg-info directory specially, selectively moving
+                # them to the dist-info directory while converting as needed
+                if filename.startswith("EGG-INFO/"):
+                    if filename == "EGG-INFO/requires.txt":
+                        requires = zip_file.read(filename).decode("utf-8")
+                        convert_requires(requires, self.metadata)
+                    elif filename == "EGG-INFO/PKG-INFO":
+                        pkginfo = zip_file.read(filename).decode("utf-8")
+                        convert_pkg_info(pkginfo, self.metadata)
+                    elif filename == "EGG-INFO/entry_points.txt":
+                        yield (
+                            f"{self.dist_info_dir}/entry_points.txt",
+                            zip_file.read(filename),
+                        )
+
+                    continue
+
+                # For any other file, just pass it through
+                yield filename, zip_file.read(filename)
+
+
+class EggDirectorySource(EggFileSource):
+    def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+        for dirpath, _, filenames in os.walk(self.path):
+            for filename in sorted(filenames):
+                path = Path(dirpath, filename)
+                if path.parent.name == "EGG-INFO":
+                    if path.name == "requires.txt":
+                        requires = path.read_text("utf-8")
+                        convert_requires(requires, self.metadata)
+                    elif path.name == "PKG-INFO":
+                        pkginfo = path.read_text("utf-8")
+                        convert_pkg_info(pkginfo, self.metadata)
+                        if name := self.metadata.get("Name"):
+                            self.name = normalize(name)
+
+                        if version := self.metadata.get("Version"):
+                            self.version = version
+                    elif path.name == "entry_points.txt":
+                        yield (
+                            f"{self.dist_info_dir}/entry_points.txt",
+                            path.read_bytes(),
+                        )
+
+                    continue
+
+                # For any other file, just pass it through
+                yield str(path.relative_to(self.path)), path.read_bytes()
+
+
+class WininstFileSource(ConvertSource):
+    """
+    Handles distributions created with ``bdist_wininst``.
+
+    The egginfo filename has the format::
+
+        name-ver(-pyver)(-arch).egg-info
+
+    The installer filename has the format::
+
+        name-ver.arch(-pyver).exe
+
+    Some things to note:
+
+    1. The installer filename is not definitive. An installer can be renamed
+       and work perfectly well as an installer. So more reliable data should
+       be used whenever possible.
+    2. The egg-info data should be preferred for the name and version, because
+       these come straight from the distutils metadata, and are mandatory.
+    3. The pyver from the egg-info data should be ignored, as it is
+       constructed from the version of Python used to build the installer,
+       which is irrelevant - the installer filename is correct here (even to
+       the point that when it's not there, any version is implied).
+    4. The architecture must be taken from the installer filename, as it is
+       not included in the egg-info data.
+    5. Architecture-neutral installers still have an architecture because the
+       installer format itself (being executable) is architecture-specific. We
+       should therefore ignore the architecture if the content is pure-python.
+    """
+
+    def __init__(self, path: Path):
+        self.path = path
+        self.metadata = Message()
+
+        # Determine the initial architecture and Python version from the file name
+        # (if possible)
+        if match := wininst_re.search(path.name):
+            self.platform = normalize(match.group("platform"))
+            if pyver := match.group("pyver"):
+                self.pyver = pyver.replace(".", "")
+
+        # Look for an .egg-info directory and any .pyd files for more precise info
+        egg_info_found = pyd_found = False
+        with ZipFile(self.path) as zip_file:
+            for filename in zip_file.namelist():
+                prefix, filename = filename.split("/", 1)
+                if not egg_info_found and (match := egg_info_re.match(filename)):
+                    egg_info_found = True
+                    self.name = normalize(match.group("name"))
+                    self.version = match.group("ver")
+                    if pyver := match.group("pyver"):
+                        self.pyver = pyver.replace(".", "")
+                elif not pyd_found and (match := pyd_re.search(filename)):
+                    pyd_found = True
+                    self.abi = match.group("abi")
+                    self.platform = match.group("platform")
+
+                if egg_info_found and pyd_found:
+                    break
+
+    def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+        dist_info_dir = f"{self.name}-{self.version}.dist-info"
+        data_dir = f"{self.name}-{self.version}.data"
+        with ZipFile(self.path, "r") as zip_file:
+            for filename in sorted(zip_file.namelist()):
+                # Skip pure directory entries
+                if filename.endswith("/"):
+                    continue
+
+                # Handle files in the egg-info directory specially, selectively moving
+                # them to the dist-info directory while converting as needed
+                prefix, target_filename = filename.split("/", 1)
+                if egg_info_re.search(target_filename):
+                    basename = target_filename.rsplit("/", 1)[-1]
+                    if basename == "requires.txt":
+                        requires = zip_file.read(filename).decode("utf-8")
+                        convert_requires(requires, self.metadata)
+                    elif basename == "PKG-INFO":
+                        pkginfo = zip_file.read(filename).decode("utf-8")
+                        convert_pkg_info(pkginfo, self.metadata)
+                    elif basename == "entry_points.txt":
+                        yield (
+                            f"{dist_info_dir}/entry_points.txt",
+                            zip_file.read(filename),
+                        )
+
+                    continue
+                elif prefix == "SCRIPTS":
+                    target_filename = f"{data_dir}/scripts/{target_filename}"
+
+                # For any other file, just pass it through
+                yield target_filename, zip_file.read(filename)
+
+
+def convert(files: list[str], dest_dir: str, verbose: bool) -> None:
+    for pat in files:
+        for archive in iglob(pat):
+            path = Path(archive)
+            if path.suffix == ".egg":
+                if path.is_dir():
+                    source: ConvertSource = EggDirectorySource(path)
+                else:
+                    source = EggFileSource(path)
+            else:
+                source = WininstFileSource(path)
+
+            if verbose:
+                print(f"{archive}...", flush=True, end="")
+
+            dest_path = Path(dest_dir) / (
+                f"{source.name}-{source.version}-{source.pyver}-{source.abi}"
+                f"-{source.platform}.whl"
+            )
+            with WheelFile(dest_path, "w") as wheelfile:
+                for name_or_zinfo, contents in source.generate_contents():
+                    wheelfile.writestr(name_or_zinfo, contents)
+
+                # Write the METADATA file
+                wheelfile.writestr(
+                    f"{source.dist_info_dir}/METADATA",
+                    source.metadata.as_string(policy=serialization_policy).encode(
+                        "utf-8"
+                    ),
+                )
+
+                # Write the WHEEL file
+                wheel_message = Message()
+                wheel_message.add_header("Wheel-Version", "1.0")
+                wheel_message.add_header("Generator", GENERATOR)
+                wheel_message.add_header(
+                    "Root-Is-Purelib", str(source.platform == "any").lower()
+                )
+                tags = parse_tag(f"{source.pyver}-{source.abi}-{source.platform}")
+                for tag in sorted(tags, key=lambda tag: tag.interpreter):
+                    wheel_message.add_header("Tag", str(tag))
+
+                wheelfile.writestr(
+                    f"{source.dist_info_dir}/WHEEL",
+                    wheel_message.as_string(policy=serialization_policy).encode(
+                        "utf-8"
+                    ),
+                )
+
+            if verbose:
+                print("OK")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/pack.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/pack.py
new file mode 100644
index 0000000..1321ce9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/pack.py
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+import email.policy
+import os.path
+import re
+from email.generator import BytesGenerator
+from email.parser import BytesParser
+
+from ..wheelfile import WheelError, WheelFile
+
+DIST_INFO_RE = re.compile(r"^(?P(?P.+?)-(?P\d.*?))\.dist-info$")
+
+
+def pack(directory: str, dest_dir: str, build_number: str | None) -> None:
+    """Repack a previously unpacked wheel directory into a new wheel file.
+
+    The .dist-info/WHEEL file must contain one or more tags so that the target
+    wheel file name can be determined.
+
+    :param directory: The unpacked wheel directory
+    :param dest_dir: Destination directory (defaults to the current directory)
+    """
+    # Find the .dist-info directory
+    dist_info_dirs = [
+        fn
+        for fn in os.listdir(directory)
+        if os.path.isdir(os.path.join(directory, fn)) and DIST_INFO_RE.match(fn)
+    ]
+    if len(dist_info_dirs) > 1:
+        raise WheelError(f"Multiple .dist-info directories found in {directory}")
+    elif not dist_info_dirs:
+        raise WheelError(f"No .dist-info directories found in {directory}")
+
+    # Determine the target wheel filename
+    dist_info_dir = dist_info_dirs[0]
+    name_version = DIST_INFO_RE.match(dist_info_dir).group("namever")
+
+    # Read the tags and the existing build number from .dist-info/WHEEL
+    wheel_file_path = os.path.join(directory, dist_info_dir, "WHEEL")
+    with open(wheel_file_path, "rb") as f:
+        info = BytesParser(policy=email.policy.compat32).parse(f)
+        tags: list[str] = info.get_all("Tag", [])
+        existing_build_number = info.get("Build")
+
+        if not tags:
+            raise WheelError(
+                f"No tags present in {dist_info_dir}/WHEEL; cannot determine target "
+                f"wheel filename"
+            )
+
+    # Set the wheel file name and add/replace/remove the Build tag in .dist-info/WHEEL
+    build_number = build_number if build_number is not None else existing_build_number
+    if build_number is not None:
+        del info["Build"]
+        if build_number:
+            info["Build"] = build_number
+            name_version += "-" + build_number
+
+        if build_number != existing_build_number:
+            with open(wheel_file_path, "wb") as f:
+                BytesGenerator(f, maxheaderlen=0).flatten(info)
+
+    # Reassemble the tags for the wheel file
+    tagline = compute_tagline(tags)
+
+    # Repack the wheel
+    wheel_path = os.path.join(dest_dir, f"{name_version}-{tagline}.whl")
+    with WheelFile(wheel_path, "w") as wf:
+        print(f"Repacking wheel as {wheel_path}...", end="", flush=True)
+        wf.write_files(directory)
+
+    print("OK")
+
+
+def compute_tagline(tags: list[str]) -> str:
+    """Compute a tagline from a list of tags.
+
+    :param tags: A list of tags
+    :return: A tagline
+    """
+    impls = sorted({tag.split("-")[0] for tag in tags})
+    abivers = sorted({tag.split("-")[1] for tag in tags})
+    platforms = sorted({tag.split("-")[2] for tag in tags})
+    return "-".join([".".join(impls), ".".join(abivers), ".".join(platforms)])
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/tags.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/tags.py
new file mode 100644
index 0000000..cec896b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/tags.py
@@ -0,0 +1,140 @@
+from __future__ import annotations
+
+import email.policy
+import itertools
+import os
+from collections.abc import Iterable
+from email.parser import BytesParser
+
+from ..wheelfile import WheelFile
+
+
+def _compute_tags(original_tags: Iterable[str], new_tags: str | None) -> set[str]:
+    """Add or replace tags. Supports dot-separated tags"""
+    if new_tags is None:
+        return set(original_tags)
+
+    if new_tags.startswith("+"):
+        return {*original_tags, *new_tags[1:].split(".")}
+
+    if new_tags.startswith("-"):
+        return set(original_tags) - set(new_tags[1:].split("."))
+
+    return set(new_tags.split("."))
+
+
+def tags(
+    wheel: str,
+    python_tags: str | None = None,
+    abi_tags: str | None = None,
+    platform_tags: str | None = None,
+    build_tag: str | None = None,
+    remove: bool = False,
+) -> str:
+    """Change the tags on a wheel file.
+
+    The tags are left unchanged if they are not specified. To specify "none",
+    use ["none"]. To append to the previous tags, a tag should start with a
+    "+".  If a tag starts with "-", it will be removed from existing tags.
+    Processing is done left to right.
+
+    :param wheel: The paths to the wheels
+    :param python_tags: The Python tags to set
+    :param abi_tags: The ABI tags to set
+    :param platform_tags: The platform tags to set
+    :param build_tag: The build tag to set
+    :param remove: Remove the original wheel
+    """
+    with WheelFile(wheel, "r") as f:
+        assert f.filename, f"{f.filename} must be available"
+
+        wheel_info = f.read(f.dist_info_path + "/WHEEL")
+        info = BytesParser(policy=email.policy.compat32).parsebytes(wheel_info)
+
+        original_wheel_name = os.path.basename(f.filename)
+        namever = f.parsed_filename.group("namever")
+        build = f.parsed_filename.group("build")
+        original_python_tags = f.parsed_filename.group("pyver").split(".")
+        original_abi_tags = f.parsed_filename.group("abi").split(".")
+        original_plat_tags = f.parsed_filename.group("plat").split(".")
+
+    tags: list[str] = info.get_all("Tag", [])
+    existing_build_tag = info.get("Build")
+
+    impls = {tag.split("-")[0] for tag in tags}
+    abivers = {tag.split("-")[1] for tag in tags}
+    platforms = {tag.split("-")[2] for tag in tags}
+
+    if impls != set(original_python_tags):
+        msg = f"Wheel internal tags {impls!r} != filename tags {original_python_tags!r}"
+        raise AssertionError(msg)
+
+    if abivers != set(original_abi_tags):
+        msg = f"Wheel internal tags {abivers!r} != filename tags {original_abi_tags!r}"
+        raise AssertionError(msg)
+
+    if platforms != set(original_plat_tags):
+        msg = (
+            f"Wheel internal tags {platforms!r} != filename tags {original_plat_tags!r}"
+        )
+        raise AssertionError(msg)
+
+    if existing_build_tag != build:
+        msg = (
+            f"Incorrect filename '{build}' "
+            f"& *.dist-info/WHEEL '{existing_build_tag}' build numbers"
+        )
+        raise AssertionError(msg)
+
+    # Start changing as needed
+    if build_tag is not None:
+        build = build_tag
+
+    final_python_tags = sorted(_compute_tags(original_python_tags, python_tags))
+    final_abi_tags = sorted(_compute_tags(original_abi_tags, abi_tags))
+    final_plat_tags = sorted(_compute_tags(original_plat_tags, platform_tags))
+
+    final_tags = [
+        namever,
+        ".".join(final_python_tags),
+        ".".join(final_abi_tags),
+        ".".join(final_plat_tags),
+    ]
+    if build:
+        final_tags.insert(1, build)
+
+    final_wheel_name = "-".join(final_tags) + ".whl"
+
+    if original_wheel_name != final_wheel_name:
+        del info["Tag"], info["Build"]
+        for a, b, c in itertools.product(
+            final_python_tags, final_abi_tags, final_plat_tags
+        ):
+            info["Tag"] = f"{a}-{b}-{c}"
+        if build:
+            info["Build"] = build
+
+        original_wheel_path = os.path.join(
+            os.path.dirname(f.filename), original_wheel_name
+        )
+        final_wheel_path = os.path.join(os.path.dirname(f.filename), final_wheel_name)
+
+        with (
+            WheelFile(original_wheel_path, "r") as fin,
+            WheelFile(final_wheel_path, "w") as fout,
+        ):
+            fout.comment = fin.comment  # preserve the comment
+            for item in fin.infolist():
+                if item.is_dir():
+                    continue
+                if item.filename == f.dist_info_path + "/RECORD":
+                    continue
+                if item.filename == f.dist_info_path + "/WHEEL":
+                    fout.writestr(item, info.as_bytes())
+                else:
+                    fout.writestr(item, fin.read(item))
+
+        if remove:
+            os.remove(original_wheel_path)
+
+    return final_wheel_name
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/unpack.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/unpack.py
new file mode 100644
index 0000000..83dc742
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_commands/unpack.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from pathlib import Path
+
+from ..wheelfile import WheelFile
+
+
+def unpack(path: str, dest: str = ".") -> None:
+    """Unpack a wheel.
+
+    Wheel content will be unpacked to {dest}/{name}-{ver}, where {name}
+    is the package name and {ver} its version.
+
+    :param path: The path to the wheel.
+    :param dest: Destination directory (default to current directory).
+    """
+    with WheelFile(path) as wf:
+        namever = wf.parsed_filename.group("namever")
+        destination = Path(dest) / namever
+        print(f"Unpacking to: {destination}...", end="", flush=True)
+        for zinfo in wf.filelist:
+            target_path = Path(wf.extract(zinfo, destination))
+
+            # Set permissions to the same values as they were set in the archive
+            # We have to do this manually due to
+            # https://github.com/python/cpython/issues/59999
+            permissions = zinfo.external_attr >> 16 & 0o777
+            target_path.chmod(permissions)
+
+    print("OK")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_metadata.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_metadata.py
new file mode 100644
index 0000000..e17a7b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_metadata.py
@@ -0,0 +1,184 @@
+"""
+Tools for converting old- to new-style metadata.
+"""
+
+from __future__ import annotations
+
+import functools
+import itertools
+import os.path
+import re
+import textwrap
+from collections.abc import Generator, Iterable, Iterator
+from email.message import Message
+from email.parser import Parser
+from typing import Literal
+
+from packaging.requirements import Requirement
+
+
+def _nonblank(str: str) -> bool | Literal[""]:
+    return str and not str.startswith("#")
+
+
+@functools.singledispatch
+def yield_lines(iterable: Iterable[str]) -> Iterator[str]:
+    r"""
+    Yield valid lines of a string or iterable.
+    >>> list(yield_lines(''))
+    []
+    >>> list(yield_lines(['foo', 'bar']))
+    ['foo', 'bar']
+    >>> list(yield_lines('foo\nbar'))
+    ['foo', 'bar']
+    >>> list(yield_lines('\nfoo\n#bar\nbaz #comment'))
+    ['foo', 'baz #comment']
+    >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n']))
+    ['foo', 'bar', 'baz', 'bing']
+    """
+    return itertools.chain.from_iterable(map(yield_lines, iterable))
+
+
+@yield_lines.register(str)
+def _(text: str) -> Iterator[str]:
+    return filter(_nonblank, map(str.strip, text.splitlines()))
+
+
+def split_sections(
+    s: str | Iterator[str],
+) -> Generator[tuple[str | None, list[str]], None, None]:
+    """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: list[str] = []
+    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 safe_extra(extra: str) -> str:
+    """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 safe_name(name: str) -> str:
+    """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 requires_to_requires_dist(requirement: Requirement) -> str:
+    """Return the version specifier for a requirement in PEP 345/566 fashion."""
+    if requirement.url:
+        return " @ " + requirement.url
+
+    requires_dist: list[str] = []
+    for spec in requirement.specifier:
+        requires_dist.append(spec.operator + spec.version)
+
+    if requires_dist:
+        return " " + ",".join(sorted(requires_dist))
+    else:
+        return ""
+
+
+def convert_requirements(requirements: list[str]) -> Iterator[str]:
+    """Yield Requires-Dist: strings for parsed requirements strings."""
+    for req in requirements:
+        parsed_requirement = Requirement(req)
+        spec = requires_to_requires_dist(parsed_requirement)
+        extras = ",".join(sorted(safe_extra(e) for e in parsed_requirement.extras))
+        if extras:
+            extras = f"[{extras}]"
+
+        yield safe_name(parsed_requirement.name) + extras + spec
+
+
+def generate_requirements(
+    extras_require: dict[str | None, list[str]],
+) -> Iterator[tuple[str, str]]:
+    """
+    Convert requirements from a setup()-style dictionary to
+    ('Requires-Dist', 'requirement') and ('Provides-Extra', 'extra') tuples.
+
+    extras_require is a dictionary of {extra: [requirements]} as passed to setup(),
+    using the empty extra {'': [requirements]} to hold install_requires.
+    """
+    for extra, depends in extras_require.items():
+        condition = ""
+        extra = extra or ""
+        if ":" in extra:  # setuptools extra:condition syntax
+            extra, condition = extra.split(":", 1)
+
+        extra = safe_extra(extra)
+        if extra:
+            yield "Provides-Extra", extra
+            if condition:
+                condition = "(" + condition + ") and "
+            condition += f"extra == '{extra}'"
+
+        if condition:
+            condition = " ; " + condition
+
+        for new_req in convert_requirements(depends):
+            canonical_req = str(Requirement(new_req + condition))
+            yield "Requires-Dist", canonical_req
+
+
+def pkginfo_to_metadata(egg_info_path: str, pkginfo_path: str) -> Message:
+    """
+    Convert .egg-info directory with PKG-INFO to the Metadata 2.1 format
+    """
+    with open(pkginfo_path, encoding="utf-8") as headers:
+        pkg_info = Parser().parse(headers)
+
+    pkg_info.replace_header("Metadata-Version", "2.1")
+    # Those will be regenerated from `requires.txt`.
+    del pkg_info["Provides-Extra"]
+    del pkg_info["Requires-Dist"]
+    requires_path = os.path.join(egg_info_path, "requires.txt")
+    if os.path.exists(requires_path):
+        with open(requires_path, encoding="utf-8") as requires_file:
+            requires = requires_file.read()
+
+        parsed_requirements = sorted(split_sections(requires), key=lambda x: x[0] or "")
+        for extra, reqs in parsed_requirements:
+            for key, value in generate_requirements({extra: reqs}):
+                if (key, value) not in pkg_info.items():
+                    pkg_info[key] = value
+
+    description = pkg_info["Description"]
+    if description:
+        description_lines = pkg_info["Description"].splitlines()
+        dedented_description = "\n".join(
+            # if the first line of long_description is blank,
+            # the first line here will be indented.
+            (
+                description_lines[0].lstrip(),
+                textwrap.dedent("\n".join(description_lines[1:])),
+                "\n",
+            )
+        )
+        pkg_info.set_payload(dedented_description)
+        del pkg_info["Description"]
+
+    return pkg_info
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_setuptools_logging.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_setuptools_logging.py
new file mode 100644
index 0000000..a1a2482
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/_setuptools_logging.py
@@ -0,0 +1,26 @@
+# copied from setuptools.logging, omitting monkeypatching
+from __future__ import annotations
+
+import logging
+import sys
+
+
+def _not_warning(record: logging.LogRecord) -> bool:
+    return record.levelno < logging.WARNING
+
+
+def configure() -> None:
+    """
+    Configure logging to emit warning and above to stderr
+    and everything else to stdout. This behavior is provided
+    for compatibility with distutils.log but may change in
+    the future.
+    """
+    err_handler = logging.StreamHandler()
+    err_handler.setLevel(logging.WARNING)
+    out_handler = logging.StreamHandler(sys.stdout)
+    out_handler.addFilter(_not_warning)
+    handlers = err_handler, out_handler
+    logging.basicConfig(
+        format="{message}", style="{", handlers=handlers, level=logging.DEBUG
+    )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/bdist_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/bdist_wheel.py
new file mode 100644
index 0000000..24199c2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/bdist_wheel.py
@@ -0,0 +1,26 @@
+from typing import TYPE_CHECKING
+from warnings import warn
+
+warn(
+    "The 'wheel' package is no longer the canonical location of the 'bdist_wheel' "
+    "command, and will be removed in a future release. Please update to setuptools "
+    "v70.1 or later which contains an integrated version of this command.",
+    FutureWarning,
+    stacklevel=1,
+)
+
+if TYPE_CHECKING:
+    from ._bdist_wheel import bdist_wheel as bdist_wheel
+else:
+    try:
+        # Better integration/compatibility with setuptools:
+        # in the case new fixes or PEPs are implemented in setuptools
+        # there is no need to backport them to the deprecated code base.
+        # This is useful in the case of old packages in the ecosystem
+        # that are still used but have low maintenance.
+        from setuptools.command.bdist_wheel import bdist_wheel
+    except ImportError:
+        # Only used in the case of old setuptools versions.
+        # If the user wants to get the latest fixes/PEPs,
+        # they are encouraged to address the deprecation warning.
+        from ._bdist_wheel import bdist_wheel as bdist_wheel
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/macosx_libfile.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/macosx_libfile.py
new file mode 100644
index 0000000..06e51af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/macosx_libfile.py
@@ -0,0 +1,486 @@
+"""
+IMPORTANT: DO NOT IMPORT THIS MODULE DIRECTLY.
+THIS IS ONLY KEPT IN PLACE FOR BACKWARDS COMPATIBILITY WITH
+setuptools.command.bdist_wheel.
+
+This module contains function to analyse dynamic library
+headers to extract system information
+
+Currently only for MacOSX
+
+Library file on macosx system starts with Mach-O or Fat field.
+This can be distinguish by first 32 bites and it is called magic number.
+Proper value of magic number is with suffix _MAGIC. Suffix _CIGAM means
+reversed bytes order.
+Both fields can occur in two types: 32 and 64 bytes.
+
+FAT field inform that this library contains few version of library
+(typically for different types version). It contains
+information where Mach-O headers starts.
+
+Each section started with Mach-O header contains one library
+(So if file starts with this field it contains only one version).
+
+After filed Mach-O there are section fields.
+Each of them starts with two fields:
+cmd - magic number for this command
+cmdsize - total size occupied by this section information.
+
+In this case only sections LC_VERSION_MIN_MACOSX (for macosx 10.13 and earlier)
+and LC_BUILD_VERSION (for macosx 10.14 and newer) are interesting,
+because them contains information about minimal system version.
+
+Important remarks:
+- For fat files this implementation looks for maximum number version.
+  It not check if it is 32 or 64 and do not compare it with currently built package.
+  So it is possible to false report higher version that needed.
+- All structures signatures are taken form macosx header files.
+- I think that binary format will be more stable than `otool` output.
+  and if apple introduce some changes both implementation will need to be updated.
+- The system compile will set the deployment target no lower than
+  11.0 for arm64 builds. For "Universal 2" builds use the x86_64 deployment
+  target when the arm64 target is 11.0.
+"""
+
+from __future__ import annotations
+
+import ctypes
+import os
+import sys
+from io import BufferedIOBase
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from typing import Union
+
+    StrPath = Union[str, os.PathLike[str]]
+
+"""here the needed const and struct from mach-o header files"""
+
+FAT_MAGIC = 0xCAFEBABE
+FAT_CIGAM = 0xBEBAFECA
+FAT_MAGIC_64 = 0xCAFEBABF
+FAT_CIGAM_64 = 0xBFBAFECA
+MH_MAGIC = 0xFEEDFACE
+MH_CIGAM = 0xCEFAEDFE
+MH_MAGIC_64 = 0xFEEDFACF
+MH_CIGAM_64 = 0xCFFAEDFE
+
+LC_VERSION_MIN_MACOSX = 0x24
+LC_BUILD_VERSION = 0x32
+
+CPU_TYPE_ARM64 = 0x0100000C
+
+mach_header_fields = [
+    ("magic", ctypes.c_uint32),
+    ("cputype", ctypes.c_int),
+    ("cpusubtype", ctypes.c_int),
+    ("filetype", ctypes.c_uint32),
+    ("ncmds", ctypes.c_uint32),
+    ("sizeofcmds", ctypes.c_uint32),
+    ("flags", ctypes.c_uint32),
+]
+"""
+struct mach_header {
+    uint32_t	magic;		/* mach magic number identifier */
+    cpu_type_t	cputype;	/* cpu specifier */
+    cpu_subtype_t	cpusubtype;	/* machine specifier */
+    uint32_t	filetype;	/* type of file */
+    uint32_t	ncmds;		/* number of load commands */
+    uint32_t	sizeofcmds;	/* the size of all the load commands */
+    uint32_t	flags;		/* flags */
+};
+typedef integer_t cpu_type_t;
+typedef integer_t cpu_subtype_t;
+"""
+
+mach_header_fields_64 = mach_header_fields + [("reserved", ctypes.c_uint32)]
+"""
+struct mach_header_64 {
+    uint32_t	magic;		/* mach magic number identifier */
+    cpu_type_t	cputype;	/* cpu specifier */
+    cpu_subtype_t	cpusubtype;	/* machine specifier */
+    uint32_t	filetype;	/* type of file */
+    uint32_t	ncmds;		/* number of load commands */
+    uint32_t	sizeofcmds;	/* the size of all the load commands */
+    uint32_t	flags;		/* flags */
+    uint32_t	reserved;	/* reserved */
+};
+"""
+
+fat_header_fields = [("magic", ctypes.c_uint32), ("nfat_arch", ctypes.c_uint32)]
+"""
+struct fat_header {
+    uint32_t	magic;		/* FAT_MAGIC or FAT_MAGIC_64 */
+    uint32_t	nfat_arch;	/* number of structs that follow */
+};
+"""
+
+fat_arch_fields = [
+    ("cputype", ctypes.c_int),
+    ("cpusubtype", ctypes.c_int),
+    ("offset", ctypes.c_uint32),
+    ("size", ctypes.c_uint32),
+    ("align", ctypes.c_uint32),
+]
+"""
+struct fat_arch {
+    cpu_type_t	cputype;	/* cpu specifier (int) */
+    cpu_subtype_t	cpusubtype;	/* machine specifier (int) */
+    uint32_t	offset;		/* file offset to this object file */
+    uint32_t	size;		/* size of this object file */
+    uint32_t	align;		/* alignment as a power of 2 */
+};
+"""
+
+fat_arch_64_fields = [
+    ("cputype", ctypes.c_int),
+    ("cpusubtype", ctypes.c_int),
+    ("offset", ctypes.c_uint64),
+    ("size", ctypes.c_uint64),
+    ("align", ctypes.c_uint32),
+    ("reserved", ctypes.c_uint32),
+]
+"""
+struct fat_arch_64 {
+    cpu_type_t	cputype;	/* cpu specifier (int) */
+    cpu_subtype_t	cpusubtype;	/* machine specifier (int) */
+    uint64_t	offset;		/* file offset to this object file */
+    uint64_t	size;		/* size of this object file */
+    uint32_t	align;		/* alignment as a power of 2 */
+    uint32_t	reserved;	/* reserved */
+};
+"""
+
+segment_base_fields = [("cmd", ctypes.c_uint32), ("cmdsize", ctypes.c_uint32)]
+"""base for reading segment info"""
+
+segment_command_fields = [
+    ("cmd", ctypes.c_uint32),
+    ("cmdsize", ctypes.c_uint32),
+    ("segname", ctypes.c_char * 16),
+    ("vmaddr", ctypes.c_uint32),
+    ("vmsize", ctypes.c_uint32),
+    ("fileoff", ctypes.c_uint32),
+    ("filesize", ctypes.c_uint32),
+    ("maxprot", ctypes.c_int),
+    ("initprot", ctypes.c_int),
+    ("nsects", ctypes.c_uint32),
+    ("flags", ctypes.c_uint32),
+]
+"""
+struct segment_command { /* for 32-bit architectures */
+    uint32_t	cmd;		/* LC_SEGMENT */
+    uint32_t	cmdsize;	/* includes sizeof section structs */
+    char		segname[16];	/* segment name */
+    uint32_t	vmaddr;		/* memory address of this segment */
+    uint32_t	vmsize;		/* memory size of this segment */
+    uint32_t	fileoff;	/* file offset of this segment */
+    uint32_t	filesize;	/* amount to map from the file */
+    vm_prot_t	maxprot;	/* maximum VM protection */
+    vm_prot_t	initprot;	/* initial VM protection */
+    uint32_t	nsects;		/* number of sections in segment */
+    uint32_t	flags;		/* flags */
+};
+typedef int vm_prot_t;
+"""
+
+segment_command_fields_64 = [
+    ("cmd", ctypes.c_uint32),
+    ("cmdsize", ctypes.c_uint32),
+    ("segname", ctypes.c_char * 16),
+    ("vmaddr", ctypes.c_uint64),
+    ("vmsize", ctypes.c_uint64),
+    ("fileoff", ctypes.c_uint64),
+    ("filesize", ctypes.c_uint64),
+    ("maxprot", ctypes.c_int),
+    ("initprot", ctypes.c_int),
+    ("nsects", ctypes.c_uint32),
+    ("flags", ctypes.c_uint32),
+]
+"""
+struct segment_command_64 { /* for 64-bit architectures */
+    uint32_t	cmd;		/* LC_SEGMENT_64 */
+    uint32_t	cmdsize;	/* includes sizeof section_64 structs */
+    char		segname[16];	/* segment name */
+    uint64_t	vmaddr;		/* memory address of this segment */
+    uint64_t	vmsize;		/* memory size of this segment */
+    uint64_t	fileoff;	/* file offset of this segment */
+    uint64_t	filesize;	/* amount to map from the file */
+    vm_prot_t	maxprot;	/* maximum VM protection */
+    vm_prot_t	initprot;	/* initial VM protection */
+    uint32_t	nsects;		/* number of sections in segment */
+    uint32_t	flags;		/* flags */
+};
+"""
+
+version_min_command_fields = segment_base_fields + [
+    ("version", ctypes.c_uint32),
+    ("sdk", ctypes.c_uint32),
+]
+"""
+struct version_min_command {
+    uint32_t	cmd;		/* LC_VERSION_MIN_MACOSX or
+                               LC_VERSION_MIN_IPHONEOS or
+                               LC_VERSION_MIN_WATCHOS or
+                               LC_VERSION_MIN_TVOS */
+    uint32_t	cmdsize;	/* sizeof(struct min_version_command) */
+    uint32_t	version;	/* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+    uint32_t	sdk;		/* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+};
+"""
+
+build_version_command_fields = segment_base_fields + [
+    ("platform", ctypes.c_uint32),
+    ("minos", ctypes.c_uint32),
+    ("sdk", ctypes.c_uint32),
+    ("ntools", ctypes.c_uint32),
+]
+"""
+struct build_version_command {
+    uint32_t	cmd;		/* LC_BUILD_VERSION */
+    uint32_t	cmdsize;	/* sizeof(struct build_version_command) plus */
+                                /* ntools * sizeof(struct build_tool_version) */
+    uint32_t	platform;	/* platform */
+    uint32_t	minos;		/* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+    uint32_t	sdk;		/* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+    uint32_t	ntools;		/* number of tool entries following this */
+};
+"""
+
+
+def swap32(x: int) -> int:
+    return (
+        ((x << 24) & 0xFF000000)
+        | ((x << 8) & 0x00FF0000)
+        | ((x >> 8) & 0x0000FF00)
+        | ((x >> 24) & 0x000000FF)
+    )
+
+
+def get_base_class_and_magic_number(
+    lib_file: BufferedIOBase,
+    seek: int | None = None,
+) -> tuple[type[ctypes.Structure], int]:
+    if seek is None:
+        seek = lib_file.tell()
+    else:
+        lib_file.seek(seek)
+    magic_number = ctypes.c_uint32.from_buffer_copy(
+        lib_file.read(ctypes.sizeof(ctypes.c_uint32))
+    ).value
+
+    # Handle wrong byte order
+    if magic_number in [FAT_CIGAM, FAT_CIGAM_64, MH_CIGAM, MH_CIGAM_64]:
+        if sys.byteorder == "little":
+            BaseClass = ctypes.BigEndianStructure
+        else:
+            BaseClass = ctypes.LittleEndianStructure
+
+        magic_number = swap32(magic_number)
+    else:
+        BaseClass = ctypes.Structure
+
+    lib_file.seek(seek)
+    return BaseClass, magic_number
+
+
+def read_data(struct_class: type[ctypes.Structure], lib_file: BufferedIOBase):
+    return struct_class.from_buffer_copy(lib_file.read(ctypes.sizeof(struct_class)))
+
+
+def extract_macosx_min_system_version(path_to_lib: str):
+    with open(path_to_lib, "rb") as lib_file:
+        BaseClass, magic_number = get_base_class_and_magic_number(lib_file, 0)
+        if magic_number not in [FAT_MAGIC, FAT_MAGIC_64, MH_MAGIC, MH_MAGIC_64]:
+            return
+
+        if magic_number in [FAT_MAGIC, FAT_CIGAM_64]:
+
+            class FatHeader(BaseClass):
+                _fields_ = fat_header_fields
+
+            fat_header = read_data(FatHeader, lib_file)
+            if magic_number == FAT_MAGIC:
+
+                class FatArch(BaseClass):
+                    _fields_ = fat_arch_fields
+
+            else:
+
+                class FatArch(BaseClass):
+                    _fields_ = fat_arch_64_fields
+
+            fat_arch_list = [
+                read_data(FatArch, lib_file) for _ in range(fat_header.nfat_arch)
+            ]
+
+            versions_list: list[tuple[int, int, int]] = []
+            for el in fat_arch_list:
+                try:
+                    version = read_mach_header(lib_file, el.offset)
+                    if version is not None:
+                        if el.cputype == CPU_TYPE_ARM64 and len(fat_arch_list) != 1:
+                            # Xcode will not set the deployment target below 11.0.0
+                            # for the arm64 architecture. Ignore the arm64 deployment
+                            # in fat binaries when the target is 11.0.0, that way
+                            # the other architectures can select a lower deployment
+                            # target.
+                            # This is safe because there is no arm64 variant for
+                            # macOS 10.15 or earlier.
+                            if version == (11, 0, 0):
+                                continue
+                        versions_list.append(version)
+                except ValueError:
+                    pass
+
+            if len(versions_list) > 0:
+                return max(versions_list)
+            else:
+                return None
+
+        else:
+            try:
+                return read_mach_header(lib_file, 0)
+            except ValueError:
+                """when some error during read library files"""
+                return None
+
+
+def read_mach_header(
+    lib_file: BufferedIOBase,
+    seek: int | None = None,
+) -> tuple[int, int, int] | None:
+    """
+    This function parses a Mach-O header and extracts
+    information about the minimal macOS version.
+
+    :param lib_file: reference to opened library file with pointer
+    """
+    base_class, magic_number = get_base_class_and_magic_number(lib_file, seek)
+    arch = "32" if magic_number == MH_MAGIC else "64"
+
+    class SegmentBase(base_class):
+        _fields_ = segment_base_fields
+
+    if arch == "32":
+
+        class MachHeader(base_class):
+            _fields_ = mach_header_fields
+
+    else:
+
+        class MachHeader(base_class):
+            _fields_ = mach_header_fields_64
+
+    mach_header = read_data(MachHeader, lib_file)
+    for _i in range(mach_header.ncmds):
+        pos = lib_file.tell()
+        segment_base = read_data(SegmentBase, lib_file)
+        lib_file.seek(pos)
+        if segment_base.cmd == LC_VERSION_MIN_MACOSX:
+
+            class VersionMinCommand(base_class):
+                _fields_ = version_min_command_fields
+
+            version_info = read_data(VersionMinCommand, lib_file)
+            return parse_version(version_info.version)
+        elif segment_base.cmd == LC_BUILD_VERSION:
+
+            class VersionBuild(base_class):
+                _fields_ = build_version_command_fields
+
+            version_info = read_data(VersionBuild, lib_file)
+            return parse_version(version_info.minos)
+        else:
+            lib_file.seek(pos + segment_base.cmdsize)
+            continue
+
+
+def parse_version(version: int) -> tuple[int, int, int]:
+    x = (version & 0xFFFF0000) >> 16
+    y = (version & 0x0000FF00) >> 8
+    z = version & 0x000000FF
+    return x, y, z
+
+
+def calculate_macosx_platform_tag(archive_root: StrPath, platform_tag: str) -> str:
+    """
+    Calculate proper macosx platform tag basing on files which are included to wheel
+
+    Example platform tag `macosx-10.14-x86_64`
+    """
+    prefix, base_version, suffix = platform_tag.split("-")
+    base_version = tuple(int(x) for x in base_version.split("."))
+    base_version = base_version[:2]
+    if base_version[0] > 10:
+        base_version = (base_version[0], 0)
+    assert len(base_version) == 2
+    if "MACOSX_DEPLOYMENT_TARGET" in os.environ:
+        deploy_target = tuple(
+            int(x) for x in os.environ["MACOSX_DEPLOYMENT_TARGET"].split(".")
+        )
+        deploy_target = deploy_target[:2]
+        if deploy_target[0] > 10:
+            deploy_target = (deploy_target[0], 0)
+        if deploy_target < base_version:
+            sys.stderr.write(
+                "[WARNING] MACOSX_DEPLOYMENT_TARGET is set to a lower value ({}) than "
+                "the version on which the Python interpreter was compiled ({}), and "
+                "will be ignored.\n".format(
+                    ".".join(str(x) for x in deploy_target),
+                    ".".join(str(x) for x in base_version),
+                )
+            )
+        else:
+            base_version = deploy_target
+
+    assert len(base_version) == 2
+    start_version = base_version
+    versions_dict: dict[str, tuple[int, int]] = {}
+    for dirpath, _dirnames, filenames in os.walk(archive_root):
+        for filename in filenames:
+            if filename.endswith(".dylib") or filename.endswith(".so"):
+                lib_path = os.path.join(dirpath, filename)
+                min_ver = extract_macosx_min_system_version(lib_path)
+                if min_ver is not None:
+                    min_ver = min_ver[0:2]
+                    if min_ver[0] > 10:
+                        min_ver = (min_ver[0], 0)
+                    versions_dict[lib_path] = min_ver
+
+    if len(versions_dict) > 0:
+        base_version = max(base_version, max(versions_dict.values()))
+
+    # macosx platform tag do not support minor bugfix release
+    fin_base_version = "_".join([str(x) for x in base_version])
+    if start_version < base_version:
+        problematic_files = [k for k, v in versions_dict.items() if v > start_version]
+        problematic_files = "\n".join(problematic_files)
+        if len(problematic_files) == 1:
+            files_form = "this file"
+        else:
+            files_form = "these files"
+        error_message = (
+            "[WARNING] This wheel needs a higher macOS version than {}  "
+            "To silence this warning, set MACOSX_DEPLOYMENT_TARGET to at least "
+            + fin_base_version
+            + " or recreate "
+            + files_form
+            + " with lower "
+            "MACOSX_DEPLOYMENT_TARGET:  \n" + problematic_files
+        )
+
+        if "MACOSX_DEPLOYMENT_TARGET" in os.environ:
+            error_message = error_message.format(
+                "is set in MACOSX_DEPLOYMENT_TARGET variable."
+            )
+        else:
+            error_message = error_message.format(
+                "the version your Python interpreter is compiled against."
+            )
+
+        sys.stderr.write(error_message)
+
+    platform_tag = prefix + "_" + fin_base_version + "_" + suffix
+    return platform_tag
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/metadata.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/metadata.py
new file mode 100644
index 0000000..e27900a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/metadata.py
@@ -0,0 +1,17 @@
+from warnings import warn
+
+from ._metadata import convert_requirements as convert_requirements
+from ._metadata import generate_requirements as generate_requirements
+from ._metadata import pkginfo_to_metadata as pkginfo_to_metadata
+from ._metadata import requires_to_requires_dist as requires_to_requires_dist
+from ._metadata import safe_extra as safe_extra
+from ._metadata import safe_name as safe_name
+from ._metadata import split_sections as split_sections
+
+warn(
+    f"The {__name__!r} package has been made private and should no longer be imported. "
+    f"Please either copy the code or find an alternative library to import it from, as "
+    f"this warning will be removed in a future version of 'wheel'.",
+    DeprecationWarning,
+    stacklevel=2,
+)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/wheelfile.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/wheelfile.py
new file mode 100644
index 0000000..7b6fd71
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/wheelfile.py
@@ -0,0 +1,241 @@
+from __future__ import annotations
+
+__all__ = ["WHEEL_INFO_RE", "WheelFile", "WheelError"]
+
+import base64
+import csv
+import hashlib
+import logging
+import os.path
+import re
+import stat
+import time
+from io import StringIO, TextIOWrapper
+from typing import IO, TYPE_CHECKING, Literal
+from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
+
+if TYPE_CHECKING:
+    from _typeshed import SizedBuffer, StrPath
+
+
+# Non-greedy matching of an optional build number may be too clever (more
+# invalid wheel filenames will match). Separate regex for .dist-info?
+WHEEL_INFO_RE = re.compile(
+    r"""^(?P(?P[^\s-]+?)-(?P[^\s-]+?))(-(?P\d[^\s-]*))?
+     -(?P[^\s-]+?)-(?P[^\s-]+?)-(?P\S+)\.whl$""",
+    re.VERBOSE,
+)
+MINIMUM_TIMESTAMP = 315532800  # 1980-01-01 00:00:00 UTC
+
+log = logging.getLogger("wheel")
+
+
+class WheelError(Exception):
+    pass
+
+
+def urlsafe_b64encode(data: bytes) -> bytes:
+    """urlsafe_b64encode without padding"""
+    return base64.urlsafe_b64encode(data).rstrip(b"=")
+
+
+def urlsafe_b64decode(data: bytes) -> bytes:
+    """urlsafe_b64decode without padding"""
+    pad = b"=" * (4 - (len(data) & 3))
+    return base64.urlsafe_b64decode(data + pad)
+
+
+def get_zipinfo_datetime(
+    timestamp: float | None = None,
+) -> tuple[int, int, int, int, int]:
+    # Some applications need reproducible .whl files, but they can't do this without
+    # forcing the timestamp of the individual ZipInfo objects. See issue #143.
+    timestamp = int(os.environ.get("SOURCE_DATE_EPOCH", timestamp or time.time()))
+    timestamp = max(timestamp, MINIMUM_TIMESTAMP)
+    return time.gmtime(timestamp)[0:6]
+
+
+class WheelFile(ZipFile):
+    """A ZipFile derivative class that also reads SHA-256 hashes from
+    .dist-info/RECORD and checks any read files against those.
+    """
+
+    _default_algorithm = hashlib.sha256
+
+    def __init__(
+        self,
+        file: StrPath,
+        mode: Literal["r", "w", "x", "a"] = "r",
+        compression: int = ZIP_DEFLATED,
+    ):
+        basename = os.path.basename(file)
+        self.parsed_filename = WHEEL_INFO_RE.match(basename)
+        if not basename.endswith(".whl") or self.parsed_filename is None:
+            raise WheelError(f"Bad wheel filename {basename!r}")
+
+        ZipFile.__init__(self, file, mode, compression=compression, allowZip64=True)
+
+        self.dist_info_path = "{}.dist-info".format(
+            self.parsed_filename.group("namever")
+        )
+        self.record_path = self.dist_info_path + "/RECORD"
+        self._file_hashes: dict[str, tuple[None, None] | tuple[int, bytes]] = {}
+        self._file_sizes = {}
+        if mode == "r":
+            # Ignore RECORD and any embedded wheel signatures
+            self._file_hashes[self.record_path] = None, None
+            self._file_hashes[self.record_path + ".jws"] = None, None
+            self._file_hashes[self.record_path + ".p7s"] = None, None
+
+            # Fill in the expected hashes by reading them from RECORD
+            try:
+                record = self.open(self.record_path)
+            except KeyError:
+                raise WheelError(f"Missing {self.record_path} file") from None
+
+            with record:
+                for line in csv.reader(
+                    TextIOWrapper(record, newline="", encoding="utf-8")
+                ):
+                    path, hash_sum, size = line
+                    if not hash_sum:
+                        continue
+
+                    algorithm, hash_sum = hash_sum.split("=")
+                    try:
+                        hashlib.new(algorithm)
+                    except ValueError:
+                        raise WheelError(
+                            f"Unsupported hash algorithm: {algorithm}"
+                        ) from None
+
+                    if algorithm.lower() in {"md5", "sha1"}:
+                        raise WheelError(
+                            f"Weak hash algorithm ({algorithm}) is not permitted by "
+                            f"PEP 427"
+                        )
+
+                    self._file_hashes[path] = (
+                        algorithm,
+                        urlsafe_b64decode(hash_sum.encode("ascii")),
+                    )
+
+    def open(
+        self,
+        name_or_info: str | ZipInfo,
+        mode: Literal["r", "w"] = "r",
+        pwd: bytes | None = None,
+    ) -> IO[bytes]:
+        def _update_crc(newdata: bytes) -> None:
+            eof = ef._eof
+            update_crc_orig(newdata)
+            running_hash.update(newdata)
+            if eof and running_hash.digest() != expected_hash:
+                raise WheelError(f"Hash mismatch for file '{ef_name}'")
+
+        ef_name = (
+            name_or_info.filename if isinstance(name_or_info, ZipInfo) else name_or_info
+        )
+        if (
+            mode == "r"
+            and not ef_name.endswith("/")
+            and ef_name not in self._file_hashes
+        ):
+            raise WheelError(f"No hash found for file '{ef_name}'")
+
+        ef = ZipFile.open(self, name_or_info, mode, pwd)
+        if mode == "r" and not ef_name.endswith("/"):
+            algorithm, expected_hash = self._file_hashes[ef_name]
+            if expected_hash is not None:
+                # Monkey patch the _update_crc method to also check for the hash from
+                # RECORD
+                running_hash = hashlib.new(algorithm)
+                update_crc_orig, ef._update_crc = ef._update_crc, _update_crc
+
+        return ef
+
+    def write_files(self, base_dir: str) -> None:
+        log.info("creating %r and adding %r to it", self.filename, base_dir)
+        deferred: list[tuple[str, str]] = []
+        for root, dirnames, filenames in os.walk(base_dir):
+            # Sort the directory names so that `os.walk` will walk them in a
+            # defined order on the next iteration.
+            dirnames.sort()
+            for name in sorted(filenames):
+                path = os.path.normpath(os.path.join(root, name))
+                if os.path.isfile(path):
+                    arcname = os.path.relpath(path, base_dir).replace(os.path.sep, "/")
+                    if arcname == self.record_path:
+                        pass
+                    elif root.endswith(".dist-info"):
+                        deferred.append((path, arcname))
+                    else:
+                        self.write(path, arcname)
+
+        deferred.sort()
+        for path, arcname in deferred:
+            self.write(path, arcname)
+
+    def write(
+        self,
+        filename: str,
+        arcname: str | None = None,
+        compress_type: int | None = None,
+    ) -> None:
+        with open(filename, "rb") as f:
+            st = os.fstat(f.fileno())
+            data = f.read()
+
+        zinfo = ZipInfo(
+            arcname or filename, date_time=get_zipinfo_datetime(st.st_mtime)
+        )
+        zinfo.external_attr = (stat.S_IMODE(st.st_mode) | stat.S_IFMT(st.st_mode)) << 16
+        zinfo.compress_type = compress_type or self.compression
+        self.writestr(zinfo, data, compress_type)
+
+    def writestr(
+        self,
+        zinfo_or_arcname: str | ZipInfo,
+        data: SizedBuffer | str,
+        compress_type: int | None = None,
+    ) -> None:
+        if isinstance(zinfo_or_arcname, str):
+            zinfo_or_arcname = ZipInfo(
+                zinfo_or_arcname, date_time=get_zipinfo_datetime()
+            )
+            zinfo_or_arcname.compress_type = self.compression
+            zinfo_or_arcname.external_attr = (0o664 | stat.S_IFREG) << 16
+
+        if isinstance(data, str):
+            data = data.encode("utf-8")
+
+        ZipFile.writestr(self, zinfo_or_arcname, data, compress_type)
+        fname = (
+            zinfo_or_arcname.filename
+            if isinstance(zinfo_or_arcname, ZipInfo)
+            else zinfo_or_arcname
+        )
+        log.info("adding %r", fname)
+        if fname != self.record_path:
+            hash_ = self._default_algorithm(data)
+            self._file_hashes[fname] = (
+                hash_.name,
+                urlsafe_b64encode(hash_.digest()).decode("ascii"),
+            )
+            self._file_sizes[fname] = len(data)
+
+    def close(self) -> None:
+        # Write RECORD
+        if self.fp is not None and self.mode == "w" and self._file_hashes:
+            data = StringIO()
+            writer = csv.writer(data, delimiter=",", quotechar='"', lineterminator="\n")
+            writer.writerows(
+                (
+                    (fname, algorithm + "=" + hash_, self._file_sizes[fname])
+                    for fname, (algorithm, hash_) in self._file_hashes.items()
+                )
+            )
+            writer.writerow((format(self.record_path), "", ""))
+            self.writestr(self.record_path, data.getvalue())
+
+        ZipFile.close(self)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/INSTALLER
new file mode 100644
index 0000000..5c69047
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/METADATA
new file mode 100644
index 0000000..6420117
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/METADATA
@@ -0,0 +1,106 @@
+Metadata-Version: 2.4
+Name: zipp
+Version: 3.23.0
+Summary: Backport of pathlib-compatible object wrapper for zip files
+Author-email: "Jason R. Coombs" 
+License-Expression: MIT
+Project-URL: Source, https://github.com/jaraco/zipp
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: jaraco.itertools; extra == "test"
+Requires-Dist: jaraco.functools; extra == "test"
+Requires-Dist: more_itertools; extra == "test"
+Requires-Dist: big-O; extra == "test"
+Requires-Dist: pytest-ignore-flaky; extra == "test"
+Requires-Dist: jaraco.test; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
+Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
+Provides-Extra: cover
+Requires-Dist: pytest-cov; extra == "cover"
+Provides-Extra: enabler
+Requires-Dist: pytest-enabler>=2.2; extra == "enabler"
+Provides-Extra: type
+Requires-Dist: pytest-mypy; extra == "type"
+Dynamic: license-file
+
+.. image:: https://img.shields.io/pypi/v/zipp.svg
+   :target: https://pypi.org/project/zipp
+
+.. image:: https://img.shields.io/pypi/pyversions/zipp.svg
+
+.. image:: https://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. image:: https://readthedocs.org/projects/zipp/badge/?version=latest
+..    :target: https://zipp.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2025-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/zipp
+   :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme
+
+
+A pathlib-compatible Zipfile object wrapper. Official backport of the standard library
+`Path object `_.
+
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+   :header-rows: 1
+
+   * - zipp
+     - stdlib
+   * - 3.18
+     - 3.13
+   * - 3.16
+     - 3.12
+   * - 3.5
+     - 3.11
+   * - 3.2
+     - 3.10
+   * - 3.3 ??
+     - 3.9
+   * - 1.0
+     - 3.8
+
+
+Usage
+=====
+
+Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more `_.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/RECORD
new file mode 100644
index 0000000..167e234
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/RECORD
@@ -0,0 +1,14 @@
+zipp-3.23.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+zipp-3.23.0.dist-info/METADATA,sha256=vdZ9TRbPC_O4k-fRjNPS13StuC837Zhbx3cMYHIms1s,3563
+zipp-3.23.0.dist-info/RECORD,,
+zipp-3.23.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+zipp-3.23.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+zipp-3.23.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076
+zipp-3.23.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5
+zipp/__init__.py,sha256=ieXh9GIMdABjKRX_JUJtP9k5wdBLK4Mt5X4nszSkmYE,11976
+zipp/_functools.py,sha256=f6Kt9LxZ4TE-cY1lJVdXSId3memSXmH9IdgMbU-_x2k,575
+zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+zipp/compat/overlay.py,sha256=oEIGAnbr8yGjuKTrVSO2ByewPui71uppbX18BLnYTKE,783
+zipp/compat/py310.py,sha256=S7i6N9mToEn3asNb2ILyjnzvITOXrATD_J4emjyBbDU,256
+zipp/compat/py313.py,sha256=RndvDNtuY7H2D9ecnnzcPBMZ8mZc42gmXD_IwQAXXAE,654
+zipp/glob.py,sha256=DLV9LBsDxA6YVW82e3-tkoNrus1h4R-j3BR6VqS0AzE,3382
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/WHEEL
new file mode 100644
index 0000000..e7fa31b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.9.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000..f60bd57
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE
@@ -0,0 +1,18 @@
+MIT License
+
+Copyright (c) 2025 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/top_level.txt
new file mode 100644
index 0000000..e82f676
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+zipp
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/__init__.py
new file mode 100644
index 0000000..ed5b214
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/__init__.py
@@ -0,0 +1,456 @@
+"""
+A Path-like interface for zipfiles.
+
+This codebase is shared between zipfile.Path in the stdlib
+and zipp in PyPI. See
+https://github.com/python/importlib_metadata/wiki/Development-Methodology
+for more detail.
+"""
+
+import functools
+import io
+import itertools
+import pathlib
+import posixpath
+import re
+import stat
+import sys
+import zipfile
+
+from ._functools import save_method_args
+from .compat.py310 import text_encoding
+from .glob import Translator
+
+__all__ = ['Path']
+
+
+def _parents(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all parents of that path.
+
+    >>> list(_parents('b/d'))
+    ['b']
+    >>> list(_parents('/b/d/'))
+    ['/b']
+    >>> list(_parents('b/d/f/'))
+    ['b/d', 'b']
+    >>> list(_parents('b'))
+    []
+    >>> list(_parents(''))
+    []
+    """
+    return itertools.islice(_ancestry(path), 1, None)
+
+
+def _ancestry(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all elements of that path.
+
+    >>> list(_ancestry('b/d'))
+    ['b/d', 'b']
+    >>> list(_ancestry('/b/d/'))
+    ['/b/d', '/b']
+    >>> list(_ancestry('b/d/f/'))
+    ['b/d/f', 'b/d', 'b']
+    >>> list(_ancestry('b'))
+    ['b']
+    >>> list(_ancestry(''))
+    []
+
+    Multiple separators are treated like a single.
+
+    >>> list(_ancestry('//b//d///f//'))
+    ['//b//d///f', '//b//d', '//b']
+    """
+    path = path.rstrip(posixpath.sep)
+    while path.rstrip(posixpath.sep):
+        yield path
+        path, tail = posixpath.split(path)
+
+
+_dedupe = dict.fromkeys
+"""Deduplicate an iterable in original order"""
+
+
+def _difference(minuend, subtrahend):
+    """
+    Return items in minuend not in subtrahend, retaining order
+    with O(1) lookup.
+    """
+    return itertools.filterfalse(set(subtrahend).__contains__, minuend)
+
+
+class InitializedState:
+    """
+    Mix-in to save the initialization state for pickling.
+    """
+
+    @save_method_args
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+    def __getstate__(self):
+        return self._saved___init__.args, self._saved___init__.kwargs
+
+    def __setstate__(self, state):
+        args, kwargs = state
+        super().__init__(*args, **kwargs)
+
+
+class CompleteDirs(InitializedState, zipfile.ZipFile):
+    """
+    A ZipFile subclass that ensures that implied directories
+    are always included in the namelist.
+
+    >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt']))
+    ['foo/', 'foo/bar/']
+    >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/']))
+    ['foo/']
+    """
+
+    @staticmethod
+    def _implied_dirs(names):
+        parents = itertools.chain.from_iterable(map(_parents, names))
+        as_dirs = (p + posixpath.sep for p in parents)
+        return _dedupe(_difference(as_dirs, names))
+
+    def namelist(self):
+        names = super().namelist()
+        return names + list(self._implied_dirs(names))
+
+    def _name_set(self):
+        return set(self.namelist())
+
+    def resolve_dir(self, name):
+        """
+        If the name represents a directory, return that name
+        as a directory (with the trailing slash).
+        """
+        names = self._name_set()
+        dirname = name + '/'
+        dir_match = name not in names and dirname in names
+        return dirname if dir_match else name
+
+    def getinfo(self, name):
+        """
+        Supplement getinfo for implied dirs.
+        """
+        try:
+            return super().getinfo(name)
+        except KeyError:
+            if not name.endswith('/') or name not in self._name_set():
+                raise
+            return zipfile.ZipInfo(filename=name)
+
+    @classmethod
+    def make(cls, source):
+        """
+        Given a source (filename or zipfile), return an
+        appropriate CompleteDirs subclass.
+        """
+        if isinstance(source, CompleteDirs):
+            return source
+
+        if not isinstance(source, zipfile.ZipFile):
+            return cls(source)
+
+        # Only allow for FastLookup when supplied zipfile is read-only
+        if 'r' not in source.mode:
+            cls = CompleteDirs
+
+        source.__class__ = cls
+        return source
+
+    @classmethod
+    def inject(cls, zf: zipfile.ZipFile) -> zipfile.ZipFile:
+        """
+        Given a writable zip file zf, inject directory entries for
+        any directories implied by the presence of children.
+        """
+        for name in cls._implied_dirs(zf.namelist()):
+            zf.writestr(name, b"")
+        return zf
+
+
+class FastLookup(CompleteDirs):
+    """
+    ZipFile subclass to ensure implicit
+    dirs exist and are resolved rapidly.
+    """
+
+    def namelist(self):
+        return self._namelist
+
+    @functools.cached_property
+    def _namelist(self):
+        return super().namelist()
+
+    def _name_set(self):
+        return self._name_set_prop
+
+    @functools.cached_property
+    def _name_set_prop(self):
+        return super()._name_set()
+
+
+def _extract_text_encoding(encoding=None, *args, **kwargs):
+    # compute stack level so that the caller of the caller sees any warning.
+    is_pypy = sys.implementation.name == 'pypy'
+    # PyPy no longer special cased after 7.3.19 (or maybe 7.3.18)
+    # See jaraco/zipp#143
+    is_old_pypi = is_pypy and sys.pypy_version_info < (7, 3, 19)
+    stack_level = 3 + is_old_pypi
+    return text_encoding(encoding, stack_level), args, kwargs
+
+
+class Path:
+    """
+    A :class:`importlib.resources.abc.Traversable` interface for zip files.
+
+    Implements many of the features users enjoy from
+    :class:`pathlib.Path`.
+
+    Consider a zip file with this structure::
+
+        .
+        ├── a.txt
+        └── b
+            ├── c.txt
+            └── d
+                └── e.txt
+
+    >>> data = io.BytesIO()
+    >>> zf = zipfile.ZipFile(data, 'w')
+    >>> zf.writestr('a.txt', 'content of a')
+    >>> zf.writestr('b/c.txt', 'content of c')
+    >>> zf.writestr('b/d/e.txt', 'content of e')
+    >>> zf.filename = 'mem/abcde.zip'
+
+    Path accepts the zipfile object itself or a filename
+
+    >>> path = Path(zf)
+
+    From there, several path operations are available.
+
+    Directory iteration (including the zip file itself):
+
+    >>> a, b = path.iterdir()
+    >>> a
+    Path('mem/abcde.zip', 'a.txt')
+    >>> b
+    Path('mem/abcde.zip', 'b/')
+
+    name property:
+
+    >>> b.name
+    'b'
+
+    join with divide operator:
+
+    >>> c = b / 'c.txt'
+    >>> c
+    Path('mem/abcde.zip', 'b/c.txt')
+    >>> c.name
+    'c.txt'
+
+    Read text:
+
+    >>> c.read_text(encoding='utf-8')
+    'content of c'
+
+    existence:
+
+    >>> c.exists()
+    True
+    >>> (b / 'missing.txt').exists()
+    False
+
+    Coercion to string:
+
+    >>> import os
+    >>> str(c).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip/b/c.txt'
+
+    At the root, ``name``, ``filename``, and ``parent``
+    resolve to the zipfile.
+
+    >>> str(path)
+    'mem/abcde.zip/'
+    >>> path.name
+    'abcde.zip'
+    >>> path.filename == pathlib.Path('mem/abcde.zip')
+    True
+    >>> str(path.parent)
+    'mem'
+
+    If the zipfile has no filename, such attributes are not
+    valid and accessing them will raise an Exception.
+
+    >>> zf.filename = None
+    >>> path.name
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+
+    >>> path.filename
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+
+    >>> path.parent
+    Traceback (most recent call last):
+    ...
+    TypeError: ...
+
+    # workaround python/cpython#106763
+    >>> pass
+    """
+
+    __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
+
+    def __init__(self, root, at=""):
+        """
+        Construct a Path from a ZipFile or filename.
+
+        Note: When the source is an existing ZipFile object,
+        its type (__class__) will be mutated to a
+        specialized type. If the caller wishes to retain the
+        original type, the caller should either create a
+        separate ZipFile object or pass a filename.
+        """
+        self.root = FastLookup.make(root)
+        self.at = at
+
+    def __eq__(self, other):
+        """
+        >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo'
+        False
+        """
+        if self.__class__ is not other.__class__:
+            return NotImplemented
+        return (self.root, self.at) == (other.root, other.at)
+
+    def __hash__(self):
+        return hash((self.root, self.at))
+
+    def open(self, mode='r', *args, pwd=None, **kwargs):
+        """
+        Open this entry as text or binary following the semantics
+        of ``pathlib.Path.open()`` by passing arguments through
+        to io.TextIOWrapper().
+        """
+        if self.is_dir():
+            raise IsADirectoryError(self)
+        zip_mode = mode[0]
+        if zip_mode == 'r' and not self.exists():
+            raise FileNotFoundError(self)
+        stream = self.root.open(self.at, zip_mode, pwd=pwd)
+        if 'b' in mode:
+            if args or kwargs:
+                raise ValueError("encoding args invalid for binary operation")
+            return stream
+        # Text mode:
+        encoding, args, kwargs = _extract_text_encoding(*args, **kwargs)
+        return io.TextIOWrapper(stream, encoding, *args, **kwargs)
+
+    def _base(self):
+        return pathlib.PurePosixPath(self.at) if self.at else self.filename
+
+    @property
+    def name(self):
+        return self._base().name
+
+    @property
+    def suffix(self):
+        return self._base().suffix
+
+    @property
+    def suffixes(self):
+        return self._base().suffixes
+
+    @property
+    def stem(self):
+        return self._base().stem
+
+    @property
+    def filename(self):
+        return pathlib.Path(self.root.filename).joinpath(self.at)
+
+    def read_text(self, *args, **kwargs):
+        encoding, args, kwargs = _extract_text_encoding(*args, **kwargs)
+        with self.open('r', encoding, *args, **kwargs) as strm:
+            return strm.read()
+
+    def read_bytes(self):
+        with self.open('rb') as strm:
+            return strm.read()
+
+    def _is_child(self, path):
+        return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
+
+    def _next(self, at):
+        return self.__class__(self.root, at)
+
+    def is_dir(self):
+        return not self.at or self.at.endswith("/")
+
+    def is_file(self):
+        return self.exists() and not self.is_dir()
+
+    def exists(self):
+        return self.at in self.root._name_set()
+
+    def iterdir(self):
+        if not self.is_dir():
+            raise ValueError("Can't listdir a file")
+        subs = map(self._next, self.root.namelist())
+        return filter(self._is_child, subs)
+
+    def match(self, path_pattern):
+        return pathlib.PurePosixPath(self.at).match(path_pattern)
+
+    def is_symlink(self):
+        """
+        Return whether this path is a symlink.
+        """
+        info = self.root.getinfo(self.at)
+        mode = info.external_attr >> 16
+        return stat.S_ISLNK(mode)
+
+    def glob(self, pattern):
+        if not pattern:
+            raise ValueError(f"Unacceptable pattern: {pattern!r}")
+
+        prefix = re.escape(self.at)
+        tr = Translator(seps='/')
+        matches = re.compile(prefix + tr.translate(pattern)).fullmatch
+        return map(self._next, filter(matches, self.root.namelist()))
+
+    def rglob(self, pattern):
+        return self.glob(f'**/{pattern}')
+
+    def relative_to(self, other, *extra):
+        return posixpath.relpath(str(self), str(other.joinpath(*extra)))
+
+    def __str__(self):
+        return posixpath.join(self.root.filename, self.at)
+
+    def __repr__(self):
+        return self.__repr.format(self=self)
+
+    def joinpath(self, *other):
+        next = posixpath.join(self.at, *other)
+        return self._next(self.root.resolve_dir(next))
+
+    __truediv__ = joinpath
+
+    @property
+    def parent(self):
+        if not self.at:
+            return self.filename.parent
+        parent_at = posixpath.dirname(self.at.rstrip('/'))
+        if parent_at:
+            parent_at += '/'
+        return self._next(parent_at)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/_functools.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/_functools.py
new file mode 100644
index 0000000..7390be2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/_functools.py
@@ -0,0 +1,20 @@
+import collections
+import functools
+
+
+# from jaraco.functools 4.0.2
+def save_method_args(method):
+    """
+    Wrap a method such that when it is called, the args and kwargs are
+    saved on the method.
+    """
+    args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs')  # noqa: PYI024
+
+    @functools.wraps(method)
+    def wrapper(self, /, *args, **kwargs):
+        attr_name = '_saved_' + method.__name__
+        attr = args_and_kwargs(args, kwargs)
+        setattr(self, attr_name, attr)
+        return method(self, *args, **kwargs)
+
+    return wrapper
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/overlay.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/overlay.py
new file mode 100644
index 0000000..5a97ee7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/overlay.py
@@ -0,0 +1,37 @@
+"""
+Expose zipp.Path as .zipfile.Path.
+
+Includes everything else in ``zipfile`` to match future usage. Just
+use:
+
+>>> from zipp.compat.overlay import zipfile
+
+in place of ``import zipfile``.
+
+Relative imports are supported too.
+
+>>> from zipp.compat.overlay.zipfile import ZipInfo
+
+The ``zipfile`` object added to ``sys.modules`` needs to be
+hashable (#126).
+
+>>> _ = hash(sys.modules['zipp.compat.overlay.zipfile'])
+"""
+
+import importlib
+import sys
+import types
+
+import zipp
+
+
+class HashableNamespace(types.SimpleNamespace):
+    def __hash__(self):
+        return hash(tuple(vars(self)))
+
+
+zipfile = HashableNamespace(**vars(importlib.import_module('zipfile')))
+zipfile.Path = zipp.Path
+zipfile._path = zipp
+
+sys.modules[__name__ + '.zipfile'] = zipfile  # type: ignore[assignment]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py310.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py310.py
new file mode 100644
index 0000000..e1e7ec2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py310.py
@@ -0,0 +1,13 @@
+import io
+import sys
+
+
+def _text_encoding(encoding, stacklevel=2, /):  # pragma: no cover
+    return encoding
+
+
+text_encoding = (
+    io.text_encoding  # type: ignore[unused-ignore, attr-defined]
+    if sys.version_info > (3, 10)
+    else _text_encoding
+)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py313.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py313.py
new file mode 100644
index 0000000..ae45869
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/compat/py313.py
@@ -0,0 +1,34 @@
+import functools
+import sys
+
+
+# from jaraco.functools 4.1
+def identity(x):
+    return x
+
+
+# from jaraco.functools 4.1
+def apply(transform):
+    def wrap(func):
+        return functools.wraps(func)(compose(transform, func))
+
+    return wrap
+
+
+# from jaraco.functools 4.1
+def compose(*funcs):
+    def compose_two(f1, f2):
+        return lambda *args, **kwargs: f1(f2(*args, **kwargs))
+
+    return functools.reduce(compose_two, funcs)
+
+
+def replace(pattern):
+    r"""
+    >>> replace(r'foo\z')
+    'foo\\Z'
+    """
+    return pattern[:-2] + pattern[-2:].replace(r'\z', r'\Z')
+
+
+legacy_end_marker = apply(replace) if sys.version_info < (3, 14) else identity
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/glob.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/glob.py
new file mode 100644
index 0000000..1b4ffb3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/zipp/glob.py
@@ -0,0 +1,116 @@
+import os
+import re
+
+from .compat.py313 import legacy_end_marker
+
+_default_seps = os.sep + str(os.altsep) * bool(os.altsep)
+
+
+class Translator:
+    """
+    >>> Translator('xyz')
+    Traceback (most recent call last):
+    ...
+    AssertionError: Invalid separators
+
+    >>> Translator('')
+    Traceback (most recent call last):
+    ...
+    AssertionError: Invalid separators
+    """
+
+    seps: str
+
+    def __init__(self, seps: str = _default_seps):
+        assert seps and set(seps) <= set(_default_seps), "Invalid separators"
+        self.seps = seps
+
+    def translate(self, pattern):
+        """
+        Given a glob pattern, produce a regex that matches it.
+        """
+        return self.extend(self.match_dirs(self.translate_core(pattern)))
+
+    @legacy_end_marker
+    def extend(self, pattern):
+        r"""
+        Extend regex for pattern-wide concerns.
+
+        Apply '(?s:)' to create a non-matching group that
+        matches newlines (valid on Unix).
+
+        Append '\z' to imply fullmatch even when match is used.
+        """
+        return rf'(?s:{pattern})\z'
+
+    def match_dirs(self, pattern):
+        """
+        Ensure that zipfile.Path directory names are matched.
+
+        zipfile.Path directory names always end in a slash.
+        """
+        return rf'{pattern}[/]?'
+
+    def translate_core(self, pattern):
+        r"""
+        Given a glob pattern, produce a regex that matches it.
+
+        >>> t = Translator()
+        >>> t.translate_core('*.txt').replace('\\\\', '')
+        '[^/]*\\.txt'
+        >>> t.translate_core('a?txt')
+        'a[^/]txt'
+        >>> t.translate_core('**/*').replace('\\\\', '')
+        '.*/[^/][^/]*'
+        """
+        self.restrict_rglob(pattern)
+        return ''.join(map(self.replace, separate(self.star_not_empty(pattern))))
+
+    def replace(self, match):
+        """
+        Perform the replacements for a match from :func:`separate`.
+        """
+        return match.group('set') or (
+            re.escape(match.group(0))
+            .replace('\\*\\*', r'.*')
+            .replace('\\*', rf'[^{re.escape(self.seps)}]*')
+            .replace('\\?', r'[^/]')
+        )
+
+    def restrict_rglob(self, pattern):
+        """
+        Raise ValueError if ** appears in anything but a full path segment.
+
+        >>> Translator().translate('**foo')
+        Traceback (most recent call last):
+        ...
+        ValueError: ** must appear alone in a path segment
+        """
+        seps_pattern = rf'[{re.escape(self.seps)}]+'
+        segments = re.split(seps_pattern, pattern)
+        if any('**' in segment and segment != '**' for segment in segments):
+            raise ValueError("** must appear alone in a path segment")
+
+    def star_not_empty(self, pattern):
+        """
+        Ensure that * will not match an empty segment.
+        """
+
+        def handle_segment(match):
+            segment = match.group(0)
+            return '?*' if segment == '*' else segment
+
+        not_seps_pattern = rf'[^{re.escape(self.seps)}]+'
+        return re.sub(not_seps_pattern, handle_segment, pattern)
+
+
+def separate(pattern):
+    """
+    Separate out character sets to avoid translating their contents.
+
+    >>> [m.group(0) for m in separate('*.txt')]
+    ['*.txt']
+    >>> [m.group(0) for m in separate('a[?]txt')]
+    ['a', '[?]', 'txt']
+    """
+    return re.finditer(r'([^\[]+)|(?P[\[].*?[\]])|([\[][^\]]*$)', pattern)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/archive_util.py b/.venv/lib/python3.12/site-packages/setuptools/archive_util.py
new file mode 100644
index 0000000..1a02010
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/archive_util.py
@@ -0,0 +1,219 @@
+"""Utilities for extracting common archive formats"""
+
+import contextlib
+import os
+import posixpath
+import shutil
+import tarfile
+import zipfile
+
+from ._path import ensure_directory
+
+from distutils.errors import DistutilsError
+
+__all__ = [
+    "unpack_archive",
+    "unpack_zipfile",
+    "unpack_tarfile",
+    "default_filter",
+    "UnrecognizedFormat",
+    "extraction_drivers",
+    "unpack_directory",
+]
+
+
+class UnrecognizedFormat(DistutilsError):
+    """Couldn't recognize the archive type"""
+
+
+def default_filter(src, dst):
+    """The default progress/filter callback; returns True for all files"""
+    return dst
+
+
+def unpack_archive(
+    filename, extract_dir, progress_filter=default_filter, drivers=None
+) -> None:
+    """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
+
+    `progress_filter` is a function taking two arguments: a source path
+    internal to the archive ('/'-separated), and a filesystem path where it
+    will be extracted.  The callback must return the desired extract path
+    (which may be the same as the one passed in), or else ``None`` to skip
+    that file or directory.  The callback can thus be used to report on the
+    progress of the extraction, as well as to filter the items extracted or
+    alter their extraction paths.
+
+    `drivers`, if supplied, must be a non-empty sequence of functions with the
+    same signature as this function (minus the `drivers` argument), that raise
+    ``UnrecognizedFormat`` if they do not support extracting the designated
+    archive type.  The `drivers` are tried in sequence until one is found that
+    does not raise an error, or until all are exhausted (in which case
+    ``UnrecognizedFormat`` is raised).  If you do not supply a sequence of
+    drivers, the module's ``extraction_drivers`` constant will be used, which
+    means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
+    order.
+    """
+    for driver in drivers or extraction_drivers:
+        try:
+            driver(filename, extract_dir, progress_filter)
+        except UnrecognizedFormat:
+            continue
+        else:
+            return
+    else:
+        raise UnrecognizedFormat(f"Not a recognized archive type: {filename}")
+
+
+def unpack_directory(filename, extract_dir, progress_filter=default_filter) -> None:
+    """ "Unpack" a directory, using the same interface as for archives
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a directory
+    """
+    if not os.path.isdir(filename):
+        raise UnrecognizedFormat(f"{filename} is not a directory")
+
+    paths = {
+        filename: ('', extract_dir),
+    }
+    for base, dirs, files in os.walk(filename):
+        src, dst = paths[base]
+        for d in dirs:
+            paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d)
+        for f in files:
+            target = os.path.join(dst, f)
+            target = progress_filter(src + f, target)
+            if not target:
+                # skip non-files
+                continue
+            ensure_directory(target)
+            f = os.path.join(base, f)
+            shutil.copyfile(f, target)
+            shutil.copystat(f, target)
+
+
+def unpack_zipfile(filename, extract_dir, progress_filter=default_filter) -> None:
+    """Unpack zip `filename` to `extract_dir`
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
+    by ``zipfile.is_zipfile()``).  See ``unpack_archive()`` for an explanation
+    of the `progress_filter` argument.
+    """
+
+    if not zipfile.is_zipfile(filename):
+        raise UnrecognizedFormat(f"{filename} is not a zip file")
+
+    with zipfile.ZipFile(filename) as z:
+        _unpack_zipfile_obj(z, extract_dir, progress_filter)
+
+
+def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):
+    """Internal/private API used by other parts of setuptools.
+    Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`
+    object instead of a filename.
+    """
+    for info in zipfile_obj.infolist():
+        name = info.filename
+
+        # don't extract absolute paths or ones with .. in them
+        if name.startswith('/') or '..' in name.split('/'):
+            continue
+
+        target = os.path.join(extract_dir, *name.split('/'))
+        target = progress_filter(name, target)
+        if not target:
+            continue
+        if name.endswith('/'):
+            # directory
+            ensure_directory(target)
+        else:
+            # file
+            ensure_directory(target)
+            data = zipfile_obj.read(info.filename)
+            with open(target, 'wb') as f:
+                f.write(data)
+        unix_attributes = info.external_attr >> 16
+        if unix_attributes:
+            os.chmod(target, unix_attributes)
+
+
+def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
+    """Resolve any links and extract link targets as normal files."""
+    while tar_member_obj is not None and (
+        tar_member_obj.islnk() or tar_member_obj.issym()
+    ):
+        linkpath = tar_member_obj.linkname
+        if tar_member_obj.issym():
+            base = posixpath.dirname(tar_member_obj.name)
+            linkpath = posixpath.join(base, linkpath)
+            linkpath = posixpath.normpath(linkpath)
+        tar_member_obj = tar_obj._getmember(linkpath)
+
+    is_file_or_dir = tar_member_obj is not None and (
+        tar_member_obj.isfile() or tar_member_obj.isdir()
+    )
+    if is_file_or_dir:
+        return tar_member_obj
+
+    raise LookupError('Got unknown file type')
+
+
+def _iter_open_tar(tar_obj, extract_dir, progress_filter):
+    """Emit member-destination pairs from a tar archive."""
+    # don't do any chowning!
+    tar_obj.chown = lambda *args: None
+
+    with contextlib.closing(tar_obj):
+        for member in tar_obj:
+            name = member.name
+            # don't extract absolute paths or ones with .. in them
+            if name.startswith('/') or '..' in name.split('/'):
+                continue
+
+            prelim_dst = os.path.join(extract_dir, *name.split('/'))
+
+            try:
+                member = _resolve_tar_file_or_dir(tar_obj, member)
+            except LookupError:
+                continue
+
+            final_dst = progress_filter(name, prelim_dst)
+            if not final_dst:
+                continue
+
+            if final_dst.endswith(os.sep):
+                final_dst = final_dst[:-1]
+
+            yield member, final_dst
+
+
+def unpack_tarfile(filename, extract_dir, progress_filter=default_filter) -> bool:
+    """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
+    by ``tarfile.open()``).  See ``unpack_archive()`` for an explanation
+    of the `progress_filter` argument.
+    """
+    try:
+        tarobj = tarfile.open(filename)
+    except tarfile.TarError as e:
+        raise UnrecognizedFormat(
+            f"{filename} is not a compressed or uncompressed tar file"
+        ) from e
+
+    for member, final_dst in _iter_open_tar(
+        tarobj,
+        extract_dir,
+        progress_filter,
+    ):
+        try:
+            # XXX Ugh
+            tarobj._extract_member(member, final_dst)
+        except tarfile.ExtractError:
+            # chown/chmod/mkfifo/mknode/makedev failed
+            pass
+
+    return True
+
+
+extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
diff --git a/.venv/lib/python3.12/site-packages/setuptools/build_meta.py b/.venv/lib/python3.12/site-packages/setuptools/build_meta.py
new file mode 100644
index 0000000..1d93b61
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/build_meta.py
@@ -0,0 +1,556 @@
+"""A PEP 517 interface to setuptools
+
+Previously, when a user or a command line tool (let's call it a "frontend")
+needed to make a request of setuptools to take a certain action, for
+example, generating a list of installation requirements, the frontend
+would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line.
+
+PEP 517 defines a different method of interfacing with setuptools. Rather
+than calling "setup.py" directly, the frontend should:
+
+  1. Set the current directory to the directory with a setup.py file
+  2. Import this module into a safe python interpreter (one in which
+     setuptools can potentially set global variables or crash hard).
+  3. Call one of the functions defined in PEP 517.
+
+What each function does is defined in PEP 517. However, here is a "casual"
+definition of the functions (this definition should not be relied on for
+bug reports or API stability):
+
+  - `build_wheel`: build a wheel in the folder and return the basename
+  - `get_requires_for_build_wheel`: get the `setup_requires` to build
+  - `prepare_metadata_for_build_wheel`: get the `install_requires`
+  - `build_sdist`: build an sdist in the folder and return the basename
+  - `get_requires_for_build_sdist`: get the `setup_requires` to build
+
+Again, this is not a formal definition! Just a "taste" of the module.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import io
+import os
+import shlex
+import shutil
+import sys
+import tempfile
+import tokenize
+import warnings
+from collections.abc import Iterable, Iterator, Mapping
+from pathlib import Path
+from typing import TYPE_CHECKING, NoReturn, Union
+
+import setuptools
+
+from . import errors
+from ._path import StrPath, same_path
+from ._reqs import parse_strings
+from .warnings import SetuptoolsDeprecationWarning
+
+import distutils
+from distutils.util import strtobool
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+__all__ = [
+    'get_requires_for_build_sdist',
+    'get_requires_for_build_wheel',
+    'prepare_metadata_for_build_wheel',
+    'build_wheel',
+    'build_sdist',
+    'get_requires_for_build_editable',
+    'prepare_metadata_for_build_editable',
+    'build_editable',
+    '__legacy__',
+    'SetupRequirementsError',
+]
+
+
+class SetupRequirementsError(BaseException):
+    def __init__(self, specifiers) -> None:
+        self.specifiers = specifiers
+
+
+class Distribution(setuptools.dist.Distribution):
+    def fetch_build_eggs(self, specifiers) -> NoReturn:
+        specifier_list = list(parse_strings(specifiers))
+
+        raise SetupRequirementsError(specifier_list)
+
+    @classmethod
+    @contextlib.contextmanager
+    def patch(cls) -> Iterator[None]:
+        """
+        Replace
+        distutils.dist.Distribution with this class
+        for the duration of this context.
+        """
+        orig = distutils.core.Distribution
+        distutils.core.Distribution = cls  # type: ignore[misc] # monkeypatching
+        try:
+            yield
+        finally:
+            distutils.core.Distribution = orig  # type: ignore[misc] # monkeypatching
+
+
+@contextlib.contextmanager
+def no_install_setup_requires():
+    """Temporarily disable installing setup_requires
+
+    Under PEP 517, the backend reports build dependencies to the frontend,
+    and the frontend is responsible for ensuring they're installed.
+    So setuptools (acting as a backend) should not try to install them.
+    """
+    orig = setuptools._install_setup_requires
+    setuptools._install_setup_requires = lambda attrs: None
+    try:
+        yield
+    finally:
+        setuptools._install_setup_requires = orig
+
+
+def _get_immediate_subdirectories(a_dir):
+    return [
+        name for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name))
+    ]
+
+
+def _file_with_extension(directory: StrPath, extension: str | tuple[str, ...]):
+    matching = (f for f in os.listdir(directory) if f.endswith(extension))
+    try:
+        (file,) = matching
+    except ValueError:
+        raise ValueError(
+            'No distribution was found. Ensure that `setup.py` '
+            'is not empty and that it calls `setup()`.'
+        ) from None
+    return file
+
+
+def _open_setup_script(setup_script):
+    if not os.path.exists(setup_script):
+        # Supply a default setup.py
+        return io.StringIO("from setuptools import setup; setup()")
+
+    return tokenize.open(setup_script)
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings():
+        warnings.filterwarnings('ignore', 'setup.py install is deprecated')
+        yield
+
+
+_ConfigSettings: TypeAlias = Union[Mapping[str, Union[str, list[str], None]], None]
+"""
+Currently the user can run::
+
+    pip install -e . --config-settings key=value
+    python -m build -C--key=value -C key=value
+
+- pip will pass both key and value as strings and overwriting repeated keys
+  (pypa/pip#11059).
+- build will accumulate values associated with repeated keys in a list.
+  It will also accept keys with no associated value.
+  This means that an option passed by build can be ``str | list[str] | None``.
+- PEP 517 specifies that ``config_settings`` is an optional dict.
+"""
+
+
+class _ConfigSettingsTranslator:
+    """Translate ``config_settings`` into distutils-style command arguments.
+    Only a limited number of options is currently supported.
+    """
+
+    # See pypa/setuptools#1928 pypa/setuptools#2491
+
+    def _get_config(self, key: str, config_settings: _ConfigSettings) -> list[str]:
+        """
+        Get the value of a specific key in ``config_settings`` as a list of strings.
+
+        >>> fn = _ConfigSettingsTranslator()._get_config
+        >>> fn("--global-option", None)
+        []
+        >>> fn("--global-option", {})
+        []
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': ['foo']})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo bar'})
+        ['foo', 'bar']
+        """
+        cfg = config_settings or {}
+        opts = cfg.get(key) or []
+        return shlex.split(opts) if isinstance(opts, str) else opts
+
+    def _global_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Let the user specify ``verbose`` or ``quiet`` + escape hatch via
+        ``--global-option``.
+        Note: ``-v``, ``-vv``, ``-vvv`` have similar effects in setuptools,
+        so we just have to cover the basic scenario ``-v``.
+
+        >>> fn = _ConfigSettingsTranslator()._global_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"verbose": "False"}))
+        ['-q']
+        >>> list(fn({"verbose": "1"}))
+        ['-v']
+        >>> list(fn({"--verbose": None}))
+        ['-v']
+        >>> list(fn({"verbose": "true", "--global-option": "-q --no-user-cfg"}))
+        ['-v', '-q', '--no-user-cfg']
+        >>> list(fn({"--quiet": None}))
+        ['-q']
+        """
+        cfg = config_settings or {}
+        falsey = {"false", "no", "0", "off"}
+        if "verbose" in cfg or "--verbose" in cfg:
+            level = str(cfg.get("verbose") or cfg.get("--verbose") or "1")
+            yield ("-q" if level.lower() in falsey else "-v")
+        if "quiet" in cfg or "--quiet" in cfg:
+            level = str(cfg.get("quiet") or cfg.get("--quiet") or "1")
+            yield ("-v" if level.lower() in falsey else "-q")
+
+        yield from self._get_config("--global-option", config_settings)
+
+    def __dist_info_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``dist_info`` command accepts ``tag-date`` and ``tag-build``.
+
+        .. warning::
+           We cannot use this yet as it requires the ``sdist`` and ``bdist_wheel``
+           commands run in ``build_sdist`` and ``build_wheel`` to reuse the egg-info
+           directory created in ``prepare_metadata_for_build_wheel``.
+
+        >>> fn = _ConfigSettingsTranslator()._ConfigSettingsTranslator__dist_info_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"tag-date": "False"}))
+        ['--no-date']
+        >>> list(fn({"tag-date": None}))
+        ['--no-date']
+        >>> list(fn({"tag-date": "true", "tag-build": ".a"}))
+        ['--tag-date', '--tag-build', '.a']
+        """
+        cfg = config_settings or {}
+        if "tag-date" in cfg:
+            val = strtobool(str(cfg["tag-date"] or "false"))
+            yield ("--tag-date" if val else "--no-date")
+        if "tag-build" in cfg:
+            yield from ["--tag-build", str(cfg["tag-build"])]
+
+    def _editable_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``editable_wheel`` command accepts ``editable-mode=strict``.
+
+        >>> fn = _ConfigSettingsTranslator()._editable_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"editable-mode": "strict"}))
+        ['--mode', 'strict']
+        """
+        cfg = config_settings or {}
+        mode = cfg.get("editable-mode") or cfg.get("editable_mode")
+        if not mode:
+            return
+        yield from ["--mode", str(mode)]
+
+    def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Users may expect to pass arbitrary lists of arguments to a command
+        via "--global-option" (example provided in PEP 517 of a "escape hatch").
+
+        >>> fn = _ConfigSettingsTranslator()._arbitrary_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({}))
+        []
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': ['foo']}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo bar'}))
+        ['foo', 'bar']
+        >>> list(fn({'--global-option': 'foo'}))
+        []
+        """
+        yield from self._get_config("--build-option", config_settings)
+
+
+class _BuildMetaBackend(_ConfigSettingsTranslator):
+    def _get_build_requires(
+        self, config_settings: _ConfigSettings, requirements: list[str]
+    ):
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "egg_info",
+        ]
+        try:
+            with Distribution.patch():
+                self.run_setup()
+        except SetupRequirementsError as e:
+            requirements += e.specifiers
+
+        return requirements
+
+    def run_setup(self, setup_script: str = 'setup.py') -> None:
+        # Note that we can reuse our build directory between calls
+        # Correctness comes first, then optimization later
+        __file__ = os.path.abspath(setup_script)
+        __name__ = '__main__'
+
+        with _open_setup_script(__file__) as f:
+            code = f.read().replace(r'\r\n', r'\n')
+
+        try:
+            exec(code, locals())
+        except SystemExit as e:
+            if e.code:
+                raise
+            # We ignore exit code indicating success
+            SetuptoolsDeprecationWarning.emit(
+                "Running `setup.py` directly as CLI tool is deprecated.",
+                "Please avoid using `sys.exit(0)` or similar statements "
+                "that don't fit in the paradigm of a configuration file.",
+                see_url="https://blog.ganssle.io/articles/2021/10/"
+                "setup-py-deprecated.html",
+            )
+
+    def get_requires_for_build_wheel(
+        self, config_settings: _ConfigSettings = None
+    ) -> list[str]:
+        return self._get_build_requires(config_settings, requirements=[])
+
+    def get_requires_for_build_sdist(
+        self, config_settings: _ConfigSettings = None
+    ) -> list[str]:
+        return self._get_build_requires(config_settings, requirements=[])
+
+    def _bubble_up_info_directory(
+        self, metadata_directory: StrPath, suffix: str
+    ) -> str:
+        """
+        PEP 517 requires that the .dist-info directory be placed in the
+        metadata_directory. To comply, we MUST copy the directory to the root.
+
+        Returns the basename of the info directory, e.g. `proj-0.0.0.dist-info`.
+        """
+        info_dir = self._find_info_directory(metadata_directory, suffix)
+        if not same_path(info_dir.parent, metadata_directory):
+            shutil.move(str(info_dir), metadata_directory)
+            # PEP 517 allow other files and dirs to exist in metadata_directory
+        return info_dir.name
+
+    def _find_info_directory(self, metadata_directory: StrPath, suffix: str) -> Path:
+        for parent, dirs, _ in os.walk(metadata_directory):
+            candidates = [f for f in dirs if f.endswith(suffix)]
+
+            if len(candidates) != 0 or len(dirs) != 1:
+                assert len(candidates) == 1, (
+                    f"Exactly one {suffix} should have been produced, but found {len(candidates)}: {candidates}"
+                )
+                return Path(parent, candidates[0])
+
+        msg = f"No {suffix} directory found in {metadata_directory}"
+        raise errors.InternalError(msg)
+
+    def prepare_metadata_for_build_wheel(
+        self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
+    ) -> str:
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "dist_info",
+            "--output-dir",
+            str(metadata_directory),
+            "--keep-egg-info",
+        ]
+        with no_install_setup_requires():
+            self.run_setup()
+
+        self._bubble_up_info_directory(metadata_directory, ".egg-info")
+        return self._bubble_up_info_directory(metadata_directory, ".dist-info")
+
+    def _build_with_temp_dir(
+        self,
+        setup_command: Iterable[str],
+        result_extension: str | tuple[str, ...],
+        result_directory: StrPath,
+        config_settings: _ConfigSettings,
+        arbitrary_args: Iterable[str] = (),
+    ):
+        result_directory = os.path.abspath(result_directory)
+
+        # Build in a temporary directory, then copy to the target.
+        os.makedirs(result_directory, exist_ok=True)
+
+        with tempfile.TemporaryDirectory(
+            prefix=".tmp-", dir=result_directory
+        ) as tmp_dist_dir:
+            sys.argv = [
+                *sys.argv[:1],
+                *self._global_args(config_settings),
+                *setup_command,
+                "--dist-dir",
+                tmp_dist_dir,
+                *arbitrary_args,
+            ]
+            with no_install_setup_requires():
+                self.run_setup()
+
+            result_basename = _file_with_extension(tmp_dist_dir, result_extension)
+            result_path = os.path.join(result_directory, result_basename)
+            if os.path.exists(result_path):
+                # os.rename will fail overwriting on non-Unix.
+                os.remove(result_path)
+            os.rename(os.path.join(tmp_dist_dir, result_basename), result_path)
+
+        return result_basename
+
+    def build_wheel(
+        self,
+        wheel_directory: StrPath,
+        config_settings: _ConfigSettings = None,
+        metadata_directory: StrPath | None = None,
+    ) -> str:
+        def _build(cmd: list[str]):
+            with suppress_known_deprecation():
+                return self._build_with_temp_dir(
+                    cmd,
+                    '.whl',
+                    wheel_directory,
+                    config_settings,
+                    self._arbitrary_args(config_settings),
+                )
+
+        if metadata_directory is None:
+            return _build(['bdist_wheel'])
+
+        try:
+            return _build(['bdist_wheel', '--dist-info-dir', str(metadata_directory)])
+        except SystemExit as ex:  # pragma: nocover
+            # pypa/setuptools#4683
+            if "--dist-info-dir not recognized" not in str(ex):
+                raise
+            _IncompatibleBdistWheel.emit()
+            return _build(['bdist_wheel'])
+
+    def build_sdist(
+        self, sdist_directory: StrPath, config_settings: _ConfigSettings = None
+    ) -> str:
+        return self._build_with_temp_dir(
+            ['sdist', '--formats', 'gztar'], '.tar.gz', sdist_directory, config_settings
+        )
+
+    def _get_dist_info_dir(self, metadata_directory: StrPath | None) -> str | None:
+        if not metadata_directory:
+            return None
+        dist_info_candidates = list(Path(metadata_directory).glob("*.dist-info"))
+        assert len(dist_info_candidates) <= 1
+        return str(dist_info_candidates[0]) if dist_info_candidates else None
+
+    def build_editable(
+        self,
+        wheel_directory: StrPath,
+        config_settings: _ConfigSettings = None,
+        metadata_directory: StrPath | None = None,
+    ) -> str:
+        # XXX can or should we hide our editable_wheel command normally?
+        info_dir = self._get_dist_info_dir(metadata_directory)
+        opts = ["--dist-info-dir", info_dir] if info_dir else []
+        cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
+        with suppress_known_deprecation():
+            return self._build_with_temp_dir(
+                cmd, ".whl", wheel_directory, config_settings
+            )
+
+    def get_requires_for_build_editable(
+        self, config_settings: _ConfigSettings = None
+    ) -> list[str]:
+        return self.get_requires_for_build_wheel(config_settings)
+
+    def prepare_metadata_for_build_editable(
+        self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
+    ) -> str:
+        return self.prepare_metadata_for_build_wheel(
+            metadata_directory, config_settings
+        )
+
+
+class _BuildMetaLegacyBackend(_BuildMetaBackend):
+    """Compatibility backend for setuptools
+
+    This is a version of setuptools.build_meta that endeavors
+    to maintain backwards
+    compatibility with pre-PEP 517 modes of invocation. It
+    exists as a temporary
+    bridge between the old packaging mechanism and the new
+    packaging mechanism,
+    and will eventually be removed.
+    """
+
+    def run_setup(self, setup_script: str = 'setup.py') -> None:
+        # In order to maintain compatibility with scripts assuming that
+        # the setup.py script is in a directory on the PYTHONPATH, inject
+        # '' into sys.path. (pypa/setuptools#1642)
+        sys_path = list(sys.path)  # Save the original path
+
+        script_dir = os.path.dirname(os.path.abspath(setup_script))
+        if script_dir not in sys.path:
+            sys.path.insert(0, script_dir)
+
+        # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to
+        # get the directory of the source code. They expect it to refer to the
+        # setup.py script.
+        sys_argv_0 = sys.argv[0]
+        sys.argv[0] = setup_script
+
+        try:
+            super().run_setup(setup_script=setup_script)
+        finally:
+            # While PEP 517 frontends should be calling each hook in a fresh
+            # subprocess according to the standard (and thus it should not be
+            # strictly necessary to restore the old sys.path), we'll restore
+            # the original path so that the path manipulation does not persist
+            # within the hook after run_setup is called.
+            sys.path[:] = sys_path
+            sys.argv[0] = sys_argv_0
+
+
+class _IncompatibleBdistWheel(SetuptoolsDeprecationWarning):
+    _SUMMARY = "wheel.bdist_wheel is deprecated, please import it from setuptools"
+    _DETAILS = """
+    Ensure that any custom bdist_wheel implementation is a subclass of
+    setuptools.command.bdist_wheel.bdist_wheel.
+    """
+    _DUE_DATE = (2025, 10, 15)
+    # Initially introduced in 2024/10/15, but maybe too disruptive to be enforced?
+    _SEE_URL = "https://github.com/pypa/wheel/pull/631"
+
+
+# The primary backend
+_BACKEND = _BuildMetaBackend()
+
+get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel
+get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
+prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
+build_wheel = _BACKEND.build_wheel
+build_sdist = _BACKEND.build_sdist
+get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
+prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
+build_editable = _BACKEND.build_editable
+
+
+# The legacy backend
+__legacy__ = _BuildMetaLegacyBackend()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-32.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-32.exe
new file mode 100644
index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6
GIT binary patch
literal 11776
zcmeHNe|%F_mcMDz5}_ppse<4Tp;#0sX_8_gNt*(JGExn+wxB>8+E3`d{(-`>tQ|+Essy*Y)*hc)h9q6zY8qLj8NF-=8YDooZK&u2FPLj}*nQvq^O
z4AiqL?F`1UsEcQ~X07OuG4ZIGeFtZxaWt6MBNZXpv;~ZvrG}HSe%SMCPd#IOz@IdN
z_iMx}h+NR^SGru!Y1fjM;wcn`%_7>}c>tsrtuv)JTKv&7R$mxsbcrs;PUQe)KpBs6
z6H3}+$JB)i8{1961O$U^*ld)v$Ie*1Fc1th0LRygHFLh()0oh-<9}g5@U?)E*3Rlt
zNZwqOw8zfa;(h8?5cp$`T&I^ML)poYR(?K-r&W_QB=cC2orRB09z6p5BfN3Q3m?lK#X$3)(^g6A
zwKcV|J5{@Psh8}Ghc3Cjno6XQhIvzfzjaFVXCA(EKVh^ZHz0t~{$eHa`
z(mmQ;nhDl*A@%ZsTdgxf`bcv74Yl5NHS#mpGbU9IVM_3*FJNTWx@7|yrX={wJF=ER
zfaT{~kAiN_v-G4v>dzs#hI))^NHX4yH
zC6kgPI~sWpjaX#pzmrKfg8|7>d6S^Om$}=q8n4>Ryecc6_6Sr
zm0oO>YL}{!ivd(+T+`3_}*H;cLgZ%gLm(R_>h${SH&7Ry`Bz#h#HNYCBMC6IuqSLh*ngqdp$;k
zRnh$)p%2%MOgmXOeJNwQ*O#f5K^+CJiNr&H+?BPgBXR-UJL9^Y9q_E~^>~Uyijh><
z3TkU2y<+pOCy25APl(nf1KsU^nh`QciEzIv8aW4iD903!^y>D+qY)Zs>V5aCg)t)N
zes)ynINlMX!I3j!Zk2akt^6j^IECw1rZc-n=5K-09b=Y%yb1LP=MS!MN)cL@6r*+9
zyT`EQ?XQtgn33>N>ke_A?)9wn1&Y^SW6kZQOczmO4rxEPphfE0rGpPANGI*>m*$7m
z-5Kl1si9A$VmC!{Wauk|0;)eL)ex`xF{iUOc`AHtQu}Mvfz*E58?Oz4VV!R0FVXp?
zv7jLaoyk*(d5nOZEAF22)~89_*RG5s*v2+ElLj+c|m#ho@(aUNv4Fo`au5c`Jk@QaH`LHs{M%f8lT7&JId|w(=
zsJ!c}i5@-;)?{8T!bmYy_}J`$xd+n6oaDUHU^GI!wKXYjE*A@md>NXbG`agAVbtae
zz>>82mSxbkmu{cSzE_S6)rLW0oM8!#x_yhg<(c;%iHJKsIr5N)E9s+)vYQ
zpD!Jzdf2`aPmm-pJoQhrLe}PO2y}NLJWi6vj{RYi4=QGQ1x8F@tn?~YGnJ;PXl=zNU
zcf0qJAh{6(6dG0|Wp^TsdA=Fe=dESS)t_0u+WLmBHr~Te8%#i9mz!&6x(ShESX@t}
z9S}(hneh6^PPD0hBvtQ8)%#NQGpYLdRQ*z_eid~xZ!#<%<5CgzBo+r5|ED6DJWR%<
zqb(PFNP=_S&Z|B1iwoo#ttmd@mY+Z~?v_X&jZP*Hlhz06;*reL@(C$aoC+Q(e^|Ef
zYT0dHlU*|`yK+e4<}sbXymDCzoqpdLxxYXI++aTW5?M@(J50D&g~LI{q~Ts@J}na0
zRdlUSSaRs352+F#Z$VhqcvW)SIL71}9Cc3l05zA=sW&JeLEEc}8stA=8^VhlcE-iv
zncvAiEn{xo3_Fwknc$wn2bO|)?OIrFl{^s$xd8xngpgo}I=QRUl+kWb(;4g3v&
zQ(n$VpP&u#nexzBP!dG0#-@QhRl++)o`*qw^5;OC;t6>NDDsnhw2nq6yp!Dh#dapo
zhh|vM1n9e#4lgHy(0Ha}{U5@@5R;E%xgCzP2dqnQ(djL>bm?}^2Lt9<5zQf_+X}z9
z4FK}PP=l5uPUvyaIiKvryCwVhQvml|;>stk`#4umCJlygHjugN1I(5Tot6KcbdWtz
zQW`WR7nX`sYvhHBUSh7apw^pFE4__-Da0gC$;&w(xUR2}uTODllMCd0nn3=~>mcQ#
zoS@1e{|pt9szH9>zj&&ErwU;nSnvLwXF{3sN1)T4O#TiDTAR{e>K-VTD$hwOiA5d#
ztDN$8z_xa6LK0;8Q_POx#`bN0U=Z*eD8r*1{Zi#%W0YQ=*xI@c_w|xDp2T}rjqC+m
zGSn}-QTNGjVzQ4#SW3Ad=PgCpAsUl;6==Ax)3A6l&yFTe87r#w34Za+$0+ZO$-EMv
zVC+n9#@Z9N9n_cO8k94QVBTbcH%}s1oJ-J_4cPQZUJ0*q=JM)hEw3^)yqd*$HE#%U
zzVFdY1A3B!9n9yo=HP79F^Be`nfj5lI0$<(TfwTrzXL=(I2XO1Og$he-jkWnsjy0>
zA=UC~*4!UwJ?&=nGhiE~FY&DvU72|i{jPo{>8=Usy?p!
zw{o2Dlhr5D$hv=Uw)%6+DRGKanQi%Yc3`ZuSgT%~Z8;vu4j-LuITiZE6yJc;@aVv$
z8d>7oL)14On2h;fw<824r)EH7IVt9v;?i4#x*v~6Xb&3W8xk+7Hqfm6#b``-=2Gxt
z*Td^_0Lml3YmD*r30Y7&W4%ni7tOT;AHS&Lj%v3#FocO3>eomiZRATaGkjSU+9+!j
zHEzYJKEquBF1Z63N(4H0HFdWrU2%>vK43s1islekG-oA;P7aANnzM$(b%5QOG@lA;
zuTOY0<@r#i&#QH_1(0_qf{(UyXXU*(7Zzd>NM`E~SW)f3k3Wbo2V+jYp1u)>rC+@6D0_#-
z$kh-yFix@MVzk#@IK6gi{KCnZ4lQ>EjXsWyq@Yh%aAf%0q_A)vVSPR%SekRo;VHrQ?=&LSv5H4dTFfV8`Z6Sk72if9|;BQ6Bvl-mOEru-n;&1ah@0ZclW=
z@GRl3rzPg)V@kwh0&k3J2B1Q&k~hKJIADgeJsom=NAX*p8$%NEltD~ep$TjAqZJzY
z52OV3GSGwkg_+ry4DT3;&PRy7vxDGBcBx8FFH)uU#BE-+{Im*tS(D$FftwRg0Q8Qy
zi~{Qz-gqCu4Llm_Ao#>ig8SPE9^OjuwatJ{k38`VuVQ<7wO`~_q?K3C`grvtH>!P)
zM)skS9GYlk4;nFQJcULNpO;dOWFPY4;8m-s5;48Y6MP
zl+4q^fL<0`le};y<~S5}TvS$Y(;4{raw5qSZ_IHK-lfb7lV;;o&|=X)qKHER|5!l$
z(^Y3Br;DZ|%1+XTZsRFw$3nh?rgbVisC;s0LU@ZfzHMCihzt=7?->bWJmHSqLjI&Y
zPu8xfm9}Z6J9d;d1e^Oqv%=eR)uHLqvPm|5=HpYun{BsHb%SjNRXQ89e_thP>nNQa
z)iC*8I3jA09@NMucuQUf3-pC&wZfGwQC0K$Nu2Jl5U_j^oKh>5Mv~%K>7CT^`F^-t
zWBDTRF^(tVJx#m>{t_?0L%Aa}?5r_aOe>R?=I2Iz`MEKaIsH{Nwfr{`Y#Y;?&Zr{p(L7o{4vaSDzTPkkTj>
z&k|{E;dA-noLo><-m$}{pl&BQJ1h+1t^*xRy|Ha)t8`CGU);AlIwty{CVICPzZKPH
zOOCVBw*IK&{EiFD1%F6#$i*JNu!8@9^HH&16nxMT`!6*%w*G8X4gJSCE{^Mo1~t(;
zwb82V&=QE5HCUF^+2UC$CeF0gXJs&PnyrGLY{;WrgbDXs6nkYKQrI6nMNMoNZST0eMz3=uw_
z(UVEGfeqL}!d&R0A9!^;9|t0QT%%Ai{0fz6#Vy3ea>WNsy*ky&sN-DpoBsY
zTbp)l)4r6!@Czy$htUXC>t0xoUk8W6gJp+yh-J@d;Dykb&hO%^>`gqE0gs8dKc__+
z1@a#iWG-&x=)rfyqQ~zxq4AxM@?Pg|Ug2o&O~AT;-u^A|(DAg!ll&vU_q5Kf#
z1sh{~QMRMFQC6drpiD#2qMWzl+W^YTC`Zw!=RJ)35M|;*&{5MM-|e{GdVqPshJdG4ENtDv
z*b?PqT1%_o2Wc#uc?&~n-6FbM{Ds11fpkdmG`34f!j|`TQqG;qK-0zn6}yR`^Z>$L$}$*lLBZA2_5oR>&2~=Psph
zw#Nyl_|AwX`v|y6S8&jdv5UZ^`PfW2%C|>xN~Xqw1CL#ax8ZN7lhNO-2G7P|kjV6H
zxE>Y%cA8I~MIX3!Za;ia%{Ooz2!Hj1yDI(i!unD*_2*8tGdl`B!}QZ>^t--gXD$@d
zfB5N!-t_%<7~ce$E{#ak*|{zjrDGizNQaal{C%H!YU6Yk#V6&gggibgnaye+}$
z=2Xr<#y(3)O(C!|cM)G@OJm3<&{QNE*m0Rv0!I3SEk0q181N}`1=yP^T+@XB&eSxb
zqfPMR&lv7>tiH>!(qt@b^!bp#S+md_6o8+`>gpOofdH85gv|{?tLSO*vzxDlt!rq(
zogaS_QOr`Tb#A`OfElFbW{j&@vihF8s#jDxip&OOrW;v<%g6st;YL>1?7ClQg^Acy
zRu^pbc|_h}nV7A$uCC4%*wjFOCoFP%YtIS-_YF39#vYnF!-4#7;JSl2y8L55!`i69-k-#urz@!C5%|-
zYHh6(x3mhZkR%IYC@2J)p}!CaFgAseL7F_9LII)9?+OH39;6jOXV}N%_lO>s&-2g-
zN$|IM0xkY#?v2}79WFS-T*IT&SxOWcP^g(Lywa`{*cwLnBF1Ks7tp9ybZw%)8)6Jr
zZcykqprvq>vATe;$rGg2iEadLV;wx=^3hW35G{~WM_$_KYPg^Bdum+@E9VsO$1mI>
ze&NA7K9LF*fzP<#Y2F2+*4*OfLix1{H`%oLQxx(fkF@ES4c=9>ptz$T3$*x}TI-P6
zy^IWioh1qs7jz+meOItLy5+IeB-ho**X(NvLJL=`XI^t~-h&?hJV>4A7F@0Kd`0t$
z=B1+XDmpwa1h>F0&ELd@zs(Xo%|bfMRdRSZej^;Jj7tquZ_#IM%Yw%|3mb5D4Pr
zwG|W<8VdA+AFsVg8McCZs+Y|xDbNQ+oU|?Gf7I5DJPb
z{7o$>X*2$UG}8<|4kLqjfev`YEvY^*0pWoVW)T{l0Z$wD>n0&MLQE$+_`5fjy;5is
ze0m>2TY)RM!r#_%zYr+UhdkvC^xJ@~pvmP63I+Q4BXX?s|NJc0`J;5Q&L3GEhFj+k
z+YO!3PP}$67iS{;rZU`QC@|b-IBfWKQK)E7(Q`##6=fL587CR7#-+xe7>^j=G@dY?
zHfl@;Q-#TEy3K4g2hEkmRmBe$KV1B5@xK-yDefx%*@8C~^eia7W5peh-SO@nvrC*M
zYfD@ukC%MET^X!D}TqBj=pwH&ZKZ~3XE
z+wz{}*Oq?E?=9ynpIa_jn01VGyfx1{%{tRM$7-@#tjn!V>pj-{tb(=0`ghhxtxsAH
zS+zE^ZIdl#+h_Z!?TGCS+nctZ+upUkZ#!pGm1;}Jm+DJzFMX`^$iXJLzFZxZ^4e9HJNERrzx8AGP+rhhOUHT9W(W%|(cg=xq%$80v+%y*ir&28pw=10um
zGw(A$Z9Zgv!Tgf>WpmuzZSFJw!hG8Nq4^{8C+0!(m*ydJMzOYdeDS2>n~L?tw-(PW
zHWrr@mliKA{&sOS-aqNRYc{HBMYD^{MI}WVqt>|6=rpb|zGysdJYRgVc+!H&Y42)a
I{a-)-138F7F#rGn

literal 0
HcmV?d00001

diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-64.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..3ea50eebfe3f0113b231a318cc1ad6e238afd60d
GIT binary patch
literal 14336
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
AO8@`>

literal 0
HcmV?d00001

diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..da96455a07a0bad4cde5dc5626544325f82c722b
GIT binary patch
literal 13824
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
I{a-)-138F7F#rGn

literal 0
HcmV?d00001

diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/command/__init__.py
new file mode 100644
index 0000000..50e6c2f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/__init__.py
@@ -0,0 +1,21 @@
+# mypy: disable_error_code=call-overload
+# pyright: reportCallIssue=false, reportArgumentType=false
+# Can't disable on the exact line because distutils doesn't exists on Python 3.12
+# and type-checkers aren't aware of distutils_hack,
+# causing distutils.command.bdist.bdist.format_commands to be Any.
+
+import sys
+
+from distutils.command.bdist import bdist
+
+if 'egg' not in bdist.format_commands:
+    try:
+        # format_commands is a dict in vendored distutils
+        # It used to be a list in older (stdlib) distutils
+        # We support both for backwards compatibility
+        bdist.format_commands['egg'] = ('bdist_egg', "Python .egg file")
+    except TypeError:
+        bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
+        bdist.format_commands.append('egg')
+
+del bdist, sys
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/_requirestxt.py b/.venv/lib/python3.12/site-packages/setuptools/command/_requirestxt.py
new file mode 100644
index 0000000..9029b12
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/_requirestxt.py
@@ -0,0 +1,131 @@
+"""Helper code used to generate ``requires.txt`` files in the egg-info directory.
+
+The ``requires.txt`` file has an specific format:
+    - Environment markers need to be part of the section headers and
+      should not be part of the requirement spec itself.
+
+See https://setuptools.pypa.io/en/latest/deprecated/python_eggs.html#requires-txt
+"""
+
+from __future__ import annotations
+
+import io
+from collections import defaultdict
+from collections.abc import Mapping
+from itertools import filterfalse
+from typing import TypeVar
+
+from jaraco.text import yield_lines
+from packaging.requirements import Requirement
+
+from .. import _reqs
+from .._reqs import _StrOrIter
+
+# dict can work as an ordered set
+_T = TypeVar("_T")
+_Ordered = dict[_T, None]
+
+
+def _prepare(
+    install_requires: _StrOrIter, extras_require: Mapping[str, _StrOrIter]
+) -> tuple[list[str], dict[str, list[str]]]:
+    """Given values for ``install_requires`` and ``extras_require``
+    create modified versions in a way that can be written in ``requires.txt``
+    """
+    extras = _convert_extras_requirements(extras_require)
+    return _move_install_requirements_markers(install_requires, extras)
+
+
+def _convert_extras_requirements(
+    extras_require: Mapping[str, _StrOrIter],
+) -> defaultdict[str, _Ordered[Requirement]]:
+    """
+    Convert requirements in `extras_require` of the form
+    `"extra": ["barbazquux; {marker}"]` to
+    `"extra:{marker}": ["barbazquux"]`.
+    """
+    output = defaultdict[str, _Ordered[Requirement]](dict)
+    for section, v in extras_require.items():
+        # Do not strip empty sections.
+        output[section]
+        for r in _reqs.parse(v):
+            output[section + _suffix_for(r)].setdefault(r)
+
+    return output
+
+
+def _move_install_requirements_markers(
+    install_requires: _StrOrIter, extras_require: Mapping[str, _Ordered[Requirement]]
+) -> tuple[list[str], dict[str, list[str]]]:
+    """
+    The ``requires.txt`` file has an specific format:
+        - Environment markers need to be part of the section headers and
+          should not be part of the requirement spec itself.
+
+    Move requirements in ``install_requires`` that are using environment
+    markers ``extras_require``.
+    """
+
+    # divide the install_requires into two sets, simple ones still
+    # handled by install_requires and more complex ones handled by extras_require.
+
+    inst_reqs = list(_reqs.parse(install_requires))
+    simple_reqs = filter(_no_marker, inst_reqs)
+    complex_reqs = filterfalse(_no_marker, inst_reqs)
+    simple_install_requires = list(map(str, simple_reqs))
+
+    for r in complex_reqs:
+        extras_require[':' + str(r.marker)].setdefault(r)
+
+    expanded_extras = dict(
+        # list(dict.fromkeys(...))  ensures a list of unique strings
+        (k, list(dict.fromkeys(str(r) for r in map(_clean_req, v))))
+        for k, v in extras_require.items()
+    )
+
+    return simple_install_requires, expanded_extras
+
+
+def _suffix_for(req):
+    """Return the 'extras_require' suffix for a given requirement."""
+    return ':' + str(req.marker) if req.marker else ''
+
+
+def _clean_req(req):
+    """Given a Requirement, remove environment markers and return it"""
+    r = Requirement(str(req))  # create a copy before modifying
+    r.marker = None
+    return r
+
+
+def _no_marker(req):
+    return not req.marker
+
+
+def _write_requirements(stream, reqs):
+    lines = yield_lines(reqs or ())
+
+    def append_cr(line):
+        return line + '\n'
+
+    lines = map(append_cr, lines)
+    stream.writelines(lines)
+
+
+def write_requirements(cmd, basename, filename):
+    dist = cmd.distribution
+    data = io.StringIO()
+    install_requires, extras_require = _prepare(
+        dist.install_requires or (), dist.extras_require or {}
+    )
+    _write_requirements(data, install_requires)
+    for extra in sorted(extras_require):
+        data.write('\n[{extra}]\n'.format(**vars()))
+        _write_requirements(data, extras_require[extra])
+    cmd.write_or_delete_file("requirements", filename, data.getvalue())
+
+
+def write_setup_requirements(cmd, basename, filename):
+    data = io.StringIO()
+    _write_requirements(data, cmd.distribution.setup_requires)
+    cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/alias.py b/.venv/lib/python3.12/site-packages/setuptools/command/alias.py
new file mode 100644
index 0000000..0ffa311
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/alias.py
@@ -0,0 +1,77 @@
+from setuptools.command.setopt import config_file, edit_config, option_base
+
+from distutils.errors import DistutilsOptionError
+
+
+def shquote(arg):
+    """Quote an argument for later parsing by shlex.split()"""
+    for c in '"', "'", "\\", "#":
+        if c in arg:
+            return repr(arg)
+    if arg.split() != [arg]:
+        return repr(arg)
+    return arg
+
+
+class alias(option_base):
+    """Define a shortcut that invokes one or more commands"""
+
+    description = "define a shortcut to invoke one or more commands"
+    command_consumes_arguments = True
+
+    user_options = [
+        ('remove', 'r', 'remove (unset) the alias'),
+    ] + option_base.user_options
+
+    boolean_options = option_base.boolean_options + ['remove']
+
+    def initialize_options(self):
+        option_base.initialize_options(self)
+        self.args = None
+        self.remove = None
+
+    def finalize_options(self) -> None:
+        option_base.finalize_options(self)
+        if self.remove and len(self.args) != 1:
+            raise DistutilsOptionError(
+                "Must specify exactly one argument (the alias name) when using --remove"
+            )
+
+    def run(self) -> None:
+        aliases = self.distribution.get_option_dict('aliases')
+
+        if not self.args:
+            print("Command Aliases")
+            print("---------------")
+            for alias in aliases:
+                print("setup.py alias", format_alias(alias, aliases))
+            return
+
+        elif len(self.args) == 1:
+            (alias,) = self.args
+            if self.remove:
+                command = None
+            elif alias in aliases:
+                print("setup.py alias", format_alias(alias, aliases))
+                return
+            else:
+                print(f"No alias definition found for {alias!r}")
+                return
+        else:
+            alias = self.args[0]
+            command = ' '.join(map(shquote, self.args[1:]))
+
+        edit_config(self.filename, {'aliases': {alias: command}})
+
+
+def format_alias(name, aliases):
+    source, command = aliases[name]
+    if source == config_file('global'):
+        source = '--global-config '
+    elif source == config_file('user'):
+        source = '--user-config '
+    elif source == config_file('local'):
+        source = ''
+    else:
+        source = f'--filename={source!r}'
+    return source + name + ' ' + command
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/bdist_egg.py b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_egg.py
new file mode 100644
index 0000000..dbabecc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_egg.py
@@ -0,0 +1,471 @@
+"""setuptools.command.bdist_egg
+
+Build .egg distributions"""
+
+from __future__ import annotations
+
+import marshal
+import os
+import re
+import sys
+import textwrap
+from collections.abc import Iterator
+from sysconfig import get_path, get_platform, get_python_version
+from types import CodeType
+from typing import TYPE_CHECKING, AnyStr, Literal
+
+from setuptools import Command
+from setuptools.extension import Library
+
+from .._path import StrPath, StrPathT, ensure_directory
+
+from distutils import log
+from distutils.dir_util import mkpath, remove_tree
+
+if TYPE_CHECKING:
+    from _typeshed import GenericPath
+    from typing_extensions import TypeAlias
+
+# Same as zipfile._ZipFileMode from typeshed
+_ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"]
+
+
+def _get_purelib():
+    return get_path("purelib")
+
+
+def strip_module(filename):
+    if '.' in filename:
+        filename = os.path.splitext(filename)[0]
+    filename = filename.removesuffix('module')
+    return filename
+
+
+def sorted_walk(
+    dir: GenericPath[AnyStr],
+) -> Iterator[tuple[AnyStr, list[AnyStr], list[AnyStr]]]:
+    """Do os.walk in a reproducible way,
+    independent of indeterministic filesystem readdir order
+    """
+    for base, dirs, files in os.walk(dir):
+        dirs.sort()
+        files.sort()
+        yield base, dirs, files
+
+
+def write_stub(resource, pyfile) -> None:
+    _stub_template = textwrap.dedent(
+        """
+        def __bootstrap__():
+            global __bootstrap__, __loader__, __file__
+            import sys, importlib.resources as irs, importlib.util
+            with irs.as_file(irs.files(__name__).joinpath(%r)) as __file__:
+                __loader__ = None; del __bootstrap__, __loader__
+                spec = importlib.util.spec_from_file_location(__name__,__file__)
+                mod = importlib.util.module_from_spec(spec)
+                spec.loader.exec_module(mod)
+        __bootstrap__()
+        """
+    ).lstrip()
+    with open(pyfile, 'w', encoding="utf-8") as f:
+        f.write(_stub_template % resource)
+
+
+class bdist_egg(Command):
+    description = 'create an "egg" distribution'
+
+    user_options = [
+        ('bdist-dir=', 'b', "temporary directory for creating the distribution"),
+        (
+            'plat-name=',
+            'p',
+            "platform name to embed in generated filenames "
+            "(by default uses `sysconfig.get_platform()`)",
+        ),
+        ('exclude-source-files', None, "remove all .py files from the generated egg"),
+        (
+            'keep-temp',
+            'k',
+            "keep the pseudo-installation tree around after "
+            "creating the distribution archive",
+        ),
+        ('dist-dir=', 'd', "directory to put final built distributions in"),
+        ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
+    ]
+
+    boolean_options = ['keep-temp', 'skip-build', 'exclude-source-files']
+
+    def initialize_options(self):
+        self.bdist_dir = None
+        self.plat_name = None
+        self.keep_temp = False
+        self.dist_dir = None
+        self.skip_build = False
+        self.egg_output = None
+        self.exclude_source_files = None
+
+    def finalize_options(self) -> None:
+        ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
+        self.egg_info = ei_cmd.egg_info
+
+        if self.bdist_dir is None:
+            bdist_base = self.get_finalized_command('bdist').bdist_base
+            self.bdist_dir = os.path.join(bdist_base, 'egg')
+
+        if self.plat_name is None:
+            self.plat_name = get_platform()
+
+        self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
+
+        if self.egg_output is None:
+            # Compute filename of the output egg
+            basename = ei_cmd._get_egg_basename(
+                py_version=get_python_version(),
+                platform=self.distribution.has_ext_modules() and self.plat_name,
+            )
+
+            self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
+
+    def do_install_data(self) -> None:
+        # Hack for packages that install data to install's --install-lib
+        self.get_finalized_command('install').install_lib = self.bdist_dir
+
+        site_packages = os.path.normcase(os.path.realpath(_get_purelib()))
+        old, self.distribution.data_files = self.distribution.data_files, []
+
+        for item in old:
+            if isinstance(item, tuple) and len(item) == 2:
+                if os.path.isabs(item[0]):
+                    realpath = os.path.realpath(item[0])
+                    normalized = os.path.normcase(realpath)
+                    if normalized == site_packages or normalized.startswith(
+                        site_packages + os.sep
+                    ):
+                        item = realpath[len(site_packages) + 1 :], item[1]
+                        # XXX else: raise ???
+            self.distribution.data_files.append(item)
+
+        try:
+            log.info("installing package data to %s", self.bdist_dir)
+            self.call_command('install_data', force=False, root=None)
+        finally:
+            self.distribution.data_files = old
+
+    def get_outputs(self):
+        return [self.egg_output]
+
+    def call_command(self, cmdname, **kw):
+        """Invoke reinitialized command `cmdname` with keyword args"""
+        for dirname in INSTALL_DIRECTORY_ATTRS:
+            kw.setdefault(dirname, self.bdist_dir)
+        kw.setdefault('skip_build', self.skip_build)
+        cmd = self.reinitialize_command(cmdname, **kw)
+        self.run_command(cmdname)
+        return cmd
+
+    def run(self) -> None:  # noqa: C901  # is too complex (14)  # FIXME
+        # Generate metadata first
+        self.run_command("egg_info")
+        # We run install_lib before install_data, because some data hacks
+        # pull their data path from the install_lib command.
+        log.info("installing library code to %s", self.bdist_dir)
+        instcmd = self.get_finalized_command('install')
+        old_root = instcmd.root
+        instcmd.root = None
+        if self.distribution.has_c_libraries() and not self.skip_build:
+            self.run_command('build_clib')
+        cmd = self.call_command('install_lib', warn_dir=False)
+        instcmd.root = old_root
+
+        all_outputs, ext_outputs = self.get_ext_outputs()
+        self.stubs = []
+        to_compile = []
+        for p, ext_name in enumerate(ext_outputs):
+            filename, _ext = os.path.splitext(ext_name)
+            pyfile = os.path.join(self.bdist_dir, strip_module(filename) + '.py')
+            self.stubs.append(pyfile)
+            log.info("creating stub loader for %s", ext_name)
+            write_stub(os.path.basename(ext_name), pyfile)
+            to_compile.append(pyfile)
+            ext_outputs[p] = ext_name.replace(os.sep, '/')
+
+        if to_compile:
+            cmd.byte_compile(to_compile)
+        if self.distribution.data_files:
+            self.do_install_data()
+
+        # Make the EGG-INFO directory
+        archive_root = self.bdist_dir
+        egg_info = os.path.join(archive_root, 'EGG-INFO')
+        self.mkpath(egg_info)
+        if self.distribution.scripts:
+            script_dir = os.path.join(egg_info, 'scripts')
+            log.info("installing scripts to %s", script_dir)
+            self.call_command('install_scripts', install_dir=script_dir, no_ep=True)
+
+        self.copy_metadata_to(egg_info)
+        native_libs = os.path.join(egg_info, "native_libs.txt")
+        if all_outputs:
+            log.info("writing %s", native_libs)
+            ensure_directory(native_libs)
+            with open(native_libs, 'wt', encoding="utf-8") as libs_file:
+                libs_file.write('\n'.join(all_outputs))
+                libs_file.write('\n')
+        elif os.path.isfile(native_libs):
+            log.info("removing %s", native_libs)
+            os.unlink(native_libs)
+
+        write_safety_flag(os.path.join(archive_root, 'EGG-INFO'), self.zip_safe())
+
+        if os.path.exists(os.path.join(self.egg_info, 'depends.txt')):
+            log.warn(
+                "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
+                "Use the install_requires/extras_require setup() args instead."
+            )
+
+        if self.exclude_source_files:
+            self.zap_pyfiles()
+
+        # Make the archive
+        make_zipfile(
+            self.egg_output,
+            archive_root,
+            verbose=self.verbose,
+            mode=self.gen_header(),
+        )
+        if not self.keep_temp:
+            remove_tree(self.bdist_dir)
+
+        # Add to 'Distribution.dist_files' so that the "upload" command works
+        getattr(self.distribution, 'dist_files', []).append((
+            'bdist_egg',
+            get_python_version(),
+            self.egg_output,
+        ))
+
+    def zap_pyfiles(self) -> None:
+        log.info("Removing .py files from temporary directory")
+        for base, dirs, files in walk_egg(self.bdist_dir):
+            for name in files:
+                path = os.path.join(base, name)
+
+                if name.endswith('.py'):
+                    log.debug("Deleting %s", path)
+                    os.unlink(path)
+
+                if base.endswith('__pycache__'):
+                    path_old = path
+
+                    pattern = r'(?P.+)\.(?P[^.]+)\.pyc'
+                    m = re.match(pattern, name)
+                    # We shouldn't find any non-pyc files in __pycache__
+                    assert m is not None
+                    path_new = os.path.join(base, os.pardir, m.group('name') + '.pyc')
+                    log.info(f"Renaming file from [{path_old}] to [{path_new}]")
+                    try:
+                        os.remove(path_new)
+                    except OSError:
+                        pass
+                    os.rename(path_old, path_new)
+
+    def zip_safe(self):
+        safe = getattr(self.distribution, 'zip_safe', None)
+        if safe is not None:
+            return safe
+        log.warn("zip_safe flag not set; analyzing archive contents...")
+        return analyze_egg(self.bdist_dir, self.stubs)
+
+    def gen_header(self) -> Literal["w"]:
+        return 'w'
+
+    def copy_metadata_to(self, target_dir) -> None:
+        "Copy metadata (egg info) to the target_dir"
+        # normalize the path (so that a forward-slash in egg_info will
+        # match using startswith below)
+        norm_egg_info = os.path.normpath(self.egg_info)
+        prefix = os.path.join(norm_egg_info, '')
+        for path in self.ei_cmd.filelist.files:
+            if path.startswith(prefix):
+                target = os.path.join(target_dir, path[len(prefix) :])
+                ensure_directory(target)
+                self.copy_file(path, target)
+
+    def get_ext_outputs(self):
+        """Get a list of relative paths to C extensions in the output distro"""
+
+        all_outputs = []
+        ext_outputs = []
+
+        paths = {self.bdist_dir: ''}
+        for base, dirs, files in sorted_walk(self.bdist_dir):
+            all_outputs.extend(
+                paths[base] + filename
+                for filename in files
+                if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS
+            )
+            for filename in dirs:
+                paths[os.path.join(base, filename)] = paths[base] + filename + '/'
+
+        if self.distribution.has_ext_modules():
+            build_cmd = self.get_finalized_command('build_ext')
+            for ext in build_cmd.extensions:
+                if isinstance(ext, Library):
+                    continue
+                fullname = build_cmd.get_ext_fullname(ext.name)
+                filename = build_cmd.get_ext_filename(fullname)
+                if not os.path.basename(filename).startswith('dl-'):
+                    if os.path.exists(os.path.join(self.bdist_dir, filename)):
+                        ext_outputs.append(filename)
+
+        return all_outputs, ext_outputs
+
+
+NATIVE_EXTENSIONS: dict[str, None] = dict.fromkeys('.dll .so .dylib .pyd'.split())
+
+
+def walk_egg(egg_dir: StrPath) -> Iterator[tuple[str, list[str], list[str]]]:
+    """Walk an unpacked egg's contents, skipping the metadata directory"""
+    walker = sorted_walk(egg_dir)
+    base, dirs, files = next(walker)
+    if 'EGG-INFO' in dirs:
+        dirs.remove('EGG-INFO')
+    yield base, dirs, files
+    yield from walker
+
+
+def analyze_egg(egg_dir, stubs):
+    # check for existing flag in EGG-INFO
+    for flag, fn in safety_flags.items():
+        if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)):
+            return flag
+    if not can_scan():
+        return False
+    safe = True
+    for base, dirs, files in walk_egg(egg_dir):
+        for name in files:
+            if name.endswith(('.py', '.pyw')):
+                continue
+            elif name.endswith(('.pyc', '.pyo')):
+                # always scan, even if we already know we're not safe
+                safe = scan_module(egg_dir, base, name, stubs) and safe
+    return safe
+
+
+def write_safety_flag(egg_dir, safe) -> None:
+    # Write or remove zip safety flag file(s)
+    for flag, fn in safety_flags.items():
+        fn = os.path.join(egg_dir, fn)
+        if os.path.exists(fn):
+            if safe is None or bool(safe) != flag:
+                os.unlink(fn)
+        elif safe is not None and bool(safe) == flag:
+            with open(fn, 'wt', encoding="utf-8") as f:
+                f.write('\n')
+
+
+safety_flags = {
+    True: 'zip-safe',
+    False: 'not-zip-safe',
+}
+
+
+def scan_module(egg_dir, base, name, stubs):
+    """Check whether module possibly uses unsafe-for-zipfile stuff"""
+
+    filename = os.path.join(base, name)
+    if filename[:-1] in stubs:
+        return True  # Extension module
+    pkg = base[len(egg_dir) + 1 :].replace(os.sep, '.')
+    module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
+    skip = 16  # skip magic & reserved? & date & file size
+    f = open(filename, 'rb')
+    f.read(skip)
+    code = marshal.load(f)
+    f.close()
+    safe = True
+    symbols = dict.fromkeys(iter_symbols(code))
+    for bad in ['__file__', '__path__']:
+        if bad in symbols:
+            log.warn("%s: module references %s", module, bad)
+            safe = False
+    if 'inspect' in symbols:
+        for bad in [
+            'getsource',
+            'getabsfile',
+            'getfile',
+            'getsourcefile',
+            'getsourcelines',
+            'findsource',
+            'getcomments',
+            'getframeinfo',
+            'getinnerframes',
+            'getouterframes',
+            'stack',
+            'trace',
+        ]:
+            if bad in symbols:
+                log.warn("%s: module MAY be using inspect.%s", module, bad)
+                safe = False
+    return safe
+
+
+def iter_symbols(code: CodeType) -> Iterator[str]:
+    """Yield names and strings used by `code` and its nested code objects"""
+    yield from code.co_names
+    for const in code.co_consts:
+        if isinstance(const, str):
+            yield const
+        elif isinstance(const, CodeType):
+            yield from iter_symbols(const)
+
+
+def can_scan() -> bool:
+    if not sys.platform.startswith('java') and sys.platform != 'cli':
+        # CPython, PyPy, etc.
+        return True
+    log.warn("Unable to analyze compiled code on this platform.")
+    log.warn(
+        "Please ask the author to include a 'zip_safe'"
+        " setting (either True or False) in the package's setup.py"
+    )
+    return False
+
+
+# Attribute names of options for commands that might need to be convinced to
+# install to the egg build directory
+
+INSTALL_DIRECTORY_ATTRS = ['install_lib', 'install_dir', 'install_data', 'install_base']
+
+
+def make_zipfile(
+    zip_filename: StrPathT,
+    base_dir,
+    verbose: bool = False,
+    compress=True,
+    mode: _ZipFileMode = 'w',
+) -> StrPathT:
+    """Create a zip file from all the files under 'base_dir'.  The output
+    zip file will be named 'base_dir' + ".zip".  Uses either the "zipfile"
+    Python module (if available) or the InfoZIP "zip" utility (if installed
+    and found on the default search path).  If neither tool is available,
+    raises DistutilsExecError.  Returns the name of the output zip file.
+    """
+    import zipfile
+
+    mkpath(os.path.dirname(zip_filename))  # type: ignore[arg-type] # python/mypy#18075
+    log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
+
+    def visit(z, dirname, names):
+        for name in names:
+            path = os.path.normpath(os.path.join(dirname, name))
+            if os.path.isfile(path):
+                p = path[len(base_dir) + 1 :]
+                z.write(path, p)
+                log.debug("adding '%s'", p)
+
+    compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
+    z = zipfile.ZipFile(zip_filename, mode, compression=compression)
+    for dirname, dirs, files in sorted_walk(base_dir):
+        visit(z, dirname, files)
+    z.close()
+    return zip_filename
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py
new file mode 100644
index 0000000..6dbb270
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_rpm.py
@@ -0,0 +1,42 @@
+from ..dist import Distribution
+from ..warnings import SetuptoolsDeprecationWarning
+
+import distutils.command.bdist_rpm as orig
+
+
+class bdist_rpm(orig.bdist_rpm):
+    """
+    Override the default bdist_rpm behavior to do the following:
+
+    1. Run egg_info to ensure the name and version are properly calculated.
+    2. Always run 'install' using --single-version-externally-managed to
+       disable eggs in RPM distributions.
+    """
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def run(self) -> None:
+        SetuptoolsDeprecationWarning.emit(
+            "Deprecated command",
+            """
+            bdist_rpm is deprecated and will be removed in a future version.
+            Use bdist_wheel (wheel packages) instead.
+            """,
+            see_url="https://github.com/pypa/setuptools/issues/1988",
+            due_date=(2023, 10, 30),  # Deprecation introduced in 22 Oct 2021.
+        )
+
+        # ensure distro name is up-to-date
+        self.run_command('egg_info')
+
+        orig.bdist_rpm.run(self)
+
+    def _make_spec_file(self):
+        spec = orig.bdist_rpm._make_spec_file(self)
+        return [
+            line.replace(
+                "setup.py install ",
+                "setup.py install --single-version-externally-managed ",
+            ).replace("%setup", "%setup -n %{name}-%{unmangled_version}")
+            for line in spec
+        ]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py
new file mode 100644
index 0000000..3bdfa0b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py
@@ -0,0 +1,603 @@
+"""
+Create a wheel (.whl) distribution.
+
+A wheel is a built archive format.
+"""
+
+from __future__ import annotations
+
+import os
+import re
+import shutil
+import struct
+import sys
+import sysconfig
+import warnings
+from collections.abc import Iterable, Sequence
+from email.generator import BytesGenerator
+from glob import iglob
+from typing import Literal, cast
+from zipfile import ZIP_DEFLATED, ZIP_STORED
+
+from packaging import tags, version as _packaging_version
+from wheel.wheelfile import WheelFile
+
+from .. import Command, __version__, _shutil
+from .._core_metadata import _safe_license_file
+from .._normalization import safer_name
+from ..warnings import SetuptoolsDeprecationWarning
+from .egg_info import egg_info as egg_info_cls
+
+from distutils import log
+
+
+def safe_version(version: str) -> str:
+    """
+    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)
+
+
+setuptools_major_version = int(__version__.split(".")[0])
+
+PY_LIMITED_API_PATTERN = r"cp3\d"
+
+
+def _is_32bit_interpreter() -> bool:
+    return struct.calcsize("P") == 4
+
+
+def python_tag() -> str:
+    return f"py{sys.version_info.major}"
+
+
+def get_platform(archive_root: str | None) -> str:
+    """Return our platform name 'win32', 'linux_x86_64'"""
+    result = sysconfig.get_platform()
+    if result.startswith("macosx") and archive_root is not None:  # pragma: no cover
+        from wheel.macosx_libfile import calculate_macosx_platform_tag
+
+        result = calculate_macosx_platform_tag(archive_root, result)
+    elif _is_32bit_interpreter():
+        if result == "linux-x86_64":
+            # pip pull request #3497
+            result = "linux-i686"
+        elif result == "linux-aarch64":
+            # packaging pull request #234
+            # TODO armv8l, packaging pull request #690 => this did not land
+            # in pip/packaging yet
+            result = "linux-armv7l"
+
+    return result.replace("-", "_")
+
+
+def get_flag(
+    var: str, fallback: bool, expected: bool = True, warn: bool = True
+) -> bool:
+    """Use a fallback value for determining SOABI flags if the needed config
+    var is unset or unavailable."""
+    val = sysconfig.get_config_var(var)
+    if val is None:
+        if warn:
+            warnings.warn(
+                f"Config variable '{var}' is unset, Python ABI tag may be incorrect",
+                RuntimeWarning,
+                stacklevel=2,
+            )
+        return fallback
+    return val == expected
+
+
+def get_abi_tag() -> str | None:
+    """Return the ABI tag based on SOABI (if available) or emulate SOABI (PyPy2)."""
+    soabi: str = sysconfig.get_config_var("SOABI")
+    impl = tags.interpreter_name()
+    if not soabi and impl in ("cp", "pp") and hasattr(sys, "maxunicode"):
+        d = ""
+        u = ""
+        if get_flag("Py_DEBUG", hasattr(sys, "gettotalrefcount"), warn=(impl == "cp")):
+            d = "d"
+
+        abi = f"{impl}{tags.interpreter_version()}{d}{u}"
+    elif soabi and impl == "cp" and soabi.startswith("cpython"):
+        # non-Windows
+        abi = "cp" + soabi.split("-")[1]
+    elif soabi and impl == "cp" and soabi.startswith("cp"):
+        # Windows
+        abi = soabi.split("-")[0]
+        if hasattr(sys, "gettotalrefcount"):
+            # using debug build; append "d" flag
+            abi += "d"
+    elif soabi and impl == "pp":
+        # we want something like pypy36-pp73
+        abi = "-".join(soabi.split("-")[:2])
+        abi = abi.replace(".", "_").replace("-", "_")
+    elif soabi and impl == "graalpy":
+        abi = "-".join(soabi.split("-")[:3])
+        abi = abi.replace(".", "_").replace("-", "_")
+    elif soabi:
+        abi = soabi.replace(".", "_").replace("-", "_")
+    else:
+        abi = None
+
+    return abi
+
+
+def safer_version(version: str) -> str:
+    return safe_version(version).replace("-", "_")
+
+
+class bdist_wheel(Command):
+    description = "create a wheel distribution"
+
+    supported_compressions = {
+        "stored": ZIP_STORED,
+        "deflated": ZIP_DEFLATED,
+    }
+
+    user_options = [
+        ("bdist-dir=", "b", "temporary directory for creating the distribution"),
+        (
+            "plat-name=",
+            "p",
+            "platform name to embed in generated filenames "
+            f"[default: {get_platform(None)}]",
+        ),
+        (
+            "keep-temp",
+            "k",
+            "keep the pseudo-installation tree around after "
+            "creating the distribution archive",
+        ),
+        ("dist-dir=", "d", "directory to put final built distributions in"),
+        ("skip-build", None, "skip rebuilding everything (for testing/debugging)"),
+        (
+            "relative",
+            None,
+            "build the archive using relative paths [default: false]",
+        ),
+        (
+            "owner=",
+            "u",
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            "group=",
+            "g",
+            "Group name used when creating a tar file [default: current group]",
+        ),
+        ("universal", None, "*DEPRECATED* make a universal wheel [default: false]"),
+        (
+            "compression=",
+            None,
+            f"zipfile compression (one of: {', '.join(supported_compressions)}) [default: 'deflated']",
+        ),
+        (
+            "python-tag=",
+            None,
+            f"Python implementation compatibility tag [default: '{python_tag()}']",
+        ),
+        (
+            "build-number=",
+            None,
+            "Build number for this particular version. "
+            "As specified in PEP-0427, this must start with a digit. "
+            "[default: None]",
+        ),
+        (
+            "py-limited-api=",
+            None,
+            "Python tag (cp32|cp33|cpNN) for abi3 wheel tag [default: false]",
+        ),
+        (
+            "dist-info-dir=",
+            None,
+            "directory where a pre-generated dist-info can be found (e.g. as a "
+            "result of calling the PEP517 'prepare_metadata_for_build_wheel' "
+            "method)",
+        ),
+    ]
+
+    boolean_options = ["keep-temp", "skip-build", "relative", "universal"]
+
+    def initialize_options(self) -> None:
+        self.bdist_dir: str | None = None
+        self.data_dir = ""
+        self.plat_name: str | None = None
+        self.plat_tag: str | None = None
+        self.format = "zip"
+        self.keep_temp = False
+        self.dist_dir: str | None = None
+        self.dist_info_dir = None
+        self.egginfo_dir: str | None = None
+        self.root_is_pure: bool | None = None
+        self.skip_build = False
+        self.relative = False
+        self.owner = None
+        self.group = None
+        self.universal = False
+        self.compression: str | int = "deflated"
+        self.python_tag = python_tag()
+        self.build_number: str | None = None
+        self.py_limited_api: str | Literal[False] = False
+        self.plat_name_supplied = False
+
+    def finalize_options(self) -> None:
+        if not self.bdist_dir:
+            bdist_base = self.get_finalized_command("bdist").bdist_base
+            self.bdist_dir = os.path.join(bdist_base, "wheel")
+
+        if self.dist_info_dir is None:
+            egg_info = cast(egg_info_cls, self.distribution.get_command_obj("egg_info"))
+            egg_info.ensure_finalized()  # needed for correct `wheel_dist_name`
+
+        self.data_dir = self.wheel_dist_name + ".data"
+        self.plat_name_supplied = bool(self.plat_name)
+
+        need_options = ("dist_dir", "plat_name", "skip_build")
+
+        self.set_undefined_options("bdist", *zip(need_options, need_options))
+
+        self.root_is_pure = not (
+            self.distribution.has_ext_modules() or self.distribution.has_c_libraries()
+        )
+
+        self._validate_py_limited_api()
+
+        # Support legacy [wheel] section for setting universal
+        wheel = self.distribution.get_option_dict("wheel")
+        if "universal" in wheel:  # pragma: no cover
+            # please don't define this in your global configs
+            log.warn("The [wheel] section is deprecated. Use [bdist_wheel] instead.")
+            val = wheel["universal"][1].strip()
+            if val.lower() in ("1", "true", "yes"):
+                self.universal = True
+
+        if self.universal:
+            SetuptoolsDeprecationWarning.emit(
+                "bdist_wheel.universal is deprecated",
+                """
+                With Python 2.7 end-of-life, support for building universal wheels
+                (i.e., wheels that support both Python 2 and Python 3)
+                is being obviated.
+                Please discontinue using this option, or if you still need it,
+                file an issue with pypa/setuptools describing your use case.
+                """,
+                due_date=(2025, 8, 30),  # Introduced in 2024-08-30
+            )
+
+        if self.build_number is not None and not self.build_number[:1].isdigit():
+            raise ValueError("Build tag (build-number) must start with a digit.")
+
+    def _validate_py_limited_api(self) -> None:
+        if not self.py_limited_api:
+            return
+
+        if not re.match(PY_LIMITED_API_PATTERN, self.py_limited_api):
+            raise ValueError(f"py-limited-api must match '{PY_LIMITED_API_PATTERN}'")
+
+        if sysconfig.get_config_var("Py_GIL_DISABLED"):
+            raise ValueError(
+                f"`py_limited_api={self.py_limited_api!r}` not supported. "
+                "`Py_LIMITED_API` is currently incompatible with "
+                "`Py_GIL_DISABLED`. "
+                "See https://github.com/python/cpython/issues/111506."
+            )
+
+    @property
+    def wheel_dist_name(self) -> str:
+        """Return distribution full name with - replaced with _"""
+        components = [
+            safer_name(self.distribution.get_name()),
+            safer_version(self.distribution.get_version()),
+        ]
+        if self.build_number:
+            components.append(self.build_number)
+        return "-".join(components)
+
+    def get_tag(self) -> tuple[str, str, str]:
+        # bdist sets self.plat_name if unset, we should only use it for purepy
+        # wheels if the user supplied it.
+        if self.plat_name_supplied and self.plat_name:
+            plat_name = self.plat_name
+        elif self.root_is_pure:
+            plat_name = "any"
+        else:
+            # macosx contains system version in platform name so need special handle
+            if self.plat_name and not self.plat_name.startswith("macosx"):
+                plat_name = self.plat_name
+            else:
+                # on macosx always limit the platform name to comply with any
+                # c-extension modules in bdist_dir, since the user can specify
+                # a higher MACOSX_DEPLOYMENT_TARGET via tools like CMake
+
+                # on other platforms, and on macosx if there are no c-extension
+                # modules, use the default platform name.
+                plat_name = get_platform(self.bdist_dir)
+
+            if _is_32bit_interpreter():
+                if plat_name in ("linux-x86_64", "linux_x86_64"):
+                    plat_name = "linux_i686"
+                if plat_name in ("linux-aarch64", "linux_aarch64"):
+                    # TODO armv8l, packaging pull request #690 => this did not land
+                    # in pip/packaging yet
+                    plat_name = "linux_armv7l"
+
+        plat_name = (
+            plat_name.lower().replace("-", "_").replace(".", "_").replace(" ", "_")
+        )
+
+        if self.root_is_pure:
+            if self.universal:
+                impl = "py2.py3"
+            else:
+                impl = self.python_tag
+            tag = (impl, "none", plat_name)
+        else:
+            impl_name = tags.interpreter_name()
+            impl_ver = tags.interpreter_version()
+            impl = impl_name + impl_ver
+            # We don't work on CPython 3.1, 3.0.
+            if self.py_limited_api and (impl_name + impl_ver).startswith("cp3"):
+                impl = self.py_limited_api
+                abi_tag = "abi3"
+            else:
+                abi_tag = str(get_abi_tag()).lower()
+            tag = (impl, abi_tag, plat_name)
+            # issue gh-374: allow overriding plat_name
+            supported_tags = [
+                (t.interpreter, t.abi, plat_name) for t in tags.sys_tags()
+            ]
+            assert tag in supported_tags, (
+                f"would build wheel with unsupported tag {tag}"
+            )
+        return tag
+
+    def run(self):
+        build_scripts = self.reinitialize_command("build_scripts")
+        build_scripts.executable = "python"
+        build_scripts.force = True
+
+        build_ext = self.reinitialize_command("build_ext")
+        build_ext.inplace = False
+
+        if not self.skip_build:
+            self.run_command("build")
+
+        install = self.reinitialize_command("install", reinit_subcommands=True)
+        install.root = self.bdist_dir
+        install.compile = False
+        install.skip_build = self.skip_build
+        install.warn_dir = False
+
+        # A wheel without setuptools scripts is more cross-platform.
+        # Use the (undocumented) `no_ep` option to setuptools'
+        # install_scripts command to avoid creating entry point scripts.
+        install_scripts = self.reinitialize_command("install_scripts")
+        install_scripts.no_ep = True
+
+        # Use a custom scheme for the archive, because we have to decide
+        # at installation time which scheme to use.
+        for key in ("headers", "scripts", "data", "purelib", "platlib"):
+            setattr(install, "install_" + key, os.path.join(self.data_dir, key))
+
+        basedir_observed = ""
+
+        if os.name == "nt":
+            # win32 barfs if any of these are ''; could be '.'?
+            # (distutils.command.install:change_roots bug)
+            basedir_observed = os.path.normpath(os.path.join(self.data_dir, ".."))
+            self.install_libbase = self.install_lib = basedir_observed
+
+        setattr(
+            install,
+            "install_purelib" if self.root_is_pure else "install_platlib",
+            basedir_observed,
+        )
+
+        log.info(f"installing to {self.bdist_dir}")
+
+        self.run_command("install")
+
+        impl_tag, abi_tag, plat_tag = self.get_tag()
+        archive_basename = f"{self.wheel_dist_name}-{impl_tag}-{abi_tag}-{plat_tag}"
+        if not self.relative:
+            archive_root = self.bdist_dir
+        else:
+            archive_root = os.path.join(
+                self.bdist_dir, self._ensure_relative(install.install_base)
+            )
+
+        self.set_undefined_options("install_egg_info", ("target", "egginfo_dir"))
+        distinfo_dirname = (
+            f"{safer_name(self.distribution.get_name())}-"
+            f"{safer_version(self.distribution.get_version())}.dist-info"
+        )
+        distinfo_dir = os.path.join(self.bdist_dir, distinfo_dirname)
+        if self.dist_info_dir:
+            # Use the given dist-info directly.
+            log.debug(f"reusing {self.dist_info_dir}")
+            shutil.copytree(self.dist_info_dir, distinfo_dir)
+            # Egg info is still generated, so remove it now to avoid it getting
+            # copied into the wheel.
+            _shutil.rmtree(self.egginfo_dir)
+        else:
+            # Convert the generated egg-info into dist-info.
+            self.egg2dist(self.egginfo_dir, distinfo_dir)
+
+        self.write_wheelfile(distinfo_dir)
+
+        # Make the archive
+        if not os.path.exists(self.dist_dir):
+            os.makedirs(self.dist_dir)
+
+        wheel_path = os.path.join(self.dist_dir, archive_basename + ".whl")
+        with WheelFile(wheel_path, "w", self._zip_compression()) as wf:
+            wf.write_files(archive_root)
+
+        # Add to 'Distribution.dist_files' so that the "upload" command works
+        getattr(self.distribution, "dist_files", []).append((
+            "bdist_wheel",
+            f"{sys.version_info.major}.{sys.version_info.minor}",
+            wheel_path,
+        ))
+
+        if not self.keep_temp:
+            log.info(f"removing {self.bdist_dir}")
+            _shutil.rmtree(self.bdist_dir)
+
+    def write_wheelfile(
+        self, wheelfile_base: str, generator: str = f"setuptools ({__version__})"
+    ) -> None:
+        from email.message import Message
+
+        msg = Message()
+        msg["Wheel-Version"] = "1.0"  # of the spec
+        msg["Generator"] = generator
+        msg["Root-Is-Purelib"] = str(self.root_is_pure).lower()
+        if self.build_number is not None:
+            msg["Build"] = self.build_number
+
+        # Doesn't work for bdist_wininst
+        impl_tag, abi_tag, plat_tag = self.get_tag()
+        for impl in impl_tag.split("."):
+            for abi in abi_tag.split("."):
+                for plat in plat_tag.split("."):
+                    msg["Tag"] = "-".join((impl, abi, plat))
+
+        wheelfile_path = os.path.join(wheelfile_base, "WHEEL")
+        log.info(f"creating {wheelfile_path}")
+        with open(wheelfile_path, "wb") as f:
+            BytesGenerator(f, maxheaderlen=0).flatten(msg)
+
+    def _ensure_relative(self, path: str) -> str:
+        # copied from dir_util, deleted
+        drive, path = os.path.splitdrive(path)
+        if path[0:1] == os.sep:
+            path = drive + path[1:]
+        return path
+
+    @property
+    def license_paths(self) -> Iterable[str]:
+        if setuptools_major_version >= 57:
+            # Setuptools has resolved any patterns to actual file names
+            return self.distribution.metadata.license_files or ()
+
+        files = set[str]()
+        metadata = self.distribution.get_option_dict("metadata")
+        if setuptools_major_version >= 42:
+            # Setuptools recognizes the license_files option but does not do globbing
+            patterns = cast(Sequence[str], self.distribution.metadata.license_files)
+        else:
+            # Prior to those, wheel is entirely responsible for handling license files
+            if "license_files" in metadata:
+                patterns = metadata["license_files"][1].split()
+            else:
+                patterns = ()
+
+        if "license_file" in metadata:
+            warnings.warn(
+                'The "license_file" option is deprecated. Use "license_files" instead.',
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            files.add(metadata["license_file"][1])
+
+        if not files and not patterns and not isinstance(patterns, list):
+            patterns = ("LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*")
+
+        for pattern in patterns:
+            for path in iglob(pattern):
+                if path.endswith("~"):
+                    log.debug(
+                        f'ignoring license file "{path}" as it looks like a backup'
+                    )
+                    continue
+
+                if path not in files and os.path.isfile(path):
+                    log.info(
+                        f'adding license file "{path}" (matched pattern "{pattern}")'
+                    )
+                    files.add(path)
+
+        return files
+
+    def egg2dist(self, egginfo_path: str, distinfo_path: str) -> None:
+        """Convert an .egg-info directory into a .dist-info directory"""
+
+        def adios(p: str) -> None:
+            """Appropriately delete directory, file or link."""
+            if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p):
+                _shutil.rmtree(p)
+            elif os.path.exists(p):
+                os.unlink(p)
+
+        adios(distinfo_path)
+
+        if not os.path.exists(egginfo_path):
+            # There is no egg-info. This is probably because the egg-info
+            # file/directory is not named matching the distribution name used
+            # to name the archive file. Check for this case and report
+            # accordingly.
+            import glob
+
+            pat = os.path.join(os.path.dirname(egginfo_path), "*.egg-info")
+            possible = glob.glob(pat)
+            err = f"Egg metadata expected at {egginfo_path} but not found"
+            if possible:
+                alt = os.path.basename(possible[0])
+                err += f" ({alt} found - possible misnamed archive file?)"
+
+            raise ValueError(err)
+
+        # .egg-info is a directory
+        pkginfo_path = os.path.join(egginfo_path, "PKG-INFO")
+
+        # ignore common egg metadata that is useless to wheel
+        shutil.copytree(
+            egginfo_path,
+            distinfo_path,
+            ignore=lambda x, y: {
+                "PKG-INFO",
+                "requires.txt",
+                "SOURCES.txt",
+                "not-zip-safe",
+            },
+        )
+
+        # delete dependency_links if it is only whitespace
+        dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt")
+        with open(dependency_links_path, encoding="utf-8") as dependency_links_file:
+            dependency_links = dependency_links_file.read().strip()
+        if not dependency_links:
+            adios(dependency_links_path)
+
+        metadata_path = os.path.join(distinfo_path, "METADATA")
+        shutil.copy(pkginfo_path, metadata_path)
+
+        licenses_folder_path = os.path.join(distinfo_path, "licenses")
+        for license_path in self.license_paths:
+            safe_path = _safe_license_file(license_path)
+            dist_info_license_path = os.path.join(licenses_folder_path, safe_path)
+            os.makedirs(os.path.dirname(dist_info_license_path), exist_ok=True)
+            shutil.copy(license_path, dist_info_license_path)
+
+        adios(egginfo_path)
+
+    def _zip_compression(self) -> int:
+        if (
+            isinstance(self.compression, int)
+            and self.compression in self.supported_compressions.values()
+        ):
+            return self.compression
+
+        compression = self.supported_compressions.get(str(self.compression))
+        if compression is not None:
+            return compression
+
+        raise ValueError(f"Unsupported compression: {self.compression!r}")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/build.py b/.venv/lib/python3.12/site-packages/setuptools/command/build.py
new file mode 100644
index 0000000..54cbb8d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/build.py
@@ -0,0 +1,135 @@
+from __future__ import annotations
+
+from typing import Protocol
+
+from ..dist import Distribution
+
+from distutils.command.build import build as _build
+
+_ORIGINAL_SUBCOMMANDS = {"build_py", "build_clib", "build_ext", "build_scripts"}
+
+
+class build(_build):
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    # copy to avoid sharing the object with parent class
+    sub_commands = _build.sub_commands[:]
+
+
+class SubCommand(Protocol):
+    """In order to support editable installations (see :pep:`660`) all
+    build subcommands **SHOULD** implement this protocol. They also **MUST** inherit
+    from ``setuptools.Command``.
+
+    When creating an :pep:`editable wheel <660>`, ``setuptools`` will try to evaluate
+    custom ``build`` subcommands using the following procedure:
+
+    1. ``setuptools`` will set the ``editable_mode`` attribute to ``True``
+    2. ``setuptools`` will execute the ``run()`` command.
+
+       .. important::
+          Subcommands **SHOULD** take advantage of ``editable_mode=True`` to adequate
+          its behaviour or perform optimisations.
+
+          For example, if a subcommand doesn't need to generate an extra file and
+          all it does is to copy a source file into the build directory,
+          ``run()`` **SHOULD** simply "early return".
+
+          Similarly, if the subcommand creates files that would be placed alongside
+          Python files in the final distribution, during an editable install
+          the command **SHOULD** generate these files "in place" (i.e. write them to
+          the original source directory, instead of using the build directory).
+          Note that ``get_output_mapping()`` should reflect that and include mappings
+          for "in place" builds accordingly.
+
+    3. ``setuptools`` use any knowledge it can derive from the return values of
+       ``get_outputs()`` and ``get_output_mapping()`` to create an editable wheel.
+       When relevant ``setuptools`` **MAY** attempt to use file links based on the value
+       of ``get_output_mapping()``. Alternatively, ``setuptools`` **MAY** attempt to use
+       :doc:`import hooks ` to redirect any attempt to import
+       to the directory with the original source code and other files built in place.
+
+    Please note that custom sub-commands **SHOULD NOT** rely on ``run()`` being
+    executed (or not) to provide correct return values for ``get_outputs()``,
+    ``get_output_mapping()`` or ``get_source_files()``. The ``get_*`` methods should
+    work independently of ``run()``.
+    """
+
+    editable_mode: bool = False
+    """Boolean flag that will be set to ``True`` when setuptools is used for an
+    editable installation (see :pep:`660`).
+    Implementations **SHOULD** explicitly set the default value of this attribute to
+    ``False``.
+    When subcommands run, they can use this flag to perform optimizations or change
+    their behaviour accordingly.
+    """
+
+    build_lib: str
+    """String representing the directory where the build artifacts should be stored,
+    e.g. ``build/lib``.
+    For example, if a distribution wants to provide a Python module named ``pkg.mod``,
+    then a corresponding file should be written to ``{build_lib}/package/module.py``.
+    A way of thinking about this is that the files saved under ``build_lib``
+    would be eventually copied to one of the directories in :obj:`site.PREFIXES`
+    upon installation.
+
+    A command that produces platform-independent files (e.g. compiling text templates
+    into Python functions), **CAN** initialize ``build_lib`` by copying its value from
+    the ``build_py`` command. On the other hand, a command that produces
+    platform-specific files **CAN** initialize ``build_lib`` by copying its value from
+    the ``build_ext`` command. In general this is done inside the ``finalize_options``
+    method with the help of the ``set_undefined_options`` command::
+
+        def finalize_options(self):
+            self.set_undefined_options("build_py", ("build_lib", "build_lib"))
+            ...
+    """
+
+    def initialize_options(self) -> None:
+        """(Required by the original :class:`setuptools.Command` interface)"""
+        ...
+
+    def finalize_options(self) -> None:
+        """(Required by the original :class:`setuptools.Command` interface)"""
+        ...
+
+    def run(self) -> None:
+        """(Required by the original :class:`setuptools.Command` interface)"""
+        ...
+
+    def get_source_files(self) -> list[str]:
+        """
+        Return a list of all files that are used by the command to create the expected
+        outputs.
+        For example, if your build command transpiles Java files into Python, you should
+        list here all the Java files.
+        The primary purpose of this function is to help populating the ``sdist``
+        with all the files necessary to build the distribution.
+        All files should be strings relative to the project root directory.
+        """
+        ...
+
+    def get_outputs(self) -> list[str]:
+        """
+        Return a list of files intended for distribution as they would have been
+        produced by the build.
+        These files should be strings in the form of
+        ``"{build_lib}/destination/file/path"``.
+
+        .. note::
+           The return value of ``get_output()`` should include all files used as keys
+           in ``get_output_mapping()`` plus files that are generated during the build
+           and don't correspond to any source file already present in the project.
+        """
+        ...
+
+    def get_output_mapping(self) -> dict[str, str]:
+        """
+        Return a mapping between destination files as they would be produced by the
+        build (dict keys) into the respective existing (source) files (dict values).
+        Existing (source) files should be represented as strings relative to the project
+        root directory.
+        Destination files should be strings in the form of
+        ``"{build_lib}/destination/file/path"``.
+        """
+        ...
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/build_clib.py b/.venv/lib/python3.12/site-packages/setuptools/command/build_clib.py
new file mode 100644
index 0000000..f376f4c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/build_clib.py
@@ -0,0 +1,103 @@
+from ..dist import Distribution
+from ..modified import newer_pairwise_group
+
+import distutils.command.build_clib as orig
+from distutils import log
+from distutils.errors import DistutilsSetupError
+
+
+class build_clib(orig.build_clib):
+    """
+    Override the default build_clib behaviour to do the following:
+
+    1. Implement a rudimentary timestamp-based dependency system
+       so 'compile()' doesn't run every time.
+    2. Add more keys to the 'build_info' dictionary:
+        * obj_deps - specify dependencies for each object compiled.
+                     this should be a dictionary mapping a key
+                     with the source filename to a list of
+                     dependencies. Use an empty string for global
+                     dependencies.
+        * cflags   - specify a list of additional flags to pass to
+                     the compiler.
+    """
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def build_libraries(self, libraries) -> None:
+        for lib_name, build_info in libraries:
+            sources = build_info.get('sources')
+            if sources is None or not isinstance(sources, (list, tuple)):
+                raise DistutilsSetupError(
+                    f"in 'libraries' option (library '{lib_name}'), "
+                    "'sources' must be present and must be "
+                    "a list of source filenames"
+                )
+            sources = sorted(list(sources))
+
+            log.info("building '%s' library", lib_name)
+
+            # Make sure everything is the correct type.
+            # obj_deps should be a dictionary of keys as sources
+            # and a list/tuple of files that are its dependencies.
+            obj_deps = build_info.get('obj_deps', dict())
+            if not isinstance(obj_deps, dict):
+                raise DistutilsSetupError(
+                    f"in 'libraries' option (library '{lib_name}'), "
+                    "'obj_deps' must be a dictionary of "
+                    "type 'source: list'"
+                )
+            dependencies = []
+
+            # Get the global dependencies that are specified by the '' key.
+            # These will go into every source's dependency list.
+            global_deps = obj_deps.get('', list())
+            if not isinstance(global_deps, (list, tuple)):
+                raise DistutilsSetupError(
+                    f"in 'libraries' option (library '{lib_name}'), "
+                    "'obj_deps' must be a dictionary of "
+                    "type 'source: list'"
+                )
+
+            # Build the list to be used by newer_pairwise_group
+            # each source will be auto-added to its dependencies.
+            for source in sources:
+                src_deps = [source]
+                src_deps.extend(global_deps)
+                extra_deps = obj_deps.get(source, list())
+                if not isinstance(extra_deps, (list, tuple)):
+                    raise DistutilsSetupError(
+                        f"in 'libraries' option (library '{lib_name}'), "
+                        "'obj_deps' must be a dictionary of "
+                        "type 'source: list'"
+                    )
+                src_deps.extend(extra_deps)
+                dependencies.append(src_deps)
+
+            expected_objects = self.compiler.object_filenames(
+                sources,
+                output_dir=self.build_temp,
+            )
+
+            if newer_pairwise_group(dependencies, expected_objects) != ([], []):
+                # First, compile the source code to object files in the library
+                # directory.  (This should probably change to putting object
+                # files in a temporary build directory.)
+                macros = build_info.get('macros')
+                include_dirs = build_info.get('include_dirs')
+                cflags = build_info.get('cflags')
+                self.compiler.compile(
+                    sources,
+                    output_dir=self.build_temp,
+                    macros=macros,
+                    include_dirs=include_dirs,
+                    extra_postargs=cflags,
+                    debug=self.debug,
+                )
+
+            # Now "link" the object files together into a static library.
+            # (On Unix at least, this isn't really linking -- it just
+            # builds an archive.  Whatever.)
+            self.compiler.create_static_lib(
+                expected_objects, lib_name, output_dir=self.build_clib, debug=self.debug
+            )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/build_ext.py b/.venv/lib/python3.12/site-packages/setuptools/command/build_ext.py
new file mode 100644
index 0000000..dbb956d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/build_ext.py
@@ -0,0 +1,470 @@
+from __future__ import annotations
+
+import itertools
+import operator
+import os
+import sys
+import textwrap
+from collections.abc import Iterator
+from importlib.machinery import EXTENSION_SUFFIXES
+from importlib.util import cache_from_source as _compiled_file_name
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+from setuptools.dist import Distribution
+from setuptools.errors import BaseError
+from setuptools.extension import Extension, Library
+
+from distutils import log
+from distutils.ccompiler import new_compiler
+from distutils.sysconfig import customize_compiler, get_config_var
+
+if TYPE_CHECKING:
+    # Cython not installed on CI tests, causing _build_ext to be `Any`
+    from distutils.command.build_ext import build_ext as _build_ext
+else:
+    try:
+        # Attempt to use Cython for building extensions, if available
+        from Cython.Distutils.build_ext import build_ext as _build_ext
+
+        # Additionally, assert that the compiler module will load
+        # also. Ref #1229.
+        __import__('Cython.Compiler.Main')
+    except ImportError:
+        from distutils.command.build_ext import build_ext as _build_ext
+
+# make sure _config_vars is initialized
+get_config_var("LDSHARED")
+# Not publicly exposed in typeshed distutils stubs, but this is done on purpose
+# See https://github.com/pypa/setuptools/pull/4228#issuecomment-1959856400
+from distutils.sysconfig import _config_vars as _CONFIG_VARS  # noqa: E402
+
+
+def _customize_compiler_for_shlib(compiler):
+    if sys.platform == "darwin":
+        # building .dylib requires additional compiler flags on OSX; here we
+        # temporarily substitute the pyconfig.h variables so that distutils'
+        # 'customize_compiler' uses them before we build the shared libraries.
+        tmp = _CONFIG_VARS.copy()
+        try:
+            # XXX Help!  I don't have any idea whether these are right...
+            _CONFIG_VARS['LDSHARED'] = (
+                "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
+            )
+            _CONFIG_VARS['CCSHARED'] = " -dynamiclib"
+            _CONFIG_VARS['SO'] = ".dylib"
+            customize_compiler(compiler)
+        finally:
+            _CONFIG_VARS.clear()
+            _CONFIG_VARS.update(tmp)
+    else:
+        customize_compiler(compiler)
+
+
+have_rtld = False
+use_stubs = False
+libtype = 'shared'
+
+if sys.platform == "darwin":
+    use_stubs = True
+elif os.name != 'nt':
+    try:
+        import dl  # type: ignore[import-not-found] # https://github.com/python/mypy/issues/13002
+
+        use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW')
+    except ImportError:
+        pass
+
+
+def get_abi3_suffix():
+    """Return the file extension for an abi3-compliant Extension()"""
+    for suffix in EXTENSION_SUFFIXES:
+        if '.abi3' in suffix:  # Unix
+            return suffix
+        elif suffix == '.pyd':  # Windows
+            return suffix
+    return None
+
+
+class build_ext(_build_ext):
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+    editable_mode = False
+    inplace = False
+
+    def run(self) -> None:
+        """Build extensions in build directory, then copy if --inplace"""
+        old_inplace, self.inplace = self.inplace, False
+        _build_ext.run(self)
+        self.inplace = old_inplace
+        if old_inplace:
+            self.copy_extensions_to_source()
+
+    def _get_inplace_equivalent(self, build_py, ext: Extension) -> tuple[str, str]:
+        fullname = self.get_ext_fullname(ext.name)
+        filename = self.get_ext_filename(fullname)
+        modpath = fullname.split('.')
+        package = '.'.join(modpath[:-1])
+        package_dir = build_py.get_package_dir(package)
+        inplace_file = os.path.join(package_dir, os.path.basename(filename))
+        regular_file = os.path.join(self.build_lib, filename)
+        return (inplace_file, regular_file)
+
+    def copy_extensions_to_source(self) -> None:
+        build_py = self.get_finalized_command('build_py')
+        for ext in self.extensions:
+            inplace_file, regular_file = self._get_inplace_equivalent(build_py, ext)
+
+            # Always copy, even if source is older than destination, to ensure
+            # that the right extensions for the current Python/platform are
+            # used.
+            if os.path.exists(regular_file) or not ext.optional:
+                self.copy_file(regular_file, inplace_file, level=self.verbose)
+
+            if ext._needs_stub:
+                inplace_stub = self._get_equivalent_stub(ext, inplace_file)
+                self._write_stub_file(inplace_stub, ext, compile=True)
+                # Always compile stub and remove the original (leave the cache behind)
+                # (this behaviour was observed in previous iterations of the code)
+
+    def _get_equivalent_stub(self, ext: Extension, output_file: str) -> str:
+        dir_ = os.path.dirname(output_file)
+        _, _, name = ext.name.rpartition(".")
+        return f"{os.path.join(dir_, name)}.py"
+
+    def _get_output_mapping(self) -> Iterator[tuple[str, str]]:
+        if not self.inplace:
+            return
+
+        build_py = self.get_finalized_command('build_py')
+        opt = self.get_finalized_command('install_lib').optimize or ""
+
+        for ext in self.extensions:
+            inplace_file, regular_file = self._get_inplace_equivalent(build_py, ext)
+            yield (regular_file, inplace_file)
+
+            if ext._needs_stub:
+                # This version of `build_ext` always builds artifacts in another dir,
+                # when "inplace=True" is given it just copies them back.
+                # This is done in the `copy_extensions_to_source` function, which
+                # always compile stub files via `_compile_and_remove_stub`.
+                # At the end of the process, a `.pyc` stub file is created without the
+                # corresponding `.py`.
+
+                inplace_stub = self._get_equivalent_stub(ext, inplace_file)
+                regular_stub = self._get_equivalent_stub(ext, regular_file)
+                inplace_cache = _compiled_file_name(inplace_stub, optimization=opt)
+                output_cache = _compiled_file_name(regular_stub, optimization=opt)
+                yield (output_cache, inplace_cache)
+
+    def get_ext_filename(self, fullname: str) -> str:
+        so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX')
+        if so_ext:
+            filename = os.path.join(*fullname.split('.')) + so_ext
+        else:
+            filename = _build_ext.get_ext_filename(self, fullname)
+            ext_suffix = get_config_var('EXT_SUFFIX')
+            if not isinstance(ext_suffix, str):
+                raise OSError(
+                    "Configuration variable EXT_SUFFIX not found for this platform "
+                    "and environment variable SETUPTOOLS_EXT_SUFFIX is missing"
+                )
+            so_ext = ext_suffix
+
+        if fullname in self.ext_map:
+            ext = self.ext_map[fullname]
+            abi3_suffix = get_abi3_suffix()
+            if ext.py_limited_api and abi3_suffix:  # Use abi3
+                filename = filename[: -len(so_ext)] + abi3_suffix
+            if isinstance(ext, Library):
+                fn, ext = os.path.splitext(filename)
+                return self.shlib_compiler.library_filename(fn, libtype)
+            elif use_stubs and ext._links_to_dynamic:
+                d, fn = os.path.split(filename)
+                return os.path.join(d, 'dl-' + fn)
+        return filename
+
+    def initialize_options(self):
+        _build_ext.initialize_options(self)
+        self.shlib_compiler = None
+        self.shlibs = []
+        self.ext_map = {}
+        self.editable_mode = False
+
+    def finalize_options(self) -> None:
+        _build_ext.finalize_options(self)
+        self.extensions = self.extensions or []
+        self.check_extensions_list(self.extensions)
+        self.shlibs = [ext for ext in self.extensions if isinstance(ext, Library)]
+        if self.shlibs:
+            self.setup_shlib_compiler()
+        for ext in self.extensions:
+            ext._full_name = self.get_ext_fullname(ext.name)
+        for ext in self.extensions:
+            fullname = ext._full_name
+            self.ext_map[fullname] = ext
+
+            # distutils 3.1 will also ask for module names
+            # XXX what to do with conflicts?
+            self.ext_map[fullname.split('.')[-1]] = ext
+
+            ltd = self.shlibs and self.links_to_dynamic(ext) or False
+            ns = ltd and use_stubs and not isinstance(ext, Library)
+            ext._links_to_dynamic = ltd
+            ext._needs_stub = ns
+            filename = ext._file_name = self.get_ext_filename(fullname)
+            libdir = os.path.dirname(os.path.join(self.build_lib, filename))
+            if ltd and libdir not in ext.library_dirs:
+                ext.library_dirs.append(libdir)
+            if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
+                ext.runtime_library_dirs.append(os.curdir)
+
+        if self.editable_mode:
+            self.inplace = True
+
+    def setup_shlib_compiler(self) -> None:
+        compiler = self.shlib_compiler = new_compiler(
+            compiler=self.compiler, force=self.force
+        )
+        _customize_compiler_for_shlib(compiler)
+
+        if self.include_dirs is not None:
+            compiler.set_include_dirs(self.include_dirs)
+        if self.define is not None:
+            # 'define' option is a list of (name,value) tuples
+            for name, value in self.define:
+                compiler.define_macro(name, value)
+        if self.undef is not None:
+            for macro in self.undef:
+                compiler.undefine_macro(macro)
+        if self.libraries is not None:
+            compiler.set_libraries(self.libraries)
+        if self.library_dirs is not None:
+            compiler.set_library_dirs(self.library_dirs)
+        if self.rpath is not None:
+            compiler.set_runtime_library_dirs(self.rpath)
+        if self.link_objects is not None:
+            compiler.set_link_objects(self.link_objects)
+
+        # hack so distutils' build_extension() builds a library instead
+        compiler.link_shared_object = link_shared_object.__get__(compiler)  # type: ignore[method-assign]
+
+    def get_export_symbols(self, ext):
+        if isinstance(ext, Library):
+            return ext.export_symbols
+        return _build_ext.get_export_symbols(self, ext)
+
+    def build_extension(self, ext) -> None:
+        ext._convert_pyx_sources_to_lang()
+        _compiler = self.compiler
+        try:
+            if isinstance(ext, Library):
+                self.compiler = self.shlib_compiler
+            _build_ext.build_extension(self, ext)
+            if ext._needs_stub:
+                build_lib = self.get_finalized_command('build_py').build_lib
+                self.write_stub(build_lib, ext)
+        finally:
+            self.compiler = _compiler
+
+    def links_to_dynamic(self, ext):
+        """Return true if 'ext' links to a dynamic lib in the same package"""
+        # XXX this should check to ensure the lib is actually being built
+        # XXX as dynamic, and not just using a locally-found version or a
+        # XXX static-compiled version
+        libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
+        pkg = '.'.join(ext._full_name.split('.')[:-1] + [''])
+        return any(pkg + libname in libnames for libname in ext.libraries)
+
+    def get_source_files(self) -> list[str]:
+        return [*_build_ext.get_source_files(self), *self._get_internal_depends()]
+
+    def _get_internal_depends(self) -> Iterator[str]:
+        """Yield ``ext.depends`` that are contained by the project directory"""
+        project_root = Path(self.distribution.src_root or os.curdir).resolve()
+        depends = (dep for ext in self.extensions for dep in ext.depends)
+
+        def skip(orig_path: str, reason: str) -> None:
+            log.info(
+                "dependency %s won't be automatically "
+                "included in the manifest: the path %s",
+                orig_path,
+                reason,
+            )
+
+        for dep in depends:
+            path = Path(dep)
+
+            if path.is_absolute():
+                skip(dep, "must be relative")
+                continue
+
+            if ".." in path.parts:
+                skip(dep, "can't have `..` segments")
+                continue
+
+            try:
+                resolved = (project_root / path).resolve(strict=True)
+            except OSError:
+                skip(dep, "doesn't exist")
+                continue
+
+            try:
+                resolved.relative_to(project_root)
+            except ValueError:
+                skip(dep, "must be inside the project root")
+                continue
+
+            yield path.as_posix()
+
+    def get_outputs(self) -> list[str]:
+        if self.inplace:
+            return list(self.get_output_mapping().keys())
+        return sorted(_build_ext.get_outputs(self) + self.__get_stubs_outputs())
+
+    def get_output_mapping(self) -> dict[str, str]:
+        """See :class:`setuptools.commands.build.SubCommand`"""
+        mapping = self._get_output_mapping()
+        return dict(sorted(mapping, key=operator.itemgetter(0)))
+
+    def __get_stubs_outputs(self):
+        # assemble the base name for each extension that needs a stub
+        ns_ext_bases = (
+            os.path.join(self.build_lib, *ext._full_name.split('.'))
+            for ext in self.extensions
+            if ext._needs_stub
+        )
+        # pair each base with the extension
+        pairs = itertools.product(ns_ext_bases, self.__get_output_extensions())
+        return list(base + fnext for base, fnext in pairs)
+
+    def __get_output_extensions(self):
+        yield '.py'
+        yield '.pyc'
+        if self.get_finalized_command('build_py').optimize:
+            yield '.pyo'
+
+    def write_stub(self, output_dir, ext, compile=False) -> None:
+        stub_file = os.path.join(output_dir, *ext._full_name.split('.')) + '.py'
+        self._write_stub_file(stub_file, ext, compile)
+
+    def _write_stub_file(self, stub_file: str, ext: Extension, compile=False):
+        log.info("writing stub loader for %s to %s", ext._full_name, stub_file)
+        if compile and os.path.exists(stub_file):
+            raise BaseError(stub_file + " already exists! Please delete.")
+        with open(stub_file, 'w', encoding="utf-8") as f:
+            content = (
+                textwrap
+                .dedent(f"""
+                def __bootstrap__():
+                   global __bootstrap__, __file__, __loader__
+                   import sys, os, importlib.resources as irs, importlib.util
+                #rtld   import dl
+                   with irs.files(__name__).joinpath(
+                     {os.path.basename(ext._file_name)!r}) as __file__:
+                      del __bootstrap__
+                      if '__loader__' in globals():
+                          del __loader__
+                #rtld      old_flags = sys.getdlopenflags()
+                      old_dir = os.getcwd()
+                      try:
+                        os.chdir(os.path.dirname(__file__))
+                #rtld        sys.setdlopenflags(dl.RTLD_NOW)
+                        spec = importlib.util.spec_from_file_location(
+                                   __name__, __file__)
+                        mod = importlib.util.module_from_spec(spec)
+                        spec.loader.exec_module(mod)
+                      finally:
+                #rtld        sys.setdlopenflags(old_flags)
+                        os.chdir(old_dir)
+                __bootstrap__()
+                """)
+                .lstrip()
+                .replace('#rtld', '#rtld' * (not have_rtld))
+            )
+            f.write(content)
+        if compile:
+            self._compile_and_remove_stub(stub_file)
+
+    def _compile_and_remove_stub(self, stub_file: str):
+        from distutils.util import byte_compile
+
+        byte_compile([stub_file], optimize=0, force=True)
+        optimize = self.get_finalized_command('install_lib').optimize
+        if optimize > 0:
+            byte_compile(
+                [stub_file],
+                optimize=optimize,
+                force=True,
+            )
+        if os.path.exists(stub_file):
+            os.unlink(stub_file)
+
+
+if use_stubs or os.name == 'nt':
+    # Build shared libraries
+    #
+    def link_shared_object(
+        self,
+        objects,
+        output_libname,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        export_symbols=None,
+        debug: bool = False,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ) -> None:
+        self.link(
+            self.SHARED_LIBRARY,
+            objects,
+            output_libname,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+else:
+    # Build static libraries everywhere else
+    libtype = 'static'
+
+    def link_shared_object(
+        self,
+        objects,
+        output_libname,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        export_symbols=None,
+        debug: bool = False,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ) -> None:
+        # XXX we need to either disallow these attrs on Library instances,
+        # or warn/abort here if set, or something...
+        # libraries=None, library_dirs=None, runtime_library_dirs=None,
+        # export_symbols=None, extra_preargs=None, extra_postargs=None,
+        # build_temp=None
+
+        assert output_dir is None  # distutils build_ext doesn't pass this
+        output_dir, filename = os.path.split(output_libname)
+        basename, _ext = os.path.splitext(filename)
+        if self.library_filename("x").startswith('lib'):
+            # strip 'lib' prefix; this is kludgy if some platform uses
+            # a different prefix
+            basename = basename[3:]
+
+        self.create_static_lib(objects, basename, output_dir, debug, target_lang)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/build_py.py b/.venv/lib/python3.12/site-packages/setuptools/command/build_py.py
new file mode 100644
index 0000000..3c7c2d1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/build_py.py
@@ -0,0 +1,403 @@
+from __future__ import annotations
+
+import fnmatch
+import itertools
+import operator
+import os
+import stat
+import textwrap
+from collections.abc import Iterable, Iterator
+from functools import partial
+from glob import glob
+from pathlib import Path
+from typing import Any
+
+from more_itertools import unique_everseen
+
+from .._path import StrPath, StrPathT
+from ..dist import Distribution
+from ..warnings import SetuptoolsDeprecationWarning
+
+import distutils.command.build_py as orig
+import distutils.errors
+from distutils.util import convert_path
+
+_IMPLICIT_DATA_FILES = ('*.pyi', 'py.typed')
+
+
+def make_writable(target) -> None:
+    os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE)
+
+
+class build_py(orig.build_py):
+    """Enhanced 'build_py' command that includes data files with packages
+
+    The data files are specified via a 'package_data' argument to 'setup()'.
+    See 'setuptools.dist.Distribution' for more details.
+
+    Also, this version of the 'build_py' command allows you to specify both
+    'py_modules' and 'packages' in the same setup operation.
+    """
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+    editable_mode: bool = False
+    existing_egg_info_dir: StrPath | None = None  #: Private API, internal use only.
+
+    def finalize_options(self) -> None:
+        orig.build_py.finalize_options(self)
+        self.package_data = self.distribution.package_data
+        self.exclude_package_data = self.distribution.exclude_package_data or {}
+        if 'data_files' in self.__dict__:
+            del self.__dict__['data_files']
+
+    def copy_file(  # type: ignore[override] # No overload, no bytes support
+        self,
+        infile: StrPath,
+        outfile: StrPathT,
+        preserve_mode: bool = True,
+        preserve_times: bool = True,
+        link: str | None = None,
+        level: object = 1,
+    ) -> tuple[StrPathT | str, bool]:
+        # Overwrite base class to allow using links
+        if link:
+            infile = str(Path(infile).resolve())
+            outfile = str(Path(outfile).resolve())  # type: ignore[assignment] # Re-assigning a str when outfile is StrPath is ok
+        return super().copy_file(  # pyright: ignore[reportReturnType] # pypa/distutils#309
+            infile, outfile, preserve_mode, preserve_times, link, level
+        )
+
+    def run(self) -> None:
+        """Build modules, packages, and copy data files to build directory"""
+        if not (self.py_modules or self.packages) or self.editable_mode:
+            return
+
+        if self.py_modules:
+            self.build_modules()
+
+        if self.packages:
+            self.build_packages()
+            self.build_package_data()
+
+        # Only compile actual .py files, using our base class' idea of what our
+        # output files are.
+        self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=False))
+
+    # Should return "list[tuple[str, str, str, list[str]]] | Any" but can't do without typed distutils on Python 3.12+
+    def __getattr__(self, attr: str) -> Any:
+        "lazily compute data files"
+        if attr == 'data_files':
+            self.data_files = self._get_data_files()
+            return self.data_files
+        return orig.build_py.__getattr__(self, attr)
+
+    def _get_data_files(self):
+        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+        self.analyze_manifest()
+        return list(map(self._get_pkg_data_files, self.packages or ()))
+
+    def get_data_files_without_manifest(self) -> list[tuple[str, str, str, list[str]]]:
+        """
+        Generate list of ``(package,src_dir,build_dir,filenames)`` tuples,
+        but without triggering any attempt to analyze or build the manifest.
+        """
+        # Prevent eventual errors from unset `manifest_files`
+        # (that would otherwise be set by `analyze_manifest`)
+        self.__dict__.setdefault('manifest_files', {})
+        return list(map(self._get_pkg_data_files, self.packages or ()))
+
+    def _get_pkg_data_files(self, package: str) -> tuple[str, str, str, list[str]]:
+        # Locate package source directory
+        src_dir = self.get_package_dir(package)
+
+        # Compute package build directory
+        build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+
+        # Strip directory from globbed filenames
+        filenames = [
+            os.path.relpath(file, src_dir)
+            for file in self.find_data_files(package, src_dir)
+        ]
+        return package, src_dir, build_dir, filenames
+
+    def find_data_files(self, package, src_dir):
+        """Return filenames for package's data files in 'src_dir'"""
+        patterns = self._get_platform_patterns(
+            self.package_data,
+            package,
+            src_dir,
+            extra_patterns=_IMPLICIT_DATA_FILES,
+        )
+        globs_expanded = map(partial(glob, recursive=True), patterns)
+        # flatten the expanded globs into an iterable of matches
+        globs_matches = itertools.chain.from_iterable(globs_expanded)
+        glob_files = filter(os.path.isfile, globs_matches)
+        files = itertools.chain(
+            self.manifest_files.get(package, []),
+            glob_files,
+        )
+        return self.exclude_data_files(package, src_dir, files)
+
+    def get_outputs(self, include_bytecode: bool = True) -> list[str]:  # type: ignore[override] # Using a real boolean instead of 0|1
+        """See :class:`setuptools.commands.build.SubCommand`"""
+        if self.editable_mode:
+            return list(self.get_output_mapping().keys())
+        return super().get_outputs(include_bytecode)
+
+    def get_output_mapping(self) -> dict[str, str]:
+        """See :class:`setuptools.commands.build.SubCommand`"""
+        mapping = itertools.chain(
+            self._get_package_data_output_mapping(),
+            self._get_module_mapping(),
+        )
+        return dict(sorted(mapping, key=operator.itemgetter(0)))
+
+    def _get_module_mapping(self) -> Iterator[tuple[str, str]]:
+        """Iterate over all modules producing (dest, src) pairs."""
+        for package, module, module_file in self.find_all_modules():
+            package = package.split('.')
+            filename = self.get_module_outfile(self.build_lib, package, module)
+            yield (filename, module_file)
+
+    def _get_package_data_output_mapping(self) -> Iterator[tuple[str, str]]:
+        """Iterate over package data producing (dest, src) pairs."""
+        for package, src_dir, build_dir, filenames in self.data_files:
+            for filename in filenames:
+                target = os.path.join(build_dir, filename)
+                srcfile = os.path.join(src_dir, filename)
+                yield (target, srcfile)
+
+    def build_package_data(self) -> None:
+        """Copy data files into build directory"""
+        for target, srcfile in self._get_package_data_output_mapping():
+            self.mkpath(os.path.dirname(target))
+            _outf, _copied = self.copy_file(srcfile, target)
+            make_writable(target)
+
+    def analyze_manifest(self) -> None:
+        self.manifest_files: dict[str, list[str]] = {}
+        if not self.distribution.include_package_data:
+            return
+        src_dirs: dict[str, str] = {}
+        for package in self.packages or ():
+            # Locate package source directory
+            src_dirs[assert_relative(self.get_package_dir(package))] = package
+
+        if (
+            self.existing_egg_info_dir
+            and Path(self.existing_egg_info_dir, "SOURCES.txt").exists()
+        ):
+            egg_info_dir = self.existing_egg_info_dir
+            manifest = Path(egg_info_dir, "SOURCES.txt")
+            files = manifest.read_text(encoding="utf-8").splitlines()
+        else:
+            self.run_command('egg_info')
+            ei_cmd = self.get_finalized_command('egg_info')
+            egg_info_dir = ei_cmd.egg_info
+            files = ei_cmd.filelist.files
+
+        check = _IncludePackageDataAbuse()
+        for path in self._filter_build_files(files, egg_info_dir):
+            d, f = os.path.split(assert_relative(path))
+            prev = None
+            oldf = f
+            while d and d != prev and d not in src_dirs:
+                prev = d
+                d, df = os.path.split(d)
+                f = os.path.join(df, f)
+            if d in src_dirs:
+                if f == oldf:
+                    if check.is_module(f):
+                        continue  # it's a module, not data
+                else:
+                    importable = check.importable_subpackage(src_dirs[d], f)
+                    if importable:
+                        check.warn(importable)
+                self.manifest_files.setdefault(src_dirs[d], []).append(path)
+
+    def _filter_build_files(
+        self, files: Iterable[str], egg_info: StrPath
+    ) -> Iterator[str]:
+        """
+        ``build_meta`` may try to create egg_info outside of the project directory,
+        and this can be problematic for certain plugins (reported in issue #3500).
+
+        Extensions might also include between their sources files created on the
+        ``build_lib`` and ``build_temp`` directories.
+
+        This function should filter this case of invalid files out.
+        """
+        build = self.get_finalized_command("build")
+        build_dirs = (egg_info, self.build_lib, build.build_temp, build.build_base)
+        norm_dirs = [os.path.normpath(p) for p in build_dirs if p]
+
+        for file in files:
+            norm_path = os.path.normpath(file)
+            if not os.path.isabs(file) or all(d not in norm_path for d in norm_dirs):
+                yield file
+
+    def get_data_files(self) -> None:
+        pass  # Lazily compute data files in _get_data_files() function.
+
+    def check_package(self, package, package_dir):
+        """Check namespace packages' __init__ for declare_namespace"""
+        try:
+            return self.packages_checked[package]
+        except KeyError:
+            pass
+
+        init_py = orig.build_py.check_package(self, package, package_dir)
+        self.packages_checked[package] = init_py
+
+        if not init_py or not self.distribution.namespace_packages:
+            return init_py
+
+        for pkg in self.distribution.namespace_packages:
+            if pkg == package or pkg.startswith(package + '.'):
+                break
+        else:
+            return init_py
+
+        with open(init_py, 'rb') as f:
+            contents = f.read()
+        if b'declare_namespace' not in contents:
+            raise distutils.errors.DistutilsError(
+                f"Namespace package problem: {package} is a namespace package, but "
+                "its\n__init__.py does not call declare_namespace()! Please "
+                'fix it.\n(See the setuptools manual under '
+                '"Namespace Packages" for details.)\n"'
+            )
+        return init_py
+
+    def initialize_options(self):
+        self.packages_checked = {}
+        orig.build_py.initialize_options(self)
+        self.editable_mode = False
+        self.existing_egg_info_dir = None
+
+    def get_package_dir(self, package: str) -> str:
+        res = orig.build_py.get_package_dir(self, package)
+        if self.distribution.src_root is not None:
+            return os.path.join(self.distribution.src_root, res)
+        return res
+
+    def exclude_data_files(self, package, src_dir, files):
+        """Filter filenames for package's data files in 'src_dir'"""
+        files = list(files)
+        patterns = self._get_platform_patterns(
+            self.exclude_package_data,
+            package,
+            src_dir,
+        )
+        match_groups = (fnmatch.filter(files, pattern) for pattern in patterns)
+        # flatten the groups of matches into an iterable of matches
+        matches = itertools.chain.from_iterable(match_groups)
+        bad = set(matches)
+        keepers = (fn for fn in files if fn not in bad)
+        # ditch dupes
+        return list(unique_everseen(keepers))
+
+    @staticmethod
+    def _get_platform_patterns(spec, package, src_dir, extra_patterns=()):
+        """
+        yield platform-specific path patterns (suitable for glob
+        or fn_match) from a glob-based spec (such as
+        self.package_data or self.exclude_package_data)
+        matching package in src_dir.
+        """
+        raw_patterns = itertools.chain(
+            extra_patterns,
+            spec.get('', []),
+            spec.get(package, []),
+        )
+        return (
+            # Each pattern has to be converted to a platform-specific path
+            os.path.join(src_dir, convert_path(pattern))
+            for pattern in raw_patterns
+        )
+
+
+def assert_relative(path):
+    if not os.path.isabs(path):
+        return path
+    from distutils.errors import DistutilsSetupError
+
+    msg = (
+        textwrap.dedent(
+            """
+        Error: setup script specifies an absolute path:
+
+            %s
+
+        setup() arguments must *always* be /-separated paths relative to the
+        setup.py directory, *never* absolute paths.
+        """
+        ).lstrip()
+        % path
+    )
+    raise DistutilsSetupError(msg)
+
+
+class _IncludePackageDataAbuse:
+    """Inform users that package or module is included as 'data file'"""
+
+    class _Warning(SetuptoolsDeprecationWarning):
+        _SUMMARY = """
+        Package {importable!r} is absent from the `packages` configuration.
+        """
+
+        _DETAILS = """
+        ############################
+        # Package would be ignored #
+        ############################
+        Python recognizes {importable!r} as an importable package[^1],
+        but it is absent from setuptools' `packages` configuration.
+
+        This leads to an ambiguous overall configuration. If you want to distribute this
+        package, please make sure that {importable!r} is explicitly added
+        to the `packages` configuration field.
+
+        Alternatively, you can also rely on setuptools' discovery methods
+        (for example by using `find_namespace_packages(...)`/`find_namespace:`
+        instead of `find_packages(...)`/`find:`).
+
+        You can read more about "package discovery" on setuptools documentation page:
+
+        - https://setuptools.pypa.io/en/latest/userguide/package_discovery.html
+
+        If you don't want {importable!r} to be distributed and are
+        already explicitly excluding {importable!r} via
+        `find_namespace_packages(...)/find_namespace` or `find_packages(...)/find`,
+        you can try to use `exclude_package_data`, or `include-package-data=False` in
+        combination with a more fine grained `package-data` configuration.
+
+        You can read more about "package data files" on setuptools documentation page:
+
+        - https://setuptools.pypa.io/en/latest/userguide/datafiles.html
+
+
+        [^1]: For Python, any directory (with suitable naming) can be imported,
+              even if it does not contain any `.py` files.
+              On the other hand, currently there is no concept of package data
+              directory, all directories are treated like packages.
+        """
+        # _DUE_DATE: still not defined as this is particularly controversial.
+        # Warning initially introduced in May 2022. See issue #3340 for discussion.
+
+    def __init__(self) -> None:
+        self._already_warned = set[str]()
+
+    def is_module(self, file):
+        return file.endswith(".py") and file[: -len(".py")].isidentifier()
+
+    def importable_subpackage(self, parent, file):
+        pkg = Path(file).parent
+        parts = list(itertools.takewhile(str.isidentifier, pkg.parts))
+        if parts:
+            return ".".join([parent, *parts])
+        return None
+
+    def warn(self, importable):
+        if importable not in self._already_warned:
+            self._Warning.emit(importable=importable)
+            self._already_warned.add(importable)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/develop.py b/.venv/lib/python3.12/site-packages/setuptools/command/develop.py
new file mode 100644
index 0000000..2d46884
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/develop.py
@@ -0,0 +1,58 @@
+import site
+import subprocess
+import sys
+from typing import cast
+
+from setuptools import Command
+from setuptools.warnings import SetuptoolsDeprecationWarning
+
+
+class develop(Command):
+    """Set up package for development"""
+
+    user_options = [
+        ("install-dir=", "d", "install package to DIR"),
+        ('no-deps', 'N', "don't install dependencies"),
+        ('user', None, f"install in user site-package '{site.USER_SITE}'"),
+        ('prefix=', None, "installation prefix"),
+        ("index-url=", "i", "base URL of Python Package Index"),
+    ]
+    boolean_options = [
+        'no-deps',
+        'user',
+    ]
+
+    install_dir = None
+    no_deps = False
+    user = False
+    prefix = None
+    index_url = None
+
+    def run(self) -> None:
+        # Casting because mypy doesn't understand bool mult conditionals
+        cmd = cast(
+            list[str],
+            [sys.executable, '-m', 'pip', 'install', '-e', '.', '--use-pep517']
+            + ['--target', self.install_dir] * bool(self.install_dir)
+            + ['--no-deps'] * self.no_deps
+            + ['--user'] * self.user
+            + ['--prefix', self.prefix] * bool(self.prefix)
+            + ['--index-url', self.index_url] * bool(self.index_url),
+        )
+        subprocess.check_call(cmd)
+
+    def initialize_options(self) -> None:
+        DevelopDeprecationWarning.emit()
+
+    def finalize_options(self) -> None:
+        pass
+
+
+class DevelopDeprecationWarning(SetuptoolsDeprecationWarning):
+    _SUMMARY = "develop command is deprecated."
+    _DETAILS = """
+    Please avoid running ``setup.py`` and ``develop``.
+    Instead, use standards-based tools like pip or uv.
+    """
+    _SEE_URL = "https://github.com/pypa/setuptools/issues/917"
+    _DUE_DATE = 2025, 10, 31
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/dist_info.py b/.venv/lib/python3.12/site-packages/setuptools/command/dist_info.py
new file mode 100644
index 0000000..dca01ff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/dist_info.py
@@ -0,0 +1,103 @@
+"""
+Create a dist_info directory
+As defined in the wheel specification
+"""
+
+import os
+import shutil
+from contextlib import contextmanager
+from pathlib import Path
+from typing import cast
+
+from .. import _normalization
+from .._shutil import rmdir as _rm
+from .egg_info import egg_info as egg_info_cls
+
+from distutils import log
+from distutils.core import Command
+
+
+class dist_info(Command):
+    """
+    This command is private and reserved for internal use of setuptools,
+    users should rely on ``setuptools.build_meta`` APIs.
+    """
+
+    description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directory"
+
+    user_options = [
+        (
+            'output-dir=',
+            'o',
+            "directory inside of which the .dist-info will be"
+            "created [default: top of the source tree]",
+        ),
+        ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
+        ('tag-build=', 'b', "Specify explicit tag to add to version number"),
+        ('no-date', 'D', "Don't include date stamp [default]"),
+        ('keep-egg-info', None, "*TRANSITIONAL* will be removed in the future"),
+    ]
+
+    boolean_options = ['tag-date', 'keep-egg-info']
+    negative_opt = {'no-date': 'tag-date'}
+
+    def initialize_options(self):
+        self.output_dir = None
+        self.name = None
+        self.dist_info_dir = None
+        self.tag_date = None
+        self.tag_build = None
+        self.keep_egg_info = False
+
+    def finalize_options(self) -> None:
+        dist = self.distribution
+        project_dir = dist.src_root or os.curdir
+        self.output_dir = Path(self.output_dir or project_dir)
+
+        egg_info = cast(egg_info_cls, self.reinitialize_command("egg_info"))
+        egg_info.egg_base = str(self.output_dir)
+
+        if self.tag_date:
+            egg_info.tag_date = self.tag_date
+        else:
+            self.tag_date = egg_info.tag_date
+
+        if self.tag_build:
+            egg_info.tag_build = self.tag_build
+        else:
+            self.tag_build = egg_info.tag_build
+
+        egg_info.finalize_options()
+        self.egg_info = egg_info
+
+        name = _normalization.safer_name(dist.get_name())
+        version = _normalization.safer_best_effort_version(dist.get_version())
+        self.name = f"{name}-{version}"
+        self.dist_info_dir = os.path.join(self.output_dir, f"{self.name}.dist-info")
+
+    @contextmanager
+    def _maybe_bkp_dir(self, dir_path: str, requires_bkp: bool):
+        if requires_bkp:
+            bkp_name = f"{dir_path}.__bkp__"
+            _rm(bkp_name, ignore_errors=True)
+            shutil.copytree(dir_path, bkp_name, dirs_exist_ok=True, symlinks=True)
+            try:
+                yield
+            finally:
+                _rm(dir_path, ignore_errors=True)
+                shutil.move(bkp_name, dir_path)
+        else:
+            yield
+
+    def run(self) -> None:
+        self.output_dir.mkdir(parents=True, exist_ok=True)
+        self.egg_info.run()
+        egg_info_dir = self.egg_info.egg_info
+        assert os.path.isdir(egg_info_dir), ".egg-info dir should have been created"
+
+        log.info(f"creating '{os.path.abspath(self.dist_info_dir)}'")
+        bdist_wheel = self.get_finalized_command('bdist_wheel')
+
+        # TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there
+        with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info):
+            bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/easy_install.py b/.venv/lib/python3.12/site-packages/setuptools/command/easy_install.py
new file mode 100644
index 0000000..8765793
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/easy_install.py
@@ -0,0 +1,30 @@
+import os
+import sys
+import types
+
+from setuptools import Command
+
+from .. import _scripts, warnings
+
+
+class easy_install(Command):
+    """Stubbed command for temporary pbr compatibility."""
+
+
+def __getattr__(name):
+    attr = getattr(
+        types.SimpleNamespace(
+            ScriptWriter=_scripts.ScriptWriter,
+            sys_executable=os.environ.get(
+                "__PYVENV_LAUNCHER__", os.path.normpath(sys.executable)
+            ),
+        ),
+        name,
+    )
+    warnings.SetuptoolsDeprecationWarning.emit(
+        summary="easy_install module is deprecated",
+        details="Avoid accessing attributes of setuptools.command.easy_install.",
+        due_date=(2025, 10, 31),
+        see_url="https://github.com/pypa/setuptools/issues/4976",
+    )
+    return attr
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/editable_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/command/editable_wheel.py
new file mode 100644
index 0000000..6f44f13
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/editable_wheel.py
@@ -0,0 +1,914 @@
+"""
+Create a wheel that, when installed, will make the source package 'editable'
+(add it to the interpreter's path, including metadata) per PEP 660. Replaces
+'setup.py develop'.
+
+.. note::
+   One of the mechanisms briefly mentioned in PEP 660 to implement editable installs is
+   to create a separated directory inside ``build`` and use a .pth file to point to that
+   directory. In the context of this file such directory is referred as
+   *auxiliary build directory* or ``auxiliary_dir``.
+"""
+
+from __future__ import annotations
+
+import io
+import logging
+import operator
+import os
+import shutil
+import traceback
+from collections.abc import Iterable, Iterator, Mapping
+from contextlib import suppress
+from enum import Enum
+from inspect import cleandoc
+from itertools import chain, starmap
+from pathlib import Path
+from tempfile import TemporaryDirectory
+from types import TracebackType
+from typing import TYPE_CHECKING, Protocol, TypeVar, cast
+
+from .. import Command, _normalization, _path, _shutil, errors, namespaces
+from .._path import StrPath
+from ..compat import py310, py312
+from ..discovery import find_package_path
+from ..dist import Distribution
+from ..warnings import InformationOnly, SetuptoolsDeprecationWarning
+from .build import build as build_cls
+from .build_py import build_py as build_py_cls
+from .dist_info import dist_info as dist_info_cls
+from .egg_info import egg_info as egg_info_cls
+from .install import install as install_cls
+from .install_scripts import install_scripts as install_scripts_cls
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+    from .._vendor.wheel.wheelfile import WheelFile
+
+_P = TypeVar("_P", bound=StrPath)
+_logger = logging.getLogger(__name__)
+
+
+class _EditableMode(Enum):
+    """
+    Possible editable installation modes:
+    `lenient` (new files automatically added to the package - DEFAULT);
+    `strict` (requires a new installation when files are added/removed); or
+    `compat` (attempts to emulate `python setup.py develop` - DEPRECATED).
+    """
+
+    STRICT = "strict"
+    LENIENT = "lenient"
+    COMPAT = "compat"  # TODO: Remove `compat` after Dec/2022.
+
+    @classmethod
+    def convert(cls, mode: str | None) -> _EditableMode:
+        if not mode:
+            return _EditableMode.LENIENT  # default
+
+        _mode = mode.upper()
+        if _mode not in _EditableMode.__members__:
+            raise errors.OptionError(f"Invalid editable mode: {mode!r}. Try: 'strict'.")
+
+        if _mode == "COMPAT":
+            SetuptoolsDeprecationWarning.emit(
+                "Compat editable installs",
+                """
+                The 'compat' editable mode is transitional and will be removed
+                in future versions of `setuptools`.
+                Please adapt your code accordingly to use either the 'strict' or the
+                'lenient' modes.
+                """,
+                see_docs="userguide/development_mode.html",
+                # TODO: define due_date
+                # There is a series of shortcomings with the available editable install
+                # methods, and they are very controversial. This is something that still
+                # needs work.
+                # Moreover, `pip` is still hiding this warning, so users are not aware.
+            )
+
+        return _EditableMode[_mode]
+
+
+_STRICT_WARNING = """
+New or renamed files may not be automatically picked up without a new installation.
+"""
+
+_LENIENT_WARNING = """
+Options like `package-data`, `include/exclude-package-data` or
+`packages.find.exclude/include` may have no effect.
+"""
+
+
+class editable_wheel(Command):
+    """Build 'editable' wheel for development.
+    This command is private and reserved for internal use of setuptools,
+    users should rely on ``setuptools.build_meta`` APIs.
+    """
+
+    description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheel"
+
+    user_options = [
+        ("dist-dir=", "d", "directory to put final built distributions in"),
+        ("dist-info-dir=", "I", "path to a pre-build .dist-info directory"),
+        ("mode=", None, cleandoc(_EditableMode.__doc__ or "")),
+    ]
+
+    def initialize_options(self):
+        self.dist_dir = None
+        self.dist_info_dir = None
+        self.project_dir = None
+        self.mode = None
+
+    def finalize_options(self) -> None:
+        dist = self.distribution
+        self.project_dir = dist.src_root or os.curdir
+        self.package_dir = dist.package_dir or {}
+        self.dist_dir = Path(self.dist_dir or os.path.join(self.project_dir, "dist"))
+
+    def run(self) -> None:
+        try:
+            self.dist_dir.mkdir(exist_ok=True)
+            self._ensure_dist_info()
+
+            # Add missing dist_info files
+            self.reinitialize_command("bdist_wheel")
+            bdist_wheel = self.get_finalized_command("bdist_wheel")
+            bdist_wheel.write_wheelfile(self.dist_info_dir)
+
+            self._create_wheel_file(bdist_wheel)
+        except Exception as ex:
+            project = self.distribution.name or self.distribution.get_name()
+            py310.add_note(
+                ex,
+                f"An error occurred when building editable wheel for {project}.\n"
+                "See debugging tips in: "
+                "https://setuptools.pypa.io/en/latest/userguide/development_mode.html#debugging-tips",
+            )
+            raise
+
+    def _ensure_dist_info(self):
+        if self.dist_info_dir is None:
+            dist_info = cast(dist_info_cls, self.reinitialize_command("dist_info"))
+            dist_info.output_dir = self.dist_dir
+            dist_info.ensure_finalized()
+            dist_info.run()
+            self.dist_info_dir = dist_info.dist_info_dir
+        else:
+            assert str(self.dist_info_dir).endswith(".dist-info")
+            assert Path(self.dist_info_dir, "METADATA").exists()
+
+    def _install_namespaces(self, installation_dir, pth_prefix):
+        # XXX: Only required to support the deprecated namespace practice
+        dist = self.distribution
+        if not dist.namespace_packages:
+            return
+
+        src_root = Path(self.project_dir, self.package_dir.get("", ".")).resolve()
+        installer = _NamespaceInstaller(dist, installation_dir, pth_prefix, src_root)
+        installer.install_namespaces()
+
+    def _find_egg_info_dir(self) -> str | None:
+        parent_dir = Path(self.dist_info_dir).parent if self.dist_info_dir else Path()
+        candidates = map(str, parent_dir.glob("*.egg-info"))
+        return next(candidates, None)
+
+    def _configure_build(
+        self, name: str, unpacked_wheel: StrPath, build_lib: StrPath, tmp_dir: StrPath
+    ):
+        """Configure commands to behave in the following ways:
+
+        - Build commands can write to ``build_lib`` if they really want to...
+          (but this folder is expected to be ignored and modules are expected to live
+          in the project directory...)
+        - Binary extensions should be built in-place (editable_mode = True)
+        - Data/header/script files are not part of the "editable" specification
+          so they are written directly to the unpacked_wheel directory.
+        """
+        # Non-editable files (data, headers, scripts) are written directly to the
+        # unpacked_wheel
+
+        dist = self.distribution
+        wheel = str(unpacked_wheel)
+        build_lib = str(build_lib)
+        data = str(Path(unpacked_wheel, f"{name}.data", "data"))
+        headers = str(Path(unpacked_wheel, f"{name}.data", "headers"))
+        scripts = str(Path(unpacked_wheel, f"{name}.data", "scripts"))
+
+        # egg-info may be generated again to create a manifest (used for package data)
+        egg_info = cast(
+            egg_info_cls, dist.reinitialize_command("egg_info", reinit_subcommands=True)
+        )
+        egg_info.egg_base = str(tmp_dir)
+        egg_info.ignore_egg_info_in_manifest = True
+
+        build = cast(
+            build_cls, dist.reinitialize_command("build", reinit_subcommands=True)
+        )
+        install = cast(
+            install_cls, dist.reinitialize_command("install", reinit_subcommands=True)
+        )
+
+        build.build_platlib = build.build_purelib = build.build_lib = build_lib
+        install.install_purelib = install.install_platlib = install.install_lib = wheel
+        install.install_scripts = build.build_scripts = scripts
+        install.install_headers = headers
+        install.install_data = data
+
+        # For portability, ensure scripts are built with #!python shebang
+        # pypa/setuptools#4863
+        build_scripts = dist.get_command_obj("build_scripts")
+        build_scripts.executable = 'python'
+
+        install_scripts = cast(
+            install_scripts_cls, dist.get_command_obj("install_scripts")
+        )
+        install_scripts.no_ep = True
+
+        build.build_temp = str(tmp_dir)
+
+        build_py = cast(build_py_cls, dist.get_command_obj("build_py"))
+        build_py.compile = False
+        build_py.existing_egg_info_dir = self._find_egg_info_dir()
+
+        self._set_editable_mode()
+
+        build.ensure_finalized()
+        install.ensure_finalized()
+
+    def _set_editable_mode(self):
+        """Set the ``editable_mode`` flag in the build sub-commands"""
+        dist = self.distribution
+        build = dist.get_command_obj("build")
+        for cmd_name in build.get_sub_commands():
+            cmd = dist.get_command_obj(cmd_name)
+            if hasattr(cmd, "editable_mode"):
+                cmd.editable_mode = True
+            elif hasattr(cmd, "inplace"):
+                cmd.inplace = True  # backward compatibility with distutils
+
+    def _collect_build_outputs(self) -> tuple[list[str], dict[str, str]]:
+        files: list[str] = []
+        mapping: dict[str, str] = {}
+        build = self.get_finalized_command("build")
+
+        for cmd_name in build.get_sub_commands():
+            cmd = self.get_finalized_command(cmd_name)
+            if hasattr(cmd, "get_outputs"):
+                files.extend(cmd.get_outputs() or [])
+            if hasattr(cmd, "get_output_mapping"):
+                mapping.update(cmd.get_output_mapping() or {})
+
+        return files, mapping
+
+    def _run_build_commands(
+        self,
+        dist_name: str,
+        unpacked_wheel: StrPath,
+        build_lib: StrPath,
+        tmp_dir: StrPath,
+    ) -> tuple[list[str], dict[str, str]]:
+        self._configure_build(dist_name, unpacked_wheel, build_lib, tmp_dir)
+        self._run_build_subcommands()
+        files, mapping = self._collect_build_outputs()
+        self._run_install("headers")
+        self._run_install("scripts")
+        self._run_install("data")
+        return files, mapping
+
+    def _run_build_subcommands(self) -> None:
+        """
+        Issue #3501 indicates that some plugins/customizations might rely on:
+
+        1. ``build_py`` not running
+        2. ``build_py`` always copying files to ``build_lib``
+
+        However both these assumptions may be false in editable_wheel.
+        This method implements a temporary workaround to support the ecosystem
+        while the implementations catch up.
+        """
+        # TODO: Once plugins/customizations had the chance to catch up, replace
+        #       `self._run_build_subcommands()` with `self.run_command("build")`.
+        #       Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023.
+        build = self.get_finalized_command("build")
+        for name in build.get_sub_commands():
+            cmd = self.get_finalized_command(name)
+            if name == "build_py" and type(cmd) is not build_py_cls:
+                self._safely_run(name)
+            else:
+                self.run_command(name)
+
+    def _safely_run(self, cmd_name: str):
+        try:
+            return self.run_command(cmd_name)
+        except Exception:
+            SetuptoolsDeprecationWarning.emit(
+                "Customization incompatible with editable install",
+                f"""
+                {traceback.format_exc()}
+
+                If you are seeing this warning it is very likely that a setuptools
+                plugin or customization overrides the `{cmd_name}` command, without
+                taking into consideration how editable installs run build steps
+                starting from setuptools v64.0.0.
+
+                Plugin authors and developers relying on custom build steps are
+                encouraged to update their `{cmd_name}` implementation considering the
+                information about editable installs in
+                https://setuptools.pypa.io/en/latest/userguide/extension.html.
+
+                For the time being `setuptools` will silence this error and ignore
+                the faulty command, but this behavior will change in future versions.
+                """,
+                # TODO: define due_date
+                # There is a series of shortcomings with the available editable install
+                # methods, and they are very controversial. This is something that still
+                # needs work.
+            )
+
+    def _create_wheel_file(self, bdist_wheel):
+        from wheel.wheelfile import WheelFile
+
+        dist_info = self.get_finalized_command("dist_info")
+        dist_name = dist_info.name
+        tag = "-".join(bdist_wheel.get_tag())
+        build_tag = "0.editable"  # According to PEP 427 needs to start with digit
+        archive_name = f"{dist_name}-{build_tag}-{tag}.whl"
+        wheel_path = Path(self.dist_dir, archive_name)
+        if wheel_path.exists():
+            wheel_path.unlink()
+
+        unpacked_wheel = TemporaryDirectory(suffix=archive_name)
+        build_lib = TemporaryDirectory(suffix=".build-lib")
+        build_tmp = TemporaryDirectory(suffix=".build-temp")
+
+        with unpacked_wheel as unpacked, build_lib as lib, build_tmp as tmp:
+            unpacked_dist_info = Path(unpacked, Path(self.dist_info_dir).name)
+            shutil.copytree(self.dist_info_dir, unpacked_dist_info)
+            self._install_namespaces(unpacked, dist_name)
+            files, mapping = self._run_build_commands(dist_name, unpacked, lib, tmp)
+            strategy = self._select_strategy(dist_name, tag, lib)
+            with strategy, WheelFile(wheel_path, "w") as wheel_obj:
+                strategy(wheel_obj, files, mapping)
+                wheel_obj.write_files(unpacked)
+
+        return wheel_path
+
+    def _run_install(self, category: str):
+        has_category = getattr(self.distribution, f"has_{category}", None)
+        if has_category and has_category():
+            _logger.info(f"Installing {category} as non editable")
+            self.run_command(f"install_{category}")
+
+    def _select_strategy(
+        self,
+        name: str,
+        tag: str,
+        build_lib: StrPath,
+    ) -> EditableStrategy:
+        """Decides which strategy to use to implement an editable installation."""
+        build_name = f"__editable__.{name}-{tag}"
+        project_dir = Path(self.project_dir)
+        mode = _EditableMode.convert(self.mode)
+
+        if mode is _EditableMode.STRICT:
+            auxiliary_dir = _empty_dir(Path(self.project_dir, "build", build_name))
+            return _LinkTree(self.distribution, name, auxiliary_dir, build_lib)
+
+        packages = _find_packages(self.distribution)
+        has_simple_layout = _simple_layout(packages, self.package_dir, project_dir)
+        is_compat_mode = mode is _EditableMode.COMPAT
+        if set(self.package_dir) == {""} and has_simple_layout or is_compat_mode:
+            # src-layout(ish) is relatively safe for a simple pth file
+            src_dir = self.package_dir.get("", ".")
+            return _StaticPth(self.distribution, name, [Path(project_dir, src_dir)])
+
+        # Use a MetaPathFinder to avoid adding accidental top-level packages/modules
+        return _TopLevelFinder(self.distribution, name)
+
+
+class EditableStrategy(Protocol):
+    def __call__(
+        self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]
+    ) -> object: ...
+    def __enter__(self) -> Self: ...
+    def __exit__(
+        self,
+        _exc_type: type[BaseException] | None,
+        _exc_value: BaseException | None,
+        _traceback: TracebackType | None,
+    ) -> object: ...
+
+
+class _StaticPth:
+    def __init__(self, dist: Distribution, name: str, path_entries: list[Path]) -> None:
+        self.dist = dist
+        self.name = name
+        self.path_entries = path_entries
+
+    def __call__(
+        self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]
+    ) -> None:
+        entries = "\n".join(str(p.resolve()) for p in self.path_entries)
+        contents = _encode_pth(f"{entries}\n")
+        wheel.writestr(f"__editable__.{self.name}.pth", contents)
+
+    def __enter__(self) -> Self:
+        msg = f"""
+        Editable install will be performed using .pth file to extend `sys.path` with:
+        {list(map(os.fspath, self.path_entries))!r}
+        """
+        _logger.warning(msg + _LENIENT_WARNING)
+        return self
+
+    def __exit__(
+        self,
+        _exc_type: object,
+        _exc_value: object,
+        _traceback: object,
+    ) -> None:
+        pass
+
+
+class _LinkTree(_StaticPth):
+    """
+    Creates a ``.pth`` file that points to a link tree in the ``auxiliary_dir``.
+
+    This strategy will only link files (not dirs), so it can be implemented in
+    any OS, even if that means using hardlinks instead of symlinks.
+
+    By collocating ``auxiliary_dir`` and the original source code, limitations
+    with hardlinks should be avoided.
+    """
+
+    def __init__(
+        self,
+        dist: Distribution,
+        name: str,
+        auxiliary_dir: StrPath,
+        build_lib: StrPath,
+    ) -> None:
+        self.auxiliary_dir = Path(auxiliary_dir)
+        self.build_lib = Path(build_lib).resolve()
+        self._file = dist.get_command_obj("build_py").copy_file
+        super().__init__(dist, name, [self.auxiliary_dir])
+
+    def __call__(
+        self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]
+    ) -> None:
+        self._create_links(files, mapping)
+        super().__call__(wheel, files, mapping)
+
+    def _normalize_output(self, file: str) -> str | None:
+        # Files relative to build_lib will be normalized to None
+        with suppress(ValueError):
+            path = Path(file).resolve().relative_to(self.build_lib)
+            return str(path).replace(os.sep, '/')
+        return None
+
+    def _create_file(self, relative_output: str, src_file: str, link=None):
+        dest = self.auxiliary_dir / relative_output
+        if not dest.parent.is_dir():
+            dest.parent.mkdir(parents=True)
+        self._file(src_file, dest, link=link)
+
+    def _create_links(self, outputs, output_mapping: Mapping[str, str]):
+        self.auxiliary_dir.mkdir(parents=True, exist_ok=True)
+        link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard"
+        normalised = ((self._normalize_output(k), v) for k, v in output_mapping.items())
+        # remove files that are not relative to build_lib
+        mappings = {k: v for k, v in normalised if k is not None}
+
+        for output in outputs:
+            relative = self._normalize_output(output)
+            if relative and relative not in mappings:
+                self._create_file(relative, output)
+
+        for relative, src in mappings.items():
+            self._create_file(relative, src, link=link_type)
+
+    def __enter__(self) -> Self:
+        msg = "Strict editable install will be performed using a link tree.\n"
+        _logger.warning(msg + _STRICT_WARNING)
+        return self
+
+    def __exit__(
+        self,
+        _exc_type: object,
+        _exc_value: object,
+        _traceback: object,
+    ) -> None:
+        msg = f"""\n
+        Strict editable installation performed using the auxiliary directory:
+            {self.auxiliary_dir}
+
+        Please be careful to not remove this directory, otherwise you might not be able
+        to import/use your package.
+        """
+        InformationOnly.emit("Editable installation.", msg)
+
+
+class _TopLevelFinder:
+    def __init__(self, dist: Distribution, name: str) -> None:
+        self.dist = dist
+        self.name = name
+
+    def template_vars(self) -> tuple[str, str, dict[str, str], dict[str, list[str]]]:
+        src_root = self.dist.src_root or os.curdir
+        top_level = chain(_find_packages(self.dist), _find_top_level_modules(self.dist))
+        package_dir = self.dist.package_dir or {}
+        roots = _find_package_roots(top_level, package_dir, src_root)
+
+        namespaces_ = dict(
+            chain(
+                _find_namespaces(self.dist.packages or [], roots),
+                ((ns, []) for ns in _find_virtual_namespaces(roots)),
+            )
+        )
+
+        legacy_namespaces = {
+            pkg: find_package_path(pkg, roots, self.dist.src_root or "")
+            for pkg in self.dist.namespace_packages or []
+        }
+
+        mapping = {**roots, **legacy_namespaces}
+        # ^-- We need to explicitly add the legacy_namespaces to the mapping to be
+        #     able to import their modules even if another package sharing the same
+        #     namespace is installed in a conventional (non-editable) way.
+
+        name = f"__editable__.{self.name}.finder"
+        finder = _normalization.safe_identifier(name)
+        return finder, name, mapping, namespaces_
+
+    def get_implementation(self) -> Iterator[tuple[str, bytes]]:
+        finder, name, mapping, namespaces_ = self.template_vars()
+
+        content = bytes(_finder_template(name, mapping, namespaces_), "utf-8")
+        yield (f"{finder}.py", content)
+
+        content = _encode_pth(f"import {finder}; {finder}.install()")
+        yield (f"__editable__.{self.name}.pth", content)
+
+    def __call__(
+        self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]
+    ) -> None:
+        for file, content in self.get_implementation():
+            wheel.writestr(file, content)
+
+    def __enter__(self) -> Self:
+        msg = "Editable install will be performed using a meta path finder.\n"
+        _logger.warning(msg + _LENIENT_WARNING)
+        return self
+
+    def __exit__(
+        self,
+        _exc_type: object,
+        _exc_value: object,
+        _traceback: object,
+    ) -> None:
+        msg = """\n
+        Please be careful with folders in your working directory with the same
+        name as your package as they may take precedence during imports.
+        """
+        InformationOnly.emit("Editable installation.", msg)
+
+
+def _encode_pth(content: str) -> bytes:
+    """
+    Prior to Python 3.13 (see https://github.com/python/cpython/issues/77102),
+    .pth files are always read with 'locale' encoding, the recommendation
+    from the cpython core developers is to write them as ``open(path, "w")``
+    and ignore warnings (see python/cpython#77102, pypa/setuptools#3937).
+    This function tries to simulate this behavior without having to create an
+    actual file, in a way that supports a range of active Python versions.
+    (There seems to be some variety in the way different version of Python handle
+    ``encoding=None``, not all of them use ``locale.getpreferredencoding(False)``
+    or ``locale.getencoding()``).
+    """
+    with io.BytesIO() as buffer:
+        wrapper = io.TextIOWrapper(buffer, encoding=py312.PTH_ENCODING)
+        # TODO: Python 3.13 replace the whole function with `bytes(content, "utf-8")`
+        wrapper.write(content)
+        wrapper.flush()
+        buffer.seek(0)
+        return buffer.read()
+
+
+def _can_symlink_files(base_dir: Path) -> bool:
+    with TemporaryDirectory(dir=str(base_dir.resolve())) as tmp:
+        path1, path2 = Path(tmp, "file1.txt"), Path(tmp, "file2.txt")
+        path1.write_text("file1", encoding="utf-8")
+        with suppress(AttributeError, NotImplementedError, OSError):
+            os.symlink(path1, path2)
+            if path2.is_symlink() and path2.read_text(encoding="utf-8") == "file1":
+                return True
+
+        try:
+            os.link(path1, path2)  # Ensure hard links can be created
+        except Exception as ex:
+            msg = (
+                "File system does not seem to support either symlinks or hard links. "
+                "Strict editable installs require one of them to be supported."
+            )
+            raise LinksNotSupported(msg) from ex
+        return False
+
+
+def _simple_layout(
+    packages: Iterable[str], package_dir: dict[str, str], project_dir: StrPath
+) -> bool:
+    """Return ``True`` if:
+    - all packages are contained by the same parent directory, **and**
+    - all packages become importable if the parent directory is added to ``sys.path``.
+
+    >>> _simple_layout(['a'], {"": "src"}, "/tmp/myproj")
+    True
+    >>> _simple_layout(['a', 'a.b'], {"": "src"}, "/tmp/myproj")
+    True
+    >>> _simple_layout(['a', 'a.b'], {}, "/tmp/myproj")
+    True
+    >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"": "src"}, "/tmp/myproj")
+    True
+    >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "a", "b": "b"}, ".")
+    True
+    >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a", "b": "_b"}, ".")
+    False
+    >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a"}, "/tmp/myproj")
+    False
+    >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a.a1.a2": "_a2"}, ".")
+    False
+    >>> _simple_layout(['a', 'a.b'], {"": "src", "a.b": "_ab"}, "/tmp/myproj")
+    False
+    >>> # Special cases, no packages yet:
+    >>> _simple_layout([], {"": "src"}, "/tmp/myproj")
+    True
+    >>> _simple_layout([], {"a": "_a", "": "src"}, "/tmp/myproj")
+    False
+    """
+    layout = {pkg: find_package_path(pkg, package_dir, project_dir) for pkg in packages}
+    if not layout:
+        return set(package_dir) in ({}, {""})
+    parent = os.path.commonpath(starmap(_parent_path, layout.items()))
+    return all(
+        _path.same_path(Path(parent, *key.split('.')), value)
+        for key, value in layout.items()
+    )
+
+
+def _parent_path(pkg, pkg_path):
+    """Infer the parent path containing a package, that if added to ``sys.path`` would
+    allow importing that package.
+    When ``pkg`` is directly mapped into a directory with a different name, return its
+    own path.
+    >>> _parent_path("a", "src/a")
+    'src'
+    >>> _parent_path("b", "src/c")
+    'src/c'
+    """
+    parent = pkg_path.removesuffix(pkg)
+    return parent.rstrip("/" + os.sep)
+
+
+def _find_packages(dist: Distribution) -> Iterator[str]:
+    yield from iter(dist.packages or [])
+
+    py_modules = dist.py_modules or []
+    nested_modules = [mod for mod in py_modules if "." in mod]
+    if dist.ext_package:
+        yield dist.ext_package
+    else:
+        ext_modules = dist.ext_modules or []
+        nested_modules += [x.name for x in ext_modules if "." in x.name]
+
+    for module in nested_modules:
+        package, _, _ = module.rpartition(".")
+        yield package
+
+
+def _find_top_level_modules(dist: Distribution) -> Iterator[str]:
+    py_modules = dist.py_modules or []
+    yield from (mod for mod in py_modules if "." not in mod)
+
+    if not dist.ext_package:
+        ext_modules = dist.ext_modules or []
+        yield from (x.name for x in ext_modules if "." not in x.name)
+
+
+def _find_package_roots(
+    packages: Iterable[str],
+    package_dir: Mapping[str, str],
+    src_root: StrPath,
+) -> dict[str, str]:
+    pkg_roots: dict[str, str] = {
+        pkg: _absolute_root(find_package_path(pkg, package_dir, src_root))
+        for pkg in sorted(packages)
+    }
+
+    return _remove_nested(pkg_roots)
+
+
+def _absolute_root(path: StrPath) -> str:
+    """Works for packages and top-level modules"""
+    path_ = Path(path)
+    parent = path_.parent
+
+    if path_.exists():
+        return str(path_.resolve())
+    else:
+        return str(parent.resolve() / path_.name)
+
+
+def _find_virtual_namespaces(pkg_roots: dict[str, str]) -> Iterator[str]:
+    """By carefully designing ``package_dir``, it is possible to implement the logical
+    structure of PEP 420 in a package without the corresponding directories.
+
+    Moreover a parent package can be purposefully/accidentally skipped in the discovery
+    phase (e.g. ``find_packages(include=["mypkg.*"])``, when ``mypkg.foo`` is included
+    by ``mypkg`` itself is not).
+    We consider this case to also be a virtual namespace (ignoring the original
+    directory) to emulate a non-editable installation.
+
+    This function will try to find these kinds of namespaces.
+    """
+    for pkg in pkg_roots:
+        if "." not in pkg:
+            continue
+        parts = pkg.split(".")
+        for i in range(len(parts) - 1, 0, -1):
+            partial_name = ".".join(parts[:i])
+            path = Path(find_package_path(partial_name, pkg_roots, ""))
+            if not path.exists() or partial_name not in pkg_roots:
+                # partial_name not in pkg_roots ==> purposefully/accidentally skipped
+                yield partial_name
+
+
+def _find_namespaces(
+    packages: list[str], pkg_roots: dict[str, str]
+) -> Iterator[tuple[str, list[str]]]:
+    for pkg in packages:
+        path = find_package_path(pkg, pkg_roots, "")
+        if Path(path).exists() and not Path(path, "__init__.py").exists():
+            yield (pkg, [path])
+
+
+def _remove_nested(pkg_roots: dict[str, str]) -> dict[str, str]:
+    output = dict(pkg_roots.copy())
+
+    for pkg, path in reversed(list(pkg_roots.items())):
+        if any(
+            pkg != other and _is_nested(pkg, path, other, other_path)
+            for other, other_path in pkg_roots.items()
+        ):
+            output.pop(pkg)
+
+    return output
+
+
+def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool:
+    """
+    Return ``True`` if ``pkg`` is nested inside ``parent`` both logically and in the
+    file system.
+    >>> _is_nested("a.b", "path/a/b", "a", "path/a")
+    True
+    >>> _is_nested("a.b", "path/a/b", "a", "otherpath/a")
+    False
+    >>> _is_nested("a.b", "path/a/b", "c", "path/c")
+    False
+    >>> _is_nested("a.a", "path/a/a", "a", "path/a")
+    True
+    >>> _is_nested("b.a", "path/b/a", "a", "path/a")
+    False
+    """
+    norm_pkg_path = _path.normpath(pkg_path)
+    rest = pkg.replace(parent, "", 1).strip(".").split(".")
+    return pkg.startswith(parent) and norm_pkg_path == _path.normpath(
+        Path(parent_path, *rest)
+    )
+
+
+def _empty_dir(dir_: _P) -> _P:
+    """Create a directory ensured to be empty. Existing files may be removed."""
+    _shutil.rmtree(dir_, ignore_errors=True)
+    os.makedirs(dir_)
+    return dir_
+
+
+class _NamespaceInstaller(namespaces.Installer):
+    def __init__(self, distribution, installation_dir, editable_name, src_root) -> None:
+        self.distribution = distribution
+        self.src_root = src_root
+        self.installation_dir = installation_dir
+        self.editable_name = editable_name
+        self.outputs: list[str] = []
+
+    def _get_nspkg_file(self):
+        """Installation target."""
+        return os.path.join(self.installation_dir, self.editable_name + self.nspkg_ext)
+
+    def _get_root(self):
+        """Where the modules/packages should be loaded from."""
+        return repr(str(self.src_root))
+
+
+_FINDER_TEMPLATE = """\
+from __future__ import annotations
+import sys
+from importlib.machinery import ModuleSpec, PathFinder
+from importlib.machinery import all_suffixes as module_suffixes
+from importlib.util import spec_from_file_location
+from itertools import chain
+from pathlib import Path
+
+MAPPING: dict[str, str] = {mapping!r}
+NAMESPACES: dict[str, list[str]] = {namespaces!r}
+PATH_PLACEHOLDER = {name!r} + ".__path_hook__"
+
+
+class _EditableFinder:  # MetaPathFinder
+    @classmethod
+    def find_spec(cls, fullname: str, path=None, target=None) -> ModuleSpec | None:  # type: ignore
+        # Top-level packages and modules (we know these exist in the FS)
+        if fullname in MAPPING:
+            pkg_path = MAPPING[fullname]
+            return cls._find_spec(fullname, Path(pkg_path))
+
+        # Handle immediate children modules (required for namespaces to work)
+        # To avoid problems with case sensitivity in the file system we delegate
+        # to the importlib.machinery implementation.
+        parent, _, child = fullname.rpartition(".")
+        if parent and parent in MAPPING:
+            return PathFinder.find_spec(fullname, path=[MAPPING[parent]])
+
+        # Other levels of nesting should be handled automatically by importlib
+        # using the parent path.
+        return None
+
+    @classmethod
+    def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None:
+        init = candidate_path / "__init__.py"
+        candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
+        for candidate in chain([init], candidates):
+            if candidate.exists():
+                return spec_from_file_location(fullname, candidate)
+        return None
+
+
+class _EditableNamespaceFinder:  # PathEntryFinder
+    @classmethod
+    def _path_hook(cls, path) -> type[_EditableNamespaceFinder]:
+        if path == PATH_PLACEHOLDER:
+            return cls
+        raise ImportError
+
+    @classmethod
+    def _paths(cls, fullname: str) -> list[str]:
+        paths = NAMESPACES[fullname]
+        if not paths and fullname in MAPPING:
+            paths = [MAPPING[fullname]]
+        # Always add placeholder, for 2 reasons:
+        # 1. __path__ cannot be empty for the spec to be considered namespace.
+        # 2. In the case of nested namespaces, we need to force
+        #    import machinery to query _EditableNamespaceFinder again.
+        return [*paths, PATH_PLACEHOLDER]
+
+    @classmethod
+    def find_spec(cls, fullname: str, target=None) -> ModuleSpec | None:  # type: ignore
+        if fullname in NAMESPACES:
+            spec = ModuleSpec(fullname, None, is_package=True)
+            spec.submodule_search_locations = cls._paths(fullname)
+            return spec
+        return None
+
+    @classmethod
+    def find_module(cls, _fullname) -> None:
+        return None
+
+
+def install():
+    if not any(finder == _EditableFinder for finder in sys.meta_path):
+        sys.meta_path.append(_EditableFinder)
+
+    if not NAMESPACES:
+        return
+
+    if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
+        # PathEntryFinder is needed to create NamespaceSpec without private APIS
+        sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
+    if PATH_PLACEHOLDER not in sys.path:
+        sys.path.append(PATH_PLACEHOLDER)  # Used just to trigger the path hook
+"""
+
+
+def _finder_template(
+    name: str, mapping: Mapping[str, str], namespaces: dict[str, list[str]]
+) -> str:
+    """Create a string containing the code for the``MetaPathFinder`` and
+    ``PathEntryFinder``.
+    """
+    mapping = dict(sorted(mapping.items(), key=operator.itemgetter(0)))
+    return _FINDER_TEMPLATE.format(name=name, mapping=mapping, namespaces=namespaces)
+
+
+class LinksNotSupported(errors.FileError):
+    """File system does not seem to support either symlinks or hard links."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/egg_info.py b/.venv/lib/python3.12/site-packages/setuptools/command/egg_info.py
new file mode 100644
index 0000000..a5b5932
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/egg_info.py
@@ -0,0 +1,716 @@
+"""setuptools.command.egg_info
+
+Create a distribution's .egg-info directory and contents"""
+
+from __future__ import annotations
+
+import functools
+import os
+import re
+import sys
+import time
+from collections.abc import Callable
+
+import packaging
+import packaging.requirements
+import packaging.version
+
+import setuptools.unicode_utils as unicode_utils
+from setuptools import Command
+from setuptools.command import bdist_egg
+from setuptools.command.sdist import sdist, walk_revctrl
+from setuptools.command.setopt import edit_config
+from setuptools.glob import glob
+
+from .. import _entry_points, _normalization
+from .._importlib import metadata
+from ..warnings import SetuptoolsDeprecationWarning
+from . import _requirestxt
+
+import distutils.errors
+import distutils.filelist
+from distutils import log
+from distutils.errors import DistutilsInternalError
+from distutils.filelist import FileList as _FileList
+from distutils.util import convert_path
+
+PY_MAJOR = f'{sys.version_info.major}.{sys.version_info.minor}'
+
+
+def translate_pattern(glob):  # noqa: C901  # is too complex (14)  # FIXME
+    """
+    Translate a file path glob like '*.txt' in to a regular expression.
+    This differs from fnmatch.translate which allows wildcards to match
+    directory separators. It also knows about '**/' which matches any number of
+    directories.
+    """
+    pat = ''
+
+    # This will split on '/' within [character classes]. This is deliberate.
+    chunks = glob.split(os.path.sep)
+
+    sep = re.escape(os.sep)
+    valid_char = f'[^{sep}]'
+
+    for c, chunk in enumerate(chunks):
+        last_chunk = c == len(chunks) - 1
+
+        # Chunks that are a literal ** are globstars. They match anything.
+        if chunk == '**':
+            if last_chunk:
+                # Match anything if this is the last component
+                pat += '.*'
+            else:
+                # Match '(name/)*'
+                pat += f'(?:{valid_char}+{sep})*'
+            continue  # Break here as the whole path component has been handled
+
+        # Find any special characters in the remainder
+        i = 0
+        chunk_len = len(chunk)
+        while i < chunk_len:
+            char = chunk[i]
+            if char == '*':
+                # Match any number of name characters
+                pat += valid_char + '*'
+            elif char == '?':
+                # Match a name character
+                pat += valid_char
+            elif char == '[':
+                # Character class
+                inner_i = i + 1
+                # Skip initial !/] chars
+                if inner_i < chunk_len and chunk[inner_i] == '!':
+                    inner_i = inner_i + 1
+                if inner_i < chunk_len and chunk[inner_i] == ']':
+                    inner_i = inner_i + 1
+
+                # Loop till the closing ] is found
+                while inner_i < chunk_len and chunk[inner_i] != ']':
+                    inner_i = inner_i + 1
+
+                if inner_i >= chunk_len:
+                    # Got to the end of the string without finding a closing ]
+                    # Do not treat this as a matching group, but as a literal [
+                    pat += re.escape(char)
+                else:
+                    # Grab the insides of the [brackets]
+                    inner = chunk[i + 1 : inner_i]
+                    char_class = ''
+
+                    # Class negation
+                    if inner[0] == '!':
+                        char_class = '^'
+                        inner = inner[1:]
+
+                    char_class += re.escape(inner)
+                    pat += f'[{char_class}]'
+
+                    # Skip to the end ]
+                    i = inner_i
+            else:
+                pat += re.escape(char)
+            i += 1
+
+        # Join each chunk with the dir separator
+        if not last_chunk:
+            pat += sep
+
+    pat += r'\Z'
+    return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
+
+
+class InfoCommon:
+    tag_build = None
+    tag_date = None
+
+    @property
+    def name(self):
+        return _normalization.safe_name(self.distribution.get_name())
+
+    def tagged_version(self):
+        tagged = self._maybe_tag(self.distribution.get_version())
+        return _normalization.safe_version(tagged)
+
+    def _maybe_tag(self, version):
+        """
+        egg_info may be called more than once for a distribution,
+        in which case the version string already contains all tags.
+        """
+        return (
+            version
+            if self.vtags and self._already_tagged(version)
+            else version + self.vtags
+        )
+
+    def _already_tagged(self, version: str) -> bool:
+        # Depending on their format, tags may change with version normalization.
+        # So in addition the regular tags, we have to search for the normalized ones.
+        return version.endswith((self.vtags, self._safe_tags()))
+
+    def _safe_tags(self) -> str:
+        # To implement this we can rely on `safe_version` pretending to be version 0
+        # followed by tags. Then we simply discard the starting 0 (fake version number)
+        try:
+            return _normalization.safe_version(f"0{self.vtags}")[1:]
+        except packaging.version.InvalidVersion:
+            return _normalization.safe_name(self.vtags.replace(' ', '.'))
+
+    def tags(self) -> str:
+        version = ''
+        if self.tag_build:
+            version += self.tag_build
+        if self.tag_date:
+            version += time.strftime("%Y%m%d")
+        return version
+
+    vtags = property(tags)
+
+
+class egg_info(InfoCommon, Command):
+    description = "create a distribution's .egg-info directory"
+
+    user_options = [
+        (
+            'egg-base=',
+            'e',
+            "directory containing .egg-info directories"
+            " [default: top of the source tree]",
+        ),
+        ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
+        ('tag-build=', 'b', "Specify explicit tag to add to version number"),
+        ('no-date', 'D', "Don't include date stamp [default]"),
+    ]
+
+    boolean_options = ['tag-date']
+    negative_opt = {
+        'no-date': 'tag-date',
+    }
+
+    def initialize_options(self):
+        self.egg_base = None
+        self.egg_name = None
+        self.egg_info = None
+        self.egg_version = None
+        self.ignore_egg_info_in_manifest = False
+
+    ####################################
+    # allow the 'tag_svn_revision' to be detected and
+    # set, supporting sdists built on older Setuptools.
+    @property
+    def tag_svn_revision(self) -> int | None:
+        pass
+
+    @tag_svn_revision.setter
+    def tag_svn_revision(self, value) -> None:
+        pass
+
+    ####################################
+
+    def save_version_info(self, filename) -> None:
+        """
+        Materialize the value of date into the
+        build tag. Install build keys in a deterministic order
+        to avoid arbitrary reordering on subsequent builds.
+        """
+        # follow the order these keys would have been added
+        # when PYTHONHASHSEED=0
+        egg_info = dict(tag_build=self.tags(), tag_date=0)
+        edit_config(filename, dict(egg_info=egg_info))
+
+    def finalize_options(self) -> None:
+        # Note: we need to capture the current value returned
+        # by `self.tagged_version()`, so we can later update
+        # `self.distribution.metadata.version` without
+        # repercussions.
+        self.egg_name = self.name
+        self.egg_version = self.tagged_version()
+        parsed_version = packaging.version.Version(self.egg_version)
+
+        try:
+            is_version = isinstance(parsed_version, packaging.version.Version)
+            spec = "%s==%s" if is_version else "%s===%s"
+            packaging.requirements.Requirement(spec % (self.egg_name, self.egg_version))
+        except ValueError as e:
+            raise distutils.errors.DistutilsOptionError(
+                f"Invalid distribution name or version syntax: {self.egg_name}-{self.egg_version}"
+            ) from e
+
+        if self.egg_base is None:
+            dirs = self.distribution.package_dir
+            self.egg_base = (dirs or {}).get('', os.curdir)
+
+        self.ensure_dirname('egg_base')
+        self.egg_info = _normalization.filename_component(self.egg_name) + '.egg-info'
+        if self.egg_base != os.curdir:
+            self.egg_info = os.path.join(self.egg_base, self.egg_info)
+
+        # Set package version for the benefit of dumber commands
+        # (e.g. sdist, bdist_wininst, etc.)
+        #
+        self.distribution.metadata.version = self.egg_version
+
+    def _get_egg_basename(self, py_version=PY_MAJOR, platform=None):
+        """Compute filename of the output egg. Private API."""
+        return _egg_basename(self.egg_name, self.egg_version, py_version, platform)
+
+    def write_or_delete_file(self, what, filename, data, force: bool = False) -> None:
+        """Write `data` to `filename` or delete if empty
+
+        If `data` is non-empty, this routine is the same as ``write_file()``.
+        If `data` is empty but not ``None``, this is the same as calling
+        ``delete_file(filename)`.  If `data` is ``None``, then this is a no-op
+        unless `filename` exists, in which case a warning is issued about the
+        orphaned file (if `force` is false), or deleted (if `force` is true).
+        """
+        if data:
+            self.write_file(what, filename, data)
+        elif os.path.exists(filename):
+            if data is None and not force:
+                log.warn("%s not set in setup(), but %s exists", what, filename)
+                return
+            else:
+                self.delete_file(filename)
+
+    def write_file(self, what, filename, data) -> None:
+        """Write `data` to `filename` (if not a dry run) after announcing it
+
+        `what` is used in a log message to identify what is being written
+        to the file.
+        """
+        log.info("writing %s to %s", what, filename)
+        data = data.encode("utf-8")
+        f = open(filename, 'wb')
+        f.write(data)
+        f.close()
+
+    def delete_file(self, filename) -> None:
+        """Delete `filename` (if not a dry run) after announcing it"""
+        log.info("deleting %s", filename)
+        os.unlink(filename)
+
+    def run(self) -> None:
+        # Pre-load to avoid iterating over entry-points while an empty .egg-info
+        # exists in sys.path. See pypa/pyproject-hooks#206
+        writers = list(metadata.entry_points(group='egg_info.writers'))
+
+        self.mkpath(self.egg_info)
+        try:
+            os.utime(self.egg_info, None)
+        except OSError as e:
+            msg = f"Cannot update time stamp of directory '{self.egg_info}'"
+            raise distutils.errors.DistutilsFileError(msg) from e
+        for ep in writers:
+            writer = ep.load()
+            writer(self, ep.name, os.path.join(self.egg_info, ep.name))
+
+        # Get rid of native_libs.txt if it was put there by older bdist_egg
+        nl = os.path.join(self.egg_info, "native_libs.txt")
+        if os.path.exists(nl):
+            self.delete_file(nl)
+
+        self.find_sources()
+
+    def find_sources(self) -> None:
+        """Generate SOURCES.txt manifest file"""
+        manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
+        mm = manifest_maker(self.distribution)
+        mm.ignore_egg_info_dir = self.ignore_egg_info_in_manifest
+        mm.manifest = manifest_filename
+        mm.run()
+        self.filelist = mm.filelist
+
+
+class FileList(_FileList):
+    # Implementations of the various MANIFEST.in commands
+
+    def __init__(
+        self, warn=None, debug_print=None, ignore_egg_info_dir: bool = False
+    ) -> None:
+        super().__init__(warn, debug_print)
+        self.ignore_egg_info_dir = ignore_egg_info_dir
+
+    def process_template_line(self, line) -> None:
+        # 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 (dir_pattern).
+        (action, patterns, dir, dir_pattern) = self._parse_template_line(line)
+
+        action_map: dict[str, Callable] = {
+            'include': self.include,
+            'exclude': self.exclude,
+            'global-include': self.global_include,
+            'global-exclude': self.global_exclude,
+            'recursive-include': functools.partial(
+                self.recursive_include,
+                dir,
+            ),
+            'recursive-exclude': functools.partial(
+                self.recursive_exclude,
+                dir,
+            ),
+            'graft': self.graft,
+            'prune': self.prune,
+        }
+        log_map = {
+            'include': "warning: no files found matching '%s'",
+            'exclude': ("warning: no previously-included files found matching '%s'"),
+            'global-include': (
+                "warning: no files found matching '%s' anywhere in distribution"
+            ),
+            'global-exclude': (
+                "warning: no previously-included files matching "
+                "'%s' found anywhere in distribution"
+            ),
+            'recursive-include': (
+                "warning: no files found matching '%s' under directory '%s'"
+            ),
+            'recursive-exclude': (
+                "warning: no previously-included files matching "
+                "'%s' found under directory '%s'"
+            ),
+            'graft': "warning: no directories found matching '%s'",
+            'prune': "no previously-included directories found matching '%s'",
+        }
+
+        try:
+            process_action = action_map[action]
+        except KeyError:
+            msg = f"Invalid MANIFEST.in: unknown action {action!r} in {line!r}"
+            raise DistutilsInternalError(msg) from None
+
+        # 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.
+
+        action_is_recursive = action.startswith('recursive-')
+        if action in {'graft', 'prune'}:
+            patterns = [dir_pattern]
+        extra_log_args = (dir,) if action_is_recursive else ()
+        log_tmpl = log_map[action]
+
+        self.debug_print(
+            ' '.join(
+                [action] + ([dir] if action_is_recursive else []) + patterns,
+            )
+        )
+        for pattern in patterns:
+            if not process_action(pattern):
+                log.warn(log_tmpl, pattern, *extra_log_args)
+
+    def _remove_files(self, predicate):
+        """
+        Remove all files from the file list that match the predicate.
+        Return True if any matching files were removed
+        """
+        found = False
+        for i in range(len(self.files) - 1, -1, -1):
+            if predicate(self.files[i]):
+                self.debug_print(" removing " + self.files[i])
+                del self.files[i]
+                found = True
+        return found
+
+    def include(self, pattern):
+        """Include files that match 'pattern'."""
+        found = [f for f in glob(pattern) if not os.path.isdir(f)]
+        self.extend(found)
+        return bool(found)
+
+    def exclude(self, pattern):
+        """Exclude files that match 'pattern'."""
+        match = translate_pattern(pattern)
+        return self._remove_files(match.match)
+
+    def recursive_include(self, dir, pattern):
+        """
+        Include all files anywhere in 'dir/' that match the pattern.
+        """
+        full_pattern = os.path.join(dir, '**', pattern)
+        found = [f for f in glob(full_pattern, recursive=True) if not os.path.isdir(f)]
+        self.extend(found)
+        return bool(found)
+
+    def recursive_exclude(self, dir, pattern):
+        """
+        Exclude any file anywhere in 'dir/' that match the pattern.
+        """
+        match = translate_pattern(os.path.join(dir, '**', pattern))
+        return self._remove_files(match.match)
+
+    def graft(self, dir):
+        """Include all files from 'dir/'."""
+        found = [
+            item
+            for match_dir in glob(dir)
+            for item in distutils.filelist.findall(match_dir)
+        ]
+        self.extend(found)
+        return bool(found)
+
+    def prune(self, dir):
+        """Filter out files from 'dir/'."""
+        match = translate_pattern(os.path.join(dir, '**'))
+        return self._remove_files(match.match)
+
+    def global_include(self, pattern):
+        """
+        Include all files anywhere in the current directory that match the
+        pattern. This is very inefficient on large file trees.
+        """
+        if self.allfiles is None:
+            self.findall()
+        match = translate_pattern(os.path.join('**', pattern))
+        found = [f for f in self.allfiles if match.match(f)]
+        self.extend(found)
+        return bool(found)
+
+    def global_exclude(self, pattern):
+        """
+        Exclude all files anywhere that match the pattern.
+        """
+        match = translate_pattern(os.path.join('**', pattern))
+        return self._remove_files(match.match)
+
+    def append(self, item) -> None:
+        item = item.removesuffix('\r')  # Fix older sdists built on Windows
+        path = convert_path(item)
+
+        if self._safe_path(path):
+            self.files.append(path)
+
+    def extend(self, paths) -> None:
+        self.files.extend(filter(self._safe_path, paths))
+
+    def _repair(self):
+        """
+        Replace self.files with only safe paths
+
+        Because some owners of FileList manipulate the underlying
+        ``files`` attribute directly, this method must be called to
+        repair those paths.
+        """
+        self.files = list(filter(self._safe_path, self.files))
+
+    def _safe_path(self, path):
+        enc_warn = "'%s' not %s encodable -- skipping"
+
+        # To avoid accidental trans-codings errors, first to unicode
+        u_path = unicode_utils.filesys_decode(path)
+        if u_path is None:
+            log.warn(f"'{path}' in unexpected encoding -- skipping")
+            return False
+
+        # Must ensure utf-8 encodability
+        utf8_path = unicode_utils.try_encode(u_path, "utf-8")
+        if utf8_path is None:
+            log.warn(enc_warn, path, 'utf-8')
+            return False
+
+        try:
+            # ignore egg-info paths
+            is_egg_info = ".egg-info" in u_path or b".egg-info" in utf8_path
+            if self.ignore_egg_info_dir and is_egg_info:
+                return False
+            # accept is either way checks out
+            if os.path.exists(u_path) or os.path.exists(utf8_path):
+                return True
+        # this will catch any encode errors decoding u_path
+        except UnicodeEncodeError:
+            log.warn(enc_warn, path, sys.getfilesystemencoding())
+
+
+class manifest_maker(sdist):
+    template = "MANIFEST.in"
+
+    def initialize_options(self) -> None:
+        self.use_defaults = True
+        self.prune = True
+        self.manifest_only = True
+        self.force_manifest = True
+        self.ignore_egg_info_dir = False
+
+    def finalize_options(self) -> None:
+        pass
+
+    def run(self) -> None:
+        self.filelist = FileList(ignore_egg_info_dir=self.ignore_egg_info_dir)
+        if not os.path.exists(self.manifest):
+            self.write_manifest()  # it must exist so it'll get in the list
+        self.add_defaults()
+        if os.path.exists(self.template):
+            self.read_template()
+        self.add_license_files()
+        self._add_referenced_files()
+        self.prune_file_list()
+        self.filelist.sort()
+        self.filelist.remove_duplicates()
+        self.write_manifest()
+
+    def _manifest_normalize(self, path):
+        path = unicode_utils.filesys_decode(path)
+        return path.replace(os.sep, '/')
+
+    def write_manifest(self) -> None:
+        """
+        Write the file list in 'self.filelist' to the manifest file
+        named by 'self.manifest'.
+        """
+        self.filelist._repair()
+
+        # Now _repairs should encodability, but not unicode
+        files = [self._manifest_normalize(f) for f in self.filelist.files]
+        msg = f"writing manifest file '{self.manifest}'"
+        self.execute(write_file, (self.manifest, files), msg)
+
+    def warn(self, msg) -> None:
+        if not self._should_suppress_warning(msg):
+            sdist.warn(self, msg)
+
+    @staticmethod
+    def _should_suppress_warning(msg):
+        """
+        suppress missing-file warnings from sdist
+        """
+        return re.match(r"standard file .*not found", msg)
+
+    def add_defaults(self) -> None:
+        sdist.add_defaults(self)
+        self.filelist.append(self.template)
+        self.filelist.append(self.manifest)
+        rcfiles = list(walk_revctrl())
+        if rcfiles:
+            self.filelist.extend(rcfiles)
+        elif os.path.exists(self.manifest):
+            self.read_manifest()
+
+        if os.path.exists("setup.py"):
+            # setup.py should be included by default, even if it's not
+            # the script called to create the sdist
+            self.filelist.append("setup.py")
+
+        ei_cmd = self.get_finalized_command('egg_info')
+        self.filelist.graft(ei_cmd.egg_info)
+
+    def add_license_files(self) -> None:
+        license_files = self.distribution.metadata.license_files or []
+        for lf in license_files:
+            log.info("adding license file '%s'", lf)
+        self.filelist.extend(license_files)
+
+    def _add_referenced_files(self):
+        """Add files referenced by the config (e.g. `file:` directive) to filelist"""
+        referenced = getattr(self.distribution, '_referenced_files', [])
+        # ^-- fallback if dist comes from distutils or is a custom class
+        for rf in referenced:
+            log.debug("adding file referenced by config '%s'", rf)
+        self.filelist.extend(referenced)
+
+    def _safe_data_files(self, build_py):
+        """
+        The parent class implementation of this method
+        (``sdist``) will try to include data files, which
+        might cause recursion problems when
+        ``include_package_data=True``.
+
+        Therefore, avoid triggering any attempt of
+        analyzing/building the manifest again.
+        """
+        if hasattr(build_py, 'get_data_files_without_manifest'):
+            return build_py.get_data_files_without_manifest()
+
+        SetuptoolsDeprecationWarning.emit(
+            "`build_py` command does not inherit from setuptools' `build_py`.",
+            """
+            Custom 'build_py' does not implement 'get_data_files_without_manifest'.
+            Please extend command classes from setuptools instead of distutils.
+            """,
+            see_url="https://peps.python.org/pep-0632/",
+            # due_date not defined yet, old projects might still do it?
+        )
+        return build_py.get_data_files()
+
+
+def write_file(filename, contents) -> None:
+    """Create a file with the specified name and write 'contents' (a
+    sequence of strings without line terminators) to it.
+    """
+    contents = "\n".join(contents)
+
+    # assuming the contents has been vetted for utf-8 encoding
+    contents = contents.encode("utf-8")
+
+    with open(filename, "wb") as f:  # always write POSIX-style manifest
+        f.write(contents)
+
+
+def write_pkg_info(cmd, basename, filename) -> None:
+    log.info("writing %s", filename)
+    metadata = cmd.distribution.metadata
+    metadata.version, oldver = cmd.egg_version, metadata.version
+    metadata.name, oldname = cmd.egg_name, metadata.name
+
+    try:
+        metadata.write_pkg_info(cmd.egg_info)
+    finally:
+        metadata.name, metadata.version = oldname, oldver
+
+    safe = getattr(cmd.distribution, 'zip_safe', None)
+
+    bdist_egg.write_safety_flag(cmd.egg_info, safe)
+
+
+def warn_depends_obsolete(cmd, basename, filename) -> None:
+    """
+    Unused: left to avoid errors when updating (from source) from <= 67.8.
+    Old installations have a .dist-info directory with the entry-point
+    ``depends.txt = setuptools.command.egg_info:warn_depends_obsolete``.
+    This may trigger errors when running the first egg_info in build_meta.
+    TODO: Remove this function in a version sufficiently > 68.
+    """
+
+
+# Export API used in entry_points
+write_requirements = _requirestxt.write_requirements
+write_setup_requirements = _requirestxt.write_setup_requirements
+
+
+def write_toplevel_names(cmd, basename, filename) -> None:
+    pkgs = dict.fromkeys([
+        k.split('.', 1)[0] for k in cmd.distribution.iter_distribution_names()
+    ])
+    cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
+
+
+def overwrite_arg(cmd, basename, filename) -> None:
+    write_arg(cmd, basename, filename, True)
+
+
+def write_arg(cmd, basename, filename, force: bool = False) -> None:
+    argname = os.path.splitext(basename)[0]
+    value = getattr(cmd.distribution, argname, None)
+    if value is not None:
+        value = '\n'.join(value) + '\n'
+    cmd.write_or_delete_file(argname, filename, value, force)
+
+
+def write_entries(cmd, basename, filename) -> None:
+    eps = _entry_points.load(cmd.distribution.entry_points)
+    defn = _entry_points.render(eps)
+    cmd.write_or_delete_file('entry points', filename, defn, True)
+
+
+def _egg_basename(egg_name, egg_version, py_version=None, platform=None):
+    """Compute filename of the output egg. Private API."""
+    name = _normalization.filename_component(egg_name)
+    version = _normalization.filename_component(egg_version)
+    egg = f"{name}-{version}-py{py_version or PY_MAJOR}"
+    if platform:
+        egg += f"-{platform}"
+    return egg
+
+
+class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
+    """Deprecated behavior warning for EggInfo, bypassing suppression."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/install.py b/.venv/lib/python3.12/site-packages/setuptools/command/install.py
new file mode 100644
index 0000000..19ca601
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/install.py
@@ -0,0 +1,131 @@
+from __future__ import annotations
+
+import inspect
+import platform
+from collections.abc import Callable
+from typing import TYPE_CHECKING, Any, ClassVar
+
+from ..dist import Distribution
+from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
+
+import distutils.command.install as orig
+from distutils.errors import DistutilsArgError
+
+if TYPE_CHECKING:
+    # This is only used for a type-cast, don't import at runtime or it'll cause deprecation warnings
+    from .easy_install import easy_install as easy_install_cls
+else:
+    easy_install_cls = None
+
+
+def __getattr__(name: str):  # pragma: no cover
+    if name == "_install":
+        SetuptoolsDeprecationWarning.emit(
+            "`setuptools.command._install` was an internal implementation detail "
+            "that was left in for numpy<1.9 support.",
+            due_date=(2025, 5, 2),  # Originally added on 2024-11-01
+        )
+        return orig.install
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+class install(orig.install):
+    """Use easy_install to install the package, w/dependencies"""
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    user_options = orig.install.user_options + [
+        ('old-and-unmanageable', None, "Try not to use this!"),
+        (
+            'single-version-externally-managed',
+            None,
+            "used by system package builders to create 'flat' eggs",
+        ),
+    ]
+    boolean_options = orig.install.boolean_options + [
+        'old-and-unmanageable',
+        'single-version-externally-managed',
+    ]
+    # Type the same as distutils.command.install.install.sub_commands
+    # Must keep the second tuple item potentially None due to invariance
+    new_commands: ClassVar[list[tuple[str, Callable[[Any], bool] | None]]] = [
+        ('install_egg_info', lambda self: True),
+        ('install_scripts', lambda self: True),
+    ]
+    _nc = dict(new_commands)
+
+    def initialize_options(self):
+        SetuptoolsDeprecationWarning.emit(
+            "setup.py install is deprecated.",
+            """
+            Please avoid running ``setup.py`` directly.
+            Instead, use pypa/build, pypa/installer or other
+            standards-based tools.
+            """,
+            see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html",
+            due_date=(2025, 10, 31),
+        )
+
+        super().initialize_options()
+        self.old_and_unmanageable = None
+        self.single_version_externally_managed = None
+
+    def finalize_options(self) -> None:
+        super().finalize_options()
+        if self.root:
+            self.single_version_externally_managed = True
+        elif self.single_version_externally_managed:
+            if not self.root and not self.record:
+                raise DistutilsArgError(
+                    "You must specify --record or --root when building system packages"
+                )
+
+    def handle_extra_path(self):
+        if self.root or self.single_version_externally_managed:
+            # explicit backward-compatibility mode, allow extra_path to work
+            return orig.install.handle_extra_path(self)
+
+        # Ignore extra_path when installing an egg (or being run by another
+        # command without --root or --single-version-externally-managed
+        self.path_file = None
+        self.extra_dirs = ''
+        return None
+
+    @staticmethod
+    def _called_from_setup(run_frame):
+        """
+        Attempt to detect whether run() was called from setup() or by another
+        command.  If called by setup(), the parent caller will be the
+        'run_command' method in 'distutils.dist', and *its* caller will be
+        the 'run_commands' method.  If called any other way, the
+        immediate caller *might* be 'run_command', but it won't have been
+        called by 'run_commands'. Return True in that case or if a call stack
+        is unavailable. Return False otherwise.
+        """
+        if run_frame is None:
+            msg = "Call stack not available. bdist_* commands may fail."
+            SetuptoolsWarning.emit(msg)
+            if platform.python_implementation() == 'IronPython':
+                msg = "For best results, pass -X:Frames to enable call stack."
+                SetuptoolsWarning.emit(msg)
+            return True
+
+        frames = inspect.getouterframes(run_frame)
+        for frame in frames[2:4]:
+            (caller,) = frame[:1]
+            info = inspect.getframeinfo(caller)
+            caller_module = caller.f_globals.get('__name__', '')
+
+            if caller_module == "setuptools.dist" and info.function == "run_command":
+                # Starting from v61.0.0 setuptools overwrites dist.run_command
+                continue
+
+            return caller_module == 'distutils.dist' and info.function == 'run_commands'
+
+        return False
+
+
+# XXX Python 3.1 doesn't see _nc if this is inside the class
+install.sub_commands = [
+    cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc
+] + install.new_commands
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/install_egg_info.py b/.venv/lib/python3.12/site-packages/setuptools/command/install_egg_info.py
new file mode 100644
index 0000000..46138d2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/install_egg_info.py
@@ -0,0 +1,57 @@
+import os
+
+from setuptools import Command, namespaces
+from setuptools.archive_util import unpack_archive
+
+from .._path import ensure_directory
+
+from distutils import dir_util, log
+
+
+class install_egg_info(namespaces.Installer, Command):
+    """Install an .egg-info directory for the package"""
+
+    description = "Install an .egg-info directory for the package"
+
+    user_options = [
+        ('install-dir=', 'd', "directory to install to"),
+    ]
+
+    def initialize_options(self):
+        self.install_dir = None
+
+    def finalize_options(self) -> None:
+        self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))
+        ei_cmd = self.get_finalized_command("egg_info")
+        basename = f"{ei_cmd._get_egg_basename()}.egg-info"
+        self.source = ei_cmd.egg_info
+        self.target = os.path.join(self.install_dir, basename)
+        self.outputs: list[str] = []
+
+    def run(self) -> None:
+        self.run_command('egg_info')
+        if os.path.isdir(self.target) and not os.path.islink(self.target):
+            dir_util.remove_tree(self.target)
+        elif os.path.exists(self.target):
+            self.execute(os.unlink, (self.target,), "Removing " + self.target)
+        ensure_directory(self.target)
+        self.execute(self.copytree, (), f"Copying {self.source} to {self.target}")
+        self.install_namespaces()
+
+    def get_outputs(self):
+        return self.outputs
+
+    def copytree(self) -> None:
+        # Copy the .egg-info tree to site-packages
+        def skimmer(src, dst):
+            # filter out source-control directories; note that 'src' is always
+            # a '/'-separated path, regardless of platform.  'dst' is a
+            # platform-specific path.
+            for skip in '.svn/', 'CVS/':
+                if src.startswith(skip) or '/' + skip in src:
+                    return None
+            self.outputs.append(dst)
+            log.debug("Copying %s to %s", src, dst)
+            return dst
+
+        unpack_archive(self.source, self.target, skimmer)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/install_lib.py b/.venv/lib/python3.12/site-packages/setuptools/command/install_lib.py
new file mode 100644
index 0000000..8e1e072
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/install_lib.py
@@ -0,0 +1,137 @@
+from __future__ import annotations
+
+import os
+import sys
+from itertools import product, starmap
+
+from .._path import StrPath
+from ..dist import Distribution
+
+import distutils.command.install_lib as orig
+
+
+class install_lib(orig.install_lib):
+    """Don't add compiled flags to filenames of non-Python files"""
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def run(self) -> None:
+        self.build()
+        outfiles = self.install()
+        if outfiles is not None:
+            # always compile, in case we have any extension stubs to deal with
+            self.byte_compile(outfiles)
+
+    def get_exclusions(self):
+        """
+        Return a collections.Sized collections.Container of paths to be
+        excluded for single_version_externally_managed installations.
+        """
+        all_packages = (
+            pkg
+            for ns_pkg in self._get_SVEM_NSPs()
+            for pkg in self._all_packages(ns_pkg)
+        )
+
+        excl_specs = product(all_packages, self._gen_exclusion_paths())
+        return set(starmap(self._exclude_pkg_path, excl_specs))
+
+    def _exclude_pkg_path(self, pkg, exclusion_path):
+        """
+        Given a package name and exclusion path within that package,
+        compute the full exclusion path.
+        """
+        parts = pkg.split('.') + [exclusion_path]
+        return os.path.join(self.install_dir, *parts)
+
+    @staticmethod
+    def _all_packages(pkg_name):
+        """
+        >>> list(install_lib._all_packages('foo.bar.baz'))
+        ['foo.bar.baz', 'foo.bar', 'foo']
+        """
+        while pkg_name:
+            yield pkg_name
+            pkg_name, _sep, _child = pkg_name.rpartition('.')
+
+    def _get_SVEM_NSPs(self):
+        """
+        Get namespace packages (list) but only for
+        single_version_externally_managed installations and empty otherwise.
+        """
+        # TODO: is it necessary to short-circuit here? i.e. what's the cost
+        # if get_finalized_command is called even when namespace_packages is
+        # False?
+        if not self.distribution.namespace_packages:
+            return []
+
+        install_cmd = self.get_finalized_command('install')
+        svem = install_cmd.single_version_externally_managed
+
+        return self.distribution.namespace_packages if svem else []
+
+    @staticmethod
+    def _gen_exclusion_paths():
+        """
+        Generate file paths to be excluded for namespace packages (bytecode
+        cache files).
+        """
+        # always exclude the package module itself
+        yield '__init__.py'
+
+        yield '__init__.pyc'
+        yield '__init__.pyo'
+
+        if not hasattr(sys, 'implementation'):
+            return
+
+        base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag)
+        yield base + '.pyc'
+        yield base + '.pyo'
+        yield base + '.opt-1.pyc'
+        yield base + '.opt-2.pyc'
+
+    def copy_tree(
+        self,
+        infile: StrPath,
+        outfile: str,
+        # override: Using actual booleans
+        preserve_mode: bool = True,  # type: ignore[override]
+        preserve_times: bool = True,  # type: ignore[override]
+        preserve_symlinks: bool = False,  # type: ignore[override]
+        level: object = 1,
+    ) -> list[str]:
+        assert preserve_mode
+        assert preserve_times
+        assert not preserve_symlinks
+        exclude = self.get_exclusions()
+
+        if not exclude:
+            return orig.install_lib.copy_tree(self, infile, outfile)
+
+        # Exclude namespace package __init__.py* files from the output
+
+        from setuptools.archive_util import unpack_directory
+
+        from distutils import log
+
+        outfiles: list[str] = []
+
+        def pf(src: str, dst: str):
+            if dst in exclude:
+                log.warn("Skipping installation of %s (namespace package)", dst)
+                return False
+
+            log.info("copying %s -> %s", src, os.path.dirname(dst))
+            outfiles.append(dst)
+            return dst
+
+        unpack_directory(infile, outfile, pf)
+        return outfiles
+
+    def get_outputs(self):
+        outputs = orig.install_lib.get_outputs(self)
+        exclude = self.get_exclusions()
+        if exclude:
+            return [f for f in outputs if f not in exclude]
+        return outputs
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/install_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/command/install_scripts.py
new file mode 100644
index 0000000..5d7d386
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/install_scripts.py
@@ -0,0 +1,66 @@
+from __future__ import annotations
+
+import os
+import sys
+
+from .._path import ensure_directory
+from ..dist import Distribution
+
+import distutils.command.install_scripts as orig
+from distutils import log
+
+
+class install_scripts(orig.install_scripts):
+    """Do normal script install, plus any egg_info wrapper scripts"""
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def initialize_options(self) -> None:
+        orig.install_scripts.initialize_options(self)
+        self.no_ep = False
+
+    def run(self) -> None:
+        self.run_command("egg_info")
+        if self.distribution.scripts:
+            orig.install_scripts.run(self)  # run first to set up self.outfiles
+        else:
+            self.outfiles: list[str] = []
+        if self.no_ep:
+            # don't install entry point scripts into .egg file!
+            return
+        self._install_ep_scripts()
+
+    def _install_ep_scripts(self):
+        # Delay import side-effects
+        from .. import _scripts
+        from .._importlib import metadata
+
+        ei_cmd = self.get_finalized_command("egg_info")
+        dist = metadata.Distribution.at(path=ei_cmd.egg_info)
+        bs_cmd = self.get_finalized_command('build_scripts')
+        exec_param = getattr(bs_cmd, 'executable', None)
+        writer = _scripts.ScriptWriter
+        if exec_param == sys.executable:
+            # In case the path to the Python executable contains a space, wrap
+            # it so it's not split up.
+            exec_param = [exec_param]
+        # resolve the writer to the environment
+        writer = writer.best()
+        cmd = writer.command_spec_class.best().from_param(exec_param)
+        for args in writer.get_args(dist, cmd.as_header()):
+            self.write_script(*args)
+
+    def write_script(self, script_name, contents, mode: str = "t", *ignored) -> None:
+        """Write an executable file to the scripts directory"""
+        from .._shutil import attempt_chmod_verbose as chmod, current_umask
+
+        log.info("Installing %s script to %s", script_name, self.install_dir)
+        target = os.path.join(self.install_dir, script_name)
+        self.outfiles.append(target)
+
+        encoding = None if "b" in mode else "utf-8"
+        mask = current_umask()
+        ensure_directory(target)
+        with open(target, "w" + mode, encoding=encoding) as f:
+            f.write(contents)
+        chmod(target, 0o777 - mask)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/rotate.py b/.venv/lib/python3.12/site-packages/setuptools/command/rotate.py
new file mode 100644
index 0000000..9ff72f5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/rotate.py
@@ -0,0 +1,64 @@
+from __future__ import annotations
+
+import os
+from typing import ClassVar
+
+from .. import Command, _shutil
+
+from distutils import log
+from distutils.errors import DistutilsOptionError
+from distutils.util import convert_path
+
+
+class rotate(Command):
+    """Delete older distributions"""
+
+    description = "delete older distributions, keeping N newest files"
+    user_options = [
+        ('match=', 'm', "patterns to match (required)"),
+        ('dist-dir=', 'd', "directory where the distributions are"),
+        ('keep=', 'k', "number of matching distributions to keep"),
+    ]
+
+    boolean_options: ClassVar[list[str]] = []
+
+    def initialize_options(self):
+        self.match = None
+        self.dist_dir = None
+        self.keep = None
+
+    def finalize_options(self) -> None:
+        if self.match is None:
+            raise DistutilsOptionError(
+                "Must specify one or more (comma-separated) match patterns "
+                "(e.g. '.zip' or '.egg')"
+            )
+        if self.keep is None:
+            raise DistutilsOptionError("Must specify number of files to keep")
+        try:
+            self.keep = int(self.keep)
+        except ValueError as e:
+            raise DistutilsOptionError("--keep must be an integer") from e
+        if isinstance(self.match, str):
+            self.match = [convert_path(p.strip()) for p in self.match.split(',')]
+        self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
+
+    def run(self) -> None:
+        self.run_command("egg_info")
+        from glob import glob
+
+        for pattern in self.match:
+            pattern = self.distribution.get_name() + '*' + pattern
+            files = glob(os.path.join(self.dist_dir, pattern))
+            files = [(os.path.getmtime(f), f) for f in files]
+            files.sort()
+            files.reverse()
+
+            log.info("%d file(s) matching %s", len(files), pattern)
+            files = files[self.keep :]
+            for t, f in files:
+                log.info("Deleting %s", f)
+                if os.path.isdir(f):
+                    _shutil.rmtree(f)
+                else:
+                    os.unlink(f)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/saveopts.py b/.venv/lib/python3.12/site-packages/setuptools/command/saveopts.py
new file mode 100644
index 0000000..76c3cdb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/saveopts.py
@@ -0,0 +1,21 @@
+from setuptools.command.setopt import edit_config, option_base
+
+
+class saveopts(option_base):
+    """Save command-line options to a file"""
+
+    description = "save supplied options to setup.cfg or other config file"
+
+    def run(self) -> None:
+        dist = self.distribution
+        settings: dict[str, dict[str, str]] = {}
+
+        for cmd in dist.command_options:
+            if cmd == 'saveopts':
+                continue  # don't save our own options!
+
+            for opt, (src, val) in dist.get_option_dict(cmd).items():
+                if src == "command line":
+                    settings.setdefault(cmd, {})[opt] = val
+
+        edit_config(self.filename, settings)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/sdist.py b/.venv/lib/python3.12/site-packages/setuptools/command/sdist.py
new file mode 100644
index 0000000..17279ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/sdist.py
@@ -0,0 +1,218 @@
+from __future__ import annotations
+
+import contextlib
+import os
+import re
+from collections.abc import Iterator
+from itertools import chain
+from typing import ClassVar
+
+from .._importlib import metadata
+from ..dist import Distribution
+from .build import _ORIGINAL_SUBCOMMANDS
+
+import distutils.command.sdist as orig
+from distutils import log
+
+_default_revctrl = list
+
+
+def walk_revctrl(dirname='') -> Iterator:
+    """Find all files under revision control"""
+    for ep in metadata.entry_points(group='setuptools.file_finders'):
+        yield from ep.load()(dirname)
+
+
+class sdist(orig.sdist):
+    """Smart sdist that finds anything supported by revision control"""
+
+    user_options = [
+        ('formats=', None, "formats for source distribution (comma-separated list)"),
+        (
+            'keep-temp',
+            'k',
+            "keep the distribution tree around after creating archive file(s)",
+        ),
+        (
+            'dist-dir=',
+            'd',
+            "directory to put the source distribution archive(s) in [default: dist]",
+        ),
+        (
+            'owner=',
+            'u',
+            "Owner name used when creating a tar file [default: current user]",
+        ),
+        (
+            'group=',
+            'g',
+            "Group name used when creating a tar file [default: current group]",
+        ),
+    ]
+
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+    negative_opt: ClassVar[dict[str, str]] = {}
+
+    README_EXTENSIONS = ['', '.rst', '.txt', '.md']
+    READMES = tuple(f'README{ext}' for ext in README_EXTENSIONS)
+
+    def run(self) -> None:
+        self.run_command('egg_info')
+        ei_cmd = self.get_finalized_command('egg_info')
+        self.filelist = ei_cmd.filelist
+        self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt'))
+        self.check_readme()
+
+        # Run sub commands
+        for cmd_name in self.get_sub_commands():
+            self.run_command(cmd_name)
+
+        self.make_distribution()
+
+        dist_files = getattr(self.distribution, 'dist_files', [])
+        for file in self.archive_files:
+            data = ('sdist', '', file)
+            if data not in dist_files:
+                dist_files.append(data)
+
+    def initialize_options(self) -> None:
+        orig.sdist.initialize_options(self)
+
+    def make_distribution(self) -> None:
+        """
+        Workaround for #516
+        """
+        with self._remove_os_link():
+            orig.sdist.make_distribution(self)
+
+    @staticmethod
+    @contextlib.contextmanager
+    def _remove_os_link():
+        """
+        In a context, remove and restore os.link if it exists
+        """
+
+        class NoValue:
+            pass
+
+        orig_val = getattr(os, 'link', NoValue)
+        try:
+            del os.link
+        except Exception:
+            pass
+        try:
+            yield
+        finally:
+            if orig_val is not NoValue:
+                os.link = orig_val
+
+    def add_defaults(self) -> None:
+        super().add_defaults()
+        self._add_defaults_build_sub_commands()
+
+    def _add_defaults_optional(self):
+        super()._add_defaults_optional()
+        if os.path.isfile('pyproject.toml'):
+            self.filelist.append('pyproject.toml')
+
+    def _add_defaults_python(self):
+        """getting python files"""
+        if self.distribution.has_pure_modules():
+            build_py = self.get_finalized_command('build_py')
+            self.filelist.extend(build_py.get_source_files())
+            self._add_data_files(self._safe_data_files(build_py))
+
+    def _add_defaults_build_sub_commands(self):
+        build = self.get_finalized_command("build")
+        missing_cmds = set(build.get_sub_commands()) - _ORIGINAL_SUBCOMMANDS
+        # ^-- the original built-in sub-commands are already handled by default.
+        cmds = (self.get_finalized_command(c) for c in missing_cmds)
+        files = (c.get_source_files() for c in cmds if hasattr(c, "get_source_files"))
+        self.filelist.extend(chain.from_iterable(files))
+
+    def _safe_data_files(self, build_py):
+        """
+        Since the ``sdist`` class is also used to compute the MANIFEST
+        (via :obj:`setuptools.command.egg_info.manifest_maker`),
+        there might be recursion problems when trying to obtain the list of
+        data_files and ``include_package_data=True`` (which in turn depends on
+        the files included in the MANIFEST).
+
+        To avoid that, ``manifest_maker`` should be able to overwrite this
+        method and avoid recursive attempts to build/analyze the MANIFEST.
+        """
+        return build_py.data_files
+
+    def _add_data_files(self, data_files):
+        """
+        Add data files as found in build_py.data_files.
+        """
+        self.filelist.extend(
+            os.path.join(src_dir, name)
+            for _, src_dir, _, filenames in data_files
+            for name in filenames
+        )
+
+    def _add_defaults_data_files(self):
+        try:
+            super()._add_defaults_data_files()
+        except TypeError:
+            log.warn("data_files contains unexpected objects")
+
+    def prune_file_list(self) -> None:
+        super().prune_file_list()
+        # Prevent accidental inclusion of test-related cache dirs at the project root
+        sep = re.escape(os.sep)
+        self.filelist.exclude_pattern(r"^(\.tox|\.nox|\.venv)" + sep, is_regex=True)
+
+    def check_readme(self) -> None:
+        for f in self.READMES:
+            if os.path.exists(f):
+                return
+        else:
+            self.warn(
+                "standard file not found: should have one of " + ', '.join(self.READMES)
+            )
+
+    def make_release_tree(self, base_dir, files) -> None:
+        orig.sdist.make_release_tree(self, base_dir, files)
+
+        # Save any egg_info command line options used to create this sdist
+        dest = os.path.join(base_dir, 'setup.cfg')
+        if hasattr(os, 'link') and os.path.exists(dest):
+            # unlink and re-copy, since it might be hard-linked, and
+            # we don't want to change the source version
+            os.unlink(dest)
+            self.copy_file('setup.cfg', dest)
+
+        self.get_finalized_command('egg_info').save_version_info(dest)
+
+    def _manifest_is_not_generated(self):
+        # check for special comment used in 2.7.1 and higher
+        if not os.path.isfile(self.manifest):
+            return False
+
+        with open(self.manifest, 'rb') as fp:
+            first_line = fp.readline()
+        return first_line != b'# file GENERATED by distutils, do NOT edit\n'
+
+    def read_manifest(self) -> None:
+        """Read the manifest file (named by 'self.manifest') and use it to
+        fill in 'self.filelist', the list of files to include in the source
+        distribution.
+        """
+        log.info("reading manifest file '%s'", self.manifest)
+        manifest = open(self.manifest, 'rb')
+        for bytes_line in manifest:
+            # The manifest must contain UTF-8. See #303.
+            try:
+                line = bytes_line.decode('UTF-8')
+            except UnicodeDecodeError:
+                log.warn(f"{line!r} not UTF-8 decodable -- skipping")
+                continue
+            # ignore comments and blank lines
+            line = line.strip()
+            if line.startswith('#') or not line:
+                continue
+            self.filelist.append(line)
+        manifest.close()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/setopt.py b/.venv/lib/python3.12/site-packages/setuptools/command/setopt.py
new file mode 100644
index 0000000..349041e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/setopt.py
@@ -0,0 +1,139 @@
+import configparser
+import os
+
+from .. import Command
+from ..unicode_utils import _cfg_read_utf8_with_fallback
+
+import distutils
+from distutils import log
+from distutils.errors import DistutilsOptionError
+from distutils.util import convert_path
+
+__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
+
+
+def config_file(kind="local"):
+    """Get the filename of the distutils, local, global, or per-user config
+
+    `kind` must be one of "local", "global", or "user"
+    """
+    if kind == 'local':
+        return 'setup.cfg'
+    if kind == 'global':
+        return os.path.join(os.path.dirname(distutils.__file__), 'distutils.cfg')
+    if kind == 'user':
+        dot = os.name == 'posix' and '.' or ''
+        return os.path.expanduser(convert_path(f"~/{dot}pydistutils.cfg"))
+    raise ValueError("config_file() type must be 'local', 'global', or 'user'", kind)
+
+
+def edit_config(filename, settings) -> None:
+    """Edit a configuration file to include `settings`
+
+    `settings` is a dictionary of dictionaries or ``None`` values, keyed by
+    command/section name.  A ``None`` value means to delete the entire section,
+    while a dictionary lists settings to be changed or deleted in that section.
+    A setting of ``None`` means to delete that setting.
+    """
+    log.debug("Reading configuration from %s", filename)
+    opts = configparser.RawConfigParser()
+    opts.optionxform = lambda optionstr: optionstr  # type: ignore[method-assign] # overriding method
+    _cfg_read_utf8_with_fallback(opts, filename)
+
+    for section, options in settings.items():
+        if options is None:
+            log.info("Deleting section [%s] from %s", section, filename)
+            opts.remove_section(section)
+        else:
+            if not opts.has_section(section):
+                log.debug("Adding new section [%s] to %s", section, filename)
+                opts.add_section(section)
+            for option, value in options.items():
+                if value is None:
+                    log.debug("Deleting %s.%s from %s", section, option, filename)
+                    opts.remove_option(section, option)
+                    if not opts.options(section):
+                        log.info(
+                            "Deleting empty [%s] section from %s", section, filename
+                        )
+                        opts.remove_section(section)
+                else:
+                    log.debug(
+                        "Setting %s.%s to %r in %s", section, option, value, filename
+                    )
+                    opts.set(section, option, value)
+
+    log.info("Writing %s", filename)
+    with open(filename, 'w', encoding="utf-8") as f:
+        opts.write(f)
+
+
+class option_base(Command):
+    """Abstract base class for commands that mess with config files"""
+
+    user_options = [
+        ('global-config', 'g', "save options to the site-wide distutils.cfg file"),
+        ('user-config', 'u', "save options to the current user's pydistutils.cfg file"),
+        ('filename=', 'f', "configuration file to use (default=setup.cfg)"),
+    ]
+
+    boolean_options = [
+        'global-config',
+        'user-config',
+    ]
+
+    def initialize_options(self):
+        self.global_config = None
+        self.user_config = None
+        self.filename = None
+
+    def finalize_options(self) -> None:
+        filenames = []
+        if self.global_config:
+            filenames.append(config_file('global'))
+        if self.user_config:
+            filenames.append(config_file('user'))
+        if self.filename is not None:
+            filenames.append(self.filename)
+        if not filenames:
+            filenames.append(config_file('local'))
+        if len(filenames) > 1:
+            raise DistutilsOptionError(
+                "Must specify only one configuration file option", filenames
+            )
+        (self.filename,) = filenames
+
+
+class setopt(option_base):
+    """Save command-line options to a file"""
+
+    description = "set an option in setup.cfg or another config file"
+
+    user_options = [
+        ('command=', 'c', 'command to set an option for'),
+        ('option=', 'o', 'option to set'),
+        ('set-value=', 's', 'value of the option'),
+        ('remove', 'r', 'remove (unset) the value'),
+    ] + option_base.user_options
+
+    boolean_options = option_base.boolean_options + ['remove']
+
+    def initialize_options(self):
+        option_base.initialize_options(self)
+        self.command = None
+        self.option = None
+        self.set_value = None
+        self.remove = None
+
+    def finalize_options(self) -> None:
+        option_base.finalize_options(self)
+        if self.command is None or self.option is None:
+            raise DistutilsOptionError("Must specify --command *and* --option")
+        if self.set_value is None and not self.remove:
+            raise DistutilsOptionError("Must specify --set-value or --remove")
+
+    def run(self) -> None:
+        edit_config(
+            self.filename,
+            {self.command: {self.option.replace('-', '_'): self.set_value}},
+        )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/command/test.py b/.venv/lib/python3.12/site-packages/setuptools/command/test.py
new file mode 100644
index 0000000..5d03c91
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/command/test.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+from typing import NoReturn
+
+from setuptools import Command
+from setuptools.warnings import SetuptoolsDeprecationWarning
+
+
+# Would restrict to Literal["test"], but mypy doesn't support it: https://github.com/python/mypy/issues/8203
+def __getattr__(name: str) -> type[_test]:
+    if name == 'test':
+        SetuptoolsDeprecationWarning.emit(
+            "The test command is disabled and references to it are deprecated.",
+            "Please remove any references to `setuptools.command.test` in all "
+            "supported versions of the affected package.",
+            due_date=(2024, 11, 15),
+            stacklevel=2,
+        )
+        return _test
+    raise AttributeError(name)
+
+
+class _test(Command):
+    """
+    Stub to warn when test command is referenced or used.
+    """
+
+    description = "stub for old test command (do not use)"
+
+    user_options = [
+        ('test-module=', 'm', "Run 'test_suite' in specified module"),
+        (
+            'test-suite=',
+            's',
+            "Run single test, case or suite (e.g. 'module.test_suite')",
+        ),
+        ('test-runner=', 'r', "Test runner to use"),
+    ]
+
+    def initialize_options(self) -> None:
+        pass
+
+    def finalize_options(self) -> None:
+        pass
+
+    def run(self) -> NoReturn:
+        raise RuntimeError("Support for the test command was removed in Setuptools 72")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/compat/py310.py b/.venv/lib/python3.12/site-packages/setuptools/compat/py310.py
new file mode 100644
index 0000000..58a4d9f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/compat/py310.py
@@ -0,0 +1,20 @@
+import sys
+
+__all__ = ['tomllib']
+
+
+if sys.version_info >= (3, 11):
+    import tomllib
+else:  # pragma: no cover
+    import tomli as tomllib
+
+
+if sys.version_info >= (3, 11):
+
+    def add_note(ex, note):
+        ex.add_note(note)
+
+else:  # pragma: no cover
+
+    def add_note(ex, note):
+        vars(ex).setdefault('__notes__', []).append(note)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/compat/py311.py b/.venv/lib/python3.12/site-packages/setuptools/compat/py311.py
new file mode 100644
index 0000000..52b58af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/compat/py311.py
@@ -0,0 +1,27 @@
+from __future__ import annotations
+
+import shutil
+import sys
+from typing import TYPE_CHECKING, Any, Callable
+
+if TYPE_CHECKING:
+    from _typeshed import ExcInfo, StrOrBytesPath
+    from typing_extensions import TypeAlias
+
+# Same as shutil._OnExcCallback from typeshed
+_OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object]
+
+
+def shutil_rmtree(
+    path: StrOrBytesPath,
+    ignore_errors: bool = False,
+    onexc: _OnExcCallback | None = None,
+) -> None:
+    if sys.version_info >= (3, 12):
+        return shutil.rmtree(path, ignore_errors, onexc=onexc)
+
+    def _handler(fn: Callable[..., Any], path: str, excinfo: ExcInfo) -> None:
+        if onexc:
+            onexc(fn, path, excinfo[1])
+
+    return shutil.rmtree(path, ignore_errors, onerror=_handler)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/compat/py312.py b/.venv/lib/python3.12/site-packages/setuptools/compat/py312.py
new file mode 100644
index 0000000..b20c5f6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/compat/py312.py
@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+import sys
+
+if sys.version_info >= (3, 12, 4):
+    # Python 3.13 should support `.pth` files encoded in UTF-8
+    # See discussion in https://github.com/python/cpython/issues/77102
+    PTH_ENCODING: str | None = "utf-8"
+else:
+    from .py39 import LOCALE_ENCODING
+
+    # PTH_ENCODING = "locale"
+    PTH_ENCODING = LOCALE_ENCODING
diff --git a/.venv/lib/python3.12/site-packages/setuptools/compat/py39.py b/.venv/lib/python3.12/site-packages/setuptools/compat/py39.py
new file mode 100644
index 0000000..04a4abe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/compat/py39.py
@@ -0,0 +1,9 @@
+import sys
+
+# Explicitly use the ``"locale"`` encoding in versions that support it,
+# otherwise just rely on the implicit handling of ``encoding=None``.
+# Since all platforms that support ``EncodingWarning`` also support
+# ``encoding="locale"``, this can be used to suppress the warning.
+# However, please try to use UTF-8 when possible
+# (.pth files are the notorious exception: python/cpython#77102, pypa/setuptools#3937).
+LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/NOTICE b/.venv/lib/python3.12/site-packages/setuptools/config/NOTICE
new file mode 100644
index 0000000..0186451
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/NOTICE
@@ -0,0 +1,10 @@
+The following files include code from opensource projects
+(either as direct copies or modified versions):
+
+- `setuptools.schema.json`, `distutils.schema.json`:
+    - project: `validate-pyproject` - licensed under MPL-2.0
+      (https://github.com/abravalheri/validate-pyproject):
+
+      This Source Code Form is subject to the terms of the Mozilla Public
+      License, v. 2.0. If a copy of the MPL was not distributed with this file,
+      You can obtain one at https://mozilla.org/MPL/2.0/.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/config/__init__.py
new file mode 100644
index 0000000..fcc7d00
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/__init__.py
@@ -0,0 +1,43 @@
+"""For backward compatibility, expose main functions from
+``setuptools.config.setupcfg``
+"""
+
+from functools import wraps
+from typing import Callable, TypeVar, cast
+
+from ..warnings import SetuptoolsDeprecationWarning
+from . import setupcfg
+
+Fn = TypeVar("Fn", bound=Callable)
+
+__all__ = ('parse_configuration', 'read_configuration')
+
+
+def _deprecation_notice(fn: Fn) -> Fn:
+    @wraps(fn)
+    def _wrapper(*args, **kwargs):
+        SetuptoolsDeprecationWarning.emit(
+            "Deprecated API usage.",
+            f"""
+            As setuptools moves its configuration towards `pyproject.toml`,
+            `{__name__}.{fn.__name__}` became deprecated.
+
+            For the time being, you can use the `{setupcfg.__name__}` module
+            to access a backward compatible API, but this module is provisional
+            and might be removed in the future.
+
+            To read project metadata, consider using
+            ``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
+            For simple scenarios, you can also try parsing the file directly
+            with the help of ``configparser``.
+            """,
+            # due_date not defined yet, because the community still heavily relies on it
+            # Warning introduced in 24 Mar 2022
+        )
+        return fn(*args, **kwargs)
+
+    return cast(Fn, _wrapper)
+
+
+read_configuration = _deprecation_notice(setupcfg.read_configuration)
+parse_configuration = _deprecation_notice(setupcfg.parse_configuration)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_apply_pyprojecttoml.py b/.venv/lib/python3.12/site-packages/setuptools/config/_apply_pyprojecttoml.py
new file mode 100644
index 0000000..4559193
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_apply_pyprojecttoml.py
@@ -0,0 +1,534 @@
+"""Translation layer between pyproject config and setuptools distribution and
+metadata objects.
+
+The distribution and metadata objects are modeled after (an old version of)
+core metadata, therefore configs in the format specified for ``pyproject.toml``
+need to be processed before being applied.
+
+**PRIVATE MODULE**: API reserved for setuptools internal usage only.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+from collections.abc import Mapping
+from email.headerregistry import Address
+from functools import partial, reduce
+from inspect import cleandoc
+from itertools import chain
+from types import MappingProxyType
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
+
+from .. import _static
+from .._path import StrPath
+from ..errors import InvalidConfigError, RemovedConfigError
+from ..extension import Extension
+from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+    from setuptools._importlib import metadata
+    from setuptools.dist import Distribution
+
+    from distutils.dist import _OptionsList  # Comes from typeshed
+
+
+EMPTY: Mapping = MappingProxyType({})  # Immutable dict-like
+_ProjectReadmeValue: TypeAlias = Union[str, dict[str, str]]
+_Correspondence: TypeAlias = Callable[["Distribution", Any, Union[StrPath, None]], None]
+_T = TypeVar("_T")
+
+_logger = logging.getLogger(__name__)
+
+
+def apply(dist: Distribution, config: dict, filename: StrPath) -> Distribution:
+    """Apply configuration dict read with :func:`read_configuration`"""
+
+    if not config:
+        return dist  # short-circuit unrelated pyproject.toml file
+
+    root_dir = os.path.dirname(filename) or "."
+
+    _apply_project_table(dist, config, root_dir)
+    _apply_tool_table(dist, config, filename)
+
+    current_directory = os.getcwd()
+    os.chdir(root_dir)
+    try:
+        dist._finalize_requires()
+        dist._finalize_license_expression()
+        dist._finalize_license_files()
+    finally:
+        os.chdir(current_directory)
+
+    return dist
+
+
+def _apply_project_table(dist: Distribution, config: dict, root_dir: StrPath):
+    orig_config = config.get("project", {})
+    if not orig_config:
+        return  # short-circuit
+
+    project_table = {k: _static.attempt_conversion(v) for k, v in orig_config.items()}
+    _handle_missing_dynamic(dist, project_table)
+    _unify_entry_points(project_table)
+
+    for field, value in project_table.items():
+        norm_key = json_compatible_key(field)
+        corresp = PYPROJECT_CORRESPONDENCE.get(norm_key, norm_key)
+        if callable(corresp):
+            corresp(dist, value, root_dir)
+        else:
+            _set_config(dist, corresp, value)
+
+
+def _apply_tool_table(dist: Distribution, config: dict, filename: StrPath):
+    tool_table = config.get("tool", {}).get("setuptools", {})
+    if not tool_table:
+        return  # short-circuit
+
+    if "license-files" in tool_table:
+        if "license-files" in config.get("project", {}):
+            # https://github.com/pypa/setuptools/pull/4837#discussion_r2004983349
+            raise InvalidConfigError(
+                "'project.license-files' is defined already. "
+                "Remove 'tool.setuptools.license-files'."
+            )
+
+        pypa_guides = "guides/writing-pyproject-toml/#license-files"
+        SetuptoolsDeprecationWarning.emit(
+            "'tool.setuptools.license-files' is deprecated in favor of "
+            "'project.license-files' (available on setuptools>=77.0.0).",
+            see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+            due_date=(2027, 2, 18),  # Warning introduced on 2025-02-18
+        )
+
+    for field, value in tool_table.items():
+        norm_key = json_compatible_key(field)
+
+        if norm_key in TOOL_TABLE_REMOVALS:
+            suggestion = cleandoc(TOOL_TABLE_REMOVALS[norm_key])
+            msg = f"""
+            The parameter `tool.setuptools.{field}` was long deprecated
+            and has been removed from `pyproject.toml`.
+            """
+            raise RemovedConfigError("\n".join([cleandoc(msg), suggestion]))
+
+        norm_key = TOOL_TABLE_RENAMES.get(norm_key, norm_key)
+        corresp = TOOL_TABLE_CORRESPONDENCE.get(norm_key, norm_key)
+        if callable(corresp):
+            corresp(dist, value)
+        else:
+            _set_config(dist, corresp, value)
+
+    _copy_command_options(config, dist, filename)
+
+
+def _handle_missing_dynamic(dist: Distribution, project_table: dict):
+    """Be temporarily forgiving with ``dynamic`` fields not listed in ``dynamic``"""
+    dynamic = set(project_table.get("dynamic", []))
+    for field, getter in _PREVIOUSLY_DEFINED.items():
+        if not (field in project_table or field in dynamic):
+            value = getter(dist)
+            if value:
+                _MissingDynamic.emit(field=field, value=value)
+                project_table[field] = _RESET_PREVIOUSLY_DEFINED.get(field)
+
+
+def json_compatible_key(key: str) -> str:
+    """As defined in :pep:`566#json-compatible-metadata`"""
+    return key.lower().replace("-", "_")
+
+
+def _set_config(dist: Distribution, field: str, value: Any):
+    val = _PREPROCESS.get(field, _noop)(dist, value)
+    setter = getattr(dist.metadata, f"set_{field}", None)
+    if setter:
+        setter(val)
+    elif hasattr(dist.metadata, field) or field in SETUPTOOLS_PATCHES:
+        setattr(dist.metadata, field, val)
+    else:
+        setattr(dist, field, val)
+
+
+_CONTENT_TYPES = {
+    ".md": "text/markdown",
+    ".rst": "text/x-rst",
+    ".txt": "text/plain",
+}
+
+
+def _guess_content_type(file: str) -> str | None:
+    _, ext = os.path.splitext(file.lower())
+    if not ext:
+        return None
+
+    if ext in _CONTENT_TYPES:
+        return _static.Str(_CONTENT_TYPES[ext])
+
+    valid = ", ".join(f"{k} ({v})" for k, v in _CONTENT_TYPES.items())
+    msg = f"only the following file extensions are recognized: {valid}."
+    raise ValueError(f"Undefined content type for {file}, {msg}")
+
+
+def _long_description(
+    dist: Distribution, val: _ProjectReadmeValue, root_dir: StrPath | None
+):
+    from setuptools.config import expand
+
+    file: str | tuple[()]
+    if isinstance(val, str):
+        file = val
+        text = expand.read_files(file, root_dir)
+        ctype = _guess_content_type(file)
+    else:
+        file = val.get("file") or ()
+        text = val.get("text") or expand.read_files(file, root_dir)
+        ctype = val["content-type"]
+
+    # XXX: Is it completely safe to assume static?
+    _set_config(dist, "long_description", _static.Str(text))
+
+    if ctype:
+        _set_config(dist, "long_description_content_type", _static.Str(ctype))
+
+    if file:
+        dist._referenced_files.add(file)
+
+
+def _license(dist: Distribution, val: str | dict, root_dir: StrPath | None):
+    from setuptools.config import expand
+
+    if isinstance(val, str):
+        if getattr(dist.metadata, "license", None):
+            SetuptoolsWarning.emit("`license` overwritten by `pyproject.toml`")
+            dist.metadata.license = None
+        _set_config(dist, "license_expression", _static.Str(val))
+    else:
+        pypa_guides = "guides/writing-pyproject-toml/#license"
+        SetuptoolsDeprecationWarning.emit(
+            "`project.license` as a TOML table is deprecated",
+            "Please use a simple string containing a SPDX expression for "
+            "`project.license`. You can also use `project.license-files`. "
+            "(Both options available on setuptools>=77.0.0).",
+            see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+            due_date=(2027, 2, 18),  # Introduced on 2025-02-18
+        )
+        if "file" in val:
+            # XXX: Is it completely safe to assume static?
+            value = expand.read_files([val["file"]], root_dir)
+            _set_config(dist, "license", _static.Str(value))
+            dist._referenced_files.add(val["file"])
+        else:
+            _set_config(dist, "license", _static.Str(val["text"]))
+
+
+def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str):
+    field = []
+    email_field = []
+    for person in val:
+        if "name" not in person:
+            email_field.append(person["email"])
+        elif "email" not in person:
+            field.append(person["name"])
+        else:
+            addr = Address(display_name=person["name"], addr_spec=person["email"])
+            email_field.append(str(addr))
+
+    if field:
+        _set_config(dist, kind, _static.Str(", ".join(field)))
+    if email_field:
+        _set_config(dist, f"{kind}_email", _static.Str(", ".join(email_field)))
+
+
+def _project_urls(dist: Distribution, val: dict, _root_dir: StrPath | None):
+    _set_config(dist, "project_urls", val)
+
+
+def _python_requires(dist: Distribution, val: str, _root_dir: StrPath | None):
+    _set_config(dist, "python_requires", _static.SpecifierSet(val))
+
+
+def _dependencies(dist: Distribution, val: list, _root_dir: StrPath | None):
+    if getattr(dist, "install_requires", []):
+        msg = "`install_requires` overwritten in `pyproject.toml` (dependencies)"
+        SetuptoolsWarning.emit(msg)
+    dist.install_requires = val
+
+
+def _optional_dependencies(dist: Distribution, val: dict, _root_dir: StrPath | None):
+    if getattr(dist, "extras_require", None):
+        msg = "`extras_require` overwritten in `pyproject.toml` (optional-dependencies)"
+        SetuptoolsWarning.emit(msg)
+    dist.extras_require = val
+
+
+def _ext_modules(dist: Distribution, val: list[dict]) -> list[Extension]:
+    existing = dist.ext_modules or []
+    args = ({k.replace("-", "_"): v for k, v in x.items()} for x in val)
+    new = (Extension(**_adjust_ext_attrs(kw)) for kw in args)
+    return [*existing, *new]
+
+
+def _adjust_ext_attrs(attrs: dict) -> dict:
+    # https://github.com/pypa/setuptools/issues/4810
+    # In TOML there is no differentiation between tuples and lists,
+    # and distutils requires tuples...
+    attrs["define_macros"] = list(map(tuple, attrs.get("define_macros") or []))
+    return attrs
+
+
+def _noop(_dist: Distribution, val: _T) -> _T:
+    return val
+
+
+def _identity(val: _T) -> _T:
+    return val
+
+
+def _unify_entry_points(project_table: dict):
+    project = project_table
+    given = project.pop("entry-points", project.pop("entry_points", {}))
+    entry_points = dict(given)  # Avoid problems with static
+    renaming = {"scripts": "console_scripts", "gui_scripts": "gui_scripts"}
+    for key, value in list(project.items()):  # eager to allow modifications
+        norm_key = json_compatible_key(key)
+        if norm_key in renaming:
+            # Don't skip even if value is empty (reason: reset missing `dynamic`)
+            entry_points[renaming[norm_key]] = project.pop(key)
+
+    if entry_points:
+        project["entry-points"] = {
+            name: [f"{k} = {v}" for k, v in group.items()]
+            for name, group in entry_points.items()
+            if group  # now we can skip empty groups
+        }
+        # Sometimes this will set `project["entry-points"] = {}`, and that is
+        # intentional (for resetting configurations that are missing `dynamic`).
+
+
+def _copy_command_options(pyproject: dict, dist: Distribution, filename: StrPath):
+    tool_table = pyproject.get("tool", {})
+    cmdclass = tool_table.get("setuptools", {}).get("cmdclass", {})
+    valid_options = _valid_command_options(cmdclass)
+
+    cmd_opts = dist.command_options
+    for cmd, config in pyproject.get("tool", {}).get("distutils", {}).items():
+        cmd = json_compatible_key(cmd)
+        valid = valid_options.get(cmd, set())
+        cmd_opts.setdefault(cmd, {})
+        for key, value in config.items():
+            key = json_compatible_key(key)
+            cmd_opts[cmd][key] = (str(filename), value)
+            if key not in valid:
+                # To avoid removing options that are specified dynamically we
+                # just log a warn...
+                _logger.warning(f"Command option {cmd}.{key} is not defined")
+
+
+def _valid_command_options(cmdclass: Mapping = EMPTY) -> dict[str, set[str]]:
+    from setuptools.dist import Distribution
+
+    from .._importlib import metadata
+
+    valid_options = {"global": _normalise_cmd_options(Distribution.global_options)}
+
+    unloaded_entry_points = metadata.entry_points(group='distutils.commands')
+    loaded_entry_points = (_load_ep(ep) for ep in unloaded_entry_points)
+    entry_points = (ep for ep in loaded_entry_points if ep)
+    for cmd, cmd_class in chain(entry_points, cmdclass.items()):
+        opts = valid_options.get(cmd, set())
+        opts = opts | _normalise_cmd_options(getattr(cmd_class, "user_options", []))
+        valid_options[cmd] = opts
+
+    return valid_options
+
+
+def _load_ep(ep: metadata.EntryPoint) -> tuple[str, type] | None:
+    if ep.value.startswith("wheel.bdist_wheel"):
+        # Ignore deprecated entrypoint from wheel and avoid warning pypa/wheel#631
+        # TODO: remove check when `bdist_wheel` has been fully removed from pypa/wheel
+        return None
+
+    # Ignore all the errors
+    try:
+        return (ep.name, ep.load())
+    except Exception as ex:
+        msg = f"{ex.__class__.__name__} while trying to load entry-point {ep.name}"
+        _logger.warning(f"{msg}: {ex}")
+        return None
+
+
+def _normalise_cmd_option_key(name: str) -> str:
+    return json_compatible_key(name).strip("_=")
+
+
+def _normalise_cmd_options(desc: _OptionsList) -> set[str]:
+    return {_normalise_cmd_option_key(fancy_option[0]) for fancy_option in desc}
+
+
+def _get_previous_entrypoints(dist: Distribution) -> dict[str, list]:
+    ignore = ("console_scripts", "gui_scripts")
+    value = getattr(dist, "entry_points", None) or {}
+    return {k: v for k, v in value.items() if k not in ignore}
+
+
+def _get_previous_scripts(dist: Distribution) -> list | None:
+    value = getattr(dist, "entry_points", None) or {}
+    return value.get("console_scripts")
+
+
+def _get_previous_gui_scripts(dist: Distribution) -> list | None:
+    value = getattr(dist, "entry_points", None) or {}
+    return value.get("gui_scripts")
+
+
+def _set_static_list_metadata(attr: str, dist: Distribution, val: list) -> None:
+    """Apply distutils metadata validation but preserve "static" behaviour"""
+    meta = dist.metadata
+    setter, getter = getattr(meta, f"set_{attr}"), getattr(meta, f"get_{attr}")
+    setter(val)
+    setattr(meta, attr, _static.List(getter()))
+
+
+def _attrgetter(attr):
+    """
+    Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found
+    >>> from types import SimpleNamespace
+    >>> obj = SimpleNamespace(a=42, b=SimpleNamespace(c=13))
+    >>> _attrgetter("a")(obj)
+    42
+    >>> _attrgetter("b.c")(obj)
+    13
+    >>> _attrgetter("d")(obj) is None
+    True
+    """
+    return partial(reduce, lambda acc, x: getattr(acc, x, None), attr.split("."))
+
+
+def _some_attrgetter(*items):
+    """
+    Return the first "truth-y" attribute or None
+    >>> from types import SimpleNamespace
+    >>> obj = SimpleNamespace(a=42, b=SimpleNamespace(c=13))
+    >>> _some_attrgetter("d", "a", "b.c")(obj)
+    42
+    >>> _some_attrgetter("d", "e", "b.c", "a")(obj)
+    13
+    >>> _some_attrgetter("d", "e", "f")(obj) is None
+    True
+    """
+
+    def _acessor(obj):
+        values = (_attrgetter(i)(obj) for i in items)
+        return next((i for i in values if i is not None), None)
+
+    return _acessor
+
+
+PYPROJECT_CORRESPONDENCE: dict[str, _Correspondence] = {
+    "readme": _long_description,
+    "license": _license,
+    "authors": partial(_people, kind="author"),
+    "maintainers": partial(_people, kind="maintainer"),
+    "urls": _project_urls,
+    "dependencies": _dependencies,
+    "optional_dependencies": _optional_dependencies,
+    "requires_python": _python_requires,
+}
+
+TOOL_TABLE_RENAMES = {"script_files": "scripts"}
+TOOL_TABLE_REMOVALS = {
+    "namespace_packages": """
+        Please migrate to implicit native namespaces instead.
+        See https://packaging.python.org/en/latest/guides/packaging-namespace-packages/.
+        """,
+}
+TOOL_TABLE_CORRESPONDENCE = {
+    # Fields with corresponding core metadata need to be marked as static:
+    "obsoletes": partial(_set_static_list_metadata, "obsoletes"),
+    "provides": partial(_set_static_list_metadata, "provides"),
+    "platforms": partial(_set_static_list_metadata, "platforms"),
+}
+
+SETUPTOOLS_PATCHES = {
+    "long_description_content_type",
+    "project_urls",
+    "provides_extras",
+    "license_file",
+    "license_files",
+    "license_expression",
+}
+
+_PREPROCESS = {
+    "ext_modules": _ext_modules,
+}
+
+_PREVIOUSLY_DEFINED = {
+    "name": _attrgetter("metadata.name"),
+    "version": _attrgetter("metadata.version"),
+    "description": _attrgetter("metadata.description"),
+    "readme": _attrgetter("metadata.long_description"),
+    "requires-python": _some_attrgetter("python_requires", "metadata.python_requires"),
+    "license": _some_attrgetter("metadata.license_expression", "metadata.license"),
+    # XXX: `license-file` is currently not considered in the context of `dynamic`.
+    #      See TestPresetField.test_license_files_exempt_from_dynamic
+    "authors": _some_attrgetter("metadata.author", "metadata.author_email"),
+    "maintainers": _some_attrgetter("metadata.maintainer", "metadata.maintainer_email"),
+    "keywords": _attrgetter("metadata.keywords"),
+    "classifiers": _attrgetter("metadata.classifiers"),
+    "urls": _attrgetter("metadata.project_urls"),
+    "entry-points": _get_previous_entrypoints,
+    "scripts": _get_previous_scripts,
+    "gui-scripts": _get_previous_gui_scripts,
+    "dependencies": _attrgetter("install_requires"),
+    "optional-dependencies": _attrgetter("extras_require"),
+}
+
+
+_RESET_PREVIOUSLY_DEFINED: dict = {
+    # Fix improper setting: given in `setup.py`, but not listed in `dynamic`
+    # Use "immutable" data structures to avoid in-place modification.
+    # dict: pyproject name => value to which reset
+    "license": "",
+    # XXX: `license-file` is currently not considered in the context of `dynamic`.
+    #      See TestPresetField.test_license_files_exempt_from_dynamic
+    "authors": _static.EMPTY_LIST,
+    "maintainers": _static.EMPTY_LIST,
+    "keywords": _static.EMPTY_LIST,
+    "classifiers": _static.EMPTY_LIST,
+    "urls": _static.EMPTY_DICT,
+    "entry-points": _static.EMPTY_DICT,
+    "scripts": _static.EMPTY_DICT,
+    "gui-scripts": _static.EMPTY_DICT,
+    "dependencies": _static.EMPTY_LIST,
+    "optional-dependencies": _static.EMPTY_DICT,
+}
+
+
+class _MissingDynamic(SetuptoolsWarning):
+    _SUMMARY = "`{field}` defined outside of `pyproject.toml` is ignored."
+
+    _DETAILS = """
+    The following seems to be defined outside of `pyproject.toml`:
+
+    `{field} = {value!r}`
+
+    According to the spec (see the link below), however, setuptools CANNOT
+    consider this value unless `{field}` is listed as `dynamic`.
+
+    https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table
+
+    To prevent this problem, you can list `{field}` under `dynamic` or alternatively
+    remove the `[project]` table from your file and rely entirely on other means of
+    configuration.
+    """
+    # TODO: Consider removing this check in the future?
+    #       There is a trade-off here between improving "debug-ability" and the cost
+    #       of running/testing/maintaining these unnecessary checks...
+
+    @classmethod
+    def details(cls, field: str, value: Any) -> str:
+        return cls._DETAILS.format(field=field, value=value)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/NOTICE b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/NOTICE
new file mode 100644
index 0000000..74e8821
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/NOTICE
@@ -0,0 +1,438 @@
+The code contained in this directory was automatically generated using the
+following command:
+
+    python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t distutils=setuptools/config/distutils.schema.json -t setuptools=setuptools/config/setuptools.schema.json
+
+Please avoid changing it manually.
+
+
+You can report issues or suggest changes directly to `validate-pyproject`
+(or to the relevant plugin repository)
+
+- https://github.com/abravalheri/validate-pyproject/issues
+
+
+***
+
+The following files include code from opensource projects
+(either as direct copies or modified versions):
+
+- `fastjsonschema_exceptions.py`:
+    - project: `fastjsonschema` - licensed under BSD-3-Clause
+      (https://github.com/horejsek/python-fastjsonschema)
+- `extra_validations.py` and `format.py`, `error_reporting.py`:
+    - project: `validate-pyproject` - licensed under MPL-2.0
+      (https://github.com/abravalheri/validate-pyproject)
+
+
+Additionally the following files are automatically generated by tools provided
+by the same projects:
+
+- `__init__.py`
+- `fastjsonschema_validations.py`
+
+The relevant copyright notes and licenses are included below.
+
+
+***
+
+`fastjsonschema`
+================
+
+Copyright (c) 2018, Michal Horejsek
+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 {organization} 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.
+
+
+
+***
+
+`validate-pyproject`
+====================
+
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. "Contributor"
+
+     means each individual or legal entity that creates, contributes to the
+     creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+
+     means the combination of the Contributions of others (if any) used by a
+     Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+
+     means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+
+     means Source Code Form to which the initial Contributor has attached the
+     notice in Exhibit A, the Executable Form of such Source Code Form, and
+     Modifications of such Source Code Form, in each case including portions
+     thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+     means
+
+     a. that the initial Contributor has attached the notice described in
+        Exhibit B to the Covered Software; or
+
+     b. that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the terms of
+        a Secondary License.
+
+1.6. "Executable Form"
+
+     means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+
+     means a work that combines Covered Software with other material, in a
+     separate file or files, that is not Covered Software.
+
+1.8. "License"
+
+     means this document.
+
+1.9. "Licensable"
+
+     means having the right to grant, to the maximum extent possible, whether
+     at the time of the initial grant or subsequently, any and all of the
+     rights conveyed by this License.
+
+1.10. "Modifications"
+
+     means any of the following:
+
+     a. any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered Software; or
+
+     b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. "Patent Claims" of a Contributor
+
+      means any patent claim(s), including without limitation, method,
+      process, and apparatus claims, in any patent Licensable by such
+      Contributor that would be infringed, but for the grant of the License,
+      by the making, using, selling, offering for sale, having made, import,
+      or transfer of either its Contributions or its Contributor Version.
+
+1.12. "Secondary License"
+
+      means either the GNU General Public License, Version 2.0, the GNU Lesser
+      General Public License, Version 2.1, the GNU Affero General Public
+      License, Version 3.0, or any later versions of those licenses.
+
+1.13. "Source Code Form"
+
+      means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+
+      means an individual or a legal entity exercising rights under this
+      License. For legal entities, "You" includes any entity that controls, is
+      controlled by, or is under common control with You. For purposes of this
+      definition, "control" means (a) the power, direct or indirect, to cause
+      the direction or management of such entity, whether by contract or
+      otherwise, or (b) ownership of more than fifty percent (50%) of the
+      outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+     Each Contributor hereby grants You a world-wide, royalty-free,
+     non-exclusive license:
+
+     a. under intellectual property rights (other than patent or trademark)
+        Licensable by such Contributor to use, reproduce, make available,
+        modify, display, perform, distribute, and otherwise exploit its
+        Contributions, either on an unmodified basis, with Modifications, or
+        as part of a Larger Work; and
+
+     b. under Patent Claims of such Contributor to make, use, sell, offer for
+        sale, have made, import, and otherwise transfer either its
+        Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+     The licenses granted in Section 2.1 with respect to any Contribution
+     become effective for each Contribution on the date the Contributor first
+     distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+     The licenses granted in this Section 2 are the only rights granted under
+     this License. No additional rights or licenses will be implied from the
+     distribution or licensing of Covered Software under this License.
+     Notwithstanding Section 2.1(b) above, no patent license is granted by a
+     Contributor:
+
+     a. for any code that a Contributor has removed from Covered Software; or
+
+     b. for infringements caused by: (i) Your and any other third party's
+        modifications of Covered Software, or (ii) the combination of its
+        Contributions with other software (except as part of its Contributor
+        Version); or
+
+     c. under Patent Claims infringed by Covered Software in the absence of
+        its Contributions.
+
+     This License does not grant any rights in the trademarks, service marks,
+     or logos of any Contributor (except as may be necessary to comply with
+     the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+     No Contributor makes additional grants as a result of Your choice to
+     distribute the Covered Software under a subsequent version of this
+     License (see Section 10.2) or under the terms of a Secondary License (if
+     permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+     Each Contributor represents that the Contributor believes its
+     Contributions are its original creation(s) or it has sufficient rights to
+     grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+     This License is not intended to limit any rights You have under
+     applicable copyright doctrines of fair use, fair dealing, or other
+     equivalents.
+
+2.7. Conditions
+
+     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+     Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+     All distribution of Covered Software in Source Code Form, including any
+     Modifications that You create or to which You contribute, must be under
+     the terms of this License. You must inform recipients that the Source
+     Code Form of the Covered Software is governed by the terms of this
+     License, and how they can obtain a copy of this License. You may not
+     attempt to alter or restrict the recipients' rights in the Source Code
+     Form.
+
+3.2. Distribution of Executable Form
+
+     If You distribute Covered Software in Executable Form then:
+
+     a. such Covered Software must also be made available in Source Code Form,
+        as described in Section 3.1, and You must inform recipients of the
+        Executable Form how they can obtain a copy of such Source Code Form by
+        reasonable means in a timely manner, at a charge no more than the cost
+        of distribution to the recipient; and
+
+     b. You may distribute such Executable Form under the terms of this
+        License, or sublicense it under different terms, provided that the
+        license for the Executable Form does not attempt to limit or alter the
+        recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+     You may create and distribute a Larger Work under terms of Your choice,
+     provided that You also comply with the requirements of this License for
+     the Covered Software. If the Larger Work is a combination of Covered
+     Software with a work governed by one or more Secondary Licenses, and the
+     Covered Software is not Incompatible With Secondary Licenses, this
+     License permits You to additionally distribute such Covered Software
+     under the terms of such Secondary License(s), so that the recipient of
+     the Larger Work may, at their option, further distribute the Covered
+     Software under the terms of either this License or such Secondary
+     License(s).
+
+3.4. Notices
+
+     You may not remove or alter the substance of any license notices
+     (including copyright notices, patent notices, disclaimers of warranty, or
+     limitations of liability) contained within the Source Code Form of the
+     Covered Software, except that You may alter any license notices to the
+     extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+     You may choose to offer, and to charge a fee for, warranty, support,
+     indemnity or liability obligations to one or more recipients of Covered
+     Software. However, You may do so only on Your own behalf, and not on
+     behalf of any Contributor. You must make it absolutely clear that any
+     such warranty, support, indemnity, or liability obligation is offered by
+     You alone, and You hereby agree to indemnify every Contributor for any
+     liability incurred by such Contributor as a result of warranty, support,
+     indemnity or liability terms You offer. You may include additional
+     disclaimers of warranty and limitations of liability specific to any
+     jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+   If it is impossible for You to comply with any of the terms of this License
+   with respect to some or all of the Covered Software due to statute,
+   judicial order, or regulation then You must: (a) comply with the terms of
+   this License to the maximum extent possible; and (b) describe the
+   limitations and the code they affect. Such description must be placed in a
+   text file included with all distributions of the Covered Software under
+   this License. Except to the extent prohibited by statute or regulation,
+   such description must be sufficiently detailed for a recipient of ordinary
+   skill to be able to understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+     fail to comply with any of its terms. However, if You become compliant,
+     then the rights granted under this License from a particular Contributor
+     are reinstated (a) provisionally, unless and until such Contributor
+     explicitly and finally terminates Your grants, and (b) on an ongoing
+     basis, if such Contributor fails to notify You of the non-compliance by
+     some reasonable means prior to 60 days after You have come back into
+     compliance. Moreover, Your grants from a particular Contributor are
+     reinstated on an ongoing basis if such Contributor notifies You of the
+     non-compliance by some reasonable means, this is the first time You have
+     received notice of non-compliance with this License from such
+     Contributor, and You become compliant prior to 30 days after Your receipt
+     of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+     infringement claim (excluding declaratory judgment actions,
+     counter-claims, and cross-claims) alleging that a Contributor Version
+     directly or indirectly infringes any patent, then the rights granted to
+     You by any and all Contributors for the Covered Software under Section
+     2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+     license agreements (excluding distributors and resellers) which have been
+     validly granted by You or Your distributors under this License prior to
+     termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+   Covered Software is provided under this License on an "as is" basis,
+   without warranty of any kind, either expressed, implied, or statutory,
+   including, without limitation, warranties that the Covered Software is free
+   of defects, merchantable, fit for a particular purpose or non-infringing.
+   The entire risk as to the quality and performance of the Covered Software
+   is with You. Should any Covered Software prove defective in any respect,
+   You (not any Contributor) assume the cost of any necessary servicing,
+   repair, or correction. This disclaimer of warranty constitutes an essential
+   part of this License. No use of  any Covered Software is authorized under
+   this License except under this disclaimer.
+
+7. Limitation of Liability
+
+   Under no circumstances and under no legal theory, whether tort (including
+   negligence), contract, or otherwise, shall any Contributor, or anyone who
+   distributes Covered Software as permitted above, be liable to You for any
+   direct, indirect, special, incidental, or consequential damages of any
+   character including, without limitation, damages for lost profits, loss of
+   goodwill, work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses, even if such party shall have been
+   informed of the possibility of such damages. This limitation of liability
+   shall not apply to liability for death or personal injury resulting from
+   such party's negligence to the extent applicable law prohibits such
+   limitation. Some jurisdictions do not allow the exclusion or limitation of
+   incidental or consequential damages, so this exclusion and limitation may
+   not apply to You.
+
+8. Litigation
+
+   Any litigation relating to this License may be brought only in the courts
+   of a jurisdiction where the defendant maintains its principal place of
+   business and such litigation shall be governed by laws of that
+   jurisdiction, without reference to its conflict-of-law provisions. Nothing
+   in this Section shall prevent a party's ability to bring cross-claims or
+   counter-claims.
+
+9. Miscellaneous
+
+   This License represents the complete agreement concerning the subject
+   matter hereof. If any provision of this License is held to be
+   unenforceable, such provision shall be reformed only to the extent
+   necessary to make it enforceable. Any law or regulation which provides that
+   the language of a contract shall be construed against the drafter shall not
+   be used to construe this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+      Mozilla Foundation is the license steward. Except as provided in Section
+      10.3, no one other than the license steward has the right to modify or
+      publish new versions of this License. Each version will be given a
+      distinguishing version number.
+
+10.2. Effect of New Versions
+
+      You may distribute the Covered Software under the terms of the version
+      of the License under which You originally received the Covered Software,
+      or under the terms of any subsequent version published by the license
+      steward.
+
+10.3. Modified Versions
+
+      If you create software not governed by this License, and you want to
+      create a new license for such software, you may create and use a
+      modified version of this License if you rename the license and remove
+      any references to the name of the license steward (except to note that
+      such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+      Licenses If You choose to distribute Source Code Form that is
+      Incompatible With Secondary Licenses under the terms of this version of
+      the License, the notice described in Exhibit B of this License must be
+      attached.
+
+Exhibit A - Source Code Form License Notice
+
+      This Source Code Form is subject to the
+      terms of the Mozilla Public License, v.
+      2.0. If a copy of the MPL was not
+      distributed with this file, You can
+      obtain one at
+      https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file,
+then You may include the notice in a location (such as a LICENSE file in a
+relevant directory) where a recipient would be likely to look for such a
+notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+      This Source Code Form is "Incompatible
+      With Secondary Licenses", as defined by
+      the Mozilla Public License, v. 2.0.
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/__init__.py
new file mode 100644
index 0000000..4f612bd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/__init__.py
@@ -0,0 +1,34 @@
+from functools import reduce
+from typing import Any, Callable, Dict
+
+from . import formats
+from .error_reporting import detailed_errors, ValidationError
+from .extra_validations import EXTRA_VALIDATIONS
+from .fastjsonschema_exceptions import JsonSchemaException, JsonSchemaValueException
+from .fastjsonschema_validations import validate as _validate
+
+__all__ = [
+    "validate",
+    "FORMAT_FUNCTIONS",
+    "EXTRA_VALIDATIONS",
+    "ValidationError",
+    "JsonSchemaException",
+    "JsonSchemaValueException",
+]
+
+
+FORMAT_FUNCTIONS: Dict[str, Callable[[str], bool]] = {
+    fn.__name__.replace("_", "-"): fn
+    for fn in formats.__dict__.values()
+    if callable(fn) and not fn.__name__.startswith("_")
+}
+
+
+def validate(data: Any) -> bool:
+    """Validate the given ``data`` object using JSON Schema
+    This function raises ``ValidationError`` if ``data`` is invalid.
+    """
+    with detailed_errors():
+        _validate(data, custom_formats=FORMAT_FUNCTIONS)
+        reduce(lambda acc, fn: fn(acc), EXTRA_VALIDATIONS, data)
+    return True
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/error_reporting.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/error_reporting.py
new file mode 100644
index 0000000..9ebe6a3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/error_reporting.py
@@ -0,0 +1,338 @@
+from __future__ import annotations
+
+import io
+import json
+import logging
+import os
+import re
+import typing
+from contextlib import contextmanager
+from textwrap import indent, wrap
+from typing import Any, Generator, Iterator, Sequence
+
+from .fastjsonschema_exceptions import JsonSchemaValueException
+
+if typing.TYPE_CHECKING:
+    import sys
+
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
+_logger = logging.getLogger(__name__)
+
+_MESSAGE_REPLACEMENTS = {
+    "must be named by propertyName definition": "keys must be named by",
+    "one of contains definition": "at least one item that matches",
+    " same as const definition:": "",
+    "only specified items": "only items matching the definition",
+}
+
+_SKIP_DETAILS = (
+    "must not be empty",
+    "is always invalid",
+    "must not be there",
+)
+
+_NEED_DETAILS = {"anyOf", "oneOf", "allOf", "contains", "propertyNames", "not", "items"}
+
+_CAMEL_CASE_SPLITTER = re.compile(r"\W+|([A-Z][^A-Z\W]*)")
+_IDENTIFIER = re.compile(r"^[\w_]+$", re.IGNORECASE)
+
+_TOML_JARGON = {
+    "object": "table",
+    "property": "key",
+    "properties": "keys",
+    "property names": "keys",
+}
+
+_FORMATS_HELP = """
+For more details about `format` see
+https://validate-pyproject.readthedocs.io/en/latest/api/validate_pyproject.formats.html
+"""
+
+
+class ValidationError(JsonSchemaValueException):
+    """Report violations of a given JSON schema.
+
+    This class extends :exc:`~fastjsonschema.JsonSchemaValueException`
+    by adding the following properties:
+
+    - ``summary``: an improved version of the ``JsonSchemaValueException`` error message
+      with only the necessary information)
+
+    - ``details``: more contextual information about the error like the failing schema
+      itself and the value that violates the schema.
+
+    Depending on the level of the verbosity of the ``logging`` configuration
+    the exception message will be only ``summary`` (default) or a combination of
+    ``summary`` and ``details`` (when the logging level is set to :obj:`logging.DEBUG`).
+    """
+
+    summary = ""
+    details = ""
+    _original_message = ""
+
+    @classmethod
+    def _from_jsonschema(cls, ex: JsonSchemaValueException) -> Self:
+        formatter = _ErrorFormatting(ex)
+        obj = cls(str(formatter), ex.value, formatter.name, ex.definition, ex.rule)
+        debug_code = os.getenv("JSONSCHEMA_DEBUG_CODE_GENERATION", "false").lower()
+        if debug_code != "false":  # pragma: no cover
+            obj.__cause__, obj.__traceback__ = ex.__cause__, ex.__traceback__
+        obj._original_message = ex.message
+        obj.summary = formatter.summary
+        obj.details = formatter.details
+        return obj
+
+
+@contextmanager
+def detailed_errors() -> Generator[None, None, None]:
+    try:
+        yield
+    except JsonSchemaValueException as ex:
+        raise ValidationError._from_jsonschema(ex) from None
+
+
+class _ErrorFormatting:
+    def __init__(self, ex: JsonSchemaValueException):
+        self.ex = ex
+        self.name = f"`{self._simplify_name(ex.name)}`"
+        self._original_message: str = self.ex.message.replace(ex.name, self.name)
+        self._summary = ""
+        self._details = ""
+
+    def __str__(self) -> str:
+        if _logger.getEffectiveLevel() <= logging.DEBUG and self.details:
+            return f"{self.summary}\n\n{self.details}"
+
+        return self.summary
+
+    @property
+    def summary(self) -> str:
+        if not self._summary:
+            self._summary = self._expand_summary()
+
+        return self._summary
+
+    @property
+    def details(self) -> str:
+        if not self._details:
+            self._details = self._expand_details()
+
+        return self._details
+
+    @staticmethod
+    def _simplify_name(name: str) -> str:
+        x = len("data.")
+        return name[x:] if name.startswith("data.") else name
+
+    def _expand_summary(self) -> str:
+        msg = self._original_message
+
+        for bad, repl in _MESSAGE_REPLACEMENTS.items():
+            msg = msg.replace(bad, repl)
+
+        if any(substring in msg for substring in _SKIP_DETAILS):
+            return msg
+
+        schema = self.ex.rule_definition
+        if self.ex.rule in _NEED_DETAILS and schema:
+            summary = _SummaryWriter(_TOML_JARGON)
+            return f"{msg}:\n\n{indent(summary(schema), '    ')}"
+
+        return msg
+
+    def _expand_details(self) -> str:
+        optional = []
+        definition = self.ex.definition or {}
+        desc_lines = definition.pop("$$description", [])
+        desc = definition.pop("description", None) or " ".join(desc_lines)
+        if desc:
+            description = "\n".join(
+                wrap(
+                    desc,
+                    width=80,
+                    initial_indent="    ",
+                    subsequent_indent="    ",
+                    break_long_words=False,
+                )
+            )
+            optional.append(f"DESCRIPTION:\n{description}")
+        schema = json.dumps(definition, indent=4)
+        value = json.dumps(self.ex.value, indent=4)
+        defaults = [
+            f"GIVEN VALUE:\n{indent(value, '    ')}",
+            f"OFFENDING RULE: {self.ex.rule!r}",
+            f"DEFINITION:\n{indent(schema, '    ')}",
+        ]
+        msg = "\n\n".join(optional + defaults)
+        epilog = f"\n{_FORMATS_HELP}" if "format" in msg.lower() else ""
+        return msg + epilog
+
+
+class _SummaryWriter:
+    _IGNORE = frozenset(("description", "default", "title", "examples"))
+
+    def __init__(self, jargon: dict[str, str] | None = None):
+        self.jargon: dict[str, str] = jargon or {}
+        # Clarify confusing terms
+        self._terms = {
+            "anyOf": "at least one of the following",
+            "oneOf": "exactly one of the following",
+            "allOf": "all of the following",
+            "not": "(*NOT* the following)",
+            "prefixItems": f"{self._jargon('items')} (in order)",
+            "items": "items",
+            "contains": "contains at least one of",
+            "propertyNames": (
+                f"non-predefined acceptable {self._jargon('property names')}"
+            ),
+            "patternProperties": f"{self._jargon('properties')} named via pattern",
+            "const": "predefined value",
+            "enum": "one of",
+        }
+        # Attributes that indicate that the definition is easy and can be done
+        # inline (e.g. string and number)
+        self._guess_inline_defs = [
+            "enum",
+            "const",
+            "maxLength",
+            "minLength",
+            "pattern",
+            "format",
+            "minimum",
+            "maximum",
+            "exclusiveMinimum",
+            "exclusiveMaximum",
+            "multipleOf",
+        ]
+
+    def _jargon(self, term: str | list[str]) -> str | list[str]:
+        if isinstance(term, list):
+            return [self.jargon.get(t, t) for t in term]
+        return self.jargon.get(term, term)
+
+    def __call__(
+        self,
+        schema: dict | list[dict],
+        prefix: str = "",
+        *,
+        _path: Sequence[str] = (),
+    ) -> str:
+        if isinstance(schema, list):
+            return self._handle_list(schema, prefix, _path)
+
+        filtered = self._filter_unecessary(schema, _path)
+        simple = self._handle_simple_dict(filtered, _path)
+        if simple:
+            return f"{prefix}{simple}"
+
+        child_prefix = self._child_prefix(prefix, "  ")
+        item_prefix = self._child_prefix(prefix, "- ")
+        indent = len(prefix) * " "
+        with io.StringIO() as buffer:
+            for i, (key, value) in enumerate(filtered.items()):
+                child_path = [*_path, key]
+                line_prefix = prefix if i == 0 else indent
+                buffer.write(f"{line_prefix}{self._label(child_path)}:")
+                # ^  just the first item should receive the complete prefix
+                if isinstance(value, dict):
+                    filtered = self._filter_unecessary(value, child_path)
+                    simple = self._handle_simple_dict(filtered, child_path)
+                    buffer.write(
+                        f" {simple}"
+                        if simple
+                        else f"\n{self(value, child_prefix, _path=child_path)}"
+                    )
+                elif isinstance(value, list) and (
+                    key != "type" or self._is_property(child_path)
+                ):
+                    children = self._handle_list(value, item_prefix, child_path)
+                    sep = " " if children.startswith("[") else "\n"
+                    buffer.write(f"{sep}{children}")
+                else:
+                    buffer.write(f" {self._value(value, child_path)}\n")
+            return buffer.getvalue()
+
+    def _is_unecessary(self, path: Sequence[str]) -> bool:
+        if self._is_property(path) or not path:  # empty path => instruction @ root
+            return False
+        key = path[-1]
+        return any(key.startswith(k) for k in "$_") or key in self._IGNORE
+
+    def _filter_unecessary(
+        self, schema: dict[str, Any], path: Sequence[str]
+    ) -> dict[str, Any]:
+        return {
+            key: value
+            for key, value in schema.items()
+            if not self._is_unecessary([*path, key])
+        }
+
+    def _handle_simple_dict(self, value: dict, path: Sequence[str]) -> str | None:
+        inline = any(p in value for p in self._guess_inline_defs)
+        simple = not any(isinstance(v, (list, dict)) for v in value.values())
+        if inline or simple:
+            return f"{{{', '.join(self._inline_attrs(value, path))}}}\n"
+        return None
+
+    def _handle_list(
+        self, schemas: list, prefix: str = "", path: Sequence[str] = ()
+    ) -> str:
+        if self._is_unecessary(path):
+            return ""
+
+        repr_ = repr(schemas)
+        if all(not isinstance(e, (dict, list)) for e in schemas) and len(repr_) < 60:
+            return f"{repr_}\n"
+
+        item_prefix = self._child_prefix(prefix, "- ")
+        return "".join(
+            self(v, item_prefix, _path=[*path, f"[{i}]"]) for i, v in enumerate(schemas)
+        )
+
+    def _is_property(self, path: Sequence[str]) -> bool:
+        """Check if the given path can correspond to an arbitrarily named property"""
+        counter = 0
+        for key in path[-2::-1]:
+            if key not in {"properties", "patternProperties"}:
+                break
+            counter += 1
+
+        # If the counter if even, the path correspond to a JSON Schema keyword
+        # otherwise it can be any arbitrary string naming a property
+        return counter % 2 == 1
+
+    def _label(self, path: Sequence[str]) -> str:
+        *parents, key = path
+        if not self._is_property(path):
+            norm_key = _separate_terms(key)
+            return self._terms.get(key) or " ".join(self._jargon(norm_key))
+
+        if parents[-1] == "patternProperties":
+            return f"(regex {key!r})"
+        return repr(key)  # property name
+
+    def _value(self, value: Any, path: Sequence[str]) -> str:
+        if path[-1] == "type" and not self._is_property(path):
+            type_ = self._jargon(value)
+            return f"[{', '.join(type_)}]" if isinstance(type_, list) else type_
+        return repr(value)
+
+    def _inline_attrs(self, schema: dict, path: Sequence[str]) -> Iterator[str]:
+        for key, value in schema.items():
+            child_path = [*path, key]
+            yield f"{self._label(child_path)}: {self._value(value, child_path)}"
+
+    def _child_prefix(self, parent_prefix: str, child_prefix: str) -> str:
+        return len(parent_prefix) * " " + child_prefix
+
+
+def _separate_terms(word: str) -> list[str]:
+    """
+    >>> _separate_terms("FooBar-foo")
+    ['foo', 'bar', 'foo']
+    """
+    return [w.lower() for w in _CAMEL_CASE_SPLITTER.split(word) if w]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/extra_validations.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/extra_validations.py
new file mode 100644
index 0000000..72a7132
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/extra_validations.py
@@ -0,0 +1,151 @@
+"""The purpose of this module is implement PEP 621 validations that are
+difficult to express as a JSON Schema (or that are not supported by the current
+JSON Schema library).
+"""
+
+import collections
+import itertools
+from inspect import cleandoc
+from typing import Generator, Iterable, Mapping, TypeVar
+
+from .error_reporting import ValidationError
+
+T = TypeVar("T", bound=Mapping)
+
+
+class RedefiningStaticFieldAsDynamic(ValidationError):
+    _DESC = """According to PEP 621:
+
+    Build back-ends MUST raise an error if the metadata specifies a field
+    statically as well as being listed in dynamic.
+    """
+    __doc__ = _DESC
+    _URL = (
+        "https://packaging.python.org/en/latest/specifications/pyproject-toml/#dynamic"
+    )
+
+
+class IncludedDependencyGroupMustExist(ValidationError):
+    _DESC = """An included dependency group must exist and must not be cyclic.
+    """
+    __doc__ = _DESC
+    _URL = "https://peps.python.org/pep-0735/"
+
+
+class ImportNameCollision(ValidationError):
+    _DESC = """According to PEP 794:
+
+    All import-names and import-namespaces items must be unique.
+    """
+    __doc__ = _DESC
+    _URL = "https://peps.python.org/pep-0794/"
+
+
+class ImportNameMissing(ValidationError):
+    _DESC = """According to PEP 794:
+
+    An import name must have all parents listed.
+    """
+    __doc__ = _DESC
+    _URL = "https://peps.python.org/pep-0794/"
+
+
+def validate_project_dynamic(pyproject: T) -> T:
+    project_table = pyproject.get("project", {})
+    dynamic = project_table.get("dynamic", [])
+
+    for field in dynamic:
+        if field in project_table:
+            raise RedefiningStaticFieldAsDynamic(
+                message=f"You cannot provide a value for `project.{field}` and "
+                "list it under `project.dynamic` at the same time",
+                value={
+                    field: project_table[field],
+                    "...": " # ...",
+                    "dynamic": dynamic,
+                },
+                name=f"data.project.{field}",
+                definition={
+                    "description": cleandoc(RedefiningStaticFieldAsDynamic._DESC),
+                    "see": RedefiningStaticFieldAsDynamic._URL,
+                },
+                rule="PEP 621",
+            )
+
+    return pyproject
+
+
+def validate_include_depenency(pyproject: T) -> T:
+    dependency_groups = pyproject.get("dependency-groups", {})
+    for key, value in dependency_groups.items():
+        for each in value:
+            if (
+                isinstance(each, dict)
+                and (include_group := each.get("include-group"))
+                and include_group not in dependency_groups
+            ):
+                raise IncludedDependencyGroupMustExist(
+                    message=f"The included dependency group {include_group} doesn't exist",
+                    value=each,
+                    name=f"data.dependency_groups.{key}",
+                    definition={
+                        "description": cleandoc(IncludedDependencyGroupMustExist._DESC),
+                        "see": IncludedDependencyGroupMustExist._URL,
+                    },
+                    rule="PEP 735",
+                )
+    # TODO: check for `include-group` cycles (can be conditional to graphlib)
+    return pyproject
+
+
+def _remove_private(items: Iterable[str]) -> Generator[str, None, None]:
+    for item in items:
+        yield item.partition(";")[0].rstrip()
+
+
+def validate_import_name_issues(pyproject: T) -> T:
+    project = pyproject.get("project", {})
+    import_names = collections.Counter(_remove_private(project.get("import-names", [])))
+    import_namespaces = collections.Counter(
+        _remove_private(project.get("import-namespaces", []))
+    )
+
+    duplicated = [k for k, v in (import_names + import_namespaces).items() if v > 1]
+
+    if duplicated:
+        raise ImportNameCollision(
+            message="Duplicated names are not allowed in import-names/import-namespaces",
+            value=duplicated,
+            name="data.project.importnames(paces)",
+            definition={
+                "description": cleandoc(ImportNameCollision._DESC),
+                "see": ImportNameCollision._URL,
+            },
+            rule="PEP 794",
+        )
+
+    names = frozenset(import_names + import_namespaces)
+    for name in names:
+        for parent in itertools.accumulate(
+            name.split(".")[:-1], lambda a, b: f"{a}.{b}"
+        ):
+            if parent not in names:
+                raise ImportNameMissing(
+                    message="All parents of an import name must also be listed in import-namespace/import-names",
+                    value=name,
+                    name="data.project.importnames(paces)",
+                    definition={
+                        "description": cleandoc(ImportNameMissing._DESC),
+                        "see": ImportNameMissing._URL,
+                    },
+                    rule="PEP 794",
+                )
+
+    return pyproject
+
+
+EXTRA_VALIDATIONS = (
+    validate_project_dynamic,
+    validate_include_depenency,
+    validate_import_name_issues,
+)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py
new file mode 100644
index 0000000..d2dddd6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py
@@ -0,0 +1,51 @@
+import re
+
+
+SPLIT_RE = re.compile(r'[\.\[\]]+')
+
+
+class JsonSchemaException(ValueError):
+    """
+    Base exception of ``fastjsonschema`` library.
+    """
+
+
+class JsonSchemaValueException(JsonSchemaException):
+    """
+    Exception raised by validation function. Available properties:
+
+     * ``message`` containing human-readable information what is wrong (e.g. ``data.property[index] must be smaller than or equal to 42``),
+     * invalid ``value`` (e.g. ``60``),
+     * ``name`` of a path in the data structure (e.g. ``data.property[index]``),
+     * ``path`` as an array in the data structure (e.g. ``['data', 'property', 'index']``),
+     * the whole ``definition`` which the ``value`` has to fulfil (e.g. ``{'type': 'number', 'maximum': 42}``),
+     * ``rule`` which the ``value`` is breaking (e.g. ``maximum``)
+     * and ``rule_definition`` (e.g. ``42``).
+
+    .. versionchanged:: 2.14.0
+        Added all extra properties.
+    """
+
+    def __init__(self, message, value=None, name=None, definition=None, rule=None):
+        super().__init__(message)
+        self.message = message
+        self.value = value
+        self.name = name
+        self.definition = definition
+        self.rule = rule
+
+    @property
+    def path(self):
+        return [item for item in SPLIT_RE.split(self.name) if item != '']
+
+    @property
+    def rule_definition(self):
+        if not self.rule or not self.definition:
+            return None
+        return self.definition.get(self.rule)
+
+
+class JsonSchemaDefinitionException(JsonSchemaException):
+    """
+    Exception raised by generator of validation function.
+    """
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py
new file mode 100644
index 0000000..d8a03de
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py
@@ -0,0 +1,1453 @@
+# noqa
+# ruff: noqa
+# flake8: noqa
+# pylint: skip-file
+# mypy: ignore-errors
+# yapf: disable
+# pylama:skip=1
+
+
+# *** PLEASE DO NOT MODIFY DIRECTLY: Automatically generated code *** 
+
+
+VERSION = "2.21.2"
+from decimal import Decimal
+import re
+from .fastjsonschema_exceptions import JsonSchemaValueException
+
+
+REGEX_PATTERNS = {
+    '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': re.compile('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])\\Z'),
+    '^.*$': re.compile('^.*$'),
+    '.+': re.compile('.+'),
+    '^.+$': re.compile('^.+$'),
+    'idn-email_re_pattern': re.compile('^[^@]+@[^@]+\\.[^@]+\\Z')
+}
+
+NoneType = type(None)
+
+def validate(data, custom_formats={}, name_prefix=None):
+    validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats, (name_prefix or "data") + "")
+    return data
+
+def validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in  :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, 'dependency-groups': {'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        if "build-system" in data_keys:
+            data_keys.remove("build-system")
+            data__buildsystem = data["build-system"]
+            if not isinstance(data__buildsystem, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must be object", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='type')
+            data__buildsystem_is_dict = isinstance(data__buildsystem, dict)
+            if data__buildsystem_is_dict:
+                data__buildsystem__missing_keys = set(['requires']) - data__buildsystem.keys()
+                if data__buildsystem__missing_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must contain " + (str(sorted(data__buildsystem__missing_keys)) + " properties"), value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='required')
+                data__buildsystem_keys = set(data__buildsystem.keys())
+                if "requires" in data__buildsystem_keys:
+                    data__buildsystem_keys.remove("requires")
+                    data__buildsystem__requires = data__buildsystem["requires"]
+                    if not isinstance(data__buildsystem__requires, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.requires must be array", value=data__buildsystem__requires, name="" + (name_prefix or "data") + ".build-system.requires", definition={'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, rule='type')
+                    data__buildsystem__requires_is_list = isinstance(data__buildsystem__requires, (list, tuple))
+                    if data__buildsystem__requires_is_list:
+                        data__buildsystem__requires_len = len(data__buildsystem__requires)
+                        for data__buildsystem__requires_x, data__buildsystem__requires_item in enumerate(data__buildsystem__requires):
+                            if not isinstance(data__buildsystem__requires_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.requires[{data__buildsystem__requires_x}]".format(**locals()) + " must be string", value=data__buildsystem__requires_item, name="" + (name_prefix or "data") + ".build-system.requires[{data__buildsystem__requires_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if "build-backend" in data__buildsystem_keys:
+                    data__buildsystem_keys.remove("build-backend")
+                    data__buildsystem__buildbackend = data__buildsystem["build-backend"]
+                    if not isinstance(data__buildsystem__buildbackend, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.build-backend must be string", value=data__buildsystem__buildbackend, name="" + (name_prefix or "data") + ".build-system.build-backend", definition={'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, rule='type')
+                    if isinstance(data__buildsystem__buildbackend, str):
+                        if not custom_formats["pep517-backend-reference"](data__buildsystem__buildbackend):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.build-backend must be pep517-backend-reference", value=data__buildsystem__buildbackend, name="" + (name_prefix or "data") + ".build-system.build-backend", definition={'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, rule='format')
+                if "backend-path" in data__buildsystem_keys:
+                    data__buildsystem_keys.remove("backend-path")
+                    data__buildsystem__backendpath = data__buildsystem["backend-path"]
+                    if not isinstance(data__buildsystem__backendpath, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.backend-path must be array", value=data__buildsystem__backendpath, name="" + (name_prefix or "data") + ".build-system.backend-path", definition={'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}, rule='type')
+                    data__buildsystem__backendpath_is_list = isinstance(data__buildsystem__backendpath, (list, tuple))
+                    if data__buildsystem__backendpath_is_list:
+                        data__buildsystem__backendpath_len = len(data__buildsystem__backendpath)
+                        for data__buildsystem__backendpath_x, data__buildsystem__backendpath_item in enumerate(data__buildsystem__backendpath):
+                            if not isinstance(data__buildsystem__backendpath_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.backend-path[{data__buildsystem__backendpath_x}]".format(**locals()) + " must be string", value=data__buildsystem__backendpath_item, name="" + (name_prefix or "data") + ".build-system.backend-path[{data__buildsystem__backendpath_x}]".format(**locals()) + "", definition={'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}, rule='type')
+                if data__buildsystem_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must not contain "+str(data__buildsystem_keys)+" properties", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='additionalProperties')
+        if "project" in data_keys:
+            data_keys.remove("project")
+            data__project = data["project"]
+            validate_https___packaging_python_org_en_latest_specifications_pyproject_toml(data__project, custom_formats, (name_prefix or "data") + ".project")
+        if "tool" in data_keys:
+            data_keys.remove("tool")
+            data__tool = data["tool"]
+            if not isinstance(data__tool, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type')
+            data__tool_is_dict = isinstance(data__tool, dict)
+            if data__tool_is_dict:
+                data__tool_keys = set(data__tool.keys())
+                if "distutils" in data__tool_keys:
+                    data__tool_keys.remove("distutils")
+                    data__tool__distutils = data__tool["distutils"]
+                    validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfile_html(data__tool__distutils, custom_formats, (name_prefix or "data") + ".tool.distutils")
+                if "setuptools" in data__tool_keys:
+                    data__tool_keys.remove("setuptools")
+                    data__tool__setuptools = data__tool["setuptools"]
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools")
+        if "dependency-groups" in data_keys:
+            data_keys.remove("dependency-groups")
+            data__dependencygroups = data["dependency-groups"]
+            if not isinstance(data__dependencygroups, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must be object", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}, rule='type')
+            data__dependencygroups_is_dict = isinstance(data__dependencygroups, dict)
+            if data__dependencygroups_is_dict:
+                data__dependencygroups_keys = set(data__dependencygroups.keys())
+                for data__dependencygroups_key, data__dependencygroups_val in data__dependencygroups.items():
+                    if REGEX_PATTERNS['^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'].search(data__dependencygroups_key):
+                        if data__dependencygroups_key in data__dependencygroups_keys:
+                            data__dependencygroups_keys.remove(data__dependencygroups_key)
+                        if not isinstance(data__dependencygroups_val, (list, tuple)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}".format(**locals()) + " must be array", value=data__dependencygroups_val, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}, rule='type')
+                        data__dependencygroups_val_is_list = isinstance(data__dependencygroups_val, (list, tuple))
+                        if data__dependencygroups_val_is_list:
+                            data__dependencygroups_val_len = len(data__dependencygroups_val)
+                            for data__dependencygroups_val_x, data__dependencygroups_val_item in enumerate(data__dependencygroups_val):
+                                data__dependencygroups_val_item_one_of_count1 = 0
+                                if data__dependencygroups_val_item_one_of_count1 < 2:
+                                    try:
+                                        if not isinstance(data__dependencygroups_val_item, (str)):
+                                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be string", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, rule='type')
+                                        if isinstance(data__dependencygroups_val_item, str):
+                                            if not custom_formats["pep508"](data__dependencygroups_val_item):
+                                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be pep508", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, rule='format')
+                                        data__dependencygroups_val_item_one_of_count1 += 1
+                                    except JsonSchemaValueException: pass
+                                if data__dependencygroups_val_item_one_of_count1 < 2:
+                                    try:
+                                        if not isinstance(data__dependencygroups_val_item, (dict)):
+                                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be object", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}, rule='type')
+                                        data__dependencygroups_val_item_is_dict = isinstance(data__dependencygroups_val_item, dict)
+                                        if data__dependencygroups_val_item_is_dict:
+                                            data__dependencygroups_val_item_keys = set(data__dependencygroups_val_item.keys())
+                                            if "include-group" in data__dependencygroups_val_item_keys:
+                                                data__dependencygroups_val_item_keys.remove("include-group")
+                                                data__dependencygroups_val_item__includegroup = data__dependencygroups_val_item["include-group"]
+                                                if not isinstance(data__dependencygroups_val_item__includegroup, (str)):
+                                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + " must be string", value=data__dependencygroups_val_item__includegroup, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + "", definition={'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}, rule='type')
+                                                if isinstance(data__dependencygroups_val_item__includegroup, str):
+                                                    if not REGEX_PATTERNS['^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'].search(data__dependencygroups_val_item__includegroup):
+                                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + " must match pattern ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$", value=data__dependencygroups_val_item__includegroup, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + "", definition={'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}, rule='pattern')
+                                            if data__dependencygroups_val_item_keys:
+                                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must not contain "+str(data__dependencygroups_val_item_keys)+" properties", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}, rule='additionalProperties')
+                                        data__dependencygroups_val_item_one_of_count1 += 1
+                                    except JsonSchemaValueException: pass
+                                if data__dependencygroups_val_item_one_of_count1 != 1:
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__dependencygroups_val_item_one_of_count1) + " matches found)"), value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}, rule='oneOf')
+                if data__dependencygroups_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must not contain "+str(data__dependencygroups_keys)+" properties", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}, rule='additionalProperties')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in  :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$ref': '#/definitions/package-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, 'dependency-groups': {'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        if "platforms" in data_keys:
+            data_keys.remove("platforms")
+            data__platforms = data["platforms"]
+            if not isinstance(data__platforms, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".platforms must be array", value=data__platforms, name="" + (name_prefix or "data") + ".platforms", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__platforms_is_list = isinstance(data__platforms, (list, tuple))
+            if data__platforms_is_list:
+                data__platforms_len = len(data__platforms)
+                for data__platforms_x, data__platforms_item in enumerate(data__platforms):
+                    if not isinstance(data__platforms_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".platforms[{data__platforms_x}]".format(**locals()) + " must be string", value=data__platforms_item, name="" + (name_prefix or "data") + ".platforms[{data__platforms_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "provides" in data_keys:
+            data_keys.remove("provides")
+            data__provides = data["provides"]
+            if not isinstance(data__provides, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides must be array", value=data__provides, name="" + (name_prefix or "data") + ".provides", definition={'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, rule='type')
+            data__provides_is_list = isinstance(data__provides, (list, tuple))
+            if data__provides_is_list:
+                data__provides_len = len(data__provides)
+                for data__provides_x, data__provides_item in enumerate(data__provides):
+                    if not isinstance(data__provides_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + " must be string", value=data__provides_item, name="" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type')
+                    if isinstance(data__provides_item, str):
+                        if not custom_formats["pep508-identifier"](data__provides_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + " must be pep508-identifier", value=data__provides_item, name="" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format')
+        if "obsoletes" in data_keys:
+            data_keys.remove("obsoletes")
+            data__obsoletes = data["obsoletes"]
+            if not isinstance(data__obsoletes, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes must be array", value=data__obsoletes, name="" + (name_prefix or "data") + ".obsoletes", definition={'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, rule='type')
+            data__obsoletes_is_list = isinstance(data__obsoletes, (list, tuple))
+            if data__obsoletes_is_list:
+                data__obsoletes_len = len(data__obsoletes)
+                for data__obsoletes_x, data__obsoletes_item in enumerate(data__obsoletes):
+                    if not isinstance(data__obsoletes_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + " must be string", value=data__obsoletes_item, name="" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type')
+                    if isinstance(data__obsoletes_item, str):
+                        if not custom_formats["pep508-identifier"](data__obsoletes_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + " must be pep508-identifier", value=data__obsoletes_item, name="" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format')
+        if "zip-safe" in data_keys:
+            data_keys.remove("zip-safe")
+            data__zipsafe = data["zip-safe"]
+            if not isinstance(data__zipsafe, (bool)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".zip-safe must be boolean", value=data__zipsafe, name="" + (name_prefix or "data") + ".zip-safe", definition={'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, rule='type')
+        if "script-files" in data_keys:
+            data_keys.remove("script-files")
+            data__scriptfiles = data["script-files"]
+            if not isinstance(data__scriptfiles, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files must be array", value=data__scriptfiles, name="" + (name_prefix or "data") + ".script-files", definition={'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, rule='type')
+            data__scriptfiles_is_list = isinstance(data__scriptfiles, (list, tuple))
+            if data__scriptfiles_is_list:
+                data__scriptfiles_len = len(data__scriptfiles)
+                for data__scriptfiles_x, data__scriptfiles_item in enumerate(data__scriptfiles):
+                    if not isinstance(data__scriptfiles_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files[{data__scriptfiles_x}]".format(**locals()) + " must be string", value=data__scriptfiles_item, name="" + (name_prefix or "data") + ".script-files[{data__scriptfiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "eager-resources" in data_keys:
+            data_keys.remove("eager-resources")
+            data__eagerresources = data["eager-resources"]
+            if not isinstance(data__eagerresources, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources must be array", value=data__eagerresources, name="" + (name_prefix or "data") + ".eager-resources", definition={'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__eagerresources_is_list = isinstance(data__eagerresources, (list, tuple))
+            if data__eagerresources_is_list:
+                data__eagerresources_len = len(data__eagerresources)
+                for data__eagerresources_x, data__eagerresources_item in enumerate(data__eagerresources):
+                    if not isinstance(data__eagerresources_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources[{data__eagerresources_x}]".format(**locals()) + " must be string", value=data__eagerresources_item, name="" + (name_prefix or "data") + ".eager-resources[{data__eagerresources_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "packages" in data_keys:
+            data_keys.remove("packages")
+            data__packages = data["packages"]
+            data__packages_one_of_count2 = 0
+            if data__packages_one_of_count2 < 2:
+                try:
+                    if not isinstance(data__packages, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, rule='type')
+                    data__packages_is_list = isinstance(data__packages, (list, tuple))
+                    if data__packages_is_list:
+                        data__packages_len = len(data__packages)
+                        for data__packages_x, data__packages_item in enumerate(data__packages):
+                            validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__packages_item, custom_formats, (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals()))
+                    data__packages_one_of_count2 += 1
+                except JsonSchemaValueException: pass
+            if data__packages_one_of_count2 < 2:
+                try:
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_find_directive(data__packages, custom_formats, (name_prefix or "data") + ".packages")
+                    data__packages_one_of_count2 += 1
+                except JsonSchemaValueException: pass
+            if data__packages_one_of_count2 != 1:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count2) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf')
+        if "package-dir" in data_keys:
+            data_keys.remove("package-dir")
+            data__packagedir = data["package-dir"]
+            if not isinstance(data__packagedir, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type')
+            data__packagedir_is_dict = isinstance(data__packagedir, dict)
+            if data__packagedir_is_dict:
+                data__packagedir_keys = set(data__packagedir.keys())
+                for data__packagedir_key, data__packagedir_val in data__packagedir.items():
+                    if REGEX_PATTERNS['^.*$'].search(data__packagedir_key):
+                        if data__packagedir_key in data__packagedir_keys:
+                            data__packagedir_keys.remove(data__packagedir_key)
+                        if not isinstance(data__packagedir_val, (str)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + " must be string", value=data__packagedir_val, name="" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if data__packagedir_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties')
+                data__packagedir_len = len(data__packagedir)
+                if data__packagedir_len != 0:
+                    data__packagedir_property_names = True
+                    for data__packagedir_key in data__packagedir:
+                        try:
+                            data__packagedir_key_any_of_count3 = 0
+                            if not data__packagedir_key_any_of_count3:
+                                try:
+                                    if data__packagedir_key != "":
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be same as const definition: ", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'const': ''}, rule='const')
+                                    data__packagedir_key_any_of_count3 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__packagedir_key_any_of_count3:
+                                try:
+                                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__packagedir_key, custom_formats, (name_prefix or "data") + ".package-dir")
+                                    data__packagedir_key_any_of_count3 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__packagedir_key_any_of_count3:
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir cannot be validated by any definition", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, rule='anyOf')
+                        except JsonSchemaValueException:
+                            data__packagedir_property_names = False
+                    if not data__packagedir_property_names:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames')
+        if "package-data" in data_keys:
+            data_keys.remove("package-data")
+            data__packagedata = data["package-data"]
+            if not isinstance(data__packagedata, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type')
+            data__packagedata_is_dict = isinstance(data__packagedata, dict)
+            if data__packagedata_is_dict:
+                data__packagedata_keys = set(data__packagedata.keys())
+                for data__packagedata_key, data__packagedata_val in data__packagedata.items():
+                    if REGEX_PATTERNS['^.*$'].search(data__packagedata_key):
+                        if data__packagedata_key in data__packagedata_keys:
+                            data__packagedata_keys.remove(data__packagedata_key)
+                        if not isinstance(data__packagedata_val, (list, tuple)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data.{data__packagedata_key}".format(**locals()) + " must be array", value=data__packagedata_val, name="" + (name_prefix or "data") + ".package-data.{data__packagedata_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+                        data__packagedata_val_is_list = isinstance(data__packagedata_val, (list, tuple))
+                        if data__packagedata_val_is_list:
+                            data__packagedata_val_len = len(data__packagedata_val)
+                            for data__packagedata_val_x, data__packagedata_val_item in enumerate(data__packagedata_val):
+                                if not isinstance(data__packagedata_val_item, (str)):
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + " must be string", value=data__packagedata_val_item, name="" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if data__packagedata_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must not contain "+str(data__packagedata_keys)+" properties", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties')
+                data__packagedata_len = len(data__packagedata)
+                if data__packagedata_len != 0:
+                    data__packagedata_property_names = True
+                    for data__packagedata_key in data__packagedata:
+                        try:
+                            data__packagedata_key_any_of_count4 = 0
+                            if not data__packagedata_key_any_of_count4:
+                                try:
+                                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__packagedata_key, custom_formats, (name_prefix or "data") + ".package-data")
+                                    data__packagedata_key_any_of_count4 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__packagedata_key_any_of_count4:
+                                try:
+                                    if data__packagedata_key != "*":
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be same as const definition: *", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'const': '*'}, rule='const')
+                                    data__packagedata_key_any_of_count4 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__packagedata_key_any_of_count4:
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data cannot be validated by any definition", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, rule='anyOf')
+                        except JsonSchemaValueException:
+                            data__packagedata_property_names = False
+                    if not data__packagedata_property_names:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be named by propertyName definition", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames')
+        if "include-package-data" in data_keys:
+            data_keys.remove("include-package-data")
+            data__includepackagedata = data["include-package-data"]
+            if not isinstance(data__includepackagedata, (bool)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-package-data must be boolean", value=data__includepackagedata, name="" + (name_prefix or "data") + ".include-package-data", definition={'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, rule='type')
+        if "exclude-package-data" in data_keys:
+            data_keys.remove("exclude-package-data")
+            data__excludepackagedata = data["exclude-package-data"]
+            if not isinstance(data__excludepackagedata, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type')
+            data__excludepackagedata_is_dict = isinstance(data__excludepackagedata, dict)
+            if data__excludepackagedata_is_dict:
+                data__excludepackagedata_keys = set(data__excludepackagedata.keys())
+                for data__excludepackagedata_key, data__excludepackagedata_val in data__excludepackagedata.items():
+                    if REGEX_PATTERNS['^.*$'].search(data__excludepackagedata_key):
+                        if data__excludepackagedata_key in data__excludepackagedata_keys:
+                            data__excludepackagedata_keys.remove(data__excludepackagedata_key)
+                        if not isinstance(data__excludepackagedata_val, (list, tuple)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}".format(**locals()) + " must be array", value=data__excludepackagedata_val, name="" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+                        data__excludepackagedata_val_is_list = isinstance(data__excludepackagedata_val, (list, tuple))
+                        if data__excludepackagedata_val_is_list:
+                            data__excludepackagedata_val_len = len(data__excludepackagedata_val)
+                            for data__excludepackagedata_val_x, data__excludepackagedata_val_item in enumerate(data__excludepackagedata_val):
+                                if not isinstance(data__excludepackagedata_val_item, (str)):
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + " must be string", value=data__excludepackagedata_val_item, name="" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if data__excludepackagedata_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must not contain "+str(data__excludepackagedata_keys)+" properties", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties')
+                data__excludepackagedata_len = len(data__excludepackagedata)
+                if data__excludepackagedata_len != 0:
+                    data__excludepackagedata_property_names = True
+                    for data__excludepackagedata_key in data__excludepackagedata:
+                        try:
+                            data__excludepackagedata_key_any_of_count5 = 0
+                            if not data__excludepackagedata_key_any_of_count5:
+                                try:
+                                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__excludepackagedata_key, custom_formats, (name_prefix or "data") + ".exclude-package-data")
+                                    data__excludepackagedata_key_any_of_count5 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__excludepackagedata_key_any_of_count5:
+                                try:
+                                    if data__excludepackagedata_key != "*":
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be same as const definition: *", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'const': '*'}, rule='const')
+                                    data__excludepackagedata_key_any_of_count5 += 1
+                                except JsonSchemaValueException: pass
+                            if not data__excludepackagedata_key_any_of_count5:
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data cannot be validated by any definition", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, rule='anyOf')
+                        except JsonSchemaValueException:
+                            data__excludepackagedata_property_names = False
+                    if not data__excludepackagedata_property_names:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be named by propertyName definition", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames')
+        if "namespace-packages" in data_keys:
+            data_keys.remove("namespace-packages")
+            data__namespacepackages = data["namespace-packages"]
+            if not isinstance(data__namespacepackages, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, rule='type')
+            data__namespacepackages_is_list = isinstance(data__namespacepackages, (list, tuple))
+            if data__namespacepackages_is_list:
+                data__namespacepackages_len = len(data__namespacepackages)
+                for data__namespacepackages_x, data__namespacepackages_item in enumerate(data__namespacepackages):
+                    if not isinstance(data__namespacepackages_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be string", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type')
+                    if isinstance(data__namespacepackages_item, str):
+                        if not custom_formats["python-module-name-relaxed"](data__namespacepackages_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be python-module-name-relaxed", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format')
+        if "py-modules" in data_keys:
+            data_keys.remove("py-modules")
+            data__pymodules = data["py-modules"]
+            if not isinstance(data__pymodules, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules must be array", value=data__pymodules, name="" + (name_prefix or "data") + ".py-modules", definition={'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, rule='type')
+            data__pymodules_is_list = isinstance(data__pymodules, (list, tuple))
+            if data__pymodules_is_list:
+                data__pymodules_len = len(data__pymodules)
+                for data__pymodules_x, data__pymodules_item in enumerate(data__pymodules):
+                    if not isinstance(data__pymodules_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be string", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type')
+                    if isinstance(data__pymodules_item, str):
+                        if not custom_formats["python-module-name-relaxed"](data__pymodules_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be python-module-name-relaxed", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format')
+        if "ext-modules" in data_keys:
+            data_keys.remove("ext-modules")
+            data__extmodules = data["ext-modules"]
+            if not isinstance(data__extmodules, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".ext-modules must be array", value=data__extmodules, name="" + (name_prefix or "data") + ".ext-modules", definition={'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, rule='type')
+            data__extmodules_is_list = isinstance(data__extmodules, (list, tuple))
+            if data__extmodules_is_list:
+                data__extmodules_len = len(data__extmodules)
+                for data__extmodules_x, data__extmodules_item in enumerate(data__extmodules):
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_ext_module(data__extmodules_item, custom_formats, (name_prefix or "data") + ".ext-modules[{data__extmodules_x}]".format(**locals()))
+        if "data-files" in data_keys:
+            data_keys.remove("data-files")
+            data__datafiles = data["data-files"]
+            if not isinstance(data__datafiles, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type')
+            data__datafiles_is_dict = isinstance(data__datafiles, dict)
+            if data__datafiles_is_dict:
+                data__datafiles_keys = set(data__datafiles.keys())
+                for data__datafiles_key, data__datafiles_val in data__datafiles.items():
+                    if REGEX_PATTERNS['^.*$'].search(data__datafiles_key):
+                        if data__datafiles_key in data__datafiles_keys:
+                            data__datafiles_keys.remove(data__datafiles_key)
+                        if not isinstance(data__datafiles_val, (list, tuple)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files.{data__datafiles_key}".format(**locals()) + " must be array", value=data__datafiles_val, name="" + (name_prefix or "data") + ".data-files.{data__datafiles_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+                        data__datafiles_val_is_list = isinstance(data__datafiles_val, (list, tuple))
+                        if data__datafiles_val_is_list:
+                            data__datafiles_val_len = len(data__datafiles_val)
+                            for data__datafiles_val_x, data__datafiles_val_item in enumerate(data__datafiles_val):
+                                if not isinstance(data__datafiles_val_item, (str)):
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files.{data__datafiles_key}[{data__datafiles_val_x}]".format(**locals()) + " must be string", value=data__datafiles_val_item, name="" + (name_prefix or "data") + ".data-files.{data__datafiles_key}[{data__datafiles_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "cmdclass" in data_keys:
+            data_keys.remove("cmdclass")
+            data__cmdclass = data["cmdclass"]
+            if not isinstance(data__cmdclass, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass must be object", value=data__cmdclass, name="" + (name_prefix or "data") + ".cmdclass", definition={'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, rule='type')
+            data__cmdclass_is_dict = isinstance(data__cmdclass, dict)
+            if data__cmdclass_is_dict:
+                data__cmdclass_keys = set(data__cmdclass.keys())
+                for data__cmdclass_key, data__cmdclass_val in data__cmdclass.items():
+                    if REGEX_PATTERNS['^.*$'].search(data__cmdclass_key):
+                        if data__cmdclass_key in data__cmdclass_keys:
+                            data__cmdclass_keys.remove(data__cmdclass_key)
+                        if not isinstance(data__cmdclass_val, (str)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + " must be string", value=data__cmdclass_val, name="" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='type')
+                        if isinstance(data__cmdclass_val, str):
+                            if not custom_formats["python-qualified-identifier"](data__cmdclass_val):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + " must be python-qualified-identifier", value=data__cmdclass_val, name="" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='format')
+        if "license-files" in data_keys:
+            data_keys.remove("license-files")
+            data__licensefiles = data["license-files"]
+            if not isinstance(data__licensefiles, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type')
+            data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple))
+            if data__licensefiles_is_list:
+                data__licensefiles_len = len(data__licensefiles)
+                for data__licensefiles_x, data__licensefiles_item in enumerate(data__licensefiles):
+                    if not isinstance(data__licensefiles_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + " must be string", value=data__licensefiles_item, name="" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "dynamic" in data_keys:
+            data_keys.remove("dynamic")
+            data__dynamic = data["dynamic"]
+            if not isinstance(data__dynamic, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be object", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}, rule='type')
+            data__dynamic_is_dict = isinstance(data__dynamic, dict)
+            if data__dynamic_is_dict:
+                data__dynamic_keys = set(data__dynamic.keys())
+                if "version" in data__dynamic_keys:
+                    data__dynamic_keys.remove("version")
+                    data__dynamic__version = data__dynamic["version"]
+                    data__dynamic__version_one_of_count6 = 0
+                    if data__dynamic__version_one_of_count6 < 2:
+                        try:
+                            validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_attr_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version")
+                            data__dynamic__version_one_of_count6 += 1
+                        except JsonSchemaValueException: pass
+                    if data__dynamic__version_one_of_count6 < 2:
+                        try:
+                            validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version")
+                            data__dynamic__version_one_of_count6 += 1
+                        except JsonSchemaValueException: pass
+                    if data__dynamic__version_one_of_count6 != 1:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.version must be valid exactly by one definition" + (" (" + str(data__dynamic__version_one_of_count6) + " matches found)"), value=data__dynamic__version, name="" + (name_prefix or "data") + ".dynamic.version", definition={'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, rule='oneOf')
+                if "classifiers" in data__dynamic_keys:
+                    data__dynamic_keys.remove("classifiers")
+                    data__dynamic__classifiers = data__dynamic["classifiers"]
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__classifiers, custom_formats, (name_prefix or "data") + ".dynamic.classifiers")
+                if "description" in data__dynamic_keys:
+                    data__dynamic_keys.remove("description")
+                    data__dynamic__description = data__dynamic["description"]
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__description, custom_formats, (name_prefix or "data") + ".dynamic.description")
+                if "entry-points" in data__dynamic_keys:
+                    data__dynamic_keys.remove("entry-points")
+                    data__dynamic__entrypoints = data__dynamic["entry-points"]
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__entrypoints, custom_formats, (name_prefix or "data") + ".dynamic.entry-points")
+                if "dependencies" in data__dynamic_keys:
+                    data__dynamic_keys.remove("dependencies")
+                    data__dynamic__dependencies = data__dynamic["dependencies"]
+                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data__dynamic__dependencies, custom_formats, (name_prefix or "data") + ".dynamic.dependencies")
+                if "optional-dependencies" in data__dynamic_keys:
+                    data__dynamic_keys.remove("optional-dependencies")
+                    data__dynamic__optionaldependencies = data__dynamic["optional-dependencies"]
+                    if not isinstance(data__dynamic__optionaldependencies, (dict)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be object", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='type')
+                    data__dynamic__optionaldependencies_is_dict = isinstance(data__dynamic__optionaldependencies, dict)
+                    if data__dynamic__optionaldependencies_is_dict:
+                        data__dynamic__optionaldependencies_keys = set(data__dynamic__optionaldependencies.keys())
+                        for data__dynamic__optionaldependencies_key, data__dynamic__optionaldependencies_val in data__dynamic__optionaldependencies.items():
+                            if REGEX_PATTERNS['.+'].search(data__dynamic__optionaldependencies_key):
+                                if data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies_keys:
+                                    data__dynamic__optionaldependencies_keys.remove(data__dynamic__optionaldependencies_key)
+                                validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}".format(**locals()))
+                        if data__dynamic__optionaldependencies_keys:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must not contain "+str(data__dynamic__optionaldependencies_keys)+" properties", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='additionalProperties')
+                        data__dynamic__optionaldependencies_len = len(data__dynamic__optionaldependencies)
+                        if data__dynamic__optionaldependencies_len != 0:
+                            data__dynamic__optionaldependencies_property_names = True
+                            for data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies:
+                                try:
+                                    if not isinstance(data__dynamic__optionaldependencies_key, (str)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be string", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type')
+                                    if isinstance(data__dynamic__optionaldependencies_key, str):
+                                        if not custom_formats["pep508-identifier"](data__dynamic__optionaldependencies_key):
+                                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be pep508-identifier", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format')
+                                except JsonSchemaValueException:
+                                    data__dynamic__optionaldependencies_property_names = False
+                            if not data__dynamic__optionaldependencies_property_names:
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be named by propertyName definition", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='propertyNames')
+                if "readme" in data__dynamic_keys:
+                    data__dynamic_keys.remove("readme")
+                    data__dynamic__readme = data__dynamic["readme"]
+                    if not isinstance(data__dynamic__readme, (dict)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must be object", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='type')
+                    data__dynamic__readme_any_of_count7 = 0
+                    if not data__dynamic__readme_any_of_count7:
+                        try:
+                            validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__readme, custom_formats, (name_prefix or "data") + ".dynamic.readme")
+                            data__dynamic__readme_any_of_count7 += 1
+                        except JsonSchemaValueException: pass
+                    if not data__dynamic__readme_any_of_count7:
+                        try:
+                            if not isinstance(data__dynamic__readme, (dict)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must be object", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}, rule='type')
+                            data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict)
+                            if data__dynamic__readme_is_dict:
+                                data__dynamic__readme_keys = set(data__dynamic__readme.keys())
+                                if "content-type" in data__dynamic__readme_keys:
+                                    data__dynamic__readme_keys.remove("content-type")
+                                    data__dynamic__readme__contenttype = data__dynamic__readme["content-type"]
+                                    if not isinstance(data__dynamic__readme__contenttype, (str)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme.content-type must be string", value=data__dynamic__readme__contenttype, name="" + (name_prefix or "data") + ".dynamic.readme.content-type", definition={'type': 'string'}, rule='type')
+                                if "file" in data__dynamic__readme_keys:
+                                    data__dynamic__readme_keys.remove("file")
+                                    data__dynamic__readme__file = data__dynamic__readme["file"]
+                                    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_properties_file(data__dynamic__readme__file, custom_formats, (name_prefix or "data") + ".dynamic.readme.file")
+                                if data__dynamic__readme_keys:
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must not contain "+str(data__dynamic__readme_keys)+" properties", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}, rule='additionalProperties')
+                            data__dynamic__readme_any_of_count7 += 1
+                        except JsonSchemaValueException: pass
+                    if not data__dynamic__readme_any_of_count7:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme cannot be validated by any definition", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='anyOf')
+                    data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict)
+                    if data__dynamic__readme_is_dict:
+                        data__dynamic__readme__missing_keys = set(['file']) - data__dynamic__readme.keys()
+                        if data__dynamic__readme__missing_keys:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must contain " + (str(sorted(data__dynamic__readme__missing_keys)) + " properties"), value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='required')
+                if data__dynamic_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}, rule='additionalProperties')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', '    cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_properties_file(data, custom_formats={}, name_prefix=None):
+    data_one_of_count8 = 0
+    if data_one_of_count8 < 2:
+        try:
+            if not isinstance(data, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string'}, rule='type')
+            data_one_of_count8 += 1
+        except JsonSchemaValueException: pass
+    if data_one_of_count8 < 2:
+        try:
+            if not isinstance(data, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must be array", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data_is_list = isinstance(data, (list, tuple))
+            if data_is_list:
+                data_len = len(data)
+                for data_x, data_item in enumerate(data):
+                    if not isinstance(data_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + "[{data_x}]".format(**locals()) + " must be string", value=data_item, name="" + (name_prefix or "data") + "[{data_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+            data_one_of_count8 += 1
+        except JsonSchemaValueException: pass
+    if data_one_of_count8 != 1:
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be valid exactly by one definition" + (" (" + str(data_one_of_count8) + " matches found)"), value=data, name="" + (name_prefix or "data") + "", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data, custom_formats={}, name_prefix=None):
+    validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data, custom_formats, (name_prefix or "data") + "")
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data__missing_keys = set(['file']) - data.keys()
+        if data__missing_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='required')
+        data_keys = set(data.keys())
+        if "file" in data_keys:
+            data_keys.remove("file")
+            data__file = data["file"]
+            data__file_one_of_count9 = 0
+            if data__file_one_of_count9 < 2:
+                try:
+                    if not isinstance(data__file, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be string", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'string'}, rule='type')
+                    data__file_one_of_count9 += 1
+                except JsonSchemaValueException: pass
+            if data__file_one_of_count9 < 2:
+                try:
+                    if not isinstance(data__file, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be array", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+                    data__file_is_list = isinstance(data__file, (list, tuple))
+                    if data__file_is_list:
+                        data__file_len = len(data__file)
+                        for data__file_x, data__file_item in enumerate(data__file):
+                            if not isinstance(data__file_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + " must be string", value=data__file_item, name="" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                    data__file_one_of_count9 += 1
+                except JsonSchemaValueException: pass
+            if data__file_one_of_count9 != 1:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be valid exactly by one definition" + (" (" + str(data__file_one_of_count9) + " matches found)"), value=data__file, name="" + (name_prefix or "data") + ".file", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_attr_directive(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data__missing_keys = set(['attr']) - data.keys()
+        if data__missing_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='required')
+        data_keys = set(data.keys())
+        if "attr" in data_keys:
+            data_keys.remove("attr")
+            data__attr = data["attr"]
+            if not isinstance(data__attr, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be string", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='type')
+            if isinstance(data__attr, str):
+                if not custom_formats["python-qualified-identifier"](data__attr):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be python-qualified-identifier", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='format')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_ext_module(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data__missing_keys = set(['name', 'sources']) - data.keys()
+        if data__missing_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='required')
+        data_keys = set(data.keys())
+        if "name" in data_keys:
+            data_keys.remove("name")
+            data__name = data["name"]
+            if not isinstance(data__name, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type')
+            if isinstance(data__name, str):
+                if not custom_formats["python-module-name-relaxed"](data__name):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be python-module-name-relaxed", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format')
+        if "sources" in data_keys:
+            data_keys.remove("sources")
+            data__sources = data["sources"]
+            if not isinstance(data__sources, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".sources must be array", value=data__sources, name="" + (name_prefix or "data") + ".sources", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__sources_is_list = isinstance(data__sources, (list, tuple))
+            if data__sources_is_list:
+                data__sources_len = len(data__sources)
+                for data__sources_x, data__sources_item in enumerate(data__sources):
+                    if not isinstance(data__sources_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".sources[{data__sources_x}]".format(**locals()) + " must be string", value=data__sources_item, name="" + (name_prefix or "data") + ".sources[{data__sources_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "include-dirs" in data_keys:
+            data_keys.remove("include-dirs")
+            data__includedirs = data["include-dirs"]
+            if not isinstance(data__includedirs, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-dirs must be array", value=data__includedirs, name="" + (name_prefix or "data") + ".include-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__includedirs_is_list = isinstance(data__includedirs, (list, tuple))
+            if data__includedirs_is_list:
+                data__includedirs_len = len(data__includedirs)
+                for data__includedirs_x, data__includedirs_item in enumerate(data__includedirs):
+                    if not isinstance(data__includedirs_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-dirs[{data__includedirs_x}]".format(**locals()) + " must be string", value=data__includedirs_item, name="" + (name_prefix or "data") + ".include-dirs[{data__includedirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "define-macros" in data_keys:
+            data_keys.remove("define-macros")
+            data__definemacros = data["define-macros"]
+            if not isinstance(data__definemacros, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros must be array", value=data__definemacros, name="" + (name_prefix or "data") + ".define-macros", definition={'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, rule='type')
+            data__definemacros_is_list = isinstance(data__definemacros, (list, tuple))
+            if data__definemacros_is_list:
+                data__definemacros_len = len(data__definemacros)
+                for data__definemacros_x, data__definemacros_item in enumerate(data__definemacros):
+                    if not isinstance(data__definemacros_item, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + " must be array", value=data__definemacros_item, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + "", definition={'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}, rule='type')
+                    data__definemacros_item_is_list = isinstance(data__definemacros_item, (list, tuple))
+                    if data__definemacros_item_is_list:
+                        data__definemacros_item_len = len(data__definemacros_item)
+                        if data__definemacros_item_len > 0:
+                            data__definemacros_item__0 = data__definemacros_item[0]
+                            if not isinstance(data__definemacros_item__0, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + " must be string", value=data__definemacros_item__0, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + "", definition={'description': 'macro name', 'type': 'string'}, rule='type')
+                        if data__definemacros_item_len > 1:
+                            data__definemacros_item__1 = data__definemacros_item[1]
+                            data__definemacros_item__1_one_of_count10 = 0
+                            if data__definemacros_item__1_one_of_count10 < 2:
+                                try:
+                                    if not isinstance(data__definemacros_item__1, (str)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be string", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                                    data__definemacros_item__1_one_of_count10 += 1
+                                except JsonSchemaValueException: pass
+                            if data__definemacros_item__1_one_of_count10 < 2:
+                                try:
+                                    if not isinstance(data__definemacros_item__1, (NoneType)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be null", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'null'}, rule='type')
+                                    data__definemacros_item__1_one_of_count10 += 1
+                                except JsonSchemaValueException: pass
+                            if data__definemacros_item__1_one_of_count10 != 1:
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__definemacros_item__1_one_of_count10) + " matches found)"), value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}, rule='oneOf')
+                        if data__definemacros_item_len > 2:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + " must contain only specified items", value=data__definemacros_item, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + "", definition={'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}, rule='items')
+        if "undef-macros" in data_keys:
+            data_keys.remove("undef-macros")
+            data__undefmacros = data["undef-macros"]
+            if not isinstance(data__undefmacros, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".undef-macros must be array", value=data__undefmacros, name="" + (name_prefix or "data") + ".undef-macros", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__undefmacros_is_list = isinstance(data__undefmacros, (list, tuple))
+            if data__undefmacros_is_list:
+                data__undefmacros_len = len(data__undefmacros)
+                for data__undefmacros_x, data__undefmacros_item in enumerate(data__undefmacros):
+                    if not isinstance(data__undefmacros_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".undef-macros[{data__undefmacros_x}]".format(**locals()) + " must be string", value=data__undefmacros_item, name="" + (name_prefix or "data") + ".undef-macros[{data__undefmacros_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "library-dirs" in data_keys:
+            data_keys.remove("library-dirs")
+            data__librarydirs = data["library-dirs"]
+            if not isinstance(data__librarydirs, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".library-dirs must be array", value=data__librarydirs, name="" + (name_prefix or "data") + ".library-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__librarydirs_is_list = isinstance(data__librarydirs, (list, tuple))
+            if data__librarydirs_is_list:
+                data__librarydirs_len = len(data__librarydirs)
+                for data__librarydirs_x, data__librarydirs_item in enumerate(data__librarydirs):
+                    if not isinstance(data__librarydirs_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".library-dirs[{data__librarydirs_x}]".format(**locals()) + " must be string", value=data__librarydirs_item, name="" + (name_prefix or "data") + ".library-dirs[{data__librarydirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "libraries" in data_keys:
+            data_keys.remove("libraries")
+            data__libraries = data["libraries"]
+            if not isinstance(data__libraries, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".libraries must be array", value=data__libraries, name="" + (name_prefix or "data") + ".libraries", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__libraries_is_list = isinstance(data__libraries, (list, tuple))
+            if data__libraries_is_list:
+                data__libraries_len = len(data__libraries)
+                for data__libraries_x, data__libraries_item in enumerate(data__libraries):
+                    if not isinstance(data__libraries_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".libraries[{data__libraries_x}]".format(**locals()) + " must be string", value=data__libraries_item, name="" + (name_prefix or "data") + ".libraries[{data__libraries_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "runtime-library-dirs" in data_keys:
+            data_keys.remove("runtime-library-dirs")
+            data__runtimelibrarydirs = data["runtime-library-dirs"]
+            if not isinstance(data__runtimelibrarydirs, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".runtime-library-dirs must be array", value=data__runtimelibrarydirs, name="" + (name_prefix or "data") + ".runtime-library-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__runtimelibrarydirs_is_list = isinstance(data__runtimelibrarydirs, (list, tuple))
+            if data__runtimelibrarydirs_is_list:
+                data__runtimelibrarydirs_len = len(data__runtimelibrarydirs)
+                for data__runtimelibrarydirs_x, data__runtimelibrarydirs_item in enumerate(data__runtimelibrarydirs):
+                    if not isinstance(data__runtimelibrarydirs_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".runtime-library-dirs[{data__runtimelibrarydirs_x}]".format(**locals()) + " must be string", value=data__runtimelibrarydirs_item, name="" + (name_prefix or "data") + ".runtime-library-dirs[{data__runtimelibrarydirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "extra-objects" in data_keys:
+            data_keys.remove("extra-objects")
+            data__extraobjects = data["extra-objects"]
+            if not isinstance(data__extraobjects, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-objects must be array", value=data__extraobjects, name="" + (name_prefix or "data") + ".extra-objects", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__extraobjects_is_list = isinstance(data__extraobjects, (list, tuple))
+            if data__extraobjects_is_list:
+                data__extraobjects_len = len(data__extraobjects)
+                for data__extraobjects_x, data__extraobjects_item in enumerate(data__extraobjects):
+                    if not isinstance(data__extraobjects_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-objects[{data__extraobjects_x}]".format(**locals()) + " must be string", value=data__extraobjects_item, name="" + (name_prefix or "data") + ".extra-objects[{data__extraobjects_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "extra-compile-args" in data_keys:
+            data_keys.remove("extra-compile-args")
+            data__extracompileargs = data["extra-compile-args"]
+            if not isinstance(data__extracompileargs, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-compile-args must be array", value=data__extracompileargs, name="" + (name_prefix or "data") + ".extra-compile-args", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__extracompileargs_is_list = isinstance(data__extracompileargs, (list, tuple))
+            if data__extracompileargs_is_list:
+                data__extracompileargs_len = len(data__extracompileargs)
+                for data__extracompileargs_x, data__extracompileargs_item in enumerate(data__extracompileargs):
+                    if not isinstance(data__extracompileargs_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-compile-args[{data__extracompileargs_x}]".format(**locals()) + " must be string", value=data__extracompileargs_item, name="" + (name_prefix or "data") + ".extra-compile-args[{data__extracompileargs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "extra-link-args" in data_keys:
+            data_keys.remove("extra-link-args")
+            data__extralinkargs = data["extra-link-args"]
+            if not isinstance(data__extralinkargs, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-link-args must be array", value=data__extralinkargs, name="" + (name_prefix or "data") + ".extra-link-args", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__extralinkargs_is_list = isinstance(data__extralinkargs, (list, tuple))
+            if data__extralinkargs_is_list:
+                data__extralinkargs_len = len(data__extralinkargs)
+                for data__extralinkargs_x, data__extralinkargs_item in enumerate(data__extralinkargs):
+                    if not isinstance(data__extralinkargs_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-link-args[{data__extralinkargs_x}]".format(**locals()) + " must be string", value=data__extralinkargs_item, name="" + (name_prefix or "data") + ".extra-link-args[{data__extralinkargs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "export-symbols" in data_keys:
+            data_keys.remove("export-symbols")
+            data__exportsymbols = data["export-symbols"]
+            if not isinstance(data__exportsymbols, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".export-symbols must be array", value=data__exportsymbols, name="" + (name_prefix or "data") + ".export-symbols", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__exportsymbols_is_list = isinstance(data__exportsymbols, (list, tuple))
+            if data__exportsymbols_is_list:
+                data__exportsymbols_len = len(data__exportsymbols)
+                for data__exportsymbols_x, data__exportsymbols_item in enumerate(data__exportsymbols):
+                    if not isinstance(data__exportsymbols_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".export-symbols[{data__exportsymbols_x}]".format(**locals()) + " must be string", value=data__exportsymbols_item, name="" + (name_prefix or "data") + ".export-symbols[{data__exportsymbols_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "swig-opts" in data_keys:
+            data_keys.remove("swig-opts")
+            data__swigopts = data["swig-opts"]
+            if not isinstance(data__swigopts, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".swig-opts must be array", value=data__swigopts, name="" + (name_prefix or "data") + ".swig-opts", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__swigopts_is_list = isinstance(data__swigopts, (list, tuple))
+            if data__swigopts_is_list:
+                data__swigopts_len = len(data__swigopts)
+                for data__swigopts_x, data__swigopts_item in enumerate(data__swigopts):
+                    if not isinstance(data__swigopts_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".swig-opts[{data__swigopts_x}]".format(**locals()) + " must be string", value=data__swigopts_item, name="" + (name_prefix or "data") + ".swig-opts[{data__swigopts_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "depends" in data_keys:
+            data_keys.remove("depends")
+            data__depends = data["depends"]
+            if not isinstance(data__depends, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".depends must be array", value=data__depends, name="" + (name_prefix or "data") + ".depends", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__depends_is_list = isinstance(data__depends, (list, tuple))
+            if data__depends_is_list:
+                data__depends_len = len(data__depends)
+                for data__depends_x, data__depends_item in enumerate(data__depends):
+                    if not isinstance(data__depends_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".depends[{data__depends_x}]".format(**locals()) + " must be string", value=data__depends_item, name="" + (name_prefix or "data") + ".depends[{data__depends_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "language" in data_keys:
+            data_keys.remove("language")
+            data__language = data["language"]
+            if not isinstance(data__language, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".language must be string", value=data__language, name="" + (name_prefix or "data") + ".language", definition={'type': 'string'}, rule='type')
+        if "optional" in data_keys:
+            data_keys.remove("optional")
+            data__optional = data["optional"]
+            if not isinstance(data__optional, (bool)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional must be boolean", value=data__optional, name="" + (name_prefix or "data") + ".optional", definition={'type': 'boolean'}, rule='type')
+        if "py-limited-api" in data_keys:
+            data_keys.remove("py-limited-api")
+            data__pylimitedapi = data["py-limited-api"]
+            if not isinstance(data__pylimitedapi, (bool)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-limited-api must be boolean", value=data__pylimitedapi, name="" + (name_prefix or "data") + ".py-limited-api", definition={'type': 'boolean'}, rule='type')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_find_directive(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        if "find" in data_keys:
+            data_keys.remove("find")
+            data__find = data["find"]
+            if not isinstance(data__find, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must be object", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}, rule='type')
+            data__find_is_dict = isinstance(data__find, dict)
+            if data__find_is_dict:
+                data__find_keys = set(data__find.keys())
+                if "where" in data__find_keys:
+                    data__find_keys.remove("where")
+                    data__find__where = data__find["where"]
+                    if not isinstance(data__find__where, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.where must be array", value=data__find__where, name="" + (name_prefix or "data") + ".find.where", definition={'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, rule='type')
+                    data__find__where_is_list = isinstance(data__find__where, (list, tuple))
+                    if data__find__where_is_list:
+                        data__find__where_len = len(data__find__where)
+                        for data__find__where_x, data__find__where_item in enumerate(data__find__where):
+                            if not isinstance(data__find__where_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.where[{data__find__where_x}]".format(**locals()) + " must be string", value=data__find__where_item, name="" + (name_prefix or "data") + ".find.where[{data__find__where_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if "exclude" in data__find_keys:
+                    data__find_keys.remove("exclude")
+                    data__find__exclude = data__find["exclude"]
+                    if not isinstance(data__find__exclude, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.exclude must be array", value=data__find__exclude, name="" + (name_prefix or "data") + ".find.exclude", definition={'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, rule='type')
+                    data__find__exclude_is_list = isinstance(data__find__exclude, (list, tuple))
+                    if data__find__exclude_is_list:
+                        data__find__exclude_len = len(data__find__exclude)
+                        for data__find__exclude_x, data__find__exclude_item in enumerate(data__find__exclude):
+                            if not isinstance(data__find__exclude_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.exclude[{data__find__exclude_x}]".format(**locals()) + " must be string", value=data__find__exclude_item, name="" + (name_prefix or "data") + ".find.exclude[{data__find__exclude_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if "include" in data__find_keys:
+                    data__find_keys.remove("include")
+                    data__find__include = data__find["include"]
+                    if not isinstance(data__find__include, (list, tuple)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.include must be array", value=data__find__include, name="" + (name_prefix or "data") + ".find.include", definition={'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, rule='type')
+                    data__find__include_is_list = isinstance(data__find__include, (list, tuple))
+                    if data__find__include_is_list:
+                        data__find__include_len = len(data__find__include)
+                        for data__find__include_x, data__find__include_item in enumerate(data__find__include):
+                            if not isinstance(data__find__include_item, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.include[{data__find__include_x}]".format(**locals()) + " must be string", value=data__find__include_item, name="" + (name_prefix or "data") + ".find.include[{data__find__include_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+                if "namespaces" in data__find_keys:
+                    data__find_keys.remove("namespaces")
+                    data__find__namespaces = data__find["namespaces"]
+                    if not isinstance(data__find__namespaces, (bool)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.namespaces must be boolean", value=data__find__namespaces, name="" + (name_prefix or "data") + ".find.namespaces", definition={'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}, rule='type')
+                if data__find_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must not contain "+str(data__find_keys)+" properties", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}, rule='additionalProperties')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='additionalProperties')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (str)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='type')
+    data_any_of_count11 = 0
+    if not data_any_of_count11:
+        try:
+            if not isinstance(data, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type')
+            if isinstance(data, str):
+                if not custom_formats["python-module-name-relaxed"](data):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name-relaxed", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format')
+            data_any_of_count11 += 1
+        except JsonSchemaValueException: pass
+    if not data_any_of_count11:
+        try:
+            if not isinstance(data, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='type')
+            if isinstance(data, str):
+                if not custom_formats["pep561-stub-name"](data):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='format')
+            data_any_of_count11 += 1
+        except JsonSchemaValueException: pass
+    if not data_any_of_count11:
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='anyOf')
+    return data
+
+def validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfile_html(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        if "global" in data_keys:
+            data_keys.remove("global")
+            data__global = data["global"]
+            if not isinstance(data__global, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".global must be object", value=data__global, name="" + (name_prefix or "data") + ".global", definition={'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}, rule='type')
+        for data_key, data_val in data.items():
+            if REGEX_PATTERNS['.+'].search(data_key):
+                if data_key in data_keys:
+                    data_keys.remove(data_key)
+                if not isinstance(data_val, (dict)):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be object", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'object'}, rule='type')
+    return data
+
+def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type')
+    try:
+        try:
+            data_is_dict = isinstance(data, dict)
+            if data_is_dict:
+                data__missing_keys = set(['dynamic']) - data.keys()
+                if data__missing_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, rule='required')
+                data_keys = set(data.keys())
+                if "dynamic" in data_keys:
+                    data_keys.remove("dynamic")
+                    data__dynamic = data["dynamic"]
+                    data__dynamic_is_list = isinstance(data__dynamic, (list, tuple))
+                    if data__dynamic_is_list:
+                        data__dynamic_contains = False
+                        for data__dynamic_key in data__dynamic:
+                            try:
+                                if data__dynamic_key != "version":
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be same as const definition: version", value=data__dynamic_key, name="" + (name_prefix or "data") + ".dynamic", definition={'const': 'version'}, rule='const')
+                                data__dynamic_contains = True
+                                break
+                            except JsonSchemaValueException: pass
+                        if not data__dynamic_contains:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must contain one of contains definition", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}, rule='contains')
+        except JsonSchemaValueException: pass
+        else:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must NOT match a disallowed definition", value=data, name="" + (name_prefix or "data") + "", definition={'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, rule='not')
+    except JsonSchemaValueException:
+        pass
+    else:
+        data_is_dict = isinstance(data, dict)
+        if data_is_dict:
+            data__missing_keys = set(['version']) - data.keys()
+            if data__missing_keys:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, rule='required')
+    try:
+        data_is_dict = isinstance(data, dict)
+        if data_is_dict:
+            data__missing_keys = set(['license-files']) - data.keys()
+            if data__missing_keys:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['license-files']}, rule='required')
+    except JsonSchemaValueException:
+        pass
+    else:
+        data_is_dict = isinstance(data, dict)
+        if data_is_dict:
+            data_keys = set(data.keys())
+            if "license" in data_keys:
+                data_keys.remove("license")
+                data__license = data["license"]
+                if not isinstance(data__license, (str)):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be string", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string'}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data__missing_keys = set(['name']) - data.keys()
+        if data__missing_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required')
+        data_keys = set(data.keys())
+        if "name" in data_keys:
+            data_keys.remove("name")
+            data__name = data["name"]
+            if not isinstance(data__name, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, rule='type')
+            if isinstance(data__name, str):
+                if not custom_formats["pep508-identifier"](data__name):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be pep508-identifier", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, rule='format')
+        if "version" in data_keys:
+            data_keys.remove("version")
+            data__version = data["version"]
+            if not isinstance(data__version, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".version must be string", value=data__version, name="" + (name_prefix or "data") + ".version", definition={'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, rule='type')
+            if isinstance(data__version, str):
+                if not custom_formats["pep440"](data__version):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".version must be pep440", value=data__version, name="" + (name_prefix or "data") + ".version", definition={'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, rule='format')
+        if "description" in data_keys:
+            data_keys.remove("description")
+            data__description = data["description"]
+            if not isinstance(data__description, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".description must be string", value=data__description, name="" + (name_prefix or "data") + ".description", definition={'type': 'string', '$$description': ['The `summary description of the project', '`_']}, rule='type')
+        if "readme" in data_keys:
+            data_keys.remove("readme")
+            data__readme = data["readme"]
+            data__readme_one_of_count12 = 0
+            if data__readme_one_of_count12 < 2:
+                try:
+                    if not isinstance(data__readme, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be string", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, rule='type')
+                    data__readme_one_of_count12 += 1
+                except JsonSchemaValueException: pass
+            if data__readme_one_of_count12 < 2:
+                try:
+                    if not isinstance(data__readme, (dict)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be object", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}, rule='type')
+                    data__readme_any_of_count13 = 0
+                    if not data__readme_any_of_count13:
+                        try:
+                            data__readme_is_dict = isinstance(data__readme, dict)
+                            if data__readme_is_dict:
+                                data__readme__missing_keys = set(['file']) - data__readme.keys()
+                                if data__readme__missing_keys:
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, rule='required')
+                                data__readme_keys = set(data__readme.keys())
+                                if "file" in data__readme_keys:
+                                    data__readme_keys.remove("file")
+                                    data__readme__file = data__readme["file"]
+                                    if not isinstance(data__readme__file, (str)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.file must be string", value=data__readme__file, name="" + (name_prefix or "data") + ".readme.file", definition={'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}, rule='type')
+                            data__readme_any_of_count13 += 1
+                        except JsonSchemaValueException: pass
+                    if not data__readme_any_of_count13:
+                        try:
+                            data__readme_is_dict = isinstance(data__readme, dict)
+                            if data__readme_is_dict:
+                                data__readme__missing_keys = set(['text']) - data__readme.keys()
+                                if data__readme__missing_keys:
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}, rule='required')
+                                data__readme_keys = set(data__readme.keys())
+                                if "text" in data__readme_keys:
+                                    data__readme_keys.remove("text")
+                                    data__readme__text = data__readme["text"]
+                                    if not isinstance(data__readme__text, (str)):
+                                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.text must be string", value=data__readme__text, name="" + (name_prefix or "data") + ".readme.text", definition={'type': 'string', 'description': 'Full text describing the project.'}, rule='type')
+                            data__readme_any_of_count13 += 1
+                        except JsonSchemaValueException: pass
+                    if not data__readme_any_of_count13:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme cannot be validated by any definition", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, rule='anyOf')
+                    data__readme_is_dict = isinstance(data__readme, dict)
+                    if data__readme_is_dict:
+                        data__readme__missing_keys = set(['content-type']) - data__readme.keys()
+                        if data__readme__missing_keys:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}, rule='required')
+                        data__readme_keys = set(data__readme.keys())
+                        if "content-type" in data__readme_keys:
+                            data__readme_keys.remove("content-type")
+                            data__readme__contenttype = data__readme["content-type"]
+                            if not isinstance(data__readme__contenttype, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.content-type must be string", value=data__readme__contenttype, name="" + (name_prefix or "data") + ".readme.content-type", definition={'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}, rule='type')
+                    data__readme_one_of_count12 += 1
+                except JsonSchemaValueException: pass
+            if data__readme_one_of_count12 != 1:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count12) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf')
+        if "requires-python" in data_keys:
+            data_keys.remove("requires-python")
+            data__requirespython = data["requires-python"]
+            if not isinstance(data__requirespython, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".requires-python must be string", value=data__requirespython, name="" + (name_prefix or "data") + ".requires-python", definition={'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, rule='type')
+            if isinstance(data__requirespython, str):
+                if not custom_formats["pep508-versionspec"](data__requirespython):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".requires-python must be pep508-versionspec", value=data__requirespython, name="" + (name_prefix or "data") + ".requires-python", definition={'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, rule='format')
+        if "license" in data_keys:
+            data_keys.remove("license")
+            data__license = data["license"]
+            data__license_one_of_count14 = 0
+            if data__license_one_of_count14 < 2:
+                try:
+                    if not isinstance(data__license, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be string", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, rule='type')
+                    if isinstance(data__license, str):
+                        if not custom_formats["SPDX"](data__license):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be SPDX", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, rule='format')
+                    data__license_one_of_count14 += 1
+                except JsonSchemaValueException: pass
+            if data__license_one_of_count14 < 2:
+                try:
+                    if not isinstance(data__license, (dict)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be object", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='type')
+                    data__license_is_dict = isinstance(data__license, dict)
+                    if data__license_is_dict:
+                        data__license__missing_keys = set(['file']) - data__license.keys()
+                        if data__license__missing_keys:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain " + (str(sorted(data__license__missing_keys)) + " properties"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='required')
+                        data__license_keys = set(data__license.keys())
+                        if "file" in data__license_keys:
+                            data__license_keys.remove("file")
+                            data__license__file = data__license["file"]
+                            if not isinstance(data__license__file, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.file must be string", value=data__license__file, name="" + (name_prefix or "data") + ".license.file", definition={'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}, rule='type')
+                    data__license_one_of_count14 += 1
+                except JsonSchemaValueException: pass
+            if data__license_one_of_count14 < 2:
+                try:
+                    if not isinstance(data__license, (dict)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be object", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}, rule='type')
+                    data__license_is_dict = isinstance(data__license, dict)
+                    if data__license_is_dict:
+                        data__license__missing_keys = set(['text']) - data__license.keys()
+                        if data__license__missing_keys:
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain " + (str(sorted(data__license__missing_keys)) + " properties"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}, rule='required')
+                        data__license_keys = set(data__license.keys())
+                        if "text" in data__license_keys:
+                            data__license_keys.remove("text")
+                            data__license__text = data__license["text"]
+                            if not isinstance(data__license__text, (str)):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.text must be string", value=data__license__text, name="" + (name_prefix or "data") + ".license.text", definition={'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}, rule='type')
+                    data__license_one_of_count14 += 1
+                except JsonSchemaValueException: pass
+            if data__license_one_of_count14 != 1:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count14) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf')
+        if "license-files" in data_keys:
+            data_keys.remove("license-files")
+            data__licensefiles = data["license-files"]
+            if not isinstance(data__licensefiles, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, rule='type')
+            data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple))
+            if data__licensefiles_is_list:
+                data__licensefiles_len = len(data__licensefiles)
+                for data__licensefiles_x, data__licensefiles_item in enumerate(data__licensefiles):
+                    if not isinstance(data__licensefiles_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + " must be string", value=data__licensefiles_item, name="" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "authors" in data_keys:
+            data_keys.remove("authors")
+            data__authors = data["authors"]
+            if not isinstance(data__authors, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, rule='type')
+            data__authors_is_list = isinstance(data__authors, (list, tuple))
+            if data__authors_is_list:
+                data__authors_len = len(data__authors)
+                for data__authors_x, data__authors_item in enumerate(data__authors):
+                    validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]".format(**locals()))
+        if "maintainers" in data_keys:
+            data_keys.remove("maintainers")
+            data__maintainers = data["maintainers"]
+            if not isinstance(data__maintainers, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, rule='type')
+            data__maintainers_is_list = isinstance(data__maintainers, (list, tuple))
+            if data__maintainers_is_list:
+                data__maintainers_len = len(data__maintainers)
+                for data__maintainers_x, data__maintainers_item in enumerate(data__maintainers):
+                    validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]".format(**locals()))
+        if "keywords" in data_keys:
+            data_keys.remove("keywords")
+            data__keywords = data["keywords"]
+            if not isinstance(data__keywords, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".keywords must be array", value=data__keywords, name="" + (name_prefix or "data") + ".keywords", definition={'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, rule='type')
+            data__keywords_is_list = isinstance(data__keywords, (list, tuple))
+            if data__keywords_is_list:
+                data__keywords_len = len(data__keywords)
+                for data__keywords_x, data__keywords_item in enumerate(data__keywords):
+                    if not isinstance(data__keywords_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".keywords[{data__keywords_x}]".format(**locals()) + " must be string", value=data__keywords_item, name="" + (name_prefix or "data") + ".keywords[{data__keywords_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
+        if "classifiers" in data_keys:
+            data_keys.remove("classifiers")
+            data__classifiers = data["classifiers"]
+            if not isinstance(data__classifiers, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers must be array", value=data__classifiers, name="" + (name_prefix or "data") + ".classifiers", definition={'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, rule='type')
+            data__classifiers_is_list = isinstance(data__classifiers, (list, tuple))
+            if data__classifiers_is_list:
+                data__classifiers_len = len(data__classifiers)
+                for data__classifiers_x, data__classifiers_item in enumerate(data__classifiers):
+                    if not isinstance(data__classifiers_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + " must be string", value=data__classifiers_item, name="" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, rule='type')
+                    if isinstance(data__classifiers_item, str):
+                        if not custom_formats["trove-classifier"](data__classifiers_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + " must be trove-classifier", value=data__classifiers_item, name="" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, rule='format')
+        if "urls" in data_keys:
+            data_keys.remove("urls")
+            data__urls = data["urls"]
+            if not isinstance(data__urls, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls must be object", value=data__urls, name="" + (name_prefix or "data") + ".urls", definition={'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, rule='type')
+            data__urls_is_dict = isinstance(data__urls, dict)
+            if data__urls_is_dict:
+                data__urls_keys = set(data__urls.keys())
+                for data__urls_key, data__urls_val in data__urls.items():
+                    if REGEX_PATTERNS['^.+$'].search(data__urls_key):
+                        if data__urls_key in data__urls_keys:
+                            data__urls_keys.remove(data__urls_key)
+                        if not isinstance(data__urls_val, (str)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + " must be string", value=data__urls_val, name="" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'url'}, rule='type')
+                        if isinstance(data__urls_val, str):
+                            if not custom_formats["url"](data__urls_val):
+                                raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + " must be url", value=data__urls_val, name="" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'url'}, rule='format')
+                if data__urls_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls must not contain "+str(data__urls_keys)+" properties", value=data__urls, name="" + (name_prefix or "data") + ".urls", definition={'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, rule='additionalProperties')
+        if "scripts" in data_keys:
+            data_keys.remove("scripts")
+            data__scripts = data["scripts"]
+            validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__scripts, custom_formats, (name_prefix or "data") + ".scripts")
+        if "gui-scripts" in data_keys:
+            data_keys.remove("gui-scripts")
+            data__guiscripts = data["gui-scripts"]
+            validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__guiscripts, custom_formats, (name_prefix or "data") + ".gui-scripts")
+        if "entry-points" in data_keys:
+            data_keys.remove("entry-points")
+            data__entrypoints = data["entry-points"]
+            data__entrypoints_is_dict = isinstance(data__entrypoints, dict)
+            if data__entrypoints_is_dict:
+                data__entrypoints_keys = set(data__entrypoints.keys())
+                for data__entrypoints_key, data__entrypoints_val in data__entrypoints.items():
+                    if REGEX_PATTERNS['^.+$'].search(data__entrypoints_key):
+                        if data__entrypoints_key in data__entrypoints_keys:
+                            data__entrypoints_keys.remove(data__entrypoints_key)
+                        validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}".format(**locals()))
+                if data__entrypoints_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must not contain "+str(data__entrypoints_keys)+" properties", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='additionalProperties')
+                data__entrypoints_len = len(data__entrypoints)
+                if data__entrypoints_len != 0:
+                    data__entrypoints_property_names = True
+                    for data__entrypoints_key in data__entrypoints:
+                        try:
+                            if isinstance(data__entrypoints_key, str):
+                                if not custom_formats["python-entrypoint-group"](data__entrypoints_key):
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must be python-entrypoint-group", value=data__entrypoints_key, name="" + (name_prefix or "data") + ".entry-points", definition={'format': 'python-entrypoint-group'}, rule='format')
+                        except JsonSchemaValueException:
+                            data__entrypoints_property_names = False
+                    if not data__entrypoints_property_names:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must be named by propertyName definition", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='propertyNames')
+        if "dependencies" in data_keys:
+            data_keys.remove("dependencies")
+            data__dependencies = data["dependencies"]
+            if not isinstance(data__dependencies, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependencies must be array", value=data__dependencies, name="" + (name_prefix or "data") + ".dependencies", definition={'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, rule='type')
+            data__dependencies_is_list = isinstance(data__dependencies, (list, tuple))
+            if data__dependencies_is_list:
+                data__dependencies_len = len(data__dependencies)
+                for data__dependencies_x, data__dependencies_item in enumerate(data__dependencies):
+                    validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]".format(**locals()))
+        if "optional-dependencies" in data_keys:
+            data_keys.remove("optional-dependencies")
+            data__optionaldependencies = data["optional-dependencies"]
+            if not isinstance(data__optionaldependencies, (dict)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be object", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type')
+            data__optionaldependencies_is_dict = isinstance(data__optionaldependencies, dict)
+            if data__optionaldependencies_is_dict:
+                data__optionaldependencies_keys = set(data__optionaldependencies.keys())
+                for data__optionaldependencies_key, data__optionaldependencies_val in data__optionaldependencies.items():
+                    if REGEX_PATTERNS['^.+$'].search(data__optionaldependencies_key):
+                        if data__optionaldependencies_key in data__optionaldependencies_keys:
+                            data__optionaldependencies_keys.remove(data__optionaldependencies_key)
+                        if not isinstance(data__optionaldependencies_val, (list, tuple)):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}".format(**locals()) + " must be array", value=data__optionaldependencies_val, name="" + (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, rule='type')
+                        data__optionaldependencies_val_is_list = isinstance(data__optionaldependencies_val, (list, tuple))
+                        if data__optionaldependencies_val_is_list:
+                            data__optionaldependencies_val_len = len(data__optionaldependencies_val)
+                            for data__optionaldependencies_val_x, data__optionaldependencies_val_item in enumerate(data__optionaldependencies_val):
+                                validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]".format(**locals()))
+                if data__optionaldependencies_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must not contain "+str(data__optionaldependencies_keys)+" properties", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties')
+                data__optionaldependencies_len = len(data__optionaldependencies)
+                if data__optionaldependencies_len != 0:
+                    data__optionaldependencies_property_names = True
+                    for data__optionaldependencies_key in data__optionaldependencies:
+                        try:
+                            if isinstance(data__optionaldependencies_key, str):
+                                if not custom_formats["pep508-identifier"](data__optionaldependencies_key):
+                                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be pep508-identifier", value=data__optionaldependencies_key, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'format': 'pep508-identifier'}, rule='format')
+                        except JsonSchemaValueException:
+                            data__optionaldependencies_property_names = False
+                    if not data__optionaldependencies_property_names:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be named by propertyName definition", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='propertyNames')
+        if "import-names" in data_keys:
+            data_keys.remove("import-names")
+            data__importnames = data["import-names"]
+            if not isinstance(data__importnames, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-names must be array", value=data__importnames, name="" + (name_prefix or "data") + ".import-names", definition={'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, rule='type')
+            data__importnames_is_list = isinstance(data__importnames, (list, tuple))
+            if data__importnames_is_list:
+                data__importnames_len = len(data__importnames)
+                for data__importnames_x, data__importnames_item in enumerate(data__importnames):
+                    if not isinstance(data__importnames_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-names[{data__importnames_x}]".format(**locals()) + " must be string", value=data__importnames_item, name="" + (name_prefix or "data") + ".import-names[{data__importnames_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'import-name'}, rule='type')
+                    if isinstance(data__importnames_item, str):
+                        if not custom_formats["import-name"](data__importnames_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-names[{data__importnames_x}]".format(**locals()) + " must be import-name", value=data__importnames_item, name="" + (name_prefix or "data") + ".import-names[{data__importnames_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'import-name'}, rule='format')
+        if "import-namespaces" in data_keys:
+            data_keys.remove("import-namespaces")
+            data__importnamespaces = data["import-namespaces"]
+            if not isinstance(data__importnamespaces, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-namespaces must be array", value=data__importnamespaces, name="" + (name_prefix or "data") + ".import-namespaces", definition={'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, rule='type')
+            data__importnamespaces_is_list = isinstance(data__importnamespaces, (list, tuple))
+            if data__importnamespaces_is_list:
+                data__importnamespaces_len = len(data__importnamespaces)
+                for data__importnamespaces_x, data__importnamespaces_item in enumerate(data__importnamespaces):
+                    if not isinstance(data__importnamespaces_item, (str)):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-namespaces[{data__importnamespaces_x}]".format(**locals()) + " must be string", value=data__importnamespaces_item, name="" + (name_prefix or "data") + ".import-namespaces[{data__importnamespaces_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'import-name'}, rule='type')
+                    if isinstance(data__importnamespaces_item, str):
+                        if not custom_formats["import-name"](data__importnamespaces_item):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + ".import-namespaces[{data__importnamespaces_x}]".format(**locals()) + " must be import-name", value=data__importnamespaces_item, name="" + (name_prefix or "data") + ".import-namespaces[{data__importnamespaces_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'import-name'}, rule='format')
+        if "dynamic" in data_keys:
+            data_keys.remove("dynamic")
+            data__dynamic = data["dynamic"]
+            if not isinstance(data__dynamic, (list, tuple)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be array", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}, rule='type')
+            data__dynamic_is_list = isinstance(data__dynamic, (list, tuple))
+            if data__dynamic_is_list:
+                data__dynamic_len = len(data__dynamic)
+                for data__dynamic_x, data__dynamic_item in enumerate(data__dynamic):
+                    if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']:
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']", value=data__dynamic_item, name="" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + "", definition={'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}, rule='enum')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'import-names': {'description': 'Lists import names which a project, when installed, would exclusively provide.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'import-namespaces': {'description': 'Lists import names that, when installed, would be provided by the project, but not exclusively.', 'type': 'array', 'items': {'type': 'string', 'format': 'import-name'}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies', 'import-names', 'import-namespaces']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', '    If the core metadata specification lists a field as "Required", then', '    the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', '    The required fields are: Metadata-Version, Name, Version.', '    All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties')
+    return data
+
+def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (str)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='type')
+    if isinstance(data, str):
+        if not custom_formats["pep508"](data):
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep508", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='format')
+    return data
+
+def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='type')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        for data_key, data_val in data.items():
+            if REGEX_PATTERNS['^.+$'].search(data_key):
+                if data_key in data_keys:
+                    data_keys.remove(data_key)
+                if not isinstance(data_val, (str)):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be string", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}, rule='type')
+                if isinstance(data_val, str):
+                    if not custom_formats["python-entrypoint-reference"](data_val):
+                        raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be python-entrypoint-reference", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}, rule='format')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='additionalProperties')
+        data_len = len(data)
+        if data_len != 0:
+            data_property_names = True
+            for data_key in data:
+                try:
+                    if isinstance(data_key, str):
+                        if not custom_formats["python-entrypoint-name"](data_key):
+                            raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-entrypoint-name", value=data_key, name="" + (name_prefix or "data") + "", definition={'format': 'python-entrypoint-name'}, rule='format')
+                except JsonSchemaValueException:
+                    data_property_names = False
+            if not data_property_names:
+                raise JsonSchemaValueException("" + (name_prefix or "data") + " must be named by propertyName definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='propertyNames')
+    return data
+
+def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data, custom_formats={}, name_prefix=None):
+    if not isinstance(data, (dict)):
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, rule='type')
+    data_any_of_count15 = 0
+    if not data_any_of_count15:
+        try:
+            data_is_dict = isinstance(data, dict)
+            if data_is_dict:
+                data__missing_keys = set(['name']) - data.keys()
+                if data__missing_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['name']}, rule='required')
+            data_any_of_count15 += 1
+        except JsonSchemaValueException: pass
+    if not data_any_of_count15:
+        try:
+            data_is_dict = isinstance(data, dict)
+            if data_is_dict:
+                data__missing_keys = set(['email']) - data.keys()
+                if data__missing_keys:
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['email']}, rule='required')
+            data_any_of_count15 += 1
+        except JsonSchemaValueException: pass
+    if not data_any_of_count15:
+        raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, rule='anyOf')
+    data_is_dict = isinstance(data, dict)
+    if data_is_dict:
+        data_keys = set(data.keys())
+        if "name" in data_keys:
+            data_keys.remove("name")
+            data__name = data["name"]
+            if not isinstance(data__name, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, rule='type')
+        if "email" in data_keys:
+            data_keys.remove("email")
+            data__email = data["email"]
+            if not isinstance(data__email, (str)):
+                raise JsonSchemaValueException("" + (name_prefix or "data") + ".email must be string", value=data__email, name="" + (name_prefix or "data") + ".email", definition={'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}, rule='type')
+            if isinstance(data__email, str):
+                if not REGEX_PATTERNS["idn-email_re_pattern"].match(data__email):
+                    raise JsonSchemaValueException("" + (name_prefix or "data") + ".email must be idn-email", value=data__email, name="" + (name_prefix or "data") + ".email", definition={'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}, rule='format')
+        if data_keys:
+            raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}, 'anyOf': [{'required': ['name']}, {'required': ['email']}]}, rule='additionalProperties')
+    return data
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/formats.py b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/formats.py
new file mode 100644
index 0000000..fe958cc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/_validate_pyproject/formats.py
@@ -0,0 +1,464 @@
+"""
+The functions in this module are used to validate schemas with the
+`format JSON Schema keyword
+`_.
+
+The correspondence is given by replacing the ``_`` character in the name of the
+function with a ``-`` to obtain the format name and vice versa.
+"""
+
+from __future__ import annotations
+
+import keyword
+import logging
+import os
+import re
+import string
+import typing
+from itertools import chain as _chain
+
+if typing.TYPE_CHECKING:
+    import builtins
+
+    from typing_extensions import Literal
+
+_logger = logging.getLogger(__name__)
+
+# -------------------------------------------------------------------------------------
+# PEP 440
+
+VERSION_PATTERN = r"""
+    v?
+    (?:
+        (?:(?P[0-9]+)!)?                           # epoch
+        (?P[0-9]+(?:\.[0-9]+)*)                  # release segment
+        (?P
                                          # pre-release
+            [-_\.]?
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [-_\.]?
+            (?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
+"""
+
+VERSION_REGEX = re.compile(
+    r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE
+)
+
+
+def pep440(version: str) -> bool:
+    """See :ref:`PyPA's version specification `
+    (initially introduced in :pep:`440`).
+    """
+    return VERSION_REGEX.match(version) is not None
+
+
+# -------------------------------------------------------------------------------------
+# PEP 508
+
+PEP508_IDENTIFIER_PATTERN = r"([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])"
+PEP508_IDENTIFIER_REGEX = re.compile(f"^{PEP508_IDENTIFIER_PATTERN}$", re.IGNORECASE)
+
+
+def pep508_identifier(name: str) -> bool:
+    """See :ref:`PyPA's name specification `
+    (initially introduced in :pep:`508#names`).
+    """
+    return PEP508_IDENTIFIER_REGEX.match(name) is not None
+
+
+try:
+    try:
+        from packaging import requirements as _req
+    except ImportError:  # pragma: no cover
+        # let's try setuptools vendored version
+        from setuptools._vendor.packaging import (  # type: ignore[no-redef]
+            requirements as _req,
+        )
+
+    def pep508(value: str) -> bool:
+        """See :ref:`PyPA's dependency specifiers `
+        (initially introduced in :pep:`508`).
+        """
+        try:
+            _req.Requirement(value)
+        except _req.InvalidRequirement:
+            return False
+        return True
+
+except ImportError:  # pragma: no cover
+    _logger.warning(
+        "Could not find an installation of `packaging`. Requirements, dependencies and "
+        "versions might not be validated. "
+        "To enforce validation, please install `packaging`."
+    )
+
+    def pep508(value: str) -> bool:  # noqa: ARG001
+        return True
+
+
+def pep508_versionspec(value: str) -> bool:
+    """Expression that can be used to specify/lock versions (including ranges)
+    See ``versionspec`` in :ref:`PyPA's dependency specifiers
+    ` (initially introduced in :pep:`508`).
+    """
+    if any(c in value for c in (";", "]", "@")):
+        # In PEP 508:
+        # conditional markers, extras and URL specs are not included in the
+        # versionspec
+        return False
+    # Let's pretend we have a dependency called `requirement` with the given
+    # version spec, then we can reuse the pep508 function for validation:
+    return pep508(f"requirement{value}")
+
+
+# -------------------------------------------------------------------------------------
+# PEP 517
+
+
+def pep517_backend_reference(value: str) -> bool:
+    """See PyPA's specification for defining build-backend references
+    introduced in :pep:`517#source-trees`.
+
+    This is similar to an entry-point reference (e.g., ``package.module:object``).
+    """
+    module, _, obj = value.partition(":")
+    identifiers = (i.strip() for i in _chain(module.split("."), obj.split(".")))
+    return all(python_identifier(i) for i in identifiers if i)
+
+
+# -------------------------------------------------------------------------------------
+# Classifiers - PEP 301
+
+
+def _download_classifiers() -> str:
+    import ssl
+    from email.message import Message
+    from urllib.request import urlopen
+
+    url = "https://pypi.org/pypi?:action=list_classifiers"
+    context = ssl.create_default_context()
+    with urlopen(url, context=context) as response:  # noqa: S310 (audit URLs)
+        headers = Message()
+        headers["content_type"] = response.getheader("content-type", "text/plain")
+        return response.read().decode(headers.get_param("charset", "utf-8"))  # type: ignore[no-any-return]
+
+
+class _TroveClassifier:
+    """The ``trove_classifiers`` package is the official way of validating classifiers,
+    however this package might not be always available.
+    As a workaround we can still download a list from PyPI.
+    We also don't want to be over strict about it, so simply skipping silently is an
+    option (classifiers will be validated anyway during the upload to PyPI).
+    """
+
+    downloaded: None | Literal[False] | set[str]
+    """
+    None => not cached yet
+    False => unavailable
+    set => cached values
+    """
+
+    def __init__(self) -> None:
+        self.downloaded = None
+        self._skip_download = False
+        self.__name__ = "trove_classifier"  # Emulate a public function
+
+    def _disable_download(self) -> None:
+        # This is a private API. Only setuptools has the consent of using it.
+        self._skip_download = True
+
+    def __call__(self, value: str) -> bool:
+        if self.downloaded is False or self._skip_download is True:
+            return True
+
+        if os.getenv("NO_NETWORK") or os.getenv("VALIDATE_PYPROJECT_NO_NETWORK"):
+            self.downloaded = False
+            msg = (
+                "Install ``trove-classifiers`` to ensure proper validation. "
+                "Skipping download of classifiers list from PyPI (NO_NETWORK)."
+            )
+            _logger.debug(msg)
+            return True
+
+        if self.downloaded is None:
+            msg = (
+                "Install ``trove-classifiers`` to ensure proper validation. "
+                "Meanwhile a list of classifiers will be downloaded from PyPI."
+            )
+            _logger.debug(msg)
+            try:
+                self.downloaded = set(_download_classifiers().splitlines())
+            except Exception:  # noqa: BLE001
+                self.downloaded = False
+                _logger.debug("Problem with download, skipping validation")
+                return True
+
+        return value in self.downloaded or value.lower().startswith("private ::")
+
+
+try:
+    from trove_classifiers import classifiers as _trove_classifiers
+
+    def trove_classifier(value: str) -> bool:
+        """See https://pypi.org/classifiers/"""
+        return value in _trove_classifiers or value.lower().startswith("private ::")
+
+except ImportError:  # pragma: no cover
+    trove_classifier = _TroveClassifier()
+
+
+# -------------------------------------------------------------------------------------
+# Stub packages - PEP 561
+
+
+def pep561_stub_name(value: str) -> bool:
+    """Name of a directory containing type stubs.
+    It must follow the name scheme ``-stubs`` as defined in
+    :pep:`561#stub-only-packages`.
+    """
+    top, *children = value.split(".")
+    if not top.endswith("-stubs"):
+        return False
+    return python_module_name(".".join([top[: -len("-stubs")], *children]))
+
+
+# -------------------------------------------------------------------------------------
+# Non-PEP related
+
+
+def url(value: str) -> bool:
+    """Valid URL (validation uses :obj:`urllib.parse`).
+    For maximum compatibility please make sure to include a ``scheme`` prefix
+    in your URL (e.g. ``http://``).
+    """
+    from urllib.parse import urlparse
+
+    try:
+        parts = urlparse(value)
+        if not parts.scheme:
+            _logger.warning(
+                "For maximum compatibility please make sure to include a "
+                "`scheme` prefix in your URL (e.g. 'http://'). "
+                f"Given value: {value}"
+            )
+            if not (value.startswith(("/", "\\")) or "@" in value):
+                parts = urlparse(f"http://{value}")
+
+        return bool(parts.scheme and parts.netloc)
+    except Exception:  # noqa: BLE001
+        return False
+
+
+# https://packaging.python.org/specifications/entry-points/
+ENTRYPOINT_PATTERN = r"[^\[\s=]([^=]*[^\s=])?"
+ENTRYPOINT_REGEX = re.compile(f"^{ENTRYPOINT_PATTERN}$", re.IGNORECASE)
+RECOMMEDED_ENTRYPOINT_PATTERN = r"[\w.-]+"
+RECOMMEDED_ENTRYPOINT_REGEX = re.compile(
+    f"^{RECOMMEDED_ENTRYPOINT_PATTERN}$", re.IGNORECASE
+)
+ENTRYPOINT_GROUP_PATTERN = r"\w+(\.\w+)*"
+ENTRYPOINT_GROUP_REGEX = re.compile(f"^{ENTRYPOINT_GROUP_PATTERN}$", re.IGNORECASE)
+
+
+def python_identifier(value: str) -> bool:
+    """Can be used as identifier in Python.
+    (Validation uses :obj:`str.isidentifier`).
+    """
+    return value.isidentifier()
+
+
+def python_qualified_identifier(value: str) -> bool:
+    """
+    Python "dotted identifier", i.e. a sequence of :obj:`python_identifier`
+    concatenated with ``"."`` (e.g.: ``package.module.submodule``).
+    """
+    if value.startswith(".") or value.endswith("."):
+        return False
+    return all(python_identifier(m) for m in value.split("."))
+
+
+def python_module_name(value: str) -> bool:
+    """Module name that can be used in an ``import``-statement in Python.
+    See :obj:`python_qualified_identifier`.
+    """
+    return python_qualified_identifier(value)
+
+
+def python_module_name_relaxed(value: str) -> bool:
+    """Similar to :obj:`python_module_name`, but relaxed to also accept
+    dash characters (``-``) and cover special cases like ``pip-run``.
+
+    It is recommended, however, that beginners avoid dash characters,
+    as they require advanced knowledge about Python internals.
+
+    The following are disallowed:
+
+    * names starting/ending in dashes,
+    * names ending in ``-stubs`` (potentially collide with :obj:`pep561_stub_name`).
+    """
+    if value.startswith("-") or value.endswith("-"):
+        return False
+    if value.endswith("-stubs"):
+        return False  # Avoid collision with PEP 561
+    return python_module_name(value.replace("-", "_"))
+
+
+def python_entrypoint_group(value: str) -> bool:
+    """See ``Data model > group`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
+    return ENTRYPOINT_GROUP_REGEX.match(value) is not None
+
+
+def python_entrypoint_name(value: str) -> bool:
+    """See ``Data model > name`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
+    if not ENTRYPOINT_REGEX.match(value):
+        return False
+    if not RECOMMEDED_ENTRYPOINT_REGEX.match(value):
+        msg = f"Entry point `{value}` does not follow recommended pattern: "
+        msg += RECOMMEDED_ENTRYPOINT_PATTERN
+        _logger.warning(msg)
+    return True
+
+
+def python_entrypoint_reference(value: str) -> bool:
+    """Reference to a Python object using in the format::
+
+        importable.module:object.attr
+
+    See ``Data model >object reference`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
+    module, _, rest = value.partition(":")
+    if "[" in rest:
+        obj, _, extras_ = rest.partition("[")
+        if extras_.strip()[-1] != "]":
+            return False
+        extras = (x.strip() for x in extras_.strip(string.whitespace + "[]").split(","))
+        if not all(pep508_identifier(e) for e in extras):
+            return False
+        _logger.warning(f"`{value}` - using extras for entry points is not recommended")
+    else:
+        obj = rest
+
+    module_parts = module.split(".")
+    identifiers = _chain(module_parts, obj.split(".")) if rest else iter(module_parts)
+    return all(python_identifier(i.strip()) for i in identifiers)
+
+
+def uint8(value: builtins.int) -> bool:
+    r"""Unsigned 8-bit integer (:math:`0 \leq x < 2^8`)"""
+    return 0 <= value < 2**8
+
+
+def uint16(value: builtins.int) -> bool:
+    r"""Unsigned 16-bit integer (:math:`0 \leq x < 2^{16}`)"""
+    return 0 <= value < 2**16
+
+
+def uint32(value: builtins.int) -> bool:
+    r"""Unsigned 32-bit integer (:math:`0 \leq x < 2^{32}`)"""
+    return 0 <= value < 2**32
+
+
+def uint64(value: builtins.int) -> bool:
+    r"""Unsigned 64-bit integer (:math:`0 \leq x < 2^{64}`)"""
+    return 0 <= value < 2**64
+
+
+def uint(value: builtins.int) -> bool:
+    r"""Signed 64-bit integer (:math:`0 \leq x < 2^{64}`)"""
+    return 0 <= value < 2**64
+
+
+def int8(value: builtins.int) -> bool:
+    r"""Signed 8-bit integer (:math:`-2^{7} \leq x < 2^{7}`)"""
+    return -(2**7) <= value < 2**7
+
+
+def int16(value: builtins.int) -> bool:
+    r"""Signed 16-bit integer (:math:`-2^{15} \leq x < 2^{15}`)"""
+    return -(2**15) <= value < 2**15
+
+
+def int32(value: builtins.int) -> bool:
+    r"""Signed 32-bit integer (:math:`-2^{31} \leq x < 2^{31}`)"""
+    return -(2**31) <= value < 2**31
+
+
+def int64(value: builtins.int) -> bool:
+    r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""
+    return -(2**63) <= value < 2**63
+
+
+def int(value: builtins.int) -> bool:
+    r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""
+    return -(2**63) <= value < 2**63
+
+
+try:
+    from packaging import licenses as _licenses
+
+    def SPDX(value: str) -> bool:
+        """See :ref:`PyPA's License-Expression specification
+        ` (added in :pep:`639`).
+        """
+        try:
+            _licenses.canonicalize_license_expression(value)
+        except _licenses.InvalidLicenseExpression:
+            return False
+        return True
+
+except ImportError:  # pragma: no cover
+    _logger.warning(
+        "Could not find an up-to-date installation of `packaging`. "
+        "License expressions might not be validated. "
+        "To enforce validation, please install `packaging>=24.2`."
+    )
+
+    def SPDX(value: str) -> bool:  # noqa: ARG001
+        return True
+
+
+VALID_IMPORT_NAME = re.compile(
+    r"""
+    ^                                  # start of string
+        [A-Za-z_][A-Za-z_0-9]+         # a valid Python identifier
+        (?:\.[A-Za-z_][A-Za-z_0-9]*)*  # optionally followed by .identifier's
+    (?:\s*;\s*private)?                # optionally followed by ; private
+    $                                  # end of string
+    """,
+    re.VERBOSE,
+)
+
+
+def import_name(value: str) -> bool:
+    """This is a valid import name. It has to be series of python identifiers
+    (not keywords), separated by dots, optionally followed by a semicolon and
+    the keyword "private".
+    """
+    if VALID_IMPORT_NAME.match(value) is None:
+        return False
+
+    idents, _, _ = value.partition(";")
+    return all(not keyword.iskeyword(ident) for ident in idents.rstrip().split("."))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/distutils.schema.json b/.venv/lib/python3.12/site-packages/setuptools/config/distutils.schema.json
new file mode 100644
index 0000000..93cd2e8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/distutils.schema.json
@@ -0,0 +1,26 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+
+  "$id": "https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html",
+  "title": "``tool.distutils`` table",
+  "$$description": [
+    "**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``",
+    "subtables to configure arguments for ``distutils`` commands.",
+    "Originally, ``distutils`` allowed developers to configure arguments for",
+    "``setup.py`` commands via `distutils configuration files",
+    "`_.",
+    "See also `the old Python docs _`."
+  ],
+
+  "type": "object",
+  "properties": {
+    "global": {
+      "type": "object",
+      "description": "Global options applied to all ``distutils`` commands"
+    }
+  },
+  "patternProperties": {
+    ".+": {"type": "object"}
+  },
+  "$comment": "TODO: Is there a practical way of making this schema more specific?"
+}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/expand.py b/.venv/lib/python3.12/site-packages/setuptools/config/expand.py
new file mode 100644
index 0000000..d9a2ded
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/expand.py
@@ -0,0 +1,452 @@
+"""Utility functions to expand configuration directives or special values
+(such glob patterns).
+
+We can split the process of interpreting configuration files into 2 steps:
+
+1. The parsing the file contents from strings to value objects
+   that can be understand by Python (for example a string with a comma
+   separated list of keywords into an actual Python list of strings).
+
+2. The expansion (or post-processing) of these values according to the
+   semantics ``setuptools`` assign to them (for example a configuration field
+   with the ``file:`` directive should be expanded from a list of file paths to
+   a single string with the contents of those files concatenated)
+
+This module focus on the second step, and therefore allow sharing the expansion
+functions among several configuration file formats.
+
+**PRIVATE MODULE**: API reserved for setuptools internal usage only.
+"""
+
+from __future__ import annotations
+
+import ast
+import importlib
+import os
+import pathlib
+import sys
+from collections.abc import Iterable, Iterator, Mapping
+from configparser import ConfigParser
+from glob import iglob
+from importlib.machinery import ModuleSpec, all_suffixes
+from itertools import chain
+from pathlib import Path
+from types import ModuleType, TracebackType
+from typing import TYPE_CHECKING, Any, Callable, TypeVar
+
+from .. import _static
+from .._path import StrPath, same_path as _same_path
+from ..discovery import find_package_path
+from ..warnings import SetuptoolsWarning
+
+from distutils.errors import DistutilsOptionError
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+    from setuptools.dist import Distribution
+
+_K = TypeVar("_K")
+_V_co = TypeVar("_V_co", covariant=True)
+
+
+class StaticModule:
+    """Proxy to a module object that avoids executing arbitrary code."""
+
+    def __init__(self, name: str, spec: ModuleSpec) -> None:
+        module = ast.parse(pathlib.Path(spec.origin).read_bytes())  # type: ignore[arg-type] # Let it raise an error on None
+        vars(self).update(locals())
+        del self.self
+
+    def _find_assignments(self) -> Iterator[tuple[ast.AST, ast.AST]]:
+        for statement in self.module.body:
+            if isinstance(statement, ast.Assign):
+                yield from ((target, statement.value) for target in statement.targets)
+            elif isinstance(statement, ast.AnnAssign) and statement.value:
+                yield (statement.target, statement.value)
+
+    def __getattr__(self, attr: str) -> Any:
+        """Attempt to load an attribute "statically", via :func:`ast.literal_eval`."""
+        try:
+            return next(
+                ast.literal_eval(value)
+                for target, value in self._find_assignments()
+                if isinstance(target, ast.Name) and target.id == attr
+            )
+        except Exception as e:
+            raise AttributeError(f"{self.name} has no attribute {attr}") from e
+
+
+def glob_relative(
+    patterns: Iterable[str], root_dir: StrPath | None = None
+) -> list[str]:
+    """Expand the list of glob patterns, but preserving relative paths.
+
+    :param list[str] patterns: List of glob patterns
+    :param str root_dir: Path to which globs should be relative
+                         (current directory by default)
+    :rtype: list
+    """
+    glob_characters = {'*', '?', '[', ']', '{', '}'}
+    expanded_values = []
+    root_dir = root_dir or os.getcwd()
+    for value in patterns:
+        # Has globby characters?
+        if any(char in value for char in glob_characters):
+            # then expand the glob pattern while keeping paths *relative*:
+            glob_path = os.path.abspath(os.path.join(root_dir, value))
+            expanded_values.extend(
+                sorted(
+                    os.path.relpath(path, root_dir).replace(os.sep, "/")
+                    for path in iglob(glob_path, recursive=True)
+                )
+            )
+
+        else:
+            # take the value as-is
+            path = os.path.relpath(value, root_dir).replace(os.sep, "/")
+            expanded_values.append(path)
+
+    return expanded_values
+
+
+def read_files(
+    filepaths: StrPath | Iterable[StrPath], root_dir: StrPath | None = None
+) -> str:
+    """Return the content of the files concatenated using ``\n`` as str
+
+    This function is sandboxed and won't reach anything outside ``root_dir``
+
+    (By default ``root_dir`` is the current directory).
+    """
+    from more_itertools import always_iterable
+
+    root_dir = os.path.abspath(root_dir or os.getcwd())
+    _filepaths = (os.path.join(root_dir, path) for path in always_iterable(filepaths))
+    return '\n'.join(
+        _read_file(path)
+        for path in _filter_existing_files(_filepaths)
+        if _assert_local(path, root_dir)
+    )
+
+
+def _filter_existing_files(filepaths: Iterable[StrPath]) -> Iterator[StrPath]:
+    for path in filepaths:
+        if os.path.isfile(path):
+            yield path
+        else:
+            SetuptoolsWarning.emit(f"File {path!r} cannot be found")
+
+
+def _read_file(filepath: bytes | StrPath) -> str:
+    with open(filepath, encoding='utf-8') as f:
+        return f.read()
+
+
+def _assert_local(filepath: StrPath, root_dir: str):
+    if Path(os.path.abspath(root_dir)) not in Path(os.path.abspath(filepath)).parents:
+        msg = f"Cannot access {filepath!r} (or anything outside {root_dir!r})"
+        raise DistutilsOptionError(msg)
+
+    return True
+
+
+def read_attr(
+    attr_desc: str,
+    package_dir: Mapping[str, str] | None = None,
+    root_dir: StrPath | None = None,
+) -> Any:
+    """Reads the value of an attribute from a module.
+
+    This function will try to read the attributed statically first
+    (via :func:`ast.literal_eval`), and only evaluate the module if it fails.
+
+    Examples:
+        read_attr("package.attr")
+        read_attr("package.module.attr")
+
+    :param str attr_desc: Dot-separated string describing how to reach the
+        attribute (see examples above)
+    :param dict[str, str] package_dir: Mapping of package names to their
+        location in disk (represented by paths relative to ``root_dir``).
+    :param str root_dir: Path to directory containing all the packages in
+        ``package_dir`` (current directory by default).
+    :rtype: str
+    """
+    root_dir = root_dir or os.getcwd()
+    attrs_path = attr_desc.strip().split('.')
+    attr_name = attrs_path.pop()
+    module_name = '.'.join(attrs_path)
+    module_name = module_name or '__init__'
+    path = _find_module(module_name, package_dir, root_dir)
+    spec = _find_spec(module_name, path)
+
+    try:
+        value = getattr(StaticModule(module_name, spec), attr_name)
+        # XXX: Is marking as static contents coming from modules too optimistic?
+        return _static.attempt_conversion(value)
+    except Exception:
+        # fallback to evaluate module
+        module = _load_spec(spec, module_name)
+        return getattr(module, attr_name)
+
+
+def _find_spec(module_name: str, module_path: StrPath | None) -> ModuleSpec:
+    spec = importlib.util.spec_from_file_location(module_name, module_path)
+    spec = spec or importlib.util.find_spec(module_name)
+
+    if spec is None:
+        raise ModuleNotFoundError(module_name)
+
+    return spec
+
+
+def _load_spec(spec: ModuleSpec, module_name: str) -> ModuleType:
+    name = getattr(spec, "__name__", module_name)
+    if name in sys.modules:
+        return sys.modules[name]
+    module = importlib.util.module_from_spec(spec)
+    sys.modules[name] = module  # cache (it also ensures `==` works on loaded items)
+    assert spec.loader is not None
+    spec.loader.exec_module(module)
+    return module
+
+
+def _find_module(
+    module_name: str, package_dir: Mapping[str, str] | None, root_dir: StrPath
+) -> str | None:
+    """Find the path to the module named ``module_name``,
+    considering the ``package_dir`` in the build configuration and ``root_dir``.
+
+    >>> tmp = getfixture('tmpdir')
+    >>> _ = tmp.ensure("a/b/c.py")
+    >>> _ = tmp.ensure("a/b/d/__init__.py")
+    >>> r = lambda x: x.replace(str(tmp), "tmp").replace(os.sep, "/")
+    >>> r(_find_module("a.b.c", None, tmp))
+    'tmp/a/b/c.py'
+    >>> r(_find_module("f.g.h", {"": "1", "f": "2", "f.g": "3", "f.g.h": "a/b/d"}, tmp))
+    'tmp/a/b/d/__init__.py'
+    """
+    path_start = find_package_path(module_name, package_dir or {}, root_dir)
+    candidates = chain.from_iterable(
+        (f"{path_start}{ext}", os.path.join(path_start, f"__init__{ext}"))
+        for ext in all_suffixes()
+    )
+    return next((x for x in candidates if os.path.isfile(x)), None)
+
+
+def resolve_class(
+    qualified_class_name: str,
+    package_dir: Mapping[str, str] | None = None,
+    root_dir: StrPath | None = None,
+) -> Callable:
+    """Given a qualified class name, return the associated class object"""
+    root_dir = root_dir or os.getcwd()
+    idx = qualified_class_name.rfind('.')
+    class_name = qualified_class_name[idx + 1 :]
+    pkg_name = qualified_class_name[:idx]
+
+    path = _find_module(pkg_name, package_dir, root_dir)
+    module = _load_spec(_find_spec(pkg_name, path), pkg_name)
+    return getattr(module, class_name)
+
+
+def cmdclass(
+    values: dict[str, str],
+    package_dir: Mapping[str, str] | None = None,
+    root_dir: StrPath | None = None,
+) -> dict[str, Callable]:
+    """Given a dictionary mapping command names to strings for qualified class
+    names, apply :func:`resolve_class` to the dict values.
+    """
+    return {k: resolve_class(v, package_dir, root_dir) for k, v in values.items()}
+
+
+def find_packages(
+    *,
+    namespaces=True,
+    fill_package_dir: dict[str, str] | None = None,
+    root_dir: StrPath | None = None,
+    **kwargs,
+) -> list[str]:
+    """Works similarly to :func:`setuptools.find_packages`, but with all
+    arguments given as keyword arguments. Moreover, ``where`` can be given
+    as a list (the results will be simply concatenated).
+
+    When the additional keyword argument ``namespaces`` is ``True``, it will
+    behave like :func:`setuptools.find_namespace_packages`` (i.e. include
+    implicit namespaces as per :pep:`420`).
+
+    The ``where`` argument will be considered relative to ``root_dir`` (or the current
+    working directory when ``root_dir`` is not given).
+
+    If the ``fill_package_dir`` argument is passed, this function will consider it as a
+    similar data structure to the ``package_dir`` configuration parameter add fill-in
+    any missing package location.
+
+    :rtype: list
+    """
+    from more_itertools import always_iterable, unique_everseen
+
+    from setuptools.discovery import construct_package_dir
+
+    # check "not namespaces" first due to python/mypy#6232
+    if not namespaces:
+        from setuptools.discovery import PackageFinder
+    else:
+        from setuptools.discovery import PEP420PackageFinder as PackageFinder
+
+    root_dir = root_dir or os.curdir
+    where = kwargs.pop('where', ['.'])
+    packages: list[str] = []
+    fill_package_dir = {} if fill_package_dir is None else fill_package_dir
+    search = list(unique_everseen(always_iterable(where)))
+
+    if len(search) == 1 and all(not _same_path(search[0], x) for x in (".", root_dir)):
+        fill_package_dir.setdefault("", search[0])
+
+    for path in search:
+        package_path = _nest_path(root_dir, path)
+        pkgs = PackageFinder.find(package_path, **kwargs)
+        packages.extend(pkgs)
+        if pkgs and not (
+            fill_package_dir.get("") == path or os.path.samefile(package_path, root_dir)
+        ):
+            fill_package_dir.update(construct_package_dir(pkgs, path))
+
+    return packages
+
+
+def _nest_path(parent: StrPath, path: StrPath) -> str:
+    path = parent if path in {".", ""} else os.path.join(parent, path)
+    return os.path.normpath(path)
+
+
+def version(value: Callable | Iterable[str | int] | str) -> str:
+    """When getting the version directly from an attribute,
+    it should be normalised to string.
+    """
+    _value = value() if callable(value) else value
+
+    if isinstance(_value, str):
+        return _value
+    if hasattr(_value, '__iter__'):
+        return '.'.join(map(str, _value))
+    return f'{_value}'
+
+
+def canonic_package_data(package_data: dict) -> dict:
+    if "*" in package_data:
+        package_data[""] = package_data.pop("*")
+    return package_data
+
+
+def canonic_data_files(
+    data_files: list | dict, root_dir: StrPath | None = None
+) -> list[tuple[str, list[str]]]:
+    """For compatibility with ``setup.py``, ``data_files`` should be a list
+    of pairs instead of a dict.
+
+    This function also expands glob patterns.
+    """
+    if isinstance(data_files, list):
+        return data_files
+
+    return [
+        (dest, glob_relative(patterns, root_dir))
+        for dest, patterns in data_files.items()
+    ]
+
+
+def entry_points(
+    text: str, text_source: str = "entry-points"
+) -> dict[str, dict[str, str]]:
+    """Given the contents of entry-points file,
+    process it into a 2-level dictionary (``dict[str, dict[str, str]]``).
+    The first level keys are entry-point groups, the second level keys are
+    entry-point names, and the second level values are references to objects
+    (that correspond to the entry-point value).
+    """
+    # Using undocumented behaviour, see python/typeshed#12700
+    parser = ConfigParser(default_section=None, delimiters=("=",))  # type: ignore[call-overload]
+    parser.optionxform = str  # case sensitive
+    parser.read_string(text, text_source)
+    groups = {k: dict(v.items()) for k, v in parser.items()}
+    groups.pop(parser.default_section, None)
+    return groups
+
+
+class EnsurePackagesDiscovered:
+    """Some expand functions require all the packages to already be discovered before
+    they run, e.g. :func:`read_attr`, :func:`resolve_class`, :func:`cmdclass`.
+
+    Therefore in some cases we will need to run autodiscovery during the evaluation of
+    the configuration. However, it is better to postpone calling package discovery as
+    much as possible, because some parameters can influence it (e.g. ``package_dir``),
+    and those might not have been processed yet.
+    """
+
+    def __init__(self, distribution: Distribution) -> None:
+        self._dist = distribution
+        self._called = False
+
+    def __call__(self) -> None:
+        """Trigger the automatic package discovery, if it is still necessary."""
+        if not self._called:
+            self._called = True
+            self._dist.set_defaults(name=False)  # Skip name, we can still be parsing
+
+    def __enter__(self) -> Self:
+        return self
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_value: BaseException | None,
+        traceback: TracebackType | None,
+    ) -> None:
+        if self._called:
+            self._dist.set_defaults.analyse_name()  # Now we can set a default name
+
+    def _get_package_dir(self) -> Mapping[str, str]:
+        self()
+        pkg_dir = self._dist.package_dir
+        return {} if pkg_dir is None else pkg_dir
+
+    @property
+    def package_dir(self) -> Mapping[str, str]:
+        """Proxy to ``package_dir`` that may trigger auto-discovery when used."""
+        return LazyMappingProxy(self._get_package_dir)
+
+
+class LazyMappingProxy(Mapping[_K, _V_co]):
+    """Mapping proxy that delays resolving the target object, until really needed.
+
+    >>> def obtain_mapping():
+    ...     print("Running expensive function!")
+    ...     return {"key": "value", "other key": "other value"}
+    >>> mapping = LazyMappingProxy(obtain_mapping)
+    >>> mapping["key"]
+    Running expensive function!
+    'value'
+    >>> mapping["other key"]
+    'other value'
+    """
+
+    def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V_co]]) -> None:
+        self._obtain = obtain_mapping_value
+        self._value: Mapping[_K, _V_co] | None = None
+
+    def _target(self) -> Mapping[_K, _V_co]:
+        if self._value is None:
+            self._value = self._obtain()
+        return self._value
+
+    def __getitem__(self, key: _K) -> _V_co:
+        return self._target()[key]
+
+    def __len__(self) -> int:
+        return len(self._target())
+
+    def __iter__(self) -> Iterator[_K]:
+        return iter(self._target())
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py b/.venv/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py
new file mode 100644
index 0000000..3dfc46d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/pyprojecttoml.py
@@ -0,0 +1,477 @@
+"""
+Load setuptools configuration from ``pyproject.toml`` files.
+
+**PRIVATE MODULE**: API reserved for setuptools internal usage only.
+
+To read project metadata, consider using
+``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
+For simple scenarios, you can also try parsing the file directly
+with the help of ``tomllib`` or ``tomli``.
+"""
+
+from __future__ import annotations
+
+import logging
+import os
+from collections.abc import Mapping
+from contextlib import contextmanager
+from functools import partial
+from types import TracebackType
+from typing import TYPE_CHECKING, Any, Callable
+
+from .._path import StrPath
+from ..errors import FileError, InvalidConfigError
+from ..warnings import SetuptoolsWarning
+from . import expand as _expand
+from ._apply_pyprojecttoml import _PREVIOUSLY_DEFINED, _MissingDynamic, apply as _apply
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+    from setuptools.dist import Distribution
+
+_logger = logging.getLogger(__name__)
+
+
+def load_file(filepath: StrPath) -> dict:
+    from ..compat.py310 import tomllib
+
+    with open(filepath, "rb") as file:
+        return tomllib.load(file)
+
+
+def validate(config: dict, filepath: StrPath) -> bool:
+    from . import _validate_pyproject as validator
+
+    trove_classifier = validator.FORMAT_FUNCTIONS.get("trove-classifier")
+    if hasattr(trove_classifier, "_disable_download"):
+        # Improve reproducibility by default. See abravalheri/validate-pyproject#31
+        trove_classifier._disable_download()  # type: ignore[union-attr]
+
+    try:
+        return validator.validate(config)
+    except validator.ValidationError as ex:
+        summary = f"configuration error: {ex.summary}"
+        if ex.name.strip("`") != "project":
+            # Probably it is just a field missing/misnamed, not worthy the verbosity...
+            _logger.debug(summary)
+            _logger.debug(ex.details)
+
+        error = f"invalid pyproject.toml config: {ex.name}."
+        raise ValueError(f"{error}\n{summary}") from None
+
+
+def apply_configuration(
+    dist: Distribution,
+    filepath: StrPath,
+    ignore_option_errors: bool = False,
+) -> Distribution:
+    """Apply the configuration from a ``pyproject.toml`` file into an existing
+    distribution object.
+    """
+    config = read_configuration(filepath, True, ignore_option_errors, dist)
+    return _apply(dist, config, filepath)
+
+
+def read_configuration(
+    filepath: StrPath,
+    expand: bool = True,
+    ignore_option_errors: bool = False,
+    dist: Distribution | None = None,
+) -> dict[str, Any]:
+    """Read given configuration file and returns options from it as a dict.
+
+    :param str|unicode filepath: Path to configuration file in the ``pyproject.toml``
+        format.
+
+    :param bool expand: Whether to expand directives and other computed values
+        (i.e. post-process the given configuration)
+
+    :param bool ignore_option_errors: Whether to silently ignore
+        options, values of which could not be resolved (e.g. due to exceptions
+        in directives such as file:, attr:, etc.).
+        If False exceptions are propagated as expected.
+
+    :param Distribution|None: Distribution object to which the configuration refers.
+        If not given a dummy object will be created and discarded after the
+        configuration is read. This is used for auto-discovery of packages and in the
+        case a dynamic configuration (e.g. ``attr`` or ``cmdclass``) is expanded.
+        When ``expand=False`` this object is simply ignored.
+
+    :rtype: dict
+    """
+    filepath = os.path.abspath(filepath)
+
+    if not os.path.isfile(filepath):
+        raise FileError(f"Configuration file {filepath!r} does not exist.")
+
+    asdict = load_file(filepath) or {}
+    project_table = asdict.get("project", {})
+    tool_table = asdict.get("tool", {})
+    setuptools_table = tool_table.get("setuptools", {})
+    if not asdict or not (project_table or setuptools_table):
+        return {}  # User is not using pyproject to configure setuptools
+
+    if "setuptools" in asdict.get("tools", {}):
+        # let the user know they probably have a typo in their metadata
+        _ToolsTypoInMetadata.emit()
+
+    if "distutils" in tool_table:
+        _ExperimentalConfiguration.emit(subject="[tool.distutils]")
+
+    # There is an overall sense in the community that making include_package_data=True
+    # the default would be an improvement.
+    # `ini2toml` backfills include_package_data=False when nothing is explicitly given,
+    # therefore setting a default here is backwards compatible.
+    if dist and dist.include_package_data is not None:
+        setuptools_table.setdefault("include-package-data", dist.include_package_data)
+    else:
+        setuptools_table.setdefault("include-package-data", True)
+    # Persist changes:
+    asdict["tool"] = tool_table
+    tool_table["setuptools"] = setuptools_table
+
+    if "ext-modules" in setuptools_table:
+        _ExperimentalConfiguration.emit(subject="[tool.setuptools.ext-modules]")
+
+    fields = ("import-names", "import-namespaces")
+    places = (project_table, project_table.get("dynamic", []))
+    if any(field in place for field in fields for place in places):
+        raise NotImplementedError(
+            "Setuptools does not support `import-names` and `import-namespaces`"
+            " in `pyproject.toml` yet. If your are interested in this feature, "
+            " please consider submitting a contribution via pull requests."
+        )
+
+    with _ignore_errors(ignore_option_errors):
+        # Don't complain about unrelated errors (e.g. tools not using the "tool" table)
+        subset = {"project": project_table, "tool": {"setuptools": setuptools_table}}
+        validate(subset, filepath)
+
+    if expand:
+        root_dir = os.path.dirname(filepath)
+        return expand_configuration(asdict, root_dir, ignore_option_errors, dist)
+
+    return asdict
+
+
+def expand_configuration(
+    config: dict,
+    root_dir: StrPath | None = None,
+    ignore_option_errors: bool = False,
+    dist: Distribution | None = None,
+) -> dict:
+    """Given a configuration with unresolved fields (e.g. dynamic, cmdclass, ...)
+    find their final values.
+
+    :param dict config: Dict containing the configuration for the distribution
+    :param str root_dir: Top-level directory for the distribution/project
+        (the same directory where ``pyproject.toml`` is place)
+    :param bool ignore_option_errors: see :func:`read_configuration`
+    :param Distribution|None: Distribution object to which the configuration refers.
+        If not given a dummy object will be created and discarded after the
+        configuration is read. Used in the case a dynamic configuration
+        (e.g. ``attr`` or ``cmdclass``).
+
+    :rtype: dict
+    """
+    return _ConfigExpander(config, root_dir, ignore_option_errors, dist).expand()
+
+
+class _ConfigExpander:
+    def __init__(
+        self,
+        config: dict,
+        root_dir: StrPath | None = None,
+        ignore_option_errors: bool = False,
+        dist: Distribution | None = None,
+    ) -> None:
+        self.config = config
+        self.root_dir = root_dir or os.getcwd()
+        self.project_cfg = config.get("project", {})
+        self.dynamic = self.project_cfg.get("dynamic", [])
+        self.setuptools_cfg = config.get("tool", {}).get("setuptools", {})
+        self.dynamic_cfg = self.setuptools_cfg.get("dynamic", {})
+        self.ignore_option_errors = ignore_option_errors
+        self._dist = dist
+        self._referenced_files = set[str]()
+
+    def _ensure_dist(self) -> Distribution:
+        from setuptools.dist import Distribution
+
+        attrs = {"src_root": self.root_dir, "name": self.project_cfg.get("name", None)}
+        return self._dist or Distribution(attrs)
+
+    def _process_field(self, container: dict, field: str, fn: Callable):
+        if field in container:
+            with _ignore_errors(self.ignore_option_errors):
+                container[field] = fn(container[field])
+
+    def _canonic_package_data(self, field="package-data"):
+        package_data = self.setuptools_cfg.get(field, {})
+        return _expand.canonic_package_data(package_data)
+
+    def expand(self):
+        self._expand_packages()
+        self._canonic_package_data()
+        self._canonic_package_data("exclude-package-data")
+
+        # A distribution object is required for discovering the correct package_dir
+        dist = self._ensure_dist()
+        ctx = _EnsurePackagesDiscovered(dist, self.project_cfg, self.setuptools_cfg)
+        with ctx as ensure_discovered:
+            package_dir = ensure_discovered.package_dir
+            self._expand_data_files()
+            self._expand_cmdclass(package_dir)
+            self._expand_all_dynamic(dist, package_dir)
+
+        dist._referenced_files.update(self._referenced_files)
+        return self.config
+
+    def _expand_packages(self):
+        packages = self.setuptools_cfg.get("packages")
+        if packages is None or isinstance(packages, (list, tuple)):
+            return
+
+        find = packages.get("find")
+        if isinstance(find, dict):
+            find["root_dir"] = self.root_dir
+            find["fill_package_dir"] = self.setuptools_cfg.setdefault("package-dir", {})
+            with _ignore_errors(self.ignore_option_errors):
+                self.setuptools_cfg["packages"] = _expand.find_packages(**find)
+
+    def _expand_data_files(self):
+        data_files = partial(_expand.canonic_data_files, root_dir=self.root_dir)
+        self._process_field(self.setuptools_cfg, "data-files", data_files)
+
+    def _expand_cmdclass(self, package_dir: Mapping[str, str]):
+        root_dir = self.root_dir
+        cmdclass = partial(_expand.cmdclass, package_dir=package_dir, root_dir=root_dir)
+        self._process_field(self.setuptools_cfg, "cmdclass", cmdclass)
+
+    def _expand_all_dynamic(self, dist: Distribution, package_dir: Mapping[str, str]):
+        special = (  # need special handling
+            "version",
+            "readme",
+            "entry-points",
+            "scripts",
+            "gui-scripts",
+            "classifiers",
+            "dependencies",
+            "optional-dependencies",
+        )
+        # `_obtain` functions are assumed to raise appropriate exceptions/warnings.
+        obtained_dynamic = {
+            field: self._obtain(dist, field, package_dir)
+            for field in self.dynamic
+            if field not in special
+        }
+        obtained_dynamic.update(
+            self._obtain_entry_points(dist, package_dir) or {},
+            version=self._obtain_version(dist, package_dir),
+            readme=self._obtain_readme(dist),
+            classifiers=self._obtain_classifiers(dist),
+            dependencies=self._obtain_dependencies(dist),
+            optional_dependencies=self._obtain_optional_dependencies(dist),
+        )
+        # `None` indicates there is nothing in `tool.setuptools.dynamic` but the value
+        # might have already been set by setup.py/extensions, so avoid overwriting.
+        updates = {k: v for k, v in obtained_dynamic.items() if v is not None}
+        self.project_cfg.update(updates)
+
+    def _ensure_previously_set(self, dist: Distribution, field: str):
+        previous = _PREVIOUSLY_DEFINED[field](dist)
+        if previous is None and not self.ignore_option_errors:
+            msg = (
+                f"No configuration found for dynamic {field!r}.\n"
+                "Some dynamic fields need to be specified via `tool.setuptools.dynamic`"
+                "\nothers must be specified via the equivalent attribute in `setup.py`."
+            )
+            raise InvalidConfigError(msg)
+
+    def _expand_directive(
+        self, specifier: str, directive, package_dir: Mapping[str, str]
+    ):
+        from more_itertools import always_iterable
+
+        with _ignore_errors(self.ignore_option_errors):
+            root_dir = self.root_dir
+            if "file" in directive:
+                self._referenced_files.update(always_iterable(directive["file"]))
+                return _expand.read_files(directive["file"], root_dir)
+            if "attr" in directive:
+                return _expand.read_attr(directive["attr"], package_dir, root_dir)
+            raise ValueError(f"invalid `{specifier}`: {directive!r}")
+        return None
+
+    def _obtain(self, dist: Distribution, field: str, package_dir: Mapping[str, str]):
+        if field in self.dynamic_cfg:
+            return self._expand_directive(
+                f"tool.setuptools.dynamic.{field}",
+                self.dynamic_cfg[field],
+                package_dir,
+            )
+        self._ensure_previously_set(dist, field)
+        return None
+
+    def _obtain_version(self, dist: Distribution, package_dir: Mapping[str, str]):
+        # Since plugins can set version, let's silently skip if it cannot be obtained
+        if "version" in self.dynamic and "version" in self.dynamic_cfg:
+            return _expand.version(
+                # We already do an early check for the presence of "version"
+                self._obtain(dist, "version", package_dir)  # pyright: ignore[reportArgumentType]
+            )
+        return None
+
+    def _obtain_readme(self, dist: Distribution) -> dict[str, str] | None:
+        if "readme" not in self.dynamic:
+            return None
+
+        dynamic_cfg = self.dynamic_cfg
+        if "readme" in dynamic_cfg:
+            return {
+                # We already do an early check for the presence of "readme"
+                "text": self._obtain(dist, "readme", {}),
+                "content-type": dynamic_cfg["readme"].get("content-type", "text/x-rst"),
+            }  # pyright: ignore[reportReturnType]
+
+        self._ensure_previously_set(dist, "readme")
+        return None
+
+    def _obtain_entry_points(
+        self, dist: Distribution, package_dir: Mapping[str, str]
+    ) -> dict[str, dict[str, Any]] | None:
+        fields = ("entry-points", "scripts", "gui-scripts")
+        if not any(field in self.dynamic for field in fields):
+            return None
+
+        text = self._obtain(dist, "entry-points", package_dir)
+        if text is None:
+            return None
+
+        groups = _expand.entry_points(text)
+        # Any is str | dict[str, str], but causes variance issues
+        expanded: dict[str, dict[str, Any]] = {"entry-points": groups}
+
+        def _set_scripts(field: str, group: str):
+            if group in groups:
+                value = groups.pop(group)
+                if field not in self.dynamic:
+                    raise InvalidConfigError(_MissingDynamic.details(field, value))
+                expanded[field] = value
+
+        _set_scripts("scripts", "console_scripts")
+        _set_scripts("gui-scripts", "gui_scripts")
+
+        return expanded
+
+    def _obtain_classifiers(self, dist: Distribution):
+        if "classifiers" in self.dynamic:
+            value = self._obtain(dist, "classifiers", {})
+            if value:
+                return value.splitlines()
+        return None
+
+    def _obtain_dependencies(self, dist: Distribution):
+        if "dependencies" in self.dynamic:
+            value = self._obtain(dist, "dependencies", {})
+            if value:
+                return _parse_requirements_list(value)
+        return None
+
+    def _obtain_optional_dependencies(self, dist: Distribution):
+        if "optional-dependencies" not in self.dynamic:
+            return None
+        if "optional-dependencies" in self.dynamic_cfg:
+            optional_dependencies_map = self.dynamic_cfg["optional-dependencies"]
+            assert isinstance(optional_dependencies_map, dict)
+            return {
+                group: _parse_requirements_list(
+                    self._expand_directive(
+                        f"tool.setuptools.dynamic.optional-dependencies.{group}",
+                        directive,
+                        {},
+                    )
+                )
+                for group, directive in optional_dependencies_map.items()
+            }
+        self._ensure_previously_set(dist, "optional-dependencies")
+        return None
+
+
+def _parse_requirements_list(value):
+    return [
+        line
+        for line in value.splitlines()
+        if line.strip() and not line.strip().startswith("#")
+    ]
+
+
+@contextmanager
+def _ignore_errors(ignore_option_errors: bool):
+    if not ignore_option_errors:
+        yield
+        return
+
+    try:
+        yield
+    except Exception as ex:
+        _logger.debug(f"ignored error: {ex.__class__.__name__} - {ex}")
+
+
+class _EnsurePackagesDiscovered(_expand.EnsurePackagesDiscovered):
+    def __init__(
+        self, distribution: Distribution, project_cfg: dict, setuptools_cfg: dict
+    ) -> None:
+        super().__init__(distribution)
+        self._project_cfg = project_cfg
+        self._setuptools_cfg = setuptools_cfg
+
+    def __enter__(self) -> Self:
+        """When entering the context, the values of ``packages``, ``py_modules`` and
+        ``package_dir`` that are missing in ``dist`` are copied from ``setuptools_cfg``.
+        """
+        dist, cfg = self._dist, self._setuptools_cfg
+        package_dir: dict[str, str] = cfg.setdefault("package-dir", {})
+        package_dir.update(dist.package_dir or {})
+        dist.package_dir = package_dir  # needs to be the same object
+
+        dist.set_defaults._ignore_ext_modules()  # pyproject.toml-specific behaviour
+
+        # Set `name`, `py_modules` and `packages` in dist to short-circuit
+        # auto-discovery, but avoid overwriting empty lists purposefully set by users.
+        if dist.metadata.name is None:
+            dist.metadata.name = self._project_cfg.get("name")
+        if dist.py_modules is None:
+            dist.py_modules = cfg.get("py-modules")
+        if dist.packages is None:
+            dist.packages = cfg.get("packages")
+
+        return super().__enter__()
+
+    def __exit__(
+        self,
+        exc_type: type[BaseException] | None,
+        exc_value: BaseException | None,
+        traceback: TracebackType | None,
+    ) -> None:
+        """When exiting the context, if values of ``packages``, ``py_modules`` and
+        ``package_dir`` are missing in ``setuptools_cfg``, copy from ``dist``.
+        """
+        # If anything was discovered set them back, so they count in the final config.
+        self._setuptools_cfg.setdefault("packages", self._dist.packages)
+        self._setuptools_cfg.setdefault("py-modules", self._dist.py_modules)
+        return super().__exit__(exc_type, exc_value, traceback)
+
+
+class _ExperimentalConfiguration(SetuptoolsWarning):
+    _SUMMARY = (
+        "`{subject}` in `pyproject.toml` is still *experimental* "
+        "and likely to change in future releases."
+    )
+
+
+class _ToolsTypoInMetadata(SetuptoolsWarning):
+    _SUMMARY = (
+        "Ignoring [tools.setuptools] in pyproject.toml, did you mean [tool.setuptools]?"
+    )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/setupcfg.py b/.venv/lib/python3.12/site-packages/setuptools/config/setupcfg.py
new file mode 100644
index 0000000..121a0fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/setupcfg.py
@@ -0,0 +1,782 @@
+"""
+Load setuptools configuration from ``setup.cfg`` files.
+
+**API will be made private in the future**
+
+To read project metadata, consider using
+``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
+For simple scenarios, you can also try parsing the file directly
+with the help of ``configparser``.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import functools
+import os
+from abc import abstractmethod
+from collections import defaultdict
+from collections.abc import Iterable, Iterator
+from functools import partial, wraps
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar, cast
+
+from packaging.markers import default_environment as marker_env
+from packaging.requirements import InvalidRequirement, Requirement
+from packaging.version import InvalidVersion, Version
+
+from .. import _static
+from .._path import StrPath
+from ..errors import FileError, OptionError
+from ..warnings import SetuptoolsDeprecationWarning
+from . import expand
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+    from setuptools.dist import Distribution
+
+    from distutils.dist import DistributionMetadata
+
+SingleCommandOptions: TypeAlias = dict[str, tuple[str, Any]]
+"""Dict that associate the name of the options of a particular command to a
+tuple. The first element of the tuple indicates the origin of the option value
+(e.g. the name of the configuration file where it was read from),
+while the second element of the tuple is the option value itself
+"""
+AllCommandOptions: TypeAlias = dict[str, SingleCommandOptions]
+"""cmd name => its options"""
+Target = TypeVar("Target", "Distribution", "DistributionMetadata")
+
+
+def read_configuration(
+    filepath: StrPath, find_others: bool = False, ignore_option_errors: bool = False
+) -> dict:
+    """Read given configuration file and returns options from it as a dict.
+
+    :param str|unicode filepath: Path to configuration file
+        to get options from.
+
+    :param bool find_others: Whether to search for other configuration files
+        which could be on in various places.
+
+    :param bool ignore_option_errors: Whether to silently ignore
+        options, values of which could not be resolved (e.g. due to exceptions
+        in directives such as file:, attr:, etc.).
+        If False exceptions are propagated as expected.
+
+    :rtype: dict
+    """
+    from setuptools.dist import Distribution
+
+    dist = Distribution()
+    filenames = dist.find_config_files() if find_others else []
+    handlers = _apply(dist, filepath, filenames, ignore_option_errors)
+    return configuration_to_dict(handlers)
+
+
+def apply_configuration(dist: Distribution, filepath: StrPath) -> Distribution:
+    """Apply the configuration from a ``setup.cfg`` file into an existing
+    distribution object.
+    """
+    _apply(dist, filepath)
+    dist._finalize_requires()
+    return dist
+
+
+def _apply(
+    dist: Distribution,
+    filepath: StrPath,
+    other_files: Iterable[StrPath] = (),
+    ignore_option_errors: bool = False,
+) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
+    """Read configuration from ``filepath`` and applies to the ``dist`` object."""
+    from setuptools.dist import _Distribution
+
+    filepath = os.path.abspath(filepath)
+
+    if not os.path.isfile(filepath):
+        raise FileError(f'Configuration file {filepath} does not exist.')
+
+    current_directory = os.getcwd()
+    os.chdir(os.path.dirname(filepath))
+    filenames = [*other_files, filepath]
+
+    try:
+        # TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed
+        _Distribution.parse_config_files(dist, filenames=cast(list[str], filenames))
+        handlers = parse_configuration(
+            dist, dist.command_options, ignore_option_errors=ignore_option_errors
+        )
+        dist._finalize_license_files()
+    finally:
+        os.chdir(current_directory)
+
+    return handlers
+
+
+def _get_option(target_obj: Distribution | DistributionMetadata, key: str):
+    """
+    Given a target object and option key, get that option from
+    the target object, either through a get_{key} method or
+    from an attribute directly.
+    """
+    getter_name = f'get_{key}'
+    by_attribute = functools.partial(getattr, target_obj, key)
+    getter = getattr(target_obj, getter_name, by_attribute)
+    return getter()
+
+
+def configuration_to_dict(
+    handlers: Iterable[
+        ConfigHandler[Distribution] | ConfigHandler[DistributionMetadata]
+    ],
+) -> dict:
+    """Returns configuration data gathered by given handlers as a dict.
+
+    :param Iterable[ConfigHandler] handlers: Handlers list,
+        usually from parse_configuration()
+
+    :rtype: dict
+    """
+    config_dict: dict = defaultdict(dict)
+
+    for handler in handlers:
+        for option in handler.set_options:
+            value = _get_option(handler.target_obj, option)
+            config_dict[handler.section_prefix][option] = value
+
+    return config_dict
+
+
+def parse_configuration(
+    distribution: Distribution,
+    command_options: AllCommandOptions,
+    ignore_option_errors: bool = False,
+) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
+    """Performs additional parsing of configuration options
+    for a distribution.
+
+    Returns a list of used option handlers.
+
+    :param Distribution distribution:
+    :param dict command_options:
+    :param bool ignore_option_errors: Whether to silently ignore
+        options, values of which could not be resolved (e.g. due to exceptions
+        in directives such as file:, attr:, etc.).
+        If False exceptions are propagated as expected.
+    :rtype: list
+    """
+    with expand.EnsurePackagesDiscovered(distribution) as ensure_discovered:
+        options = ConfigOptionsHandler(
+            distribution,
+            command_options,
+            ignore_option_errors,
+            ensure_discovered,
+        )
+
+        options.parse()
+        if not distribution.package_dir:
+            distribution.package_dir = options.package_dir  # Filled by `find_packages`
+
+        meta = ConfigMetadataHandler(
+            distribution.metadata,
+            command_options,
+            ignore_option_errors,
+            ensure_discovered,
+            distribution.package_dir,
+            distribution.src_root,
+        )
+        meta.parse()
+        distribution._referenced_files.update(
+            options._referenced_files, meta._referenced_files
+        )
+
+    return meta, options
+
+
+def _warn_accidental_env_marker_misconfig(label: str, orig_value: str, parsed: list):
+    """Because users sometimes misinterpret this configuration:
+
+    [options.extras_require]
+    foo = bar;python_version<"4"
+
+    It looks like one requirement with an environment marker
+    but because there is no newline, it's parsed as two requirements
+    with a semicolon as separator.
+
+    Therefore, if:
+        * input string does not contain a newline AND
+        * parsed result contains two requirements AND
+        * parsing of the two parts from the result (";")
+        leads in a valid Requirement with a valid marker
+    a UserWarning is shown to inform the user about the possible problem.
+    """
+    if "\n" in orig_value or len(parsed) != 2:
+        return
+
+    markers = marker_env().keys()
+
+    try:
+        req = Requirement(parsed[1])
+        if req.name in markers:
+            _AmbiguousMarker.emit(field=label, req=parsed[1])
+    except InvalidRequirement as ex:
+        if any(parsed[1].startswith(marker) for marker in markers):
+            msg = _AmbiguousMarker.message(field=label, req=parsed[1])
+            raise InvalidRequirement(msg) from ex
+
+
+class ConfigHandler(Generic[Target]):
+    """Handles metadata supplied in configuration files."""
+
+    section_prefix: str
+    """Prefix for config sections handled by this handler.
+    Must be provided by class heirs.
+
+    """
+
+    aliases: ClassVar[dict[str, str]] = {}
+    """Options aliases.
+    For compatibility with various packages. E.g.: d2to1 and pbr.
+    Note: `-` in keys is replaced with `_` by config parser.
+
+    """
+
+    def __init__(
+        self,
+        target_obj: Target,
+        options: AllCommandOptions,
+        ignore_option_errors,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+    ) -> None:
+        self.ignore_option_errors = ignore_option_errors
+        self.target_obj: Target = target_obj
+        self.sections = dict(self._section_options(options))
+        self.set_options: list[str] = []
+        self.ensure_discovered = ensure_discovered
+        self._referenced_files = set[str]()
+        """After parsing configurations, this property will enumerate
+        all files referenced by the "file:" directive. Private API for setuptools only.
+        """
+
+    @classmethod
+    def _section_options(
+        cls, options: AllCommandOptions
+    ) -> Iterator[tuple[str, SingleCommandOptions]]:
+        for full_name, value in options.items():
+            pre, _sep, name = full_name.partition(cls.section_prefix)
+            if pre:
+                continue
+            yield name.lstrip('.'), value
+
+    @property
+    @abstractmethod
+    def parsers(self) -> dict[str, Callable]:
+        """Metadata item name to parser function mapping."""
+        raise NotImplementedError(
+            f'{self.__class__.__name__} must provide .parsers property'
+        )
+
+    def __setitem__(self, option_name, value) -> None:
+        target_obj = self.target_obj
+
+        # Translate alias into real name.
+        option_name = self.aliases.get(option_name, option_name)
+
+        try:
+            current_value = getattr(target_obj, option_name)
+        except AttributeError as e:
+            raise KeyError(option_name) from e
+
+        if current_value:
+            # Already inhabited. Skipping.
+            return
+
+        try:
+            parsed = self.parsers.get(option_name, lambda x: x)(value)
+        except (Exception,) * self.ignore_option_errors:
+            return
+
+        simple_setter = functools.partial(target_obj.__setattr__, option_name)
+        setter = getattr(target_obj, f"set_{option_name}", simple_setter)
+        setter(parsed)
+
+        self.set_options.append(option_name)
+
+    @classmethod
+    def _parse_list(cls, value, separator=','):
+        """Represents value as a list.
+
+        Value is split either by separator (defaults to comma) or by lines.
+
+        :param value:
+        :param separator: List items separator character.
+        :rtype: list
+        """
+        if isinstance(value, list):  # _get_parser_compound case
+            return value
+
+        if '\n' in value:
+            value = value.splitlines()
+        else:
+            value = value.split(separator)
+
+        return [chunk.strip() for chunk in value if chunk.strip()]
+
+    @classmethod
+    def _parse_dict(cls, value):
+        """Represents value as a dict.
+
+        :param value:
+        :rtype: dict
+        """
+        separator = '='
+        result = {}
+        for line in cls._parse_list(value):
+            key, sep, val = line.partition(separator)
+            if sep != separator:
+                raise OptionError(f"Unable to parse option value to dict: {value}")
+            result[key.strip()] = val.strip()
+
+        return result
+
+    @classmethod
+    def _parse_bool(cls, value):
+        """Represents value as boolean.
+
+        :param value:
+        :rtype: bool
+        """
+        value = value.lower()
+        return value in ('1', 'true', 'yes')
+
+    @classmethod
+    def _exclude_files_parser(cls, key):
+        """Returns a parser function to make sure field inputs
+        are not files.
+
+        Parses a value after getting the key so error messages are
+        more informative.
+
+        :param key:
+        :rtype: callable
+        """
+
+        def parser(value):
+            exclude_directive = 'file:'
+            if value.startswith(exclude_directive):
+                raise ValueError(
+                    f'Only strings are accepted for the {key} field, '
+                    'files are not accepted'
+                )
+            return _static.Str(value)
+
+        return parser
+
+    def _parse_file(self, value, root_dir: StrPath | None):
+        """Represents value as a string, allowing including text
+        from nearest files using `file:` directive.
+
+        Directive is sandboxed and won't reach anything outside
+        directory with setup.py.
+
+        Examples:
+            file: README.rst, CHANGELOG.md, src/file.txt
+
+        :param str value:
+        :rtype: str
+        """
+        include_directive = 'file:'
+
+        if not isinstance(value, str):
+            return value
+
+        if not value.startswith(include_directive):
+            return _static.Str(value)
+
+        spec = value[len(include_directive) :]
+        filepaths = [path.strip() for path in spec.split(',')]
+        self._referenced_files.update(filepaths)
+        # XXX: Is marking as static contents coming from files too optimistic?
+        return _static.Str(expand.read_files(filepaths, root_dir))
+
+    def _parse_attr(self, value, package_dir, root_dir: StrPath):
+        """Represents value as a module attribute.
+
+        Examples:
+            attr: package.attr
+            attr: package.module.attr
+
+        :param str value:
+        :rtype: str
+        """
+        attr_directive = 'attr:'
+        if not value.startswith(attr_directive):
+            return _static.Str(value)
+
+        attr_desc = value.replace(attr_directive, '')
+
+        # Make sure package_dir is populated correctly, so `attr:` directives can work
+        package_dir.update(self.ensure_discovered.package_dir)
+        return expand.read_attr(attr_desc, package_dir, root_dir)
+
+    @classmethod
+    def _get_parser_compound(cls, *parse_methods):
+        """Returns parser function to represents value as a list.
+
+        Parses a value applying given methods one after another.
+
+        :param parse_methods:
+        :rtype: callable
+        """
+
+        def parse(value):
+            parsed = value
+
+            for method in parse_methods:
+                parsed = method(parsed)
+
+            return parsed
+
+        return parse
+
+    @classmethod
+    def _parse_section_to_dict_with_key(cls, section_options, values_parser):
+        """Parses section options into a dictionary.
+
+        Applies a given parser to each option in a section.
+
+        :param dict section_options:
+        :param callable values_parser: function with 2 args corresponding to key, value
+        :rtype: dict
+        """
+        value = {}
+        for key, (_, val) in section_options.items():
+            value[key] = values_parser(key, val)
+        return value
+
+    @classmethod
+    def _parse_section_to_dict(cls, section_options, values_parser=None):
+        """Parses section options into a dictionary.
+
+        Optionally applies a given parser to each value.
+
+        :param dict section_options:
+        :param callable values_parser: function with 1 arg corresponding to option value
+        :rtype: dict
+        """
+        parser = (lambda _, v: values_parser(v)) if values_parser else (lambda _, v: v)
+        return cls._parse_section_to_dict_with_key(section_options, parser)
+
+    def parse_section(self, section_options) -> None:
+        """Parses configuration file section.
+
+        :param dict section_options:
+        """
+        for name, (_, value) in section_options.items():
+            with contextlib.suppress(KeyError):
+                # Keep silent for a new option may appear anytime.
+                self[name] = value
+
+    def parse(self) -> None:
+        """Parses configuration file items from one
+        or more related sections.
+
+        """
+        for section_name, section_options in self.sections.items():
+            method_postfix = ''
+            if section_name:  # [section.option] variant
+                method_postfix = f"_{section_name}"
+
+            section_parser_method: Callable | None = getattr(
+                self,
+                # Dots in section names are translated into dunderscores.
+                f'parse_section{method_postfix}'.replace('.', '__'),
+                None,
+            )
+
+            if section_parser_method is None:
+                raise OptionError(
+                    "Unsupported distribution option section: "
+                    f"[{self.section_prefix}.{section_name}]"
+                )
+
+            section_parser_method(section_options)
+
+    def _deprecated_config_handler(self, func, msg, **kw):
+        """this function will wrap around parameters that are deprecated
+
+        :param msg: deprecation message
+        :param func: function to be wrapped around
+        """
+
+        @wraps(func)
+        def config_handler(*args, **kwargs):
+            kw.setdefault("stacklevel", 2)
+            _DeprecatedConfig.emit("Deprecated config in `setup.cfg`", msg, **kw)
+            return func(*args, **kwargs)
+
+        return config_handler
+
+
+class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
+    section_prefix = 'metadata'
+
+    aliases = {
+        'home_page': 'url',
+        'summary': 'description',
+        'classifier': 'classifiers',
+        'platform': 'platforms',
+    }
+
+    strict_mode = False
+    """We need to keep it loose, to be partially compatible with
+    `pbr` and `d2to1` packages which also uses `metadata` section.
+
+    """
+
+    def __init__(
+        self,
+        target_obj: DistributionMetadata,
+        options: AllCommandOptions,
+        ignore_option_errors: bool,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+        package_dir: dict | None = None,
+        root_dir: StrPath | None = os.curdir,
+    ) -> None:
+        super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
+        self.package_dir = package_dir
+        self.root_dir = root_dir
+
+    @property
+    def parsers(self) -> dict[str, Callable]:
+        """Metadata item name to parser function mapping."""
+        parse_list_static = self._get_parser_compound(self._parse_list, _static.List)
+        parse_dict_static = self._get_parser_compound(self._parse_dict, _static.Dict)
+        parse_file = partial(self._parse_file, root_dir=self.root_dir)
+        exclude_files_parser = self._exclude_files_parser
+
+        return {
+            'author': _static.Str,
+            'author_email': _static.Str,
+            'maintainer': _static.Str,
+            'maintainer_email': _static.Str,
+            'platforms': parse_list_static,
+            'keywords': parse_list_static,
+            'provides': parse_list_static,
+            'obsoletes': parse_list_static,
+            'classifiers': self._get_parser_compound(parse_file, parse_list_static),
+            'license': exclude_files_parser('license'),
+            'license_files': parse_list_static,
+            'description': parse_file,
+            'long_description': parse_file,
+            'long_description_content_type': _static.Str,
+            'version': self._parse_version,  # Cannot be marked as dynamic
+            'url': _static.Str,
+            'project_urls': parse_dict_static,
+        }
+
+    def _parse_version(self, value):
+        """Parses `version` option value.
+
+        :param value:
+        :rtype: str
+
+        """
+        version = self._parse_file(value, self.root_dir)
+
+        if version != value:
+            version = version.strip()
+            # Be strict about versions loaded from file because it's easy to
+            # accidentally include newlines and other unintended content
+            try:
+                Version(version)
+            except InvalidVersion as e:
+                raise OptionError(
+                    f'Version loaded from {value} does not '
+                    f'comply with PEP 440: {version}'
+                ) from e
+
+            return version
+
+        return expand.version(self._parse_attr(value, self.package_dir, self.root_dir))
+
+
+class ConfigOptionsHandler(ConfigHandler["Distribution"]):
+    section_prefix = 'options'
+
+    def __init__(
+        self,
+        target_obj: Distribution,
+        options: AllCommandOptions,
+        ignore_option_errors: bool,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+    ) -> None:
+        super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
+        self.root_dir = target_obj.src_root
+        self.package_dir: dict[str, str] = {}  # To be filled by `find_packages`
+
+    @classmethod
+    def _parse_list_semicolon(cls, value):
+        return cls._parse_list(value, separator=';')
+
+    def _parse_file_in_root(self, value):
+        return self._parse_file(value, root_dir=self.root_dir)
+
+    def _parse_requirements_list(self, label: str, value: str):
+        # Parse a requirements list, either by reading in a `file:`, or a list.
+        parsed = self._parse_list_semicolon(self._parse_file_in_root(value))
+        _warn_accidental_env_marker_misconfig(label, value, parsed)
+        # Filter it to only include lines that are not comments. `parse_list`
+        # will have stripped each line and filtered out empties.
+        return _static.List(line for line in parsed if not line.startswith("#"))
+        # ^-- Use `_static.List` to mark a non-`Dynamic` Core Metadata
+
+    @property
+    def parsers(self) -> dict[str, Callable]:
+        """Metadata item name to parser function mapping."""
+        parse_list = self._parse_list
+        parse_bool = self._parse_bool
+        parse_cmdclass = self._parse_cmdclass
+
+        return {
+            'zip_safe': parse_bool,
+            'include_package_data': parse_bool,
+            'package_dir': self._parse_dict,
+            'scripts': parse_list,
+            'eager_resources': parse_list,
+            'dependency_links': parse_list,
+            'namespace_packages': self._deprecated_config_handler(
+                parse_list,
+                "The namespace_packages parameter is deprecated, "
+                "consider using implicit namespaces instead (PEP 420).",
+                # TODO: define due date, see setuptools.dist:check_nsp.
+            ),
+            'install_requires': partial(  # Core Metadata
+                self._parse_requirements_list, "install_requires"
+            ),
+            'setup_requires': self._parse_list_semicolon,
+            'packages': self._parse_packages,
+            'entry_points': self._parse_file_in_root,
+            'py_modules': parse_list,
+            'python_requires': _static.SpecifierSet,  # Core Metadata
+            'cmdclass': parse_cmdclass,
+        }
+
+    def _parse_cmdclass(self, value):
+        package_dir = self.ensure_discovered.package_dir
+        return expand.cmdclass(self._parse_dict(value), package_dir, self.root_dir)
+
+    def _parse_packages(self, value):
+        """Parses `packages` option value.
+
+        :param value:
+        :rtype: list
+        """
+        find_directives = ['find:', 'find_namespace:']
+        trimmed_value = value.strip()
+
+        if trimmed_value not in find_directives:
+            return self._parse_list(value)
+
+        # Read function arguments from a dedicated section.
+        find_kwargs = self.parse_section_packages__find(
+            self.sections.get('packages.find', {})
+        )
+
+        find_kwargs.update(
+            namespaces=(trimmed_value == find_directives[1]),
+            root_dir=self.root_dir,
+            fill_package_dir=self.package_dir,
+        )
+
+        return expand.find_packages(**find_kwargs)
+
+    def parse_section_packages__find(self, section_options):
+        """Parses `packages.find` configuration file section.
+
+        To be used in conjunction with _parse_packages().
+
+        :param dict section_options:
+        """
+        section_data = self._parse_section_to_dict(section_options, self._parse_list)
+
+        valid_keys = ['where', 'include', 'exclude']
+        find_kwargs = {k: v for k, v in section_data.items() if k in valid_keys and v}
+
+        where = find_kwargs.get('where')
+        if where is not None:
+            find_kwargs['where'] = where[0]  # cast list to single val
+
+        return find_kwargs
+
+    def parse_section_entry_points(self, section_options) -> None:
+        """Parses `entry_points` configuration file section.
+
+        :param dict section_options:
+        """
+        parsed = self._parse_section_to_dict(section_options, self._parse_list)
+        self['entry_points'] = parsed
+
+    def _parse_package_data(self, section_options):
+        package_data = self._parse_section_to_dict(section_options, self._parse_list)
+        return expand.canonic_package_data(package_data)
+
+    def parse_section_package_data(self, section_options) -> None:
+        """Parses `package_data` configuration file section.
+
+        :param dict section_options:
+        """
+        self['package_data'] = self._parse_package_data(section_options)
+
+    def parse_section_exclude_package_data(self, section_options) -> None:
+        """Parses `exclude_package_data` configuration file section.
+
+        :param dict section_options:
+        """
+        self['exclude_package_data'] = self._parse_package_data(section_options)
+
+    def parse_section_extras_require(self, section_options) -> None:  # Core Metadata
+        """Parses `extras_require` configuration file section.
+
+        :param dict section_options:
+        """
+        parsed = self._parse_section_to_dict_with_key(
+            section_options,
+            lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v),
+        )
+
+        self['extras_require'] = _static.Dict(parsed)
+        # ^-- Use `_static.Dict` to mark a non-`Dynamic` Core Metadata
+
+    def parse_section_data_files(self, section_options) -> None:
+        """Parses `data_files` configuration file section.
+
+        :param dict section_options:
+        """
+        parsed = self._parse_section_to_dict(section_options, self._parse_list)
+        self['data_files'] = expand.canonic_data_files(parsed, self.root_dir)
+
+
+class _AmbiguousMarker(SetuptoolsDeprecationWarning):
+    _SUMMARY = "Ambiguous requirement marker."
+    _DETAILS = """
+    One of the parsed requirements in `{field}` looks like a valid environment marker:
+
+        {req!r}
+
+    Please make sure that the configuration file is correct.
+    You can use dangling lines to avoid this problem.
+    """
+    _SEE_DOCS = "userguide/declarative_config.html#opt-2"
+    # TODO: should we include due_date here? Initially introduced in 6 Aug 2022.
+    # Does this make sense with latest version of packaging?
+
+    @classmethod
+    def message(cls, **kw):
+        docs = f"https://setuptools.pypa.io/en/latest/{cls._SEE_DOCS}"
+        return cls._format(cls._SUMMARY, cls._DETAILS, see_url=docs, format_args=kw)
+
+
+class _DeprecatedConfig(SetuptoolsDeprecationWarning):
+    _SEE_DOCS = "userguide/declarative_config.html"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/config/setuptools.schema.json b/.venv/lib/python3.12/site-packages/setuptools/config/setuptools.schema.json
new file mode 100644
index 0000000..6146abd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/config/setuptools.schema.json
@@ -0,0 +1,433 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+
+  "$id": "https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html",
+  "title": "``tool.setuptools`` table",
+  "$$description": [
+    "``setuptools``-specific configurations that can be set by users that require",
+    "customization.",
+    "These configurations are completely optional and probably can be skipped when",
+    "creating simple packages. They are equivalent to some of the `Keywords",
+    "`_",
+    "used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.",
+    "It considers only ``setuptools`` `parameters",
+    "`_",
+    "that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``",
+    "and ``setup_requires`` (incompatible with modern workflows/standards)."
+  ],
+
+  "type": "object",
+  "additionalProperties": false,
+  "properties": {
+    "platforms": {
+      "type": "array",
+      "items": {"type": "string"}
+    },
+    "provides": {
+      "$$description": [
+        "Package and virtual package names contained within this package",
+        "**(not supported by pip)**"
+      ],
+      "type": "array",
+      "items": {"type": "string", "format": "pep508-identifier"}
+    },
+    "obsoletes": {
+      "$$description": [
+        "Packages which this package renders obsolete",
+        "**(not supported by pip)**"
+      ],
+      "type": "array",
+      "items": {"type": "string", "format": "pep508-identifier"}
+    },
+    "zip-safe": {
+      "$$description": [
+        "Whether the project can be safely installed and run from a zip file.",
+        "**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and",
+        "``setup.py install`` in the context of ``eggs`` (**DEPRECATED**)."
+      ],
+      "type": "boolean"
+    },
+    "script-files": {
+      "$$description": [
+        "Legacy way of defining scripts (entry-points are preferred).",
+        "Equivalent to the ``script`` keyword in ``setup.py``",
+        "(it was renamed to avoid confusion with entry-point based ``project.scripts``",
+        "defined in :pep:`621`).",
+        "**DISCOURAGED**: generic script wrappers are tricky and may not work properly.",
+        "Whenever possible, please use ``project.scripts`` instead."
+      ],
+      "type": "array",
+      "items": {"type": "string"},
+      "$comment": "TODO: is this field deprecated/should be removed?"
+    },
+    "eager-resources": {
+      "$$description": [
+        "Resources that should be extracted together, if any of them is needed,",
+        "or if any C extensions included in the project are imported.",
+        "**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and",
+        "``setup.py install`` in the context of ``eggs`` (**DEPRECATED**)."
+      ],
+      "type": "array",
+      "items": {"type": "string"}
+    },
+    "packages": {
+      "$$description": [
+        "Packages that should be included in the distribution.",
+        "It can be given either as a list of package identifiers",
+        "or as a ``dict``-like structure with a single key ``find``",
+        "which corresponds to a dynamic call to",
+        "``setuptools.config.expand.find_packages`` function.",
+        "The ``find`` key is associated with a nested ``dict``-like structure that can",
+        "contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,",
+        "mimicking the keyword arguments of the associated function."
+      ],
+      "oneOf": [
+        {
+          "title": "Array of Python package identifiers",
+          "type": "array",
+          "items": {"$ref": "#/definitions/package-name"}
+        },
+        {"$ref": "#/definitions/find-directive"}
+      ]
+    },
+    "package-dir": {
+      "$$description": [
+        ":class:`dict`-like structure mapping from package names to directories where their",
+        "code can be found.",
+        "The empty string (as key) means that all packages are contained inside",
+        "the given directory will be included in the distribution."
+      ],
+      "type": "object",
+      "additionalProperties": false,
+      "propertyNames": {
+        "anyOf": [{"const": ""}, {"$ref": "#/definitions/package-name"}]
+      },
+      "patternProperties": {
+        "^.*$": {"type": "string" }
+      }
+    },
+    "package-data": {
+      "$$description": [
+        "Mapping from package names to lists of glob patterns.",
+        "Usually this option is not needed when using ``include-package-data = true``",
+        "For more information on how to include data files, check ``setuptools`` `docs",
+        "`_."
+      ],
+      "type": "object",
+      "additionalProperties": false,
+      "propertyNames": {
+        "anyOf": [{"$ref": "#/definitions/package-name"}, {"const": "*"}]
+      },
+      "patternProperties": {
+        "^.*$": {"type": "array", "items": {"type": "string"}}
+      }
+    },
+    "include-package-data": {
+      "$$description": [
+        "Automatically include any data files inside the package directories",
+        "that are specified by ``MANIFEST.in``",
+        "For more information on how to include data files, check ``setuptools`` `docs",
+        "`_."
+      ],
+      "type": "boolean"
+    },
+    "exclude-package-data": {
+      "$$description": [
+        "Mapping from package names to lists of glob patterns that should be excluded",
+        "For more information on how to include data files, check ``setuptools`` `docs",
+        "`_."
+      ],
+      "type": "object",
+      "additionalProperties": false,
+      "propertyNames": {
+        "anyOf": [{"$ref": "#/definitions/package-name"}, {"const": "*"}]
+      },
+      "patternProperties": {
+          "^.*$": {"type": "array", "items": {"type": "string"}}
+      }
+    },
+    "namespace-packages": {
+      "type": "array",
+      "items": {"type": "string", "format": "python-module-name-relaxed"},
+      "$comment": "https://setuptools.pypa.io/en/latest/userguide/package_discovery.html",
+      "description": "**DEPRECATED**: use implicit namespaces instead (:pep:`420`)."
+    },
+    "py-modules": {
+      "description": "Modules that setuptools will manipulate",
+      "type": "array",
+      "items": {"type": "string", "format": "python-module-name-relaxed"},
+      "$comment": "TODO: clarify the relationship with ``packages``"
+    },
+    "ext-modules": {
+      "description": "Extension modules to be compiled by setuptools",
+      "type": "array",
+      "items": {"$ref": "#/definitions/ext-module"}
+    },
+    "data-files": {
+      "$$description": [
+        "``dict``-like structure where each key represents a directory and",
+        "the value is a list of glob patterns that should be installed in them.",
+        "**DISCOURAGED**: please notice this might not work as expected with wheels.",
+        "Whenever possible, consider using data files inside the package directories",
+        "(or create a new namespace package that only contains data files).",
+        "See `data files support",
+        "`_."
+      ],
+      "type": "object",
+      "patternProperties": {
+          "^.*$": {"type": "array", "items": {"type": "string"}}
+      }
+    },
+    "cmdclass": {
+      "$$description": [
+        "Mapping of distutils-style command names to ``setuptools.Command`` subclasses",
+        "which in turn should be represented by strings with a qualified class name",
+        "(i.e., \"dotted\" form with module), e.g.::\n\n",
+        "    cmdclass = {mycmd = \"pkg.subpkg.module.CommandClass\"}\n\n",
+        "The command class should be a directly defined at the top-level of the",
+        "containing module (no class nesting)."
+      ],
+      "type": "object",
+      "patternProperties": {
+          "^.*$": {"type": "string", "format": "python-qualified-identifier"}
+      }
+    },
+    "license-files": {
+      "type": "array",
+      "items": {"type": "string"},
+      "$$description": [
+        "**PROVISIONAL**: list of glob patterns for all license files being distributed.",
+        "(likely to become standard with :pep:`639`).",
+        "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"
+      ],
+      "$comment": "TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?"
+    },
+    "dynamic": {
+      "type": "object",
+      "description": "Instructions for loading :pep:`621`-related metadata dynamically",
+      "additionalProperties": false,
+      "properties": {
+        "version": {
+          "$$description": [
+            "A version dynamically loaded via either the ``attr:`` or ``file:``",
+            "directives. Please make sure the given file or attribute respects :pep:`440`.",
+            "Also ensure to set ``project.dynamic`` accordingly."
+          ],
+          "oneOf": [
+            {"$ref": "#/definitions/attr-directive"},
+            {"$ref": "#/definitions/file-directive"}
+          ]
+        },
+        "classifiers": {"$ref": "#/definitions/file-directive"},
+        "description": {"$ref": "#/definitions/file-directive"},
+        "entry-points": {"$ref": "#/definitions/file-directive"},
+        "dependencies": {"$ref": "#/definitions/file-directive-for-dependencies"},
+        "optional-dependencies": {
+          "type": "object",
+          "propertyNames": {"type": "string", "format": "pep508-identifier"},
+          "additionalProperties": false,
+          "patternProperties": {
+            ".+": {"$ref": "#/definitions/file-directive-for-dependencies"}
+          }
+        },
+        "readme": {
+          "type": "object",
+          "anyOf": [
+            {"$ref": "#/definitions/file-directive"},
+            {
+              "type": "object",
+              "properties": {
+                "content-type": {"type": "string"},
+                "file": { "$ref": "#/definitions/file-directive/properties/file" }
+              },
+              "additionalProperties": false}
+          ],
+          "required": ["file"]
+        }
+      }
+    }
+  },
+
+  "definitions": {
+    "package-name": {
+      "$id": "#/definitions/package-name",
+      "title": "Valid package name",
+      "description": "Valid package name (importable or :pep:`561`).",
+      "type": "string",
+      "anyOf": [
+        {"type": "string", "format": "python-module-name-relaxed"},
+        {"type": "string", "format": "pep561-stub-name"}
+      ]
+    },
+    "ext-module": {
+      "$id": "#/definitions/ext-module",
+      "title": "Extension module",
+      "description": "Parameters to construct a :class:`setuptools.Extension` object",
+      "type": "object",
+      "required": ["name", "sources"],
+      "additionalProperties": false,
+      "properties": {
+        "name": {
+          "type": "string",
+          "format": "python-module-name-relaxed"
+        },
+        "sources": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "include-dirs":{
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "define-macros": {
+          "type": "array",
+          "items": {
+            "type": "array",
+            "items": [
+              {"description": "macro name", "type": "string"},
+              {"description": "macro value", "oneOf": [{"type": "string"}, {"type": "null"}]}
+            ],
+            "additionalItems": false
+          }
+        },
+        "undef-macros": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "library-dirs": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "libraries": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "runtime-library-dirs": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "extra-objects": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "extra-compile-args": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "extra-link-args": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "export-symbols": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "swig-opts": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "depends": {
+          "type": "array",
+          "items": {"type": "string"}
+        },
+        "language": {"type": "string"},
+        "optional": {"type": "boolean"},
+        "py-limited-api": {"type": "boolean"}
+      }
+    },
+    "file-directive": {
+      "$id": "#/definitions/file-directive",
+      "title": "'file:' directive",
+      "description":
+        "Value is read from a file (or list of files and then concatenated)",
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "file": {
+          "oneOf": [
+            {"type": "string"},
+            {"type": "array", "items": {"type": "string"}}
+          ]
+        }
+      },
+      "required": ["file"]
+    },
+    "file-directive-for-dependencies": {
+      "title": "'file:' directive for dependencies",
+      "allOf": [
+        {
+          "$$description": [
+            "**BETA**: subset of the ``requirements.txt`` format",
+            "without ``pip`` flags and options",
+            "(one :pep:`508`-compliant string per line,",
+            "lines that are blank or start with ``#`` are excluded).",
+            "See `dynamic metadata",
+            "`_."
+          ]
+        },
+        {"$ref": "#/definitions/file-directive"}
+      ]
+    },
+    "attr-directive": {
+      "title": "'attr:' directive",
+      "$id": "#/definitions/attr-directive",
+      "$$description": [
+        "Value is read from a module attribute. Supports callables and iterables;",
+        "unsupported types are cast via ``str()``"
+      ],
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "attr": {"type": "string", "format": "python-qualified-identifier"}
+      },
+      "required": ["attr"]
+    },
+    "find-directive": {
+      "$id": "#/definitions/find-directive",
+      "title": "'find:' directive",
+      "type": "object",
+      "additionalProperties": false,
+      "properties": {
+        "find": {
+          "type": "object",
+          "$$description": [
+            "Dynamic `package discovery",
+            "`_."
+          ],
+          "additionalProperties": false,
+          "properties": {
+            "where": {
+              "description":
+                "Directories to be searched for packages (Unix-style relative path)",
+              "type": "array",
+              "items": {"type": "string"}
+            },
+            "exclude": {
+              "type": "array",
+              "$$description": [
+                "Exclude packages that match the values listed in this field.",
+                "Can container shell-style wildcards (e.g. ``'pkg.*'``)"
+              ],
+              "items": {"type": "string"}
+            },
+            "include": {
+              "type": "array",
+              "$$description": [
+                "Restrict the found packages to just the ones listed in this field.",
+                "Can container shell-style wildcards (e.g. ``'pkg.*'``)"
+              ],
+              "items": {"type": "string"}
+            },
+            "namespaces": {
+              "type": "boolean",
+              "$$description": [
+                "When ``True``, directories without a ``__init__.py`` file will also",
+                "be scanned for :pep:`420`-style implicit namespaces"
+              ]
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/depends.py b/.venv/lib/python3.12/site-packages/setuptools/depends.py
new file mode 100644
index 0000000..e5223b7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/depends.py
@@ -0,0 +1,185 @@
+from __future__ import annotations
+
+import contextlib
+import dis
+import marshal
+import sys
+from types import CodeType
+from typing import Any, Literal, TypeVar
+
+from packaging.version import Version
+
+from . import _imp
+from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module
+
+_T = TypeVar("_T")
+
+__all__ = ['Require', 'find_module']
+
+
+class Require:
+    """A prerequisite to building or installing a distribution"""
+
+    def __init__(
+        self,
+        name,
+        requested_version,
+        module,
+        homepage: str = '',
+        attribute=None,
+        format=None,
+    ) -> None:
+        if format is None and requested_version is not None:
+            format = Version
+
+        if format is not None:
+            requested_version = format(requested_version)
+            if attribute is None:
+                attribute = '__version__'
+
+        self.__dict__.update(locals())
+        del self.self
+
+    def full_name(self):
+        """Return full package/distribution name, w/version"""
+        if self.requested_version is not None:
+            return f'{self.name}-{self.requested_version}'
+        return self.name
+
+    def version_ok(self, version):
+        """Is 'version' sufficiently up-to-date?"""
+        return (
+            self.attribute is None
+            or self.format is None
+            or str(version) != "unknown"
+            and self.format(version) >= self.requested_version
+        )
+
+    def get_version(
+        self, paths=None, default: _T | Literal["unknown"] = "unknown"
+    ) -> _T | Literal["unknown"] | None | Any:
+        """Get version number of installed module, 'None', or 'default'
+
+        Search 'paths' for module.  If not found, return 'None'.  If found,
+        return the extracted version attribute, or 'default' if no version
+        attribute was specified, or the value cannot be determined without
+        importing the module.  The version is formatted according to the
+        requirement's version format (if any), unless it is 'None' or the
+        supplied 'default'.
+        """
+
+        if self.attribute is None:
+            try:
+                f, _p, _i = find_module(self.module, paths)
+            except ImportError:
+                return None
+            if f:
+                f.close()
+            return default
+
+        v = get_module_constant(self.module, self.attribute, default, paths)
+
+        if v is not None and v is not default and self.format is not None:
+            return self.format(v)
+
+        return v
+
+    def is_present(self, paths=None):
+        """Return true if dependency is present on 'paths'"""
+        return self.get_version(paths) is not None
+
+    def is_current(self, paths=None):
+        """Return true if dependency is present and up-to-date on 'paths'"""
+        version = self.get_version(paths)
+        if version is None:
+            return False
+        return self.version_ok(str(version))
+
+
+def maybe_close(f):
+    @contextlib.contextmanager
+    def empty():
+        yield
+        return
+
+    if not f:
+        return empty()
+
+    return contextlib.closing(f)
+
+
+# Some objects are not available on some platforms.
+# XXX it'd be better to test assertions about bytecode instead.
+if not sys.platform.startswith('java') and sys.platform != 'cli':
+
+    def get_module_constant(
+        module, symbol, default: _T | int = -1, paths=None
+    ) -> _T | int | None | Any:
+        """Find 'module' by searching 'paths', and extract 'symbol'
+
+        Return 'None' if 'module' does not exist on 'paths', or it does not define
+        'symbol'.  If the module defines 'symbol' as a constant, return the
+        constant.  Otherwise, return 'default'."""
+
+        try:
+            f, path, (_suffix, _mode, kind) = info = find_module(module, paths)
+        except ImportError:
+            # Module doesn't exist
+            return None
+
+        with maybe_close(f):
+            if kind == PY_COMPILED:
+                f.read(8)  # skip magic & date
+                code = marshal.load(f)
+            elif kind == PY_FROZEN:
+                code = _imp.get_frozen_object(module, paths)
+            elif kind == PY_SOURCE:
+                code = compile(f.read(), path, 'exec')
+            else:
+                # Not something we can parse; we'll have to import it.  :(
+                imported = _imp.get_module(module, paths, info)
+                return getattr(imported, symbol, None)
+
+        return extract_constant(code, symbol, default)
+
+    def extract_constant(
+        code: CodeType, symbol: str, default: _T | int = -1
+    ) -> _T | int | None | Any:
+        """Extract the constant value of 'symbol' from 'code'
+
+        If the name 'symbol' is bound to a constant value by the Python code
+        object 'code', return that value.  If 'symbol' is bound to an expression,
+        return 'default'.  Otherwise, return 'None'.
+
+        Return value is based on the first assignment to 'symbol'.  'symbol' must
+        be a global, or at least a non-"fast" local in the code block.  That is,
+        only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
+        must be present in 'code.co_names'.
+        """
+        if symbol not in code.co_names:
+            # name's not there, can't possibly be an assignment
+            return None
+
+        name_idx = list(code.co_names).index(symbol)
+
+        STORE_NAME = dis.opmap['STORE_NAME']
+        STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
+        LOAD_CONST = dis.opmap['LOAD_CONST']
+
+        const = default
+
+        for byte_code in dis.Bytecode(code):
+            op = byte_code.opcode
+            arg = byte_code.arg
+
+            if op == LOAD_CONST:
+                assert arg is not None
+                const = code.co_consts[arg]
+            elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
+                return const
+            else:
+                const = default
+
+        return None
+
+    __all__ += ['get_module_constant', 'extract_constant']
diff --git a/.venv/lib/python3.12/site-packages/setuptools/discovery.py b/.venv/lib/python3.12/site-packages/setuptools/discovery.py
new file mode 100644
index 0000000..296d319
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/discovery.py
@@ -0,0 +1,614 @@
+"""Automatic discovery of Python modules and packages (for inclusion in the
+distribution) and other config values.
+
+For the purposes of this module, the following nomenclature is used:
+
+- "src-layout": a directory representing a Python project that contains a "src"
+  folder. Everything under the "src" folder is meant to be included in the
+  distribution when packaging the project. Example::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── src/
+        └── mypkg/
+            ├── __init__.py
+            ├── mymodule.py
+            └── my_data_file.txt
+
+- "flat-layout": a Python project that does not use "src-layout" but instead
+  have a directory under the project root for each package::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mypkg/
+        ├── __init__.py
+        ├── mymodule.py
+        └── my_data_file.txt
+
+- "single-module": a project that contains a single Python script direct under
+  the project root (no directory used)::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mymodule.py
+
+"""
+
+from __future__ import annotations
+
+import itertools
+import os
+from collections.abc import Iterable, Iterator, Mapping
+from fnmatch import fnmatchcase
+from glob import glob
+from pathlib import Path
+from typing import TYPE_CHECKING, ClassVar
+
+import _distutils_hack.override  # noqa: F401
+
+from ._path import StrPath
+
+from distutils import log
+from distutils.util import convert_path
+
+if TYPE_CHECKING:
+    from setuptools import Distribution
+
+chain_iter = itertools.chain.from_iterable
+
+
+def _valid_name(path: StrPath) -> bool:
+    # Ignore invalid names that cannot be imported directly
+    return os.path.basename(path).isidentifier()
+
+
+class _Filter:
+    """
+    Given a list of patterns, create a callable that will be true only if
+    the input matches at least one of the patterns.
+    """
+
+    def __init__(self, *patterns: str) -> None:
+        self._patterns = dict.fromkeys(patterns)
+
+    def __call__(self, item: str) -> bool:
+        return any(fnmatchcase(item, pat) for pat in self._patterns)
+
+    def __contains__(self, item: str) -> bool:
+        return item in self._patterns
+
+
+class _Finder:
+    """Base class that exposes functionality for module/package finders"""
+
+    ALWAYS_EXCLUDE: ClassVar[tuple[str, ...]] = ()
+    DEFAULT_EXCLUDE: ClassVar[tuple[str, ...]] = ()
+
+    @classmethod
+    def find(
+        cls,
+        where: StrPath = '.',
+        exclude: Iterable[str] = (),
+        include: Iterable[str] = ('*',),
+    ) -> list[str]:
+        """Return a list of all Python items (packages or modules, depending on
+        the finder implementation) found within directory ``where``.
+
+        ``where`` is the root directory which will be searched.
+        It should be supplied as a "cross-platform" (i.e. URL-style) path;
+        it will be converted to the appropriate local path syntax.
+
+        ``exclude`` is a sequence of names to exclude; ``*`` can be used
+        as a wildcard in the names.
+        When finding packages, ``foo.*`` will exclude all subpackages of ``foo``
+        (but not ``foo`` itself).
+
+        ``include`` is a sequence of names to include.
+        If it's specified, only the named items will be included.
+        If it's not specified, all found items will be included.
+        ``include`` can contain shell style wildcard patterns just like
+        ``exclude``.
+        """
+
+        exclude = exclude or cls.DEFAULT_EXCLUDE
+        return list(
+            cls._find_iter(
+                convert_path(str(where)),
+                _Filter(*cls.ALWAYS_EXCLUDE, *exclude),
+                _Filter(*include),
+            )
+        )
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        raise NotImplementedError
+
+
+class PackageFinder(_Finder):
+    """
+    Generate a list of all Python packages found within a directory
+    """
+
+    ALWAYS_EXCLUDE = ("ez_setup", "*__pycache__")
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        """
+        All the packages found in 'where' that pass the 'include' filter, but
+        not the 'exclude' filter.
+        """
+        for root, dirs, files in os.walk(str(where), followlinks=True):
+            # Copy dirs to iterate over it, then empty dirs.
+            all_dirs = dirs[:]
+            dirs[:] = []
+
+            for dir in all_dirs:
+                full_path = os.path.join(root, dir)
+                rel_path = os.path.relpath(full_path, where)
+                package = rel_path.replace(os.path.sep, '.')
+
+                # Skip directory trees that are not valid packages
+                if '.' in dir or not cls._looks_like_package(full_path, package):
+                    continue
+
+                # Should this package be included?
+                if include(package) and not exclude(package):
+                    yield package
+
+                # Early pruning if there is nothing else to be scanned
+                if f"{package}*" in exclude or f"{package}.*" in exclude:
+                    continue
+
+                # Keep searching subdirectories, as there may be more packages
+                # down there, even if the parent was excluded.
+                dirs.append(dir)
+
+    @staticmethod
+    def _looks_like_package(path: StrPath, _package_name: str) -> bool:
+        """Does a directory look like a package?"""
+        return os.path.isfile(os.path.join(path, '__init__.py'))
+
+
+class PEP420PackageFinder(PackageFinder):
+    @staticmethod
+    def _looks_like_package(_path: StrPath, _package_name: str) -> bool:
+        return True
+
+
+class ModuleFinder(_Finder):
+    """Find isolated Python modules.
+    This function will **not** recurse subdirectories.
+    """
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        for file in glob(os.path.join(where, "*.py")):
+            module, _ext = os.path.splitext(os.path.basename(file))
+
+            if not cls._looks_like_module(module):
+                continue
+
+            if include(module) and not exclude(module):
+                yield module
+
+    _looks_like_module = staticmethod(_valid_name)
+
+
+# We have to be extra careful in the case of flat layout to not include files
+# and directories not meant for distribution (e.g. tool-related)
+
+
+class FlatLayoutPackageFinder(PEP420PackageFinder):
+    _EXCLUDE = (
+        "ci",
+        "bin",
+        "debian",
+        "doc",
+        "docs",
+        "documentation",
+        "manpages",
+        "news",
+        "newsfragments",
+        "changelog",
+        "test",
+        "tests",
+        "unit_test",
+        "unit_tests",
+        "example",
+        "examples",
+        "scripts",
+        "tools",
+        "util",
+        "utils",
+        "python",
+        "build",
+        "dist",
+        "venv",
+        "env",
+        "requirements",
+        # ---- Task runners / Build tools ----
+        "tasks",  # invoke
+        "fabfile",  # fabric
+        "site_scons",  # SCons
+        # ---- Other tools ----
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        "htmlcov",  # Coverage.py
+        # ---- Hidden directories/Private packages ----
+        "[._]*",
+    )
+
+    DEFAULT_EXCLUDE = tuple(chain_iter((p, f"{p}.*") for p in _EXCLUDE))
+    """Reserved package names"""
+
+    @staticmethod
+    def _looks_like_package(_path: StrPath, package_name: str) -> bool:
+        names = package_name.split('.')
+        # Consider PEP 561
+        root_pkg_is_valid = names[0].isidentifier() or names[0].endswith("-stubs")
+        return root_pkg_is_valid and all(name.isidentifier() for name in names[1:])
+
+
+class FlatLayoutModuleFinder(ModuleFinder):
+    DEFAULT_EXCLUDE = (
+        "setup",
+        "conftest",
+        "test",
+        "tests",
+        "example",
+        "examples",
+        "build",
+        # ---- Task runners ----
+        "toxfile",
+        "noxfile",
+        "pavement",
+        "dodo",
+        "tasks",
+        "fabfile",
+        # ---- Other tools ----
+        "[Ss][Cc]onstruct",  # SCons
+        "conanfile",  # Connan: C/C++ build tool
+        "manage",  # Django
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        # ---- Hidden files/Private modules ----
+        "[._]*",
+    )
+    """Reserved top-level module names"""
+
+
+def _find_packages_within(root_pkg: str, pkg_dir: StrPath) -> list[str]:
+    nested = PEP420PackageFinder.find(pkg_dir)
+    return [root_pkg] + [".".join((root_pkg, n)) for n in nested]
+
+
+class ConfigDiscovery:
+    """Fill-in metadata and options that can be automatically derived
+    (from other metadata/options, the file system or conventions)
+    """
+
+    def __init__(self, distribution: Distribution) -> None:
+        self.dist = distribution
+        self._called = False
+        self._disabled = False
+        self._skip_ext_modules = False
+
+    def _disable(self):
+        """Internal API to disable automatic discovery"""
+        self._disabled = True
+
+    def _ignore_ext_modules(self):
+        """Internal API to disregard ext_modules.
+
+        Normally auto-discovery would not be triggered if ``ext_modules`` are set
+        (this is done for backward compatibility with existing packages relying on
+        ``setup.py`` or ``setup.cfg``). However, ``setuptools`` can call this function
+        to ignore given ``ext_modules`` and proceed with the auto-discovery if
+        ``packages`` and ``py_modules`` are not given (e.g. when using pyproject.toml
+        metadata).
+        """
+        self._skip_ext_modules = True
+
+    @property
+    def _root_dir(self) -> StrPath:
+        # The best is to wait until `src_root` is set in dist, before using _root_dir.
+        return self.dist.src_root or os.curdir
+
+    @property
+    def _package_dir(self) -> dict[str, str]:
+        if self.dist.package_dir is None:
+            return {}
+        return self.dist.package_dir
+
+    def __call__(
+        self, force: bool = False, name: bool = True, ignore_ext_modules: bool = False
+    ) -> None:
+        """Automatically discover missing configuration fields
+        and modifies the given ``distribution`` object in-place.
+
+        Note that by default this will only have an effect the first time the
+        ``ConfigDiscovery`` object is called.
+
+        To repeatedly invoke automatic discovery (e.g. when the project
+        directory changes), please use ``force=True`` (or create a new
+        ``ConfigDiscovery`` instance).
+        """
+        if force is False and (self._called or self._disabled):
+            # Avoid overhead of multiple calls
+            return
+
+        self._analyse_package_layout(ignore_ext_modules)
+        if name:
+            self.analyse_name()  # depends on ``packages`` and ``py_modules``
+
+        self._called = True
+
+    def _explicitly_specified(self, ignore_ext_modules: bool) -> bool:
+        """``True`` if the user has specified some form of package/module listing"""
+        ignore_ext_modules = ignore_ext_modules or self._skip_ext_modules
+        ext_modules = not (self.dist.ext_modules is None or ignore_ext_modules)
+        return (
+            self.dist.packages is not None
+            or self.dist.py_modules is not None
+            or ext_modules
+            or hasattr(self.dist, "configuration")
+            and self.dist.configuration
+            # ^ Some projects use numpy.distutils.misc_util.Configuration
+        )
+
+    def _analyse_package_layout(self, ignore_ext_modules: bool) -> bool:
+        if self._explicitly_specified(ignore_ext_modules):
+            # For backward compatibility, just try to find modules/packages
+            # when nothing is given
+            return True
+
+        log.debug(
+            "No `packages` or `py_modules` configuration, performing "
+            "automatic discovery."
+        )
+
+        return (
+            self._analyse_explicit_layout()
+            or self._analyse_src_layout()
+            # flat-layout is the trickiest for discovery so it should be last
+            or self._analyse_flat_layout()
+        )
+
+    def _analyse_explicit_layout(self) -> bool:
+        """The user can explicitly give a package layout via ``package_dir``"""
+        package_dir = self._package_dir.copy()  # don't modify directly
+        package_dir.pop("", None)  # This falls under the "src-layout" umbrella
+        root_dir = self._root_dir
+
+        if not package_dir:
+            return False
+
+        log.debug(f"`explicit-layout` detected -- analysing {package_dir}")
+        pkgs = chain_iter(
+            _find_packages_within(pkg, os.path.join(root_dir, parent_dir))
+            for pkg, parent_dir in package_dir.items()
+        )
+        self.dist.packages = list(pkgs)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        return True
+
+    def _analyse_src_layout(self) -> bool:
+        """Try to find all packages or modules under the ``src`` directory
+        (or anything pointed by ``package_dir[""]``).
+
+        The "src-layout" is relatively safe for automatic discovery.
+        We assume that everything within is meant to be included in the
+        distribution.
+
+        If ``package_dir[""]`` is not given, but the ``src`` directory exists,
+        this function will set ``package_dir[""] = "src"``.
+        """
+        package_dir = self._package_dir
+        src_dir = os.path.join(self._root_dir, package_dir.get("", "src"))
+        if not os.path.isdir(src_dir):
+            return False
+
+        log.debug(f"`src-layout` detected -- analysing {src_dir}")
+        package_dir.setdefault("", os.path.basename(src_dir))
+        self.dist.package_dir = package_dir  # persist eventual modifications
+        self.dist.packages = PEP420PackageFinder.find(src_dir)
+        self.dist.py_modules = ModuleFinder.find(src_dir)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        return True
+
+    def _analyse_flat_layout(self) -> bool:
+        """Try to find all packages and modules under the project root.
+
+        Since the ``flat-layout`` is more dangerous in terms of accidentally including
+        extra files/directories, this function is more conservative and will raise an
+        error if multiple packages or modules are found.
+
+        This assumes that multi-package dists are uncommon and refuse to support that
+        use case in order to be able to prevent unintended errors.
+        """
+        log.debug(f"`flat-layout` detected -- analysing {self._root_dir}")
+        return self._analyse_flat_packages() or self._analyse_flat_modules()
+
+    def _analyse_flat_packages(self) -> bool:
+        self.dist.packages = FlatLayoutPackageFinder.find(self._root_dir)
+        top_level = remove_nested_packages(remove_stubs(self.dist.packages))
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        self._ensure_no_accidental_inclusion(top_level, "packages")
+        return bool(top_level)
+
+    def _analyse_flat_modules(self) -> bool:
+        self.dist.py_modules = FlatLayoutModuleFinder.find(self._root_dir)
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        self._ensure_no_accidental_inclusion(self.dist.py_modules, "modules")
+        return bool(self.dist.py_modules)
+
+    def _ensure_no_accidental_inclusion(self, detected: list[str], kind: str):
+        if len(detected) > 1:
+            from inspect import cleandoc
+
+            from setuptools.errors import PackageDiscoveryError
+
+            msg = f"""Multiple top-level {kind} discovered in a flat-layout: {detected}.
+
+            To avoid accidental inclusion of unwanted files or directories,
+            setuptools will not proceed with this build.
+
+            If you are trying to create a single distribution with multiple {kind}
+            on purpose, you should not rely on automatic discovery.
+            Instead, consider the following options:
+
+            1. set up custom discovery (`find` directive with `include` or `exclude`)
+            2. use a `src-layout`
+            3. explicitly set `py_modules` or `packages` with a list of names
+
+            To find more information, look for "package discovery" on setuptools docs.
+            """
+            raise PackageDiscoveryError(cleandoc(msg))
+
+    def analyse_name(self) -> None:
+        """The packages/modules are the essential contribution of the author.
+        Therefore the name of the distribution can be derived from them.
+        """
+        if self.dist.metadata.name or self.dist.name:
+            # get_name() is not reliable (can return "UNKNOWN")
+            return
+
+        log.debug("No `name` configuration, performing automatic discovery")
+
+        name = (
+            self._find_name_single_package_or_module()
+            or self._find_name_from_packages()
+        )
+        if name:
+            self.dist.metadata.name = name
+
+    def _find_name_single_package_or_module(self) -> str | None:
+        """Exactly one module or package"""
+        for field in ('packages', 'py_modules'):
+            items = getattr(self.dist, field, None) or []
+            if items and len(items) == 1:
+                log.debug(f"Single module/package detected, name: {items[0]}")
+                return items[0]
+
+        return None
+
+    def _find_name_from_packages(self) -> str | None:
+        """Try to find the root package that is not a PEP 420 namespace"""
+        if not self.dist.packages:
+            return None
+
+        packages = remove_stubs(sorted(self.dist.packages, key=len))
+        package_dir = self.dist.package_dir or {}
+
+        parent_pkg = find_parent_package(packages, package_dir, self._root_dir)
+        if parent_pkg:
+            log.debug(f"Common parent package detected, name: {parent_pkg}")
+            return parent_pkg
+
+        log.warn("No parent package detected, impossible to derive `name`")
+        return None
+
+
+def remove_nested_packages(packages: list[str]) -> list[str]:
+    """Remove nested packages from a list of packages.
+
+    >>> remove_nested_packages(["a", "a.b1", "a.b2", "a.b1.c1"])
+    ['a']
+    >>> remove_nested_packages(["a", "b", "c.d", "c.d.e.f", "g.h", "a.a1"])
+    ['a', 'b', 'c.d', 'g.h']
+    """
+    pkgs = sorted(packages, key=len)
+    top_level = pkgs[:]
+    size = len(pkgs)
+    for i, name in enumerate(reversed(pkgs)):
+        if any(name.startswith(f"{other}.") for other in top_level):
+            top_level.pop(size - i - 1)
+
+    return top_level
+
+
+def remove_stubs(packages: list[str]) -> list[str]:
+    """Remove type stubs (:pep:`561`) from a list of packages.
+
+    >>> remove_stubs(["a", "a.b", "a-stubs", "a-stubs.b.c", "b", "c-stubs"])
+    ['a', 'a.b', 'b']
+    """
+    return [pkg for pkg in packages if not pkg.split(".")[0].endswith("-stubs")]
+
+
+def find_parent_package(
+    packages: list[str], package_dir: Mapping[str, str], root_dir: StrPath
+) -> str | None:
+    """Find the parent package that is not a namespace."""
+    packages = sorted(packages, key=len)
+    common_ancestors = []
+    for i, name in enumerate(packages):
+        if not all(n.startswith(f"{name}.") for n in packages[i + 1 :]):
+            # Since packages are sorted by length, this condition is able
+            # to find a list of all common ancestors.
+            # When there is divergence (e.g. multiple root packages)
+            # the list will be empty
+            break
+        common_ancestors.append(name)
+
+    for name in common_ancestors:
+        pkg_path = find_package_path(name, package_dir, root_dir)
+        init = os.path.join(pkg_path, "__init__.py")
+        if os.path.isfile(init):
+            return name
+
+    return None
+
+
+def find_package_path(
+    name: str, package_dir: Mapping[str, str], root_dir: StrPath
+) -> str:
+    """Given a package name, return the path where it should be found on
+    disk, considering the ``package_dir`` option.
+
+    >>> path = find_package_path("my.pkg", {"": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/my/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested'
+
+    >>> path = find_package_path("other.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './other/pkg'
+    """
+    parts = name.split(".")
+    for i in range(len(parts), 0, -1):
+        # Look backwards, the most specific package_dir first
+        partial_name = ".".join(parts[:i])
+        if partial_name in package_dir:
+            parent = package_dir[partial_name]
+            return os.path.join(root_dir, parent, *parts[i:])
+
+    parent = package_dir.get("") or ""
+    return os.path.join(root_dir, *parent.split("/"), *parts)
+
+
+def construct_package_dir(packages: list[str], package_path: StrPath) -> dict[str, str]:
+    parent_pkgs = remove_nested_packages(packages)
+    prefix = Path(package_path).parts
+    return {pkg: "/".join([*prefix, *pkg.split(".")]) for pkg in parent_pkgs}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/dist.py b/.venv/lib/python3.12/site-packages/setuptools/dist.py
new file mode 100644
index 0000000..a224b3e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/dist.py
@@ -0,0 +1,1124 @@
+from __future__ import annotations
+
+import functools
+import io
+import itertools
+import numbers
+import os
+import re
+import sys
+from collections.abc import Iterable, Iterator, MutableMapping, Sequence
+from glob import glob
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Union
+
+from more_itertools import partition, unique_everseen
+from packaging.markers import InvalidMarker, Marker
+from packaging.specifiers import InvalidSpecifier, SpecifierSet
+from packaging.version import Version
+
+from . import (
+    _entry_points,
+    _reqs,
+    _static,
+    command as _,  # noqa: F401 # imported for side-effects
+)
+from ._importlib import metadata
+from ._normalization import _canonicalize_license_expression
+from ._path import StrPath
+from ._reqs import _StrOrIter
+from .config import pyprojecttoml, setupcfg
+from .discovery import ConfigDiscovery
+from .errors import InvalidConfigError
+from .monkey import get_unpatched
+from .warnings import InformationOnly, SetuptoolsDeprecationWarning
+
+import distutils.cmd
+import distutils.command
+import distutils.core
+import distutils.dist
+import distutils.log
+from distutils.debug import DEBUG
+from distutils.errors import DistutilsOptionError, DistutilsSetupError
+from distutils.fancy_getopt import translate_longopt
+from distutils.util import strtobool
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+
+__all__ = ['Distribution']
+
+_sequence = tuple, list
+"""
+:meta private:
+
+Supported iterable types that are known to be:
+- ordered (which `set` isn't)
+- not match a str (which `Sequence[str]` does)
+- not imply a nested type (like `dict`)
+for use with `isinstance`.
+"""
+_Sequence: TypeAlias = Union[tuple[str, ...], list[str]]
+# This is how stringifying _Sequence would look in Python 3.10
+_sequence_type_repr = "tuple[str, ...] | list[str]"
+_OrderedStrSequence: TypeAlias = Union[str, dict[str, Any], Sequence[str]]
+"""
+:meta private:
+Avoid single-use iterable. Disallow sets.
+A poor approximation of an OrderedSequence (dict doesn't match a Sequence).
+"""
+
+
+def __getattr__(name: str) -> Any:  # pragma: no cover
+    if name == "sequence":
+        SetuptoolsDeprecationWarning.emit(
+            "`setuptools.dist.sequence` is an internal implementation detail.",
+            "Please define your own `sequence = tuple, list` instead.",
+            due_date=(2025, 8, 28),  # Originally added on 2024-08-27
+        )
+        return _sequence
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+def check_importable(dist, attr, value):
+    try:
+        ep = metadata.EntryPoint(value=value, name=None, group=None)
+        assert not ep.extras
+    except (TypeError, ValueError, AttributeError, AssertionError) as e:
+        raise DistutilsSetupError(
+            f"{attr!r} must be importable 'module:attrs' string (got {value!r})"
+        ) from e
+
+
+def assert_string_list(dist, attr: str, value: _Sequence) -> None:
+    """Verify that value is a string list"""
+    try:
+        # verify that value is a list or tuple to exclude unordered
+        # or single-use iterables
+        assert isinstance(value, _sequence)
+        # verify that elements of value are strings
+        assert ''.join(value) != value
+    except (TypeError, ValueError, AttributeError, AssertionError) as e:
+        raise DistutilsSetupError(
+            f"{attr!r} must be of type <{_sequence_type_repr}> (got {value!r})"
+        ) from e
+
+
+def check_nsp(dist, attr, value):
+    """Verify that namespace packages are valid"""
+    ns_packages = value
+    assert_string_list(dist, attr, ns_packages)
+    for nsp in ns_packages:
+        if not dist.has_contents_for(nsp):
+            raise DistutilsSetupError(
+                f"Distribution contains no modules or packages for namespace package {nsp!r}"
+            )
+        parent, _sep, _child = nsp.rpartition('.')
+        if parent and parent not in ns_packages:
+            distutils.log.warn(
+                "WARNING: %r is declared as a package namespace, but %r"
+                " is not: please correct this in setup.py",
+                nsp,
+                parent,
+            )
+        SetuptoolsDeprecationWarning.emit(
+            "The namespace_packages parameter is deprecated.",
+            "Please replace its usage with implicit namespaces (PEP 420).",
+            see_docs="references/keywords.html#keyword-namespace-packages",
+            # TODO: define due_date, it may break old packages that are no longer
+            # maintained (e.g. sphinxcontrib extensions) when installed from source.
+            # Warning officially introduced in May 2022, however the deprecation
+            # was mentioned much earlier in the docs (May 2020, see #2149).
+        )
+
+
+def check_extras(dist, attr, value):
+    """Verify that extras_require mapping is valid"""
+    try:
+        list(itertools.starmap(_check_extra, value.items()))
+    except (TypeError, ValueError, AttributeError) as e:
+        raise DistutilsSetupError(
+            "'extras_require' must be a dictionary whose values are "
+            "strings or lists of strings containing valid project/version "
+            "requirement specifiers."
+        ) from e
+
+
+def _check_extra(extra, reqs):
+    _name, _sep, marker = extra.partition(':')
+    try:
+        _check_marker(marker)
+    except InvalidMarker:
+        msg = f"Invalid environment marker: {marker} ({extra!r})"
+        raise DistutilsSetupError(msg) from None
+    list(_reqs.parse(reqs))
+
+
+def _check_marker(marker):
+    if not marker:
+        return
+    m = Marker(marker)
+    m.evaluate()
+
+
+def assert_bool(dist, attr, value):
+    """Verify that value is True, False, 0, or 1"""
+    if bool(value) != value:
+        raise DistutilsSetupError(f"{attr!r} must be a boolean value (got {value!r})")
+
+
+def invalid_unless_false(dist, attr, value):
+    if not value:
+        DistDeprecationWarning.emit(f"{attr} is ignored.")
+        # TODO: should there be a `due_date` here?
+        return
+    raise DistutilsSetupError(f"{attr} is invalid.")
+
+
+def check_requirements(dist, attr: str, value: _OrderedStrSequence) -> None:
+    """Verify that install_requires is a valid requirements list"""
+    try:
+        list(_reqs.parse(value))
+        if isinstance(value, set):
+            raise TypeError("Unordered types are not allowed")
+    except (TypeError, ValueError) as error:
+        msg = (
+            f"{attr!r} must be a string or iterable of strings "
+            f"containing valid project/version requirement specifiers; {error}"
+        )
+        raise DistutilsSetupError(msg) from error
+
+
+def check_specifier(dist, attr, value):
+    """Verify that value is a valid version specifier"""
+    try:
+        SpecifierSet(value)
+    except (InvalidSpecifier, AttributeError) as error:
+        msg = f"{attr!r} must be a string containing valid version specifiers; {error}"
+        raise DistutilsSetupError(msg) from error
+
+
+def check_entry_points(dist, attr, value):
+    """Verify that entry_points map is parseable"""
+    try:
+        _entry_points.load(value)
+    except Exception as e:
+        raise DistutilsSetupError(e) from e
+
+
+def check_package_data(dist, attr, value):
+    """Verify that value is a dictionary of package names to glob lists"""
+    if not isinstance(value, dict):
+        raise DistutilsSetupError(
+            f"{attr!r} must be a dictionary mapping package names to lists of "
+            "string wildcard patterns"
+        )
+    for k, v in value.items():
+        if not isinstance(k, str):
+            raise DistutilsSetupError(
+                f"keys of {attr!r} dict must be strings (got {k!r})"
+            )
+        assert_string_list(dist, f'values of {attr!r} dict', v)
+
+
+def check_packages(dist, attr, value):
+    for pkgname in value:
+        if not re.match(r'\w+(\.\w+)*', pkgname):
+            distutils.log.warn(
+                "WARNING: %r not a valid package name; please use only "
+                ".-separated package names in setup.py",
+                pkgname,
+            )
+
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Distribution as _Distribution
+else:
+    _Distribution = get_unpatched(distutils.core.Distribution)
+
+
+class Distribution(_Distribution):
+    """Distribution with support for tests and package data
+
+    This is an enhanced version of 'distutils.dist.Distribution' that
+    effectively adds the following new optional keyword arguments to 'setup()':
+
+     'install_requires' -- a string or sequence of strings specifying project
+        versions that the distribution requires when installed, in the format
+        used by 'pkg_resources.require()'.  They will be installed
+        automatically when the package is installed.  If you wish to use
+        packages that are not available in PyPI, or want to give your users an
+        alternate download location, you can add a 'find_links' option to the
+        '[easy_install]' section of your project's 'setup.cfg' file, and then
+        setuptools will scan the listed web pages for links that satisfy the
+        requirements.
+
+     'extras_require' -- a dictionary mapping names of optional "extras" to the
+        additional requirement(s) that using those extras incurs. For example,
+        this::
+
+            extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
+
+        indicates that the distribution can optionally provide an extra
+        capability called "reST", but it can only be used if docutils and
+        reSTedit are installed.  If the user installs your package using
+        EasyInstall and requests one of your extras, the corresponding
+        additional requirements will be installed if needed.
+
+     'package_data' -- a dictionary mapping package names to lists of filenames
+        or globs to use to find data files contained in the named packages.
+        If the dictionary has filenames or globs listed under '""' (the empty
+        string), those names will be searched for in every package, in addition
+        to any names for the specific package.  Data files found using these
+        names/globs will be installed along with the package, in the same
+        location as the package.  Note that globs are allowed to reference
+        the contents of non-package subdirectories, as long as you use '/' as
+        a path separator.  (Globs are automatically converted to
+        platform-specific paths at runtime.)
+
+    In addition to these new keywords, this class also has several new methods
+    for manipulating the distribution's contents.  For example, the 'include()'
+    and 'exclude()' methods can be thought of as in-place add and subtract
+    commands that add or remove packages, modules, extensions, and so on from
+    the distribution.
+    """
+
+    _DISTUTILS_UNSUPPORTED_METADATA = {
+        'long_description_content_type': lambda: None,
+        'project_urls': dict,
+        'provides_extras': dict,  # behaves like an ordered set
+        'license_expression': lambda: None,
+        'license_file': lambda: None,
+        'license_files': lambda: None,
+        'install_requires': list,
+        'extras_require': dict,
+    }
+
+    # Used by build_py, editable_wheel and install_lib commands for legacy namespaces
+    namespace_packages: list[str]  #: :meta private: DEPRECATED
+
+    # Any: Dynamic assignment results in Incompatible types in assignment
+    def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None:
+        have_package_data = hasattr(self, "package_data")
+        if not have_package_data:
+            self.package_data: dict[str, list[str]] = {}
+        attrs = attrs or {}
+        self.dist_files: list[tuple[str, str, str]] = []
+        self.include_package_data: bool | None = None
+        self.exclude_package_data: dict[str, list[str]] | None = None
+        # Filter-out setuptools' specific options.
+        self.src_root: str | None = attrs.pop("src_root", None)
+        self.dependency_links: list[str] = attrs.pop('dependency_links', [])
+        self.setup_requires: list[str] = attrs.pop('setup_requires', [])
+        for ep in metadata.entry_points(group='distutils.setup_keywords'):
+            vars(self).setdefault(ep.name, None)
+
+        metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA)
+        metadata_only -= {"install_requires", "extras_require"}
+        dist_attrs = {k: v for k, v in attrs.items() if k not in metadata_only}
+        _Distribution.__init__(self, dist_attrs)
+
+        # Private API (setuptools-use only, not restricted to Distribution)
+        # Stores files that are referenced by the configuration and need to be in the
+        # sdist (e.g. `version = file: VERSION.txt`)
+        self._referenced_files = set[str]()
+
+        self.set_defaults = ConfigDiscovery(self)
+
+        self._set_metadata_defaults(attrs)
+
+        self.metadata.version = self._normalize_version(self.metadata.version)
+        self._finalize_requires()
+
+    def _validate_metadata(self):
+        required = {"name"}
+        provided = {
+            key
+            for key in vars(self.metadata)
+            if getattr(self.metadata, key, None) is not None
+        }
+        missing = required - provided
+
+        if missing:
+            msg = f"Required package metadata is missing: {missing}"
+            raise DistutilsSetupError(msg)
+
+    def _set_metadata_defaults(self, attrs):
+        """
+        Fill-in missing metadata fields not supported by distutils.
+        Some fields may have been set by other tools (e.g. pbr).
+        Those fields (vars(self.metadata)) take precedence to
+        supplied attrs.
+        """
+        for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
+            vars(self.metadata).setdefault(option, attrs.get(option, default()))
+
+    @staticmethod
+    def _normalize_version(version):
+        from . import sic
+
+        if isinstance(version, numbers.Number):
+            # Some people apparently take "version number" too literally :)
+            version = str(version)
+        elif isinstance(version, sic) or version is None:
+            return version
+
+        normalized = str(Version(version))
+        if version != normalized:
+            InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'")
+            return normalized
+        return version
+
+    def _finalize_requires(self):
+        """
+        Set `metadata.python_requires` and fix environment markers
+        in `install_requires` and `extras_require`.
+        """
+        if getattr(self, 'python_requires', None):
+            self.metadata.python_requires = self.python_requires
+
+        self._normalize_requires()
+        self.metadata.install_requires = self.install_requires
+        self.metadata.extras_require = self.extras_require
+
+        if self.extras_require:
+            for extra in self.extras_require.keys():
+                # Setuptools allows a weird ": syntax for extras
+                extra = extra.split(':')[0]
+                if extra:
+                    self.metadata.provides_extras.setdefault(extra)
+
+    def _normalize_requires(self):
+        """Make sure requirement-related attributes exist and are normalized"""
+        install_requires = getattr(self, "install_requires", None) or []
+        extras_require = getattr(self, "extras_require", None) or {}
+
+        # Preserve the "static"-ness of values parsed from config files
+        list_ = _static.List if _static.is_static(install_requires) else list
+        self.install_requires = list_(map(str, _reqs.parse(install_requires)))
+
+        dict_ = _static.Dict if _static.is_static(extras_require) else dict
+        self.extras_require = dict_(
+            (k, list(map(str, _reqs.parse(v or [])))) for k, v in extras_require.items()
+        )
+
+    def _finalize_license_expression(self) -> None:
+        """
+        Normalize license and license_expression.
+        >>> dist = Distribution({"license_expression": _static.Str("mit aNd  gpl-3.0-OR-later")})
+        >>> _static.is_static(dist.metadata.license_expression)
+        True
+        >>> dist._finalize_license_expression()
+        >>> _static.is_static(dist.metadata.license_expression)  # preserve "static-ness"
+        True
+        >>> print(dist.metadata.license_expression)
+        MIT AND GPL-3.0-or-later
+        """
+        classifiers = self.metadata.get_classifiers()
+        license_classifiers = [cl for cl in classifiers if cl.startswith("License :: ")]
+
+        license_expr = self.metadata.license_expression
+        if license_expr:
+            str_ = _static.Str if _static.is_static(license_expr) else str
+            normalized = str_(_canonicalize_license_expression(license_expr))
+            if license_expr != normalized:
+                InformationOnly.emit(f"Normalizing '{license_expr}' to '{normalized}'")
+                self.metadata.license_expression = normalized
+            if license_classifiers:
+                raise InvalidConfigError(
+                    "License classifiers have been superseded by license expressions "
+                    "(see https://peps.python.org/pep-0639/). Please remove:\n\n"
+                    + "\n".join(license_classifiers),
+                )
+        elif license_classifiers:
+            pypa_guides = "guides/writing-pyproject-toml/#license"
+            SetuptoolsDeprecationWarning.emit(
+                "License classifiers are deprecated.",
+                "Please consider removing the following classifiers in favor of a "
+                "SPDX license expression:\n\n" + "\n".join(license_classifiers),
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                # Warning introduced on 2025-02-17
+                # TODO: Should we add a due date? It may affect old/unmaintained
+                #       packages in the ecosystem and cause problems...
+            )
+
+    def _finalize_license_files(self) -> None:
+        """Compute names of all license files which should be included."""
+        license_files: list[str] | None = self.metadata.license_files
+        patterns = license_files or []
+
+        license_file: str | None = self.metadata.license_file
+        if license_file and license_file not in patterns:
+            patterns.append(license_file)
+
+        if license_files is None and license_file is None:
+            # Default patterns match the ones wheel uses
+            # See https://wheel.readthedocs.io/en/stable/user_guide.html
+            # -> 'Including license files in the generated wheel file'
+            patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']
+            files = self._expand_patterns(patterns, enforce_match=False)
+        else:  # Patterns explicitly given by the user
+            files = self._expand_patterns(patterns, enforce_match=True)
+
+        self.metadata.license_files = list(unique_everseen(files))
+
+    @classmethod
+    def _expand_patterns(
+        cls, patterns: list[str], enforce_match: bool = True
+    ) -> Iterator[str]:
+        """
+        >>> getfixture('sample_project_cwd')
+        >>> list(Distribution._expand_patterns(['LICENSE.txt']))
+        ['LICENSE.txt']
+        >>> list(Distribution._expand_patterns(['pyproject.toml', 'LIC*']))
+        ['pyproject.toml', 'LICENSE.txt']
+        >>> list(Distribution._expand_patterns(['src/**/*.dat']))
+        ['src/sample/package_data.dat']
+        """
+        return (
+            path.replace(os.sep, "/")
+            for pattern in patterns
+            for path in sorted(cls._find_pattern(pattern, enforce_match))
+            if not path.endswith('~') and os.path.isfile(path)
+        )
+
+    @staticmethod
+    def _find_pattern(pattern: str, enforce_match: bool = True) -> list[str]:
+        r"""
+        >>> getfixture('sample_project_cwd')
+        >>> Distribution._find_pattern("LICENSE.txt")
+        ['LICENSE.txt']
+        >>> Distribution._find_pattern("/LICENSE.MIT")
+        Traceback (most recent call last):
+        ...
+        setuptools.errors.InvalidConfigError: Pattern '/LICENSE.MIT' should be relative...
+        >>> Distribution._find_pattern("../LICENSE.MIT")
+        Traceback (most recent call last):
+        ...
+        setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern '../LICENSE.MIT' cannot contain '..'...
+        >>> Distribution._find_pattern("LICEN{CSE*")
+        Traceback (most recent call last):
+        ...
+        setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern 'LICEN{CSE*' contains invalid characters...
+        """
+        pypa_guides = "specifications/glob-patterns/"
+        if ".." in pattern:
+            SetuptoolsDeprecationWarning.emit(
+                f"Pattern {pattern!r} cannot contain '..'",
+                """
+                Please ensure the files specified are contained by the root
+                of the Python package (normally marked by `pyproject.toml`).
+                """,
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                due_date=(2027, 2, 18),  # Introduced in 2025-03-20
+                # Replace with InvalidConfigError after deprecation
+            )
+        if pattern.startswith((os.sep, "/")) or ":\\" in pattern:
+            raise InvalidConfigError(
+                f"Pattern {pattern!r} should be relative and must not start with '/'"
+            )
+        if re.match(r'^[\w\-\.\/\*\?\[\]]+$', pattern) is None:
+            SetuptoolsDeprecationWarning.emit(
+                "Please provide a valid glob pattern.",
+                "Pattern {pattern!r} contains invalid characters.",
+                pattern=pattern,
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                due_date=(2027, 2, 18),  # Introduced in 2025-02-20
+            )
+
+        found = glob(pattern, recursive=True)
+
+        if enforce_match and not found:
+            SetuptoolsDeprecationWarning.emit(
+                "Cannot find any files for the given pattern.",
+                "Pattern {pattern!r} did not match any files.",
+                pattern=pattern,
+                due_date=(2027, 2, 18),  # Introduced in 2025-02-20
+                # PEP 639 requires us to error, but as a transition period
+                # we will only issue a warning to give people time to prepare.
+                # After the transition, this should raise an InvalidConfigError.
+            )
+        return found
+
+    # FIXME: 'Distribution._parse_config_files' is too complex (14)
+    def _parse_config_files(self, filenames=None):  # noqa: C901
+        """
+        Adapted from distutils.dist.Distribution.parse_config_files,
+        this method provides the same functionality in subtly-improved
+        ways.
+        """
+        from configparser import ConfigParser
+
+        # Ignore install directory options if we have a venv
+        ignore_options = (
+            []
+            if sys.prefix == sys.base_prefix
+            else [
+                'install-base',
+                'install-platbase',
+                'install-lib',
+                'install-platlib',
+                'install-purelib',
+                'install-headers',
+                'install-scripts',
+                'install-data',
+                'prefix',
+                'exec-prefix',
+                'home',
+                'user',
+                'root',
+            ]
+        )
+
+        ignore_options = frozenset(ignore_options)
+
+        if filenames is None:
+            filenames = self.find_config_files()
+
+        if DEBUG:
+            self.announce("Distribution.parse_config_files():")
+
+        parser = ConfigParser()
+        parser.optionxform = str
+        for filename in filenames:
+            with open(filename, encoding='utf-8') as reader:
+                if DEBUG:
+                    self.announce("  reading {filename}".format(**locals()))
+                parser.read_file(reader)
+            for section in parser.sections():
+                options = parser.options(section)
+                opt_dict = self.get_option_dict(section)
+
+                for opt in options:
+                    if opt == '__name__' or opt in ignore_options:
+                        continue
+
+                    val = parser.get(section, opt)
+                    opt = self._enforce_underscore(opt, section)
+                    opt = self._enforce_option_lowercase(opt, section)
+                    opt_dict[opt] = (filename, val)
+
+            # Make the ConfigParser forget everything (so we retain
+            # the original filenames that options come from)
+            parser.__init__()
+
+        if 'global' not in self.command_options:
+            return
+
+        # If there was a "global" section in the config file, use it
+        # to set Distribution options.
+
+        for opt, (src, val) in self.command_options['global'].items():
+            alias = self.negative_opt.get(opt)
+            if alias:
+                val = not strtobool(val)
+            elif opt == 'verbose':
+                val = strtobool(val)
+
+            try:
+                setattr(self, alias or opt, val)
+            except ValueError as e:
+                raise DistutilsOptionError(e) from e
+
+    def _enforce_underscore(self, opt: str, section: str) -> str:
+        if "-" not in opt or self._skip_setupcfg_normalization(section):
+            return opt
+
+        underscore_opt = opt.replace('-', '_')
+        affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
+        SetuptoolsDeprecationWarning.emit(
+            f"Invalid dash-separated key {opt!r} in {section!r} (setup.cfg), "
+            f"please use the underscore name {underscore_opt!r} instead.",
+            f"""
+            Usage of dash-separated {opt!r} will not be supported in future
+            versions. Please use the underscore name {underscore_opt!r} instead.
+            {affected}
+
+            Available configuration options are listed in:
+            https://setuptools.pypa.io/en/latest/userguide/declarative_config.html
+            """,
+            see_url="https://github.com/pypa/setuptools/discussions/5011",
+            due_date=(2026, 3, 3),
+            # Warning initially introduced in 3 Mar 2021
+        )
+        return underscore_opt
+
+    def _enforce_option_lowercase(self, opt: str, section: str) -> str:
+        if opt.islower() or self._skip_setupcfg_normalization(section):
+            return opt
+
+        lowercase_opt = opt.lower()
+        affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
+        SetuptoolsDeprecationWarning.emit(
+            f"Invalid uppercase key {opt!r} in {section!r} (setup.cfg), "
+            f"please use lowercase {lowercase_opt!r} instead.",
+            f"""
+            Usage of uppercase key {opt!r} in {section!r} will not be supported in
+            future versions. Please use lowercase {lowercase_opt!r} instead.
+            {affected}
+
+            Available configuration options are listed in:
+            https://setuptools.pypa.io/en/latest/userguide/declarative_config.html
+            """,
+            see_url="https://github.com/pypa/setuptools/discussions/5011",
+            due_date=(2026, 3, 3),
+            # Warning initially introduced in 6 Mar 2021
+        )
+        return lowercase_opt
+
+    def _skip_setupcfg_normalization(self, section: str) -> bool:
+        skip = (
+            'options.extras_require',
+            'options.data_files',
+            'options.entry_points',
+            'options.package_data',
+            'options.exclude_package_data',
+        )
+        return section in skip or not self._is_setuptools_section(section)
+
+    def _is_setuptools_section(self, section: str) -> bool:
+        return (
+            section == "metadata"
+            or section.startswith("options")
+            or section in _setuptools_commands()
+        )
+
+    # FIXME: 'Distribution._set_command_options' is too complex (14)
+    def _set_command_options(self, command_obj, option_dict=None):  # noqa: C901
+        """
+        Set the options for 'command_obj' from 'option_dict'.  Basically
+        this means copying elements of a dictionary ('option_dict') to
+        attributes of an instance ('command').
+
+        'command_obj' must be a Command instance.  If 'option_dict' is not
+        supplied, uses the standard option dictionary for this command
+        (from 'self.command_options').
+
+        (Adopted from distutils.dist.Distribution._set_command_options)
+        """
+        command_name = command_obj.get_command_name()
+        if option_dict is None:
+            option_dict = self.get_option_dict(command_name)
+
+        if DEBUG:
+            self.announce(f"  setting options for '{command_name}' command:")
+        for option, (source, value) in option_dict.items():
+            if DEBUG:
+                self.announce(f"    {option} = {value} (from {source})")
+            try:
+                bool_opts = [translate_longopt(o) for o in command_obj.boolean_options]
+            except AttributeError:
+                bool_opts = []
+            try:
+                neg_opt = command_obj.negative_opt
+            except AttributeError:
+                neg_opt = {}
+
+            try:
+                is_string = isinstance(value, str)
+                if option in neg_opt and is_string:
+                    setattr(command_obj, neg_opt[option], not strtobool(value))
+                elif option in bool_opts and is_string:
+                    setattr(command_obj, option, strtobool(value))
+                elif hasattr(command_obj, option):
+                    setattr(command_obj, option, value)
+                else:
+                    raise DistutilsOptionError(
+                        f"error in {source}: command '{command_name}' has no such option '{option}'"
+                    )
+            except ValueError as e:
+                raise DistutilsOptionError(e) from e
+
+    def _get_project_config_files(self, filenames: Iterable[StrPath] | None):
+        """Add default file and split between INI and TOML"""
+        tomlfiles = []
+        standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml")
+        if filenames is not None:
+            parts = partition(lambda f: Path(f).suffix == ".toml", filenames)
+            filenames = list(parts[0])  # 1st element => predicate is False
+            tomlfiles = list(parts[1])  # 2nd element => predicate is True
+        elif standard_project_metadata.exists():
+            tomlfiles = [standard_project_metadata]
+        return filenames, tomlfiles
+
+    def parse_config_files(
+        self,
+        filenames: Iterable[StrPath] | None = None,
+        ignore_option_errors: bool = False,
+    ) -> None:
+        """Parses configuration files from various levels
+        and loads configuration.
+        """
+        inifiles, tomlfiles = self._get_project_config_files(filenames)
+
+        self._parse_config_files(filenames=inifiles)
+
+        setupcfg.parse_configuration(
+            self, self.command_options, ignore_option_errors=ignore_option_errors
+        )
+        for filename in tomlfiles:
+            pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
+
+        self._finalize_requires()
+        self._finalize_license_expression()
+        self._finalize_license_files()
+
+    def fetch_build_eggs(self, requires: _StrOrIter) -> list[metadata.Distribution]:
+        """Resolve pre-setup requirements"""
+        from .installer import _fetch_build_eggs
+
+        return _fetch_build_eggs(self, requires)
+
+    def finalize_options(self) -> None:
+        """
+        Allow plugins to apply arbitrary operations to the
+        distribution. Each hook may optionally define a 'order'
+        to influence the order of execution. Smaller numbers
+        go first and the default is 0.
+        """
+        group = 'setuptools.finalize_distribution_options'
+
+        def by_order(hook):
+            return getattr(hook, 'order', 0)
+
+        defined = metadata.entry_points(group=group)
+        filtered = itertools.filterfalse(self._removed, defined)
+        loaded = map(lambda e: e.load(), filtered)
+        for ep in sorted(loaded, key=by_order):
+            ep(self)
+
+    @staticmethod
+    def _removed(ep):
+        """
+        When removing an entry point, if metadata is loaded
+        from an older version of Setuptools, that removed
+        entry point will attempt to be loaded and will fail.
+        See #2765 for more details.
+        """
+        removed = {
+            # removed 2021-09-05
+            '2to3_doctests',
+        }
+        return ep.name in removed
+
+    def _finalize_setup_keywords(self):
+        for ep in metadata.entry_points(group='distutils.setup_keywords'):
+            value = getattr(self, ep.name, None)
+            if value is not None:
+                ep.load()(self, ep.name, value)
+
+    def get_egg_cache_dir(self) -> str:
+        from . import windows_support
+
+        egg_cache_dir = os.path.join(os.curdir, '.eggs')
+        if not os.path.exists(egg_cache_dir):
+            os.mkdir(egg_cache_dir)
+            windows_support.hide_file(egg_cache_dir)
+            readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt')
+            with open(readme_txt_filename, 'w', encoding="utf-8") as f:
+                f.write(
+                    'This directory contains eggs that were downloaded '
+                    'by setuptools to build, test, and run plug-ins.\n\n'
+                )
+                f.write(
+                    'This directory caches those eggs to prevent '
+                    'repeated downloads.\n\n'
+                )
+                f.write('However, it is safe to delete this directory.\n\n')
+
+        return egg_cache_dir
+
+    def fetch_build_egg(self, req):
+        """Fetch an egg needed for building"""
+        from .installer import fetch_build_egg
+
+        return fetch_build_egg(self, req)
+
+    def get_command_class(self, command: str) -> type[distutils.cmd.Command]:  # type: ignore[override] # Not doing complex overrides yet
+        """Pluggable version of get_command_class()"""
+        if command in self.cmdclass:
+            return self.cmdclass[command]
+
+        # Special case bdist_wheel so it's never loaded from "wheel"
+        if command == 'bdist_wheel':
+            from .command.bdist_wheel import bdist_wheel
+
+            return bdist_wheel
+
+        eps = metadata.entry_points(group='distutils.commands', name=command)
+        for ep in eps:
+            self.cmdclass[command] = cmdclass = ep.load()
+            return cmdclass
+        else:
+            return _Distribution.get_command_class(self, command)
+
+    def print_commands(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                self.cmdclass[ep.name] = cmdclass
+        return _Distribution.print_commands(self)
+
+    def get_command_list(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                self.cmdclass[ep.name] = cmdclass
+        return _Distribution.get_command_list(self)
+
+    def include(self, **attrs) -> None:
+        """Add items to distribution that are named in keyword arguments
+
+        For example, 'dist.include(py_modules=["x"])' would add 'x' to
+        the distribution's 'py_modules' attribute, if it was not already
+        there.
+
+        Currently, this method only supports inclusion for attributes that are
+        lists or tuples.  If you need to add support for adding to other
+        attributes in this or a subclass, you can add an '_include_X' method,
+        where 'X' is the name of the attribute.  The method will be called with
+        the value passed to 'include()'.  So, 'dist.include(foo={"bar":"baz"})'
+        will try to call 'dist._include_foo({"bar":"baz"})', which can then
+        handle whatever special inclusion logic is needed.
+        """
+        for k, v in attrs.items():
+            include = getattr(self, '_include_' + k, None)
+            if include:
+                include(v)
+            else:
+                self._include_misc(k, v)
+
+    def exclude_package(self, package: str) -> None:
+        """Remove packages, modules, and extensions in named package"""
+
+        pfx = package + '.'
+        if self.packages:
+            self.packages = [
+                p for p in self.packages if p != package and not p.startswith(pfx)
+            ]
+
+        if self.py_modules:
+            self.py_modules = [
+                p for p in self.py_modules if p != package and not p.startswith(pfx)
+            ]
+
+        if self.ext_modules:
+            self.ext_modules = [
+                p
+                for p in self.ext_modules
+                if p.name != package and not p.name.startswith(pfx)
+            ]
+
+    def has_contents_for(self, package: str) -> bool:
+        """Return true if 'exclude_package(package)' would do something"""
+
+        pfx = package + '.'
+
+        for p in self.iter_distribution_names():
+            if p == package or p.startswith(pfx):
+                return True
+
+        return False
+
+    def _exclude_misc(self, name: str, value: _Sequence) -> None:
+        """Handle 'exclude()' for list/tuple attrs without a special handler"""
+        if not isinstance(value, _sequence):
+            raise DistutilsSetupError(
+                f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
+            )
+        try:
+            old = getattr(self, name)
+        except AttributeError as e:
+            raise DistutilsSetupError(f"{name}: No such distribution setting") from e
+        if old is not None and not isinstance(old, _sequence):
+            raise DistutilsSetupError(
+                name + ": this setting cannot be changed via include/exclude"
+            )
+        elif old:
+            setattr(self, name, [item for item in old if item not in value])
+
+    def _include_misc(self, name: str, value: _Sequence) -> None:
+        """Handle 'include()' for list/tuple attrs without a special handler"""
+
+        if not isinstance(value, _sequence):
+            raise DistutilsSetupError(
+                f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
+            )
+        try:
+            old = getattr(self, name)
+        except AttributeError as e:
+            raise DistutilsSetupError(f"{name}: No such distribution setting") from e
+        if old is None:
+            setattr(self, name, value)
+        elif not isinstance(old, _sequence):
+            raise DistutilsSetupError(
+                name + ": this setting cannot be changed via include/exclude"
+            )
+        else:
+            new = [item for item in value if item not in old]
+            setattr(self, name, list(old) + new)
+
+    def exclude(self, **attrs) -> None:
+        """Remove items from distribution that are named in keyword arguments
+
+        For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
+        the distribution's 'py_modules' attribute.  Excluding packages uses
+        the 'exclude_package()' method, so all of the package's contained
+        packages, modules, and extensions are also excluded.
+
+        Currently, this method only supports exclusion from attributes that are
+        lists or tuples.  If you need to add support for excluding from other
+        attributes in this or a subclass, you can add an '_exclude_X' method,
+        where 'X' is the name of the attribute.  The method will be called with
+        the value passed to 'exclude()'.  So, 'dist.exclude(foo={"bar":"baz"})'
+        will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
+        handle whatever special exclusion logic is needed.
+        """
+        for k, v in attrs.items():
+            exclude = getattr(self, '_exclude_' + k, None)
+            if exclude:
+                exclude(v)
+            else:
+                self._exclude_misc(k, v)
+
+    def _exclude_packages(self, packages: _Sequence) -> None:
+        if not isinstance(packages, _sequence):
+            raise DistutilsSetupError(
+                f"packages: setting must be of type <{_sequence_type_repr}> (got {packages!r})"
+            )
+        list(map(self.exclude_package, packages))
+
+    def _parse_command_opts(self, parser, args):
+        # Remove --with-X/--without-X options when processing command args
+        self.global_options = self.__class__.global_options
+        self.negative_opt = self.__class__.negative_opt
+
+        # First, expand any aliases
+        command = args[0]
+        aliases = self.get_option_dict('aliases')
+        while command in aliases:
+            _src, alias = aliases[command]
+            del aliases[command]  # ensure each alias can expand only once!
+            import shlex
+
+            args[:1] = shlex.split(alias, True)
+            command = args[0]
+
+        nargs = _Distribution._parse_command_opts(self, parser, args)
+
+        # Handle commands that want to consume all remaining arguments
+        cmd_class = self.get_command_class(command)
+        if getattr(cmd_class, 'command_consumes_arguments', None):
+            self.get_option_dict(command)['args'] = ("command line", nargs)
+            if nargs is not None:
+                return []
+
+        return nargs
+
+    def get_cmdline_options(self) -> dict[str, dict[str, str | None]]:
+        """Return a '{cmd: {opt:val}}' map of all command-line options
+
+        Option names are all long, but do not include the leading '--', and
+        contain dashes rather than underscores.  If the option doesn't take
+        an argument (e.g. '--quiet'), the 'val' is 'None'.
+
+        Note that options provided by config files are intentionally excluded.
+        """
+
+        d: dict[str, dict[str, str | None]] = {}
+
+        for cmd, opts in self.command_options.items():
+            val: str | None
+            for opt, (src, val) in opts.items():
+                if src != "command line":
+                    continue
+
+                opt = opt.replace('_', '-')
+
+                if val == 0:
+                    cmdobj = self.get_command_obj(cmd)
+                    neg_opt = self.negative_opt.copy()
+                    neg_opt.update(getattr(cmdobj, 'negative_opt', {}))
+                    for neg, pos in neg_opt.items():
+                        if pos == opt:
+                            opt = neg
+                            val = None
+                            break
+                    else:
+                        raise AssertionError("Shouldn't be able to get here")
+
+                elif val == 1:
+                    val = None
+
+                d.setdefault(cmd, {})[opt] = val
+
+        return d
+
+    def iter_distribution_names(self) -> Iterator[str]:
+        """Yield all packages, modules, and extension names in distribution"""
+
+        yield from self.packages or ()
+
+        yield from self.py_modules or ()
+
+        for ext in self.ext_modules or ():
+            if isinstance(ext, tuple):
+                name, _buildinfo = ext
+            else:
+                name = ext.name
+            name = name.removesuffix('module')
+            yield name
+
+    def handle_display_options(self, option_order):
+        """If there were any non-global "display-only" options
+        (--help-commands or the metadata display options) on the command
+        line, display the requested info and return true; else return
+        false.
+        """
+        import sys
+
+        if self.help_commands:
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Stdout may be StringIO (e.g. in tests)
+        if not isinstance(sys.stdout, io.TextIOWrapper):
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Don't wrap stdout if utf-8 is already the encoding. Provides
+        #  workaround for #334.
+        if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Print metadata in UTF-8 no matter the platform
+        encoding = sys.stdout.encoding
+        sys.stdout.reconfigure(encoding='utf-8')
+        try:
+            return _Distribution.handle_display_options(self, option_order)
+        finally:
+            sys.stdout.reconfigure(encoding=encoding)
+
+    def run_command(self, command) -> None:
+        self.set_defaults()
+        # Postpone defaults until all explicit configuration is considered
+        # (setup() args, config files, command line and plugins)
+
+        super().run_command(command)
+
+
+@functools.cache
+def _setuptools_commands() -> set[str]:
+    try:
+        # Use older API for importlib.metadata compatibility
+        entry_points = metadata.distribution('setuptools').entry_points
+        eps: Iterable[str] = (ep.name for ep in entry_points)
+    except metadata.PackageNotFoundError:
+        # during bootstrapping, distribution doesn't exist
+        eps = []
+    return {*distutils.command.__all__, *eps}
+
+
+class DistDeprecationWarning(SetuptoolsDeprecationWarning):
+    """Class for warning about deprecations in dist in
+    setuptools. Not ignored by default, unlike DeprecationWarning."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/errors.py b/.venv/lib/python3.12/site-packages/setuptools/errors.py
new file mode 100644
index 0000000..990ecbf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/errors.py
@@ -0,0 +1,67 @@
+"""setuptools.errors
+
+Provides exceptions used by setuptools modules.
+"""
+
+from __future__ import annotations
+
+from distutils import errors as _distutils_errors
+
+# Re-export errors from distutils to facilitate the migration to PEP632
+
+ByteCompileError = _distutils_errors.DistutilsByteCompileError
+CCompilerError = _distutils_errors.CCompilerError
+ClassError = _distutils_errors.DistutilsClassError
+CompileError = _distutils_errors.CompileError
+ExecError = _distutils_errors.DistutilsExecError
+FileError = _distutils_errors.DistutilsFileError
+InternalError = _distutils_errors.DistutilsInternalError
+LibError = _distutils_errors.LibError
+LinkError = _distutils_errors.LinkError
+ModuleError = _distutils_errors.DistutilsModuleError
+OptionError = _distutils_errors.DistutilsOptionError
+PlatformError = _distutils_errors.DistutilsPlatformError
+PreprocessError = _distutils_errors.PreprocessError
+SetupError = _distutils_errors.DistutilsSetupError
+TemplateError = _distutils_errors.DistutilsTemplateError
+UnknownFileError = _distutils_errors.UnknownFileError
+
+# The root error class in the hierarchy
+BaseError = _distutils_errors.DistutilsError
+
+
+class InvalidConfigError(OptionError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for invalid configurations."""
+
+
+class RemovedConfigError(OptionError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for configurations that were deprecated and removed."""
+
+
+class RemovedCommandError(BaseError, RuntimeError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for commands that have been removed in setuptools.
+
+    Since ``setuptools`` is built on ``distutils``, simply removing a command
+    from ``setuptools`` will make the behavior fall back to ``distutils``; this
+    error is raised if a command exists in ``distutils`` but has been actively
+    removed in ``setuptools``.
+    """
+
+
+class PackageDiscoveryError(BaseError, RuntimeError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Impossible to perform automatic discovery of packages and/or modules.
+
+    The current project layout or given discovery options can lead to problems when
+    scanning the project directory.
+
+    Setuptools might also refuse to complete auto-discovery if an error prone condition
+    is detected (e.g. when a project is organised as a flat-layout but contains
+    multiple directories that can be taken as top-level packages inside a single
+    distribution [*]_). In these situations the users are encouraged to be explicit
+    about which packages to include or to make the discovery parameters more specific.
+
+    .. [*] Since multi-package distributions are uncommon it is very likely that the
+       developers did not intend for all the directories to be packaged, and are just
+       leaving auxiliary code in the repository top-level, such as maintenance-related
+       scripts.
+    """
diff --git a/.venv/lib/python3.12/site-packages/setuptools/extension.py b/.venv/lib/python3.12/site-packages/setuptools/extension.py
new file mode 100644
index 0000000..3e63cbe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/extension.py
@@ -0,0 +1,179 @@
+from __future__ import annotations
+
+import functools
+import re
+from collections.abc import Iterable
+from typing import TYPE_CHECKING
+
+from setuptools._path import StrPath
+
+from .monkey import get_unpatched
+
+import distutils.core
+import distutils.errors
+import distutils.extension
+
+
+def _have_cython() -> bool:
+    """
+    Return True if Cython can be imported.
+    """
+    cython_impl = 'Cython.Distutils.build_ext'
+    try:
+        # from (cython_impl) import build_ext
+        __import__(cython_impl, fromlist=['build_ext']).build_ext
+    except Exception:
+        return False
+    return True
+
+
+# for compatibility
+have_pyrex = _have_cython
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Extension as _Extension
+else:
+    _Extension = get_unpatched(distutils.core.Extension)
+
+
+class Extension(_Extension):
+    """
+    Describes a single extension module.
+
+    This means that all source files will be compiled into a single binary file
+    ``.`` (with ```` derived from ``name`` and
+    ```` defined by one of the values in
+    ``importlib.machinery.EXTENSION_SUFFIXES``).
+
+    In the case ``.pyx`` files are passed as ``sources and`` ``Cython`` is **not**
+    installed in the build environment, ``setuptools`` may also try to look for the
+    equivalent ``.cpp`` or ``.c`` files.
+
+    :arg str name:
+      the full name of the extension, including any packages -- ie.
+      *not* a filename or pathname, but Python dotted name
+
+    :arg Iterable[str | os.PathLike[str]] sources:
+      iterable of source filenames, (except strings, which could be misinterpreted
+      as a single filename), relative to the distribution root
+      (where the setup script lives), in Unix form (slash-separated)
+      for portability.  Source files may be C, C++, SWIG (.i),
+      platform-specific resource files, or whatever else is recognized
+      by the "build_ext" command as source for a Python extension.
+
+    :keyword list[str] include_dirs:
+      list of directories to search for C/C++ header files (in Unix
+      form for portability)
+
+    :keyword list[tuple[str, str|None]] define_macros:
+      list of macros to define; each macro is defined using a 2-tuple:
+      the first item corresponding to the name of the macro and the second
+      item either a string with its value or None to
+      define it without a particular value (equivalent of "#define
+      FOO" in source or -DFOO on Unix C compiler command line)
+
+    :keyword list[str] undef_macros:
+      list of macros to undefine explicitly
+
+    :keyword list[str] library_dirs:
+      list of directories to search for C/C++ libraries at link time
+
+    :keyword list[str] libraries:
+      list of library names (not filenames or paths) to link against
+
+    :keyword list[str] runtime_library_dirs:
+      list of directories to search for C/C++ libraries at run time
+      (for shared extensions, this is when the extension is loaded).
+      Setting this will cause an exception during build on Windows
+      platforms.
+
+    :keyword list[str] extra_objects:
+      list of extra files to link with (eg. object files not implied
+      by 'sources', static library that must be explicitly specified,
+      binary resource files, etc.)
+
+    :keyword list[str] extra_compile_args:
+      any extra platform- and compiler-specific information to use
+      when compiling the source files in 'sources'.  For platforms and
+      compilers where "command line" makes sense, this is typically a
+      list of command-line arguments, but for other platforms it could
+      be anything.
+
+    :keyword list[str] extra_link_args:
+      any extra platform- and compiler-specific information to use
+      when linking object files together to create the extension (or
+      to create a new static Python interpreter).  Similar
+      interpretation as for 'extra_compile_args'.
+
+    :keyword list[str] export_symbols:
+      list of symbols to be exported from a shared extension.  Not
+      used on all platforms, and not generally necessary for Python
+      extensions, which typically export exactly one symbol: "init" +
+      extension_name.
+
+    :keyword list[str] swig_opts:
+      any extra options to pass to SWIG if a source file has the .i
+      extension.
+
+    :keyword list[str] depends:
+      list of files that the extension depends on
+
+    :keyword str language:
+      extension language (i.e. "c", "c++", "objc"). Will be detected
+      from the source extensions if not provided.
+
+    :keyword bool optional:
+      specifies that a build failure in the extension should not abort the
+      build process, but simply not install the failing extension.
+
+    :keyword bool py_limited_api:
+      opt-in flag for the usage of :doc:`Python's limited API `.
+
+    :raises setuptools.errors.PlatformError: if ``runtime_library_dirs`` is
+      specified on Windows. (since v63)
+    """
+
+    # These 4 are set and used in setuptools/command/build_ext.py
+    # The lack of a default value and risk of `AttributeError` is purposeful
+    # to avoid people forgetting to call finalize_options if they modify the extension list.
+    # See example/rationale in https://github.com/pypa/setuptools/issues/4529.
+    _full_name: str  #: Private API, internal use only.
+    _links_to_dynamic: bool  #: Private API, internal use only.
+    _needs_stub: bool  #: Private API, internal use only.
+    _file_name: str  #: Private API, internal use only.
+
+    def __init__(
+        self,
+        name: str,
+        sources: Iterable[StrPath],
+        *args,
+        py_limited_api: bool = False,
+        **kw,
+    ) -> None:
+        # The *args is needed for compatibility as calls may use positional
+        # arguments. py_limited_api may be set only via keyword.
+        self.py_limited_api = py_limited_api
+        super().__init__(
+            name,
+            sources,  # type: ignore[arg-type] # Vendored version of setuptools supports PathLike
+            *args,
+            **kw,
+        )
+
+    def _convert_pyx_sources_to_lang(self):
+        """
+        Replace sources with .pyx extensions to sources with the target
+        language extension. This mechanism allows language authors to supply
+        pre-converted sources but to prefer the .pyx sources.
+        """
+        if _have_cython():
+            # the build has Cython, so allow it to compile the .pyx files
+            return
+        lang = self.language or ''
+        target_ext = '.cpp' if lang.lower() == 'c++' else '.c'
+        sub = functools.partial(re.sub, '.pyx$', target_ext)
+        self.sources = list(map(sub, self.sources))
+
+
+class Library(Extension):
+    """Just like a regular Extension, but built as a library instead"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/glob.py b/.venv/lib/python3.12/site-packages/setuptools/glob.py
new file mode 100644
index 0000000..1dfff2c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/glob.py
@@ -0,0 +1,185 @@
+"""
+Filename globbing utility. Mostly a copy of `glob` from Python 3.5.
+
+Changes include:
+ * `yield from` and PEP3102 `*` removed.
+ * Hidden files are not ignored.
+"""
+
+from __future__ import annotations
+
+import fnmatch
+import os
+import re
+from collections.abc import Iterable, Iterator
+from typing import TYPE_CHECKING, AnyStr, overload
+
+if TYPE_CHECKING:
+    from _typeshed import BytesPath, StrOrBytesPath, StrPath
+
+__all__ = ["glob", "iglob", "escape"]
+
+
+def glob(pathname: AnyStr, recursive: bool = False) -> list[AnyStr]:
+    """Return a list of paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    return list(iglob(pathname, recursive=recursive))
+
+
+def iglob(pathname: AnyStr, recursive: bool = False) -> Iterator[AnyStr]:
+    """Return an iterator which yields the paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    it = _iglob(pathname, recursive)
+    if recursive and _isrecursive(pathname):
+        s = next(it)  # skip empty string
+        assert not s
+    return it
+
+
+def _iglob(pathname: AnyStr, recursive: bool) -> Iterator[AnyStr]:
+    dirname, basename = os.path.split(pathname)
+    glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1
+
+    if not has_magic(pathname):
+        if basename:
+            if os.path.lexists(pathname):
+                yield pathname
+        else:
+            # Patterns ending with a slash should match only directories
+            if os.path.isdir(dirname):
+                yield pathname
+        return
+
+    if not dirname:
+        yield from glob_in_dir(dirname, basename)
+        return
+    # `os.path.split()` returns the argument itself as a dirname if it is a
+    # drive or UNC path.  Prevent an infinite recursion if a drive or UNC path
+    # contains magic characters (i.e. r'\\?\C:').
+    if dirname != pathname and has_magic(dirname):
+        dirs: Iterable[AnyStr] = _iglob(dirname, recursive)
+    else:
+        dirs = [dirname]
+    if not has_magic(basename):
+        glob_in_dir = glob0
+    for dirname in dirs:
+        for name in glob_in_dir(dirname, basename):
+            yield os.path.join(dirname, name)
+
+
+# These 2 helper functions non-recursively glob inside a literal directory.
+# They return a list of basenames. `glob1` accepts a pattern while `glob0`
+# takes a literal basename (so it only has to check for its existence).
+
+
+@overload
+def glob1(dirname: StrPath, pattern: str) -> list[str]: ...
+@overload
+def glob1(dirname: BytesPath, pattern: bytes) -> list[bytes]: ...
+def glob1(dirname: StrOrBytesPath, pattern: str | bytes) -> list[str] | list[bytes]:
+    if not dirname:
+        if isinstance(pattern, bytes):
+            dirname = os.curdir.encode('ASCII')
+        else:
+            dirname = os.curdir
+    try:
+        names = os.listdir(dirname)
+    except OSError:
+        return []
+    # mypy false-positives: str or bytes type possibility is always kept in sync
+    return fnmatch.filter(names, pattern)  # type: ignore[type-var, return-value]
+
+
+def glob0(dirname, basename):
+    if not basename:
+        # `os.path.split()` returns an empty basename for paths ending with a
+        # directory separator.  'q*x/' should match only directories.
+        if os.path.isdir(dirname):
+            return [basename]
+    else:
+        if os.path.lexists(os.path.join(dirname, basename)):
+            return [basename]
+    return []
+
+
+# This helper function recursively yields relative pathnames inside a literal
+# directory.
+
+
+@overload
+def glob2(dirname: StrPath, pattern: str) -> Iterator[str]: ...
+@overload
+def glob2(dirname: BytesPath, pattern: bytes) -> Iterator[bytes]: ...
+def glob2(dirname: StrOrBytesPath, pattern: str | bytes) -> Iterator[str | bytes]:
+    assert _isrecursive(pattern)
+    yield pattern[:0]
+    yield from _rlistdir(dirname)
+
+
+# Recursively yields relative pathnames inside a literal directory.
+@overload
+def _rlistdir(dirname: StrPath) -> Iterator[str]: ...
+@overload
+def _rlistdir(dirname: BytesPath) -> Iterator[bytes]: ...
+def _rlistdir(dirname: StrOrBytesPath) -> Iterator[str | bytes]:
+    if not dirname:
+        if isinstance(dirname, bytes):
+            dirname = os.curdir.encode('ASCII')
+        else:
+            dirname = os.curdir
+    try:
+        names = os.listdir(dirname)
+    except OSError:
+        return
+    for x in names:
+        yield x
+        # mypy false-positives: str or bytes type possibility is always kept in sync
+        path = os.path.join(dirname, x) if dirname else x  # type: ignore[arg-type]
+        for y in _rlistdir(path):
+            yield os.path.join(x, y)  # type: ignore[arg-type]
+
+
+magic_check = re.compile('([*?[])')
+magic_check_bytes = re.compile(b'([*?[])')
+
+
+def has_magic(s: str | bytes) -> bool:
+    if isinstance(s, bytes):
+        return magic_check_bytes.search(s) is not None
+    else:
+        return magic_check.search(s) is not None
+
+
+def _isrecursive(pattern: str | bytes) -> bool:
+    if isinstance(pattern, bytes):
+        return pattern == b'**'
+    else:
+        return pattern == '**'
+
+
+def escape(pathname):
+    """Escape all special characters."""
+    # Escaping is done by wrapping any of "*?[" between square brackets.
+    # Metacharacters do not work in the drive part and shouldn't be escaped.
+    drive, pathname = os.path.splitdrive(pathname)
+    if isinstance(pathname, bytes):
+        pathname = magic_check_bytes.sub(rb'[\1]', pathname)
+    else:
+        pathname = magic_check.sub(r'[\1]', pathname)
+    return drive + pathname
diff --git a/.venv/lib/python3.12/site-packages/setuptools/gui-32.exe b/.venv/lib/python3.12/site-packages/setuptools/gui-32.exe
new file mode 100644
index 0000000000000000000000000000000000000000..1eb430c6d614a5daea4139badc09c222a4b0e72a
GIT binary patch
literal 11776
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 metadata.Distribution | metadata.PathDistribution:
+    """Fetch an egg needed for building.
+
+    Use pip/wheel to fetch/build a wheel."""
+    _DeprecatedInstaller.emit()
+    _warn_wheel_not_available(dist)
+    return _fetch_build_egg_no_warn(dist, req)
+
+
+def _present(req):
+    return any(_dist_matches_req(dist, req) for dist in metadata.distributions())
+
+
+def _fetch_build_eggs(dist, requires: _reqs._StrOrIter) -> list[metadata.Distribution]:
+    _DeprecatedInstaller.emit(stacklevel=3)
+    _warn_wheel_not_available(dist)
+
+    parsed_reqs = _reqs.parse(requires)
+
+    missing_reqs = itertools.filterfalse(_present, parsed_reqs)
+
+    needed_reqs = (
+        req for req in missing_reqs if not req.marker or req.marker.evaluate()
+    )
+    resolved_dists = [_fetch_build_egg_no_warn(dist, req) for req in needed_reqs]
+    for dist in resolved_dists:
+        # dist.locate_file('') is the directory containing EGG-INFO, where the importabl
+        # contents can be found.
+        sys.path.insert(0, str(dist.locate_file('')))
+    return resolved_dists
+
+
+def _dist_matches_req(egg_dist, req):
+    return (
+        packaging.utils.canonicalize_name(egg_dist.name)
+        == packaging.utils.canonicalize_name(req.name)
+        and egg_dist.version in req.specifier
+    )
+
+
+def _fetch_build_egg_no_warn(dist, req):  # noqa: C901  # is too complex (16)  # FIXME
+    # Ignore environment markers; if supplied, it is required.
+    req = strip_marker(req)
+    # Take easy_install options into account, but do not override relevant
+    # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
+    # take precedence.
+    opts = dist.get_option_dict('easy_install')
+    if 'allow_hosts' in opts:
+        raise DistutilsError(
+            'the `allow-hosts` option is not supported '
+            'when using pip to install requirements.'
+        )
+    quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ
+    if 'PIP_INDEX_URL' in os.environ:
+        index_url = None
+    elif 'index_url' in opts:
+        index_url = opts['index_url'][1]
+    else:
+        index_url = None
+    find_links = (
+        _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts else []
+    )
+    if dist.dependency_links:
+        find_links.extend(dist.dependency_links)
+    eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
+    cached_dists = metadata.Distribution.discover(path=glob.glob(f'{eggs_dir}/*.egg'))
+    for egg_dist in cached_dists:
+        if _dist_matches_req(egg_dist, req):
+            return egg_dist
+    with tempfile.TemporaryDirectory() as tmpdir:
+        cmd = [
+            sys.executable,
+            '-m',
+            'pip',
+            '--disable-pip-version-check',
+            'wheel',
+            '--no-deps',
+            '-w',
+            tmpdir,
+        ]
+        if quiet:
+            cmd.append('--quiet')
+        if index_url is not None:
+            cmd.extend(('--index-url', index_url))
+        for link in find_links or []:
+            cmd.extend(('--find-links', link))
+        # If requirement is a PEP 508 direct URL, directly pass
+        # the URL to pip, as `req @ url` does not work on the
+        # command line.
+        cmd.append(req.url or str(req))
+        try:
+            subprocess.check_call(cmd)
+        except subprocess.CalledProcessError as e:
+            raise DistutilsError(str(e)) from e
+        wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
+        dist_location = os.path.join(eggs_dir, wheel.egg_name())
+        wheel.install_as_egg(dist_location)
+        return metadata.Distribution.at(dist_location + '/EGG-INFO')
+
+
+def strip_marker(req) -> packaging.requirements.Requirement:
+    """
+    Return a new requirement without the environment marker to avoid
+    calling pip with something like `babel; extra == "i18n"`, which
+    would always be ignored.
+    """
+    # create a copy to avoid mutating the input
+    req = packaging.requirements.Requirement(str(req))
+    req.marker = None
+    return req
+
+
+def _warn_wheel_not_available(dist):
+    try:
+        metadata.distribution('wheel')
+    except metadata.PackageNotFoundError:
+        dist.announce('WARNING: The wheel package is not available.', log.WARN)
+
+
+class _DeprecatedInstaller(SetuptoolsDeprecationWarning):
+    _SUMMARY = "setuptools.installer and fetch_build_eggs are deprecated."
+    _DETAILS = """
+    Requirements should be satisfied by a PEP 517 installer.
+    If you are using pip, you can try `pip install --use-pep517`.
+    """
+    _DUE_DATE = 2025, 10, 31
diff --git a/.venv/lib/python3.12/site-packages/setuptools/launch.py b/.venv/lib/python3.12/site-packages/setuptools/launch.py
new file mode 100644
index 0000000..0d16264
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/launch.py
@@ -0,0 +1,36 @@
+"""
+Launch the Python script on the command line after
+setuptools is bootstrapped via import.
+"""
+
+# Note that setuptools gets imported implicitly by the
+# invocation of this script using python -m setuptools.launch
+
+import sys
+import tokenize
+
+
+def run() -> None:
+    """
+    Run the script in sys.argv[1] as if it had
+    been invoked naturally.
+    """
+    __builtins__
+    script_name = sys.argv[1]
+    namespace = dict(
+        __file__=script_name,
+        __name__='__main__',
+        __doc__=None,
+    )
+    sys.argv[:] = sys.argv[1:]
+
+    open_ = getattr(tokenize, 'open', open)
+    with open_(script_name) as fid:
+        script = fid.read()
+    norm_script = script.replace('\\r\\n', '\\n')
+    code = compile(norm_script, script_name, 'exec')
+    exec(code, namespace)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/launcher manifest.xml b/.venv/lib/python3.12/site-packages/setuptools/launcher manifest.xml
new file mode 100644
index 0000000..5972a96
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/launcher manifest.xml	
@@ -0,0 +1,15 @@
+
+
+    
+    
+    
+        
+            
+                
+            
+        
+    
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/logging.py b/.venv/lib/python3.12/site-packages/setuptools/logging.py
new file mode 100644
index 0000000..532da89
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/logging.py
@@ -0,0 +1,40 @@
+import inspect
+import logging
+import sys
+
+from . import monkey
+
+import distutils.log
+
+
+def _not_warning(record):
+    return record.levelno < logging.WARNING
+
+
+def configure() -> None:
+    """
+    Configure logging to emit warning and above to stderr
+    and everything else to stdout. This behavior is provided
+    for compatibility with distutils.log but may change in
+    the future.
+    """
+    err_handler = logging.StreamHandler()
+    err_handler.setLevel(logging.WARNING)
+    out_handler = logging.StreamHandler(sys.stdout)
+    out_handler.addFilter(_not_warning)
+    handlers = err_handler, out_handler
+    logging.basicConfig(
+        format="{message}", style='{', handlers=handlers, level=logging.DEBUG
+    )
+    if inspect.ismodule(distutils.dist.log):
+        monkey.patch_func(set_threshold, distutils.log, 'set_threshold')
+        # For some reason `distutils.log` module is getting cached in `distutils.dist`
+        # and then loaded again when patched,
+        # implying: id(distutils.log) != id(distutils.dist.log).
+        # Make sure the same module object is used everywhere:
+        distutils.dist.log = distutils.log
+
+
+def set_threshold(level: int) -> int:
+    logging.root.setLevel(level * 10)
+    return set_threshold.unpatched(level)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/modified.py b/.venv/lib/python3.12/site-packages/setuptools/modified.py
new file mode 100644
index 0000000..6ba02fa
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/modified.py
@@ -0,0 +1,18 @@
+try:
+    # Ensure a DistutilsError raised by these methods is the same as distutils.errors.DistutilsError
+    from distutils._modified import (
+        newer,
+        newer_group,
+        newer_pairwise,
+        newer_pairwise_group,
+    )
+except ImportError:
+    # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib, because _modified never existed in stdlib
+    from ._distutils._modified import (
+        newer,
+        newer_group,
+        newer_pairwise,
+        newer_pairwise_group,
+    )
+
+__all__ = ['newer', 'newer_pairwise', 'newer_group', 'newer_pairwise_group']
diff --git a/.venv/lib/python3.12/site-packages/setuptools/monkey.py b/.venv/lib/python3.12/site-packages/setuptools/monkey.py
new file mode 100644
index 0000000..24bb818
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/monkey.py
@@ -0,0 +1,126 @@
+"""
+Monkey patching of distutils.
+"""
+
+from __future__ import annotations
+
+import inspect
+import platform
+import sys
+import types
+from typing import TypeVar, cast, overload
+
+import distutils.filelist
+
+_T = TypeVar("_T")
+_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType)
+
+
+__all__: list[str] = []
+"""
+Everything is private. Contact the project team
+if you think you need this functionality.
+"""
+
+
+def _get_mro(cls):
+    """
+    Returns the bases classes for cls sorted by the MRO.
+
+    Works around an issue on Jython where inspect.getmro will not return all
+    base classes if multiple classes share the same name. Instead, this
+    function will return a tuple containing the class itself, and the contents
+    of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
+    """
+    if platform.python_implementation() == "Jython":
+        return (cls,) + cls.__bases__
+    return inspect.getmro(cls)
+
+
+@overload
+def get_unpatched(item: _UnpatchT) -> _UnpatchT: ...
+@overload
+def get_unpatched(item: object) -> None: ...
+def get_unpatched(
+    item: type | types.FunctionType | object,
+) -> type | types.FunctionType | None:
+    if isinstance(item, type):
+        return get_unpatched_class(item)
+    if isinstance(item, types.FunctionType):
+        return get_unpatched_function(item)
+    return None
+
+
+def get_unpatched_class(cls: type[_T]) -> type[_T]:
+    """Protect against re-patching the distutils if reloaded
+
+    Also ensures that no other distutils extension monkeypatched the distutils
+    first.
+    """
+    external_bases = (
+        cast(type[_T], cls)
+        for cls in _get_mro(cls)
+        if not cls.__module__.startswith('setuptools')
+    )
+    base = next(external_bases)
+    if not base.__module__.startswith('distutils'):
+        msg = f"distutils has already been patched by {cls!r}"
+        raise AssertionError(msg)
+    return base
+
+
+def patch_all() -> None:
+    import setuptools
+
+    # we can't patch distutils.cmd, alas
+    distutils.core.Command = setuptools.Command  # type: ignore[misc,assignment] # monkeypatching
+
+    _patch_distribution_metadata()
+
+    # Install Distribution throughout the distutils
+    for module in distutils.dist, distutils.core, distutils.cmd:
+        module.Distribution = setuptools.dist.Distribution
+
+    # Install the patched Extension
+    distutils.core.Extension = setuptools.extension.Extension  # type: ignore[misc,assignment] # monkeypatching
+    distutils.extension.Extension = setuptools.extension.Extension  # type: ignore[misc,assignment] # monkeypatching
+    if 'distutils.command.build_ext' in sys.modules:
+        sys.modules[
+            'distutils.command.build_ext'
+        ].Extension = setuptools.extension.Extension
+
+
+def _patch_distribution_metadata():
+    from . import _core_metadata
+
+    """Patch write_pkg_file and read_pkg_file for higher metadata standards"""
+    for attr in (
+        'write_pkg_info',
+        'write_pkg_file',
+        'read_pkg_file',
+        'get_metadata_version',
+        'get_fullname',
+    ):
+        new_val = getattr(_core_metadata, attr)
+        setattr(distutils.dist.DistributionMetadata, attr, new_val)
+
+
+def patch_func(replacement, target_mod, func_name) -> None:
+    """
+    Patch func_name in target_mod with replacement
+
+    Important - original must be resolved by name to avoid
+    patching an already patched function.
+    """
+    original = getattr(target_mod, func_name)
+
+    # set the 'unpatched' attribute on the replacement to
+    # point to the original.
+    vars(replacement).setdefault('unpatched', original)
+
+    # replace the function in the original module
+    setattr(target_mod, func_name, replacement)
+
+
+def get_unpatched_function(candidate):
+    return candidate.unpatched
diff --git a/.venv/lib/python3.12/site-packages/setuptools/msvc.py b/.venv/lib/python3.12/site-packages/setuptools/msvc.py
new file mode 100644
index 0000000..f506c82
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/msvc.py
@@ -0,0 +1,1557 @@
+"""
+Environment info about Microsoft Compilers.
+
+>>> getfixture('windows_only')
+>>> ei = EnvironmentInfo('amd64')
+"""
+
+from __future__ import annotations
+
+import contextlib
+import itertools
+import json
+import os
+import os.path
+import platform
+from typing import TYPE_CHECKING, TypedDict, overload
+
+from more_itertools import unique_everseen
+
+from ._path import StrPath
+from .compat import py310
+
+import distutils.errors
+
+if TYPE_CHECKING:
+    from typing_extensions import LiteralString, NotRequired
+
+# https://github.com/python/mypy/issues/8166
+if not TYPE_CHECKING and platform.system() == 'Windows':
+    import winreg
+    from os import environ
+else:
+    # Mock winreg and environ so the module can be imported on this platform.
+
+    class winreg:
+        HKEY_USERS = None
+        HKEY_CURRENT_USER = None
+        HKEY_LOCAL_MACHINE = None
+        HKEY_CLASSES_ROOT = None
+
+    environ: dict[str, str] = dict()
+
+
+class PlatformInfo:
+    """
+    Current and Target Architectures information.
+
+    Parameters
+    ----------
+    arch: str
+        Target architecture.
+    """
+
+    current_cpu = environ.get('processor_architecture', '').lower()
+
+    def __init__(self, arch: str) -> None:
+        self.arch = arch.lower().replace('x64', 'amd64')
+
+    @property
+    def target_cpu(self) -> str:
+        """
+        Return Target CPU architecture.
+
+        Return
+        ------
+        str
+            Target CPU
+        """
+        return self.arch[self.arch.find('_') + 1 :]
+
+    def target_is_x86(self) -> bool:
+        """
+        Return True if target CPU is x86 32 bits..
+
+        Return
+        ------
+        bool
+            CPU is x86 32 bits
+        """
+        return self.target_cpu == 'x86'
+
+    def current_is_x86(self) -> bool:
+        """
+        Return True if current CPU is x86 32 bits..
+
+        Return
+        ------
+        bool
+            CPU is x86 32 bits
+        """
+        return self.current_cpu == 'x86'
+
+    def current_dir(self, hidex86=False, x64=False) -> str:
+        """
+        Current platform specific subfolder.
+
+        Parameters
+        ----------
+        hidex86: bool
+            return '' and not '\x86' if architecture is x86.
+        x64: bool
+            return '\x64' and not '\amd64' if architecture is amd64.
+
+        Return
+        ------
+        str
+            subfolder: '\target', or '' (see hidex86 parameter)
+        """
+        return (
+            ''
+            if (self.current_cpu == 'x86' and hidex86)
+            else r'\x64'
+            if (self.current_cpu == 'amd64' and x64)
+            else rf'\{self.current_cpu}'
+        )
+
+    def target_dir(self, hidex86=False, x64=False) -> str:
+        r"""
+        Target platform specific subfolder.
+
+        Parameters
+        ----------
+        hidex86: bool
+            return '' and not '\x86' if architecture is x86.
+        x64: bool
+            return '\x64' and not '\amd64' if architecture is amd64.
+
+        Return
+        ------
+        str
+            subfolder: '\current', or '' (see hidex86 parameter)
+        """
+        return (
+            ''
+            if (self.target_cpu == 'x86' and hidex86)
+            else r'\x64'
+            if (self.target_cpu == 'amd64' and x64)
+            else rf'\{self.target_cpu}'
+        )
+
+    def cross_dir(self, forcex86=False) -> str:
+        r"""
+        Cross platform specific subfolder.
+
+        Parameters
+        ----------
+        forcex86: bool
+            Use 'x86' as current architecture even if current architecture is
+            not x86.
+
+        Return
+        ------
+        str
+            subfolder: '' if target architecture is current architecture,
+            '\current_target' if not.
+        """
+        current = 'x86' if forcex86 else self.current_cpu
+        return (
+            ''
+            if self.target_cpu == current
+            else self.target_dir().replace('\\', f'\\{current}_')
+        )
+
+
+class RegistryInfo:
+    """
+    Microsoft Visual Studio related registry information.
+
+    Parameters
+    ----------
+    platform_info: PlatformInfo
+        "PlatformInfo" instance.
+    """
+
+    HKEYS = (
+        winreg.HKEY_USERS,
+        winreg.HKEY_CURRENT_USER,
+        winreg.HKEY_LOCAL_MACHINE,
+        winreg.HKEY_CLASSES_ROOT,
+    )
+
+    def __init__(self, platform_info: PlatformInfo) -> None:
+        self.pi = platform_info
+
+    @property
+    def visualstudio(self) -> LiteralString:
+        """
+        Microsoft Visual Studio root registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return 'VisualStudio'
+
+    @property
+    def sxs(self) -> LiteralString:
+        """
+        Microsoft Visual Studio SxS registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.visualstudio, 'SxS')
+
+    @property
+    def vc(self) -> LiteralString:
+        """
+        Microsoft Visual C++ VC7 registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.sxs, 'VC7')
+
+    @property
+    def vs(self) -> LiteralString:
+        """
+        Microsoft Visual Studio VS7 registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.sxs, 'VS7')
+
+    @property
+    def vc_for_python(self) -> LiteralString:
+        """
+        Microsoft Visual C++ for Python registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return r'DevDiv\VCForPython'
+
+    @property
+    def microsoft_sdk(self) -> LiteralString:
+        """
+        Microsoft SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return 'Microsoft SDKs'
+
+    @property
+    def windows_sdk(self) -> LiteralString:
+        """
+        Microsoft Windows/Platform SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.microsoft_sdk, 'Windows')
+
+    @property
+    def netfx_sdk(self) -> LiteralString:
+        """
+        Microsoft .NET Framework SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.microsoft_sdk, 'NETFXSDK')
+
+    @property
+    def windows_kits_roots(self) -> LiteralString:
+        """
+        Microsoft Windows Kits Roots registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return r'Windows Kits\Installed Roots'
+
+    @overload
+    def microsoft(self, key: LiteralString, x86: bool = False) -> LiteralString: ...
+    @overload
+    def microsoft(self, key: str, x86: bool = False) -> str: ...  # type: ignore[misc]
+    def microsoft(self, key: str, x86: bool = False) -> str:
+        """
+        Return key in Microsoft software registry.
+
+        Parameters
+        ----------
+        key: str
+            Registry key path where look.
+        x86: bool
+            Force x86 software registry.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
+        return os.path.join('Software', node64, 'Microsoft', key)
+
+    def lookup(self, key: str, name: str) -> str | None:
+        """
+        Look for values in registry in Microsoft software registry.
+
+        Parameters
+        ----------
+        key: str
+            Registry key path where look.
+        name: str
+            Value name to find.
+
+        Return
+        ------
+        str | None
+            value
+        """
+        key_read = winreg.KEY_READ
+        openkey = winreg.OpenKey
+        closekey = winreg.CloseKey
+        ms = self.microsoft
+        for hkey in self.HKEYS:
+            bkey = None
+            try:
+                bkey = openkey(hkey, ms(key), 0, key_read)
+            except OSError:
+                if not self.pi.current_is_x86():
+                    try:
+                        bkey = openkey(hkey, ms(key, True), 0, key_read)
+                    except OSError:
+                        continue
+                else:
+                    continue
+            try:
+                return winreg.QueryValueEx(bkey, name)[0]
+            except OSError:
+                pass
+            finally:
+                if bkey:
+                    closekey(bkey)
+        return None
+
+
+class SystemInfo:
+    """
+    Microsoft Windows and Visual Studio related system information.
+
+    Parameters
+    ----------
+    registry_info: RegistryInfo
+        "RegistryInfo" instance.
+    vc_ver: float
+        Required Microsoft Visual C++ version.
+    """
+
+    # Variables and properties in this class use originals CamelCase variables
+    # names from Microsoft source files for more easy comparison.
+    WinDir = environ.get('WinDir', '')
+    ProgramFiles = environ.get('ProgramFiles', '')
+    ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
+
+    def __init__(
+        self, registry_info: RegistryInfo, vc_ver: float | None = None
+    ) -> None:
+        self.ri = registry_info
+        self.pi = self.ri.pi
+
+        self.known_vs_paths = self.find_programdata_vs_vers()
+
+        # Except for VS15+, VC version is aligned with VS version
+        self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver()
+
+    def _find_latest_available_vs_ver(self):
+        """
+        Find the latest VC version
+
+        Return
+        ------
+        float
+            version
+        """
+        reg_vc_vers = self.find_reg_vs_vers()
+
+        if not (reg_vc_vers or self.known_vs_paths):
+            raise distutils.errors.DistutilsPlatformError(
+                'No Microsoft Visual C++ version found'
+            )
+
+        vc_vers = set(reg_vc_vers)
+        vc_vers.update(self.known_vs_paths)
+        return max(vc_vers)
+
+    def find_reg_vs_vers(self) -> list[float]:
+        """
+        Find Microsoft Visual Studio versions available in registry.
+
+        Return
+        ------
+        list of float
+            Versions
+        """
+        ms = self.ri.microsoft
+        vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
+        vs_vers = []
+        for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
+            try:
+                bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
+            except OSError:
+                continue
+            with bkey:
+                subkeys, values, _ = winreg.QueryInfoKey(bkey)
+                for i in range(values):
+                    with contextlib.suppress(ValueError):
+                        ver = float(winreg.EnumValue(bkey, i)[0])
+                        if ver not in vs_vers:
+                            vs_vers.append(ver)
+                for i in range(subkeys):
+                    with contextlib.suppress(ValueError):
+                        ver = float(winreg.EnumKey(bkey, i))
+                        if ver not in vs_vers:
+                            vs_vers.append(ver)
+        return sorted(vs_vers)
+
+    def find_programdata_vs_vers(self) -> dict[float, str]:
+        r"""
+        Find Visual studio 2017+ versions from information in
+        "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
+
+        Return
+        ------
+        dict
+            float version as key, path as value.
+        """
+        vs_versions: dict[float, str] = {}
+        instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
+
+        try:
+            hashed_names = os.listdir(instances_dir)
+
+        except OSError:
+            # Directory not exists with all Visual Studio versions
+            return vs_versions
+
+        for name in hashed_names:
+            try:
+                # Get VS installation path from "state.json" file
+                state_path = os.path.join(instances_dir, name, 'state.json')
+                with open(state_path, 'rt', encoding='utf-8') as state_file:
+                    state = json.load(state_file)
+                vs_path = state['installationPath']
+
+                # Raises OSError if this VS installation does not contain VC
+                os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC'))
+
+                # Store version and path
+                vs_versions[self._as_float_version(state['installationVersion'])] = (
+                    vs_path
+                )
+
+            except (OSError, KeyError):
+                # Skip if "state.json" file is missing or bad format
+                continue
+
+        return vs_versions
+
+    @staticmethod
+    def _as_float_version(version):
+        """
+        Return a string version as a simplified float version (major.minor)
+
+        Parameters
+        ----------
+        version: str
+            Version.
+
+        Return
+        ------
+        float
+            version
+        """
+        return float('.'.join(version.split('.')[:2]))
+
+    @property
+    def VSInstallDir(self) -> str:
+        """
+        Microsoft Visual Studio directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        default = os.path.join(
+            self.ProgramFilesx86, f'Microsoft Visual Studio {self.vs_ver:0.1f}'
+        )
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vs, f'{self.vs_ver:0.1f}') or default
+
+    @property
+    def VCInstallDir(self) -> str:
+        """
+        Microsoft Visual C++ directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        path = self._guess_vc() or self._guess_vc_legacy()
+
+        if not os.path.isdir(path):
+            msg = 'Microsoft Visual C++ directory not found'
+            raise distutils.errors.DistutilsPlatformError(msg)
+
+        return path
+
+    def _guess_vc(self):
+        """
+        Locate Visual C++ for VS2017+.
+
+        Return
+        ------
+        str
+            path
+        """
+        if self.vs_ver <= 14.0:
+            return ''
+
+        try:
+            # First search in known VS paths
+            vs_dir = self.known_vs_paths[self.vs_ver]
+        except KeyError:
+            # Else, search with path from registry
+            vs_dir = self.VSInstallDir
+
+        guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC')
+
+        # Subdir with VC exact version as name
+        try:
+            # Update the VC version with real one instead of VS version
+            vc_ver = os.listdir(guess_vc)[-1]
+            self.vc_ver = self._as_float_version(vc_ver)
+            return os.path.join(guess_vc, vc_ver)
+        except (OSError, IndexError):
+            return ''
+
+    def _guess_vc_legacy(self):
+        """
+        Locate Visual C++ for versions prior to 2017.
+
+        Return
+        ------
+        str
+            path
+        """
+        default = os.path.join(
+            self.ProgramFilesx86,
+            rf'Microsoft Visual Studio {self.vs_ver:0.1f}\VC',
+        )
+
+        # Try to get "VC++ for Python" path from registry as default path
+        reg_path = os.path.join(self.ri.vc_for_python, f'{self.vs_ver:0.1f}')
+        python_vc = self.ri.lookup(reg_path, 'installdir')
+        default_vc = os.path.join(python_vc, 'VC') if python_vc else default
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, f'{self.vs_ver:0.1f}') or default_vc
+
+    @property
+    def WindowsSdkVersion(self) -> tuple[LiteralString, ...]:
+        """
+        Microsoft Windows SDK versions for specified MSVC++ version.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        if self.vs_ver <= 9.0:
+            return '7.0', '6.1', '6.0a'
+        elif self.vs_ver == 10.0:
+            return '7.1', '7.0a'
+        elif self.vs_ver == 11.0:
+            return '8.0', '8.0a'
+        elif self.vs_ver == 12.0:
+            return '8.1', '8.1a'
+        elif self.vs_ver >= 14.0:
+            return '10.0', '8.1'
+        return ()
+
+    @property
+    def WindowsSdkLastVersion(self) -> str:
+        """
+        Microsoft Windows SDK last version.
+
+        Return
+        ------
+        str
+            version
+        """
+        return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))
+
+    @property
+    def WindowsSdkDir(self) -> str:  # noqa: C901  # is too complex (12)  # FIXME
+        """
+        Microsoft Windows SDK directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        sdkdir: str | None = ''
+        for ver in self.WindowsSdkVersion:
+            # Try to get it from registry
+            loc = os.path.join(self.ri.windows_sdk, f'v{ver}')
+            sdkdir = self.ri.lookup(loc, 'installationfolder')
+            if sdkdir:
+                break
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # Try to get "VC++ for Python" version from registry
+            path = os.path.join(self.ri.vc_for_python, f'{self.vc_ver:0.1f}')
+            install_base = self.ri.lookup(path, 'installdir')
+            if install_base:
+                sdkdir = os.path.join(install_base, 'WinSDK')
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # If fail, use default new path
+            for ver in self.WindowsSdkVersion:
+                intver = ver[: ver.rfind('.')]
+                path = rf'Microsoft SDKs\Windows Kits\{intver}'
+                d = os.path.join(self.ProgramFiles, path)
+                if os.path.isdir(d):
+                    sdkdir = d
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # If fail, use default old path
+            for ver in self.WindowsSdkVersion:
+                path = rf'Microsoft SDKs\Windows\v{ver}'
+                d = os.path.join(self.ProgramFiles, path)
+                if os.path.isdir(d):
+                    sdkdir = d
+        if not sdkdir:
+            # If fail, use Platform SDK
+            sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
+        return sdkdir
+
+    @property
+    def WindowsSDKExecutablePath(self) -> str | None:
+        """
+        Microsoft Windows SDK executable directory.
+
+        Return
+        ------
+        str | None
+            path
+        """
+        # Find WinSDK NetFx Tools registry dir name
+        if self.vs_ver <= 11.0:
+            netfxver = 35
+            arch = ''
+        else:
+            netfxver = 40
+            hidex86 = True if self.vs_ver <= 12.0 else False
+            arch = self.pi.current_dir(x64=True, hidex86=hidex86).replace('\\', '-')
+        fx = f'WinSDK-NetFx{netfxver}Tools{arch}'
+
+        # list all possibles registry paths
+        regpaths = []
+        if self.vs_ver >= 14.0:
+            for ver in self.NetFxSdkVersion:
+                regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
+
+        for ver in self.WindowsSdkVersion:
+            regpaths += [os.path.join(self.ri.windows_sdk, f'v{ver}A', fx)]
+
+        # Return installation folder from the more recent path
+        for path in regpaths:
+            execpath = self.ri.lookup(path, 'installationfolder')
+            if execpath:
+                return execpath
+
+        return None
+
+    @property
+    def FSharpInstallDir(self) -> str:
+        """
+        Microsoft Visual F# directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        path = os.path.join(self.ri.visualstudio, rf'{self.vs_ver:0.1f}\Setup\F#')
+        return self.ri.lookup(path, 'productdir') or ''
+
+    @property
+    def UniversalCRTSdkDir(self) -> str | None:
+        """
+        Microsoft Universal CRT SDK directory.
+
+        Return
+        ------
+        str | None
+            path
+        """
+        # Set Kit Roots versions for specified MSVC++ version
+        vers = ('10', '81') if self.vs_ver >= 14.0 else ()
+
+        # Find path of the more recent Kit
+        for ver in vers:
+            sdkdir = self.ri.lookup(self.ri.windows_kits_roots, f'kitsroot{ver}')
+            if sdkdir:
+                return sdkdir
+
+        return None
+
+    @property
+    def UniversalCRTSdkLastVersion(self) -> str:
+        """
+        Microsoft Universal C Runtime SDK last version.
+
+        Return
+        ------
+        str
+            version
+        """
+        try:
+            return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib'))  # type: ignore[arg-type] # Expected TypeError
+        except TypeError as ex:
+            py310.add_note(ex, "Cannot find UniversalCRTSdkDir")
+            raise
+
+    @property
+    def NetFxSdkVersion(self) -> tuple[LiteralString, ...]:
+        """
+        Microsoft .NET Framework SDK versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        # Set FxSdk versions for specified VS version
+        return (
+            ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5')
+            if self.vs_ver >= 14.0
+            else ()
+        )
+
+    @property
+    def NetFxSdkDir(self) -> str | None:
+        """
+        Microsoft .NET Framework SDK directory.
+
+        Return
+        ------
+        str | None
+            path
+        """
+        sdkdir: str | None = ''
+        for ver in self.NetFxSdkVersion:
+            loc = os.path.join(self.ri.netfx_sdk, ver)
+            sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
+            if sdkdir:
+                break
+        return sdkdir
+
+    @property
+    def FrameworkDir32(self) -> str:
+        """
+        Microsoft .NET Framework 32bit directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
+
+    @property
+    def FrameworkDir64(self) -> str:
+        """
+        Microsoft .NET Framework 64bit directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
+
+    @property
+    def FrameworkVersion32(self) -> tuple[str, ...]:
+        """
+        Microsoft .NET Framework 32bit versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        return self._find_dot_net_versions(32)
+
+    @property
+    def FrameworkVersion64(self) -> tuple[str, ...]:
+        """
+        Microsoft .NET Framework 64bit versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        return self._find_dot_net_versions(64)
+
+    def _find_dot_net_versions(self, bits) -> tuple[str, ...]:
+        """
+        Find Microsoft .NET Framework versions.
+
+        Parameters
+        ----------
+        bits: int
+            Platform number of bits: 32 or 64.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        # Find actual .NET version in registry
+        reg_ver = self.ri.lookup(self.ri.vc, f'frameworkver{bits}')
+        dot_net_dir = getattr(self, f'FrameworkDir{bits}')
+        ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
+
+        # Set .NET versions for specified MSVC++ version
+        if self.vs_ver >= 12.0:
+            return ver, 'v4.0'
+        elif self.vs_ver >= 10.0:
+            return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
+        elif self.vs_ver == 9.0:
+            return 'v3.5', 'v2.0.50727'
+        elif self.vs_ver == 8.0:
+            return 'v3.0', 'v2.0.50727'
+        return ()
+
+    @staticmethod
+    def _use_last_dir_name(path: StrPath, prefix: str = '') -> str:
+        """
+        Return name of the last dir in path or '' if no dir found.
+
+        Parameters
+        ----------
+        path: StrPath
+            Use dirs in this path
+        prefix: str
+            Use only dirs starting by this prefix
+
+        Return
+        ------
+        str
+            name
+        """
+        matching_dirs = (
+            dir_name
+            for dir_name in reversed(os.listdir(path))
+            if os.path.isdir(os.path.join(path, dir_name))
+            and dir_name.startswith(prefix)
+        )
+        return next(matching_dirs, '')
+
+
+class _EnvironmentDict(TypedDict):
+    include: str
+    lib: str
+    libpath: str
+    path: str
+    py_vcruntime_redist: NotRequired[str | None]
+
+
+class EnvironmentInfo:
+    """
+    Return environment variables for specified Microsoft Visual C++ version
+    and platform : Lib, Include, Path and libpath.
+
+    This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
+
+    Script created by analysing Microsoft environment configuration files like
+    "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
+
+    Parameters
+    ----------
+    arch: str
+        Target architecture.
+    vc_ver: float
+        Required Microsoft Visual C++ version. If not set, autodetect the last
+        version.
+    vc_min_ver: float
+        Minimum Microsoft Visual C++ version.
+    """
+
+    # Variables and properties in this class use originals CamelCase variables
+    # names from Microsoft source files for more easy comparison.
+
+    def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None:
+        self.pi = PlatformInfo(arch)
+        self.ri = RegistryInfo(self.pi)
+        self.si = SystemInfo(self.ri, vc_ver)
+
+        if self.vc_ver < vc_min_ver:
+            err = 'No suitable Microsoft Visual C++ version found'
+            raise distutils.errors.DistutilsPlatformError(err)
+
+    @property
+    def vs_ver(self):
+        """
+        Microsoft Visual Studio.
+
+        Return
+        ------
+        float
+            version
+        """
+        return self.si.vs_ver
+
+    @property
+    def vc_ver(self):
+        """
+        Microsoft Visual C++ version.
+
+        Return
+        ------
+        float
+            version
+        """
+        return self.si.vc_ver
+
+    @property
+    def VSTools(self):
+        """
+        Microsoft Visual Studio Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        paths = [r'Common7\IDE', r'Common7\Tools']
+
+        if self.vs_ver >= 14.0:
+            arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
+            paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
+            paths += [r'Team Tools\Performance Tools']
+            paths += [rf'Team Tools\Performance Tools{arch_subdir}']
+
+        return [os.path.join(self.si.VSInstallDir, path) for path in paths]
+
+    @property
+    def VCIncludes(self):
+        """
+        Microsoft Visual C++ & Microsoft Foundation Class Includes.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return [
+            os.path.join(self.si.VCInstallDir, 'Include'),
+            os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'),
+        ]
+
+    @property
+    def VCLibraries(self):
+        """
+        Microsoft Visual C++ & Microsoft Foundation Class Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver >= 15.0:
+            arch_subdir = self.pi.target_dir(x64=True)
+        else:
+            arch_subdir = self.pi.target_dir(hidex86=True)
+        paths = [f'Lib{arch_subdir}', rf'ATLMFC\Lib{arch_subdir}']
+
+        if self.vs_ver >= 14.0:
+            paths += [rf'Lib\store{arch_subdir}']
+
+        return [os.path.join(self.si.VCInstallDir, path) for path in paths]
+
+    @property
+    def VCStoreRefs(self):
+        """
+        Microsoft Visual C++ store references Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+        return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
+
+    @property
+    def VCTools(self):
+        """
+        Microsoft Visual C++ Tools.
+
+        Return
+        ------
+        list of str
+            paths
+
+        When host CPU is ARM, the tools should be found for ARM.
+
+        >>> getfixture('windows_only')
+        >>> mp = getfixture('monkeypatch')
+        >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64')
+        >>> ei = EnvironmentInfo(arch='irrelevant')
+        >>> paths = ei.VCTools
+        >>> any('HostARM64' in path for path in paths)
+        True
+        """
+        si = self.si
+        tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
+
+        forcex86 = True if self.vs_ver <= 10.0 else False
+        arch_subdir = self.pi.cross_dir(forcex86)
+        if arch_subdir:
+            tools += [os.path.join(si.VCInstallDir, f'Bin{arch_subdir}')]
+
+        if self.vs_ver == 14.0:
+            path = f'Bin{self.pi.current_dir(hidex86=True)}'
+            tools += [os.path.join(si.VCInstallDir, path)]
+
+        elif self.vs_ver >= 15.0:
+            host_id = self.pi.current_cpu.replace('amd64', 'x64').upper()
+            host_dir = os.path.join('bin', f'Host{host_id}%s')
+            tools += [
+                os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))
+            ]
+
+            if self.pi.current_cpu != self.pi.target_cpu:
+                tools += [
+                    os.path.join(
+                        si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)
+                    )
+                ]
+
+        else:
+            tools += [os.path.join(si.VCInstallDir, 'Bin')]
+
+        return tools
+
+    @property
+    def OSLibraries(self):
+        """
+        Microsoft Windows SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver <= 10.0:
+            arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
+            return [os.path.join(self.si.WindowsSdkDir, f'Lib{arch_subdir}')]
+
+        else:
+            arch_subdir = self.pi.target_dir(x64=True)
+            lib = os.path.join(self.si.WindowsSdkDir, 'lib')
+            libver = self._sdk_subdir
+            return [os.path.join(lib, f'{libver}um{arch_subdir}')]
+
+    @property
+    def OSIncludes(self):
+        """
+        Microsoft Windows SDK Include.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        include = os.path.join(self.si.WindowsSdkDir, 'include')
+
+        if self.vs_ver <= 10.0:
+            return [include, os.path.join(include, 'gl')]
+
+        else:
+            if self.vs_ver >= 14.0:
+                sdkver = self._sdk_subdir
+            else:
+                sdkver = ''
+            return [
+                os.path.join(include, f'{sdkver}shared'),
+                os.path.join(include, f'{sdkver}um'),
+                os.path.join(include, f'{sdkver}winrt'),
+            ]
+
+    @property
+    def OSLibpath(self):
+        """
+        Microsoft Windows SDK Libraries Paths.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        ref = os.path.join(self.si.WindowsSdkDir, 'References')
+        libpath = []
+
+        if self.vs_ver <= 9.0:
+            libpath += self.OSLibraries
+
+        if self.vs_ver >= 11.0:
+            libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
+
+        if self.vs_ver >= 14.0:
+            libpath += [
+                ref,
+                os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
+                os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
+                os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
+                os.path.join(
+                    ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'
+                ),
+                os.path.join(
+                    self.si.WindowsSdkDir,
+                    'ExtensionSDKs',
+                    'Microsoft.VCLibs',
+                    f'{self.vs_ver:0.1f}',
+                    'References',
+                    'CommonConfiguration',
+                    'neutral',
+                ),
+            ]
+        return libpath
+
+    @property
+    def SdkTools(self):
+        """
+        Microsoft Windows SDK Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return list(self._sdk_tools())
+
+    def _sdk_tools(self):
+        """
+        Microsoft Windows SDK Tools paths generator.
+
+        Return
+        ------
+        generator of str
+            paths
+        """
+        if self.vs_ver < 15.0:
+            bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
+            yield os.path.join(self.si.WindowsSdkDir, bin_dir)
+
+        if not self.pi.current_is_x86():
+            arch_subdir = self.pi.current_dir(x64=True)
+            path = f'Bin{arch_subdir}'
+            yield os.path.join(self.si.WindowsSdkDir, path)
+
+        if self.vs_ver in (10.0, 11.0):
+            if self.pi.target_is_x86():
+                arch_subdir = ''
+            else:
+                arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
+            path = rf'Bin\NETFX 4.0 Tools{arch_subdir}'
+            yield os.path.join(self.si.WindowsSdkDir, path)
+
+        elif self.vs_ver >= 15.0:
+            path = os.path.join(self.si.WindowsSdkDir, 'Bin')
+            arch_subdir = self.pi.current_dir(x64=True)
+            sdkver = self.si.WindowsSdkLastVersion
+            yield os.path.join(path, f'{sdkver}{arch_subdir}')
+
+        if self.si.WindowsSDKExecutablePath:
+            yield self.si.WindowsSDKExecutablePath
+
+    @property
+    def _sdk_subdir(self) -> str:
+        """
+        Microsoft Windows SDK version subdir.
+
+        Return
+        ------
+        str
+            subdir
+        """
+        ucrtver = self.si.WindowsSdkLastVersion
+        return (f'{ucrtver}\\') if ucrtver else ''
+
+    @property
+    def SdkSetup(self):
+        """
+        Microsoft Windows SDK Setup.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver > 9.0:
+            return []
+
+        return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
+
+    @property
+    def FxTools(self):
+        """
+        Microsoft .NET Framework Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        pi = self.pi
+        si = self.si
+
+        if self.vs_ver <= 10.0:
+            include32 = True
+            include64 = not pi.target_is_x86() and not pi.current_is_x86()
+        else:
+            include32 = pi.target_is_x86() or pi.current_is_x86()
+            include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
+
+        tools = []
+        if include32:
+            tools += [
+                os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32
+            ]
+        if include64:
+            tools += [
+                os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64
+            ]
+        return tools
+
+    @property
+    def NetFxSDKLibraries(self):
+        """
+        Microsoft .Net Framework SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
+            return []
+
+        arch_subdir = self.pi.target_dir(x64=True)
+        return [os.path.join(self.si.NetFxSdkDir, rf'lib\um{arch_subdir}')]
+
+    @property
+    def NetFxSDKIncludes(self):
+        """
+        Microsoft .Net Framework SDK Includes.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
+            return []
+
+        return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
+
+    @property
+    def VsTDb(self):
+        """
+        Microsoft Visual Studio Team System Database.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
+
+    @property
+    def MSBuild(self):
+        """
+        Microsoft Build Engine.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 12.0:
+            return []
+        elif self.vs_ver < 15.0:
+            base_path = self.si.ProgramFilesx86
+            arch_subdir = self.pi.current_dir(hidex86=True)
+        else:
+            base_path = self.si.VSInstallDir
+            arch_subdir = ''
+
+        path = rf'MSBuild\{self.vs_ver:0.1f}\bin{arch_subdir}'
+        build = [os.path.join(base_path, path)]
+
+        if self.vs_ver >= 15.0:
+            # Add Roslyn C# & Visual Basic Compiler
+            build += [os.path.join(base_path, path, 'Roslyn')]
+
+        return build
+
+    @property
+    def HTMLHelpWorkshop(self):
+        """
+        Microsoft HTML Help Workshop.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 11.0:
+            return []
+
+        return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
+
+    @property
+    def UCRTLibraries(self) -> list[str]:
+        """
+        Microsoft Universal C Runtime SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+
+        arch_subdir = self.pi.target_dir(x64=True)
+        try:
+            lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')  # type: ignore[arg-type] # Expected TypeError
+        except TypeError as ex:
+            py310.add_note(ex, "Cannot find UniversalCRTSdkDir")
+            raise
+        ucrtver = self._ucrt_subdir
+        return [os.path.join(lib, f'{ucrtver}ucrt{arch_subdir}')]
+
+    @property
+    def UCRTIncludes(self) -> list[str]:
+        """
+        Microsoft Universal C Runtime SDK Include.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+
+        try:
+            include = os.path.join(self.si.UniversalCRTSdkDir, 'include')  # type: ignore[arg-type] # Expected TypeError
+        except TypeError as ex:
+            py310.add_note(ex, "Cannot find UniversalCRTSdkDir")
+            raise
+        return [os.path.join(include, f'{self._ucrt_subdir}ucrt')]
+
+    @property
+    def _ucrt_subdir(self) -> str:
+        """
+        Microsoft Universal C Runtime SDK version subdir.
+
+        Return
+        ------
+        str
+            subdir
+        """
+        ucrtver = self.si.UniversalCRTSdkLastVersion
+        return (f'{ucrtver}\\') if ucrtver else ''
+
+    @property
+    def FSharp(self):
+        """
+        Microsoft Visual F#.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if 11.0 > self.vs_ver > 12.0:
+            return []
+
+        return [self.si.FSharpInstallDir]
+
+    @property
+    def VCRuntimeRedist(self) -> str | None:
+        """
+        Microsoft Visual C++ runtime redistributable dll.
+
+        Returns the first suitable path found or None.
+        """
+        vcruntime = f'vcruntime{self.vc_ver}0.dll'
+        arch_subdir = self.pi.target_dir(x64=True).strip('\\')
+
+        # Installation prefixes candidates
+        prefixes = []
+        tools_path = self.si.VCInstallDir
+        redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist'))
+        if os.path.isdir(redist_path):
+            # Redist version may not be exactly the same as tools
+            redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1])
+            prefixes += [redist_path, os.path.join(redist_path, 'onecore')]
+
+        prefixes += [os.path.join(tools_path, 'redist')]  # VS14 legacy path
+
+        # CRT directory
+        crt_dirs = (
+            f'Microsoft.VC{self.vc_ver * 10}.CRT',
+            # Sometime store in directory with VS version instead of VC
+            f'Microsoft.VC{int(self.vs_ver) * 10}.CRT',
+        )
+
+        # vcruntime path
+        candidate_paths = (
+            os.path.join(prefix, arch_subdir, crt_dir, vcruntime)
+            for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs)
+        )
+        return next(filter(os.path.isfile, candidate_paths), None)  # type: ignore[arg-type] #python/mypy#12682
+
+    def return_env(self, exists: bool = True) -> _EnvironmentDict:
+        """
+        Return environment dict.
+
+        Parameters
+        ----------
+        exists: bool
+            It True, only return existing paths.
+
+        Return
+        ------
+        dict
+            environment
+        """
+        env = _EnvironmentDict(
+            include=self._build_paths(
+                'include',
+                [
+                    self.VCIncludes,
+                    self.OSIncludes,
+                    self.UCRTIncludes,
+                    self.NetFxSDKIncludes,
+                ],
+                exists,
+            ),
+            lib=self._build_paths(
+                'lib',
+                [
+                    self.VCLibraries,
+                    self.OSLibraries,
+                    self.FxTools,
+                    self.UCRTLibraries,
+                    self.NetFxSDKLibraries,
+                ],
+                exists,
+            ),
+            libpath=self._build_paths(
+                'libpath',
+                [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath],
+                exists,
+            ),
+            path=self._build_paths(
+                'path',
+                [
+                    self.VCTools,
+                    self.VSTools,
+                    self.VsTDb,
+                    self.SdkTools,
+                    self.SdkSetup,
+                    self.FxTools,
+                    self.MSBuild,
+                    self.HTMLHelpWorkshop,
+                    self.FSharp,
+                ],
+                exists,
+            ),
+        )
+        if self.vs_ver >= 14 and self.VCRuntimeRedist:
+            env['py_vcruntime_redist'] = self.VCRuntimeRedist
+        return env
+
+    def _build_paths(self, name, spec_path_lists, exists):
+        """
+        Given an environment variable name and specified paths,
+        return a pathsep-separated string of paths containing
+        unique, extant, directories from those paths and from
+        the environment variable. Raise an error if no paths
+        are resolved.
+
+        Parameters
+        ----------
+        name: str
+            Environment variable name
+        spec_path_lists: list of str
+            Paths
+        exists: bool
+            It True, only return existing paths.
+
+        Return
+        ------
+        str
+            Pathsep-separated paths
+        """
+        # flatten spec_path_lists
+        spec_paths = itertools.chain.from_iterable(spec_path_lists)
+        env_paths = environ.get(name, '').split(os.pathsep)
+        paths = itertools.chain(spec_paths, env_paths)
+        extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
+        if not extant_paths:
+            msg = f"{name.upper()} environment variable is empty"
+            raise distutils.errors.DistutilsPlatformError(msg)
+        unique_paths = unique_everseen(extant_paths)
+        return os.pathsep.join(unique_paths)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/namespaces.py b/.venv/lib/python3.12/site-packages/setuptools/namespaces.py
new file mode 100644
index 0000000..7760f07
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/namespaces.py
@@ -0,0 +1,101 @@
+import itertools
+import os
+
+from .compat import py312
+
+from distutils import log
+
+flatten = itertools.chain.from_iterable
+
+
+class Installer:
+    nspkg_ext = '-nspkg.pth'
+
+    def install_namespaces(self) -> None:
+        nsp = self._get_all_ns_packages()
+        if not nsp:
+            return
+        filename = self._get_nspkg_file()
+        self.outputs.append(filename)
+        log.info("Installing %s", filename)
+        lines = map(self._gen_nspkg_line, nsp)
+
+        with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f:
+            # Python<3.13 requires encoding="locale" instead of "utf-8"
+            # See: python/cpython#77102
+            f.writelines(lines)
+
+    def uninstall_namespaces(self) -> None:
+        filename = self._get_nspkg_file()
+        if not os.path.exists(filename):
+            return
+        log.info("Removing %s", filename)
+        os.remove(filename)
+
+    def _get_nspkg_file(self):
+        filename, _ = os.path.splitext(self._get_target())
+        return filename + self.nspkg_ext
+
+    def _get_target(self):
+        return self.target
+
+    _nspkg_tmpl = (
+        "import sys, types, os",
+        "p = os.path.join(%(root)s, *%(pth)r)",
+        "importlib = __import__('importlib.util')",
+        "__import__('importlib.machinery')",
+        (
+            "m = "
+            "sys.modules.setdefault(%(pkg)r, "
+            "importlib.util.module_from_spec("
+            "importlib.machinery.PathFinder.find_spec(%(pkg)r, "
+            "[os.path.dirname(p)])))"
+        ),
+        ("m = m or sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"),
+        "mp = (m or []) and m.__dict__.setdefault('__path__',[])",
+        "(p not in mp) and mp.append(p)",
+    )
+    "lines for the namespace installer"
+
+    _nspkg_tmpl_multi = ('m and setattr(sys.modules[%(parent)r], %(child)r, m)',)
+    "additional line(s) when a parent package is indicated"
+
+    def _get_root(self):
+        return "sys._getframe(1).f_locals['sitedir']"
+
+    def _gen_nspkg_line(self, pkg):
+        pth = tuple(pkg.split('.'))
+        root = self._get_root()
+        tmpl_lines = self._nspkg_tmpl
+        parent, sep, child = pkg.rpartition('.')
+        if parent:
+            tmpl_lines += self._nspkg_tmpl_multi
+        return ';'.join(tmpl_lines) % locals() + '\n'
+
+    def _get_all_ns_packages(self):
+        """Return sorted list of all package namespaces"""
+        pkgs = self.distribution.namespace_packages or []
+        return sorted(set(flatten(map(self._pkg_names, pkgs))))
+
+    @staticmethod
+    def _pkg_names(pkg):
+        """
+        Given a namespace package, yield the components of that
+        package.
+
+        >>> names = Installer._pkg_names('a.b.c')
+        >>> set(names) == set(['a', 'a.b', 'a.b.c'])
+        True
+        """
+        parts = pkg.split('.')
+        while parts:
+            yield '.'.join(parts)
+            parts.pop()
+
+
+class DevelopInstaller(Installer):
+    def _get_root(self):
+        return repr(str(self.egg_path))
+
+    def _get_target(self):
+        return self.egg_link
diff --git a/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl b/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl
new file mode 100644
index 0000000..39a24b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl	
@@ -0,0 +1,6 @@
+# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r
+__requires__ = %(spec)r
+__import__('pkg_resources').require(%(spec)r)
+__file__ = %(dev_path)r
+with open(__file__) as f:
+    exec(compile(f.read(), __file__, 'exec'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/script.tmpl b/.venv/lib/python3.12/site-packages/setuptools/script.tmpl
new file mode 100644
index 0000000..ff5efbc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/script.tmpl
@@ -0,0 +1,3 @@
+# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r
+__requires__ = %(spec)r
+__import__('pkg_resources').run_script(%(spec)r, %(script_name)r)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/tests/__init__.py
new file mode 100644
index 0000000..eb70bfb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/__init__.py
@@ -0,0 +1,13 @@
+import locale
+import sys
+
+import pytest
+
+__all__ = ['fail_on_ascii']
+
+if sys.version_info >= (3, 11):
+    locale_encoding = locale.getencoding()
+else:
+    locale_encoding = locale.getpreferredencoding(False)
+is_ascii = locale_encoding == 'ANSI_X3.4-1968'
+fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/compat/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/tests/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/compat/py39.py b/.venv/lib/python3.12/site-packages/setuptools/tests/compat/py39.py
new file mode 100644
index 0000000..1fdb9da
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/compat/py39.py
@@ -0,0 +1,3 @@
+from jaraco.test.cpython import from_test_support, try_import
+
+os_helper = try_import('os_helper') or from_test_support('can_symlink')
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py
new file mode 100644
index 0000000..00a1642
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/__init__.py
@@ -0,0 +1,59 @@
+from __future__ import annotations
+
+import re
+import time
+from pathlib import Path
+from urllib.error import HTTPError
+from urllib.request import urlopen
+
+__all__ = ["DOWNLOAD_DIR", "retrieve_file", "output_file", "urls_from_file"]
+
+
+NAME_REMOVE = ("http://", "https://", "github.com/", "/raw/")
+DOWNLOAD_DIR = Path(__file__).parent
+
+
+# ----------------------------------------------------------------------
+# Please update ./preload.py accordingly when modifying this file
+# ----------------------------------------------------------------------
+
+
+def output_file(url: str, download_dir: Path = DOWNLOAD_DIR) -> Path:
+    file_name = url.strip()
+    for part in NAME_REMOVE:
+        file_name = file_name.replace(part, '').strip().strip('/:').strip()
+    return Path(download_dir, re.sub(r"[^\-_\.\w\d]+", "_", file_name))
+
+
+def retrieve_file(url: str, download_dir: Path = DOWNLOAD_DIR, wait: float = 5) -> Path:
+    path = output_file(url, download_dir)
+    if path.exists():
+        print(f"Skipping {url} (already exists: {path})")
+    else:
+        download_dir.mkdir(exist_ok=True, parents=True)
+        print(f"Downloading {url} to {path}")
+        try:
+            download(url, path)
+        except HTTPError:
+            time.sleep(wait)  # wait a few seconds and try again.
+            download(url, path)
+    return path
+
+
+def urls_from_file(list_file: Path) -> list[str]:
+    """``list_file`` should be a text file where each line corresponds to a URL to
+    download.
+    """
+    print(f"file: {list_file}")
+    content = list_file.read_text(encoding="utf-8")
+    return [url for url in content.splitlines() if not url.startswith("#")]
+
+
+def download(url: str, dest: Path):
+    with urlopen(url) as f:
+        data = f.read()
+
+    with open(dest, "wb") as f:
+        f.write(data)
+
+    assert Path(dest).exists()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py
new file mode 100644
index 0000000..8eeb5dd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/downloads/preload.py
@@ -0,0 +1,18 @@
+"""This file can be used to preload files needed for testing.
+
+For example you can use::
+
+    cd setuptools/tests/config
+    python -m downloads.preload setupcfg_examples.txt
+
+to make sure the `setup.cfg` examples are downloaded before starting the tests.
+"""
+
+import sys
+from pathlib import Path
+
+from . import retrieve_file, urls_from_file
+
+if __name__ == "__main__":
+    urls = urls_from_file(Path(sys.argv[1]))
+    list(map(retrieve_file, urls))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt b/.venv/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt
new file mode 100644
index 0000000..6aab887
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/setupcfg_examples.txt
@@ -0,0 +1,22 @@
+# ====================================================================
+# Some popular packages that use setup.cfg (and others not so popular)
+# Reference: https://hugovk.github.io/top-pypi-packages/
+# ====================================================================
+https://github.com/pypa/setuptools/raw/52c990172fec37766b3566679724aa8bf70ae06d/setup.cfg
+https://github.com/pypa/wheel/raw/0acd203cd896afec7f715aa2ff5980a403459a3b/setup.cfg
+https://github.com/python/importlib_metadata/raw/2f05392ca980952a6960d82b2f2d2ea10aa53239/setup.cfg
+https://github.com/jaraco/skeleton/raw/d9008b5c510cd6969127a6a2ab6f832edddef296/setup.cfg
+https://github.com/jaraco/zipp/raw/700d3a96390e970b6b962823bfea78b4f7e1c537/setup.cfg
+https://github.com/pallets/jinja/raw/7d72eb7fefb7dce065193967f31f805180508448/setup.cfg
+https://github.com/tkem/cachetools/raw/2fd87a94b8d3861d80e9e4236cd480bfdd21c90d/setup.cfg
+https://github.com/aio-libs/aiohttp/raw/5e0e6b7080f2408d5f1dd544c0e1cf88378b7b10/setup.cfg
+https://github.com/pallets/flask/raw/9486b6cf57bd6a8a261f67091aca8ca78eeec1e3/setup.cfg
+https://github.com/pallets/click/raw/6411f425fae545f42795665af4162006b36c5e4a/setup.cfg
+https://github.com/sqlalchemy/sqlalchemy/raw/533f5718904b620be8d63f2474229945d6f8ba5d/setup.cfg
+https://github.com/pytest-dev/pluggy/raw/461ef63291d13589c4e21aa182cd1529257e9a0a/setup.cfg
+https://github.com/pytest-dev/pytest/raw/c7be96dae487edbd2f55b561b31b68afac1dabe6/setup.cfg
+https://github.com/platformdirs/platformdirs/raw/7b7852128dd6f07511b618d6edea35046bd0c6ff/setup.cfg
+https://github.com/pandas-dev/pandas/raw/bc17343f934a33dc231c8c74be95d8365537c376/setup.cfg
+https://github.com/django/django/raw/4e249d11a6e56ca8feb4b055b681cec457ef3a3d/setup.cfg
+https://github.com/pyscaffold/pyscaffold/raw/de7aa5dc059fbd04307419c667cc4961bc9df4b8/setup.cfg
+https://github.com/pypa/virtualenv/raw/f92eda6e3da26a4d28c2663ffb85c4960bdb990c/setup.cfg
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py
new file mode 100644
index 0000000..b6b21a3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_apply_pyprojecttoml.py
@@ -0,0 +1,794 @@
+"""Make sure that applying the configuration from pyproject.toml is equivalent to
+applying a similar configuration from setup.cfg
+
+To run these tests offline, please have a look on ``./downloads/preload.py``
+"""
+
+from __future__ import annotations
+
+import io
+import re
+import tarfile
+from inspect import cleandoc
+from pathlib import Path
+from unittest.mock import Mock
+
+import pytest
+from ini2toml.api import LiteTranslator
+from packaging.metadata import Metadata
+
+import setuptools  # noqa: F401 # ensure monkey patch to metadata
+from setuptools._static import is_static
+from setuptools.command.egg_info import write_requirements
+from setuptools.config import expand, pyprojecttoml, setupcfg
+from setuptools.config._apply_pyprojecttoml import _MissingDynamic, _some_attrgetter
+from setuptools.dist import Distribution
+from setuptools.errors import InvalidConfigError, RemovedConfigError
+from setuptools.warnings import InformationOnly, SetuptoolsDeprecationWarning
+
+from .downloads import retrieve_file, urls_from_file
+
+HERE = Path(__file__).parent
+EXAMPLES_FILE = "setupcfg_examples.txt"
+
+
+def makedist(path, **attrs):
+    return Distribution({"src_root": path, **attrs})
+
+
+def _mock_expand_patterns(patterns, *_, **__):
+    """
+    Allow comparing the given patterns for 2 dist objects.
+    We need to strip special chars to avoid errors when validating.
+    """
+    return [
+        re.sub("[^a-z0-9]+", "", p, flags=re.IGNORECASE) or "empty" for p in patterns
+    ]
+
+
+@pytest.mark.parametrize("url", urls_from_file(HERE / EXAMPLES_FILE))
+@pytest.mark.filterwarnings("ignore")
+@pytest.mark.uses_network
+def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path):
+    monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.0.1"))
+    monkeypatch.setattr(
+        Distribution, "_expand_patterns", Mock(side_effect=_mock_expand_patterns)
+    )
+    setupcfg_example = retrieve_file(url)
+    pyproject_example = Path(tmp_path, "pyproject.toml")
+    setupcfg_text = setupcfg_example.read_text(encoding="utf-8")
+    toml_config = LiteTranslator().translate(setupcfg_text, "setup.cfg")
+    pyproject_example.write_text(toml_config, encoding="utf-8")
+
+    dist_toml = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject_example)
+    dist_cfg = setupcfg.apply_configuration(makedist(tmp_path), setupcfg_example)
+
+    pkg_info_toml = core_metadata(dist_toml)
+    pkg_info_cfg = core_metadata(dist_cfg)
+    assert pkg_info_toml == pkg_info_cfg
+
+    if any(getattr(d, "license_files", None) for d in (dist_toml, dist_cfg)):
+        assert set(dist_toml.license_files) == set(dist_cfg.license_files)
+
+    if any(getattr(d, "entry_points", None) for d in (dist_toml, dist_cfg)):
+        print(dist_cfg.entry_points)
+        ep_toml = {
+            (k, *sorted(i.replace(" ", "") for i in v))
+            for k, v in dist_toml.entry_points.items()
+        }
+        ep_cfg = {
+            (k, *sorted(i.replace(" ", "") for i in v))
+            for k, v in dist_cfg.entry_points.items()
+        }
+        assert ep_toml == ep_cfg
+
+    if any(getattr(d, "package_data", None) for d in (dist_toml, dist_cfg)):
+        pkg_data_toml = {(k, *sorted(v)) for k, v in dist_toml.package_data.items()}
+        pkg_data_cfg = {(k, *sorted(v)) for k, v in dist_cfg.package_data.items()}
+        assert pkg_data_toml == pkg_data_cfg
+
+    if any(getattr(d, "data_files", None) for d in (dist_toml, dist_cfg)):
+        data_files_toml = {(k, *sorted(v)) for k, v in dist_toml.data_files}
+        data_files_cfg = {(k, *sorted(v)) for k, v in dist_cfg.data_files}
+        assert data_files_toml == data_files_cfg
+
+    assert set(dist_toml.install_requires) == set(dist_cfg.install_requires)
+    if any(getattr(d, "extras_require", None) for d in (dist_toml, dist_cfg)):
+        extra_req_toml = {(k, *sorted(v)) for k, v in dist_toml.extras_require.items()}
+        extra_req_cfg = {(k, *sorted(v)) for k, v in dist_cfg.extras_require.items()}
+        assert extra_req_toml == extra_req_cfg
+
+
+PEP621_EXAMPLE = """\
+[project]
+name = "spam"
+version = "2020.0.0"
+description = "Lovely Spam! Wonderful Spam!"
+readme = "README.rst"
+requires-python = ">=3.8"
+license-files = ["LICENSE.txt"]  # Updated to be PEP 639 compliant
+keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
+authors = [
+  {email = "hi@pradyunsg.me"},
+  {name = "Tzu-Ping Chung"}
+]
+maintainers = [
+  {name = "Brett Cannon", email = "brett@python.org"},
+  {name = "John X. Ãørçeč", email = "john@utf8.org"},
+  {name = "Γαμα קּ 東", email = "gama@utf8.org"},
+]
+classifiers = [
+  "Development Status :: 4 - Beta",
+  "Programming Language :: Python"
+]
+
+dependencies = [
+  "httpx",
+  "gidgethub[httpx]>4.0.0",
+  "django>2.1; os_name != 'nt'",
+  "django>2.0; os_name == 'nt'"
+]
+
+[project.optional-dependencies]
+test = [
+  "pytest < 5.0.0",
+  "pytest-cov[all]"
+]
+
+[project.urls]
+homepage = "http://example.com"
+documentation = "http://readthedocs.org"
+repository = "http://github.com"
+changelog = "http://github.com/me/spam/blob/master/CHANGELOG.md"
+
+[project.scripts]
+spam-cli = "spam:main_cli"
+
+[project.gui-scripts]
+spam-gui = "spam:main_gui"
+
+[project.entry-points."spam.magical"]
+tomatoes = "spam:main_tomatoes"
+"""
+
+PEP621_INTERNATIONAL_EMAIL_EXAMPLE = """\
+[project]
+name = "spam"
+version = "2020.0.0"
+authors = [
+  {email = "hi@pradyunsg.me"},
+  {name = "Tzu-Ping Chung"}
+]
+maintainers = [
+  {name = "अंकित अहलावत", email = "ankit@example.com"},
+]
+"""
+
+PEP621_EXAMPLE_SCRIPT = """
+def main_cli(): pass
+def main_gui(): pass
+def main_tomatoes(): pass
+"""
+
+PEP639_LICENSE_TEXT = """\
+[project]
+name = "spam"
+version = "2020.0.0"
+authors = [
+  {email = "hi@pradyunsg.me"},
+  {name = "Tzu-Ping Chung"}
+]
+license = {text = "MIT"}
+"""
+
+PEP639_LICENSE_EXPRESSION = """\
+[project]
+name = "spam"
+version = "2020.0.0"
+authors = [
+  {email = "hi@pradyunsg.me"},
+  {name = "Tzu-Ping Chung"}
+]
+license = "mit or apache-2.0"  # should be normalized in metadata
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Programming Language :: Python",
+]
+"""
+
+
+def _pep621_example_project(
+    tmp_path,
+    readme="README.rst",
+    pyproject_text=PEP621_EXAMPLE,
+):
+    pyproject = tmp_path / "pyproject.toml"
+    text = pyproject_text
+    replacements = {'readme = "README.rst"': f'readme = "{readme}"'}
+    for orig, subst in replacements.items():
+        text = text.replace(orig, subst)
+    pyproject.write_text(text, encoding="utf-8")
+
+    (tmp_path / readme).write_text("hello world", encoding="utf-8")
+    (tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---", encoding="utf-8")
+    (tmp_path / "spam.py").write_text(PEP621_EXAMPLE_SCRIPT, encoding="utf-8")
+    return pyproject
+
+
+def test_pep621_example(tmp_path):
+    """Make sure the example in PEP 621 works"""
+    pyproject = _pep621_example_project(tmp_path)
+    dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+    assert set(dist.metadata.license_files) == {"LICENSE.txt"}
+
+
+@pytest.mark.parametrize(
+    ("readme", "ctype"),
+    [
+        ("Readme.txt", "text/plain"),
+        ("readme.md", "text/markdown"),
+        ("text.rst", "text/x-rst"),
+    ],
+)
+def test_readme_content_type(tmp_path, readme, ctype):
+    pyproject = _pep621_example_project(tmp_path, readme)
+    dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+    assert dist.metadata.long_description_content_type == ctype
+
+
+def test_undefined_content_type(tmp_path):
+    pyproject = _pep621_example_project(tmp_path, "README.tex")
+    with pytest.raises(ValueError, match="Undefined content type for README.tex"):
+        pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+
+def test_no_explicit_content_type_for_missing_extension(tmp_path):
+    pyproject = _pep621_example_project(tmp_path, "README")
+    dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+    assert dist.metadata.long_description_content_type is None
+
+
+@pytest.mark.parametrize(
+    ("pyproject_text", "expected_maintainers_meta_value"),
+    (
+        pytest.param(
+            PEP621_EXAMPLE,
+            (
+                'Brett Cannon , "John X. Ãørçeč" , '
+                'Γαμα קּ 東 '
+            ),
+            id='non-international-emails',
+        ),
+        pytest.param(
+            PEP621_INTERNATIONAL_EMAIL_EXAMPLE,
+            'Ankit Ahlawat <अंकित@उदाहरण.भारत>',
+            marks=pytest.mark.xfail(
+                reason="CPython's `email.headerregistry.Address` only supports "
+                'RFC 5322, as of Oct 20, 2025 and latest Python 3.13.0',
+                strict=True,
+            ),
+            id='international-email',
+        ),
+    ),
+)
+def test_utf8_maintainer_in_metadata(  # issue-3663
+    expected_maintainers_meta_value,
+    pyproject_text,
+    tmp_path,
+):
+    pyproject = _pep621_example_project(
+        tmp_path,
+        "README",
+        pyproject_text=pyproject_text,
+    )
+    dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+    assert dist.metadata.maintainer_email == expected_maintainers_meta_value
+    pkg_file = tmp_path / "PKG-FILE"
+    with open(pkg_file, "w", encoding="utf-8") as fh:
+        dist.metadata.write_pkg_file(fh)
+    content = pkg_file.read_text(encoding="utf-8")
+    assert f"Maintainer-email: {expected_maintainers_meta_value}" in content
+
+
+@pytest.mark.parametrize(
+    (
+        'pyproject_text',
+        'license',
+        'license_expression',
+        'content_str',
+        'not_content_str',
+    ),
+    (
+        pytest.param(
+            PEP639_LICENSE_TEXT,
+            'MIT',
+            None,
+            'License: MIT',
+            'License-Expression: ',
+            id='license-text',
+            marks=[
+                pytest.mark.filterwarnings(
+                    "ignore:.project.license. as a TOML table is deprecated",
+                )
+            ],
+        ),
+        pytest.param(
+            PEP639_LICENSE_EXPRESSION,
+            None,
+            'MIT OR Apache-2.0',
+            'License-Expression: MIT OR Apache-2.0',
+            'License: ',
+            id='license-expression',
+        ),
+    ),
+)
+def test_license_in_metadata(
+    license,
+    license_expression,
+    content_str,
+    not_content_str,
+    pyproject_text,
+    tmp_path,
+):
+    pyproject = _pep621_example_project(
+        tmp_path,
+        "README",
+        pyproject_text=pyproject_text,
+    )
+    dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+    assert dist.metadata.license == license
+    assert dist.metadata.license_expression == license_expression
+    pkg_file = tmp_path / "PKG-FILE"
+    with open(pkg_file, "w", encoding="utf-8") as fh:
+        dist.metadata.write_pkg_file(fh)
+    content = pkg_file.read_text(encoding="utf-8")
+    assert "Metadata-Version: 2.4" in content
+    assert content_str in content
+    assert not_content_str not in content
+
+
+def test_license_classifier_with_license_expression(tmp_path):
+    text = PEP639_LICENSE_EXPRESSION.rsplit("\n", 2)[0]
+    pyproject = _pep621_example_project(
+        tmp_path,
+        "README",
+        f"{text}\n    \"License :: OSI Approved :: MIT License\"\n]",
+    )
+    msg = "License classifiers have been superseded by license expressions"
+    with pytest.raises(InvalidConfigError, match=msg) as exc:
+        pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+    assert "License :: OSI Approved :: MIT License" in str(exc.value)
+
+
+def test_license_classifier_without_license_expression(tmp_path):
+    text = """\
+    [project]
+    name = "spam"
+    version = "2020.0.0"
+    license = {text = "mit or apache-2.0"}
+    classifiers = ["License :: OSI Approved :: MIT License"]
+    """
+    pyproject = _pep621_example_project(tmp_path, "README", text)
+
+    msg1 = "License classifiers are deprecated(?:.|\n)*MIT License"
+    msg2 = ".project.license. as a TOML table is deprecated"
+    with (
+        pytest.warns(SetuptoolsDeprecationWarning, match=msg1),
+        pytest.warns(SetuptoolsDeprecationWarning, match=msg2),
+    ):
+        dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+    # Check license classifier is still included
+    assert dist.metadata.get_classifiers() == ["License :: OSI Approved :: MIT License"]
+
+
+class TestLicenseFiles:
+    def base_pyproject(
+        self,
+        tmp_path,
+        additional_text="",
+        license_toml='license = {file = "LICENSE.txt"}\n',
+    ):
+        text = PEP639_LICENSE_EXPRESSION
+
+        # Sanity-check
+        assert 'license = "mit or apache-2.0"' in text
+        assert 'license-files' not in text
+        assert "[tool.setuptools]" not in text
+
+        text = re.sub(
+            r"(license = .*)\n",
+            license_toml,
+            text,
+            count=1,
+        )
+        assert license_toml in text  # sanity check
+        text = f"{text}\n{additional_text}\n"
+        pyproject = _pep621_example_project(tmp_path, "README", pyproject_text=text)
+        return pyproject
+
+    def base_pyproject_license_pep639(self, tmp_path, additional_text=""):
+        return self.base_pyproject(
+            tmp_path,
+            additional_text=additional_text,
+            license_toml='license = "licenseref-Proprietary"'
+            '\nlicense-files = ["_FILE*"]\n',
+        )
+
+    def test_both_license_and_license_files_defined(self, tmp_path):
+        setuptools_config = '[tool.setuptools]\nlicense-files = ["_FILE*"]'
+        pyproject = self.base_pyproject(tmp_path, setuptools_config)
+
+        (tmp_path / "_FILE.txt").touch()
+        (tmp_path / "_FILE.rst").touch()
+
+        # Would normally match the `license_files` patterns, but we want to exclude it
+        # by being explicit. On the other hand, contents should be added to `license`
+        license = tmp_path / "LICENSE.txt"
+        license.write_text("LicenseRef-Proprietary\n", encoding="utf-8")
+
+        msg1 = "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'"
+        msg2 = ".project.license. as a TOML table is deprecated"
+        with (
+            pytest.warns(SetuptoolsDeprecationWarning, match=msg1),
+            pytest.warns(SetuptoolsDeprecationWarning, match=msg2),
+        ):
+            dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+        assert set(dist.metadata.license_files) == {"_FILE.rst", "_FILE.txt"}
+        assert dist.metadata.license == "LicenseRef-Proprietary\n"
+
+    def test_both_license_and_license_files_defined_pep639(self, tmp_path):
+        # Set license and license-files
+        pyproject = self.base_pyproject_license_pep639(tmp_path)
+
+        (tmp_path / "_FILE.txt").touch()
+        (tmp_path / "_FILE.rst").touch()
+
+        msg = "Normalizing.*LicenseRef"
+        with pytest.warns(InformationOnly, match=msg):
+            dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+        assert set(dist.metadata.license_files) == {"_FILE.rst", "_FILE.txt"}
+        assert dist.metadata.license is None
+        assert dist.metadata.license_expression == "LicenseRef-Proprietary"
+
+    def test_license_files_defined_twice(self, tmp_path):
+        # Set project.license-files and tools.setuptools.license-files
+        setuptools_config = '[tool.setuptools]\nlicense-files = ["_FILE*"]'
+        pyproject = self.base_pyproject_license_pep639(tmp_path, setuptools_config)
+
+        msg = "'project.license-files' is defined already. Remove 'tool.setuptools.license-files'"
+        with pytest.raises(InvalidConfigError, match=msg):
+            pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+    def test_default_patterns(self, tmp_path):
+        setuptools_config = '[tool.setuptools]\nzip-safe = false'
+        # ^ used just to trigger section validation
+        pyproject = self.base_pyproject(tmp_path, setuptools_config, license_toml="")
+
+        license_files = "LICENCE-a.html COPYING-abc.txt AUTHORS-xyz NOTICE,def".split()
+
+        for fname in license_files:
+            (tmp_path / fname).write_text(f"{fname}\n", encoding="utf-8")
+
+        dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+        assert (tmp_path / "LICENSE.txt").exists()  # from base example
+        assert set(dist.metadata.license_files) == {*license_files, "LICENSE.txt"}
+
+    def test_missing_patterns(self, tmp_path):
+        pyproject = self.base_pyproject_license_pep639(tmp_path)
+        assert list(tmp_path.glob("_FILE*")) == []  # sanity check
+
+        msg1 = "Cannot find any files for the given pattern.*"
+        msg2 = "Normalizing 'licenseref-Proprietary' to 'LicenseRef-Proprietary'"
+        with (
+            pytest.warns(SetuptoolsDeprecationWarning, match=msg1),
+            pytest.warns(InformationOnly, match=msg2),
+        ):
+            pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+    def test_deprecated_file_expands_to_text(self, tmp_path):
+        """Make sure the old example with ``license = {text = ...}`` works"""
+
+        assert 'license-files = ["LICENSE.txt"]' in PEP621_EXAMPLE  # sanity check
+        text = PEP621_EXAMPLE.replace(
+            'license-files = ["LICENSE.txt"]',
+            'license = {file = "LICENSE.txt"}',
+        )
+        pyproject = _pep621_example_project(tmp_path, pyproject_text=text)
+
+        msg = ".project.license. as a TOML table is deprecated"
+        with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
+            dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+        assert dist.metadata.license == "--- LICENSE stub ---"
+        assert set(dist.metadata.license_files) == {"LICENSE.txt"}  # auto-filled
+
+
+class TestPyModules:
+    # https://github.com/pypa/setuptools/issues/4316
+
+    def dist(self, name):
+        toml_config = f"""
+        [project]
+        name = "test"
+        version = "42.0"
+        [tool.setuptools]
+        py-modules = [{name!r}]
+        """
+        pyproject = Path("pyproject.toml")
+        pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
+        return pyprojecttoml.apply_configuration(Distribution({}), pyproject)
+
+    @pytest.mark.parametrize("module", ["pip-run", "abc-d.λ-xyz-e"])
+    def test_valid_module_name(self, tmp_path, monkeypatch, module):
+        monkeypatch.chdir(tmp_path)
+        assert module in self.dist(module).py_modules
+
+    @pytest.mark.parametrize("module", ["pip run", "-pip-run", "pip-run-stubs"])
+    def test_invalid_module_name(self, tmp_path, monkeypatch, module):
+        monkeypatch.chdir(tmp_path)
+        with pytest.raises(ValueError, match="py-modules"):
+            self.dist(module).py_modules
+
+
+class TestExtModules:
+    def make_dist(self, toml_config):
+        pyproject = Path("pyproject.toml")
+        pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
+        with pytest.warns(pyprojecttoml._ExperimentalConfiguration):
+            return pyprojecttoml.apply_configuration(Distribution({}), pyproject)
+
+    def test_pyproject_sets_attribute(self, tmp_path, monkeypatch):
+        monkeypatch.chdir(tmp_path)
+        toml_config = """
+        [project]
+        name = "test"
+        version = "42.0"
+        [tool.setuptools]
+        ext-modules = [
+          {name = "my.ext", sources = ["hello.c", "world.c"]}
+        ]
+        """
+        dist = self.make_dist(toml_config)
+        assert len(dist.ext_modules) == 1
+        assert dist.ext_modules[0].name == "my.ext"
+        assert set(dist.ext_modules[0].sources) == {"hello.c", "world.c"}
+
+    def test_pyproject_define_macros_as_tuples(self, tmp_path, monkeypatch):
+        # https://github.com/pypa/setuptools/issues/4810
+        monkeypatch.chdir(tmp_path)
+        toml_config = """
+        [project]
+        name = "test"
+        version = "42.0"
+        [[tool.setuptools.ext-modules]]
+        name = "my.ext"
+        sources = ["hello.c", "world.c"]
+        define-macros = [["FIRST_SINGLE"], ["SECOND_TWO", "1"]]
+        """
+        dist = self.make_dist(toml_config)
+        assert isinstance(dist.ext_modules[0].define_macros[0], tuple)
+        assert dist.ext_modules[0].define_macros[0] == ("FIRST_SINGLE",)
+        assert dist.ext_modules[0].define_macros[1] == ("SECOND_TWO", "1")
+
+
+class TestDeprecatedFields:
+    def test_namespace_packages(self, tmp_path):
+        pyproject = tmp_path / "pyproject.toml"
+        config = """
+        [project]
+        name = "myproj"
+        version = "42"
+        [tool.setuptools]
+        namespace-packages = ["myproj.pkg"]
+        """
+        pyproject.write_text(cleandoc(config), encoding="utf-8")
+        with pytest.raises(RemovedConfigError, match="namespace-packages"):
+            pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject)
+
+
+class TestPresetField:
+    def pyproject(self, tmp_path, dynamic, extra_content=""):
+        content = f"[project]\nname = 'proj'\ndynamic = {dynamic!r}\n"
+        if "version" not in dynamic:
+            content += "version = '42'\n"
+        file = tmp_path / "pyproject.toml"
+        file.write_text(content + extra_content, encoding="utf-8")
+        return file
+
+    @pytest.mark.parametrize(
+        ("attr", "field", "value"),
+        [
+            ("license_expression", "license", "MIT"),
+            pytest.param(
+                *("license", "license", "Not SPDX"),
+                marks=[pytest.mark.filterwarnings("ignore:.*license. overwritten")],
+            ),
+            ("classifiers", "classifiers", ["Private :: Classifier"]),
+            ("entry_points", "scripts", {"console_scripts": ["foobar=foobar:main"]}),
+            ("entry_points", "gui-scripts", {"gui_scripts": ["bazquux=bazquux:main"]}),
+            pytest.param(
+                *("install_requires", "dependencies", ["six"]),
+                marks=[
+                    pytest.mark.filterwarnings("ignore:.*install_requires. overwritten")
+                ],
+            ),
+        ],
+    )
+    def test_not_listed_in_dynamic(self, tmp_path, attr, field, value):
+        """Setuptools cannot set a field if not listed in ``dynamic``"""
+        pyproject = self.pyproject(tmp_path, [])
+        dist = makedist(tmp_path, **{attr: value})
+        msg = re.compile(f"defined outside of `pyproject.toml`:.*{field}", re.DOTALL)
+        with pytest.warns(_MissingDynamic, match=msg):
+            dist = pyprojecttoml.apply_configuration(dist, pyproject)
+
+        dist_value = _some_attrgetter(f"metadata.{attr}", attr)(dist)
+        assert not dist_value
+
+    @pytest.mark.parametrize(
+        ("attr", "field", "value"),
+        [
+            ("license_expression", "license", "MIT"),
+            ("install_requires", "dependencies", []),
+            ("extras_require", "optional-dependencies", {}),
+            ("install_requires", "dependencies", ["six"]),
+            ("classifiers", "classifiers", ["Private :: Classifier"]),
+        ],
+    )
+    def test_listed_in_dynamic(self, tmp_path, attr, field, value):
+        pyproject = self.pyproject(tmp_path, [field])
+        dist = makedist(tmp_path, **{attr: value})
+        dist = pyprojecttoml.apply_configuration(dist, pyproject)
+        dist_value = _some_attrgetter(f"metadata.{attr}", attr)(dist)
+        assert dist_value == value
+
+    def test_license_files_exempt_from_dynamic(self, monkeypatch, tmp_path):
+        """
+        license-file is currently not considered in the context of dynamic.
+        As per 2025-02-19, https://packaging.python.org/en/latest/specifications/pyproject-toml/#license-files
+        allows setuptools to fill-in `license-files` the way it sees fit:
+
+        > If the license-files key is not defined, tools can decide how to handle license files.
+        > For example they can choose not to include any files or use their own
+        > logic to discover the appropriate files in the distribution.
+
+        Using license_files from setup.py to fill-in the value is in accordance
+        with this rule.
+        """
+        monkeypatch.chdir(tmp_path)
+        pyproject = self.pyproject(tmp_path, [])
+        dist = makedist(tmp_path, license_files=["LIC*"])
+        (tmp_path / "LIC1").write_text("42", encoding="utf-8")
+        dist = pyprojecttoml.apply_configuration(dist, pyproject)
+        assert dist.metadata.license_files == ["LIC1"]
+
+    def test_warning_overwritten_dependencies(self, tmp_path):
+        src = "[project]\nname='pkg'\nversion='0.1'\ndependencies=['click']\n"
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(src, encoding="utf-8")
+        dist = makedist(tmp_path, install_requires=["wheel"])
+        with pytest.warns(match="`install_requires` overwritten"):
+            dist = pyprojecttoml.apply_configuration(dist, pyproject)
+        assert "wheel" not in dist.install_requires
+
+    def test_optional_dependencies_dont_remove_env_markers(self, tmp_path):
+        """
+        Internally setuptools converts dependencies with markers to "extras".
+        If ``install_requires`` is given by ``setup.py``, we have to ensure that
+        applying ``optional-dependencies`` does not overwrite the mandatory
+        dependencies with markers (see #3204).
+        """
+        # If setuptools replace its internal mechanism that uses `requires.txt`
+        # this test has to be rewritten to adapt accordingly
+        extra = "\n[project.optional-dependencies]\nfoo = ['bar>1']\n"
+        pyproject = self.pyproject(tmp_path, ["dependencies"], extra)
+        install_req = ['importlib-resources (>=3.0.0) ; python_version < "3.7"']
+        dist = makedist(tmp_path, install_requires=install_req)
+        dist = pyprojecttoml.apply_configuration(dist, pyproject)
+        assert "foo" in dist.extras_require
+        egg_info = dist.get_command_obj("egg_info")
+        write_requirements(egg_info, tmp_path, tmp_path / "requires.txt")
+        reqs = (tmp_path / "requires.txt").read_text(encoding="utf-8")
+        assert "importlib-resources" in reqs
+        assert "bar" in reqs
+        assert ':python_version < "3.7"' in reqs
+
+    @pytest.mark.parametrize(
+        ("field", "group"),
+        [("scripts", "console_scripts"), ("gui-scripts", "gui_scripts")],
+    )
+    @pytest.mark.filterwarnings("error")
+    def test_scripts_dont_require_dynamic_entry_points(self, tmp_path, field, group):
+        # Issue 3862
+        pyproject = self.pyproject(tmp_path, [field])
+        dist = makedist(tmp_path, entry_points={group: ["foobar=foobar:main"]})
+        dist = pyprojecttoml.apply_configuration(dist, pyproject)
+        assert group in dist.entry_points
+
+
+class TestMeta:
+    def test_example_file_in_sdist(self, setuptools_sdist):
+        """Meta test to ensure tests can run from sdist"""
+        with tarfile.open(setuptools_sdist) as tar:
+            assert any(name.endswith(EXAMPLES_FILE) for name in tar.getnames())
+
+
+class TestInteropCommandLineParsing:
+    def test_version(self, tmp_path, monkeypatch, capsys):
+        # See pypa/setuptools#4047
+        # This test can be removed once the CLI interface of setup.py is removed
+        monkeypatch.chdir(tmp_path)
+        toml_config = """
+        [project]
+        name = "test"
+        version = "42.0"
+        """
+        pyproject = Path(tmp_path, "pyproject.toml")
+        pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
+        opts = {"script_args": ["--version"]}
+        dist = pyprojecttoml.apply_configuration(Distribution(opts), pyproject)
+        dist.parse_command_line()  # <-- there should be no exception here.
+        captured = capsys.readouterr()
+        assert "42.0" in captured.out
+
+
+class TestStaticConfig:
+    def test_mark_static_fields(self, tmp_path, monkeypatch):
+        monkeypatch.chdir(tmp_path)
+        toml_config = """
+        [project]
+        name = "test"
+        version = "42.0"
+        dependencies = ["hello"]
+        keywords = ["world"]
+        classifiers = ["private :: hello world"]
+        [tool.setuptools]
+        obsoletes = ["abcd"]
+        provides = ["abcd"]
+        platforms = ["abcd"]
+        """
+        pyproject = Path(tmp_path, "pyproject.toml")
+        pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
+        dist = pyprojecttoml.apply_configuration(Distribution({}), pyproject)
+        assert is_static(dist.install_requires)
+        assert is_static(dist.metadata.keywords)
+        assert is_static(dist.metadata.classifiers)
+        assert is_static(dist.metadata.obsoletes)
+        assert is_static(dist.metadata.provides)
+        assert is_static(dist.metadata.platforms)
+
+
+# --- Auxiliary Functions ---
+
+
+def core_metadata(dist) -> str:
+    with io.StringIO() as buffer:
+        dist.metadata.write_pkg_file(buffer)
+        pkg_file_txt = buffer.getvalue()
+
+    # Make sure core metadata is valid
+    Metadata.from_email(pkg_file_txt, validate=True)  # can raise exceptions
+
+    skip_prefixes: tuple[str, ...] = ()
+    skip_lines = set()
+    # ---- DIFF NORMALISATION ----
+    # PEP 621 is very particular about author/maintainer metadata conversion, so skip
+    skip_prefixes += ("Author:", "Author-email:", "Maintainer:", "Maintainer-email:")
+    # May be redundant with Home-page
+    skip_prefixes += ("Project-URL: Homepage,", "Home-page:")
+    # May be missing in original (relying on default) but backfilled in the TOML
+    skip_prefixes += ("Description-Content-Type:",)
+    # Remove empty lines
+    skip_lines.add("")
+
+    result = []
+    for line in pkg_file_txt.splitlines():
+        if line.startswith(skip_prefixes) or line in skip_lines:
+            continue
+        result.append(line + "\n")
+
+    return "".join(result)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py
new file mode 100644
index 0000000..c5710ec
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_expand.py
@@ -0,0 +1,247 @@
+import os
+import sys
+from pathlib import Path
+
+import pytest
+
+from setuptools._static import is_static
+from setuptools.config import expand
+from setuptools.discovery import find_package_path
+
+from distutils.errors import DistutilsOptionError
+
+
+def write_files(files, root_dir):
+    for file, content in files.items():
+        path = root_dir / file
+        path.parent.mkdir(exist_ok=True, parents=True)
+        path.write_text(content, encoding="utf-8")
+
+
+def test_glob_relative(tmp_path, monkeypatch):
+    files = {
+        "dir1/dir2/dir3/file1.txt",
+        "dir1/dir2/file2.txt",
+        "dir1/file3.txt",
+        "a.ini",
+        "b.ini",
+        "dir1/c.ini",
+        "dir1/dir2/a.ini",
+    }
+
+    write_files({k: "" for k in files}, tmp_path)
+    patterns = ["**/*.txt", "[ab].*", "**/[ac].ini"]
+    monkeypatch.chdir(tmp_path)
+    assert set(expand.glob_relative(patterns)) == files
+    # Make sure the same APIs work outside cwd
+    assert set(expand.glob_relative(patterns, tmp_path)) == files
+
+
+def test_read_files(tmp_path, monkeypatch):
+    dir_ = tmp_path / "dir_"
+    (tmp_path / "_dir").mkdir(exist_ok=True)
+    (tmp_path / "a.txt").touch()
+    files = {"a.txt": "a", "dir1/b.txt": "b", "dir1/dir2/c.txt": "c"}
+    write_files(files, dir_)
+
+    secrets = Path(str(dir_) + "secrets")
+    secrets.mkdir(exist_ok=True)
+    write_files({"secrets.txt": "secret keys"}, secrets)
+
+    with monkeypatch.context() as m:
+        m.chdir(dir_)
+        assert expand.read_files(list(files)) == "a\nb\nc"
+
+        cannot_access_msg = r"Cannot access '.*\.\..a\.txt'"
+        with pytest.raises(DistutilsOptionError, match=cannot_access_msg):
+            expand.read_files(["../a.txt"])
+
+        cannot_access_secrets_msg = r"Cannot access '.*secrets\.txt'"
+        with pytest.raises(DistutilsOptionError, match=cannot_access_secrets_msg):
+            expand.read_files(["../dir_secrets/secrets.txt"])
+
+    # Make sure the same APIs work outside cwd
+    assert expand.read_files(list(files), dir_) == "a\nb\nc"
+    with pytest.raises(DistutilsOptionError, match=cannot_access_msg):
+        expand.read_files(["../a.txt"], dir_)
+
+
+class TestReadAttr:
+    @pytest.mark.parametrize(
+        "example",
+        [
+            # No cookie means UTF-8:
+            b"__version__ = '\xc3\xa9'\nraise SystemExit(1)\n",
+            # If a cookie is present, honor it:
+            b"# -*- coding: utf-8 -*-\n__version__ = '\xc3\xa9'\nraise SystemExit(1)\n",
+            b"# -*- coding: latin1 -*-\n__version__ = '\xe9'\nraise SystemExit(1)\n",
+        ],
+    )
+    def test_read_attr_encoding_cookie(self, example, tmp_path):
+        (tmp_path / "mod.py").write_bytes(example)
+        assert expand.read_attr('mod.__version__', root_dir=tmp_path) == 'é'
+
+    def test_read_attr(self, tmp_path, monkeypatch):
+        files = {
+            "pkg/__init__.py": "",
+            "pkg/sub/__init__.py": "VERSION = '0.1.1'",
+            "pkg/sub/mod.py": (
+                "VALUES = {'a': 0, 'b': {42}, 'c': (0, 1, 1)}\nraise SystemExit(1)"
+            ),
+        }
+        write_files(files, tmp_path)
+
+        with monkeypatch.context() as m:
+            m.chdir(tmp_path)
+            # Make sure it can read the attr statically without evaluating the module
+            version = expand.read_attr('pkg.sub.VERSION')
+            values = expand.read_attr('lib.mod.VALUES', {'lib': 'pkg/sub'})
+
+        assert version == '0.1.1'
+        assert is_static(values)
+
+        assert values['a'] == 0
+        assert values['b'] == {42}
+        assert is_static(values)
+
+        # Make sure the same APIs work outside cwd
+        assert expand.read_attr('pkg.sub.VERSION', root_dir=tmp_path) == '0.1.1'
+        values = expand.read_attr('lib.mod.VALUES', {'lib': 'pkg/sub'}, tmp_path)
+        assert values['c'] == (0, 1, 1)
+
+    @pytest.mark.parametrize(
+        "example",
+        [
+            "VERSION: str\nVERSION = '0.1.1'\nraise SystemExit(1)\n",
+            "VERSION: str = '0.1.1'\nraise SystemExit(1)\n",
+        ],
+    )
+    def test_read_annotated_attr(self, tmp_path, example):
+        files = {
+            "pkg/__init__.py": "",
+            "pkg/sub/__init__.py": example,
+        }
+        write_files(files, tmp_path)
+        # Make sure this attribute can be read statically
+        version = expand.read_attr('pkg.sub.VERSION', root_dir=tmp_path)
+        assert version == '0.1.1'
+        assert is_static(version)
+
+    @pytest.mark.parametrize(
+        "example",
+        [
+            "VERSION = (lambda: '0.1.1')()\n",
+            "def fn(): return '0.1.1'\nVERSION = fn()\n",
+            "VERSION: str = (lambda: '0.1.1')()\n",
+        ],
+    )
+    def test_read_dynamic_attr(self, tmp_path, monkeypatch, example):
+        files = {
+            "pkg/__init__.py": "",
+            "pkg/sub/__init__.py": example,
+        }
+        write_files(files, tmp_path)
+        monkeypatch.chdir(tmp_path)
+        version = expand.read_attr('pkg.sub.VERSION')
+        assert version == '0.1.1'
+        assert not is_static(version)
+
+    def test_import_order(self, tmp_path):
+        """
+        Sometimes the import machinery will import the parent package of a nested
+        module, which triggers side-effects and might create problems (see issue #3176)
+
+        ``read_attr`` should bypass these limitations by resolving modules statically
+        (via ast.literal_eval).
+        """
+        files = {
+            "src/pkg/__init__.py": "from .main import func\nfrom .about import version",
+            "src/pkg/main.py": "import super_complicated_dep\ndef func(): return 42",
+            "src/pkg/about.py": "version = '42'",
+        }
+        write_files(files, tmp_path)
+        attr_desc = "pkg.about.version"
+        package_dir = {"": "src"}
+        # `import super_complicated_dep` should not run, otherwise the build fails
+        assert expand.read_attr(attr_desc, package_dir, tmp_path) == "42"
+
+
+@pytest.mark.parametrize(
+    ("package_dir", "file", "module", "return_value"),
+    [
+        ({"": "src"}, "src/pkg/main.py", "pkg.main", 42),
+        ({"pkg": "lib"}, "lib/main.py", "pkg.main", 13),
+        ({}, "single_module.py", "single_module", 70),
+        ({}, "flat_layout/pkg.py", "flat_layout.pkg", 836),
+    ],
+)
+def test_resolve_class(monkeypatch, tmp_path, package_dir, file, module, return_value):
+    monkeypatch.setattr(sys, "modules", {})  # reproducibility
+    files = {file: f"class Custom:\n    def testing(self): return {return_value}"}
+    write_files(files, tmp_path)
+    cls = expand.resolve_class(f"{module}.Custom", package_dir, tmp_path)
+    assert cls().testing() == return_value
+
+
+@pytest.mark.parametrize(
+    ("args", "pkgs"),
+    [
+        ({"where": ["."], "namespaces": False}, {"pkg", "other"}),
+        ({"where": [".", "dir1"], "namespaces": False}, {"pkg", "other", "dir2"}),
+        ({"namespaces": True}, {"pkg", "other", "dir1", "dir1.dir2"}),
+        ({}, {"pkg", "other", "dir1", "dir1.dir2"}),  # default value for `namespaces`
+    ],
+)
+def test_find_packages(tmp_path, args, pkgs):
+    files = {
+        "pkg/__init__.py",
+        "other/__init__.py",
+        "dir1/dir2/__init__.py",
+    }
+    write_files({k: "" for k in files}, tmp_path)
+
+    package_dir = {}
+    kwargs = {"root_dir": tmp_path, "fill_package_dir": package_dir, **args}
+    where = kwargs.get("where", ["."])
+    assert set(expand.find_packages(**kwargs)) == pkgs
+    for pkg in pkgs:
+        pkg_path = find_package_path(pkg, package_dir, tmp_path)
+        assert os.path.exists(pkg_path)
+
+    # Make sure the same APIs work outside cwd
+    where = [
+        str((tmp_path / p).resolve()).replace(os.sep, "/")  # ensure posix-style paths
+        for p in args.pop("where", ["."])
+    ]
+
+    assert set(expand.find_packages(where=where, **args)) == pkgs
+
+
+@pytest.mark.parametrize(
+    ("files", "where", "expected_package_dir"),
+    [
+        (["pkg1/__init__.py", "pkg1/other.py"], ["."], {}),
+        (["pkg1/__init__.py", "pkg2/__init__.py"], ["."], {}),
+        (["src/pkg1/__init__.py", "src/pkg1/other.py"], ["src"], {"": "src"}),
+        (["src/pkg1/__init__.py", "src/pkg2/__init__.py"], ["src"], {"": "src"}),
+        (
+            ["src1/pkg1/__init__.py", "src2/pkg2/__init__.py"],
+            ["src1", "src2"],
+            {"pkg1": "src1/pkg1", "pkg2": "src2/pkg2"},
+        ),
+        (
+            ["src/pkg1/__init__.py", "pkg2/__init__.py"],
+            ["src", "."],
+            {"pkg1": "src/pkg1"},
+        ),
+    ],
+)
+def test_fill_package_dir(tmp_path, files, where, expected_package_dir):
+    write_files({k: "" for k in files}, tmp_path)
+    pkg_dir = {}
+    kwargs = {"root_dir": tmp_path, "fill_package_dir": pkg_dir, "namespaces": False}
+    pkgs = expand.find_packages(where=where, **kwargs)
+    assert set(pkg_dir.items()) == set(expected_package_dir.items())
+    for pkg in pkgs:
+        pkg_path = find_package_path(pkg, pkg_dir, tmp_path)
+        assert os.path.exists(pkg_path)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py
new file mode 100644
index 0000000..e031ea8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml.py
@@ -0,0 +1,421 @@
+import re
+from configparser import ConfigParser
+from inspect import cleandoc
+
+import jaraco.path
+import pytest
+import tomli_w
+from path import Path
+
+import setuptools  # noqa: F401 # force distutils.core to be patched
+from setuptools.config.pyprojecttoml import (
+    _ToolsTypoInMetadata,
+    apply_configuration,
+    expand_configuration,
+    read_configuration,
+    validate,
+)
+from setuptools.dist import Distribution
+from setuptools.errors import OptionError
+
+import distutils.core
+
+EXAMPLE = """
+[project]
+name = "myproj"
+keywords = ["some", "key", "words"]
+dynamic = ["version", "readme"]
+requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+dependencies = [
+    'importlib-metadata>=0.12;python_version<"3.8"',
+    'importlib-resources>=1.0;python_version<"3.7"',
+    'pathlib2>=2.3.3,<3;python_version < "3.4" and sys.platform != "win32"',
+]
+
+[project.optional-dependencies]
+docs = [
+    "sphinx>=3",
+    "sphinx-argparse>=0.2.5",
+    "sphinx-rtd-theme>=0.4.3",
+]
+testing = [
+    "pytest>=1",
+    "coverage>=3,<5",
+]
+
+[project.scripts]
+exec = "pkg.__main__:exec"
+
+[build-system]
+requires = ["setuptools", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools]
+package-dir = {"" = "src"}
+zip-safe = true
+platforms = ["any"]
+
+[tool.setuptools.packages.find]
+where = ["src"]
+
+[tool.setuptools.cmdclass]
+sdist = "pkg.mod.CustomSdist"
+
+[tool.setuptools.dynamic.version]
+attr = "pkg.__version__.VERSION"
+
+[tool.setuptools.dynamic.readme]
+file = ["README.md"]
+content-type = "text/markdown"
+
+[tool.setuptools.package-data]
+"*" = ["*.txt"]
+
+[tool.setuptools.data-files]
+"data" = ["_files/*.txt"]
+
+[tool.distutils.sdist]
+formats = "gztar"
+
+[tool.distutils.bdist_wheel]
+universal = true
+"""
+
+
+def create_example(path, pkg_root):
+    files = {
+        "pyproject.toml": EXAMPLE,
+        "README.md": "hello world",
+        "_files": {
+            "file.txt": "",
+        },
+    }
+    packages = {
+        "pkg": {
+            "__init__.py": "",
+            "mod.py": "class CustomSdist: pass",
+            "__version__.py": "VERSION = (3, 10)",
+            "__main__.py": "def exec(): print('hello')",
+        },
+    }
+
+    assert pkg_root  # Meta-test: cannot be empty string.
+
+    if pkg_root == ".":
+        files = {**files, **packages}
+        # skip other files: flat-layout will raise error for multi-package dist
+    else:
+        # Use this opportunity to ensure namespaces are discovered
+        files[pkg_root] = {**packages, "other": {"nested": {"__init__.py": ""}}}
+
+    jaraco.path.build(files, prefix=path)
+
+
+def verify_example(config, path, pkg_root):
+    pyproject = path / "pyproject.toml"
+    pyproject.write_text(tomli_w.dumps(config), encoding="utf-8")
+    expanded = expand_configuration(config, path)
+    expanded_project = expanded["project"]
+    assert read_configuration(pyproject, expand=True) == expanded
+    assert expanded_project["version"] == "3.10"
+    assert expanded_project["readme"]["text"] == "hello world"
+    assert "packages" in expanded["tool"]["setuptools"]
+    if pkg_root == ".":
+        # Auto-discovery will raise error for multi-package dist
+        assert set(expanded["tool"]["setuptools"]["packages"]) == {"pkg"}
+    else:
+        assert set(expanded["tool"]["setuptools"]["packages"]) == {
+            "pkg",
+            "other",
+            "other.nested",
+        }
+    assert expanded["tool"]["setuptools"]["include-package-data"] is True
+    assert "" in expanded["tool"]["setuptools"]["package-data"]
+    assert "*" not in expanded["tool"]["setuptools"]["package-data"]
+    assert expanded["tool"]["setuptools"]["data-files"] == [
+        ("data", ["_files/file.txt"])
+    ]
+
+
+def test_read_configuration(tmp_path):
+    create_example(tmp_path, "src")
+    pyproject = tmp_path / "pyproject.toml"
+
+    config = read_configuration(pyproject, expand=False)
+    assert config["project"].get("version") is None
+    assert config["project"].get("readme") is None
+
+    verify_example(config, tmp_path, "src")
+
+
+@pytest.mark.parametrize(
+    ("pkg_root", "opts"),
+    [
+        (".", {}),
+        ("src", {}),
+        ("lib", {"packages": {"find": {"where": ["lib"]}}}),
+    ],
+)
+def test_discovered_package_dir_with_attr_directive_in_config(tmp_path, pkg_root, opts):
+    create_example(tmp_path, pkg_root)
+
+    pyproject = tmp_path / "pyproject.toml"
+
+    config = read_configuration(pyproject, expand=False)
+    assert config["project"].get("version") is None
+    assert config["project"].get("readme") is None
+    config["tool"]["setuptools"].pop("packages", None)
+    config["tool"]["setuptools"].pop("package-dir", None)
+
+    config["tool"]["setuptools"].update(opts)
+    verify_example(config, tmp_path, pkg_root)
+
+
+ENTRY_POINTS = {
+    "console_scripts": {"a": "mod.a:func"},
+    "gui_scripts": {"b": "mod.b:func"},
+    "other": {"c": "mod.c:func [extra]"},
+}
+
+
+class TestEntryPoints:
+    def write_entry_points(self, tmp_path):
+        entry_points = ConfigParser()
+        entry_points.read_dict(ENTRY_POINTS)
+        with open(tmp_path / "entry-points.txt", "w", encoding="utf-8") as f:
+            entry_points.write(f)
+
+    def pyproject(self, dynamic=None):
+        project = {"dynamic": dynamic or ["scripts", "gui-scripts", "entry-points"]}
+        tool = {"dynamic": {"entry-points": {"file": "entry-points.txt"}}}
+        return {"project": project, "tool": {"setuptools": tool}}
+
+    def test_all_listed_in_dynamic(self, tmp_path):
+        self.write_entry_points(tmp_path)
+        expanded = expand_configuration(self.pyproject(), tmp_path)
+        expanded_project = expanded["project"]
+        assert len(expanded_project["scripts"]) == 1
+        assert expanded_project["scripts"]["a"] == "mod.a:func"
+        assert len(expanded_project["gui-scripts"]) == 1
+        assert expanded_project["gui-scripts"]["b"] == "mod.b:func"
+        assert len(expanded_project["entry-points"]) == 1
+        assert expanded_project["entry-points"]["other"]["c"] == "mod.c:func [extra]"
+
+    @pytest.mark.parametrize("missing_dynamic", ("scripts", "gui-scripts"))
+    def test_scripts_not_listed_in_dynamic(self, tmp_path, missing_dynamic):
+        self.write_entry_points(tmp_path)
+        dynamic = {"scripts", "gui-scripts", "entry-points"} - {missing_dynamic}
+
+        msg = f"defined outside of `pyproject.toml`:.*{missing_dynamic}"
+        with pytest.raises(OptionError, match=re.compile(msg, re.DOTALL)):
+            expand_configuration(self.pyproject(dynamic), tmp_path)
+
+
+class TestClassifiers:
+    def test_dynamic(self, tmp_path):
+        # Let's create a project example that has dynamic classifiers
+        # coming from a txt file.
+        create_example(tmp_path, "src")
+        classifiers = cleandoc(
+            """
+            Framework :: Flask
+            Programming Language :: Haskell
+            """
+        )
+        (tmp_path / "classifiers.txt").write_text(classifiers, encoding="utf-8")
+
+        pyproject = tmp_path / "pyproject.toml"
+        config = read_configuration(pyproject, expand=False)
+        dynamic = config["project"]["dynamic"]
+        config["project"]["dynamic"] = list({*dynamic, "classifiers"})
+        dynamic_config = config["tool"]["setuptools"]["dynamic"]
+        dynamic_config["classifiers"] = {"file": "classifiers.txt"}
+
+        # When the configuration is expanded,
+        # each line of the file should be an different classifier.
+        validate(config, pyproject)
+        expanded = expand_configuration(config, tmp_path)
+
+        assert set(expanded["project"]["classifiers"]) == {
+            "Framework :: Flask",
+            "Programming Language :: Haskell",
+        }
+
+    def test_dynamic_without_config(self, tmp_path):
+        config = """
+        [project]
+        name = "myproj"
+        version = '42'
+        dynamic = ["classifiers"]
+        """
+
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(cleandoc(config), encoding="utf-8")
+        with pytest.raises(OptionError, match="No configuration .* .classifiers."):
+            read_configuration(pyproject)
+
+    def test_dynamic_readme_from_setup_script_args(self, tmp_path):
+        config = """
+        [project]
+        name = "myproj"
+        version = '42'
+        dynamic = ["readme"]
+        """
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(cleandoc(config), encoding="utf-8")
+        dist = Distribution(attrs={"long_description": "42"})
+        # No error should occur because of missing `readme`
+        dist = apply_configuration(dist, pyproject)
+        assert dist.metadata.long_description == "42"
+
+    def test_dynamic_without_file(self, tmp_path):
+        config = """
+        [project]
+        name = "myproj"
+        version = '42'
+        dynamic = ["classifiers"]
+
+        [tool.setuptools.dynamic]
+        classifiers = {file = ["classifiers.txt"]}
+        """
+
+        pyproject = tmp_path / "pyproject.toml"
+        pyproject.write_text(cleandoc(config), encoding="utf-8")
+        with pytest.warns(UserWarning, match="File .*classifiers.txt. cannot be found"):
+            expanded = read_configuration(pyproject)
+        assert "classifiers" not in expanded["project"]
+
+
+class TestImportNames:
+    EXAMPLES = [
+        'import-names = ["hello", "world"]',
+        'import-namespaces = ["hello", "world"]',
+        'dynamic = ["import-names"]',
+        'dynamic = ["import-namespaces"]',
+    ]
+
+    @pytest.mark.parametrize("example", EXAMPLES)
+    def test_not_implemented(self, monkeypatch, tmp_path, example):
+        monkeypatch.chdir(tmp_path)
+        pyproject = Path("pyproject.toml")
+        toml_config = f"""
+        [project]
+        name = 'proj'
+        version = '42'
+        {example}
+        """
+        pyproject.write_text(cleandoc(toml_config), encoding="utf-8")
+        with pytest.raises(NotImplementedError, match='import-names'):
+            apply_configuration(Distribution({}), pyproject)
+
+
+@pytest.mark.parametrize(
+    "example",
+    (
+        """
+        [project]
+        name = "myproj"
+        version = "1.2"
+
+        [my-tool.that-disrespect.pep518]
+        value = 42
+        """,
+    ),
+)
+def test_ignore_unrelated_config(tmp_path, example):
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(cleandoc(example), encoding="utf-8")
+
+    # Make sure no error is raised due to 3rd party configs in pyproject.toml
+    assert read_configuration(pyproject) is not None
+
+
+@pytest.mark.parametrize(
+    ("example", "error_msg"),
+    [
+        (
+            """
+            [project]
+            name = "myproj"
+            version = "1.2"
+            requires = ['pywin32; platform_system=="Windows"' ]
+            """,
+            "configuration error: .project. must not contain ..requires.. properties",
+        ),
+    ],
+)
+def test_invalid_example(tmp_path, example, error_msg):
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(cleandoc(example), encoding="utf-8")
+
+    pattern = re.compile(
+        f"invalid pyproject.toml.*{error_msg}.*", re.MULTILINE | re.DOTALL
+    )
+    with pytest.raises(ValueError, match=pattern):
+        read_configuration(pyproject)
+
+
+@pytest.mark.parametrize("config", ("", "[tool.something]\nvalue = 42"))
+def test_empty(tmp_path, config):
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(config, encoding="utf-8")
+
+    # Make sure no error is raised
+    assert read_configuration(pyproject) == {}
+
+
+@pytest.mark.parametrize("config", ("[project]\nname = 'myproj'\nversion='42'\n",))
+def test_include_package_data_by_default(tmp_path, config):
+    """Builds with ``pyproject.toml`` should consider ``include-package-data=True`` as
+    default.
+    """
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(config, encoding="utf-8")
+
+    config = read_configuration(pyproject)
+    assert config["tool"]["setuptools"]["include-package-data"] is True
+
+
+def test_include_package_data_in_setuppy(tmp_path):
+    """Builds with ``pyproject.toml`` should consider ``include_package_data`` set in
+    ``setup.py``.
+
+    See https://github.com/pypa/setuptools/issues/3197#issuecomment-1079023889
+    """
+    files = {
+        "pyproject.toml": "[project]\nname = 'myproj'\nversion='42'\n",
+        "setup.py": "__import__('setuptools').setup(include_package_data=False)",
+    }
+    jaraco.path.build(files, prefix=tmp_path)
+
+    with Path(tmp_path):
+        dist = distutils.core.run_setup("setup.py", {}, stop_after="config")
+
+    assert dist.get_name() == "myproj"
+    assert dist.get_version() == "42"
+    assert dist.include_package_data is False
+
+
+def test_warn_tools_typo(tmp_path):
+    """Test that the common ``tools.setuptools`` typo in ``pyproject.toml`` issues a warning
+
+    See https://github.com/pypa/setuptools/issues/4150
+    """
+    config = """
+    [build-system]
+    requires = ["setuptools"]
+    build-backend = "setuptools.build_meta"
+
+    [project]
+    name = "myproj"
+    version = '42'
+
+    [tools.setuptools]
+    packages = ["package"]
+    """
+
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(cleandoc(config), encoding="utf-8")
+
+    with pytest.warns(_ToolsTypoInMetadata):
+        read_configuration(pyproject)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py
new file mode 100644
index 0000000..9fc8050
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py
@@ -0,0 +1,111 @@
+from inspect import cleandoc
+
+import pytest
+from jaraco import path
+
+from setuptools.config.pyprojecttoml import apply_configuration
+from setuptools.dist import Distribution
+from setuptools.warnings import SetuptoolsWarning
+
+
+def test_dynamic_dependencies(tmp_path):
+    files = {
+        "requirements.txt": "six\n  # comment\n",
+        "pyproject.toml": cleandoc(
+            """
+            [project]
+            name = "myproj"
+            version = "1.0"
+            dynamic = ["dependencies"]
+
+            [build-system]
+            requires = ["setuptools", "wheel"]
+            build-backend = "setuptools.build_meta"
+
+            [tool.setuptools.dynamic.dependencies]
+            file = ["requirements.txt"]
+            """
+        ),
+    }
+    path.build(files, prefix=tmp_path)
+    dist = Distribution()
+    dist = apply_configuration(dist, tmp_path / "pyproject.toml")
+    assert dist.install_requires == ["six"]
+
+
+def test_dynamic_optional_dependencies(tmp_path):
+    files = {
+        "requirements-docs.txt": "sphinx\n  # comment\n",
+        "pyproject.toml": cleandoc(
+            """
+            [project]
+            name = "myproj"
+            version = "1.0"
+            dynamic = ["optional-dependencies"]
+
+            [tool.setuptools.dynamic.optional-dependencies.docs]
+            file = ["requirements-docs.txt"]
+
+            [build-system]
+            requires = ["setuptools", "wheel"]
+            build-backend = "setuptools.build_meta"
+            """
+        ),
+    }
+    path.build(files, prefix=tmp_path)
+    dist = Distribution()
+    dist = apply_configuration(dist, tmp_path / "pyproject.toml")
+    assert dist.extras_require == {"docs": ["sphinx"]}
+
+
+def test_mixed_dynamic_optional_dependencies(tmp_path):
+    """
+    Test that if PEP 621 was loosened to allow mixing of dynamic and static
+    configurations in the case of fields containing sub-fields (groups),
+    things would work out.
+    """
+    files = {
+        "requirements-images.txt": "pillow~=42.0\n  # comment\n",
+        "pyproject.toml": cleandoc(
+            """
+            [project]
+            name = "myproj"
+            version = "1.0"
+            dynamic = ["optional-dependencies"]
+
+            [project.optional-dependencies]
+            docs = ["sphinx"]
+
+            [tool.setuptools.dynamic.optional-dependencies.images]
+            file = ["requirements-images.txt"]
+            """
+        ),
+    }
+
+    path.build(files, prefix=tmp_path)
+    pyproject = tmp_path / "pyproject.toml"
+    with pytest.raises(ValueError, match="project.optional-dependencies"):
+        apply_configuration(Distribution(), pyproject)
+
+
+def test_mixed_extras_require_optional_dependencies(tmp_path):
+    files = {
+        "pyproject.toml": cleandoc(
+            """
+            [project]
+            name = "myproj"
+            version = "1.0"
+            optional-dependencies.docs = ["sphinx"]
+            """
+        ),
+    }
+
+    path.build(files, prefix=tmp_path)
+    pyproject = tmp_path / "pyproject.toml"
+
+    dist = Distribution({"extras_require": {"hello": ["world"]}})
+
+    with pytest.warns(SetuptoolsWarning, match=".extras_require. overwritten"):
+        dist = apply_configuration(dist, pyproject)
+
+    assert dist.extras_require == {"docs": ["sphinx"]}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py
new file mode 100644
index 0000000..495337a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/config/test_setupcfg.py
@@ -0,0 +1,987 @@
+import configparser
+import contextlib
+import inspect
+import re
+import sys
+from pathlib import Path
+from unittest.mock import Mock, patch
+
+import pytest
+from packaging.requirements import InvalidRequirement
+
+from setuptools.config.setupcfg import ConfigHandler, Target, read_configuration
+from setuptools.dist import Distribution, _Distribution
+from setuptools.warnings import SetuptoolsDeprecationWarning
+
+from ..textwrap import DALS
+
+from distutils.errors import DistutilsFileError, DistutilsOptionError
+
+IS_PYPY = '__pypy__' in sys.builtin_module_names
+
+
+class ErrConfigHandler(ConfigHandler[Target]):
+    """Erroneous handler. Fails to implement required methods."""
+
+    section_prefix = "**err**"
+
+
+def make_package_dir(name, base_dir, ns=False):
+    dir_package = base_dir
+    for dir_name in name.split('/'):
+        dir_package = dir_package.mkdir(dir_name)
+    init_file = None
+    if not ns:
+        init_file = dir_package.join('__init__.py')
+        init_file.write('')
+    return dir_package, init_file
+
+
+def fake_env(
+    tmpdir, setup_cfg, setup_py=None, encoding='ascii', package_path='fake_package'
+):
+    if setup_py is None:
+        setup_py = 'from setuptools import setup\nsetup()\n'
+
+    tmpdir.join('setup.py').write(setup_py)
+    config = tmpdir.join('setup.cfg')
+    config.write(setup_cfg.encode(encoding), mode='wb')
+
+    package_dir, init_file = make_package_dir(package_path, tmpdir)
+
+    init_file.write(
+        'VERSION = (1, 2, 3)\n'
+        '\n'
+        'VERSION_MAJOR = 1'
+        '\n'
+        'def get_version():\n'
+        '    return [3, 4, 5, "dev"]\n'
+        '\n'
+    )
+
+    return package_dir, config
+
+
+@contextlib.contextmanager
+def get_dist(tmpdir, kwargs_initial=None, parse=True):
+    kwargs_initial = kwargs_initial or {}
+
+    with tmpdir.as_cwd():
+        dist = Distribution(kwargs_initial)
+        dist.script_name = 'setup.py'
+        parse and dist.parse_config_files()
+
+        yield dist
+
+
+def test_parsers_implemented():
+    with pytest.raises(NotImplementedError):
+        handler = ErrConfigHandler(None, {}, False, Mock())
+        handler.parsers
+
+
+class TestConfigurationReader:
+    def test_basic(self, tmpdir):
+        _, config = fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'version = 10.1.1\n'
+            'keywords = one, two\n'
+            '\n'
+            '[options]\n'
+            'scripts = bin/a.py, bin/b.py\n',
+        )
+        config_dict = read_configuration(str(config))
+        assert config_dict['metadata']['version'] == '10.1.1'
+        assert config_dict['metadata']['keywords'] == ['one', 'two']
+        assert config_dict['options']['scripts'] == ['bin/a.py', 'bin/b.py']
+
+    def test_no_config(self, tmpdir):
+        with pytest.raises(DistutilsFileError):
+            read_configuration(str(tmpdir.join('setup.cfg')))
+
+    def test_ignore_errors(self, tmpdir):
+        _, config = fake_env(
+            tmpdir,
+            '[metadata]\nversion = attr: none.VERSION\nkeywords = one, two\n',
+        )
+        with pytest.raises(ImportError):
+            read_configuration(str(config))
+
+        config_dict = read_configuration(str(config), ignore_option_errors=True)
+
+        assert config_dict['metadata']['keywords'] == ['one', 'two']
+        assert 'version' not in config_dict['metadata']
+
+        config.remove()
+
+
+class TestMetadata:
+    def test_basic(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'version = 10.1.1\n'
+            'description = Some description\n'
+            'long_description_content_type = text/something\n'
+            'long_description = file: README\n'
+            'name = fake_name\n'
+            'keywords = one, two\n'
+            'provides = package, package.sub\n'
+            'license = otherlic\n'
+            'download_url = http://test.test.com/test/\n'
+            'maintainer_email = test@test.com\n',
+        )
+
+        tmpdir.join('README').write('readme contents\nline2')
+
+        meta_initial = {
+            # This will be used so `otherlic` won't replace it.
+            'license': 'BSD 3-Clause License',
+        }
+
+        with get_dist(tmpdir, meta_initial) as dist:
+            metadata = dist.metadata
+
+            assert metadata.version == '10.1.1'
+            assert metadata.description == 'Some description'
+            assert metadata.long_description_content_type == 'text/something'
+            assert metadata.long_description == 'readme contents\nline2'
+            assert metadata.provides == ['package', 'package.sub']
+            assert metadata.license == 'BSD 3-Clause License'
+            assert metadata.name == 'fake_name'
+            assert metadata.keywords == ['one', 'two']
+            assert metadata.download_url == 'http://test.test.com/test/'
+            assert metadata.maintainer_email == 'test@test.com'
+
+    def test_license_cfg(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS(
+                """
+            [metadata]
+            name=foo
+            version=0.0.1
+            license=Apache 2.0
+            """
+            ),
+        )
+
+        with get_dist(tmpdir) as dist:
+            metadata = dist.metadata
+
+            assert metadata.name == "foo"
+            assert metadata.version == "0.0.1"
+            assert metadata.license == "Apache 2.0"
+
+    def test_file_mixed(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\nlong_description = file: README.rst, CHANGES.rst\n\n',
+        )
+
+        tmpdir.join('README.rst').write('readme contents\nline2')
+        tmpdir.join('CHANGES.rst').write('changelog contents\nand stuff')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.long_description == (
+                'readme contents\nline2\nchangelog contents\nand stuff'
+            )
+
+    def test_file_sandboxed(self, tmpdir):
+        tmpdir.ensure("README")
+        project = tmpdir.join('depth1', 'depth2')
+        project.ensure(dir=True)
+        fake_env(project, '[metadata]\nlong_description = file: ../../README\n')
+
+        with get_dist(project, parse=False) as dist:
+            with pytest.raises(DistutilsOptionError):
+                dist.parse_config_files()  # file: out of sandbox
+
+    def test_aliases(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'author_email = test@test.com\n'
+            'home_page = http://test.test.com/test/\n'
+            'summary = Short summary\n'
+            'platform = a, b\n'
+            'classifier =\n'
+            '  Framework :: Django\n'
+            '  Programming Language :: Python :: 3.5\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            metadata = dist.metadata
+            assert metadata.author_email == 'test@test.com'
+            assert metadata.url == 'http://test.test.com/test/'
+            assert metadata.description == 'Short summary'
+            assert metadata.platforms == ['a', 'b']
+            assert metadata.classifiers == [
+                'Framework :: Django',
+                'Programming Language :: Python :: 3.5',
+            ]
+
+    def test_multiline(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'name = fake_name\n'
+            'keywords =\n'
+            '  one\n'
+            '  two\n'
+            'classifiers =\n'
+            '  Framework :: Django\n'
+            '  Programming Language :: Python :: 3.5\n',
+        )
+        with get_dist(tmpdir) as dist:
+            metadata = dist.metadata
+            assert metadata.keywords == ['one', 'two']
+            assert metadata.classifiers == [
+                'Framework :: Django',
+                'Programming Language :: Python :: 3.5',
+            ]
+
+    def test_dict(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'project_urls =\n'
+            '  Link One = https://example.com/one/\n'
+            '  Link Two = https://example.com/two/\n',
+        )
+        with get_dist(tmpdir) as dist:
+            metadata = dist.metadata
+            assert metadata.project_urls == {
+                'Link One': 'https://example.com/one/',
+                'Link Two': 'https://example.com/two/',
+            }
+
+    def test_version(self, tmpdir):
+        package_dir, config = fake_env(
+            tmpdir, '[metadata]\nversion = attr: fake_package.VERSION\n'
+        )
+
+        sub_a = package_dir.mkdir('subpkg_a')
+        sub_a.join('__init__.py').write('')
+        sub_a.join('mod.py').write('VERSION = (2016, 11, 26)')
+
+        sub_b = package_dir.mkdir('subpkg_b')
+        sub_b.join('__init__.py').write('')
+        sub_b.join('mod.py').write(
+            'import third_party_module\nVERSION = (2016, 11, 26)'
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1.2.3'
+
+        config.write('[metadata]\nversion = attr: fake_package.get_version\n')
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '3.4.5.dev'
+
+        config.write('[metadata]\nversion = attr: fake_package.VERSION_MAJOR\n')
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1'
+
+        config.write('[metadata]\nversion = attr: fake_package.subpkg_a.mod.VERSION\n')
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '2016.11.26'
+
+        config.write('[metadata]\nversion = attr: fake_package.subpkg_b.mod.VERSION\n')
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '2016.11.26'
+
+    def test_version_file(self, tmpdir):
+        fake_env(tmpdir, '[metadata]\nversion = file: fake_package/version.txt\n')
+        tmpdir.join('fake_package', 'version.txt').write('1.2.3\n')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1.2.3'
+
+        tmpdir.join('fake_package', 'version.txt').write('1.2.3\n4.5.6\n')
+        with pytest.raises(DistutilsOptionError):
+            with get_dist(tmpdir) as dist:
+                dist.metadata.version
+
+    def test_version_with_package_dir_simple(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'version = attr: fake_package_simple.VERSION\n'
+            '[options]\n'
+            'package_dir =\n'
+            '    = src\n',
+            package_path='src/fake_package_simple',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1.2.3'
+
+    def test_version_with_package_dir_rename(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'version = attr: fake_package_rename.VERSION\n'
+            '[options]\n'
+            'package_dir =\n'
+            '    fake_package_rename = fake_dir\n',
+            package_path='fake_dir',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1.2.3'
+
+    def test_version_with_package_dir_complex(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'version = attr: fake_package_complex.VERSION\n'
+            '[options]\n'
+            'package_dir =\n'
+            '    fake_package_complex = src/fake_dir\n',
+            package_path='src/fake_dir',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.version == '1.2.3'
+
+    def test_unknown_meta_item(self, tmpdir):
+        fake_env(tmpdir, '[metadata]\nname = fake_name\nunknown = some\n')
+        with get_dist(tmpdir, parse=False) as dist:
+            dist.parse_config_files()  # Skip unknown.
+
+    def test_usupported_section(self, tmpdir):
+        fake_env(tmpdir, '[metadata.some]\nkey = val\n')
+        with get_dist(tmpdir, parse=False) as dist:
+            with pytest.raises(DistutilsOptionError):
+                dist.parse_config_files()
+
+    def test_classifiers(self, tmpdir):
+        expected = set([
+            'Framework :: Django',
+            'Programming Language :: Python :: 3',
+            'Programming Language :: Python :: 3.5',
+        ])
+
+        # From file.
+        _, config = fake_env(tmpdir, '[metadata]\nclassifiers = file: classifiers\n')
+
+        tmpdir.join('classifiers').write(
+            'Framework :: Django\n'
+            'Programming Language :: Python :: 3\n'
+            'Programming Language :: Python :: 3.5\n'
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert set(dist.metadata.classifiers) == expected
+
+        # From list notation
+        config.write(
+            '[metadata]\n'
+            'classifiers =\n'
+            '    Framework :: Django\n'
+            '    Programming Language :: Python :: 3\n'
+            '    Programming Language :: Python :: 3.5\n'
+        )
+        with get_dist(tmpdir) as dist:
+            assert set(dist.metadata.classifiers) == expected
+
+    def test_interpolation(self, tmpdir):
+        fake_env(tmpdir, '[metadata]\ndescription = %(message)s\n')
+        with pytest.raises(configparser.InterpolationMissingOptionError):
+            with get_dist(tmpdir):
+                pass
+
+    def test_non_ascii_1(self, tmpdir):
+        fake_env(tmpdir, '[metadata]\ndescription = éàïôñ\n', encoding='utf-8')
+        with get_dist(tmpdir):
+            pass
+
+    def test_non_ascii_3(self, tmpdir):
+        fake_env(tmpdir, '\n# -*- coding: invalid\n')
+        with get_dist(tmpdir):
+            pass
+
+    def test_non_ascii_4(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '# -*- coding: utf-8\n[metadata]\ndescription = éàïôñ\n',
+            encoding='utf-8',
+        )
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.description == 'éàïôñ'
+
+    def test_not_utf8(self, tmpdir):
+        """
+        Config files encoded not in UTF-8 will fail
+        """
+        fake_env(
+            tmpdir,
+            '# vim: set fileencoding=iso-8859-15 :\n[metadata]\ndescription = éàïôñ\n',
+            encoding='iso-8859-15',
+        )
+        with pytest.raises(UnicodeDecodeError):
+            with get_dist(tmpdir):
+                pass
+
+    @pytest.mark.parametrize(
+        ("error_msg", "config", "invalid"),
+        [
+            (
+                "Invalid dash-separated key 'author-email' in 'metadata' (setup.cfg)",
+                DALS(
+                    """
+                    [metadata]
+                    author-email = test@test.com
+                    maintainer_email = foo@foo.com
+                    """
+                ),
+                {"author-email": "test@test.com"},
+            ),
+            (
+                "Invalid uppercase key 'Name' in 'metadata' (setup.cfg)",
+                DALS(
+                    """
+                    [metadata]
+                    Name = foo
+                    description = Some description
+                    """
+                ),
+                {"Name": "foo"},
+            ),
+        ],
+    )
+    def test_invalid_options_previously_deprecated(
+        self, tmpdir, error_msg, config, invalid
+    ):
+        # This test and related methods can be removed when no longer needed.
+        # Deprecation postponed due to push-back from the community in
+        # https://github.com/pypa/setuptools/issues/4910
+        fake_env(tmpdir, config)
+        with pytest.warns(SetuptoolsDeprecationWarning, match=re.escape(error_msg)):
+            dist = get_dist(tmpdir).__enter__()
+
+        tmpdir.join('setup.cfg').remove()
+
+        for field, value in invalid.items():
+            attr = field.replace("-", "_").lower()
+            assert getattr(dist.metadata, attr) == value
+
+
+class TestOptions:
+    def test_basic(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options]\n'
+            'zip_safe = True\n'
+            'include_package_data = yes\n'
+            'package_dir = b=c, =src\n'
+            'packages = pack_a, pack_b.subpack\n'
+            'namespace_packages = pack1, pack2\n'
+            'scripts = bin/one.py, bin/two.py\n'
+            'eager_resources = bin/one.py, bin/two.py\n'
+            'install_requires = docutils>=0.3; pack ==1.1, ==1.3; hey\n'
+            'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there\n'
+            'dependency_links = http://some.com/here/1, '
+            'http://some.com/there/2\n'
+            'python_requires = >=1.0, !=2.8\n'
+            'py_modules = module1, module2\n',
+        )
+        deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages")
+        with deprec, get_dist(tmpdir) as dist:
+            assert dist.zip_safe
+            assert dist.include_package_data
+            assert dist.package_dir == {'': 'src', 'b': 'c'}
+            assert dist.packages == ['pack_a', 'pack_b.subpack']
+            assert dist.namespace_packages == ['pack1', 'pack2']
+            assert dist.scripts == ['bin/one.py', 'bin/two.py']
+            assert dist.dependency_links == ([
+                'http://some.com/here/1',
+                'http://some.com/there/2',
+            ])
+            assert dist.install_requires == ([
+                'docutils>=0.3',
+                'pack==1.1,==1.3',
+                'hey',
+            ])
+            assert dist.setup_requires == ([
+                'docutils>=0.3',
+                'spack ==1.1, ==1.3',
+                'there',
+            ])
+            assert dist.python_requires == '>=1.0, !=2.8'
+            assert dist.py_modules == ['module1', 'module2']
+
+    def test_multiline(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options]\n'
+            'package_dir = \n'
+            '  b=c\n'
+            '  =src\n'
+            'packages = \n'
+            '  pack_a\n'
+            '  pack_b.subpack\n'
+            'namespace_packages = \n'
+            '  pack1\n'
+            '  pack2\n'
+            'scripts = \n'
+            '  bin/one.py\n'
+            '  bin/two.py\n'
+            'eager_resources = \n'
+            '  bin/one.py\n'
+            '  bin/two.py\n'
+            'install_requires = \n'
+            '  docutils>=0.3\n'
+            '  pack ==1.1, ==1.3\n'
+            '  hey\n'
+            'setup_requires = \n'
+            '  docutils>=0.3\n'
+            '  spack ==1.1, ==1.3\n'
+            '  there\n'
+            'dependency_links = \n'
+            '  http://some.com/here/1\n'
+            '  http://some.com/there/2\n',
+        )
+        deprec = pytest.warns(SetuptoolsDeprecationWarning, match="namespace_packages")
+        with deprec, get_dist(tmpdir) as dist:
+            assert dist.package_dir == {'': 'src', 'b': 'c'}
+            assert dist.packages == ['pack_a', 'pack_b.subpack']
+            assert dist.namespace_packages == ['pack1', 'pack2']
+            assert dist.scripts == ['bin/one.py', 'bin/two.py']
+            assert dist.dependency_links == ([
+                'http://some.com/here/1',
+                'http://some.com/there/2',
+            ])
+            assert dist.install_requires == ([
+                'docutils>=0.3',
+                'pack==1.1,==1.3',
+                'hey',
+            ])
+            assert dist.setup_requires == ([
+                'docutils>=0.3',
+                'spack ==1.1, ==1.3',
+                'there',
+            ])
+
+    def test_package_dir_fail(self, tmpdir):
+        fake_env(tmpdir, '[options]\npackage_dir = a b\n')
+        with get_dist(tmpdir, parse=False) as dist:
+            with pytest.raises(DistutilsOptionError):
+                dist.parse_config_files()
+
+    def test_package_data(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options.package_data]\n'
+            '* = *.txt, *.rst\n'
+            'hello = *.msg\n'
+            '\n'
+            '[options.exclude_package_data]\n'
+            '* = fake1.txt, fake2.txt\n'
+            'hello = *.dat\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.package_data == {
+                '': ['*.txt', '*.rst'],
+                'hello': ['*.msg'],
+            }
+            assert dist.exclude_package_data == {
+                '': ['fake1.txt', 'fake2.txt'],
+                'hello': ['*.dat'],
+            }
+
+    def test_packages(self, tmpdir):
+        fake_env(tmpdir, '[options]\npackages = find:\n')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.packages == ['fake_package']
+
+    def test_find_directive(self, tmpdir):
+        dir_package, config = fake_env(tmpdir, '[options]\npackages = find:\n')
+
+        make_package_dir('sub_one', dir_package)
+        make_package_dir('sub_two', dir_package)
+
+        with get_dist(tmpdir) as dist:
+            assert set(dist.packages) == set([
+                'fake_package',
+                'fake_package.sub_two',
+                'fake_package.sub_one',
+            ])
+
+        config.write(
+            '[options]\n'
+            'packages = find:\n'
+            '\n'
+            '[options.packages.find]\n'
+            'where = .\n'
+            'include =\n'
+            '    fake_package.sub_one\n'
+            '    two\n'
+        )
+        with get_dist(tmpdir) as dist:
+            assert dist.packages == ['fake_package.sub_one']
+
+        config.write(
+            '[options]\n'
+            'packages = find:\n'
+            '\n'
+            '[options.packages.find]\n'
+            'exclude =\n'
+            '    fake_package.sub_one\n'
+        )
+        with get_dist(tmpdir) as dist:
+            assert set(dist.packages) == set(['fake_package', 'fake_package.sub_two'])
+
+    def test_find_namespace_directive(self, tmpdir):
+        dir_package, config = fake_env(
+            tmpdir, '[options]\npackages = find_namespace:\n'
+        )
+
+        make_package_dir('sub_one', dir_package)
+        make_package_dir('sub_two', dir_package, ns=True)
+
+        with get_dist(tmpdir) as dist:
+            assert set(dist.packages) == {
+                'fake_package',
+                'fake_package.sub_two',
+                'fake_package.sub_one',
+            }
+
+        config.write(
+            '[options]\n'
+            'packages = find_namespace:\n'
+            '\n'
+            '[options.packages.find]\n'
+            'where = .\n'
+            'include =\n'
+            '    fake_package.sub_one\n'
+            '    two\n'
+        )
+        with get_dist(tmpdir) as dist:
+            assert dist.packages == ['fake_package.sub_one']
+
+        config.write(
+            '[options]\n'
+            'packages = find_namespace:\n'
+            '\n'
+            '[options.packages.find]\n'
+            'exclude =\n'
+            '    fake_package.sub_one\n'
+        )
+        with get_dist(tmpdir) as dist:
+            assert set(dist.packages) == {'fake_package', 'fake_package.sub_two'}
+
+    def test_extras_require(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options.extras_require]\n'
+            'pdf = ReportLab>=1.2; RXP\n'
+            'rest = \n'
+            '  docutils>=0.3\n'
+            '  pack ==1.1, ==1.3\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.extras_require == {
+                'pdf': ['ReportLab>=1.2', 'RXP'],
+                'rest': ['docutils>=0.3', 'pack==1.1,==1.3'],
+            }
+            assert set(dist.metadata.provides_extras) == {'pdf', 'rest'}
+
+    @pytest.mark.parametrize(
+        "config",
+        [
+            "[options.extras_require]\nfoo = bar;python_version<'3'",
+            "[options.extras_require]\nfoo = bar;os_name=='linux'",
+            "[options.extras_require]\nfoo = bar;python_version<'3'\n",
+            "[options.extras_require]\nfoo = bar;os_name=='linux'\n",
+            "[options]\ninstall_requires = bar;python_version<'3'",
+            "[options]\ninstall_requires = bar;os_name=='linux'",
+            "[options]\ninstall_requires = bar;python_version<'3'\n",
+            "[options]\ninstall_requires = bar;os_name=='linux'\n",
+        ],
+    )
+    @pytest.mark.xfail(IS_PYPY, reason="Exceptions missing on PyPy")
+    # TODO: investigate PyPy problem
+    def test_raises_accidental_env_marker_misconfig(self, config, tmpdir):
+        fake_env(tmpdir, config)
+        match = (
+            r"One of the parsed requirements in `(install_requires|extras_require.+)` "
+            "looks like a valid environment marker.*"
+        )
+        with pytest.raises(InvalidRequirement, match=match):
+            with get_dist(tmpdir) as _:
+                pass
+
+    @pytest.mark.parametrize(
+        "config",
+        [
+            "[options.extras_require]\nfoo = bar;python_version<3",
+            "[options.extras_require]\nfoo = bar;python_version<3\n",
+            "[options]\ninstall_requires = bar;python_version<3",
+            "[options]\ninstall_requires = bar;python_version<3\n",
+        ],
+    )
+    @pytest.mark.xfail(IS_PYPY, reason="Warnings missing on PyPy (minor issue)")
+    # TODO: investigate PyPy problem
+    def test_warn_accidental_env_marker_misconfig(self, config, tmpdir):
+        fake_env(tmpdir, config)
+        match = (
+            r"One of the parsed requirements in `(install_requires|extras_require.+)` "
+            "looks like a valid environment marker.*"
+        )
+        with pytest.warns(SetuptoolsDeprecationWarning, match=match):
+            with get_dist(tmpdir) as _:
+                pass
+
+    @pytest.mark.parametrize(
+        "config",
+        [
+            "[options.extras_require]\nfoo =\n    bar;python_version<'3'",
+            "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy",
+            "[options.extras_require]\nfoo =\n    bar;python_version<'3'\n",
+            "[options.extras_require]\nfoo = bar;baz\nboo = xxx;yyy\n",
+            "[options.extras_require]\nfoo =\n    bar\n    python_version<3\n",
+            "[options]\ninstall_requires =\n    bar;python_version<'3'",
+            "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy",
+            "[options]\ninstall_requires =\n    bar;python_version<'3'\n",
+            "[options]\ninstall_requires = bar;baz\nboo = xxx;yyy\n",
+            "[options]\ninstall_requires =\n    bar\n    python_version<3\n",
+        ],
+    )
+    @pytest.mark.filterwarnings("error::setuptools.SetuptoolsDeprecationWarning")
+    def test_nowarn_accidental_env_marker_misconfig(self, config, tmpdir, recwarn):
+        fake_env(tmpdir, config)
+        num_warnings = len(recwarn)
+        with get_dist(tmpdir) as _:
+            pass
+        # The examples are valid, no warnings shown
+        assert len(recwarn) == num_warnings
+
+    def test_dash_preserved_extras_require(self, tmpdir):
+        fake_env(tmpdir, '[options.extras_require]\nfoo-a = foo\nfoo_b = test\n')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.extras_require == {'foo-a': ['foo'], 'foo_b': ['test']}
+
+    def test_entry_points(self, tmpdir):
+        _, config = fake_env(
+            tmpdir,
+            '[options.entry_points]\n'
+            'group1 = point1 = pack.module:func, '
+            '.point2 = pack.module2:func_rest [rest]\n'
+            'group2 = point3 = pack.module:func2\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.entry_points == {
+                'group1': [
+                    'point1 = pack.module:func',
+                    '.point2 = pack.module2:func_rest [rest]',
+                ],
+                'group2': ['point3 = pack.module:func2'],
+            }
+
+        expected = (
+            '[blogtool.parsers]\n'
+            '.rst = some.nested.module:SomeClass.some_classmethod[reST]\n'
+        )
+
+        tmpdir.join('entry_points').write(expected)
+
+        # From file.
+        config.write('[options]\nentry_points = file: entry_points\n')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.entry_points == expected
+
+    def test_case_sensitive_entry_points(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options.entry_points]\n'
+            'GROUP1 = point1 = pack.module:func, '
+            '.point2 = pack.module2:func_rest [rest]\n'
+            'group2 = point3 = pack.module:func2\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.entry_points == {
+                'GROUP1': [
+                    'point1 = pack.module:func',
+                    '.point2 = pack.module2:func_rest [rest]',
+                ],
+                'group2': ['point3 = pack.module:func2'],
+            }
+
+    def test_data_files(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options.data_files]\n'
+            'cfg =\n'
+            '      a/b.conf\n'
+            '      c/d.conf\n'
+            'data = e/f.dat, g/h.dat\n',
+        )
+
+        with get_dist(tmpdir) as dist:
+            expected = [
+                ('cfg', ['a/b.conf', 'c/d.conf']),
+                ('data', ['e/f.dat', 'g/h.dat']),
+            ]
+            assert sorted(dist.data_files) == sorted(expected)
+
+    def test_data_files_globby(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[options.data_files]\n'
+            'cfg =\n'
+            '      a/b.conf\n'
+            '      c/d.conf\n'
+            'data = *.dat\n'
+            'icons = \n'
+            '      *.ico\n'
+            'audio = \n'
+            '      *.wav\n'
+            '      sounds.db\n',
+        )
+
+        # Create dummy files for glob()'s sake:
+        tmpdir.join('a.dat').write('')
+        tmpdir.join('b.dat').write('')
+        tmpdir.join('c.dat').write('')
+        tmpdir.join('a.ico').write('')
+        tmpdir.join('b.ico').write('')
+        tmpdir.join('c.ico').write('')
+        tmpdir.join('beep.wav').write('')
+        tmpdir.join('boop.wav').write('')
+        tmpdir.join('sounds.db').write('')
+
+        with get_dist(tmpdir) as dist:
+            expected = [
+                ('cfg', ['a/b.conf', 'c/d.conf']),
+                ('data', ['a.dat', 'b.dat', 'c.dat']),
+                ('icons', ['a.ico', 'b.ico', 'c.ico']),
+                ('audio', ['beep.wav', 'boop.wav', 'sounds.db']),
+            ]
+            assert sorted(dist.data_files) == sorted(expected)
+
+    def test_python_requires_simple(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS(
+                """
+            [options]
+            python_requires=>=2.7
+            """
+            ),
+        )
+        with get_dist(tmpdir) as dist:
+            dist.parse_config_files()
+
+    def test_python_requires_compound(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS(
+                """
+            [options]
+            python_requires=>=2.7,!=3.0.*
+            """
+            ),
+        )
+        with get_dist(tmpdir) as dist:
+            dist.parse_config_files()
+
+    def test_python_requires_invalid(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS(
+                """
+            [options]
+            python_requires=invalid
+            """
+            ),
+        )
+        with pytest.raises(Exception):
+            with get_dist(tmpdir) as dist:
+                dist.parse_config_files()
+
+    def test_cmdclass(self, tmpdir):
+        module_path = Path(tmpdir, "src/custom_build.py")  # auto discovery for src
+        module_path.parent.mkdir(parents=True, exist_ok=True)
+        module_path.write_text(
+            "from distutils.core import Command\nclass CustomCmd(Command): pass\n",
+            encoding="utf-8",
+        )
+
+        setup_cfg = """
+            [options]
+            cmdclass =
+                customcmd = custom_build.CustomCmd
+        """
+        fake_env(tmpdir, inspect.cleandoc(setup_cfg))
+
+        with get_dist(tmpdir) as dist:
+            cmdclass = dist.cmdclass['customcmd']
+            assert cmdclass.__name__ == "CustomCmd"
+            assert cmdclass.__module__ == "custom_build"
+            assert module_path.samefile(inspect.getfile(cmdclass))
+
+    def test_requirements_file(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS(
+                """
+            [options]
+            install_requires = file:requirements.txt
+            [options.extras_require]
+            colors = file:requirements-extra.txt
+            """
+            ),
+        )
+
+        tmpdir.join('requirements.txt').write('\ndocutils>=0.3\n\n')
+        tmpdir.join('requirements-extra.txt').write('colorama')
+
+        with get_dist(tmpdir) as dist:
+            assert dist.install_requires == ['docutils>=0.3']
+            assert dist.extras_require == {'colors': ['colorama']}
+
+
+saved_dist_init = _Distribution.__init__
+
+
+class TestExternalSetters:
+    # During creation of the setuptools Distribution() object, we call
+    # the init of the parent distutils Distribution object via
+    # _Distribution.__init__ ().
+    #
+    # It's possible distutils calls out to various keyword
+    # implementations (i.e. distutils.setup_keywords entry points)
+    # that may set a range of variables.
+    #
+    # This wraps distutil's Distribution.__init__ and simulates
+    # pbr or something else setting these values.
+    def _fake_distribution_init(self, dist, attrs):
+        saved_dist_init(dist, attrs)
+        # see self._DISTUTILS_UNSUPPORTED_METADATA
+        dist.metadata.long_description_content_type = 'text/something'
+        # Test overwrite setup() args
+        dist.metadata.project_urls = {
+            'Link One': 'https://example.com/one/',
+            'Link Two': 'https://example.com/two/',
+        }
+
+    @patch.object(_Distribution, '__init__', autospec=True)
+    def test_external_setters(self, mock_parent_init, tmpdir):
+        mock_parent_init.side_effect = self._fake_distribution_init
+
+        dist = Distribution(attrs={'project_urls': {'will_be': 'ignored'}})
+
+        assert dist.metadata.long_description_content_type == 'text/something'
+        assert dist.metadata.project_urls == {
+            'Link One': 'https://example.com/one/',
+            'Link Two': 'https://example.com/two/',
+        }
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/contexts.py b/.venv/lib/python3.12/site-packages/setuptools/tests/contexts.py
new file mode 100644
index 0000000..3c931bb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/contexts.py
@@ -0,0 +1,131 @@
+import contextlib
+import io
+import os
+import shutil
+import site
+import sys
+import tempfile
+
+from filelock import FileLock
+
+
+@contextlib.contextmanager
+def tempdir(cd=lambda dir: None, **kwargs):
+    temp_dir = tempfile.mkdtemp(**kwargs)
+    orig_dir = os.getcwd()
+    try:
+        cd(temp_dir)
+        yield temp_dir
+    finally:
+        cd(orig_dir)
+        shutil.rmtree(temp_dir)
+
+
+@contextlib.contextmanager
+def environment(**replacements):
+    """
+    In a context, patch the environment with replacements. Pass None values
+    to clear the values.
+    """
+    saved = dict((key, os.environ[key]) for key in replacements if key in os.environ)
+
+    # remove values that are null
+    remove = (key for (key, value) in replacements.items() if value is None)
+    for key in list(remove):
+        os.environ.pop(key, None)
+        replacements.pop(key)
+
+    os.environ.update(replacements)
+
+    try:
+        yield saved
+    finally:
+        for key in replacements:
+            os.environ.pop(key, None)
+        os.environ.update(saved)
+
+
+@contextlib.contextmanager
+def quiet():
+    """
+    Redirect stdout/stderr to StringIO objects to prevent console output from
+    distutils commands.
+    """
+
+    old_stdout = sys.stdout
+    old_stderr = sys.stderr
+    new_stdout = sys.stdout = io.StringIO()
+    new_stderr = sys.stderr = io.StringIO()
+    try:
+        yield new_stdout, new_stderr
+    finally:
+        new_stdout.seek(0)
+        new_stderr.seek(0)
+        sys.stdout = old_stdout
+        sys.stderr = old_stderr
+
+
+@contextlib.contextmanager
+def save_user_site_setting():
+    saved = site.ENABLE_USER_SITE
+    try:
+        yield saved
+    finally:
+        site.ENABLE_USER_SITE = saved
+
+
+@contextlib.contextmanager
+def suppress_exceptions(*excs):
+    try:
+        yield
+    except excs:
+        pass
+
+
+def multiproc(request):
+    """
+    Return True if running under xdist and multiple
+    workers are used.
+    """
+    try:
+        worker_id = request.getfixturevalue('worker_id')
+    except Exception:
+        return False
+    return worker_id != 'master'
+
+
+@contextlib.contextmanager
+def session_locked_tmp_dir(request, tmp_path_factory, name):
+    """Uses a file lock to guarantee only one worker can access a temp dir"""
+    # get the temp directory shared by all workers
+    base = tmp_path_factory.getbasetemp()
+    shared_dir = base.parent if multiproc(request) else base
+
+    locked_dir = shared_dir / name
+    with FileLock(locked_dir.with_suffix(".lock")):
+        # ^-- prevent multiple workers to access the directory at once
+        locked_dir.mkdir(exist_ok=True, parents=True)
+        yield locked_dir
+
+
+@contextlib.contextmanager
+def save_paths():
+    """Make sure ``sys.path``, ``sys.meta_path`` and ``sys.path_hooks`` are preserved"""
+    prev = sys.path[:], sys.meta_path[:], sys.path_hooks[:]
+
+    try:
+        yield
+    finally:
+        sys.path, sys.meta_path, sys.path_hooks = prev
+
+
+@contextlib.contextmanager
+def save_sys_modules():
+    """Make sure initial ``sys.modules`` is preserved"""
+    prev_modules = sys.modules
+
+    try:
+        sys.modules = sys.modules.copy()
+        yield
+    finally:
+        sys.modules = prev_modules
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/environment.py b/.venv/lib/python3.12/site-packages/setuptools/tests/environment.py
new file mode 100644
index 0000000..ed5499e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/environment.py
@@ -0,0 +1,95 @@
+import os
+import subprocess
+import sys
+import unicodedata
+from subprocess import PIPE as _PIPE, Popen as _Popen
+
+import jaraco.envs
+
+
+class VirtualEnv(jaraco.envs.VirtualEnv):
+    name = '.env'
+    # Some version of PyPy will import distutils on startup, implicitly
+    # importing setuptools, and thus leading to BackendInvalid errors
+    # when upgrading Setuptools. Bypass this behavior by avoiding the
+    # early availability and need to upgrade.
+    create_opts = ['--no-setuptools']
+
+    def run(self, cmd, *args, **kwargs):
+        cmd = [self.exe(cmd[0])] + cmd[1:]
+        kwargs = {"cwd": self.root, "encoding": "utf-8", **kwargs}  # Allow overriding
+        # In some environments (eg. downstream distro packaging), where:
+        # - tox isn't used to run tests and
+        # - PYTHONPATH is set to point to a specific setuptools codebase and
+        # - no custom env is explicitly set by a test
+        # PYTHONPATH will leak into the spawned processes.
+        # In that case tests look for module in the wrong place (on PYTHONPATH).
+        # Unless the test sets its own special env, pass a copy of the existing
+        # environment with removed PYTHONPATH to the subprocesses.
+        if "env" not in kwargs:
+            env = dict(os.environ)
+            if "PYTHONPATH" in env:
+                del env["PYTHONPATH"]
+            kwargs["env"] = env
+        return subprocess.check_output(cmd, *args, **kwargs)
+
+
+def _which_dirs(cmd):
+    result = set()
+    for path in os.environ.get('PATH', '').split(os.pathsep):
+        filename = os.path.join(path, cmd)
+        if os.access(filename, os.X_OK):
+            result.add(path)
+    return result
+
+
+def run_setup_py(cmd, pypath=None, path=None, data_stream=0, env=None):
+    """
+    Execution command for tests, separate from those used by the
+    code directly to prevent accidental behavior issues
+    """
+    if env is None:
+        env = dict()
+        for envname in os.environ:
+            env[envname] = os.environ[envname]
+
+    # override the python path if needed
+    if pypath is not None:
+        env["PYTHONPATH"] = pypath
+
+    # override the execution path if needed
+    if path is not None:
+        env["PATH"] = path
+    if not env.get("PATH", ""):
+        env["PATH"] = _which_dirs("tar").union(_which_dirs("gzip"))
+        env["PATH"] = os.pathsep.join(env["PATH"])
+
+    cmd = [sys.executable, "setup.py"] + list(cmd)
+
+    # https://bugs.python.org/issue8557
+    shell = sys.platform == 'win32'
+
+    try:
+        proc = _Popen(
+            cmd,
+            stdout=_PIPE,
+            stderr=_PIPE,
+            shell=shell,
+            env=env,
+            encoding="utf-8",
+        )
+
+        if isinstance(data_stream, tuple):
+            data_stream = slice(*data_stream)
+        data = proc.communicate()[data_stream]
+    except OSError:
+        return 1, ''
+
+    # decode the console string if needed
+    if hasattr(data, "decode"):
+        # use the default encoding
+        data = data.decode()
+        data = unicodedata.normalize('NFC', data)
+
+    # communicate calls wait()
+    return proc.returncode, data
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/fixtures.py b/.venv/lib/python3.12/site-packages/setuptools/tests/fixtures.py
new file mode 100644
index 0000000..20b31d4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/fixtures.py
@@ -0,0 +1,406 @@
+import contextlib
+import io
+import os
+import subprocess
+import sys
+import tarfile
+import time
+from pathlib import Path
+
+import jaraco.path
+import path
+import pytest
+
+from setuptools._normalization import safer_name
+
+from . import contexts, environment
+from .textwrap import DALS
+
+
+@pytest.fixture
+def user_override(monkeypatch):
+    """
+    Override site.USER_BASE and site.USER_SITE with temporary directories in
+    a context.
+    """
+    with contexts.tempdir() as user_base:
+        monkeypatch.setattr('site.USER_BASE', user_base)
+        with contexts.tempdir() as user_site:
+            monkeypatch.setattr('site.USER_SITE', user_site)
+            with contexts.save_user_site_setting():
+                yield
+
+
+@pytest.fixture
+def tmpdir_cwd(tmpdir):
+    with tmpdir.as_cwd() as orig:
+        yield orig
+
+
+@pytest.fixture(autouse=True, scope="session")
+def workaround_xdist_376(request):
+    """
+    Workaround pytest-dev/pytest-xdist#376
+
+    ``pytest-xdist`` tends to inject '' into ``sys.path``,
+    which may break certain isolation expectations.
+    Remove the entry so the import
+    machinery behaves the same irrespective of xdist.
+    """
+    if not request.config.pluginmanager.has_plugin('xdist'):
+        return
+
+    with contextlib.suppress(ValueError):
+        sys.path.remove('')
+
+
+@pytest.fixture
+def sample_project(tmp_path):
+    """
+    Clone the 'sampleproject' and return a path to it.
+    """
+    cmd = ['git', 'clone', 'https://github.com/pypa/sampleproject']
+    try:
+        subprocess.check_call(cmd, cwd=str(tmp_path))
+    except Exception:
+        pytest.skip("Unable to clone sampleproject")
+    return tmp_path / 'sampleproject'
+
+
+@pytest.fixture
+def sample_project_cwd(sample_project):
+    with path.Path(sample_project):
+        yield
+
+
+# sdist and wheel artifacts should be stable across a round of tests
+# so we can build them once per session and use the files as "readonly"
+
+# In the case of setuptools, building the wheel without sdist may cause
+# it to contain the `build` directory, and therefore create situations with
+# `setuptools/build/lib/build/lib/...`. To avoid that, build both artifacts at once.
+
+
+def _build_distributions(tmp_path_factory, request):
+    with contexts.session_locked_tmp_dir(
+        request, tmp_path_factory, "dist_build"
+    ) as tmp:  # pragma: no cover
+        sdist = next(tmp.glob("*.tar.gz"), None)
+        wheel = next(tmp.glob("*.whl"), None)
+        if sdist and wheel:
+            return (sdist, wheel)
+
+        # Sanity check: should not create recursive setuptools/build/lib/build/lib/...
+        assert not Path(request.config.rootdir, "build/lib/build").exists()
+
+        subprocess.check_output([
+            sys.executable,
+            "-m",
+            "build",
+            "--outdir",
+            str(tmp),
+            str(request.config.rootdir),
+        ])
+
+        # Sanity check: should not create recursive setuptools/build/lib/build/lib/...
+        assert not Path(request.config.rootdir, "build/lib/build").exists()
+
+        return next(tmp.glob("*.tar.gz")), next(tmp.glob("*.whl"))
+
+
+@pytest.fixture(scope="session")
+def setuptools_sdist(tmp_path_factory, request):
+    prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_SDIST")
+    if prebuilt and os.path.exists(prebuilt):  # pragma: no cover
+        return Path(prebuilt).resolve()
+
+    sdist, _ = _build_distributions(tmp_path_factory, request)
+    return sdist
+
+
+@pytest.fixture(scope="session")
+def setuptools_wheel(tmp_path_factory, request):
+    prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_WHEEL")
+    if prebuilt and os.path.exists(prebuilt):  # pragma: no cover
+        return Path(prebuilt).resolve()
+
+    _, wheel = _build_distributions(tmp_path_factory, request)
+    return wheel
+
+
+@pytest.fixture
+def venv(tmp_path, setuptools_wheel):
+    """Virtual env with the version of setuptools under test installed"""
+    env = environment.VirtualEnv()
+    env.root = path.Path(tmp_path / 'venv')
+    env.create_opts = ['--no-setuptools', '--wheel=bundle']
+    # TODO: Use `--no-wheel` when setuptools implements its own bdist_wheel
+    env.req = str(setuptools_wheel)
+    # In some environments (eg. downstream distro packaging),
+    # where tox isn't used to run tests and PYTHONPATH is set to point to
+    # a specific setuptools codebase, PYTHONPATH will leak into the spawned
+    # processes.
+    # env.create() should install the just created setuptools
+    # wheel, but it doesn't if it finds another existing matching setuptools
+    # installation present on PYTHONPATH:
+    # `setuptools is already installed with the same version as the provided
+    # wheel. Use --force-reinstall to force an installation of the wheel.`
+    # This prevents leaking PYTHONPATH to the created environment.
+    with contexts.environment(PYTHONPATH=None):
+        return env.create()
+
+
+@pytest.fixture
+def venv_without_setuptools(tmp_path):
+    """Virtual env without any version of setuptools installed"""
+    env = environment.VirtualEnv()
+    env.root = path.Path(tmp_path / 'venv_without_setuptools')
+    env.create_opts = ['--no-setuptools', '--no-wheel']
+    env.ensure_env()
+    return env
+
+
+@pytest.fixture
+def bare_venv(tmp_path):
+    """Virtual env without any common packages installed"""
+    env = environment.VirtualEnv()
+    env.root = path.Path(tmp_path / 'bare_venv')
+    env.create_opts = ['--no-setuptools', '--no-pip', '--no-wheel', '--no-seed']
+    env.ensure_env()
+    return env
+
+
+def make_sdist(dist_path, files):
+    """
+    Create a simple sdist tarball at dist_path, containing the files
+    listed in ``files`` as ``(filename, content)`` tuples.
+    """
+
+    # Distributions with only one file don't play well with pip.
+    assert len(files) > 1
+    with tarfile.open(dist_path, 'w:gz') as dist:
+        for filename, content in files:
+            file_bytes = io.BytesIO(content.encode('utf-8'))
+            file_info = tarfile.TarInfo(name=filename)
+            file_info.size = len(file_bytes.getvalue())
+            file_info.mtime = int(time.time())
+            dist.addfile(file_info, fileobj=file_bytes)
+
+
+def make_trivial_sdist(dist_path, distname, version, setuptools_wheel=None):
+    """
+    Create a simple sdist tarball at dist_path, containing just a simple
+    setup.py.
+
+    If ``setuptools_wheel`` is passed, a ``pyproject.toml`` file will also
+    be generated and the passed value will be used as location for
+    setuptools (as build dependency).
+    """
+    files = [
+        (
+            'setup.py',
+            DALS(
+                f"""\
+                 import setuptools
+                 setuptools.setup(
+                     name={distname!r},
+                     version={version!r}
+                 )
+                 """
+            ),
+        ),
+        ('setup.cfg', ''),
+    ]
+
+    if setuptools_wheel:
+        files.append((
+            "pyproject.toml",
+            DALS(
+                f"""\
+                [build-system]
+                requires = ["setuptools @ {setuptools_wheel.as_uri()}"]
+                build-backend = "setuptools.build_meta"
+                """
+            ),
+        ))
+
+    make_sdist(dist_path, files)
+
+
+def make_nspkg_sdist(dist_path, distname, version):
+    """
+    Make an sdist tarball with distname and version which also contains one
+    package with the same name as distname.  The top-level package is
+    designated a namespace package).
+    """
+    # Assert that the distname contains at least one period
+    assert '.' in distname
+
+    parts = distname.split('.')
+    nspackage = parts[0]
+
+    packages = ['.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)]
+
+    setup_py = DALS(
+        f"""\
+        import setuptools
+        setuptools.setup(
+            name={distname!r},
+            version={version!r},
+            packages={packages!r},
+            namespace_packages=[{nspackage!r}]
+        )
+    """
+    )
+
+    init = "__import__('pkg_resources').declare_namespace(__name__)"
+
+    files = [('setup.py', setup_py), (os.path.join(nspackage, '__init__.py'), init)]
+    for package in packages[1:]:
+        filename = os.path.join(*(package.split('.') + ['__init__.py']))
+        files.append((filename, ''))
+
+    make_sdist(dist_path, files)
+
+
+def make_python_requires_sdist(dist_path, distname, version, python_requires):
+    make_sdist(
+        dist_path,
+        [
+            (
+                'setup.py',
+                DALS(
+                    """\
+                import setuptools
+                setuptools.setup(
+                  name={name!r},
+                  version={version!r},
+                  python_requires={python_requires!r},
+                )
+                """
+                ).format(
+                    name=distname, version=version, python_requires=python_requires
+                ),
+            ),
+            ('setup.cfg', ''),
+        ],
+    )
+
+
+def create_setup_requires_package(
+    path,
+    distname='foobar',
+    version='0.1',
+    make_package=make_trivial_sdist,
+    setup_py_template=None,
+    setup_attrs=None,
+    use_setup_cfg=(),
+):
+    """Creates a source tree under path for a trivial test package that has a
+    single requirement in setup_requires--a tarball for that requirement is
+    also created and added to the dependency_links argument.
+
+    ``distname`` and ``version`` refer to the name/version of the package that
+    the test package requires via ``setup_requires``.  The name of the test
+    package itself is just 'test_pkg'.
+    """
+
+    normalized_distname = safer_name(distname)
+    test_setup_attrs = {
+        'name': 'test_pkg',
+        'version': '0.0',
+        'setup_requires': [f'{normalized_distname}=={version}'],
+        'dependency_links': [os.path.abspath(path)],
+    }
+    if setup_attrs:
+        test_setup_attrs.update(setup_attrs)
+
+    test_pkg = os.path.join(path, 'test_pkg')
+    os.mkdir(test_pkg)
+
+    # setup.cfg
+    if use_setup_cfg:
+        options = []
+        metadata = []
+        for name in use_setup_cfg:
+            value = test_setup_attrs.pop(name)
+            if name in 'name version'.split():
+                section = metadata
+            else:
+                section = options
+            if isinstance(value, (tuple, list)):
+                value = ';'.join(value)
+            section.append(f'{name}: {value}')
+        test_setup_cfg_contents = DALS(
+            """
+            [metadata]
+            {metadata}
+            [options]
+            {options}
+            """
+        ).format(
+            options='\n'.join(options),
+            metadata='\n'.join(metadata),
+        )
+    else:
+        test_setup_cfg_contents = ''
+    with open(os.path.join(test_pkg, 'setup.cfg'), 'w', encoding="utf-8") as f:
+        f.write(test_setup_cfg_contents)
+
+    # setup.py
+    if setup_py_template is None:
+        setup_py_template = DALS(
+            """\
+            import setuptools
+            setuptools.setup(**%r)
+        """
+        )
+    with open(os.path.join(test_pkg, 'setup.py'), 'w', encoding="utf-8") as f:
+        f.write(setup_py_template % test_setup_attrs)
+
+    foobar_path = os.path.join(path, f'{normalized_distname}-{version}.tar.gz')
+    make_package(foobar_path, distname, version)
+
+    return test_pkg
+
+
+@pytest.fixture
+def pbr_package(tmp_path, monkeypatch, venv):
+    files = {
+        "pyproject.toml": DALS(
+            """
+            [build-system]
+            requires = ["setuptools"]
+            build-backend = "setuptools.build_meta"
+            """
+        ),
+        "setup.py": DALS(
+            """
+            __import__('setuptools').setup(
+                pbr=True,
+                setup_requires=["pbr"],
+            )
+            """
+        ),
+        "setup.cfg": DALS(
+            """
+            [metadata]
+            name = mypkg
+
+            [files]
+            packages =
+                mypkg
+            """
+        ),
+        "mypkg": {
+            "__init__.py": "",
+            "hello.py": "print('Hello world!')",
+        },
+        "other": {"test.txt": "Another file in here."},
+    }
+    venv.run(["python", "-m", "pip", "install", "pbr"])
+    prefix = tmp_path / 'mypkg'
+    prefix.mkdir()
+    jaraco.path.build(files, prefix=prefix)
+    monkeypatch.setenv('PBR_VERSION', "0.42")
+    return prefix
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html b/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html
new file mode 100644
index 0000000..92e4702
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/external.html
@@ -0,0 +1,3 @@
+
+bad old link
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html b/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html
new file mode 100644
index 0000000..fefb028
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/tests/indexes/test_links_priority/simple/foobar/index.html
@@ -0,0 +1,4 @@
+
+foobar-0.1.tar.gz
+external homepage
+ diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/integration/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py new file mode 100644 index 0000000..16b1302 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/helpers.py @@ -0,0 +1,80 @@ +"""Reusable functions and classes for different types of integration tests. + +For example ``Archive`` can be used to check the contents of distribution built +with setuptools, and ``run`` will always try to be as verbose as possible to +facilitate debugging. +""" + +from __future__ import annotations + +import os +import subprocess +import tarfile +from collections.abc import Iterator +from pathlib import Path +from zipfile import ZipFile, ZipInfo + + +def run(cmd, env=None): + r = subprocess.run( + cmd, + capture_output=True, + text=True, + encoding="utf-8", + env={**os.environ, **(env or {})}, + # ^-- allow overwriting instead of discarding the current env + ) + + out = r.stdout + "\n" + r.stderr + # pytest omits stdout/err by default, if the test fails they help debugging + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print(f"Command: {cmd}\nreturn code: {r.returncode}\n\n{out}") + + if r.returncode == 0: + return out + raise subprocess.CalledProcessError(r.returncode, cmd, r.stdout, r.stderr) + + +class Archive: + """Compatibility layer for ZipFile/Info and TarFile/Info""" + + def __init__(self, filename) -> None: + self._filename = filename + if filename.endswith("tar.gz"): + self._obj: tarfile.TarFile | ZipFile = tarfile.open(filename, "r:gz") + elif filename.endswith("zip"): + self._obj = ZipFile(filename) + else: + raise ValueError(f"{filename} doesn't seem to be a zip or tar.gz") + + def __iter__(self) -> Iterator[ZipInfo] | Iterator[tarfile.TarInfo]: + if hasattr(self._obj, "infolist"): + return iter(self._obj.infolist()) + return iter(self._obj) + + def get_name(self, zip_or_tar_info): + if hasattr(zip_or_tar_info, "filename"): + return zip_or_tar_info.filename + return zip_or_tar_info.name + + def get_content(self, zip_or_tar_info): + if hasattr(self._obj, "extractfile"): + content = self._obj.extractfile(zip_or_tar_info) + if content is None: + msg = f"Invalid {zip_or_tar_info.name} in {self._filename}" + raise ValueError(msg) + return str(content.read(), "utf-8") + return str(self._obj.read(zip_or_tar_info), "utf-8") + + +def get_sdist_members(sdist_path): + with tarfile.open(sdist_path, "r:gz") as tar: + files = [Path(f) for f in tar.getnames()] + # remove root folder + relative_files = ("/".join(f.parts[1:]) for f in files) + return {f for f in relative_files if f} + + +def get_wheel_members(wheel_path): + with ZipFile(wheel_path) as zipfile: + return set(zipfile.namelist()) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py new file mode 100644 index 0000000..f89e5b8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pbr.py @@ -0,0 +1,20 @@ +import subprocess + +import pytest + + +@pytest.mark.uses_network +def test_pbr_integration(pbr_package, venv): + """Ensure pbr packages install.""" + cmd = [ + 'python', + '-m', + 'pip', + '-v', + 'install', + '--no-build-isolation', + pbr_package, + ] + venv.run(cmd, stderr=subprocess.STDOUT) + out = venv.run(["python", "-c", "import mypkg.hello"]) + assert "Hello world!" in out diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py new file mode 100644 index 0000000..4e84f21 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/integration/test_pip_install_sdist.py @@ -0,0 +1,223 @@ +# https://github.com/python/mypy/issues/16936 +# mypy: disable-error-code="has-type" +"""Integration tests for setuptools that focus on building packages via pip. + +The idea behind these tests is not to exhaustively check all the possible +combinations of packages, operating systems, supporting libraries, etc, but +rather check a limited number of popular packages and how they interact with +the exposed public API. This way if any change in API is introduced, we hope to +identify backward compatibility problems before publishing a release. + +The number of tested packages is purposefully kept small, to minimise duration +and the associated maintenance cost (changes in the way these packages define +their build process may require changes in the tests). +""" + +import json +import os +import shutil +import sys +from enum import Enum +from glob import glob +from hashlib import md5 +from urllib.request import urlopen + +import pytest +from packaging.requirements import Requirement + +from .helpers import Archive, run + +pytestmark = pytest.mark.integration + + +(LATEST,) = Enum("v", "LATEST") # type: ignore[misc] # https://github.com/python/mypy/issues/16936 +"""Default version to be checked""" +# There are positive and negative aspects of checking the latest version of the +# packages. +# The main positive aspect is that the latest version might have already +# removed the use of APIs deprecated in previous releases of setuptools. + + +# Packages to be tested: +# (Please notice the test environment cannot support EVERY library required for +# compiling binary extensions. In Ubuntu/Debian nomenclature, we only assume +# that `build-essential`, `gfortran` and `libopenblas-dev` are installed, +# due to their relevance to the numerical/scientific programming ecosystem) +EXAMPLES = [ + ("pip", LATEST), # just in case... + ("pytest", LATEST), # uses setuptools_scm + ("mypy", LATEST), # custom build_py + ext_modules + # --- Popular packages: https://hugovk.github.io/top-pypi-packages/ --- + ("botocore", LATEST), + ("kiwisolver", LATEST), # build_ext + ("brotli", LATEST), # not in the list but used by urllib3 + ("pyyaml", LATEST), # cython + custom build_ext + custom distclass + ("charset-normalizer", LATEST), # uses mypyc, used by aiohttp + ("protobuf", LATEST), + # ("requests", LATEST), # XXX: https://github.com/psf/requests/pull/6920 + ("celery", LATEST), + # When adding packages to this list, make sure they expose a `__version__` + # attribute, or modify the tests below +] + + +# Some packages have "optional" dependencies that modify their build behaviour +# and are not listed in pyproject.toml, others still use `setup_requires` +EXTRA_BUILD_DEPS = { + "pyyaml": ("Cython<3.0",), # constraint to avoid errors + "charset-normalizer": ("mypy>=1.4.1",), # no pyproject.toml available +} + +EXTRA_ENV_VARS = { + "pyyaml": {"PYYAML_FORCE_CYTHON": "1"}, + "charset-normalizer": {"CHARSET_NORMALIZER_USE_MYPYC": "1"}, +} + +IMPORT_NAME = { + "pyyaml": "yaml", + "protobuf": "google.protobuf", +} + + +VIRTUALENV = (sys.executable, "-m", "virtualenv") + + +# By default, pip will try to build packages in isolation (PEP 517), which +# means it will download the previous stable version of setuptools. +# `pip` flags can avoid that (the version of setuptools under test +# should be the one to be used) +INSTALL_OPTIONS = ( + "--ignore-installed", + "--no-build-isolation", + # Omit "--no-binary :all:" the sdist is supplied directly. + # Allows dependencies as wheels. +) +# The downside of `--no-build-isolation` is that pip will not download build +# dependencies. The test script will have to also handle that. + + +@pytest.fixture +def venv_python(tmp_path): + run([*VIRTUALENV, str(tmp_path / ".venv")]) + possible_path = (str(p.parent) for p in tmp_path.glob(".venv/*/python*")) + return shutil.which("python", path=os.pathsep.join(possible_path)) + + +@pytest.fixture(autouse=True) +def _prepare(tmp_path, venv_python, monkeypatch): + download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path)) + os.makedirs(download_path, exist_ok=True) + + # Environment vars used for building some of the packages + monkeypatch.setenv("USE_MYPYC", "1") + + yield + + # Let's provide the maximum amount of information possible in the case + # it is necessary to debug the tests directly from the CI logs. + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print("Temporary directory:") + map(print, tmp_path.glob("*")) + print("Virtual environment:") + run([venv_python, "-m", "pip", "freeze"]) + + +@pytest.mark.parametrize(("package", "version"), EXAMPLES) +@pytest.mark.uses_network +def test_install_sdist(package, version, tmp_path, venv_python, setuptools_wheel): + venv_pip = (venv_python, "-m", "pip") + sdist = retrieve_sdist(package, version, tmp_path) + deps = build_deps(package, sdist) + if deps: + print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") + print("Dependencies:", deps) + run([*venv_pip, "install", *deps]) + + # Use a virtualenv to simulate PEP 517 isolation + # but install fresh setuptools wheel to ensure the version under development + env = EXTRA_ENV_VARS.get(package, {}) + run([*venv_pip, "install", "--force-reinstall", setuptools_wheel]) + run([*venv_pip, "install", *INSTALL_OPTIONS, sdist], env) + + # Execute a simple script to make sure the package was installed correctly + pkg = IMPORT_NAME.get(package, package).replace("-", "_") + script = f"import {pkg}; print(getattr({pkg}, '__version__', 0))" + run([venv_python, "-c", script]) + + +# ---- Helper Functions ---- + + +def retrieve_sdist(package, version, tmp_path): + """Either use cached sdist file or download it from PyPI""" + # `pip download` cannot be used due to + # https://github.com/pypa/pip/issues/1884 + # https://discuss.python.org/t/pep-625-file-name-of-a-source-distribution/4686 + # We have to find the correct distribution file and download it + download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path)) + dist = retrieve_pypi_sdist_metadata(package, version) + + # Remove old files to prevent cache to grow indefinitely + for file in glob(os.path.join(download_path, f"{package}*")): + if dist["filename"] != file: + os.unlink(file) + + dist_file = os.path.join(download_path, dist["filename"]) + if not os.path.exists(dist_file): + download(dist["url"], dist_file, dist["md5_digest"]) + return dist_file + + +def retrieve_pypi_sdist_metadata(package, version): + # https://warehouse.pypa.io/api-reference/json.html + id_ = package if version is LATEST else f"{package}/{version}" + with urlopen(f"https://pypi.org/pypi/{id_}/json") as f: + metadata = json.load(f) + + if metadata["info"]["yanked"]: + raise ValueError(f"Release for {package} {version} was yanked") + + version = metadata["info"]["version"] + release = metadata["releases"][version] if version is LATEST else metadata["urls"] + (sdist,) = filter(lambda d: d["packagetype"] == "sdist", release) + return sdist + + +def download(url, dest, md5_digest): + with urlopen(url) as f: + data = f.read() + + assert md5(data).hexdigest() == md5_digest + + with open(dest, "wb") as f: + f.write(data) + + assert os.path.exists(dest) + + +def build_deps(package, sdist_file): + """Find out what are the build dependencies for a package. + + "Manually" install them, since pip will not install build + deps with `--no-build-isolation`. + """ + # delay importing, since pytest discovery phase may hit this file from a + # testenv without tomli + from setuptools.compat.py310 import tomllib + + archive = Archive(sdist_file) + info = tomllib.loads(_read_pyproject(archive)) + deps = info.get("build-system", {}).get("requires", []) + deps += EXTRA_BUILD_DEPS.get(package, []) + # Remove setuptools from requirements (and deduplicate) + requirements = {Requirement(d).name: d for d in deps} + return [v for k, v in requirements.items() if k != "setuptools"] + + +def _read_pyproject(archive): + contents = ( + archive.get_content(member) + for member in archive + if os.path.basename(archive.get_name(member)) == "pyproject.toml" + ) + return next(contents, "") diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py b/.venv/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py new file mode 100644 index 0000000..ef755dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/mod_with_constant.py @@ -0,0 +1 @@ +value = 'three, sir!' diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/namespaces.py b/.venv/lib/python3.12/site-packages/setuptools/tests/namespaces.py new file mode 100644 index 0000000..248db98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/namespaces.py @@ -0,0 +1,90 @@ +import ast +import json +import textwrap +from pathlib import Path + + +def iter_namespace_pkgs(namespace): + parts = namespace.split(".") + for i in range(len(parts)): + yield ".".join(parts[: i + 1]) + + +def build_namespace_package(tmpdir, name, version="1.0", impl="pkg_resources"): + src_dir = tmpdir / name + src_dir.mkdir() + setup_py = src_dir / 'setup.py' + namespace, _, rest = name.rpartition('.') + namespaces = list(iter_namespace_pkgs(namespace)) + setup_args = { + "name": name, + "version": version, + "packages": namespaces, + } + + if impl == "pkg_resources": + tmpl = '__import__("pkg_resources").declare_namespace(__name__)' + setup_args["namespace_packages"] = namespaces + elif impl == "pkgutil": + tmpl = '__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + else: + raise ValueError(f"Cannot recognise {impl=} when creating namespaces") + + args = json.dumps(setup_args, indent=4) + assert ast.literal_eval(args) # ensure it is valid Python + + script = textwrap.dedent( + """\ + import setuptools + args = {args} + setuptools.setup(**args) + """ + ).format(args=args) + setup_py.write_text(script, encoding='utf-8') + + ns_pkg_dir = Path(src_dir, namespace.replace(".", "/")) + ns_pkg_dir.mkdir(parents=True) + + for ns in namespaces: + pkg_init = src_dir / ns.replace(".", "/") / '__init__.py' + pkg_init.write_text(tmpl, encoding='utf-8') + + pkg_mod = ns_pkg_dir / (rest + '.py') + some_functionality = 'name = {rest!r}'.format(**locals()) + pkg_mod.write_text(some_functionality, encoding='utf-8') + return src_dir + + +def build_pep420_namespace_package(tmpdir, name): + src_dir = tmpdir / name + src_dir.mkdir() + pyproject = src_dir / "pyproject.toml" + namespace, _, rest = name.rpartition(".") + script = f"""\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "{name}" + version = "3.14159" + """ + pyproject.write_text(textwrap.dedent(script), encoding='utf-8') + ns_pkg_dir = Path(src_dir, namespace.replace(".", "/")) + ns_pkg_dir.mkdir(parents=True) + pkg_mod = ns_pkg_dir / (rest + ".py") + some_functionality = f"name = {rest!r}" + pkg_mod.write_text(some_functionality, encoding='utf-8') + return src_dir + + +def make_site_dir(target): + """ + Add a sitecustomize.py module in target to cause + target to be added to site dirs such that .pth files + are processed there. + """ + sc = target / 'sitecustomize.py' + target_str = str(target) + tmpl = '__import__("site").addsitedir({target_str!r})' + sc.write_text(tmpl.format(**locals()), encoding='utf-8') diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py b/.venv/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py new file mode 100644 index 0000000..c074d26 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/script-with-bom.py @@ -0,0 +1 @@ +result = 'passed' diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py new file mode 100644 index 0000000..e3efc62 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_archive_util.py @@ -0,0 +1,36 @@ +import io +import tarfile + +import pytest + +from setuptools import archive_util + + +@pytest.fixture +def tarfile_with_unicode(tmpdir): + """ + Create a tarfile containing only a file whose name is + a zero byte file called testimäge.png. + """ + tarobj = io.BytesIO() + + with tarfile.open(fileobj=tarobj, mode="w:gz") as tgz: + data = b"" + + filename = "testimäge.png" + + t = tarfile.TarInfo(filename) + t.size = len(data) + + tgz.addfile(t, io.BytesIO(data)) + + target = tmpdir / 'unicode-pkg-1.0.tar.gz' + with open(str(target), mode='wb') as tf: + tf.write(tarobj.getvalue()) + return str(target) + + +@pytest.mark.xfail(reason="#710 and #712") +def test_unicode_files(tarfile_with_unicode, tmpdir): + target = tmpdir / 'out' + archive_util.unpack_archive(tarfile_with_unicode, str(target)) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py new file mode 100644 index 0000000..d9d67b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_deprecations.py @@ -0,0 +1,28 @@ +"""develop tests""" + +import sys +from unittest import mock + +import pytest + +from setuptools import SetuptoolsDeprecationWarning +from setuptools.dist import Distribution + + +@pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') +@pytest.mark.xfail(reason="bdist_rpm is long deprecated, should we remove it? #1988") +@mock.patch('distutils.command.bdist_rpm.bdist_rpm') +def test_bdist_rpm_warning(distutils_cmd, tmpdir_cwd): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_rpm'], + name='foo', + py_modules=['hi'], + ) + ) + dist.parse_command_line() + with pytest.warns(SetuptoolsDeprecationWarning): + dist.run_commands() + + distutils_cmd.run.assert_called_once() diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py new file mode 100644 index 0000000..036167d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_egg.py @@ -0,0 +1,73 @@ +"""develop tests""" + +import os +import re +import zipfile + +import pytest + +from setuptools.dist import Distribution + +from . import contexts + +SETUP_PY = """\ +from setuptools import setup + +setup(py_modules=['hi']) +""" + + +@pytest.fixture +def setup_context(tmpdir): + with (tmpdir / 'setup.py').open('w') as f: + f.write(SETUP_PY) + with (tmpdir / 'hi.py').open('w') as f: + f.write('1\n') + with tmpdir.as_cwd(): + yield tmpdir + + +class Test: + @pytest.mark.usefixtures("user_override") + @pytest.mark.usefixtures("setup_context") + def test_bdist_egg(self): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_egg'], + name='foo', + py_modules=['hi'], + ) + ) + os.makedirs(os.path.join('build', 'src')) + with contexts.quiet(): + dist.parse_command_line() + dist.run_commands() + + # let's see if we got our egg link at the right place + [content] = os.listdir('dist') + assert re.match(r'foo-0.0.0-py[23].\d+.egg$', content) + + @pytest.mark.xfail( + os.environ.get('PYTHONDONTWRITEBYTECODE', False), + reason="Byte code disabled", + ) + @pytest.mark.usefixtures("user_override") + @pytest.mark.usefixtures("setup_context") + def test_exclude_source_files(self): + dist = Distribution( + dict( + script_name='setup.py', + script_args=['bdist_egg', '--exclude-source-files'], + py_modules=['hi'], + ) + ) + with contexts.quiet(): + dist.parse_command_line() + dist.run_commands() + [dist_name] = os.listdir('dist') + dist_filename = os.path.join('dist', dist_name) + zip = zipfile.ZipFile(dist_filename) + names = list(zi.filename for zi in zip.filelist) + assert 'hi.pyc' in names + assert 'hi.py' not in names diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py new file mode 100644 index 0000000..68cc0c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_bdist_wheel.py @@ -0,0 +1,708 @@ +from __future__ import annotations + +import builtins +import importlib +import os.path +import platform +import shutil +import stat +import struct +import sys +import sysconfig +from contextlib import suppress +from inspect import cleandoc +from zipfile import ZipFile + +import jaraco.path +import pytest +from packaging import tags + +import setuptools +from setuptools.command.bdist_wheel import bdist_wheel, get_abi_tag +from setuptools.dist import Distribution +from setuptools.warnings import SetuptoolsDeprecationWarning + +from distutils.core import run_setup + +DEFAULT_FILES = { + "dummy_dist-1.0.dist-info/top_level.txt", + "dummy_dist-1.0.dist-info/METADATA", + "dummy_dist-1.0.dist-info/WHEEL", + "dummy_dist-1.0.dist-info/RECORD", +} +DEFAULT_LICENSE_FILES = { + "LICENSE", + "LICENSE.txt", + "LICENCE", + "LICENCE.txt", + "COPYING", + "COPYING.md", + "NOTICE", + "NOTICE.rst", + "AUTHORS", + "AUTHORS.txt", +} +OTHER_IGNORED_FILES = { + "LICENSE~", + "AUTHORS~", +} +SETUPPY_EXAMPLE = """\ +from setuptools import setup + +setup( + name='dummy_dist', + version='1.0', +) +""" + + +EXAMPLES = { + "dummy-dist": { + "setup.py": SETUPPY_EXAMPLE, + "licenses_dir": {"DUMMYFILE": ""}, + **dict.fromkeys(DEFAULT_LICENSE_FILES | OTHER_IGNORED_FILES, ""), + }, + "simple-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="simple.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + extras_require={"voting": ["beaglevote"]}, + ) + """ + ), + "simpledist": "", + }, + "complex-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="complex-dist", + version="0.1", + description="Another testing distribution \N{SNOWMAN}", + long_description="Another testing distribution \N{SNOWMAN}", + author="Illustrious Author", + author_email="illustrious@example.org", + url="http://example.org/exemplary", + packages=["complexdist"], + setup_requires=["setuptools"], + install_requires=["quux", "splort"], + extras_require={"simple": ["simple.dist"]}, + entry_points={ + "console_scripts": [ + "complex-dist=complexdist:main", + "complex-dist2=complexdist:main", + ], + }, + ) + """ + ), + "complexdist": {"__init__.py": "def main(): return"}, + }, + "headers-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="headers.dist", + version="0.1", + description="A distribution with headers", + headers=["header.h"], + ) + """ + ), + "headersdist.py": "", + "header.h": "", + }, + "commasinfilenames-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="testrepo", + version="0.1", + packages=["mypackage"], + description="A test package with commas in file names", + include_package_data=True, + package_data={"mypackage.data": ["*"]}, + ) + """ + ), + "mypackage": { + "__init__.py": "", + "data": {"__init__.py": "", "1,2,3.txt": ""}, + }, + "testrepo-0.1.0": { + "mypackage": {"__init__.py": ""}, + }, + }, + "unicode-dist": { + "setup.py": cleandoc( + """ + from setuptools import setup + + setup( + name="unicode.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + packages=["unicodedist"], + zip_safe=True, + ) + """ + ), + "unicodedist": {"__init__.py": "", "åäö_日本語.py": ""}, + }, + "utf8-metadata-dist": { + "setup.cfg": cleandoc( + """ + [metadata] + name = utf8-metadata-dist + version = 42 + author_email = "John X. Ãørçeč" , Γαμα קּ 東 + long_description = file: README.rst + """ + ), + "README.rst": "UTF-8 描述 説明", + }, + "licenses-dist": { + "setup.cfg": cleandoc( + """ + [metadata] + name = licenses-dist + version = 1.0 + license_files = **/LICENSE + """ + ), + "LICENSE": "", + "src": { + "vendor": {"LICENSE": ""}, + }, + }, +} + + +if sys.platform != "win32": + # ABI3 extensions don't really work on Windows + EXAMPLES["abi3extension-dist"] = { + "setup.py": cleandoc( + """ + from setuptools import Extension, setup + + setup( + name="extension.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + ext_modules=[ + Extension( + name="extension", sources=["extension.c"], py_limited_api=True + ) + ], + ) + """ + ), + "setup.cfg": "[bdist_wheel]\npy_limited_api=cp32", + "extension.c": "#define Py_LIMITED_API 0x03020000\n#include ", + } + + +def bdist_wheel_cmd(**kwargs): + """Run command in the same process so that it is easier to collect coverage""" + dist_obj = ( + run_setup("setup.py", stop_after="init") + if os.path.exists("setup.py") + else Distribution({"script_name": "%%build_meta%%"}) + ) + dist_obj.parse_config_files() + cmd = bdist_wheel(dist_obj) + for attr, value in kwargs.items(): + setattr(cmd, attr, value) + cmd.finalize_options() + return cmd + + +def mkexample(tmp_path_factory, name): + basedir = tmp_path_factory.mktemp(name) + jaraco.path.build(EXAMPLES[name], prefix=str(basedir)) + return basedir + + +@pytest.fixture(scope="session") +def wheel_paths(tmp_path_factory): + build_base = tmp_path_factory.mktemp("build") + dist_dir = tmp_path_factory.mktemp("dist") + for name in EXAMPLES: + example_dir = mkexample(tmp_path_factory, name) + build_dir = build_base / name + with jaraco.path.DirectoryStack().context(example_dir): + bdist_wheel_cmd(bdist_dir=str(build_dir), dist_dir=str(dist_dir)).run() + + return sorted(str(fname) for fname in dist_dir.glob("*.whl")) + + +@pytest.fixture +def dummy_dist(tmp_path_factory): + return mkexample(tmp_path_factory, "dummy-dist") + + +@pytest.fixture +def licenses_dist(tmp_path_factory): + return mkexample(tmp_path_factory, "licenses-dist") + + +def test_no_scripts(wheel_paths): + """Make sure entry point scripts are not generated.""" + path = next(path for path in wheel_paths if "complex_dist" in path) + for entry in ZipFile(path).infolist(): + assert ".data/scripts/" not in entry.filename + + +def test_unicode_record(wheel_paths): + path = next(path for path in wheel_paths if "unicode_dist" in path) + with ZipFile(path) as zf: + record = zf.read("unicode_dist-0.1.dist-info/RECORD") + + assert "åäö_日本語.py".encode() in record + + +UTF8_PKG_INFO = """\ +Metadata-Version: 2.1 +Name: helloworld +Version: 42 +Author-email: "John X. Ãørçeč" , Γαμα קּ 東 + + +UTF-8 描述 説明 +""" + + +def test_preserve_unicode_metadata(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + egginfo = tmp_path / "dummy_dist.egg-info" + distinfo = tmp_path / "dummy_dist.dist-info" + + egginfo.mkdir() + (egginfo / "PKG-INFO").write_text(UTF8_PKG_INFO, encoding="utf-8") + (egginfo / "dependency_links.txt").touch() + + class simpler_bdist_wheel(bdist_wheel): + """Avoid messing with setuptools/distutils internals""" + + def __init__(self) -> None: + pass + + @property + def license_paths(self): + return [] + + cmd_obj = simpler_bdist_wheel() + cmd_obj.egg2dist(egginfo, distinfo) + + metadata = (distinfo / "METADATA").read_text(encoding="utf-8") + assert 'Author-email: "John X. Ãørçeč"' in metadata + assert "Γαμα קּ 東 " in metadata + assert "UTF-8 描述 説明" in metadata + + +def test_licenses_default(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = { + "dummy_dist-1.0.dist-info/licenses/" + fname + for fname in DEFAULT_LICENSE_FILES + } + assert set(wf.namelist()) == DEFAULT_FILES | license_files + + +def test_licenses_deprecated(dummy_dist, monkeypatch, tmp_path): + dummy_dist.joinpath("setup.cfg").write_text( + "[metadata]\nlicense_file=licenses_dir/DUMMYFILE", encoding="utf-8" + ) + monkeypatch.chdir(dummy_dist) + + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = {"dummy_dist-1.0.dist-info/licenses/licenses_dir/DUMMYFILE"} + assert set(wf.namelist()) == DEFAULT_FILES | license_files + + +@pytest.mark.parametrize( + ("config_file", "config"), + [ + ("setup.cfg", "[metadata]\nlicense_files=licenses_dir/*\n LICENSE"), + ("setup.cfg", "[metadata]\nlicense_files=licenses_dir/*, LICENSE"), + ( + "setup.py", + SETUPPY_EXAMPLE.replace( + ")", " license_files=['licenses_dir/DUMMYFILE', 'LICENSE'])" + ), + ), + ], +) +def test_licenses_override(dummy_dist, monkeypatch, tmp_path, config_file, config): + dummy_dist.joinpath(config_file).write_text(config, encoding="utf-8") + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + license_files = { + "dummy_dist-1.0.dist-info/licenses/" + fname + for fname in {"licenses_dir/DUMMYFILE", "LICENSE"} + } + assert set(wf.namelist()) == DEFAULT_FILES | license_files + metadata = wf.read("dummy_dist-1.0.dist-info/METADATA").decode("utf8") + assert "License-File: licenses_dir/DUMMYFILE" in metadata + assert "License-File: LICENSE" in metadata + + +def test_licenses_preserve_folder_structure(licenses_dist, monkeypatch, tmp_path): + monkeypatch.chdir(licenses_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + print(os.listdir("dist")) + with ZipFile("dist/licenses_dist-1.0-py3-none-any.whl") as wf: + default_files = {name.replace("dummy_", "licenses_") for name in DEFAULT_FILES} + license_files = { + "licenses_dist-1.0.dist-info/licenses/LICENSE", + "licenses_dist-1.0.dist-info/licenses/src/vendor/LICENSE", + } + assert set(wf.namelist()) == default_files | license_files + metadata = wf.read("licenses_dist-1.0.dist-info/METADATA").decode("utf8") + assert "License-File: src/vendor/LICENSE" in metadata + assert "License-File: LICENSE" in metadata + + +def test_licenses_disabled(dummy_dist, monkeypatch, tmp_path): + dummy_dist.joinpath("setup.cfg").write_text( + "[metadata]\nlicense_files=\n", encoding="utf-8" + ) + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path)).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + assert set(wf.namelist()) == DEFAULT_FILES + + +def test_build_number(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), build_number="2").run() + with ZipFile("dist/dummy_dist-1.0-2-py3-none-any.whl") as wf: + filenames = set(wf.namelist()) + assert "dummy_dist-1.0.dist-info/RECORD" in filenames + assert "dummy_dist-1.0.dist-info/METADATA" in filenames + + +def test_universal_deprecated(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + with pytest.warns(SetuptoolsDeprecationWarning, match=".*universal is deprecated"): + bdist_wheel_cmd(bdist_dir=str(tmp_path), universal=True).run() + + # For now we still respect the option + assert os.path.exists("dist/dummy_dist-1.0-py2.py3-none-any.whl") + + +EXTENSION_EXAMPLE = """\ +#include + +static PyMethodDef methods[] = { + { NULL, NULL, 0, NULL } +}; + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "extension", + "Dummy extension module", + -1, + methods +}; + +PyMODINIT_FUNC PyInit_extension(void) { + return PyModule_Create(&module_def); +} +""" +EXTENSION_SETUPPY = """\ +from __future__ import annotations + +from setuptools import Extension, setup + +setup( + name="extension.dist", + version="0.1", + description="A testing distribution \N{SNOWMAN}", + ext_modules=[Extension(name="extension", sources=["extension.c"])], +) +""" + + +@pytest.mark.filterwarnings( + "once:Config variable '.*' is unset.*, Python ABI tag may be incorrect" +) +def test_limited_abi(monkeypatch, tmp_path, tmp_path_factory): + """Test that building a binary wheel with the limited ABI works.""" + source_dir = tmp_path_factory.mktemp("extension_dist") + (source_dir / "setup.py").write_text(EXTENSION_SETUPPY, encoding="utf-8") + (source_dir / "extension.c").write_text(EXTENSION_EXAMPLE, encoding="utf-8") + build_dir = tmp_path.joinpath("build") + dist_dir = tmp_path.joinpath("dist") + monkeypatch.chdir(source_dir) + bdist_wheel_cmd(bdist_dir=str(build_dir), dist_dir=str(dist_dir)).run() + + +def test_build_from_readonly_tree(dummy_dist, monkeypatch, tmp_path): + basedir = str(tmp_path.joinpath("dummy")) + shutil.copytree(str(dummy_dist), basedir) + monkeypatch.chdir(basedir) + + # Make the tree read-only + for root, _dirs, files in os.walk(basedir): + for fname in files: + os.chmod(os.path.join(root, fname), stat.S_IREAD) + + bdist_wheel_cmd().run() + + +@pytest.mark.parametrize( + ("option", "compress_type"), + list(bdist_wheel.supported_compressions.items()), + ids=list(bdist_wheel.supported_compressions), +) +def test_compression(dummy_dist, monkeypatch, tmp_path, option, compress_type): + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), compression=option).run() + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + filenames = set(wf.namelist()) + assert "dummy_dist-1.0.dist-info/RECORD" in filenames + assert "dummy_dist-1.0.dist-info/METADATA" in filenames + for zinfo in wf.filelist: + assert zinfo.compress_type == compress_type + + +def test_wheelfile_line_endings(wheel_paths): + for path in wheel_paths: + with ZipFile(path) as wf: + wheelfile = next(fn for fn in wf.filelist if fn.filename.endswith("WHEEL")) + wheelfile_contents = wf.read(wheelfile) + assert b"\r" not in wheelfile_contents + + +def test_unix_epoch_timestamps(dummy_dist, monkeypatch, tmp_path): + monkeypatch.setenv("SOURCE_DATE_EPOCH", "0") + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(bdist_dir=str(tmp_path), build_number="2a").run() + with ZipFile("dist/dummy_dist-1.0-2a-py3-none-any.whl") as wf: + for zinfo in wf.filelist: + assert zinfo.date_time >= (1980, 1, 1, 0, 0, 0) # min epoch is used + + +def test_get_abi_tag_windows(monkeypatch): + monkeypatch.setattr(tags, "interpreter_name", lambda: "cp") + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "cp313-win_amd64") + assert get_abi_tag() == "cp313" + monkeypatch.setattr(sys, "gettotalrefcount", lambda: 1, False) + assert get_abi_tag() == "cp313d" + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "cp313t-win_amd64") + assert get_abi_tag() == "cp313td" + monkeypatch.delattr(sys, "gettotalrefcount") + assert get_abi_tag() == "cp313t" + + +def test_get_abi_tag_pypy_old(monkeypatch): + monkeypatch.setattr(tags, "interpreter_name", lambda: "pp") + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "pypy36-pp73") + assert get_abi_tag() == "pypy36_pp73" + + +def test_get_abi_tag_pypy_new(monkeypatch): + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "pypy37-pp73-darwin") + monkeypatch.setattr(tags, "interpreter_name", lambda: "pp") + assert get_abi_tag() == "pypy37_pp73" + + +def test_get_abi_tag_graalpy(monkeypatch): + monkeypatch.setattr( + sysconfig, "get_config_var", lambda x: "graalpy231-310-native-x86_64-linux" + ) + monkeypatch.setattr(tags, "interpreter_name", lambda: "graalpy") + assert get_abi_tag() == "graalpy231_310_native" + + +def test_get_abi_tag_fallback(monkeypatch): + monkeypatch.setattr(sysconfig, "get_config_var", lambda x: "unknown-python-310") + monkeypatch.setattr(tags, "interpreter_name", lambda: "unknown-python") + assert get_abi_tag() == "unknown_python_310" + + +def test_platform_with_space(dummy_dist, monkeypatch): + """Ensure building on platforms with a space in the name succeed.""" + monkeypatch.chdir(dummy_dist) + bdist_wheel_cmd(plat_name="isilon onefs").run() + + +def test_data_dir_with_tag_build(monkeypatch, tmp_path): + """ + Setuptools allow authors to set PEP 440's local version segments + using ``egg_info.tag_build``. This should be reflected not only in the + ``.whl`` file name, but also in the ``.dist-info`` and ``.data`` dirs. + See pypa/setuptools#3997. + """ + monkeypatch.chdir(tmp_path) + files = { + "setup.py": """ + from setuptools import setup + setup(headers=["hello.h"]) + """, + "setup.cfg": """ + [metadata] + name = test + version = 1.0 + + [options.data_files] + hello/world = file.txt + + [egg_info] + tag_build = +what + tag_date = 0 + """, + "file.txt": "", + "hello.h": "", + } + for file, content in files.items(): + with open(file, "w", encoding="utf-8") as fh: + fh.write(cleandoc(content)) + + bdist_wheel_cmd().run() + + # Ensure .whl, .dist-info and .data contain the local segment + wheel_path = "dist/test-1.0+what-py3-none-any.whl" + assert os.path.exists(wheel_path) + entries = set(ZipFile(wheel_path).namelist()) + for expected in ( + "test-1.0+what.data/headers/hello.h", + "test-1.0+what.data/data/hello/world/file.txt", + "test-1.0+what.dist-info/METADATA", + "test-1.0+what.dist-info/WHEEL", + ): + assert expected in entries + + for not_expected in ( + "test.data/headers/hello.h", + "test-1.0.data/data/hello/world/file.txt", + "test.dist-info/METADATA", + "test-1.0.dist-info/WHEEL", + ): + assert not_expected not in entries + + +@pytest.mark.parametrize( + ("reported", "expected"), + [("linux-x86_64", "linux_i686"), ("linux-aarch64", "linux_armv7l")], +) +@pytest.mark.skipif( + platform.system() != "Linux", reason="Only makes sense to test on Linux" +) +def test_platform_linux32(reported, expected, monkeypatch): + monkeypatch.setattr(struct, "calcsize", lambda x: 4) + dist = setuptools.Distribution() + cmd = bdist_wheel(dist) + cmd.plat_name = reported + cmd.root_is_pure = False + _, _, actual = cmd.get_tag() + assert actual == expected + + +def test_no_ctypes(monkeypatch) -> None: + def _fake_import(name: str, *args, **kwargs): + if name == "ctypes": + raise ModuleNotFoundError(f"No module named {name}") + + return importlib.__import__(name, *args, **kwargs) + + with suppress(KeyError): + monkeypatch.delitem(sys.modules, "wheel.macosx_libfile") + + # Install an importer shim that refuses to load ctypes + monkeypatch.setattr(builtins, "__import__", _fake_import) + with pytest.raises(ModuleNotFoundError, match="No module named ctypes"): + import wheel.macosx_libfile # noqa: F401 + + # Unload and reimport the bdist_wheel command module to make sure it won't try to + # import ctypes + monkeypatch.delitem(sys.modules, "setuptools.command.bdist_wheel") + + import setuptools.command.bdist_wheel # noqa: F401 + + +def test_dist_info_provided(dummy_dist, monkeypatch, tmp_path): + monkeypatch.chdir(dummy_dist) + distinfo = tmp_path / "dummy_dist.dist-info" + + distinfo.mkdir() + (distinfo / "METADATA").write_text("name: helloworld", encoding="utf-8") + + # We don't control the metadata. According to PEP-517, "The hook MAY also + # create other files inside this directory, and a build frontend MUST + # preserve". + (distinfo / "FOO").write_text("bar", encoding="utf-8") + + bdist_wheel_cmd(bdist_dir=str(tmp_path), dist_info_dir=str(distinfo)).run() + expected = { + "dummy_dist-1.0.dist-info/FOO", + "dummy_dist-1.0.dist-info/RECORD", + } + with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf: + files_found = set(wf.namelist()) + # Check that all expected files are there. + assert expected - files_found == set() + # Make sure there is no accidental egg-info bleeding into the wheel. + assert not [path for path in files_found if 'egg-info' in str(path)] + + +def test_allow_grace_period_parent_directory_license(monkeypatch, tmp_path): + # Motivation: https://github.com/pypa/setuptools/issues/4892 + # TODO: Remove this test after deprecation period is over + files = { + "LICENSE.txt": "parent license", # <---- the license files are outside + "NOTICE.txt": "parent notice", + "python": { + "pyproject.toml": cleandoc( + """ + [project] + name = "test-proj" + dynamic = ["version"] # <---- testing dynamic will not break + [tool.setuptools.dynamic] + version.file = "VERSION" + """ + ), + "setup.cfg": cleandoc( + """ + [metadata] + license_files = + ../LICENSE.txt + ../NOTICE.txt + """ + ), + "VERSION": "42", + }, + } + jaraco.path.build(files, prefix=str(tmp_path)) + monkeypatch.chdir(tmp_path / "python") + msg = "Pattern '../.*.txt' cannot contain '..'" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): + bdist_wheel_cmd().run() + with ZipFile("dist/test_proj-42-py3-none-any.whl") as wf: + files_found = set(wf.namelist()) + expected_files = { + "test_proj-42.dist-info/licenses/LICENSE.txt", + "test_proj-42.dist-info/licenses/NOTICE.txt", + } + assert expected_files <= files_found + + metadata = wf.read("test_proj-42.dist-info/METADATA").decode("utf8") + assert "License-File: LICENSE.txt" in metadata + assert "License-File: NOTICE.txt" in metadata diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_build.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build.py new file mode 100644 index 0000000..f0f1d9d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build.py @@ -0,0 +1,33 @@ +from setuptools import Command +from setuptools.command.build import build +from setuptools.dist import Distribution + + +def test_distribution_gives_setuptools_build_obj(tmpdir_cwd): + """ + Check that the setuptools Distribution uses the + setuptools specific build object. + """ + + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build'], + packages=[], + package_data={'': ['path/*']}, + ) + ) + assert isinstance(dist.get_command_obj("build"), build) + + +class Subcommand(Command): + """Dummy command to be used in tests""" + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + raise NotImplementedError("just to check if the command runs") diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py new file mode 100644 index 0000000..b5315df --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_clib.py @@ -0,0 +1,84 @@ +import random +from unittest import mock + +import pytest + +from setuptools.command.build_clib import build_clib +from setuptools.dist import Distribution + +from distutils.errors import DistutilsSetupError + + +class TestBuildCLib: + @mock.patch('setuptools.command.build_clib.newer_pairwise_group') + def test_build_libraries(self, mock_newer): + dist = Distribution() + cmd = build_clib(dist) + + # this will be a long section, just making sure all + # exceptions are properly raised + libs = [('example', {'sources': 'broken.c'})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = 'some_string' + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'source.c': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + # with that out of the way, let's see if the crude dependency + # system works + cmd.compiler = mock.MagicMock(spec=cmd.compiler) + mock_newer.return_value = ([], []) + + obj_deps = {'': ('global.h',), 'example.c': ('example.h',)} + libs = [('example', {'sources': ['example.c'], 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0] + assert not cmd.compiler.compile.called + assert cmd.compiler.create_static_lib.call_count == 1 + + # reset the call numbers so we can test again + cmd.compiler.reset_mock() + + mock_newer.return_value = '' # anything as long as it's not ([],[]) + cmd.build_libraries(libs) + assert cmd.compiler.compile.call_count == 1 + assert cmd.compiler.create_static_lib.call_count == 1 + + @mock.patch('setuptools.command.build_clib.newer_pairwise_group') + def test_build_libraries_reproducible(self, mock_newer): + dist = Distribution() + cmd = build_clib(dist) + + # with that out of the way, let's see if the crude dependency + # system works + cmd.compiler = mock.MagicMock(spec=cmd.compiler) + mock_newer.return_value = ([], []) + + original_sources = ['a-example.c', 'example.c'] + sources = original_sources + + obj_deps = {'': ('global.h',), 'example.c': ('example.h',)} + libs = [('example', {'sources': sources, 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + computed_call_args = mock_newer.call_args[0] + + while sources == original_sources: + sources = random.sample(original_sources, len(original_sources)) + libs = [('example', {'sources': sources, 'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + assert computed_call_args == mock_newer.call_args[0] diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py new file mode 100644 index 0000000..c7b60ac --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_ext.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import os +import sys +from importlib.util import cache_from_source as _compiled_file_name + +import pytest +from jaraco import path + +from setuptools.command.build_ext import build_ext, get_abi3_suffix +from setuptools.dist import Distribution +from setuptools.errors import CompileError +from setuptools.extension import Extension + +from . import environment +from .textwrap import DALS + +import distutils.command.build_ext as orig +from distutils.sysconfig import get_config_var + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +class TestBuildExt: + def test_get_ext_filename(self): + """ + Setuptools needs to give back the same + result as distutils, even if the fullname + is not in ext_map. + """ + dist = Distribution() + cmd = build_ext(dist) + cmd.ext_map['foo/bar'] = '' + res = cmd.get_ext_filename('foo') + wanted = orig.build_ext.get_ext_filename(cmd, 'foo') + assert res == wanted + + def test_abi3_filename(self): + """ + Filename needs to be loadable by several versions + of Python 3 if 'is_abi3' is truthy on Extension() + """ + print(get_abi3_suffix()) + + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=True) + dist = Distribution(dict(ext_modules=[extension])) + cmd = build_ext(dist) + cmd.finalize_options() + assert 'spam.eggs' in cmd.ext_map + res = cmd.get_ext_filename('spam.eggs') + + if not get_abi3_suffix(): + assert res.endswith(get_config_var('EXT_SUFFIX')) + elif sys.platform == 'win32': + assert res.endswith('eggs.pyd') + else: + assert 'abi3' in res + + def test_ext_suffix_override(self): + """ + SETUPTOOLS_EXT_SUFFIX variable always overrides + default extension options. + """ + dist = Distribution() + cmd = build_ext(dist) + cmd.ext_map['for_abi3'] = ext = Extension( + 'for_abi3', + ['s.c'], + # Override shouldn't affect abi3 modules + py_limited_api=True, + ) + # Mock value needed to pass tests + ext._links_to_dynamic = False + + if not IS_PYPY: + expect = cmd.get_ext_filename('for_abi3') + else: + # PyPy builds do not use ABI3 tag, so they will + # also get the overridden suffix. + expect = 'for_abi3.test-suffix' + + try: + os.environ['SETUPTOOLS_EXT_SUFFIX'] = '.test-suffix' + res = cmd.get_ext_filename('normal') + assert 'normal.test-suffix' == res + res = cmd.get_ext_filename('for_abi3') + assert expect == res + finally: + del os.environ['SETUPTOOLS_EXT_SUFFIX'] + + def dist_with_example(self): + files = { + "src": {"mypkg": {"subpkg": {"ext2.c": ""}}}, + "c-extensions": {"ext1": {"main.c": ""}}, + } + + ext1 = Extension("mypkg.ext1", ["c-extensions/ext1/main.c"]) + ext2 = Extension("mypkg.subpkg.ext2", ["src/mypkg/subpkg/ext2.c"]) + ext3 = Extension("ext3", ["c-extension/ext3.c"]) + + path.build(files) + return Distribution({ + "script_name": "%test%", + "ext_modules": [ext1, ext2, ext3], + "package_dir": {"": "src"}, + }) + + def test_get_outputs(self, tmpdir_cwd, monkeypatch): + monkeypatch.setenv('SETUPTOOLS_EXT_SUFFIX', '.mp3') # make test OS-independent + monkeypatch.setattr('setuptools.command.build_ext.use_stubs', False) + dist = self.dist_with_example() + + # Regular build: get_outputs not empty, but get_output_mappings is empty + build_ext = dist.get_command_obj("build_ext") + build_ext.editable_mode = False + build_ext.ensure_finalized() + build_lib = build_ext.build_lib.replace(os.sep, "/") + outputs = [x.replace(os.sep, "/") for x in build_ext.get_outputs()] + assert outputs == [ + f"{build_lib}/ext3.mp3", + f"{build_lib}/mypkg/ext1.mp3", + f"{build_lib}/mypkg/subpkg/ext2.mp3", + ] + assert build_ext.get_output_mapping() == {} + + # Editable build: get_output_mappings should contain everything in get_outputs + dist.reinitialize_command("build_ext") + build_ext.editable_mode = True + build_ext.ensure_finalized() + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_ext.get_output_mapping().items() + } + assert mapping == { + f"{build_lib}/ext3.mp3": "src/ext3.mp3", + f"{build_lib}/mypkg/ext1.mp3": "src/mypkg/ext1.mp3", + f"{build_lib}/mypkg/subpkg/ext2.mp3": "src/mypkg/subpkg/ext2.mp3", + } + + def test_get_output_mapping_with_stub(self, tmpdir_cwd, monkeypatch): + monkeypatch.setenv('SETUPTOOLS_EXT_SUFFIX', '.mp3') # make test OS-independent + monkeypatch.setattr('setuptools.command.build_ext.use_stubs', True) + dist = self.dist_with_example() + + # Editable build should create compiled stubs (.pyc files only, no .py) + build_ext = dist.get_command_obj("build_ext") + build_ext.editable_mode = True + build_ext.ensure_finalized() + for ext in build_ext.extensions: + monkeypatch.setattr(ext, "_needs_stub", True) + + build_lib = build_ext.build_lib.replace(os.sep, "/") + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_ext.get_output_mapping().items() + } + + def C(file): + """Make it possible to do comparisons and tests in a OS-independent way""" + return _compiled_file_name(file).replace(os.sep, "/") + + assert mapping == { + C(f"{build_lib}/ext3.py"): C("src/ext3.py"), + f"{build_lib}/ext3.mp3": "src/ext3.mp3", + C(f"{build_lib}/mypkg/ext1.py"): C("src/mypkg/ext1.py"), + f"{build_lib}/mypkg/ext1.mp3": "src/mypkg/ext1.mp3", + C(f"{build_lib}/mypkg/subpkg/ext2.py"): C("src/mypkg/subpkg/ext2.py"), + f"{build_lib}/mypkg/subpkg/ext2.mp3": "src/mypkg/subpkg/ext2.mp3", + } + + # Ensure only the compiled stubs are present not the raw .py stub + assert f"{build_lib}/mypkg/ext1.py" not in mapping + assert f"{build_lib}/mypkg/subpkg/ext2.py" not in mapping + + # Visualize what the cached stub files look like + example_stub = C(f"{build_lib}/mypkg/ext1.py") + assert example_stub in mapping + assert example_stub.startswith(f"{build_lib}/mypkg/__pycache__/ext1") + assert example_stub.endswith(".pyc") + + +class TestBuildExtInplace: + def get_build_ext_cmd(self, optional: bool, **opts) -> build_ext: + files: dict[str, str | dict[str, dict[str, str]]] = { + "eggs.c": "#include missingheader.h\n", + ".build": {"lib": {}, "tmp": {}}, + } + path.build(files) + extension = Extension('spam.eggs', ['eggs.c'], optional=optional) + dist = Distribution(dict(ext_modules=[extension])) + dist.script_name = 'setup.py' + cmd = build_ext(dist) + vars(cmd).update(build_lib=".build/lib", build_temp=".build/tmp", **opts) + cmd.ensure_finalized() + return cmd + + def get_log_messages(self, caplog, capsys): + """ + Historically, distutils "logged" by printing to sys.std*. + Later versions adopted the logging framework. Grab + messages regardless of how they were captured. + """ + std = capsys.readouterr() + return std.out.splitlines() + std.err.splitlines() + caplog.messages + + def test_optional(self, tmpdir_cwd, caplog, capsys): + """ + If optional extensions fail to build, setuptools should show the error + in the logs but not fail to build + """ + cmd = self.get_build_ext_cmd(optional=True, inplace=True) + cmd.run() + assert any( + 'build_ext: building extension "spam.eggs" failed' + for msg in self.get_log_messages(caplog, capsys) + ) + # No compile error exception should be raised + + def test_non_optional(self, tmpdir_cwd): + # Non-optional extensions should raise an exception + cmd = self.get_build_ext_cmd(optional=False, inplace=True) + with pytest.raises(CompileError): + cmd.run() + + +def test_build_ext_config_handling(tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import Extension, setup + setup( + name='foo', + version='0.0.0', + ext_modules=[Extension('foo', ['foo.c'])], + ) + """ + ), + 'foo.c': DALS( + """ + #include "Python.h" + + #if PY_MAJOR_VERSION >= 3 + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "foo", + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + #define INITERROR return NULL + + PyMODINIT_FUNC PyInit_foo(void) + + #else + + #define INITERROR return + + void initfoo(void) + + #endif + { + #if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); + #else + PyObject *module = Py_InitModule("extension", NULL); + #endif + if (module == NULL) + INITERROR; + #if PY_MAJOR_VERSION >= 3 + return module; + #endif + } + """ + ), + 'setup.cfg': DALS( + """ + [build] + build_base = foo_build + """ + ), + } + path.build(files) + code, (stdout, stderr) = environment.run_setup_py( + cmd=['build'], + data_stream=(0, 2), + ) + assert code == 0, f'\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}' diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py new file mode 100644 index 0000000..2cd0a0a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_meta.py @@ -0,0 +1,959 @@ +import contextlib +import importlib +import os +import re +import shutil +import signal +import sys +import tarfile +import warnings +from concurrent import futures +from pathlib import Path +from typing import Any, Callable +from zipfile import ZipFile + +import pytest +from jaraco import path +from packaging.requirements import Requirement + +from setuptools.warnings import SetuptoolsDeprecationWarning + +from .textwrap import DALS + +SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" + + +TIMEOUT = int(os.getenv("TIMEOUT_BACKEND_TEST", "180")) # in seconds +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +pytestmark = pytest.mark.skipif( + sys.platform == "win32" and IS_PYPY, + reason="The combination of PyPy + Windows + pytest-xdist + ProcessPoolExecutor " + "is flaky and problematic", +) + + +class BuildBackendBase: + def __init__(self, cwd='.', env=None, backend_name='setuptools.build_meta') -> None: + self.cwd = cwd + self.env = env or {} + self.backend_name = backend_name + + +class BuildBackend(BuildBackendBase): + """PEP 517 Build Backend""" + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.pool = futures.ProcessPoolExecutor(max_workers=1) + + def __getattr__(self, name: str) -> Callable[..., Any]: + """Handles arbitrary function invocations on the build backend.""" + + def method(*args, **kw): + root = os.path.abspath(self.cwd) + caller = BuildBackendCaller(root, self.env, self.backend_name) + pid = None + try: + pid = self.pool.submit(os.getpid).result(TIMEOUT) + return self.pool.submit(caller, name, *args, **kw).result(TIMEOUT) + except futures.TimeoutError: + self.pool.shutdown(wait=False) # doesn't stop already running processes + self._kill(pid) + pytest.xfail(f"Backend did not respond before timeout ({TIMEOUT} s)") + except (futures.process.BrokenProcessPool, MemoryError, OSError): + if IS_PYPY: + pytest.xfail("PyPy frequently fails tests with ProcessPoolExector") + raise + + return method + + def _kill(self, pid): + if pid is None: + return + with contextlib.suppress(ProcessLookupError, OSError): + os.kill(pid, signal.SIGTERM if os.name == "nt" else signal.SIGKILL) + + +class BuildBackendCaller(BuildBackendBase): + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + (self.backend_name, _, self.backend_obj) = self.backend_name.partition(':') + + def __call__(self, name, *args, **kw) -> Any: + """Handles arbitrary function invocations on the build backend.""" + os.chdir(self.cwd) + os.environ.update(self.env) + mod = importlib.import_module(self.backend_name) + + if self.backend_obj: + backend = getattr(mod, self.backend_obj) + else: + backend = mod + + return getattr(backend, name)(*args, **kw) + + +defns = [ + { # simple setup.py script + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py that relies on __name__ + 'setup.py': DALS( + """ + assert __name__ == '__main__' + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py script that runs arbitrary code + 'setup.py': DALS( + """ + variable = True + def function(): + return variable + assert variable + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.py script that constructs temp files to be included in the distribution + 'setup.py': DALS( + """ + # Some packages construct files on the fly, include them in the package, + # and immediately remove them after `setup()` (e.g. pybind11==2.9.1). + # Therefore, we cannot use `distutils.core.run_setup(..., stop_after=...)` + # to obtain a distribution object first, and then run the distutils + # commands later, because these files will be removed in the meantime. + + with open('world.py', 'w', encoding="utf-8") as f: + f.write('x = 42') + + try: + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['world'], + setup_requires=['six'], + ) + finally: + # Some packages will clean temporary files + __import__('os').unlink('world.py') + """ + ), + }, + { # setup.cfg only + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = 0.0.0 + + [options] + py_modules=hello + setup_requires=six + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, + { # setup.cfg and setup.py + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = 0.0.0 + + [options] + py_modules=hello + setup_requires=six + """ + ), + 'setup.py': "__import__('setuptools').setup()", + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }, +] + + +class TestBuildMetaBackend: + backend_name = 'setuptools.build_meta' + + def get_build_backend(self): + return BuildBackend(backend_name=self.backend_name) + + @pytest.fixture(params=defns) + def build_backend(self, tmpdir, request): + path.build(request.param, prefix=str(tmpdir)) + with tmpdir.as_cwd(): + yield self.get_build_backend() + + def test_get_requires_for_build_wheel(self, build_backend): + actual = build_backend.get_requires_for_build_wheel() + expected = ['six'] + assert sorted(actual) == sorted(expected) + + def test_get_requires_for_build_sdist(self, build_backend): + actual = build_backend.get_requires_for_build_sdist() + expected = ['six'] + assert sorted(actual) == sorted(expected) + + def test_build_wheel(self, build_backend): + dist_dir = os.path.abspath('pip-wheel') + os.makedirs(dist_dir) + wheel_name = build_backend.build_wheel(dist_dir) + + wheel_file = os.path.join(dist_dir, wheel_name) + assert os.path.isfile(wheel_file) + + # Temporary files should be removed + assert not os.path.isfile('world.py') + + with ZipFile(wheel_file) as zipfile: + wheel_contents = set(zipfile.namelist()) + + # Each one of the examples have a single module + # that should be included in the distribution + python_scripts = (f for f in wheel_contents if f.endswith('.py')) + modules = [f for f in python_scripts if not f.endswith('setup.py')] + assert len(modules) == 1 + + @pytest.mark.parametrize('build_type', ('wheel', 'sdist')) + def test_build_with_existing_file_present(self, build_type, tmpdir_cwd): + # Building a sdist/wheel should still succeed if there's + # already a sdist/wheel in the destination directory. + files = { + 'setup.py': "from setuptools import setup\nsetup()", + 'VERSION': "0.0.1", + 'setup.cfg': DALS( + """ + [metadata] + name = foo + version = file: VERSION + """ + ), + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + """ + ), + } + + path.build(files) + + dist_dir = os.path.abspath('preexisting-' + build_type) + + build_backend = self.get_build_backend() + build_method = getattr(build_backend, 'build_' + build_type) + + # Build a first sdist/wheel. + # Note: this also check the destination directory is + # successfully created if it does not exist already. + first_result = build_method(dist_dir) + + # Change version. + with open("VERSION", "wt", encoding="utf-8") as version_file: + version_file.write("0.0.2") + + # Build a *second* sdist/wheel. + second_result = build_method(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, first_result)) + assert first_result != second_result + + # And if rebuilding the exact same sdist/wheel? + open(os.path.join(dist_dir, second_result), 'wb').close() + third_result = build_method(dist_dir) + assert third_result == second_result + assert os.path.getsize(os.path.join(dist_dir, third_result)) > 0 + + @pytest.mark.parametrize("setup_script", [None, SETUP_SCRIPT_STUB]) + def test_build_with_pyproject_config(self, tmpdir, setup_script): + files = { + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "foo" + license = {text = "MIT"} + description = "This is a Python package" + dynamic = ["version", "readme"] + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers" + ] + urls = {Homepage = "http://github.com"} + dependencies = [ + "appdirs", + ] + + [project.optional-dependencies] + all = [ + "tomli>=1", + "pyscaffold>=4,<5", + 'importlib; python_version == "2.6"', + ] + + [project.scripts] + foo = "foo.cli:main" + + [tool.setuptools] + zip-safe = false + package-dir = {"" = "src"} + packages = {find = {where = ["src"]}} + license-files = ["LICENSE*"] + + [tool.setuptools.dynamic] + version = {attr = "foo.__version__"} + readme = {file = "README.rst"} + + [tool.distutils.sdist] + formats = "gztar" + """ + ), + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + """ + ), + "README.rst": "This is a ``README``", + "LICENSE.txt": "---- placeholder MIT license ----", + "src": { + "foo": { + "__init__.py": "__version__ = '0.1'", + "__init__.pyi": "__version__: str", + "cli.py": "def main(): print('hello world')", + "data.txt": "def main(): print('hello world')", + "py.typed": "", + } + }, + } + if setup_script: + files["setup.py"] = setup_script + + build_backend = self.get_build_backend() + with tmpdir.as_cwd(): + path.build(files) + msgs = [ + "'tool.setuptools.license-files' is deprecated in favor of 'project.license-files'", + "`project.license` as a TOML table is deprecated", + ] + with warnings.catch_warnings(): + for msg in msgs: + warnings.filterwarnings("ignore", msg, SetuptoolsDeprecationWarning) + sdist_path = build_backend.build_sdist("temp") + wheel_file = build_backend.build_wheel("temp") + + with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: + sdist_contents = set(tar.getnames()) + + with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: + wheel_contents = set(zipfile.namelist()) + metadata = str(zipfile.read("foo-0.1.dist-info/METADATA"), "utf-8") + license = str( + zipfile.read("foo-0.1.dist-info/licenses/LICENSE.txt"), "utf-8" + ) + epoints = str(zipfile.read("foo-0.1.dist-info/entry_points.txt"), "utf-8") + + assert sdist_contents - {"foo-0.1/setup.py"} == { + 'foo-0.1', + 'foo-0.1/LICENSE.txt', + 'foo-0.1/MANIFEST.in', + 'foo-0.1/PKG-INFO', + 'foo-0.1/README.rst', + 'foo-0.1/pyproject.toml', + 'foo-0.1/setup.cfg', + 'foo-0.1/src', + 'foo-0.1/src/foo', + 'foo-0.1/src/foo/__init__.py', + 'foo-0.1/src/foo/__init__.pyi', + 'foo-0.1/src/foo/cli.py', + 'foo-0.1/src/foo/data.txt', + 'foo-0.1/src/foo/py.typed', + 'foo-0.1/src/foo.egg-info', + 'foo-0.1/src/foo.egg-info/PKG-INFO', + 'foo-0.1/src/foo.egg-info/SOURCES.txt', + 'foo-0.1/src/foo.egg-info/dependency_links.txt', + 'foo-0.1/src/foo.egg-info/entry_points.txt', + 'foo-0.1/src/foo.egg-info/requires.txt', + 'foo-0.1/src/foo.egg-info/top_level.txt', + 'foo-0.1/src/foo.egg-info/not-zip-safe', + } + assert wheel_contents == { + "foo/__init__.py", + "foo/__init__.pyi", # include type information by default + "foo/cli.py", + "foo/data.txt", # include_package_data defaults to True + "foo/py.typed", # include type information by default + "foo-0.1.dist-info/licenses/LICENSE.txt", + "foo-0.1.dist-info/METADATA", + "foo-0.1.dist-info/WHEEL", + "foo-0.1.dist-info/entry_points.txt", + "foo-0.1.dist-info/top_level.txt", + "foo-0.1.dist-info/RECORD", + } + assert license == "---- placeholder MIT license ----" + + for line in ( + "Summary: This is a Python package", + "License: MIT", + "License-File: LICENSE.txt", + "Classifier: Intended Audience :: Developers", + "Requires-Dist: appdirs", + "Requires-Dist: " + str(Requirement('tomli>=1 ; extra == "all"')), + "Requires-Dist: " + + str(Requirement('importlib; python_version=="2.6" and extra =="all"')), + ): + assert line in metadata, (line, metadata) + + assert metadata.strip().endswith("This is a ``README``") + assert epoints.strip() == "[console_scripts]\nfoo = foo.cli:main" + + def test_static_metadata_in_pyproject_config(self, tmpdir): + # Make sure static metadata in pyproject.toml is not overwritten by setup.py + # as required by PEP 621 + files = { + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "foo" + description = "This is a Python package" + version = "42" + dependencies = ["six"] + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='bar', + version='13', + ) + """ + ), + } + build_backend = self.get_build_backend() + with tmpdir.as_cwd(): + path.build(files) + sdist_path = build_backend.build_sdist("temp") + wheel_file = build_backend.build_wheel("temp") + + assert (tmpdir / "temp/foo-42.tar.gz").exists() + assert (tmpdir / "temp/foo-42-py3-none-any.whl").exists() + assert not (tmpdir / "temp/bar-13.tar.gz").exists() + assert not (tmpdir / "temp/bar-42.tar.gz").exists() + assert not (tmpdir / "temp/foo-13.tar.gz").exists() + assert not (tmpdir / "temp/bar-13-py3-none-any.whl").exists() + assert not (tmpdir / "temp/bar-42-py3-none-any.whl").exists() + assert not (tmpdir / "temp/foo-13-py3-none-any.whl").exists() + + with tarfile.open(os.path.join(tmpdir, "temp", sdist_path)) as tar: + pkg_info = str(tar.extractfile('foo-42/PKG-INFO').read(), "utf-8") + members = tar.getnames() + assert "bar-13/PKG-INFO" not in members + + with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile: + metadata = str(zipfile.read("foo-42.dist-info/METADATA"), "utf-8") + members = zipfile.namelist() + assert "bar-13.dist-info/METADATA" not in members + + for file in pkg_info, metadata: + for line in ("Name: foo", "Version: 42"): + assert line in file + for line in ("Name: bar", "Version: 13"): + assert line not in file + + def test_build_sdist(self, build_backend): + dist_dir = os.path.abspath('pip-sdist') + os.makedirs(dist_dir) + sdist_name = build_backend.build_sdist(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, sdist_name)) + + def test_prepare_metadata_for_build_wheel(self, build_backend): + dist_dir = os.path.abspath('pip-dist-info') + os.makedirs(dist_dir) + + dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir) + + assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA')) + + def test_prepare_metadata_inplace(self, build_backend): + """ + Some users might pass metadata_directory pre-populated with `.tox` or `.venv`. + See issue #3523. + """ + for pre_existing in [ + ".tox/python/lib/python3.10/site-packages/attrs-22.1.0.dist-info", + ".tox/python/lib/python3.10/site-packages/autocommand-2.2.1.dist-info", + ".nox/python/lib/python3.10/site-packages/build-0.8.0.dist-info", + ".venv/python3.10/site-packages/click-8.1.3.dist-info", + "venv/python3.10/site-packages/distlib-0.3.5.dist-info", + "env/python3.10/site-packages/docutils-0.19.dist-info", + ]: + os.makedirs(pre_existing, exist_ok=True) + dist_info = build_backend.prepare_metadata_for_build_wheel(".") + assert os.path.isfile(os.path.join(dist_info, 'METADATA')) + + def test_build_sdist_explicit_dist(self, build_backend): + # explicitly specifying the dist folder should work + # the folder sdist_directory and the ``--dist-dir`` can be the same + dist_dir = os.path.abspath('dist') + sdist_name = build_backend.build_sdist(dist_dir) + assert os.path.isfile(os.path.join(dist_dir, sdist_name)) + + def test_build_sdist_version_change(self, build_backend): + sdist_into_directory = os.path.abspath("out_sdist") + os.makedirs(sdist_into_directory) + + sdist_name = build_backend.build_sdist(sdist_into_directory) + assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name)) + + # if the setup.py changes subsequent call of the build meta + # should still succeed, given the + # sdist_directory the frontend specifies is empty + setup_loc = os.path.abspath("setup.py") + if not os.path.exists(setup_loc): + setup_loc = os.path.abspath("setup.cfg") + + with open(setup_loc, 'rt', encoding="utf-8") as file_handler: + content = file_handler.read() + with open(setup_loc, 'wt', encoding="utf-8") as file_handler: + file_handler.write(content.replace("version='0.0.0'", "version='0.0.1'")) + + shutil.rmtree(sdist_into_directory) + os.makedirs(sdist_into_directory) + + sdist_name = build_backend.build_sdist("out_sdist") + assert os.path.isfile(os.path.join(os.path.abspath("out_sdist"), sdist_name)) + + def test_build_sdist_pyproject_toml_exists(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'pyproject.toml': DALS( + """ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + """ + ), + } + path.build(files) + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert any('pyproject.toml' in name for name in tar.getnames()) + + def test_build_sdist_setup_py_exists(self, tmpdir_cwd): + # If build_sdist is called from a script other than setup.py, + # ensure setup.py is included + path.build(defns[0]) + + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert any('setup.py' in name for name in tar.getnames()) + + def test_build_sdist_setup_py_manifest_excluded(self, tmpdir_cwd): + # Ensure that MANIFEST.in can exclude setup.py + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'MANIFEST.in': DALS( + """ + exclude setup.py + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + targz_path = build_backend.build_sdist("temp") + with tarfile.open(os.path.join("temp", targz_path)) as tar: + assert not any('setup.py' in name for name in tar.getnames()) + + def test_build_sdist_builds_targz_even_if_zip_indicated(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'] + )""" + ), + 'hello.py': '', + 'setup.cfg': DALS( + """ + [sdist] + formats=zip + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + _relative_path_import_files = { + 'setup.py': DALS( + """ + __import__('setuptools').setup( + name='foo', + version=__import__('hello').__version__, + py_modules=['hello'] + )""" + ), + 'hello.py': '__version__ = "0.0.0"', + 'setup.cfg': DALS( + """ + [sdist] + formats=zip + """ + ), + } + + def test_build_sdist_relative_path_import(self, tmpdir_cwd): + path.build(self._relative_path_import_files) + build_backend = self.get_build_backend() + with pytest.raises(ImportError, match="^No module named 'hello'$"): + build_backend.build_sdist("temp") + + _simple_pyproject_example = { + "pyproject.toml": DALS( + """ + [project] + name = "proj" + version = "42" + """ + ), + "src": {"proj": {"__init__.py": ""}}, + } + + def _assert_link_tree(self, parent_dir): + """All files in the directory should be either links or hard links""" + files = list(Path(parent_dir).glob("**/*")) + assert files # Should not be empty + for file in files: + assert file.is_symlink() or os.stat(file).st_nlink > 0 + + def test_editable_without_config_settings(self, tmpdir_cwd): + """ + Sanity check to ensure tests with --mode=strict are different from the ones + without --mode. + + --mode=strict should create a local directory with a package tree. + The directory should not get created otherwise. + """ + path.build(self._simple_pyproject_example) + build_backend = self.get_build_backend() + assert not Path("build").exists() + build_backend.build_editable("temp") + assert not Path("build").exists() + + def test_build_wheel_inplace(self, tmpdir_cwd): + config_settings = {"--build-option": ["build_ext", "--inplace"]} + path.build(self._simple_pyproject_example) + build_backend = self.get_build_backend() + assert not Path("build").exists() + Path("build").mkdir() + build_backend.prepare_metadata_for_build_wheel("build", config_settings) + build_backend.build_wheel("build", config_settings) + assert Path("build/proj-42-py3-none-any.whl").exists() + + @pytest.mark.parametrize("config_settings", [{"editable-mode": "strict"}]) + def test_editable_with_config_settings(self, tmpdir_cwd, config_settings): + path.build({**self._simple_pyproject_example, '_meta': {}}) + assert not Path("build").exists() + build_backend = self.get_build_backend() + build_backend.prepare_metadata_for_build_editable("_meta", config_settings) + build_backend.build_editable("temp", config_settings, "_meta") + self._assert_link_tree(next(Path("build").glob("__editable__.*"))) + + @pytest.mark.parametrize( + ("setup_literal", "requirements"), + [ + ("'foo'", ['foo']), + ("['foo']", ['foo']), + (r"'foo\n'", ['foo']), + (r"'foo\n\n'", ['foo']), + ("['foo', 'bar']", ['foo', 'bar']), + (r"'# Has a comment line\nfoo'", ['foo']), + (r"'foo # Has an inline comment'", ['foo']), + (r"'foo \\\n >=3.0'", ['foo>=3.0']), + (r"'foo\nbar'", ['foo', 'bar']), + (r"'foo\nbar\n'", ['foo', 'bar']), + (r"['foo\n', 'bar\n']", ['foo', 'bar']), + ], + ) + @pytest.mark.parametrize('use_wheel', [True, False]) + def test_setup_requires(self, setup_literal, requirements, use_wheel, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import setup + + setup( + name="qux", + version="0.0.0", + py_modules=["hello"], + setup_requires={setup_literal}, + ) + """ + ).format(setup_literal=setup_literal), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + + if use_wheel: + get_requires = build_backend.get_requires_for_build_wheel + else: + get_requires = build_backend.get_requires_for_build_sdist + + # Ensure that the build requirements are properly parsed + expected = sorted(requirements) + actual = get_requires() + + assert expected == sorted(actual) + + def test_setup_requires_with_auto_discovery(self, tmpdir_cwd): + # Make sure patches introduced to retrieve setup_requires don't accidentally + # activate auto-discovery and cause problems due to the incomplete set of + # attributes passed to MinimalDistribution + files = { + 'pyproject.toml': DALS( + """ + [project] + name = "proj" + version = "42" + """ + ), + "setup.py": DALS( + """ + __import__('setuptools').setup( + setup_requires=["foo"], + py_modules = ["hello", "world"] + ) + """ + ), + 'hello.py': "'hello'", + 'world.py': "'world'", + } + path.build(files) + build_backend = self.get_build_backend() + setup_requires = build_backend.get_requires_for_build_wheel() + assert setup_requires == ["foo"] + + def test_dont_install_setup_requires(self, tmpdir_cwd): + files = { + 'setup.py': DALS( + """ + from setuptools import setup + + setup( + name="qux", + version="0.0.0", + py_modules=["hello"], + setup_requires=["does-not-exist >99"], + ) + """ + ), + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + } + + path.build(files) + + build_backend = self.get_build_backend() + + dist_dir = os.path.abspath('pip-dist-info') + os.makedirs(dist_dir) + + # does-not-exist can't be satisfied, so if it attempts to install + # setup_requires, it will fail. + build_backend.prepare_metadata_for_build_wheel(dist_dir) + + _sys_argv_0_passthrough = { + 'setup.py': DALS( + """ + import os + import sys + + __import__('setuptools').setup( + name='foo', + version='0.0.0', + ) + + sys_argv = os.path.abspath(sys.argv[0]) + file_path = os.path.abspath('setup.py') + assert sys_argv == file_path + """ + ) + } + + def test_sys_argv_passthrough(self, tmpdir_cwd): + path.build(self._sys_argv_0_passthrough) + build_backend = self.get_build_backend() + with pytest.raises(AssertionError): + build_backend.build_sdist("temp") + + _setup_py_file_abspath = { + 'setup.py': DALS( + """ + import os + assert os.path.isabs(__file__) + __import__('setuptools').setup( + name='foo', + version='0.0.0', + py_modules=['hello'], + setup_requires=['six'], + ) + """ + ) + } + + def test_setup_py_file_abspath(self, tmpdir_cwd): + path.build(self._setup_py_file_abspath) + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + @pytest.mark.parametrize('build_hook', ('build_sdist', 'build_wheel')) + def test_build_with_empty_setuppy(self, build_backend, build_hook): + files = {'setup.py': ''} + path.build(files) + + msg = re.escape('No distribution was found.') + with pytest.raises(ValueError, match=msg): + getattr(build_backend, build_hook)("temp") + + +class TestBuildMetaLegacyBackend(TestBuildMetaBackend): + backend_name = 'setuptools.build_meta:__legacy__' + + # build_meta_legacy-specific tests + def test_build_sdist_relative_path_import(self, tmpdir_cwd): + # This must fail in build_meta, but must pass in build_meta_legacy + path.build(self._relative_path_import_files) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + def test_sys_argv_passthrough(self, tmpdir_cwd): + path.build(self._sys_argv_0_passthrough) + + build_backend = self.get_build_backend() + build_backend.build_sdist("temp") + + +@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning") +def test_sys_exit_0_in_setuppy(monkeypatch, tmp_path): + """Setuptools should be resilient to setup.py with ``sys.exit(0)`` (#3973).""" + monkeypatch.chdir(tmp_path) + setuppy = """ + import sys, setuptools + setuptools.setup(name='foo', version='0.0.0') + sys.exit(0) + """ + (tmp_path / "setup.py").write_text(DALS(setuppy), encoding="utf-8") + backend = BuildBackend(backend_name="setuptools.build_meta") + assert backend.get_requires_for_build_wheel() == [] + + +def test_system_exit_in_setuppy(monkeypatch, tmp_path): + monkeypatch.chdir(tmp_path) + setuppy = "import sys; sys.exit('some error')" + (tmp_path / "setup.py").write_text(setuppy, encoding="utf-8") + with pytest.raises(SystemExit, match="some error"): + backend = BuildBackend(backend_name="setuptools.build_meta") + backend.get_requires_for_build_wheel() diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_py.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_py.py new file mode 100644 index 0000000..78848f7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_build_py.py @@ -0,0 +1,480 @@ +import os +import shutil +import stat +import warnings +from pathlib import Path +from unittest.mock import Mock + +import jaraco.path +import pytest + +from setuptools import SetuptoolsDeprecationWarning +from setuptools.dist import Distribution + +from .textwrap import DALS + + +def test_directories_in_package_data_glob(tmpdir_cwd): + """ + Directories matching the glob in package_data should + not be included in the package data. + + Regression test for #261. + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=[''], + package_data={'': ['path/*']}, + ) + ) + os.makedirs('path/subpath') + dist.parse_command_line() + dist.run_commands() + + +def test_recursive_in_package_data_glob(tmpdir_cwd): + """ + Files matching recursive globs (**) in package_data should + be included in the package data. + + #1806 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=[''], + package_data={'': ['path/**/data']}, + ) + ) + os.makedirs('path/subpath/subsubpath') + open('path/subpath/subsubpath/data', 'wb').close() + + dist.parse_command_line() + dist.run_commands() + + assert stat.S_ISREG(os.stat('build/lib/path/subpath/subsubpath/data').st_mode), ( + "File is not included" + ) + + +def test_read_only(tmpdir_cwd): + """ + Ensure read-only flag is not preserved in copy + for package modules and package data, as that + causes problems with deleting read-only files on + Windows. + + #1451 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['data.dat']}, + ) + ) + os.makedirs('pkg') + open('pkg/__init__.py', 'wb').close() + open('pkg/data.dat', 'wb').close() + os.chmod('pkg/__init__.py', stat.S_IREAD) + os.chmod('pkg/data.dat', stat.S_IREAD) + dist.parse_command_line() + dist.run_commands() + shutil.rmtree('build') + + +@pytest.mark.xfail( + 'platform.system() == "Windows"', + reason="On Windows, files do not have executable bits", + raises=AssertionError, + strict=True, +) +def test_executable_data(tmpdir_cwd): + """ + Ensure executable bit is preserved in copy for + package data, as users rely on it for scripts. + + #2041 + """ + dist = Distribution( + dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['run-me']}, + ) + ) + os.makedirs('pkg') + open('pkg/__init__.py', 'wb').close() + open('pkg/run-me', 'wb').close() + os.chmod('pkg/run-me', 0o700) + + dist.parse_command_line() + dist.run_commands() + + assert os.stat('build/lib/pkg/run-me').st_mode & stat.S_IEXEC, ( + "Script is not executable" + ) + + +EXAMPLE_WITH_MANIFEST = { + "setup.cfg": DALS( + """ + [metadata] + name = mypkg + version = 42 + + [options] + include_package_data = True + packages = find: + + [options.packages.find] + exclude = *.tests* + """ + ), + "mypkg": { + "__init__.py": "", + "resource_file.txt": "", + "tests": { + "__init__.py": "", + "test_mypkg.py": "", + "test_file.txt": "", + }, + }, + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + prune dist + prune build + prune *.egg-info + """ + ), +} + + +def test_excluded_subpackages(tmpdir_cwd): + jaraco.path.build(EXAMPLE_WITH_MANIFEST) + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + + build_py = dist.get_command_obj("build_py") + + msg = r"Python recognizes 'mypkg\.tests' as an importable package" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): # noqa: PT031 + # TODO: To fix #3260 we need some transition period to deprecate the + # existing behavior of `include_package_data`. After the transition, we + # should remove the warning and fix the behavior. + + if os.getenv("SETUPTOOLS_USE_DISTUTILS") == "stdlib": + # pytest.warns reset the warning filter temporarily + # https://github.com/pytest-dev/pytest/issues/4011#issuecomment-423494810 + warnings.filterwarnings( + "ignore", + "'encoding' argument not specified", + module="distutils.text_file", + # This warning is already fixed in pypa/distutils but not in stdlib + ) + + build_py.finalize_options() + build_py.run() + + build_dir = Path(dist.get_command_obj("build_py").build_lib) + assert (build_dir / "mypkg/__init__.py").exists() + assert (build_dir / "mypkg/resource_file.txt").exists() + + # Setuptools is configured to ignore `mypkg.tests`, therefore the following + # files/dirs should not be included in the distribution. + for f in [ + "mypkg/tests/__init__.py", + "mypkg/tests/test_mypkg.py", + "mypkg/tests/test_file.txt", + "mypkg/tests", + ]: + with pytest.raises(AssertionError): + # TODO: Enforce the following assertion once #3260 is fixed + # (remove context manager and the following xfail). + assert not (build_dir / f).exists() + + pytest.xfail("#3260") + + +@pytest.mark.filterwarnings("ignore::setuptools.SetuptoolsDeprecationWarning") +def test_existing_egg_info(tmpdir_cwd, monkeypatch): + """When provided with the ``existing_egg_info_dir`` attribute, build_py should not + attempt to run egg_info again. + """ + # == Pre-condition == + # Generate an egg-info dir + jaraco.path.build(EXAMPLE_WITH_MANIFEST) + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + assert dist.include_package_data + + egg_info = dist.get_command_obj("egg_info") + dist.run_command("egg_info") + egg_info_dir = next(Path(egg_info.egg_base).glob("*.egg-info")) + assert egg_info_dir.is_dir() + + # == Setup == + build_py = dist.get_command_obj("build_py") + build_py.finalize_options() + egg_info = dist.get_command_obj("egg_info") + egg_info_run = Mock(side_effect=egg_info.run) + monkeypatch.setattr(egg_info, "run", egg_info_run) + + # == Remove caches == + # egg_info is called when build_py looks for data_files, which gets cached. + # We need to ensure it is not cached yet, otherwise it may impact on the tests + build_py.__dict__.pop('data_files', None) + dist.reinitialize_command(egg_info) + + # == Sanity check == + # Ensure that if existing_egg_info is not given, build_py attempts to run egg_info + build_py.existing_egg_info_dir = None + build_py.run() + egg_info_run.assert_called() + + # == Remove caches == + egg_info_run.reset_mock() + build_py.__dict__.pop('data_files', None) + dist.reinitialize_command(egg_info) + + # == Actual test == + # Ensure that if existing_egg_info_dir is given, egg_info doesn't run + build_py.existing_egg_info_dir = egg_info_dir + build_py.run() + egg_info_run.assert_not_called() + assert build_py.data_files + + # Make sure the list of outputs is actually OK + outputs = map(lambda x: x.replace(os.sep, "/"), build_py.get_outputs()) + assert outputs + example = str(Path(build_py.build_lib, "mypkg/__init__.py")).replace(os.sep, "/") + assert example in outputs + + +EXAMPLE_ARBITRARY_MAPPING = { + "pyproject.toml": DALS( + """ + [project] + name = "mypkg" + version = "42" + + [tool.setuptools] + packages = ["mypkg", "mypkg.sub1", "mypkg.sub2", "mypkg.sub2.nested"] + + [tool.setuptools.package-dir] + "" = "src" + "mypkg.sub2" = "src/mypkg/_sub2" + "mypkg.sub2.nested" = "other" + """ + ), + "src": { + "mypkg": { + "__init__.py": "", + "resource_file.txt": "", + "sub1": { + "__init__.py": "", + "mod1.py": "", + }, + "_sub2": { + "mod2.py": "", + }, + }, + }, + "other": { + "__init__.py": "", + "mod3.py": "", + }, + "MANIFEST.in": DALS( + """ + global-include *.py *.txt + global-exclude *.py[cod] + """ + ), +} + + +def test_get_outputs(tmpdir_cwd): + jaraco.path.build(EXAMPLE_ARBITRARY_MAPPING) + dist = Distribution({"script_name": "%test%"}) + dist.parse_config_files() + + build_py = dist.get_command_obj("build_py") + build_py.editable_mode = True + build_py.ensure_finalized() + build_lib = build_py.build_lib.replace(os.sep, "/") + outputs = {x.replace(os.sep, "/") for x in build_py.get_outputs()} + assert outputs == { + f"{build_lib}/mypkg/__init__.py", + f"{build_lib}/mypkg/resource_file.txt", + f"{build_lib}/mypkg/sub1/__init__.py", + f"{build_lib}/mypkg/sub1/mod1.py", + f"{build_lib}/mypkg/sub2/mod2.py", + f"{build_lib}/mypkg/sub2/nested/__init__.py", + f"{build_lib}/mypkg/sub2/nested/mod3.py", + } + mapping = { + k.replace(os.sep, "/"): v.replace(os.sep, "/") + for k, v in build_py.get_output_mapping().items() + } + assert mapping == { + f"{build_lib}/mypkg/__init__.py": "src/mypkg/__init__.py", + f"{build_lib}/mypkg/resource_file.txt": "src/mypkg/resource_file.txt", + f"{build_lib}/mypkg/sub1/__init__.py": "src/mypkg/sub1/__init__.py", + f"{build_lib}/mypkg/sub1/mod1.py": "src/mypkg/sub1/mod1.py", + f"{build_lib}/mypkg/sub2/mod2.py": "src/mypkg/_sub2/mod2.py", + f"{build_lib}/mypkg/sub2/nested/__init__.py": "other/__init__.py", + f"{build_lib}/mypkg/sub2/nested/mod3.py": "other/mod3.py", + } + + +class TestTypeInfoFiles: + PYPROJECTS = { + "default_pyproject": DALS( + """ + [project] + name = "foo" + version = "1" + """ + ), + "dont_include_package_data": DALS( + """ + [project] + name = "foo" + version = "1" + + [tool.setuptools] + include-package-data = false + """ + ), + "exclude_type_info": DALS( + """ + [project] + name = "foo" + version = "1" + + [tool.setuptools] + include-package-data = false + + [tool.setuptools.exclude-package-data] + "*" = ["py.typed", "*.pyi"] + """ + ), + } + + EXAMPLES = { + "simple_namespace": { + "directory_structure": { + "foo": { + "bar.pyi": "", + "py.typed": "", + "__init__.py": "", + } + }, + "expected_type_files": {"foo/bar.pyi", "foo/py.typed"}, + }, + "nested_inside_namespace": { + "directory_structure": { + "foo": { + "bar": { + "py.typed": "", + "mod.pyi": "", + } + } + }, + "expected_type_files": {"foo/bar/mod.pyi", "foo/bar/py.typed"}, + }, + "namespace_nested_inside_regular": { + "directory_structure": { + "foo": { + "namespace": { + "foo.pyi": "", + }, + "__init__.pyi": "", + "py.typed": "", + } + }, + "expected_type_files": { + "foo/namespace/foo.pyi", + "foo/__init__.pyi", + "foo/py.typed", + }, + }, + } + + @pytest.mark.parametrize( + "pyproject", + [ + "default_pyproject", + pytest.param( + "dont_include_package_data", + marks=pytest.mark.xfail(reason="pypa/setuptools#4350"), + ), + ], + ) + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_type_files_included_by_default(self, tmpdir_cwd, pyproject, example): + structure = { + **self.EXAMPLES[example]["directory_structure"], + "pyproject.toml": self.PYPROJECTS[pyproject], + } + expected_type_files = self.EXAMPLES[example]["expected_type_files"] + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files <= outputs + + @pytest.mark.parametrize("pyproject", ["exclude_type_info"]) + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_type_files_can_be_excluded(self, tmpdir_cwd, pyproject, example): + structure = { + **self.EXAMPLES[example]["directory_structure"], + "pyproject.toml": self.PYPROJECTS[pyproject], + } + expected_type_files = self.EXAMPLES[example]["expected_type_files"] + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files.isdisjoint(outputs) + + def test_stub_only_package(self, tmpdir_cwd): + structure = { + "pyproject.toml": DALS( + """ + [project] + name = "foo-stubs" + version = "1" + """ + ), + "foo-stubs": {"__init__.pyi": "", "bar.pyi": ""}, + } + expected_type_files = {"foo-stubs/__init__.pyi", "foo-stubs/bar.pyi"} + jaraco.path.build(structure) + + build_py = get_finalized_build_py() + outputs = get_outputs(build_py) + assert expected_type_files <= outputs + + +def get_finalized_build_py(script_name="%build_py-test%"): + dist = Distribution({"script_name": script_name}) + dist.parse_config_files() + build_py = dist.get_command_obj("build_py") + build_py.finalize_options() + return build_py + + +def get_outputs(build_py): + build_dir = Path(build_py.build_lib) + return { + os.path.relpath(x, build_dir).replace(os.sep, "/") + for x in build_py.get_outputs() + } diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py new file mode 100644 index 0000000..b5df820 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_config_discovery.py @@ -0,0 +1,647 @@ +import os +import sys +from configparser import ConfigParser +from itertools import product +from typing import cast + +import jaraco.path +import pytest +from path import Path + +import setuptools # noqa: F401 # force distutils.core to be patched +from setuptools.command.sdist import sdist +from setuptools.discovery import find_package_path, find_parent_package +from setuptools.dist import Distribution +from setuptools.errors import PackageDiscoveryError + +from .contexts import quiet +from .integration.helpers import get_sdist_members, get_wheel_members, run +from .textwrap import DALS + +import distutils.core + + +class TestFindParentPackage: + def test_single_package(self, tmp_path): + # find_parent_package should find a non-namespace parent package + (tmp_path / "src/namespace/pkg/nested").mkdir(exist_ok=True, parents=True) + (tmp_path / "src/namespace/pkg/nested/__init__.py").touch() + (tmp_path / "src/namespace/pkg/__init__.py").touch() + packages = ["namespace", "namespace.pkg", "namespace.pkg.nested"] + assert find_parent_package(packages, {"": "src"}, tmp_path) == "namespace.pkg" + + def test_multiple_toplevel(self, tmp_path): + # find_parent_package should return null if the given list of packages does not + # have a single parent package + multiple = ["pkg", "pkg1", "pkg2"] + for name in multiple: + (tmp_path / f"src/{name}").mkdir(exist_ok=True, parents=True) + (tmp_path / f"src/{name}/__init__.py").touch() + assert find_parent_package(multiple, {"": "src"}, tmp_path) is None + + +class TestDiscoverPackagesAndPyModules: + """Make sure discovered values for ``packages`` and ``py_modules`` work + similarly to explicit configuration for the simple scenarios. + """ + + OPTIONS = { + # Different options according to the circumstance being tested + "explicit-src": {"package_dir": {"": "src"}, "packages": ["pkg"]}, + "variation-lib": { + "package_dir": {"": "lib"}, # variation of the source-layout + }, + "explicit-flat": {"packages": ["pkg"]}, + "explicit-single_module": {"py_modules": ["pkg"]}, + "explicit-namespace": {"packages": ["ns", "ns.pkg"]}, + "automatic-src": {}, + "automatic-flat": {}, + "automatic-single_module": {}, + "automatic-namespace": {}, + } + FILES = { + "src": ["src/pkg/__init__.py", "src/pkg/main.py"], + "lib": ["lib/pkg/__init__.py", "lib/pkg/main.py"], + "flat": ["pkg/__init__.py", "pkg/main.py"], + "single_module": ["pkg.py"], + "namespace": ["ns/pkg/__init__.py"], + } + + def _get_info(self, circumstance): + _, _, layout = circumstance.partition("-") + files = self.FILES[layout] + options = self.OPTIONS[circumstance] + return files, options + + @pytest.mark.parametrize("circumstance", OPTIONS.keys()) + def test_sdist_filelist(self, tmp_path, circumstance): + files, options = self._get_info(circumstance) + _populate_project_dir(tmp_path, files, options) + + _, cmd = _run_sdist_programatically(tmp_path, options) + + manifest = [f.replace(os.sep, "/") for f in cmd.filelist.files] + for file in files: + assert any(f.endswith(file) for f in manifest) + + @pytest.mark.parametrize("circumstance", OPTIONS.keys()) + def test_project(self, tmp_path, circumstance): + files, options = self._get_info(circumstance) + _populate_project_dir(tmp_path, files, options) + + # Simulate a pre-existing `build` directory + (tmp_path / "build").mkdir() + (tmp_path / "build/lib").mkdir() + (tmp_path / "build/bdist.linux-x86_64").mkdir() + (tmp_path / "build/bdist.linux-x86_64/file.py").touch() + (tmp_path / "build/lib/__init__.py").touch() + (tmp_path / "build/lib/file.py").touch() + (tmp_path / "dist").mkdir() + (tmp_path / "dist/file.py").touch() + + _run_build(tmp_path) + + sdist_files = get_sdist_members(next(tmp_path.glob("dist/*.tar.gz"))) + print("~~~~~ sdist_members ~~~~~") + print('\n'.join(sdist_files)) + assert sdist_files >= set(files) + + wheel_files = get_wheel_members(next(tmp_path.glob("dist/*.whl"))) + print("~~~~~ wheel_members ~~~~~") + print('\n'.join(wheel_files)) + orig_files = {f.replace("src/", "").replace("lib/", "") for f in files} + assert wheel_files >= orig_files + + # Make sure build files are not included by mistake + for file in wheel_files: + assert "build" not in files + assert "dist" not in files + + PURPOSEFULLY_EMPY = { + "setup.cfg": DALS( + """ + [metadata] + name = myproj + version = 0.0.0 + + [options] + {param} = + """ + ), + "setup.py": DALS( + """ + __import__('setuptools').setup( + name="myproj", + version="0.0.0", + {param}=[] + ) + """ + ), + "pyproject.toml": DALS( + """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + + [project] + name = "myproj" + version = "0.0.0" + + [tool.setuptools] + {param} = [] + """ + ), + "template-pyproject.toml": DALS( + """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + """ + ), + } + + @pytest.mark.parametrize( + ("config_file", "param", "circumstance"), + product( + ["setup.cfg", "setup.py", "pyproject.toml"], + ["packages", "py_modules"], + FILES.keys(), + ), + ) + def test_purposefully_empty(self, tmp_path, config_file, param, circumstance): + files = self.FILES[circumstance] + ["mod.py", "other.py", "src/pkg/__init__.py"] + _populate_project_dir(tmp_path, files, {}) + + if config_file == "pyproject.toml": + template_param = param.replace("_", "-") + else: + # Make sure build works with or without setup.cfg + pyproject = self.PURPOSEFULLY_EMPY["template-pyproject.toml"] + (tmp_path / "pyproject.toml").write_text(pyproject, encoding="utf-8") + template_param = param + + config = self.PURPOSEFULLY_EMPY[config_file].format(param=template_param) + (tmp_path / config_file).write_text(config, encoding="utf-8") + + dist = _get_dist(tmp_path, {}) + # When either parameter package or py_modules is an empty list, + # then there should be no discovery + assert getattr(dist, param) == [] + other = {"py_modules": "packages", "packages": "py_modules"}[param] + assert getattr(dist, other) is None + + @pytest.mark.parametrize( + ("extra_files", "pkgs"), + [ + (["venv/bin/simulate_venv"], {"pkg"}), + (["pkg-stubs/__init__.pyi"], {"pkg", "pkg-stubs"}), + (["other-stubs/__init__.pyi"], {"pkg", "other-stubs"}), + ( + # Type stubs can also be namespaced + ["namespace-stubs/pkg/__init__.pyi"], + {"pkg", "namespace-stubs", "namespace-stubs.pkg"}, + ), + ( + # Just the top-level package can have `-stubs`, ignore nested ones + ["namespace-stubs/pkg-stubs/__init__.pyi"], + {"pkg", "namespace-stubs"}, + ), + (["_hidden/file.py"], {"pkg"}), + (["news/finalize.py"], {"pkg"}), + ], + ) + def test_flat_layout_with_extra_files(self, tmp_path, extra_files, pkgs): + files = self.FILES["flat"] + extra_files + _populate_project_dir(tmp_path, files, {}) + dist = _get_dist(tmp_path, {}) + assert set(dist.packages) == pkgs + + @pytest.mark.parametrize( + "extra_files", + [ + ["other/__init__.py"], + ["other/finalize.py"], + ], + ) + def test_flat_layout_with_dangerous_extra_files(self, tmp_path, extra_files): + files = self.FILES["flat"] + extra_files + _populate_project_dir(tmp_path, files, {}) + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + def test_flat_layout_with_single_module(self, tmp_path): + files = self.FILES["single_module"] + ["invalid-module-name.py"] + _populate_project_dir(tmp_path, files, {}) + dist = _get_dist(tmp_path, {}) + assert set(dist.py_modules) == {"pkg"} + + def test_flat_layout_with_multiple_modules(self, tmp_path): + files = self.FILES["single_module"] + ["valid_module_name.py"] + _populate_project_dir(tmp_path, files, {}) + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + def test_py_modules_when_wheel_dir_is_cwd(self, tmp_path): + """Regression for issue 3692""" + from setuptools import build_meta + + pyproject = '[project]\nname = "test"\nversion = "1"' + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + (tmp_path / "foo.py").touch() + with jaraco.path.DirectoryStack().context(tmp_path): + build_meta.build_wheel(".") + # Ensure py_modules are found + wheel_files = get_wheel_members(next(tmp_path.glob("*.whl"))) + assert "foo.py" in wheel_files + + +class TestNoConfig: + DEFAULT_VERSION = "0.0.0" # Default version given by setuptools + + EXAMPLES = { + "pkg1": ["src/pkg1.py"], + "pkg2": ["src/pkg2/__init__.py"], + "pkg3": ["src/pkg3/__init__.py", "src/pkg3-stubs/__init__.py"], + "pkg4": ["pkg4/__init__.py", "pkg4-stubs/__init__.py"], + "ns.nested.pkg1": ["src/ns/nested/pkg1/__init__.py"], + "ns.nested.pkg2": ["ns/nested/pkg2/__init__.py"], + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_discover_name(self, tmp_path, example): + _populate_project_dir(tmp_path, self.EXAMPLES[example], {}) + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == example + + def test_build_with_discovered_name(self, tmp_path): + files = ["src/ns/nested/pkg/__init__.py"] + _populate_project_dir(tmp_path, files, {}) + _run_build(tmp_path, "--sdist") + # Expected distribution file + dist_file = tmp_path / f"dist/ns_nested_pkg-{self.DEFAULT_VERSION}.tar.gz" + assert dist_file.is_file() + + +class TestWithAttrDirective: + @pytest.mark.parametrize( + ("folder", "opts"), + [ + ("src", {}), + ("lib", {"packages": "find:", "packages.find": {"where": "lib"}}), + ], + ) + def test_setupcfg_metadata(self, tmp_path, folder, opts): + files = [f"{folder}/pkg/__init__.py", "setup.cfg"] + _populate_project_dir(tmp_path, files, opts) + + config = (tmp_path / "setup.cfg").read_text(encoding="utf-8") + overwrite = { + folder: {"pkg": {"__init__.py": "version = 42"}}, + "setup.cfg": "[metadata]\nversion = attr: pkg.version\n" + config, + } + jaraco.path.build(overwrite, prefix=tmp_path) + + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == "pkg" + assert dist.get_version() == "42" + assert dist.package_dir + package_path = find_package_path("pkg", dist.package_dir, tmp_path) + assert os.path.exists(package_path) + assert folder in Path(package_path).parts() + + _run_build(tmp_path, "--sdist") + dist_file = tmp_path / "dist/pkg-42.tar.gz" + assert dist_file.is_file() + + def test_pyproject_metadata(self, tmp_path): + _populate_project_dir(tmp_path, ["src/pkg/__init__.py"], {}) + + overwrite = { + "src": {"pkg": {"__init__.py": "version = 42"}}, + "pyproject.toml": ( + "[project]\nname = 'pkg'\ndynamic = ['version']\n" + "[tool.setuptools.dynamic]\nversion = {attr = 'pkg.version'}\n" + ), + } + jaraco.path.build(overwrite, prefix=tmp_path) + + dist = _get_dist(tmp_path, {}) + assert dist.get_version() == "42" + assert dist.package_dir == {"": "src"} + + +class TestWithCExtension: + def _simulate_package_with_extension(self, tmp_path): + # This example is based on: https://github.com/nucleic/kiwi/tree/1.4.0 + files = [ + "benchmarks/file.py", + "docs/Makefile", + "docs/requirements.txt", + "docs/source/conf.py", + "proj/header.h", + "proj/file.py", + "py/proj.cpp", + "py/other.cpp", + "py/file.py", + "py/py.typed", + "py/tests/test_proj.py", + "README.rst", + ] + _populate_project_dir(tmp_path, files, {}) + + setup_script = """ + from setuptools import Extension, setup + + ext_modules = [ + Extension( + "proj", + ["py/proj.cpp", "py/other.cpp"], + include_dirs=["."], + language="c++", + ), + ] + setup(ext_modules=ext_modules) + """ + (tmp_path / "setup.py").write_text(DALS(setup_script), encoding="utf-8") + + def test_skip_discovery_with_setupcfg_metadata(self, tmp_path): + """Ensure that auto-discovery is not triggered when the project is based on + C-extensions only, for backward compatibility. + """ + self._simulate_package_with_extension(tmp_path) + + pyproject = """ + [build-system] + requires = [] + build-backend = 'setuptools.build_meta' + """ + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + + setupcfg = """ + [metadata] + name = proj + version = 42 + """ + (tmp_path / "setup.cfg").write_text(DALS(setupcfg), encoding="utf-8") + + dist = _get_dist(tmp_path, {}) + assert dist.get_name() == "proj" + assert dist.get_version() == "42" + assert dist.py_modules is None + assert dist.packages is None + assert len(dist.ext_modules) == 1 + assert dist.ext_modules[0].name == "proj" + + def test_dont_skip_discovery_with_pyproject_metadata(self, tmp_path): + """When opting-in to pyproject.toml metadata, auto-discovery will be active if + the package lists C-extensions, but does not configure py-modules or packages. + + This way we ensure users with complex package layouts that would lead to the + discovery of multiple top-level modules/packages see errors and are forced to + explicitly set ``packages`` or ``py-modules``. + """ + self._simulate_package_with_extension(tmp_path) + + pyproject = """ + [project] + name = 'proj' + version = '42' + """ + (tmp_path / "pyproject.toml").write_text(DALS(pyproject), encoding="utf-8") + with pytest.raises(PackageDiscoveryError, match="multiple (packages|modules)"): + _get_dist(tmp_path, {}) + + +class TestWithPackageData: + def _simulate_package_with_data_files(self, tmp_path, src_root): + files = [ + f"{src_root}/proj/__init__.py", + f"{src_root}/proj/file1.txt", + f"{src_root}/proj/nested/file2.txt", + ] + _populate_project_dir(tmp_path, files, {}) + + manifest = """ + global-include *.py *.txt + """ + (tmp_path / "MANIFEST.in").write_text(DALS(manifest), encoding="utf-8") + + EXAMPLE_SETUPCFG = """ + [metadata] + name = proj + version = 42 + + [options] + include_package_data = True + """ + EXAMPLE_PYPROJECT = """ + [project] + name = "proj" + version = "42" + """ + + PYPROJECT_PACKAGE_DIR = """ + [tool.setuptools] + package-dir = {"" = "src"} + """ + + @pytest.mark.parametrize( + ("src_root", "files"), + [ + (".", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}), + (".", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}), + ("src", {"setup.cfg": DALS(EXAMPLE_SETUPCFG)}), + ("src", {"pyproject.toml": DALS(EXAMPLE_PYPROJECT)}), + ( + "src", + { + "setup.cfg": DALS(EXAMPLE_SETUPCFG) + + DALS( + """ + packages = find: + package_dir = + =src + + [options.packages.find] + where = src + """ + ) + }, + ), + ( + "src", + { + "pyproject.toml": DALS(EXAMPLE_PYPROJECT) + + DALS( + """ + [tool.setuptools] + package-dir = {"" = "src"} + """ + ) + }, + ), + ], + ) + def test_include_package_data(self, tmp_path, src_root, files): + """ + Make sure auto-discovery does not affect package include_package_data. + See issue #3196. + """ + jaraco.path.build(files, prefix=str(tmp_path)) + self._simulate_package_with_data_files(tmp_path, src_root) + + expected = { + os.path.normpath(f"{src_root}/proj/file1.txt").replace(os.sep, "/"), + os.path.normpath(f"{src_root}/proj/nested/file2.txt").replace(os.sep, "/"), + } + + _run_build(tmp_path) + + sdist_files = get_sdist_members(next(tmp_path.glob("dist/*.tar.gz"))) + print("~~~~~ sdist_members ~~~~~") + print('\n'.join(sdist_files)) + assert sdist_files >= expected + + wheel_files = get_wheel_members(next(tmp_path.glob("dist/*.whl"))) + print("~~~~~ wheel_members ~~~~~") + print('\n'.join(wheel_files)) + orig_files = {f.replace("src/", "").replace("lib/", "") for f in expected} + assert wheel_files >= orig_files + + +def test_compatible_with_numpy_configuration(tmp_path): + files = [ + "dir1/__init__.py", + "dir2/__init__.py", + "file.py", + ] + _populate_project_dir(tmp_path, files, {}) + dist = Distribution({}) + dist.configuration = object() + dist.set_defaults() + assert dist.py_modules is None + assert dist.packages is None + + +def test_name_discovery_doesnt_break_cli(tmpdir_cwd): + jaraco.path.build({"pkg.py": ""}) + dist = Distribution({}) + dist.script_args = ["--name"] + dist.set_defaults() + dist.parse_command_line() # <-- no exception should be raised here. + assert dist.get_name() == "pkg" + + +def test_preserve_explicit_name_with_dynamic_version(tmpdir_cwd, monkeypatch): + """According to #3545 it seems that ``name`` discovery is running, + even when the project already explicitly sets it. + This seems to be related to parsing of dynamic versions (via ``attr`` directive), + which requires the auto-discovery of ``package_dir``. + """ + files = { + "src": { + "pkg": {"__init__.py": "__version__ = 42\n"}, + }, + "pyproject.toml": DALS( + """ + [project] + name = "myproj" # purposefully different from package name + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {"attr" = "pkg.__version__"} + """ + ), + } + jaraco.path.build(files) + dist = Distribution({}) + orig_analyse_name = dist.set_defaults.analyse_name + + def spy_analyse_name(): + # We can check if name discovery was triggered by ensuring the original + # name remains instead of the package name. + orig_analyse_name() + assert dist.get_name() == "myproj" + + monkeypatch.setattr(dist.set_defaults, "analyse_name", spy_analyse_name) + dist.parse_config_files() + assert dist.get_version() == "42" + assert set(dist.packages) == {"pkg"} + + +def _populate_project_dir(root, files, options): + # NOTE: Currently pypa/build will refuse to build the project if no + # `pyproject.toml` or `setup.py` is found. So it is impossible to do + # completely "config-less" projects. + basic = { + "setup.py": "import setuptools\nsetuptools.setup()", + "README.md": "# Example Package", + "LICENSE": "Copyright (c) 2018", + } + jaraco.path.build(basic, prefix=root) + _write_setupcfg(root, options) + paths = (root / f for f in files) + for path in paths: + path.parent.mkdir(exist_ok=True, parents=True) + path.touch() + + +def _write_setupcfg(root, options): + if not options: + print("~~~~~ **NO** setup.cfg ~~~~~") + return + setupcfg = ConfigParser() + setupcfg.add_section("options") + for key, value in options.items(): + if key == "packages.find": + setupcfg.add_section(f"options.{key}") + setupcfg[f"options.{key}"].update(value) + elif isinstance(value, list): + setupcfg["options"][key] = ", ".join(value) + elif isinstance(value, dict): + str_value = "\n".join(f"\t{k} = {v}" for k, v in value.items()) + setupcfg["options"][key] = "\n" + str_value + else: + setupcfg["options"][key] = str(value) + with open(root / "setup.cfg", "w", encoding="utf-8") as f: + setupcfg.write(f) + print("~~~~~ setup.cfg ~~~~~") + print((root / "setup.cfg").read_text(encoding="utf-8")) + + +def _run_build(path, *flags): + cmd = [sys.executable, "-m", "build", "--no-isolation", *flags, str(path)] + return run(cmd, env={'DISTUTILS_DEBUG': ''}) + + +def _get_dist(dist_path, attrs): + root = "/".join(os.path.split(dist_path)) # POSIX-style + + script = dist_path / 'setup.py' + if script.exists(): + with Path(dist_path): + dist = cast( + Distribution, + distutils.core.run_setup("setup.py", {}, stop_after="init"), + ) + else: + dist = Distribution(attrs) + + dist.src_root = root + dist.script_name = "setup.py" + with Path(dist_path): + dist.parse_config_files() + + dist.set_defaults() + return dist + + +def _run_sdist_programatically(dist_path, attrs): + dist = _get_dist(dist_path, attrs) + cmd = sdist(dist) + cmd.ensure_finalized() + assert cmd.distribution.packages or cmd.distribution.py_modules + + with quiet(), Path(dist_path): + cmd.run() + + return dist, cmd diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py new file mode 100644 index 0000000..5ec6df3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_core_metadata.py @@ -0,0 +1,550 @@ +from __future__ import annotations + +import functools +import io +from email import message_from_string +from email.generator import Generator +from email.message import EmailMessage +from email.parser import Parser +from email.policy import EmailPolicy +from inspect import cleandoc +from pathlib import Path +from unittest.mock import Mock + +import jaraco.path +import pytest +from packaging.metadata import Metadata + +from setuptools import sic +from setuptools._core_metadata import rfc822_escape, rfc822_unescape +from setuptools.config import expand, setupcfg +from setuptools.dist import Distribution + +from .config.downloads import retrieve_file, urls_from_file + +EXAMPLE_BASE_INFO = dict( + name="package", + version="0.0.1", + author="Foo Bar", + author_email="foo@bar.net", + long_description="Long\ndescription", + description="Short description", + keywords=["one", "two"], +) + + +@pytest.mark.parametrize( + ("content", "result"), + ( + pytest.param( + "Just a single line", + None, + id="single_line", + ), + pytest.param( + "Multiline\nText\nwithout\nextra indents\n", + None, + id="multiline", + ), + pytest.param( + "Multiline\n With\n\nadditional\n indentation", + None, + id="multiline_with_indentation", + ), + pytest.param( + " Leading whitespace", + "Leading whitespace", + id="remove_leading_whitespace", + ), + pytest.param( + " Leading whitespace\nIn\n Multiline comment", + "Leading whitespace\nIn\n Multiline comment", + id="remove_leading_whitespace_multiline", + ), + ), +) +def test_rfc822_unescape(content, result): + assert (result or content) == rfc822_unescape(rfc822_escape(content)) + + +def __read_test_cases(): + base = EXAMPLE_BASE_INFO + + params = functools.partial(dict, base) + + return [ + ('Metadata version 1.0', params()), + ( + 'Metadata Version 1.0: Short long description', + params( + long_description='Short long description', + ), + ), + ( + 'Metadata version 1.1: Classifiers', + params( + classifiers=[ + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.7', + 'License :: OSI Approved :: MIT License', + ], + ), + ), + ( + 'Metadata version 1.1: Download URL', + params( + download_url='https://example.com', + ), + ), + ( + 'Metadata Version 1.2: Requires-Python', + params( + python_requires='>=3.7', + ), + ), + pytest.param( + 'Metadata Version 1.2: Project-Url', + params(project_urls=dict(Foo='https://example.bar')), + marks=pytest.mark.xfail( + reason="Issue #1578: project_urls not read", + ), + ), + ( + 'Metadata Version 2.1: Long Description Content Type', + params( + long_description_content_type='text/x-rst; charset=UTF-8', + ), + ), + ( + 'License', + params( + license='MIT', + ), + ), + ( + 'License multiline', + params( + license='This is a long license \nover multiple lines', + ), + ), + pytest.param( + 'Metadata Version 2.1: Provides Extra', + params(provides_extras=['foo', 'bar']), + marks=pytest.mark.xfail(reason="provides_extras not read"), + ), + ( + 'Missing author', + dict( + name='foo', + version='1.0.0', + author_email='snorri@sturluson.name', + ), + ), + ( + 'Missing author e-mail', + dict( + name='foo', + version='1.0.0', + author='Snorri Sturluson', + ), + ), + ( + 'Missing author and e-mail', + dict( + name='foo', + version='1.0.0', + ), + ), + ( + 'Bypass normalized version', + dict( + name='foo', + version=sic('1.0.0a'), + ), + ), + ] + + +@pytest.mark.parametrize(("name", "attrs"), __read_test_cases()) +def test_read_metadata(name, attrs): + dist = Distribution(attrs) + metadata_out = dist.metadata + dist_class = metadata_out.__class__ + + # Write to PKG_INFO and then load into a new metadata object + PKG_INFO = io.StringIO() + + metadata_out.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + pkg_info = PKG_INFO.read() + assert _valid_metadata(pkg_info) + + PKG_INFO.seek(0) + metadata_in = dist_class() + metadata_in.read_pkg_file(PKG_INFO) + + tested_attrs = [ + ('name', dist_class.get_name), + ('version', dist_class.get_version), + ('author', dist_class.get_contact), + ('author_email', dist_class.get_contact_email), + ('metadata_version', dist_class.get_metadata_version), + ('provides', dist_class.get_provides), + ('description', dist_class.get_description), + ('long_description', dist_class.get_long_description), + ('download_url', dist_class.get_download_url), + ('keywords', dist_class.get_keywords), + ('platforms', dist_class.get_platforms), + ('obsoletes', dist_class.get_obsoletes), + ('requires', dist_class.get_requires), + ('classifiers', dist_class.get_classifiers), + ('project_urls', lambda s: getattr(s, 'project_urls', {})), + ('provides_extras', lambda s: getattr(s, 'provides_extras', {})), + ] + + for attr, getter in tested_attrs: + assert getter(metadata_in) == getter(metadata_out) + + +def __maintainer_test_cases(): + attrs = {"name": "package", "version": "1.0", "description": "xxx"} + + def merge_dicts(d1, d2): + d1 = d1.copy() + d1.update(d2) + + return d1 + + return [ + ('No author, no maintainer', attrs.copy()), + ( + 'Author (no e-mail), no maintainer', + merge_dicts(attrs, {'author': 'Author Name'}), + ), + ( + 'Author (e-mail), no maintainer', + merge_dicts( + attrs, {'author': 'Author Name', 'author_email': 'author@name.com'} + ), + ), + ( + 'No author, maintainer (no e-mail)', + merge_dicts(attrs, {'maintainer': 'Maintainer Name'}), + ), + ( + 'No author, maintainer (e-mail)', + merge_dicts( + attrs, + { + 'maintainer': 'Maintainer Name', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ( + 'Author (no e-mail), Maintainer (no-email)', + merge_dicts( + attrs, {'author': 'Author Name', 'maintainer': 'Maintainer Name'} + ), + ), + ( + 'Author (e-mail), Maintainer (e-mail)', + merge_dicts( + attrs, + { + 'author': 'Author Name', + 'author_email': 'author@name.com', + 'maintainer': 'Maintainer Name', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ( + 'No author (e-mail), no maintainer (e-mail)', + merge_dicts( + attrs, + { + 'author_email': 'author@name.com', + 'maintainer_email': 'maintainer@name.com', + }, + ), + ), + ('Author unicode', merge_dicts(attrs, {'author': '鉄沢寛'})), + ('Maintainer unicode', merge_dicts(attrs, {'maintainer': 'Jan Łukasiewicz'})), + ] + + +@pytest.mark.parametrize(("name", "attrs"), __maintainer_test_cases()) +def test_maintainer_author(name, attrs, tmpdir): + tested_keys = { + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + } + + # Generate a PKG-INFO file + dist = Distribution(attrs) + fn = tmpdir.mkdir('pkg_info') + fn_s = str(fn) + + dist.metadata.write_pkg_info(fn_s) + + with open(str(fn.join('PKG-INFO')), 'r', encoding='utf-8') as f: + pkg_info = f.read() + + assert _valid_metadata(pkg_info) + + # Drop blank lines and strip lines from default description + raw_pkg_lines = pkg_info.splitlines() + pkg_lines = list(filter(None, raw_pkg_lines[:-2])) + + pkg_lines_set = set(pkg_lines) + + # Duplicate lines should not be generated + assert len(pkg_lines) == len(pkg_lines_set) + + for fkey, dkey in tested_keys.items(): + val = attrs.get(dkey, None) + if val is None: + for line in pkg_lines: + assert not line.startswith(fkey + ':') + else: + line = f'{fkey}: {val}' + assert line in pkg_lines_set + + +class TestParityWithMetadataFromPyPaWheel: + def base_example(self): + attrs = dict( + **EXAMPLE_BASE_INFO, + # Example with complex requirement definition + python_requires=">=3.8", + install_requires=""" + packaging==23.2 + more-itertools==8.8.0; extra == "other" + jaraco.text==3.7.0 + importlib-resources==5.10.2; python_version<"3.8" + importlib-metadata==6.0.0 ; python_version<"3.8" + colorama>=0.4.4; sys_platform == "win32" + """, + extras_require={ + "testing": """ + pytest >= 6 + pytest-checkdocs >= 2.4 + tomli ; \\ + # Using stdlib when possible + python_version < "3.11" + ini2toml[lite]>=0.9 + """, + "other": [], + }, + ) + # Generate a PKG-INFO file using setuptools + return Distribution(attrs) + + def test_requires_dist(self, tmp_path): + dist = self.base_example() + pkg_info = _get_pkginfo(dist) + assert _valid_metadata(pkg_info) + + # Ensure Requires-Dist is present + expected = [ + 'Metadata-Version:', + 'Requires-Python: >=3.8', + 'Provides-Extra: other', + 'Provides-Extra: testing', + 'Requires-Dist: tomli; python_version < "3.11" and extra == "testing"', + 'Requires-Dist: more-itertools==8.8.0; extra == "other"', + 'Requires-Dist: ini2toml[lite]>=0.9; extra == "testing"', + ] + for line in expected: + assert line in pkg_info + + HERE = Path(__file__).parent + EXAMPLES_FILE = HERE / "config/setupcfg_examples.txt" + + @pytest.fixture(params=[None, *urls_from_file(EXAMPLES_FILE)]) + def dist(self, request, monkeypatch, tmp_path): + """Example of distribution with arbitrary configuration""" + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(expand, "read_attr", Mock(return_value="0.42")) + monkeypatch.setattr(expand, "read_files", Mock(return_value="hello world")) + monkeypatch.setattr( + Distribution, "_finalize_license_files", Mock(return_value=None) + ) + if request.param is None: + yield self.base_example() + else: + # Real-world usage + config = retrieve_file(request.param) + yield setupcfg.apply_configuration(Distribution({}), config) + + @pytest.mark.uses_network + def test_pkg_info_roundtrip(self, tmp_path, dist): + """Ensure PKG-INFO round trips according to pypa/wheel's methodology""" + # Generate an simplified "egg-info" with PKG-INFO + pkg_info = _get_pkginfo(dist) + + # Emulate the way old versions of wheel.bdist_wheel used to parse and regenerate + # the message, then ensures the metadata generated by setuptools is compatible. + with io.StringIO(pkg_info) as buffer: + msg = Parser(EmailMessage).parse(buffer) + + serialization_policy = EmailPolicy( + utf8=True, + mangle_from_=False, + max_line_length=0, + ) + with io.BytesIO() as buffer: + out = io.TextIOWrapper(buffer, encoding="utf-8") + Generator(out, policy=serialization_policy).flatten(msg) + out.flush() + regenerated = buffer.getvalue() + + raw_metadata = bytes(pkg_info, "utf-8") + # Normalise newlines to avoid test errors on Windows: + raw_metadata = b"\n".join(raw_metadata.splitlines()) + regenerated = b"\n".join(regenerated.splitlines()) + assert regenerated == raw_metadata + + +class TestPEP643: + STATIC_CONFIG = { + "setup.cfg": cleandoc( + """ + [metadata] + name = package + version = 0.0.1 + author = Foo Bar + author_email = foo@bar.net + long_description = Long + description + description = Short description + keywords = one, two + platforms = abcd + [options] + install_requires = requests + """ + ), + "pyproject.toml": cleandoc( + """ + [project] + name = "package" + version = "0.0.1" + authors = [ + {name = "Foo Bar", email = "foo@bar.net"} + ] + description = "Short description" + readme = {text = "Long\\ndescription", content-type = "text/plain"} + keywords = ["one", "two"] + dependencies = ["requests"] + license = "AGPL-3.0-or-later" + [tool.setuptools] + provides = ["abcd"] + obsoletes = ["abcd"] + """ + ), + } + + @pytest.mark.parametrize("file", STATIC_CONFIG.keys()) + def test_static_config_has_no_dynamic(self, file, tmpdir_cwd): + Path(file).write_text(self.STATIC_CONFIG[file], encoding="utf-8") + metadata = _get_metadata() + assert metadata.get_all("Dynamic") is None + assert metadata.get_all("dynamic") is None + + @pytest.mark.parametrize("file", STATIC_CONFIG.keys()) + @pytest.mark.parametrize( + "fields", + [ + # Single dynamic field + {"requires-python": ("python_requires", ">=3.12")}, + {"author-email": ("author_email", "snoopy@peanuts.com")}, + {"keywords": ("keywords", ["hello", "world"])}, + {"platform": ("platforms", ["abcd"])}, + # Multiple dynamic fields + { + "summary": ("description", "hello world"), + "description": ("long_description", "bla bla bla bla"), + "requires-dist": ("install_requires", ["hello-world"]), + }, + ], + ) + def test_modified_fields_marked_as_dynamic(self, file, fields, tmpdir_cwd): + # We start with a static config + Path(file).write_text(self.STATIC_CONFIG[file], encoding="utf-8") + dist = _makedist() + + # ... but then we simulate the effects of a plugin modifying the distribution + for attr, value in fields.values(): + # `dist` and `dist.metadata` are complicated... + # Some attributes work when set on `dist`, others on `dist.metadata`... + # Here we set in both just in case (this also avoids calling `_finalize_*`) + setattr(dist, attr, value) + setattr(dist.metadata, attr, value) + + # Then we should be able to list the modified fields as Dynamic + metadata = _get_metadata(dist) + assert set(metadata.get_all("Dynamic")) == set(fields) + + @pytest.mark.parametrize( + "extra_toml", + [ + "# Let setuptools autofill license-files", + "license-files = ['LICENSE*', 'AUTHORS*', 'NOTICE']", + ], + ) + def test_license_files_dynamic(self, extra_toml, tmpdir_cwd): + # For simplicity (and for the time being) setuptools is not making + # any special handling to guarantee `License-File` is considered static. + # Instead we rely in the fact that, although suboptimal, it is OK to have + # it as dynamics, as per: + # https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677 + files = { + "pyproject.toml": self.STATIC_CONFIG["pyproject.toml"].replace( + 'license = "AGPL-3.0-or-later"', + f"dynamic = ['license']\n{extra_toml}", + ), + "LICENSE.md": "--- mock license ---", + "NOTICE": "--- mock notice ---", + "AUTHORS.txt": "--- me ---", + } + # Sanity checks: + assert extra_toml in files["pyproject.toml"] + assert 'license = "AGPL-3.0-or-later"' not in extra_toml + + jaraco.path.build(files) + dist = _makedist(license_expression="AGPL-3.0-or-later") + metadata = _get_metadata(dist) + assert set(metadata.get_all("Dynamic")) == { + 'license-file', + 'license-expression', + } + assert metadata.get("License-Expression") == "AGPL-3.0-or-later" + assert set(metadata.get_all("License-File")) == { + "NOTICE", + "AUTHORS.txt", + "LICENSE.md", + } + + +def _makedist(**attrs): + dist = Distribution(attrs) + dist.parse_config_files() + return dist + + +def _get_pkginfo(dist: Distribution): + with io.StringIO() as fp: + dist.metadata.write_pkg_file(fp) + return fp.getvalue() + + +def _get_metadata(dist: Distribution | None = None): + return message_from_string(_get_pkginfo(dist or _makedist())) + + +def _valid_metadata(text: str) -> bool: + metadata = Metadata.from_email(text, validate=True) # can raise exceptions + return metadata is not None diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_depends.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_depends.py new file mode 100644 index 0000000..1714c04 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_depends.py @@ -0,0 +1,15 @@ +import sys + +from setuptools import depends + + +class TestGetModuleConstant: + def test_basic(self): + """ + Invoke get_module_constant on a module in + the test package. + """ + mod_name = 'setuptools.tests.mod_with_constant' + val = depends.get_module_constant(mod_name, 'value') + assert val == 'three, sir!' + assert 'setuptools.tests.mod_with_constant' not in sys.modules diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_develop.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_develop.py new file mode 100644 index 0000000..c2a3be2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_develop.py @@ -0,0 +1,113 @@ +"""develop tests""" + +import os +import platform +import subprocess +import sys + +import pytest + +from setuptools._path import paths_on_pythonpath + +from . import contexts, namespaces + +SETUP_PY = """\ +from setuptools import setup + +setup(name='foo', + packages=['foo'], +) +""" + +INIT_PY = """print "foo" +""" + + +@pytest.fixture +def temp_user(monkeypatch): + with contexts.tempdir() as user_base: + with contexts.tempdir() as user_site: + monkeypatch.setattr('site.USER_BASE', user_base) + monkeypatch.setattr('site.USER_SITE', user_site) + yield + + +@pytest.fixture +def test_env(tmpdir, temp_user): + target = tmpdir + foo = target.mkdir('foo') + setup = target / 'setup.py' + if setup.isfile(): + raise ValueError(dir(target)) + with setup.open('w') as f: + f.write(SETUP_PY) + init = foo / '__init__.py' + with init.open('w') as f: + f.write(INIT_PY) + with target.as_cwd(): + yield target + + +class TestNamespaces: + @staticmethod + def install_develop(src_dir, target): + develop_cmd = [ + sys.executable, + 'setup.py', + 'develop', + '--install-dir', + str(target), + ] + with src_dir.as_cwd(): + with paths_on_pythonpath([str(target)]): + subprocess.check_call(develop_cmd) + + @pytest.mark.xfail(reason="pkg_resources has been removed") + @pytest.mark.skipif( + bool(os.environ.get("APPVEYOR")), + reason="https://github.com/pypa/setuptools/issues/851", + ) + @pytest.mark.skipif( + platform.python_implementation() == 'PyPy', + reason="https://github.com/pypa/setuptools/issues/1202", + ) + @pytest.mark.uses_network + def test_namespace_package_importable(self, tmpdir): + """ + Installing two packages sharing the same namespace, one installed + naturally using pip or `--single-version-externally-managed` + and the other installed using `develop` should leave the namespace + in tact and both packages reachable by import. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip', + 'install', + str(pkg_A), + '-t', + str(target), + ] + subprocess.check_call(install_cmd) + self.install_develop(pkg_B, target) + namespaces.make_site_dir(target) + try_import = [ + sys.executable, + '-c', + 'import myns.pkgA; import myns.pkgB', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(try_import) + + # additionally ensure that pkg_resources import works + pkg_resources_imp = [ + sys.executable, + '-c', + 'import pkg_resources', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist.py new file mode 100644 index 0000000..9685dcd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist.py @@ -0,0 +1,280 @@ +import os +import re +import urllib.parse +import urllib.request + +import pytest + +from setuptools import Distribution +from setuptools.dist import check_package_data, check_specifier + +from .fixtures import make_trivial_sdist +from .test_find_packages import ensure_files +from .textwrap import DALS + +from distutils.errors import DistutilsSetupError + + +def test_dist_fetch_build_egg(tmpdir, setuptools_wheel): + """ + Check multiple calls to `Distribution.fetch_build_egg` work as expected. + """ + index = tmpdir.mkdir('index') + index_url = urllib.parse.urljoin('file://', urllib.request.pathname2url(str(index))) + + def sdist_with_index(distname, version): + dist_dir = index.mkdir(distname) + dist_sdist = f'{distname}-{version}.tar.gz' + make_trivial_sdist( + str(dist_dir.join(dist_sdist)), distname, version, setuptools_wheel + ) + with dist_dir.join('index.html').open('w') as fp: + fp.write( + DALS( + """ + + {dist_sdist}
+ + """ + ).format(dist_sdist=dist_sdist) + ) + + sdist_with_index('barbazquux', '3.2.0') + sdist_with_index('barbazquux-runner', '2.11.1') + with tmpdir.join('setup.cfg').open('w') as fp: + fp.write( + DALS( + """ + [easy_install] + index_url = {index_url} + """ + ).format(index_url=index_url) + ) + reqs = """ + barbazquux-runner + barbazquux + """.split() + with tmpdir.as_cwd(): + dist = Distribution() + dist.parse_config_files() + resolved_dists = [dist.fetch_build_egg(r) for r in reqs] + assert [dist.name for dist in resolved_dists if dist] == reqs + + +EXAMPLE_BASE_INFO = dict( + name="package", + version="0.0.1", + author="Foo Bar", + author_email="foo@bar.net", + long_description="Long\ndescription", + description="Short description", + keywords=["one", "two"], +) + + +def test_provides_extras_deterministic_order(): + attrs = dict(extras_require=dict(a=['foo'], b=['bar'])) + dist = Distribution(attrs) + assert list(dist.metadata.provides_extras) == ['a', 'b'] + attrs['extras_require'] = dict(reversed(attrs['extras_require'].items())) + dist = Distribution(attrs) + assert list(dist.metadata.provides_extras) == ['b', 'a'] + + +CHECK_PACKAGE_DATA_TESTS = ( + # Valid. + ( + { + '': ['*.txt', '*.rst'], + 'hello': ['*.msg'], + }, + None, + ), + # Not a dictionary. + ( + ( + ('', ['*.txt', '*.rst']), + ('hello', ['*.msg']), + ), + ( + "'package_data' must be a dictionary mapping package" + " names to lists of string wildcard patterns" + ), + ), + # Invalid key type. + ( + { + 400: ['*.txt', '*.rst'], + }, + ("keys of 'package_data' dict must be strings (got 400)"), + ), + # Invalid value type. + ( + { + 'hello': '*.msg', + }, + ( + "\"values of 'package_data' dict\" must be of type " + " (got '*.msg')" + ), + ), + # Invalid value type (generators are single use) + ( + { + 'hello': (x for x in "generator"), + }, + ( + "\"values of 'package_data' dict\" must be of type " + " (got =3.0, !=3.1'} + dist = Distribution(attrs) + check_specifier(dist, attrs, attrs['python_requires']) + + attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']} + dist = Distribution(attrs) + check_specifier(dist, attrs, attrs['python_requires']) + + # invalid specifier value + attrs = {'name': 'foo', 'python_requires': '>=invalid-version'} + with pytest.raises(DistutilsSetupError): + dist = Distribution(attrs) + + +def test_metadata_name(): + with pytest.raises(DistutilsSetupError, match='missing.*name'): + Distribution()._validate_metadata() + + +@pytest.mark.parametrize( + ('dist_name', 'py_module'), + [ + ("my.pkg", "my_pkg"), + ("my-pkg", "my_pkg"), + ("my_pkg", "my_pkg"), + ("pkg", "pkg"), + ], +) +def test_dist_default_py_modules(tmp_path, dist_name, py_module): + (tmp_path / f"{py_module}.py").touch() + + (tmp_path / "setup.py").touch() + (tmp_path / "noxfile.py").touch() + # ^-- make sure common tool files are ignored + + attrs = {**EXAMPLE_BASE_INFO, "name": dist_name, "src_root": str(tmp_path)} + # Find `py_modules` corresponding to dist_name if not given + dist = Distribution(attrs) + dist.set_defaults() + assert dist.py_modules == [py_module] + # When `py_modules` is given, don't do anything + dist = Distribution({**attrs, "py_modules": ["explicity_py_module"]}) + dist.set_defaults() + assert dist.py_modules == ["explicity_py_module"] + # When `packages` is given, don't do anything + dist = Distribution({**attrs, "packages": ["explicity_package"]}) + dist.set_defaults() + assert not dist.py_modules + + +@pytest.mark.parametrize( + ('dist_name', 'package_dir', 'package_files', 'packages'), + [ + ("my.pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my-pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/mod.py"], ["my_pkg"]), + ("my.pkg", None, ["my/pkg/__init__.py"], ["my", "my.pkg"]), + ( + "my_pkg", + None, + ["src/my_pkg/__init__.py", "src/my_pkg2/__init__.py"], + ["my_pkg", "my_pkg2"], + ), + ( + "my_pkg", + {"pkg": "lib", "pkg2": "lib2"}, + ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"], + ["pkg", "pkg.nested", "pkg2"], + ), + ], +) +def test_dist_default_packages( + tmp_path, dist_name, package_dir, package_files, packages +): + ensure_files(tmp_path, package_files) + + (tmp_path / "setup.py").touch() + (tmp_path / "noxfile.py").touch() + # ^-- should not be included by default + + attrs = { + **EXAMPLE_BASE_INFO, + "name": dist_name, + "src_root": str(tmp_path), + "package_dir": package_dir, + } + # Find `packages` either corresponding to dist_name or inside src + dist = Distribution(attrs) + dist.set_defaults() + assert not dist.py_modules + assert not dist.py_modules + assert set(dist.packages) == set(packages) + # When `py_modules` is given, don't do anything + dist = Distribution({**attrs, "py_modules": ["explicit_py_module"]}) + dist.set_defaults() + assert not dist.packages + assert set(dist.py_modules) == {"explicit_py_module"} + # When `packages` is given, don't do anything + dist = Distribution({**attrs, "packages": ["explicit_package"]}) + dist.set_defaults() + assert not dist.py_modules + assert set(dist.packages) == {"explicit_package"} + + +@pytest.mark.parametrize( + ('dist_name', 'package_dir', 'package_files'), + [ + ("my.pkg.nested", None, ["my/pkg/nested/__init__.py"]), + ("my.pkg", None, ["my/pkg/__init__.py", "my/pkg/file.py"]), + ("my_pkg", None, ["my_pkg.py"]), + ("my_pkg", None, ["my_pkg/__init__.py", "my_pkg/nested/__init__.py"]), + ("my_pkg", None, ["src/my_pkg/__init__.py", "src/my_pkg/nested/__init__.py"]), + ( + "my_pkg", + {"my_pkg": "lib", "my_pkg.lib2": "lib2"}, + ["lib/__init__.py", "lib/nested/__init__.pyt", "lib2/__init__.py"], + ), + # Should not try to guess a name from multiple py_modules/packages + ("UNKNOWN", None, ["src/mod1.py", "src/mod2.py"]), + ("UNKNOWN", None, ["src/pkg1/__ini__.py", "src/pkg2/__init__.py"]), + ], +) +def test_dist_default_name(tmp_path, dist_name, package_dir, package_files): + """Make sure dist.name is discovered from packages/py_modules""" + ensure_files(tmp_path, package_files) + attrs = { + **EXAMPLE_BASE_INFO, + "src_root": "/".join(os.path.split(tmp_path)), # POSIX-style + "package_dir": package_dir, + } + del attrs["name"] + + dist = Distribution(attrs) + dist.set_defaults() + assert dist.py_modules or dist.packages + assert dist.get_name() == dist_name diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py new file mode 100644 index 0000000..f65d0af --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_dist_info.py @@ -0,0 +1,147 @@ +"""Test .dist-info style distributions.""" + +import pathlib +import re +import shutil +import subprocess +import sys +from functools import partial + +import pytest + +from setuptools.archive_util import unpack_archive + +from .textwrap import DALS + +read = partial(pathlib.Path.read_text, encoding="utf-8") + + +class TestDistInfo: + def test_invalid_version(self, tmp_path): + """ + Supplying an invalid version crashes dist_info. + """ + config = "[metadata]\nname=proj\nversion=42\n[egg_info]\ntag_build=invalid!!!\n" + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + msg = re.compile("invalid version", re.MULTILINE | re.IGNORECASE) + proc = run_command_inner("dist_info", cwd=tmp_path, check=False) + assert proc.returncode + assert msg.search(proc.stdout) + assert not list(tmp_path.glob("*.dist-info")) + + def test_tag_arguments(self, tmp_path): + config = """ + [metadata] + name=proj + version=42 + [egg_info] + tag_date=1 + tag_build=.post + """ + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + + print(run_command("dist_info", "--no-date", cwd=tmp_path)) + dist_info = next(tmp_path.glob("*.dist-info")) + assert dist_info.name.startswith("proj-42") + shutil.rmtree(dist_info) + + print(run_command("dist_info", "--tag-build", ".a", cwd=tmp_path)) + dist_info = next(tmp_path.glob("*.dist-info")) + assert dist_info.name.startswith("proj-42a") + + @pytest.mark.parametrize("keep_egg_info", (False, True)) + def test_output_dir(self, tmp_path, keep_egg_info): + config = "[metadata]\nname=proj\nversion=42\n" + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + out = tmp_path / "__out" + out.mkdir() + opts = ["--keep-egg-info"] if keep_egg_info else [] + run_command("dist_info", "--output-dir", out, *opts, cwd=tmp_path) + assert len(list(out.glob("*.dist-info"))) == 1 + assert len(list(tmp_path.glob("*.dist-info"))) == 0 + expected_egg_info = int(keep_egg_info) + assert len(list(out.glob("*.egg-info"))) == expected_egg_info + assert len(list(tmp_path.glob("*.egg-info"))) == 0 + assert len(list(out.glob("*.__bkp__"))) == 0 + assert len(list(tmp_path.glob("*.__bkp__"))) == 0 + + +class TestWheelCompatibility: + """Make sure the .dist-info directory produced with the ``dist_info`` command + is the same as the one produced by ``bdist_wheel``. + """ + + SETUPCFG = DALS( + """ + [metadata] + name = {name} + version = {version} + + [options] + install_requires = + foo>=12; sys_platform != "linux" + + [options.extras_require] + test = pytest + + [options.entry_points] + console_scripts = + executable-name = my_package.module:function + discover = + myproj = my_package.other_module:function + """ + ) + + EGG_INFO_OPTS = [ + # Related: #3088 #2872 + ("", ""), + (".post", "[egg_info]\ntag_build = post\n"), + (".post", "[egg_info]\ntag_build = .post\n"), + (".post", "[egg_info]\ntag_build = post\ntag_date = 1\n"), + (".dev", "[egg_info]\ntag_build = .dev\n"), + (".dev", "[egg_info]\ntag_build = .dev\ntag_date = 1\n"), + ("a1", "[egg_info]\ntag_build = .a1\n"), + ("+local", "[egg_info]\ntag_build = +local\n"), + ] + + @pytest.mark.parametrize("name", "my-proj my_proj my.proj My.Proj".split()) + @pytest.mark.parametrize("version", ["0.42.13"]) + @pytest.mark.parametrize(("suffix", "cfg"), EGG_INFO_OPTS) + def test_dist_info_is_the_same_as_in_wheel( + self, name, version, tmp_path, suffix, cfg + ): + config = self.SETUPCFG.format(name=name, version=version) + cfg + + for i in "dir_wheel", "dir_dist": + (tmp_path / i).mkdir() + (tmp_path / i / "setup.cfg").write_text(config, encoding="utf-8") + + run_command("bdist_wheel", cwd=tmp_path / "dir_wheel") + wheel = next(tmp_path.glob("dir_wheel/dist/*.whl")) + unpack_archive(wheel, tmp_path / "unpack") + wheel_dist_info = next(tmp_path.glob("unpack/*.dist-info")) + + run_command("dist_info", cwd=tmp_path / "dir_dist") + dist_info = next(tmp_path.glob("dir_dist/*.dist-info")) + + assert dist_info.name == wheel_dist_info.name + assert dist_info.name.startswith(f"my_proj-{version}{suffix}") + for file in "METADATA", "entry_points.txt": + assert read(dist_info / file) == read(wheel_dist_info / file) + + +def run_command_inner(*cmd, **kwargs): + opts = { + "stderr": subprocess.STDOUT, + "stdout": subprocess.PIPE, + "text": True, + "encoding": "utf-8", + "check": True, + **kwargs, + } + cmd = [sys.executable, "-c", "__import__('setuptools').setup()", *map(str, cmd)] + return subprocess.run(cmd, **opts) + + +def run_command(*args, **kwargs): + return run_command_inner(*args, **kwargs).stdout diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py new file mode 100644 index 0000000..f99a588 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_distutils_adoption.py @@ -0,0 +1,198 @@ +import os +import platform +import sys +import textwrap + +import pytest + +IS_PYPY = '__pypy__' in sys.builtin_module_names + +_TEXT_KWARGS = {"text": True, "encoding": "utf-8"} # For subprocess.run + + +def win_sr(env): + """ + On Windows, SYSTEMROOT must be present to avoid + + > Fatal Python error: _Py_HashRandomization_Init: failed to + > get random numbers to initialize Python + """ + if env and platform.system() == 'Windows': + env['SYSTEMROOT'] = os.environ['SYSTEMROOT'] + return env + + +def find_distutils(venv, imports='distutils', env=None, **kwargs): + py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) + cmd = ['python', '-c', py_cmd] + return venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS, **kwargs) + + +def count_meta_path(venv, env=None): + py_cmd = textwrap.dedent( + """ + import sys + is_distutils = lambda finder: finder.__class__.__name__ == "DistutilsMetaFinder" + print(len(list(filter(is_distutils, sys.meta_path)))) + """ + ) + cmd = ['python', '-c', py_cmd] + return int(venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS)) + + +skip_without_stdlib_distutils = pytest.mark.skipif( + sys.version_info >= (3, 12), + reason='stdlib distutils is removed from Python 3.12+', +) + + +@skip_without_stdlib_distutils +def test_distutils_stdlib(venv): + """ + Ensure stdlib distutils is used when appropriate. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib') + assert venv.name not in find_distutils(venv, env=env).split(os.sep) + assert count_meta_path(venv, env=env) == 0 + + +def test_distutils_local_with_setuptools(venv): + """ + Ensure local distutils is used when appropriate. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + loc = find_distutils(venv, imports='setuptools, distutils', env=env) + assert venv.name in loc.split(os.sep) + assert count_meta_path(venv, env=env) <= 1 + + +@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup') +def test_distutils_local(venv): + """ + Even without importing, the setuptools-local copy of distutils is + preferred. + """ + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + assert venv.name in find_distutils(venv, env=env).split(os.sep) + assert count_meta_path(venv, env=env) <= 1 + + +def test_pip_import(venv): + """ + Ensure pip can be imported. + Regression test for #3002. + """ + cmd = ['python', '-c', 'import pip'] + venv.run(cmd, **_TEXT_KWARGS) + + +def test_distutils_has_origin(): + """ + Distutils module spec should have an origin. #2990. + """ + assert __import__('distutils').__spec__.origin + + +ENSURE_IMPORTS_ARE_NOT_DUPLICATED = r""" +# Depending on the importlib machinery and _distutils_hack, some imports are +# duplicated resulting in different module objects being loaded, which prevents +# patches as shown in #3042. +# This script provides a way of verifying if this duplication is happening. + +from distutils import cmd +import distutils.command.sdist as sdist + +# import last to prevent caching +from distutils import {imported_module} + +for mod in (cmd, sdist): + assert mod.{imported_module} == {imported_module}, ( + f"\n{{mod.dir_util}}\n!=\n{{{imported_module}}}" + ) + +print("success") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + ('distutils_version', 'imported_module'), + [ + pytest.param("stdlib", "dir_util", marks=skip_without_stdlib_distutils), + pytest.param("stdlib", "file_util", marks=skip_without_stdlib_distutils), + pytest.param("stdlib", "archive_util", marks=skip_without_stdlib_distutils), + ("local", "dir_util"), + ("local", "file_util"), + ("local", "archive_util"), + ], +) +def test_modules_are_not_duplicated_on_import(distutils_version, imported_module, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + script = ENSURE_IMPORTS_ARE_NOT_DUPLICATED.format(imported_module=imported_module) + cmd = ['python', '-c', script] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" + + +ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED = r""" +import types +import distutils.dist as dist +from distutils import log +if isinstance(dist.log, types.ModuleType): + assert dist.log == log, f"\n{dist.log}\n!=\n{log}" +print("success") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + "distutils_version", + [ + "local", + pytest.param("stdlib", marks=skip_without_stdlib_distutils), + ], +) +def test_log_module_is_not_duplicated_on_import(distutils_version, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + cmd = ['python', '-c', ENSURE_LOG_IMPORT_IS_NOT_DUPLICATED] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" + + +ENSURE_CONSISTENT_ERROR_FROM_MODIFIED_PY = r""" +from setuptools.modified import newer +from {imported_module}.errors import DistutilsError + +# Can't use pytest.raises in this context +try: + newer("", "") +except DistutilsError: + print("success") +else: + raise AssertionError("Expected to raise") +""" + + +@pytest.mark.usefixtures("tmpdir_cwd") +@pytest.mark.parametrize( + ('distutils_version', 'imported_module'), + [ + ("local", "distutils"), + # Unfortunately we still get ._distutils.errors.DistutilsError with SETUPTOOLS_USE_DISTUTILS=stdlib + # But that's a deprecated use-case we don't mind not fully supporting in newer code + pytest.param( + "stdlib", "setuptools._distutils", marks=skip_without_stdlib_distutils + ), + ], +) +def test_consistent_error_from_modified_py(distutils_version, imported_module, venv): + env = dict(SETUPTOOLS_USE_DISTUTILS=distutils_version) + cmd = [ + 'python', + '-c', + ENSURE_CONSISTENT_ERROR_FROM_MODIFIED_PY.format( + imported_module=imported_module + ), + ] + output = venv.run(cmd, env=win_sr(env), **_TEXT_KWARGS).strip() + assert output == "success" diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py new file mode 100644 index 0000000..cf2dbe9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_editable_install.py @@ -0,0 +1,1261 @@ +from __future__ import annotations + +import os +import platform +import stat +import subprocess +import sys +from copy import deepcopy +from importlib import import_module +from importlib.machinery import EXTENSION_SUFFIXES +from pathlib import Path +from textwrap import dedent +from typing import Any +from unittest.mock import Mock +from uuid import uuid4 + +import jaraco.envs +import jaraco.path +import pytest +from path import Path as _Path + +from setuptools._importlib import resources as importlib_resources +from setuptools.command.editable_wheel import ( + _encode_pth, + _find_namespaces, + _find_package_roots, + _find_virtual_namespaces, + _finder_template, + _LinkTree, + _TopLevelFinder, + editable_wheel, +) +from setuptools.dist import Distribution +from setuptools.extension import Extension +from setuptools.warnings import SetuptoolsDeprecationWarning + +from . import contexts, namespaces + +from distutils.core import run_setup + + +@pytest.fixture(params=["strict", "lenient"]) +def editable_opts(request): + if request.param == "strict": + return ["--config-settings", "editable-mode=strict"] + return [] + + +EXAMPLE = { + 'pyproject.toml': dedent( + """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + license = {text = "MIT"} + description = "This is a Python package" + dynamic = ["readme"] + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers" + ] + urls = {Homepage = "https://github.com"} + + [tool.setuptools] + package-dir = {"" = "src"} + packages = {find = {where = ["src"]}} + license-files = ["LICENSE*"] + + [tool.setuptools.dynamic] + readme = {file = "README.rst"} + + [tool.distutils.egg_info] + tag-build = ".post0" + """ + ), + "MANIFEST.in": dedent( + """\ + global-include *.py *.txt + global-exclude *.py[cod] + prune dist + prune build + """ + ).strip(), + "README.rst": "This is a ``README``", + "LICENSE.txt": "---- placeholder MIT license ----", + "src": { + "mypkg": { + "__init__.py": dedent( + """\ + import sys + from importlib.metadata import PackageNotFoundError, version + + try: + __version__ = version(__name__) + except PackageNotFoundError: + __version__ = "unknown" + """ + ), + "__main__.py": dedent( + """\ + from importlib.resources import read_text + from . import __version__, __name__ as parent + from .mod import x + + data = read_text(parent, "data.txt") + print(__version__, data, x) + """ + ), + "mod.py": "x = ''", + "data.txt": "Hello World", + } + }, +} + + +SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" + + +@pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") +@pytest.mark.parametrize( + "files", + [ + {**EXAMPLE, "setup.py": SETUP_SCRIPT_STUB}, + EXAMPLE, # No setup.py script + ], +) +def test_editable_with_pyproject(tmp_path, venv, files, editable_opts): + project = tmp_path / "mypkg" + project.mkdir() + jaraco.path.build(files, prefix=project) + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + + cmd = ["python", "-m", "mypkg"] + assert venv.run(cmd).strip() == "3.14159.post0 Hello World" + + (project / "src/mypkg/data.txt").write_text("foobar", encoding="utf-8") + (project / "src/mypkg/mod.py").write_text("x = 42", encoding="utf-8") + assert venv.run(cmd).strip() == "3.14159.post0 foobar 42" + + +def test_editable_with_flat_layout(tmp_path, venv, editable_opts): + files = { + "mypkg": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + + [tool.setuptools] + packages = ["pkg"] + py-modules = ["mod"] + """ + ), + "pkg": {"__init__.py": "a = 4"}, + "mod.py": "b = 2", + }, + } + jaraco.path.build(files, prefix=tmp_path) + project = tmp_path / "mypkg" + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + cmd = ["python", "-c", "import pkg, mod; print(pkg.a, mod.b)"] + assert venv.run(cmd).strip() == "4 2" + + +def test_editable_with_single_module(tmp_path, venv, editable_opts): + files = { + "mypkg": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "mod" + version = "3.14159" + + [tool.setuptools] + py-modules = ["mod"] + """ + ), + "mod.py": "b = 2", + }, + } + jaraco.path.build(files, prefix=tmp_path) + project = tmp_path / "mypkg" + + cmd = [ + "python", + "-m", + "pip", + "install", + "--no-build-isolation", # required to force current version of setuptools + "-e", + str(project), + *editable_opts, + ] + print(venv.run(cmd)) + cmd = ["python", "-c", "import mod; print(mod.b)"] + assert venv.run(cmd).strip() == "2" + + +class TestLegacyNamespaces: + # legacy => pkg_resources.declare_namespace(...) + setup(namespace_packages=...) + + def test_nspkg_file_is_unique(self, tmp_path, monkeypatch): + deprecation = pytest.warns( + SetuptoolsDeprecationWarning, match=".*namespace_packages parameter.*" + ) + installation_dir = tmp_path / ".installation_dir" + installation_dir.mkdir() + examples = ( + "myns.pkgA", + "myns.pkgB", + "myns.n.pkgA", + "myns.n.pkgB", + ) + + for name in examples: + pkg = namespaces.build_namespace_package(tmp_path, name, version="42") + with deprecation, monkeypatch.context() as ctx: + ctx.chdir(pkg) + dist = run_setup("setup.py", stop_after="config") + cmd = editable_wheel(dist) + cmd.finalize_options() + editable_name = cmd.get_finalized_command("dist_info").name + cmd._install_namespaces(installation_dir, editable_name) + + files = list(installation_dir.glob("*-nspkg.pth")) + assert len(files) == len(examples) + + @pytest.mark.parametrize( + "impl", + ( + "pkg_resources", + # "pkgutil", => does not work + ), + ) + @pytest.mark.parametrize("ns", ("myns.n",)) + def test_namespace_package_importable( + self, venv, tmp_path, ns, impl, editable_opts + ): + """ + Installing two packages sharing the same namespace, one installed + naturally using pip or `--single-version-externally-managed` + and the other installed in editable mode should leave the namespace + intact and both packages reachable by import. + (Ported from test_develop). + """ + build_system = """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + """ + pkg_A = namespaces.build_namespace_package(tmp_path, f"{ns}.pkgA", impl=impl) + pkg_B = namespaces.build_namespace_package(tmp_path, f"{ns}.pkgB", impl=impl) + (pkg_A / "pyproject.toml").write_text(build_system, encoding="utf-8") + (pkg_B / "pyproject.toml").write_text(build_system, encoding="utf-8") + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-c", f"import {ns}.pkgA; import {ns}.pkgB"]) + + +class TestPep420Namespaces: + def test_namespace_package_importable(self, venv, tmp_path, editable_opts): + """ + Installing two packages sharing the same namespace, one installed + normally using pip and the other installed in editable mode + should allow importing both packages. + """ + pkg_A = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgA') + pkg_B = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgB') + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-c", "import myns.n.pkgA; import myns.n.pkgB"]) + + def test_namespace_created_via_package_dir(self, venv, tmp_path, editable_opts): + """Currently users can create a namespace by tweaking `package_dir`""" + files = { + "pkgA": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "pkgA" + version = "3.14159" + + [tool.setuptools] + package-dir = {"myns.n.pkgA" = "src"} + """ + ), + "src": {"__init__.py": "a = 1"}, + }, + } + jaraco.path.build(files, prefix=tmp_path) + pkg_A = tmp_path / "pkgA" + pkg_B = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgB') + pkg_C = namespaces.build_pep420_namespace_package(tmp_path, 'myns.n.pkgC') + + # use pip to install to the target directory + opts = editable_opts[:] + opts.append("--no-build-isolation") # force current version of setuptools + venv.run(["python", "-m", "pip", "install", str(pkg_A), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B), *opts]) + venv.run(["python", "-m", "pip", "install", "-e", str(pkg_C), *opts]) + venv.run(["python", "-c", "from myns.n import pkgA, pkgB, pkgC"]) + + def test_namespace_accidental_config_in_lenient_mode(self, venv, tmp_path): + """Sometimes users might specify an ``include`` pattern that ignores parent + packages. In a normal installation this would ignore all modules inside the + parent packages, and make them namespaces (reported in issue #3504), + so the editable mode should preserve this behaviour. + """ + files = { + "pkgA": { + "pyproject.toml": dedent( + """\ + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build_meta" + + [project] + name = "pkgA" + version = "3.14159" + + [tool.setuptools] + packages.find.include = ["mypkg.*"] + """ + ), + "mypkg": { + "__init__.py": "", + "other.py": "b = 1", + "n": { + "__init__.py": "", + "pkgA.py": "a = 1", + }, + }, + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + }, + } + jaraco.path.build(files, prefix=tmp_path) + pkg_A = tmp_path / "pkgA" + + # use pip to install to the target directory + opts = ["--no-build-isolation"] # force current version of setuptools + venv.run(["python", "-m", "pip", "-v", "install", "-e", str(pkg_A), *opts]) + out = venv.run(["python", "-c", "from mypkg.n import pkgA; print(pkgA.a)"]) + assert out.strip() == "1" + cmd = """\ + try: + import mypkg.other + except ImportError: + print("mypkg.other not defined") + """ + out = venv.run(["python", "-c", dedent(cmd)]) + assert "mypkg.other not defined" in out + + +def test_editable_with_prefix(tmp_path, sample_project, editable_opts): + """ + Editable install to a prefix should be discoverable. + """ + prefix = tmp_path / 'prefix' + + # figure out where pip will likely install the package + site_packages_all = [ + prefix / Path(path).relative_to(sys.prefix) + for path in sys.path + if 'site-packages' in path and path.startswith(sys.prefix) + ] + + for sp in site_packages_all: + sp.mkdir(parents=True) + + # install workaround + _addsitedirs(site_packages_all) + + env = dict(os.environ, PYTHONPATH=os.pathsep.join(map(str, site_packages_all))) + cmd = [ + sys.executable, + '-m', + 'pip', + 'install', + '--editable', + str(sample_project), + '--prefix', + str(prefix), + '--no-build-isolation', + *editable_opts, + ] + subprocess.check_call(cmd, env=env) + + # now run 'sample' with the prefix on the PYTHONPATH + bin = 'Scripts' if platform.system() == 'Windows' else 'bin' + exe = prefix / bin / 'sample' + subprocess.check_call([exe], env=env) + + +class TestFinderTemplate: + """This test focus in getting a particular implementation detail right. + If at some point in time the implementation is changed for something different, + this test can be modified or even excluded. + """ + + def install_finder(self, finder): + loc = {} + exec(finder, loc, loc) + loc["install"]() + + def test_packages(self, tmp_path): + files = { + "src1": { + "pkg1": { + "__init__.py": "", + "subpkg": {"mod1.py": "a = 42"}, + }, + }, + "src2": {"mod2.py": "a = 43"}, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "pkg1": str(tmp_path / "src1/pkg1"), + "mod2": str(tmp_path / "src2/mod2"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("pkg1", "pkg1.subpkg", "pkg1.subpkg.mod1", "mod2"): + sys.modules.pop(mod, None) + + self.install_finder(template) + mod1 = import_module("pkg1.subpkg.mod1") + mod2 = import_module("mod2") + subpkg = import_module("pkg1.subpkg") + + assert mod1.a == 42 + assert mod2.a == 43 + expected = str((tmp_path / "src1/pkg1/subpkg").resolve()) + assert_path(subpkg, expected) + + def test_namespace(self, tmp_path): + files = {"pkg": {"__init__.py": "a = 13", "text.txt": "abc"}} + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"ns.othername": str(tmp_path / "pkg")} + namespaces = {"ns": []} + + template = _finder_template(str(uuid4()), mapping, namespaces) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.othername"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkg = import_module("ns.othername") + text = importlib_resources.files(pkg) / "text.txt" + + expected = str((tmp_path / "pkg").resolve()) + assert_path(pkg, expected) + assert pkg.a == 13 + + # Make sure resources can also be found + assert text.read_text(encoding="utf-8") == "abc" + + def test_combine_namespaces(self, tmp_path): + files = { + "src1": {"ns": {"pkg1": {"__init__.py": "a = 13"}}}, + "src2": {"ns": {"mod2.py": "b = 37"}}, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "ns.pkgA": str(tmp_path / "src1/ns/pkg1"), + "ns": str(tmp_path / "src2/ns"), + } + namespaces_ = {"ns": [str(tmp_path / "src1"), str(tmp_path / "src2")]} + template = _finder_template(str(uuid4()), mapping, namespaces_) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.pkgA", "ns.mod2"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkgA = import_module("ns.pkgA") + mod2 = import_module("ns.mod2") + + expected = str((tmp_path / "src1/ns/pkg1").resolve()) + assert_path(pkgA, expected) + assert pkgA.a == 13 + assert mod2.b == 37 + + def test_combine_namespaces_nested(self, tmp_path): + """ + Users may attempt to combine namespace packages in a nested way via + ``package_dir`` as shown in pypa/setuptools#4248. + """ + + files = { + "src": {"my_package": {"my_module.py": "a = 13"}}, + "src2": {"my_package2": {"my_module2.py": "b = 37"}}, + } + + stack = jaraco.path.DirectoryStack() + with stack.context(tmp_path): + jaraco.path.build(files) + attrs = { + "script_name": "%PEP 517%", + "package_dir": { + "different_name": "src/my_package", + "different_name.subpkg": "src2/my_package2", + }, + "packages": ["different_name", "different_name.subpkg"], + } + dist = Distribution(attrs) + finder = _TopLevelFinder(dist, str(uuid4())) + code = next(v for k, v in finder.get_implementation() if k.endswith(".py")) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in attrs["packages"]: + sys.modules.pop(mod, None) + + self.install_finder(code) + mod1 = import_module("different_name.my_module") + mod2 = import_module("different_name.subpkg.my_module2") + + expected = str((tmp_path / "src/my_package/my_module.py").resolve()) + assert str(Path(mod1.__file__).resolve()) == expected + + expected = str((tmp_path / "src2/my_package2/my_module2.py").resolve()) + assert str(Path(mod2.__file__).resolve()) == expected + + assert mod1.a == 13 + assert mod2.b == 37 + + def test_dynamic_path_computation(self, tmp_path): + # Follows the example in PEP 420 + files = { + "project1": {"parent": {"child": {"one.py": "x = 1"}}}, + "project2": {"parent": {"child": {"two.py": "x = 2"}}}, + "project3": {"parent": {"child": {"three.py": "x = 3"}}}, + } + jaraco.path.build(files, prefix=tmp_path) + mapping = {} + namespaces_ = {"parent": [str(tmp_path / "project1/parent")]} + template = _finder_template(str(uuid4()), mapping, namespaces_) + + mods = (f"parent.child.{name}" for name in ("one", "two", "three")) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("parent", "parent.child", "parent.child", *mods): + sys.modules.pop(mod, None) + + self.install_finder(template) + + one = import_module("parent.child.one") + assert one.x == 1 + + with pytest.raises(ImportError): + import_module("parent.child.two") + + sys.path.append(str(tmp_path / "project2")) + two = import_module("parent.child.two") + assert two.x == 2 + + with pytest.raises(ImportError): + import_module("parent.child.three") + + sys.path.append(str(tmp_path / "project3")) + three = import_module("parent.child.three") + assert three.x == 3 + + def test_no_recursion(self, tmp_path): + # See issue #3550 + files = { + "pkg": { + "__init__.py": "from . import pkg", + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "pkg": str(tmp_path / "pkg"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("pkg", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="pkg"): + import_module("pkg") + + def test_similar_name(self, tmp_path): + files = { + "foo": { + "__init__.py": "", + "bar": { + "__init__.py": "", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = { + "foo": str(tmp_path / "foo"), + } + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("foo", None) + sys.modules.pop("foo.bar", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="foobar"): + import_module("foobar") + + def test_case_sensitivity(self, tmp_path): + files = { + "foo": { + "__init__.py": "", + "lowercase.py": "x = 1", + "bar": { + "__init__.py": "", + "lowercase.py": "x = 2", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + mapping = { + "foo": str(tmp_path / "foo"), + } + template = _finder_template(str(uuid4()), mapping, {}) + with contexts.save_paths(), contexts.save_sys_modules(): + sys.modules.pop("foo", None) + + self.install_finder(template) + with pytest.raises(ImportError, match="'FOO'"): + import_module("FOO") + + with pytest.raises(ImportError, match="'foo\\.LOWERCASE'"): + import_module("foo.LOWERCASE") + + with pytest.raises(ImportError, match="'foo\\.bar\\.Lowercase'"): + import_module("foo.bar.Lowercase") + + with pytest.raises(ImportError, match="'foo\\.BAR'"): + import_module("foo.BAR.lowercase") + + with pytest.raises(ImportError, match="'FOO'"): + import_module("FOO.bar.lowercase") + + mod = import_module("foo.lowercase") + assert mod.x == 1 + + mod = import_module("foo.bar.lowercase") + assert mod.x == 2 + + def test_namespace_case_sensitivity(self, tmp_path): + files = { + "pkg": { + "__init__.py": "a = 13", + "foo": { + "__init__.py": "b = 37", + "bar.py": "c = 42", + }, + }, + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"ns.othername": str(tmp_path / "pkg")} + namespaces = {"ns": []} + + template = _finder_template(str(uuid4()), mapping, namespaces) + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ("ns", "ns.othername"): + sys.modules.pop(mod, None) + + self.install_finder(template) + pkg = import_module("ns.othername") + expected = str((tmp_path / "pkg").resolve()) + assert_path(pkg, expected) + assert pkg.a == 13 + + foo = import_module("ns.othername.foo") + assert foo.b == 37 + + bar = import_module("ns.othername.foo.bar") + assert bar.c == 42 + + with pytest.raises(ImportError, match="'NS'"): + import_module("NS.othername.foo") + + with pytest.raises(ImportError, match="'ns\\.othername\\.FOO\\'"): + import_module("ns.othername.FOO") + + with pytest.raises(ImportError, match="'ns\\.othername\\.foo\\.BAR\\'"): + import_module("ns.othername.foo.BAR") + + def test_intermediate_packages(self, tmp_path): + """ + The finder should not import ``fullname`` if the intermediate segments + don't exist (see pypa/setuptools#4019). + """ + files = { + "src": { + "mypkg": { + "__init__.py": "", + "config.py": "a = 13", + "helloworld.py": "b = 13", + "components": { + "config.py": "a = 37", + }, + }, + } + } + jaraco.path.build(files, prefix=tmp_path) + + mapping = {"mypkg": str(tmp_path / "src/mypkg")} + template = _finder_template(str(uuid4()), mapping, {}) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in ( + "mypkg", + "mypkg.config", + "mypkg.helloworld", + "mypkg.components", + "mypkg.components.config", + "mypkg.components.helloworld", + ): + sys.modules.pop(mod, None) + + self.install_finder(template) + + config = import_module("mypkg.components.config") + assert config.a == 37 + + helloworld = import_module("mypkg.helloworld") + assert helloworld.b == 13 + + with pytest.raises(ImportError): + import_module("mypkg.components.helloworld") + + +def test_pkg_roots(tmp_path): + """This test focus in getting a particular implementation detail right. + If at some point in time the implementation is changed for something different, + this test can be modified or even excluded. + """ + files = { + "a": {"b": {"__init__.py": "ab = 1"}, "__init__.py": "a = 1"}, + "d": {"__init__.py": "d = 1", "e": {"__init__.py": "de = 1"}}, + "f": {"g": {"h": {"__init__.py": "fgh = 1"}}}, + "other": {"__init__.py": "abc = 1"}, + "another": {"__init__.py": "abcxyz = 1"}, + "yet_another": {"__init__.py": "mnopq = 1"}, + } + jaraco.path.build(files, prefix=tmp_path) + package_dir = { + "a.b.c": "other", + "a.b.c.x.y.z": "another", + "m.n.o.p.q": "yet_another", + } + packages = [ + "a", + "a.b", + "a.b.c", + "a.b.c.x.y", + "a.b.c.x.y.z", + "d", + "d.e", + "f", + "f.g", + "f.g.h", + "m.n.o.p.q", + ] + roots = _find_package_roots(packages, package_dir, tmp_path) + assert roots == { + "a": str(tmp_path / "a"), + "a.b.c": str(tmp_path / "other"), + "a.b.c.x.y.z": str(tmp_path / "another"), + "d": str(tmp_path / "d"), + "f": str(tmp_path / "f"), + "m.n.o.p.q": str(tmp_path / "yet_another"), + } + + ns = set(dict(_find_namespaces(packages, roots))) + assert ns == {"f", "f.g"} + + ns = set(_find_virtual_namespaces(roots)) + assert ns == {"a.b", "a.b.c.x", "a.b.c.x.y", "m", "m.n", "m.n.o", "m.n.o.p"} + + +class TestOverallBehaviour: + PYPROJECT = """\ + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "mypkg" + version = "3.14159" + """ + + # Any: Would need a TypedDict. Keep it simple for tests + FLAT_LAYOUT: dict[str, Any] = { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "mypkg": { + "__init__.py": "", + "mod1.py": "var = 42", + "subpackage": { + "__init__.py": "", + "mod2.py": "var = 13", + "resource_file.txt": "resource 39", + }, + }, + } + + EXAMPLES = { + "flat-layout": FLAT_LAYOUT, + "src-layout": { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "src": {"mypkg": FLAT_LAYOUT["mypkg"]}, + }, + "custom-layout": { + "pyproject.toml": dedent(PYPROJECT) + + dedent( + """\ + [tool.setuptools] + packages = ["mypkg", "mypkg.subpackage"] + + [tool.setuptools.package-dir] + "mypkg.subpackage" = "other" + """ + ), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "mypkg": { + "__init__.py": "", + "mod1.py": FLAT_LAYOUT["mypkg"]["mod1.py"], + }, + "other": FLAT_LAYOUT["mypkg"]["subpackage"], + }, + "namespace": { + "pyproject.toml": dedent(PYPROJECT), + "MANIFEST.in": EXAMPLE["MANIFEST.in"], + "otherfile.py": "", + "src": { + "mypkg": { + "mod1.py": FLAT_LAYOUT["mypkg"]["mod1.py"], + "subpackage": FLAT_LAYOUT["mypkg"]["subpackage"], + }, + }, + }, + } + + @pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") + @pytest.mark.parametrize("layout", EXAMPLES.keys()) + def test_editable_install(self, tmp_path, venv, layout, editable_opts): + project, _ = install_project( + "mypkg", venv, tmp_path, self.EXAMPLES[layout], *editable_opts + ) + + # Ensure stray files are not importable + cmd_import_error = """\ + try: + import otherfile + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_import_error)]) + assert "No module named 'otherfile'" in out + + # Ensure the modules are importable + cmd_get_vars = """\ + import mypkg, mypkg.mod1, mypkg.subpackage.mod2 + print(mypkg.mod1.var, mypkg.subpackage.mod2.var) + """ + out = venv.run(["python", "-c", dedent(cmd_get_vars)]) + assert "42 13" in out + + # Ensure resources are reachable + cmd_get_resource = """\ + import mypkg.subpackage + from setuptools._importlib import resources as importlib_resources + text = importlib_resources.files(mypkg.subpackage) / "resource_file.txt" + print(text.read_text(encoding="utf-8")) + """ + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "resource 39" in out + + # Ensure files are editable + mod1 = next(project.glob("**/mod1.py")) + mod2 = next(project.glob("**/mod2.py")) + resource_file = next(project.glob("**/resource_file.txt")) + + mod1.write_text("var = 17", encoding="utf-8") + mod2.write_text("var = 781", encoding="utf-8") + resource_file.write_text("resource 374", encoding="utf-8") + + out = venv.run(["python", "-c", dedent(cmd_get_vars)]) + assert "42 13" not in out + assert "17 781" in out + + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "resource 39" not in out + assert "resource 374" in out + + +class TestLinkTree: + FILES = deepcopy(TestOverallBehaviour.EXAMPLES["src-layout"]) + FILES["pyproject.toml"] += dedent( + """\ + [tool.setuptools] + # Temporary workaround: both `include-package-data` and `package-data` configs + # can be removed after #3260 is fixed. + include-package-data = false + package-data = {"*" = ["*.txt"]} + + [tool.setuptools.packages.find] + where = ["src"] + exclude = ["*.subpackage*"] + """ + ) + FILES["src"]["mypkg"]["resource.not_in_manifest"] = "abc" + + def test_generated_tree(self, tmp_path): + jaraco.path.build(self.FILES, prefix=tmp_path) + + with _Path(tmp_path): + name = "mypkg-3.14159" + dist = Distribution({"script_name": "%PEP 517%"}) + dist.parse_config_files() + + wheel = Mock() + aux = tmp_path / ".aux" + build = tmp_path / ".build" + aux.mkdir() + build.mkdir() + + build_py = dist.get_command_obj("build_py") + build_py.editable_mode = True + build_py.build_lib = str(build) + build_py.ensure_finalized() + outputs = build_py.get_outputs() + output_mapping = build_py.get_output_mapping() + + make_tree = _LinkTree(dist, name, aux, build) + make_tree(wheel, outputs, output_mapping) + + mod1 = next(aux.glob("**/mod1.py")) + expected = tmp_path / "src/mypkg/mod1.py" + assert_link_to(mod1, expected) + + assert next(aux.glob("**/subpackage"), None) is None + assert next(aux.glob("**/mod2.py"), None) is None + assert next(aux.glob("**/resource_file.txt"), None) is None + + assert next(aux.glob("**/resource.not_in_manifest"), None) is None + + def test_strict_install(self, tmp_path, venv): + opts = ["--config-settings", "editable-mode=strict"] + install_project("mypkg", venv, tmp_path, self.FILES, *opts) + + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + # Ensure packages excluded from distribution are not importable + cmd_import_error = """\ + try: + from mypkg import subpackage + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_import_error)]) + assert "cannot import name 'subpackage'" in out + + # Ensure resource files excluded from distribution are not reachable + cmd_get_resource = """\ + import mypkg + from setuptools._importlib import resources as importlib_resources + try: + text = importlib_resources.files(mypkg) / "resource.not_in_manifest" + print(text.read_text(encoding="utf-8")) + except FileNotFoundError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd_get_resource)]) + assert "No such file or directory" in out + assert "resource.not_in_manifest" in out + + +@pytest.mark.filterwarnings("ignore:.*compat.*:setuptools.SetuptoolsDeprecationWarning") +def test_compat_install(tmp_path, venv): + # TODO: Remove `compat` after Dec/2022. + opts = ["--config-settings", "editable-mode=compat"] + files = TestOverallBehaviour.EXAMPLES["custom-layout"] + install_project("mypkg", venv, tmp_path, files, *opts) + + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + expected_path = comparable_path(str(tmp_path)) + + # Compatible behaviour will make spurious modules and excluded + # files importable directly from the original path + for cmd in ( + "import otherfile; print(otherfile)", + "import other; print(other)", + "import mypkg; print(mypkg)", + ): + out = comparable_path(venv.run(["python", "-c", cmd])) + assert expected_path in out + + # Compatible behaviour will not consider custom mappings + cmd = """\ + try: + from mypkg import subpackage; + except ImportError as ex: + print(ex) + """ + out = venv.run(["python", "-c", dedent(cmd)]) + assert "cannot import name 'subpackage'" in out + + +@pytest.mark.uses_network +def test_pbr_integration(pbr_package, venv, editable_opts): + """Ensure editable installs work with pbr, issue #3500""" + cmd = [ + 'python', + '-m', + 'pip', + '-v', + 'install', + '--editable', + pbr_package, + *editable_opts, + ] + venv.run(cmd, stderr=subprocess.STDOUT) + out = venv.run(["python", "-c", "import mypkg.hello"]) + assert "Hello world!" in out + + +class TestCustomBuildPy: + """ + Issue #3501 indicates that some plugins/customizations might rely on: + + 1. ``build_py`` not running + 2. ``build_py`` always copying files to ``build_lib`` + + During the transition period setuptools should prevent potential errors from + happening due to those assumptions. + """ + + # TODO: Remove tests after _run_build_steps is removed. + + FILES = { + **TestOverallBehaviour.EXAMPLES["flat-layout"], + "setup.py": dedent( + """\ + import pathlib + from setuptools import setup + from setuptools.command.build_py import build_py as orig + + class my_build_py(orig): + def run(self): + super().run() + raise ValueError("TEST_RAISE") + + setup(cmdclass={"build_py": my_build_py}) + """ + ), + } + + def test_safeguarded_from_errors(self, tmp_path, venv): + """Ensure that errors in custom build_py are reported as warnings""" + # Warnings should show up + _, out = install_project("mypkg", venv, tmp_path, self.FILES) + assert "SetuptoolsDeprecationWarning" in out + assert "ValueError: TEST_RAISE" in out + # but installation should be successful + out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) + assert "42" in out + + +class TestCustomBuildWheel: + def install_custom_build_wheel(self, dist): + bdist_wheel_cls = dist.get_command_class("bdist_wheel") + + class MyBdistWheel(bdist_wheel_cls): + def get_tag(self): + # In issue #3513, we can see that some extensions may try to access + # the `plat_name` property in bdist_wheel + if self.plat_name.startswith("macosx-"): + _ = "macOS platform" + return super().get_tag() + + dist.cmdclass["bdist_wheel"] = MyBdistWheel + + def test_access_plat_name(self, tmpdir_cwd): + # Even when a custom bdist_wheel tries to access plat_name the build should + # be successful + jaraco.path.build({"module.py": "x = 42"}) + dist = Distribution() + dist.script_name = "setup.py" + dist.set_defaults() + self.install_custom_build_wheel(dist) + cmd = editable_wheel(dist) + cmd.ensure_finalized() + cmd.run() + wheel_file = str(next(Path().glob('dist/*.whl'))) + assert "editable" in wheel_file + + +class TestCustomBuildExt: + def install_custom_build_ext_distutils(self, dist): + from distutils.command.build_ext import build_ext as build_ext_cls + + class MyBuildExt(build_ext_cls): + pass + + dist.cmdclass["build_ext"] = MyBuildExt + + @pytest.mark.skipif( + sys.platform != "linux", reason="compilers may fail without correct setup" + ) + def test_distutils_leave_inplace_files(self, tmpdir_cwd): + jaraco.path.build({"module.c": ""}) + attrs = { + "ext_modules": [Extension("module", ["module.c"])], + } + dist = Distribution(attrs) + dist.script_name = "setup.py" + dist.set_defaults() + self.install_custom_build_ext_distutils(dist) + cmd = editable_wheel(dist) + cmd.ensure_finalized() + cmd.run() + wheel_file = str(next(Path().glob('dist/*.whl'))) + assert "editable" in wheel_file + files = [p for p in Path().glob("module.*") if p.suffix != ".c"] + assert len(files) == 1 + name = files[0].name + assert any(name.endswith(ext) for ext in EXTENSION_SUFFIXES) + + +def test_debugging_tips(tmpdir_cwd, monkeypatch): + """Make sure to display useful debugging tips to the user.""" + jaraco.path.build({"module.py": "x = 42"}) + dist = Distribution() + dist.script_name = "setup.py" + dist.set_defaults() + cmd = editable_wheel(dist) + cmd.ensure_finalized() + + SimulatedErr = type("SimulatedErr", (Exception,), {}) + simulated_failure = Mock(side_effect=SimulatedErr()) + monkeypatch.setattr(cmd, "get_finalized_command", simulated_failure) + + with pytest.raises(SimulatedErr) as ctx: + cmd.run() + assert any('debugging-tips' in note for note in ctx.value.__notes__) + + +@pytest.mark.filterwarnings("error") +def test_encode_pth(): + """Ensure _encode_pth function does not produce encoding warnings""" + content = _encode_pth("tkmilan_ç_utf8") # no warnings (would be turned into errors) + assert isinstance(content, bytes) + + +def install_project(name, venv, tmp_path, files, *opts): + project = tmp_path / name + project.mkdir() + jaraco.path.build(files, prefix=project) + opts = [*opts, "--no-build-isolation"] # force current version of setuptools + out = venv.run( + ["python", "-m", "pip", "-v", "install", "-e", str(project), *opts], + stderr=subprocess.STDOUT, + ) + return project, out + + +def _addsitedirs(new_dirs): + """To use this function, it is necessary to insert new_dir in front of sys.path. + The Python process will try to import a ``sitecustomize`` module on startup. + If we manipulate sys.path/PYTHONPATH, we can force it to run our code, + which invokes ``addsitedir`` and ensure ``.pth`` files are loaded. + """ + content = '\n'.join( + ("import site",) + + tuple(f"site.addsitedir({os.fspath(new_dir)!r})" for new_dir in new_dirs) + ) + (new_dirs[0] / "sitecustomize.py").write_text(content, encoding="utf-8") + + +# ---- Assertion Helpers ---- + + +def assert_path(pkg, expected): + # __path__ is not guaranteed to exist, so we have to account for that + if pkg.__path__: + path = next(iter(pkg.__path__), None) + if path: + assert str(Path(path).resolve()) == expected + + +def assert_link_to(file: Path, other: Path) -> None: + if file.is_symlink(): + assert str(file.resolve()) == str(other.resolve()) + else: + file_stat = file.stat() + other_stat = other.stat() + assert file_stat[stat.ST_INO] == other_stat[stat.ST_INO] + assert file_stat[stat.ST_DEV] == other_stat[stat.ST_DEV] + + +def comparable_path(str_with_path: str) -> str: + return str_with_path.lower().replace(os.sep, "/").replace("//", "/") diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py new file mode 100644 index 0000000..3653be0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_egg_info.py @@ -0,0 +1,1306 @@ +from __future__ import annotations + +import ast +import glob +import os +import re +import stat +import sys +import time +from pathlib import Path +from unittest import mock + +import pytest +from jaraco import path + +from setuptools import errors +from setuptools.command.egg_info import egg_info, manifest_maker, write_entries +from setuptools.dist import Distribution + +from . import contexts, environment +from .textwrap import DALS + + +class Environment(str): + pass + + +@pytest.fixture +def env(): + with contexts.tempdir(prefix='setuptools-test.') as env_dir: + env = Environment(env_dir) + os.chmod(env_dir, stat.S_IRWXU) + subs = 'home', 'lib', 'scripts', 'data', 'egg-base' + env.paths = dict((dirname, os.path.join(env_dir, dirname)) for dirname in subs) + list(map(os.mkdir, env.paths.values())) + path.build({ + env.paths['home']: { + '.pydistutils.cfg': DALS( + """ + [egg_info] + egg-base = {egg-base} + """.format(**env.paths) + ) + } + }) + yield env + + +class TestEggInfo: + setup_script = DALS( + """ + from setuptools import setup + + setup( + name='foo', + py_modules=['hello'], + entry_points={'console_scripts': ['hi = hello.run']}, + zip_safe=False, + ) + """ + ) + + def _create_project(self): + path.build({ + 'setup.py': self.setup_script, + 'hello.py': DALS( + """ + def run(): + print('hello') + """ + ), + }) + + @staticmethod + def _extract_mv_version(pkg_info_lines: list[str]) -> tuple[int, int]: + version_str = pkg_info_lines[0].split(' ')[1] + major, minor = map(int, version_str.split('.')[:2]) + return major, minor + + def test_egg_info_save_version_info_setup_empty(self, tmpdir_cwd, env): + """ + When the egg_info section is empty or not present, running + save_version_info should add the settings to the setup.cfg + in a deterministic order. + """ + setup_cfg = os.path.join(env.paths['home'], 'setup.cfg') + dist = Distribution() + ei = egg_info(dist) + ei.initialize_options() + ei.save_version_info(setup_cfg) + + with open(setup_cfg, 'r', encoding="utf-8") as f: + content = f.read() + + assert '[egg_info]' in content + assert 'tag_build =' in content + assert 'tag_date = 0' in content + + expected_order = ( + 'tag_build', + 'tag_date', + ) + + self._validate_content_order(content, expected_order) + + @staticmethod + def _validate_content_order(content, expected): + """ + Assert that the strings in expected appear in content + in order. + """ + pattern = '.*'.join(expected) + flags = re.MULTILINE | re.DOTALL + assert re.search(pattern, content, flags) + + def test_egg_info_save_version_info_setup_defaults(self, tmpdir_cwd, env): + """ + When running save_version_info on an existing setup.cfg + with the 'default' values present from a previous run, + the file should remain unchanged. + """ + setup_cfg = os.path.join(env.paths['home'], 'setup.cfg') + path.build({ + setup_cfg: DALS( + """ + [egg_info] + tag_build = + tag_date = 0 + """ + ), + }) + dist = Distribution() + ei = egg_info(dist) + ei.initialize_options() + ei.save_version_info(setup_cfg) + + with open(setup_cfg, 'r', encoding="utf-8") as f: + content = f.read() + + assert '[egg_info]' in content + assert 'tag_build =' in content + assert 'tag_date = 0' in content + + expected_order = ( + 'tag_build', + 'tag_date', + ) + + self._validate_content_order(content, expected_order) + + def test_expected_files_produced(self, tmpdir_cwd, env): + self._create_project() + + self._run_egg_info_command(tmpdir_cwd, env) + actual = os.listdir('foo.egg-info') + + expected = [ + 'PKG-INFO', + 'SOURCES.txt', + 'dependency_links.txt', + 'entry_points.txt', + 'not-zip-safe', + 'top_level.txt', + ] + assert sorted(actual) == expected + + def test_handling_utime_error(self, tmpdir_cwd, env): + dist = Distribution() + ei = egg_info(dist) + utime_patch = mock.patch('os.utime', side_effect=OSError("TEST")) + mkpath_patch = mock.patch( + 'setuptools.command.egg_info.egg_info.mkpath', return_val=None + ) + + with utime_patch, mkpath_patch: + import distutils.errors + + msg = r"Cannot update time stamp of directory 'None'" + with pytest.raises(distutils.errors.DistutilsFileError, match=msg): + ei.run() + + def test_license_is_a_string(self, tmpdir_cwd, env): + setup_config = DALS( + """ + [metadata] + name=foo + version=0.0.1 + license=file:MIT + """ + ) + + setup_script = DALS( + """ + from setuptools import setup + + setup() + """ + ) + + path.build({ + 'setup.py': setup_script, + 'setup.cfg': setup_config, + }) + + # This command should fail with a ValueError, but because it's + # currently configured to use a subprocess, the actual traceback + # object is lost and we need to parse it from stderr + with pytest.raises(AssertionError) as exc: + self._run_egg_info_command(tmpdir_cwd, env) + + # The only argument to the assertion error should be a traceback + # containing a ValueError + assert 'ValueError' in exc.value.args[0] + + def test_rebuilt(self, tmpdir_cwd, env): + """Ensure timestamps are updated when the command is re-run.""" + self._create_project() + + self._run_egg_info_command(tmpdir_cwd, env) + timestamp_a = os.path.getmtime('foo.egg-info') + + # arbitrary sleep just to handle *really* fast systems + time.sleep(0.001) + + self._run_egg_info_command(tmpdir_cwd, env) + timestamp_b = os.path.getmtime('foo.egg-info') + + assert timestamp_a != timestamp_b + + def test_manifest_template_is_read(self, tmpdir_cwd, env): + self._create_project() + path.build({ + 'MANIFEST.in': DALS( + """ + recursive-include docs *.rst + """ + ), + 'docs': { + 'usage.rst': "Run 'hi'", + }, + }) + self._run_egg_info_command(tmpdir_cwd, env) + egg_info_dir = os.path.join('.', 'foo.egg-info') + sources_txt = os.path.join(egg_info_dir, 'SOURCES.txt') + with open(sources_txt, encoding="utf-8") as f: + assert 'docs/usage.rst' in f.read().split('\n') + + def _setup_script_with_requires(self, requires, use_setup_cfg=False): + setup_script = DALS( + """ + from setuptools import setup + + setup(name='foo', zip_safe=False, %s) + """ + ) % ('' if use_setup_cfg else requires) + setup_config = requires if use_setup_cfg else '' + path.build({ + 'setup.py': setup_script, + 'setup.cfg': setup_config, + }) + + mismatch_marker = f"python_version<'{sys.version_info[0]}'" + # Alternate equivalent syntax. + mismatch_marker_alternate = f'python_version < "{sys.version_info[0]}"' + invalid_marker = "<=>++" + + class RequiresTestHelper: + @staticmethod + def parametrize(*test_list, **format_dict): + idlist = [] + argvalues = [] + for test in test_list: + test_params = test.lstrip().split('\n\n', 3) + name_kwargs = test_params.pop(0).split('\n') + if len(name_kwargs) > 1: + val = name_kwargs[1].strip() + install_cmd_kwargs = ast.literal_eval(val) + else: + install_cmd_kwargs = {} + name = name_kwargs[0].strip() + setup_py_requires, setup_cfg_requires, expected_requires = [ + DALS(a).format(**format_dict) for a in test_params + ] + for id_, requires, use_cfg in ( + (name, setup_py_requires, False), + (name + '_in_setup_cfg', setup_cfg_requires, True), + ): + idlist.append(id_) + marks = () + if requires.startswith('@xfail\n'): + requires = requires[7:] + marks = pytest.mark.xfail + argvalues.append( + pytest.param( + requires, + use_cfg, + expected_requires, + install_cmd_kwargs, + marks=marks, + ) + ) + return pytest.mark.parametrize( + ( + "requires", + "use_setup_cfg", + "expected_requires", + "install_cmd_kwargs", + ), + argvalues, + ids=idlist, + ) + + @RequiresTestHelper.parametrize( + # Format of a test: + # + # id + # install_cmd_kwargs [optional] + # + # requires block (when used in setup.py) + # + # requires block (when used in setup.cfg) + # + # expected contents of requires.txt + """ + install_requires_deterministic + + install_requires=["wheel>=0.5", "pytest"] + + [options] + install_requires = + wheel>=0.5 + pytest + + wheel>=0.5 + pytest + """, + """ + install_requires_ordered + + install_requires=["pytest>=3.0.2,!=10.9999"] + + [options] + install_requires = + pytest>=3.0.2,!=10.9999 + + pytest!=10.9999,>=3.0.2 + """, + """ + install_requires_with_marker + + install_requires=["barbazquux;{mismatch_marker}"], + + [options] + install_requires = + barbazquux; {mismatch_marker} + + [:{mismatch_marker_alternate}] + barbazquux + """, + """ + install_requires_with_extra + {'cmd': ['egg_info']} + + install_requires=["barbazquux [test]"], + + [options] + install_requires = + barbazquux [test] + + barbazquux[test] + """, + """ + install_requires_with_extra_and_marker + + install_requires=["barbazquux [test]; {mismatch_marker}"], + + [options] + install_requires = + barbazquux [test]; {mismatch_marker} + + [:{mismatch_marker_alternate}] + barbazquux[test] + """, + """ + setup_requires_with_markers + + setup_requires=["barbazquux;{mismatch_marker}"], + + [options] + setup_requires = + barbazquux; {mismatch_marker} + + """, + """ + extras_require_with_extra + {'cmd': ['egg_info']} + + extras_require={{"extra": ["barbazquux [test]"]}}, + + [options.extras_require] + extra = barbazquux [test] + + [extra] + barbazquux[test] + """, + """ + extras_require_with_extra_and_marker_in_req + + extras_require={{"extra": ["barbazquux [test]; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux [test]; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux[test] + """, + # FIXME: ConfigParser does not allow : in key names! + """ + extras_require_with_marker + + extras_require={{":{mismatch_marker}": ["barbazquux"]}}, + + @xfail + [options.extras_require] + :{mismatch_marker} = barbazquux + + [:{mismatch_marker}] + barbazquux + """, + """ + extras_require_with_marker_in_req + + extras_require={{"extra": ["barbazquux; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux + """, + """ + extras_require_with_empty_section + + extras_require={{"empty": []}}, + + [options.extras_require] + empty = + + [empty] + """, + # Format arguments. + invalid_marker=invalid_marker, + mismatch_marker=mismatch_marker, + mismatch_marker_alternate=mismatch_marker_alternate, + ) + def test_requires( + self, + tmpdir_cwd, + env, + requires, + use_setup_cfg, + expected_requires, + install_cmd_kwargs, + ): + self._setup_script_with_requires(requires, use_setup_cfg) + self._run_egg_info_command(tmpdir_cwd, env, **install_cmd_kwargs) + egg_info_dir = os.path.join('.', 'foo.egg-info') + requires_txt = os.path.join(egg_info_dir, 'requires.txt') + if os.path.exists(requires_txt): + with open(requires_txt, encoding="utf-8") as fp: + install_requires = fp.read() + else: + install_requires = '' + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_install_requires_unordered_disallowed(self, tmpdir_cwd, env): + """ + Packages that pass unordered install_requires sequences + should be rejected as they produce non-deterministic + builds. See #458. + """ + req = 'install_requires={"fake-factory==0.5.2", "pytz"}' + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + + def test_extras_require_with_invalid_marker(self, tmpdir_cwd, env): + tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_invalid_marker_in_req(self, tmpdir_cwd, env): + tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_egg_info_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_provides_extra(self, tmpdir_cwd, env): + self._setup_script_with_requires('extras_require={"foobar": ["barbazquux"]},') + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Provides-Extra: foobar' in pkg_info_lines + assert 'Metadata-Version: 2.4' in pkg_info_lines + + def test_doesnt_provides_extra(self, tmpdir_cwd, env): + self._setup_script_with_requires( + """install_requires=["spam ; python_version<'3.6'"]""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_text = fp.read() + assert 'Provides-Extra:' not in pkg_info_text + + @pytest.mark.parametrize( + ('files', 'license_in_sources'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE + """ + ), + 'LICENSE': "Test license", + }, + True, + ), # with license + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = INVALID_LICENSE + """ + ), + 'LICENSE': "Test license", + }, + False, + ), # with an invalid license + ( + { + 'setup.cfg': DALS( + """ + """ + ), + 'LICENSE': "Test license", + }, + True, + ), # no license_file attribute, LICENSE auto-included + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE + """ + ), + 'MANIFEST.in': "exclude LICENSE", + 'LICENSE': "Test license", + }, + True, + ), # manifest is overwritten by license_file + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICEN[CS]E* + """ + ), + 'LICENSE': "Test license", + }, + True, + id="glob_pattern", + ), + ], + ) + def test_setup_cfg_license_file(self, tmpdir_cwd, env, files, license_in_sources): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + + if license_in_sources: + assert 'LICENSE' in sources_text + else: + assert 'LICENSE' not in sources_text + # for invalid license test + assert 'INVALID_LICENSE' not in sources_text + + @pytest.mark.parametrize( + ('files', 'incl_licenses', 'excl_licenses'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), # with licenses + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE-ABC, LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), # with commas + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC'], + ['LICENSE-XYZ'], + ), # with one license + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), # empty + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-XYZ'], + ['LICENSE-ABC'], + ), # on same line + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + INVALID_LICENSE + """ + ), + 'LICENSE-ABC': "Test license", + }, + ['LICENSE-ABC'], + ['INVALID_LICENSE'], + ), # with an invalid license + ( + { + 'setup.cfg': DALS( + """ + """ + ), + 'LICENSE': "Test license", + }, + ['LICENSE'], + [], + ), # no license_files attribute, LICENSE auto-included + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = LICENSE + """ + ), + 'MANIFEST.in': "exclude LICENSE", + 'LICENSE': "Test license", + }, + ['LICENSE'], + [], + ), # manifest is overwritten by license_files + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'MANIFEST.in': "exclude LICENSE-XYZ", + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + # manifest is overwritten by license_files + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + [], + ), + pytest.param( + { + 'setup.cfg': "", + 'LICENSE-ABC': "ABC license", + 'COPYING-ABC': "ABC copying", + 'NOTICE-ABC': "ABC notice", + 'AUTHORS-ABC': "ABC authors", + 'LICENCE-XYZ': "XYZ license", + 'LICENSE': "License", + 'INVALID-LICENSE': "Invalid license", + }, + [ + 'LICENSE-ABC', + 'COPYING-ABC', + 'NOTICE-ABC', + 'AUTHORS-ABC', + 'LICENCE-XYZ', + 'LICENSE', + ], + ['INVALID-LICENSE'], + # ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + id="default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-XYZ': "XYZ notice", + }, + ['LICENSE-ABC'], + ['NOTICE-XYZ'], + id="no_default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = + LICENSE-ABC + LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + }, + ['LICENSE-ABC'], + [], + id="files_only_added_once", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_files = **/LICENSE + """ + ), + 'LICENSE': "ABC license", + 'LICENSE-OTHER': "Don't include", + 'vendor': {'LICENSE': "Vendor license"}, + }, + ['LICENSE', 'vendor/LICENSE'], + ['LICENSE-OTHER'], + id="recursive_glob", + ), + ], + ) + def test_setup_cfg_license_files( + self, tmpdir_cwd, env, files, incl_licenses, excl_licenses + ): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + sources_lines = [line.strip() for line in sources_text.splitlines()] + + for lf in incl_licenses: + assert sources_lines.count(lf) == 1 + + for lf in excl_licenses: + assert sources_lines.count(lf) == 0 + + @pytest.mark.parametrize( + ('files', 'incl_licenses', 'excl_licenses'), + [ + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = + license_files = + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), # both empty + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = + LICENSE-ABC + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-XYZ': "XYZ license", + # license_file is still singular + }, + [], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), # combined + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-ABC + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # duplicate license + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + """ + ), + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # combined subset + }, + ['LICENSE-ABC', 'LICENSE-XYZ'], + ['LICENSE-PQR'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-XYZ + LICENSE-PQR + """ + ), + 'LICENSE-PQR': "Test license", + # with invalid licenses + }, + ['LICENSE-PQR'], + ['LICENSE-ABC', 'LICENSE-XYZ'], + ), + ( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE-ABC + license_files = + LICENSE-PQR + LICENSE-XYZ + """ + ), + 'MANIFEST.in': "exclude LICENSE-ABC\nexclude LICENSE-PQR", + 'LICENSE-ABC': "ABC license", + 'LICENSE-PQR': "PQR license", + 'LICENSE-XYZ': "XYZ license", + # manifest is overwritten + }, + ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], + [], + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-XYZ': "XYZ notice", + }, + ['LICENSE-ABC'], + ['NOTICE-XYZ'], + id="no_default_glob_patterns", + ), + pytest.param( + { + 'setup.cfg': DALS( + """ + [metadata] + license_file = LICENSE* + license_files = + NOTICE* + """ + ), + 'LICENSE-ABC': "ABC license", + 'NOTICE-ABC': "ABC notice", + 'AUTHORS-ABC': "ABC authors", + }, + ['LICENSE-ABC', 'NOTICE-ABC'], + ['AUTHORS-ABC'], + id="combined_glob_patterrns", + ), + ], + ) + def test_setup_cfg_license_file_license_files( + self, tmpdir_cwd, env, files, incl_licenses, excl_licenses + ): + self._create_project() + path.build(files) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + + sources_text = Path(egg_info_dir, "SOURCES.txt").read_text(encoding="utf-8") + sources_lines = [line.strip() for line in sources_text.splitlines()] + + for lf in incl_licenses: + assert sources_lines.count(lf) == 1 + + for lf in excl_licenses: + assert sources_lines.count(lf) == 0 + + def test_license_file_attr_pkg_info(self, tmpdir_cwd, env): + """All matched license files should have a corresponding License-File.""" + self._create_project() + path.build({ + "setup.cfg": DALS( + """ + [metadata] + license_files = + NOTICE* + LICENSE* + **/LICENSE + """ + ), + "LICENSE-ABC": "ABC license", + "LICENSE-XYZ": "XYZ license", + "NOTICE": "included", + "IGNORE": "not include", + "vendor": {'LICENSE': "Vendor license"}, + }) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + license_file_lines = [ + line for line in pkg_info_lines if line.startswith('License-File:') + ] + + # Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched + # Also assert that order from license_files is keeped + assert len(license_file_lines) == 4 + assert "License-File: NOTICE" == license_file_lines[0] + assert "License-File: LICENSE-ABC" in license_file_lines[1:] + assert "License-File: LICENSE-XYZ" in license_file_lines[1:] + assert "License-File: vendor/LICENSE" in license_file_lines[3] + + def test_metadata_version(self, tmpdir_cwd, env): + """Make sure latest metadata version is used by default.""" + self._setup_script_with_requires("") + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + # Update metadata version if changed + assert self._extract_mv_version(pkg_info_lines) == (2, 4) + + def test_long_description_content_type(self, tmpdir_cwd, env): + # Test that specifying a `long_description_content_type` keyword arg to + # the `setup` function results in writing a `Description-Content-Type` + # line to the `PKG-INFO` file in the `.egg-info` + # directory. + # `Description-Content-Type` is described at + # https://github.com/pypa/python-packaging-user-guide/pull/258 + + self._setup_script_with_requires( + """long_description_content_type='text/markdown',""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + expected_line = 'Description-Content-Type: text/markdown' + assert expected_line in pkg_info_lines + assert 'Metadata-Version: 2.4' in pkg_info_lines + + def test_long_description(self, tmpdir_cwd, env): + # Test that specifying `long_description` and `long_description_content_type` + # keyword args to the `setup` function results in writing + # the description in the message payload of the `PKG-INFO` file + # in the `.egg-info` directory. + self._setup_script_with_requires( + "long_description='This is a long description\\nover multiple lines'," + "long_description_content_type='text/markdown'," + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Metadata-Version: 2.4' in pkg_info_lines + assert '' == pkg_info_lines[-1] # last line should be empty + long_desc_lines = pkg_info_lines[pkg_info_lines.index('') :] + assert 'This is a long description' in long_desc_lines + assert 'over multiple lines' in long_desc_lines + + def test_project_urls(self, tmpdir_cwd, env): + # Test that specifying a `project_urls` dict to the `setup` + # function results in writing multiple `Project-URL` lines to + # the `PKG-INFO` file in the `.egg-info` + # directory. + # `Project-URL` is described at https://packaging.python.org + # /specifications/core-metadata/#project-url-multiple-use + + self._setup_script_with_requires( + """project_urls={ + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + },""" + ) + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + expected_line = 'Project-URL: Link One, https://example.com/one/' + assert expected_line in pkg_info_lines + expected_line = 'Project-URL: Link Two, https://example.com/two/' + assert expected_line in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) + + def test_license(self, tmpdir_cwd, env): + """Test single line license.""" + self._setup_script_with_requires("license='MIT',") + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'License: MIT' in pkg_info_lines + + def test_license_escape(self, tmpdir_cwd, env): + """Test license is escaped correctly if longer than one line.""" + self._setup_script_with_requires( + "license='This is a long license text \\nover multiple lines'," + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + + assert 'License: This is a long license text ' in pkg_info_lines + assert ' over multiple lines' in pkg_info_lines + assert 'text \n over multiple' in '\n'.join(pkg_info_lines) + + def test_python_requires_egg_info(self, tmpdir_cwd, env): + self._setup_script_with_requires("""python_requires='>=2.7.12',""") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Requires-Python: >=2.7.12' in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) + + def test_manifest_maker_warning_suppression(self): + fixtures = [ + "standard file not found: should have one of foo.py, bar.py", + "standard file 'setup.py' not found", + ] + + for msg in fixtures: + assert manifest_maker._should_suppress_warning(msg) + + def test_egg_info_includes_setup_py(self, tmpdir_cwd): + self._create_project() + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.script_name = "non_setup.py" + egg_info_instance = egg_info(dist) + egg_info_instance.finalize_options() + egg_info_instance.run() + + assert 'setup.py' in egg_info_instance.filelist.files + + with open(egg_info_instance.egg_info + "/SOURCES.txt", encoding="utf-8") as f: + sources = f.read().split('\n') + assert 'setup.py' in sources + + def _run_egg_info_command(self, tmpdir_cwd, env, cmd=None, output=None): + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + if cmd is None: + cmd = [ + 'egg_info', + ] + code, data = environment.run_setup_py( + cmd=cmd, + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + assert not code, data + + if output: + assert output in data + + def test_egg_info_tag_only_once(self, tmpdir_cwd, env): + self._create_project() + path.build({ + 'setup.cfg': DALS( + """ + [egg_info] + tag_build = dev + tag_date = 0 + tag_svn_revision = 0 + """ + ), + }) + self._run_egg_info_command(tmpdir_cwd, env) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO'), encoding="utf-8") as fp: + pkg_info_lines = fp.read().split('\n') + assert 'Version: 0.0.0.dev0' in pkg_info_lines + + +class TestWriteEntries: + def test_invalid_entry_point(self, tmpdir_cwd, env): + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.entry_points = {"foo": "foo = invalid-identifier:foo"} + cmd = dist.get_command_obj("egg_info") + expected_msg = r"(Invalid object reference|Problems to parse)" + with pytest.raises((errors.OptionError, ValueError), match=expected_msg) as ex: + write_entries(cmd, "entry_points", "entry_points.txt") + assert "ensure entry-point follows the spec" in ex.value.args[0] + assert "invalid-identifier" in str(ex.value) + + def test_valid_entry_point(self, tmpdir_cwd, env): + dist = Distribution({"name": "foo", "version": "0.0.1"}) + dist.entry_points = { + "abc": "foo = bar:baz", + "def": ["faa = bor:boz"], + } + cmd = dist.get_command_obj("egg_info") + write_entries(cmd, "entry_points", "entry_points.txt") + content = Path("entry_points.txt").read_text(encoding="utf-8") + assert "[abc]\nfoo = bar:baz\n" in content + assert "[def]\nfaa = bor:boz\n" in content diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_extern.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_extern.py new file mode 100644 index 0000000..d7eb3c6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_extern.py @@ -0,0 +1,15 @@ +import importlib +import pickle + +import packaging + +from setuptools import Distribution + + +def test_reimport_extern(): + packaging2 = importlib.import_module(packaging.__name__) + assert packaging is packaging2 + + +def test_distribution_picklable(): + pickle.loads(pickle.dumps(Distribution())) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py new file mode 100644 index 0000000..9fd9f8f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_packages.py @@ -0,0 +1,218 @@ +"""Tests for automatic package discovery""" + +import os +import shutil +import tempfile + +import pytest + +from setuptools import find_namespace_packages, find_packages +from setuptools.discovery import FlatLayoutPackageFinder + +from .compat.py39 import os_helper + + +class TestFindPackages: + def setup_method(self, method): + self.dist_dir = tempfile.mkdtemp() + self._make_pkg_structure() + + def teardown_method(self, method): + shutil.rmtree(self.dist_dir) + + def _make_pkg_structure(self): + """Make basic package structure. + + dist/ + docs/ + conf.py + pkg/ + __pycache__/ + nspkg/ + mod.py + subpkg/ + assets/ + asset + __init__.py + setup.py + + """ + self.docs_dir = self._mkdir('docs', self.dist_dir) + self._touch('conf.py', self.docs_dir) + self.pkg_dir = self._mkdir('pkg', self.dist_dir) + self._mkdir('__pycache__', self.pkg_dir) + self.ns_pkg_dir = self._mkdir('nspkg', self.pkg_dir) + self._touch('mod.py', self.ns_pkg_dir) + self.sub_pkg_dir = self._mkdir('subpkg', self.pkg_dir) + self.asset_dir = self._mkdir('assets', self.sub_pkg_dir) + self._touch('asset', self.asset_dir) + self._touch('__init__.py', self.sub_pkg_dir) + self._touch('setup.py', self.dist_dir) + + def _mkdir(self, path, parent_dir=None): + if parent_dir: + path = os.path.join(parent_dir, path) + os.mkdir(path) + return path + + def _touch(self, path, dir_=None): + if dir_: + path = os.path.join(dir_, path) + open(path, 'wb').close() + return path + + def test_regular_package(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_packages(self.dist_dir) + assert packages == ['pkg', 'pkg.subpkg'] + + def test_exclude(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_packages(self.dist_dir, exclude=('pkg.*',)) + assert packages == ['pkg'] + + def test_exclude_recursive(self): + """ + Excluding a parent package should not exclude child packages as well. + """ + self._touch('__init__.py', self.pkg_dir) + self._touch('__init__.py', self.sub_pkg_dir) + packages = find_packages(self.dist_dir, exclude=('pkg',)) + assert packages == ['pkg.subpkg'] + + def test_include_excludes_other(self): + """ + If include is specified, other packages should be excluded. + """ + self._touch('__init__.py', self.pkg_dir) + alt_dir = self._mkdir('other_pkg', self.dist_dir) + self._touch('__init__.py', alt_dir) + packages = find_packages(self.dist_dir, include=['other_pkg']) + assert packages == ['other_pkg'] + + def test_dir_with_dot_is_skipped(self): + shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) + data_dir = self._mkdir('some.data', self.pkg_dir) + self._touch('__init__.py', data_dir) + self._touch('file.dat', data_dir) + packages = find_packages(self.dist_dir) + assert 'pkg.some.data' not in packages + + def test_dir_with_packages_in_subdir_is_excluded(self): + """ + Ensure that a package in a non-package such as build/pkg/__init__.py + is excluded. + """ + build_dir = self._mkdir('build', self.dist_dir) + build_pkg_dir = self._mkdir('pkg', build_dir) + self._touch('__init__.py', build_pkg_dir) + packages = find_packages(self.dist_dir) + assert 'build.pkg' not in packages + + @pytest.mark.skipif(not os_helper.can_symlink(), reason='Symlink support required') + def test_symlinked_packages_are_included(self): + """ + A symbolically-linked directory should be treated like any other + directory when matched as a package. + + Create a link from lpkg -> pkg. + """ + self._touch('__init__.py', self.pkg_dir) + linked_pkg = os.path.join(self.dist_dir, 'lpkg') + os.symlink('pkg', linked_pkg) + assert os.path.isdir(linked_pkg) + packages = find_packages(self.dist_dir) + assert 'lpkg' in packages + + def _assert_packages(self, actual, expected): + assert set(actual) == set(expected) + + def test_pep420_ns_package(self): + packages = find_namespace_packages( + self.dist_dir, include=['pkg*'], exclude=['pkg.subpkg.assets'] + ) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_includes(self): + packages = find_namespace_packages(self.dist_dir, exclude=['pkg.subpkg.assets']) + self._assert_packages(packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_includes_or_excludes(self): + packages = find_namespace_packages(self.dist_dir) + expected = ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets'] + self._assert_packages(packages, expected) + + def test_regular_package_with_nested_pep420_ns_packages(self): + self._touch('__init__.py', self.pkg_dir) + packages = find_namespace_packages( + self.dist_dir, exclude=['docs', 'pkg.subpkg.assets'] + ) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + def test_pep420_ns_package_no_non_package_dirs(self): + shutil.rmtree(self.docs_dir) + shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) + packages = find_namespace_packages(self.dist_dir) + self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) + + +class TestFlatLayoutPackageFinder: + EXAMPLES = { + "hidden-folders": ( + [".pkg/__init__.py", "pkg/__init__.py", "pkg/nested/file.txt"], + ["pkg", "pkg.nested"], + ), + "private-packages": ( + ["_pkg/__init__.py", "pkg/_private/__init__.py"], + ["pkg", "pkg._private"], + ), + "invalid-name": ( + ["invalid-pkg/__init__.py", "other.pkg/__init__.py", "yet,another/file.py"], + [], + ), + "docs": (["pkg/__init__.py", "docs/conf.py", "docs/readme.rst"], ["pkg"]), + "tests": ( + ["pkg/__init__.py", "tests/test_pkg.py", "tests/__init__.py"], + ["pkg"], + ), + "examples": ( + [ + "pkg/__init__.py", + "examples/__init__.py", + "examples/file.py", + "example/other_file.py", + # Sub-packages should always be fine + "pkg/example/__init__.py", + "pkg/examples/__init__.py", + ], + ["pkg", "pkg.examples", "pkg.example"], + ), + "tool-specific": ( + [ + "htmlcov/index.html", + "pkg/__init__.py", + "tasks/__init__.py", + "tasks/subpackage/__init__.py", + "fabfile/__init__.py", + "fabfile/subpackage/__init__.py", + # Sub-packages should always be fine + "pkg/tasks/__init__.py", + "pkg/fabfile/__init__.py", + ], + ["pkg", "pkg.tasks", "pkg.fabfile"], + ), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_unwanted_directories_not_included(self, tmp_path, example): + files, expected_packages = self.EXAMPLES[example] + ensure_files(tmp_path, files) + found_packages = FlatLayoutPackageFinder.find(str(tmp_path)) + assert set(found_packages) == set(expected_packages) + + +def ensure_files(root_path, files): + for file in files: + path = root_path / file + path.parent.mkdir(parents=True, exist_ok=True) + path.touch() diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py new file mode 100644 index 0000000..8034b54 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_find_py_modules.py @@ -0,0 +1,73 @@ +"""Tests for automatic discovery of modules""" + +import os + +import pytest + +from setuptools.discovery import FlatLayoutModuleFinder, ModuleFinder + +from .compat.py39 import os_helper +from .test_find_packages import ensure_files + + +class TestModuleFinder: + def find(self, path, *args, **kwargs): + return set(ModuleFinder.find(str(path), *args, **kwargs)) + + EXAMPLES = { + # circumstance: (files, kwargs, expected_modules) + "simple_folder": ( + ["file.py", "other.py"], + {}, # kwargs + ["file", "other"], + ), + "exclude": ( + ["file.py", "other.py"], + {"exclude": ["f*"]}, + ["other"], + ), + "include": ( + ["file.py", "fole.py", "other.py"], + {"include": ["f*"], "exclude": ["fo*"]}, + ["file"], + ), + "invalid-name": (["my-file.py", "other.file.py"], {}, []), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_finder(self, tmp_path, example): + files, kwargs, expected_modules = self.EXAMPLES[example] + ensure_files(tmp_path, files) + assert self.find(tmp_path, **kwargs) == set(expected_modules) + + @pytest.mark.skipif(not os_helper.can_symlink(), reason='Symlink support required') + def test_symlinked_packages_are_included(self, tmp_path): + src = "_myfiles/file.py" + ensure_files(tmp_path, [src]) + os.symlink(tmp_path / src, tmp_path / "link.py") + assert self.find(tmp_path) == {"link"} + + +class TestFlatLayoutModuleFinder: + def find(self, path, *args, **kwargs): + return set(FlatLayoutModuleFinder.find(str(path))) + + EXAMPLES = { + # circumstance: (files, expected_modules) + "hidden-files": ([".module.py"], []), + "private-modules": (["_module.py"], []), + "common-names": ( + ["setup.py", "conftest.py", "test.py", "tests.py", "example.py", "mod.py"], + ["mod"], + ), + "tool-specific": ( + ["tasks.py", "fabfile.py", "noxfile.py", "dodo.py", "manage.py", "mod.py"], + ["mod"], + ), + } + + @pytest.mark.parametrize("example", EXAMPLES.keys()) + def test_unwanted_files_not_included(self, tmp_path, example): + files, expected_modules = self.EXAMPLES[example] + ensure_files(tmp_path, files) + assert self.find(tmp_path) == set(expected_modules) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_glob.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_glob.py new file mode 100644 index 0000000..8d225a4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_glob.py @@ -0,0 +1,45 @@ +import pytest +from jaraco import path + +from setuptools.glob import glob + + +@pytest.mark.parametrize( + ('tree', 'pattern', 'matches'), + ( + ('', b'', []), + ('', '', []), + ( + """ + appveyor.yml + CHANGES.rst + LICENSE + MANIFEST.in + pyproject.toml + README.rst + setup.cfg + setup.py + """, + '*.rst', + ('CHANGES.rst', 'README.rst'), + ), + ( + """ + appveyor.yml + CHANGES.rst + LICENSE + MANIFEST.in + pyproject.toml + README.rst + setup.cfg + setup.py + """, + b'*.rst', + (b'CHANGES.rst', b'README.rst'), + ), + ), +) +def test_glob(monkeypatch, tmpdir, tree, pattern, matches): + monkeypatch.chdir(tmpdir) + path.build({name: '' for name in tree.split()}) + assert list(sorted(glob(pattern))) == list(sorted(matches)) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py new file mode 100644 index 0000000..e62a6b7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_install_scripts.py @@ -0,0 +1,89 @@ +"""install_scripts tests""" + +import sys + +import pytest + +from setuptools.command.install_scripts import install_scripts +from setuptools.dist import Distribution + +from . import contexts + + +class TestInstallScripts: + settings = dict( + name='foo', + entry_points={'console_scripts': ['foo=foo:foo']}, + version='0.0', + ) + unix_exe = '/usr/dummy-test-path/local/bin/python' + unix_spaces_exe = '/usr/bin/env dummy-test-python' + win32_exe = 'C:\\Dummy Test Path\\Program Files\\Python 3.6\\python.exe' + + def _run_install_scripts(self, install_dir, executable=None): + dist = Distribution(self.settings) + dist.script_name = 'setup.py' + cmd = install_scripts(dist) + cmd.install_dir = install_dir + if executable is not None: + bs = cmd.get_finalized_command('build_scripts') + bs.executable = executable + cmd.ensure_finalized() + with contexts.quiet(): + cmd.run() + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_sys_executable_escaping_unix(self, tmpdir, monkeypatch): + """ + Ensure that shebang is not quoted on Unix when getting the Python exe + from sys.executable. + """ + expected = f'#!{self.unix_exe}\n' + monkeypatch.setattr('sys.executable', self.unix_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with open(str(tmpdir.join('foo')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_sys_executable_escaping_win32(self, tmpdir, monkeypatch): + """ + Ensure that shebang is quoted on Windows when getting the Python exe + from sys.executable and it contains a space. + """ + expected = f'#!"{self.win32_exe}"\n' + monkeypatch.setattr('sys.executable', self.win32_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with open(str(tmpdir.join('foo-script.py')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_executable_with_spaces_escaping_unix(self, tmpdir): + """ + Ensure that shebang on Unix is not quoted, even when + a value with spaces + is specified using --executable. + """ + expected = f'#!{self.unix_spaces_exe}\n' + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), self.unix_spaces_exe) + with open(str(tmpdir.join('foo')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_executable_arg_escaping_win32(self, tmpdir): + """ + Ensure that shebang on Windows is quoted when + getting a path with spaces + from --executable, that is itself properly quoted. + """ + expected = f'#!"{self.win32_exe}"\n' + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), '"' + self.win32_exe + '"') + with open(str(tmpdir.join('foo-script.py')), 'r', encoding="utf-8") as f: + actual = f.readline() + assert actual == expected diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_logging.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_logging.py new file mode 100644 index 0000000..5d487bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_logging.py @@ -0,0 +1,76 @@ +import functools +import inspect +import logging +import sys + +import pytest + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +setup_py = """\ +from setuptools import setup + +setup( + name="test_logging", + version="0.0" +) +""" + + +@pytest.mark.parametrize( + ('flags', 'expected_level'), [([], "INFO"), (["--verbose"], "DEBUG")] +) +def test_verbosity_level(tmp_path, monkeypatch, flags, expected_level): + """Make sure the correct verbosity level is set (issue #3038)""" + import setuptools # noqa: F401 # import setuptools to monkeypatch distutils + + import distutils # <- load distutils after all the patches take place + + logger = logging.Logger(__name__) + monkeypatch.setattr(logging, "root", logger) + unset_log_level = logger.getEffectiveLevel() + assert logging.getLevelName(unset_log_level) == "NOTSET" + + setup_script = tmp_path / "setup.py" + setup_script.write_text(setup_py, encoding="utf-8") + dist = distutils.core.run_setup(setup_script, stop_after="init") + dist.script_args = flags + ["sdist"] + dist.parse_command_line() # <- where the log level is set + log_level = logger.getEffectiveLevel() + log_level_name = logging.getLevelName(log_level) + assert log_level_name == expected_level + + +def flaky_on_pypy(func): + @functools.wraps(func) + def _func(): + try: + func() + except AssertionError: # pragma: no cover + if IS_PYPY: + msg = "Flaky monkeypatch on PyPy (#4124)" + pytest.xfail(f"{msg}. Original discussion in #3707, #3709.") + raise + + return _func + + +@flaky_on_pypy +def test_patching_does_not_cause_problems(): + # Ensure `dist.log` is only patched if necessary + + import _distutils_hack + + import setuptools.logging + + from distutils import dist + + setuptools.logging.configure() + + if _distutils_hack.enabled(): + # Modern logging infra, no problematic patching. + assert dist.__file__ is None or "setuptools" in dist.__file__ + assert isinstance(dist.log, logging.Logger) + else: + assert inspect.ismodule(dist.log) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_manifest.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_manifest.py new file mode 100644 index 0000000..903a528 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_manifest.py @@ -0,0 +1,622 @@ +"""sdist tests""" + +from __future__ import annotations + +import contextlib +import io +import itertools +import logging +import os +import shutil +import sys +import tempfile + +import pytest + +from setuptools.command.egg_info import FileList, egg_info, translate_pattern +from setuptools.dist import Distribution +from setuptools.tests.textwrap import DALS + +from distutils import log +from distutils.errors import DistutilsTemplateError + +IS_PYPY = '__pypy__' in sys.builtin_module_names + + +def make_local_path(s): + """Converts '/' in a string to os.sep""" + return s.replace('/', os.sep) + + +SETUP_ATTRS = { + 'name': 'app', + 'version': '0.0', + 'packages': ['app'], +} + +SETUP_PY = f"""\ +from setuptools import setup + +setup(**{SETUP_ATTRS!r}) +""" + + +@contextlib.contextmanager +def quiet(): + old_stdout, old_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() + try: + yield + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + + +def touch(filename): + open(filename, 'wb').close() + + +# The set of files always in the manifest, including all files in the +# .egg-info directory +default_files = frozenset( + map( + make_local_path, + [ + 'README.rst', + 'MANIFEST.in', + 'setup.py', + 'app.egg-info/PKG-INFO', + 'app.egg-info/SOURCES.txt', + 'app.egg-info/dependency_links.txt', + 'app.egg-info/top_level.txt', + 'app/__init__.py', + ], + ) +) + + +translate_specs: list[tuple[str, list[str], list[str]]] = [ + ('foo', ['foo'], ['bar', 'foobar']), + ('foo/bar', ['foo/bar'], ['foo/bar/baz', './foo/bar', 'foo']), + # Glob matching + ('*.txt', ['foo.txt', 'bar.txt'], ['foo/foo.txt']), + ('dir/*.txt', ['dir/foo.txt', 'dir/bar.txt', 'dir/.txt'], ['notdir/foo.txt']), + ('*/*.py', ['bin/start.py'], []), + ('docs/page-?.txt', ['docs/page-9.txt'], ['docs/page-10.txt']), + # Globstars change what they mean depending upon where they are + ( + 'foo/**/bar', + ['foo/bing/bar', 'foo/bing/bang/bar', 'foo/bar'], + ['foo/abar'], + ), + ( + 'foo/**', + ['foo/bar/bing.py', 'foo/x'], + ['/foo/x'], + ), + ( + '**', + ['x', 'abc/xyz', '@nything'], + [], + ), + # Character classes + ( + 'pre[one]post', + ['preopost', 'prenpost', 'preepost'], + ['prepost', 'preonepost'], + ), + ( + 'hello[!one]world', + ['helloxworld', 'helloyworld'], + ['hellooworld', 'helloworld', 'hellooneworld'], + ), + ( + '[]one].txt', + ['o.txt', '].txt', 'e.txt'], + ['one].txt'], + ), + ( + 'foo[!]one]bar', + ['fooybar'], + ['foo]bar', 'fooobar', 'fooebar'], + ), +] +""" +A spec of inputs for 'translate_pattern' and matches and mismatches +for that input. +""" + +match_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), matches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=match_params) +def pattern_match(request): + return map(make_local_path, request.param) + + +mismatch_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), mismatches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=mismatch_params) +def pattern_mismatch(request): + return map(make_local_path, request.param) + + +def test_translated_pattern_match(pattern_match): + pattern, target = pattern_match + assert translate_pattern(pattern).match(target) + + +def test_translated_pattern_mismatch(pattern_mismatch): + pattern, target = pattern_mismatch + assert not translate_pattern(pattern).match(target) + + +class TempDirTestCase: + def setup_method(self, method): + self.temp_dir = tempfile.mkdtemp() + self.old_cwd = os.getcwd() + os.chdir(self.temp_dir) + + def teardown_method(self, method): + os.chdir(self.old_cwd) + shutil.rmtree(self.temp_dir) + + +class TestManifestTest(TempDirTestCase): + def setup_method(self, method): + super().setup_method(method) + + f = open(os.path.join(self.temp_dir, 'setup.py'), 'w', encoding="utf-8") + f.write(SETUP_PY) + f.close() + """ + Create a file tree like: + - LICENSE + - README.rst + - testing.rst + - .hidden.rst + - app/ + - __init__.py + - a.txt + - b.txt + - c.rst + - static/ + - app.js + - app.js.map + - app.css + - app.css.map + """ + + for fname in ['README.rst', '.hidden.rst', 'testing.rst', 'LICENSE']: + touch(os.path.join(self.temp_dir, fname)) + + # Set up the rest of the test package + test_pkg = os.path.join(self.temp_dir, 'app') + os.mkdir(test_pkg) + for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']: + touch(os.path.join(test_pkg, fname)) + + # Some compiled front-end assets to include + static = os.path.join(test_pkg, 'static') + os.mkdir(static) + for fname in ['app.js', 'app.js.map', 'app.css', 'app.css.map']: + touch(os.path.join(static, fname)) + + def make_manifest(self, contents): + """Write a MANIFEST.in.""" + manifest = os.path.join(self.temp_dir, 'MANIFEST.in') + with open(manifest, 'w', encoding="utf-8") as f: + f.write(DALS(contents)) + + def get_files(self): + """Run egg_info and get all the files to include, as a set""" + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = egg_info(dist) + cmd.ensure_finalized() + + cmd.run() + + return set(cmd.filelist.files) + + def test_no_manifest(self): + """Check a missing MANIFEST.in includes only the standard files.""" + assert (default_files - set(['MANIFEST.in'])) == self.get_files() + + def test_empty_files(self): + """Check an empty MANIFEST.in includes only the standard files.""" + self.make_manifest("") + assert default_files == self.get_files() + + def test_include(self): + """Include extra rst files in the project root.""" + self.make_manifest("include *.rst") + files = default_files | set(['testing.rst', '.hidden.rst']) + assert files == self.get_files() + + def test_exclude(self): + """Include everything in app/ except the text files""" + ml = make_local_path + self.make_manifest( + """ + include app/* + exclude app/*.txt + """ + ) + files = default_files | set([ml('app/c.rst')]) + assert files == self.get_files() + + def test_include_multiple(self): + """Include with multiple patterns.""" + ml = make_local_path + self.make_manifest("include app/*.txt app/static/*") + files = default_files | set([ + ml('app/a.txt'), + ml('app/b.txt'), + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft(self): + """Include the whole app/static/ directory.""" + ml = make_local_path + self.make_manifest("graft app/static") + files = default_files | set([ + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft_glob_syntax(self): + """Include the whole app/static/ directory.""" + ml = make_local_path + self.make_manifest("graft */static") + files = default_files | set([ + ml('app/static/app.js'), + ml('app/static/app.js.map'), + ml('app/static/app.css'), + ml('app/static/app.css.map'), + ]) + assert files == self.get_files() + + def test_graft_global_exclude(self): + """Exclude all *.map files in the project.""" + ml = make_local_path + self.make_manifest( + """ + graft app/static + global-exclude *.map + """ + ) + files = default_files | set([ml('app/static/app.js'), ml('app/static/app.css')]) + assert files == self.get_files() + + def test_global_include(self): + """Include all *.rst, *.js, and *.css files in the whole tree.""" + ml = make_local_path + self.make_manifest( + """ + global-include *.rst *.js *.css + """ + ) + files = default_files | set([ + '.hidden.rst', + 'testing.rst', + ml('app/c.rst'), + ml('app/static/app.js'), + ml('app/static/app.css'), + ]) + assert files == self.get_files() + + def test_graft_prune(self): + """Include all files in app/, except for the whole app/static/ dir.""" + ml = make_local_path + self.make_manifest( + """ + graft app + prune app/static + """ + ) + files = default_files | set([ml('app/a.txt'), ml('app/b.txt'), ml('app/c.rst')]) + assert files == self.get_files() + + +class TestFileListTest(TempDirTestCase): + """ + A copy of the relevant bits of distutils/tests/test_filelist.py, + to ensure setuptools' version of FileList keeps parity with distutils. + """ + + @pytest.fixture(autouse=os.getenv("SETUPTOOLS_USE_DISTUTILS") == "stdlib") + def _compat_record_logs(self, monkeypatch, caplog): + """Account for stdlib compatibility""" + + def _log(_logger, level, msg, args): + exc = sys.exc_info() + rec = logging.LogRecord("distutils", level, "", 0, msg, args, exc) + caplog.records.append(rec) + + monkeypatch.setattr(log.Log, "_log", _log) + + def get_records(self, caplog, *levels): + return [r for r in caplog.records if r.levelno in levels] + + def assertNoWarnings(self, caplog): + assert self.get_records(caplog, log.WARN) == [] + caplog.clear() + + def assertWarnings(self, caplog): + if IS_PYPY and not caplog.records: + pytest.xfail("caplog checks may not work well in PyPy") + else: + assert len(self.get_records(caplog, log.WARN)) > 0 + caplog.clear() + + def make_files(self, files): + for file in files: + file = os.path.join(self.temp_dir, file) + dirname, _basename = os.path.split(file) + os.makedirs(dirname, exist_ok=True) + touch(file) + + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + ml = make_local_path + + # simulated file list + self.make_files([ + 'foo.tmp', + 'ok', + 'xo', + 'four.txt', + 'buildout.cfg', + # filelist does not filter out VCS directories, + # it's sdist that does + ml('.hg/last-message.txt'), + ml('global/one.txt'), + ml('global/two.txt'), + ml('global/files.x'), + ml('global/here.tmp'), + ml('f/o/f.oo'), + ml('dir/graft-one'), + ml('dir/dir2/graft2'), + ml('dir3/ok'), + ml('dir3/sub/ok.txt'), + ]) + + MANIFEST_IN = DALS( + """\ + include ok + include xo + exclude xo + include foo.tmp + include buildout.cfg + global-include *.x + global-include *.txt + global-exclude *.tmp + recursive-include f *.oo + recursive-exclude global *.x + graft dir + prune dir3 + """ + ) + + for line in MANIFEST_IN.split('\n'): + if not line: + continue + file_list.process_template_line(line) + + wanted = [ + 'buildout.cfg', + 'four.txt', + 'ok', + ml('.hg/last-message.txt'), + ml('dir/graft-one'), + ml('dir/dir2/graft2'), + ml('f/o/f.oo'), + ml('global/one.txt'), + ml('global/two.txt'), + ] + + file_list.sort() + assert file_list.files == wanted + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + assert not file_list.exclude_pattern('*.py') + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + assert file_list.exclude_pattern('*.py') + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + file_list.sort() + assert file_list.files == ['a.txt'] + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + self.make_files([]) + assert not file_list.include_pattern('*.py') + + # return True if files match + file_list = FileList() + self.make_files(['a.py', 'b.txt']) + assert file_list.include_pattern('*.py') + + # test * matches all files + file_list = FileList() + self.make_files(['a.py', 'b.txt']) + file_list.include_pattern('*') + file_list.sort() + assert file_list.files == ['a.py', 'b.txt'] + + def test_process_template_line_invalid(self): + # invalid lines + file_list = FileList() + for action in ( + 'include', + 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', + 'prune', + 'blarg', + ): + with pytest.raises(DistutilsTemplateError): + file_list.process_template_line(action) + + def test_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # include + file_list = FileList() + self.make_files(['a.py', 'b.txt', ml('d/c.py')]) + + file_list.process_template_line('include *.py') + file_list.sort() + assert file_list.files == ['a.py'] + self.assertNoWarnings(caplog) + + file_list.process_template_line('include *.rb') + file_list.sort() + assert file_list.files == ['a.py'] + self.assertWarnings(caplog) + + def test_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', ml('d/c.py')] + + file_list.process_template_line('exclude *.py') + file_list.sort() + assert file_list.files == ['b.txt', ml('d/c.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('exclude *.rb') + file_list.sort() + assert file_list.files == ['b.txt', ml('d/c.py')] + self.assertWarnings(caplog) + + def test_global_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # global-include + file_list = FileList() + self.make_files(['a.py', 'b.txt', ml('d/c.py')]) + + file_list.process_template_line('global-include *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('global-include *.rb') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.py')] + self.assertWarnings(caplog) + + def test_global_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', ml('d/c.py')] + + file_list.process_template_line('global-exclude *.py') + file_list.sort() + assert file_list.files == ['b.txt'] + self.assertNoWarnings(caplog) + + file_list.process_template_line('global-exclude *.rb') + file_list.sort() + assert file_list.files == ['b.txt'] + self.assertWarnings(caplog) + + def test_recursive_include(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # recursive-include + file_list = FileList() + self.make_files(['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')]) + + file_list.process_template_line('recursive-include d *.py') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('recursive-include e *.py') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertWarnings(caplog) + + def test_recursive_exclude(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')] + + file_list.process_template_line('recursive-exclude d *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.txt')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('recursive-exclude e *.py') + file_list.sort() + assert file_list.files == ['a.py', ml('d/c.txt')] + self.assertWarnings(caplog) + + def test_graft(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # graft + file_list = FileList() + self.make_files(['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')]) + + file_list.process_template_line('graft d') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('graft e') + file_list.sort() + assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')] + self.assertWarnings(caplog) + + def test_prune(self, caplog): + caplog.set_level(logging.DEBUG) + ml = make_local_path + # prune + file_list = FileList() + file_list.files = ['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')] + + file_list.process_template_line('prune d') + file_list.sort() + assert file_list.files == ['a.py', ml('f/f.py')] + self.assertNoWarnings(caplog) + + file_list.process_template_line('prune e') + file_list.sort() + assert file_list.files == ['a.py', ml('f/f.py')] + self.assertWarnings(caplog) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py new file mode 100644 index 0000000..ef4a6f6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_namespaces.py @@ -0,0 +1,79 @@ +import subprocess +import sys + +from setuptools._path import paths_on_pythonpath + +from . import namespaces + + +class TestNamespaces: + def test_mixed_site_and_non_site(self, tmpdir): + """ + Installing two packages sharing the same namespace, one installed + to a site dir and the other installed just to a path on PYTHONPATH + should leave the namespace in tact and both packages reachable by + import. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB') + site_packages = tmpdir / 'site-packages' + path_packages = tmpdir / 'path-packages' + targets = site_packages, path_packages + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_A), + '-t', + str(site_packages), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(site_packages) + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_B), + '-t', + str(path_packages), + ] + subprocess.check_call(install_cmd) + try_import = [ + sys.executable, + '-c', + 'import myns.pkgA; import myns.pkgB', + ] + with paths_on_pythonpath(map(str, targets)): + subprocess.check_call(try_import) + + def test_namespace_package_installed_and_cwd(self, tmpdir): + """ + Installing a namespace packages but also having it in the current + working directory, only one version should take precedence. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + sys.executable, + '-m', + 'pip.__main__', + 'install', + str(pkg_A), + '-t', + str(target), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(target) + + # ensure that package imports + pkg_resources_imp = [ + sys.executable, + '-c', + 'import myns.pkgA', + ] + with paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp, cwd=str(pkg_A)) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_scripts.py new file mode 100644 index 0000000..8641f7b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_scripts.py @@ -0,0 +1,12 @@ +from setuptools import _scripts + + +class TestWindowsScriptWriter: + def test_header(self): + hdr = _scripts.WindowsScriptWriter.get_header('') + assert hdr.startswith('#!') + assert hdr.endswith('\n') + hdr = hdr.lstrip('#!') + hdr = hdr.rstrip('\n') + # header should not start with an escaped quote + assert not hdr.startswith('\\"') diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_sdist.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_sdist.py new file mode 100644 index 0000000..5b435fe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_sdist.py @@ -0,0 +1,980 @@ +"""sdist tests""" + +import contextlib +import io +import logging +import os +import pathlib +import sys +import tarfile +import tempfile +import unicodedata +from inspect import cleandoc +from pathlib import Path +from unittest import mock + +import jaraco.path +import pytest + +from setuptools import Command, SetuptoolsDeprecationWarning +from setuptools._importlib import metadata +from setuptools.command.egg_info import manifest_maker +from setuptools.command.sdist import sdist +from setuptools.dist import Distribution +from setuptools.extension import Extension +from setuptools.tests import fail_on_ascii + +from .text import Filenames + +import distutils +from distutils.core import run_setup + +SETUP_ATTRS = { + 'name': 'sdist_test', + 'version': '0.0', + 'packages': ['sdist_test'], + 'package_data': {'sdist_test': ['*.txt']}, + 'data_files': [("data", [os.path.join("d", "e.dat")])], +} + +SETUP_PY = f"""\ +from setuptools import setup + +setup(**{SETUP_ATTRS!r}) +""" + +EXTENSION = Extension( + name="sdist_test.f", + sources=[os.path.join("sdist_test", "f.c")], + depends=[os.path.join("sdist_test", "f.h")], +) +EXTENSION_SOURCES = EXTENSION.sources + EXTENSION.depends + + +@contextlib.contextmanager +def quiet(): + old_stdout, old_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() + try: + yield + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + + +# Convert to POSIX path +def posix(path): + if not isinstance(path, str): + return path.replace(os.sep.encode('ascii'), b'/') + else: + return path.replace(os.sep, '/') + + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, str): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def read_all_bytes(filename): + with open(filename, 'rb') as fp: + return fp.read() + + +def latin1_fail(): + try: + desc, filename = tempfile.mkstemp(suffix=Filenames.latin_1) + os.close(desc) + os.remove(filename) + except Exception: + return True + + +fail_on_latin1_encoded_filenames = pytest.mark.xfail( + latin1_fail(), + reason="System does not support latin-1 filenames", +) + + +skip_under_xdist = pytest.mark.skipif( + "os.environ.get('PYTEST_XDIST_WORKER')", + reason="pytest-dev/pytest-xdist#843", +) +skip_under_stdlib_distutils = pytest.mark.skipif( + not distutils.__package__.startswith('setuptools'), + reason="the test is not supported with stdlib distutils", +) + + +def touch(path): + open(path, 'wb').close() + return path + + +def symlink_or_skip_test(src, dst): + try: + os.symlink(src, dst) + except (OSError, NotImplementedError): + pytest.skip("symlink not supported in OS") + return None + return dst + + +class TestSdistTest: + @pytest.fixture(autouse=True) + def source_dir(self, tmpdir): + tmpdir = tmpdir / "project_root" + tmpdir.mkdir() + + (tmpdir / 'setup.py').write_text(SETUP_PY, encoding='utf-8') + + # Set up the rest of the test package + test_pkg = tmpdir / 'sdist_test' + test_pkg.mkdir() + data_folder = tmpdir / 'd' + data_folder.mkdir() + # *.rst was not included in package_data, so c.rst should not be + # automatically added to the manifest when not under version control + for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']: + touch(test_pkg / fname) + touch(data_folder / 'e.dat') + # C sources are not included by default, but they will be, + # if an extension module uses them as sources or depends + for fname in EXTENSION_SOURCES: + touch(tmpdir / fname) + + with tmpdir.as_cwd(): + yield tmpdir + + def assert_package_data_in_manifest(self, cmd): + manifest = cmd.filelist.files + assert os.path.join('sdist_test', 'a.txt') in manifest + assert os.path.join('sdist_test', 'b.txt') in manifest + assert os.path.join('sdist_test', 'c.rst') not in manifest + assert os.path.join('d', 'e.dat') in manifest + + def setup_with_extension(self): + setup_attrs = {**SETUP_ATTRS, 'ext_modules': [EXTENSION]} + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + return cmd + + def test_package_data_in_sdist(self): + """Regression test for pull request #4: ensures that files listed in + package_data are included in the manifest even if they're not added to + version control. + """ + + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + + def test_package_data_and_include_package_data_in_sdist(self): + """ + Ensure package_data and include_package_data work + together. + """ + setup_attrs = {**SETUP_ATTRS, 'include_package_data': True} + assert setup_attrs['package_data'] + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + + def test_extension_sources_in_sdist(self): + """ + Ensure that the files listed in Extension.sources and Extension.depends + are automatically included in the manifest. + """ + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path in manifest + + def test_missing_extension_sources(self): + """ + Similar to test_extension_sources_in_sdist but the referenced files don't exist. + Missing files should not be included in distribution (with no error raised). + """ + for path in EXTENSION_SOURCES: + os.remove(path) + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path not in manifest + + def test_symlinked_extension_sources(self): + """ + Similar to test_extension_sources_in_sdist but the referenced files are + instead symbolic links to project-local files. Referenced file paths + should be included. Symlink targets themselves should NOT be included. + """ + symlinked = [] + for path in EXTENSION_SOURCES: + base, ext = os.path.splitext(path) + target = base + "_target." + ext + + os.rename(path, target) + symlink_or_skip_test(os.path.basename(target), path) + symlinked.append(target) + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + for path in EXTENSION_SOURCES: + assert path in manifest + for path in symlinked: + assert path not in manifest + + _INVALID_PATHS = { + "must be relative": lambda: os.path.abspath(os.path.join("sdist_test", "f.h")), + "can't have `..` segments": lambda: os.path.join( + "sdist_test", "..", "sdist_test", "f.h" + ), + "doesn't exist": lambda: os.path.join( + "sdist_test", "this_file_does_not_exist.h" + ), + "must be inside the project root": lambda: symlink_or_skip_test( + touch(os.path.join("..", "outside_of_project_root.h")), + "symlink.h", + ), + } + + @skip_under_stdlib_distutils + @pytest.mark.parametrize("reason", _INVALID_PATHS.keys()) + def test_invalid_extension_depends(self, reason, caplog): + """ + Due to backwards compatibility reasons, `Extension.depends` should accept + invalid/weird paths, but then ignore them when building a sdist. + + This test verifies that the source distribution is still built + successfully with such paths, but that instead of adding these paths to + the manifest, we emit an informational message, notifying the user that + the invalid path won't be automatically included. + """ + invalid_path = self._INVALID_PATHS[reason]() + extension = Extension( + name="sdist_test.f", + sources=[], + depends=[invalid_path], + ) + setup_attrs = {**SETUP_ATTRS, 'ext_modules': [extension]} + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(), caplog.at_level(logging.INFO): + cmd.run() + + self.assert_package_data_in_manifest(cmd) + manifest = cmd.filelist.files + assert invalid_path not in manifest + + expected_message = [ + message + for (logger, level, message) in caplog.record_tuples + if ( + logger == "root" # + and level == logging.INFO # + and invalid_path in message # + ) + ] + assert len(expected_message) == 1 + (expected_message,) = expected_message + assert reason in expected_message + + def test_custom_build_py(self): + """ + Ensure projects defining custom build_py don't break + when creating sdists (issue #2849) + """ + from distutils.command.build_py import build_py as OrigBuildPy + + using_custom_command_guard = mock.Mock() + + class CustomBuildPy(OrigBuildPy): + """ + Some projects have custom commands inheriting from `distutils` + """ + + def get_data_files(self): + using_custom_command_guard() + return super().get_data_files() + + setup_attrs = {**SETUP_ATTRS, 'include_package_data': True} + assert setup_attrs['package_data'] + + dist = Distribution(setup_attrs) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Make sure we use the custom command + cmd.cmdclass = {'build_py': CustomBuildPy} + cmd.distribution.cmdclass = {'build_py': CustomBuildPy} + assert cmd.distribution.get_command_class('build_py') == CustomBuildPy + + msg = "setuptools instead of distutils" + with quiet(), pytest.warns(SetuptoolsDeprecationWarning, match=msg): + cmd.run() + + using_custom_command_guard.assert_called() + self.assert_package_data_in_manifest(cmd) + + def test_setup_py_exists(self): + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' in manifest + + def test_setup_py_missing(self): + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + if os.path.exists("setup.py"): + os.remove("setup.py") + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' not in manifest + + def test_setup_py_excluded(self): + with open("MANIFEST.in", "w", encoding="utf-8") as manifest_file: + manifest_file.write("exclude setup.py") + + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'foo.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + manifest = cmd.filelist.files + assert 'setup.py' not in manifest + + def test_defaults_case_sensitivity(self, source_dir): + """ + Make sure default files (README.*, etc.) are added in a case-sensitive + way to avoid problems with packages built on Windows. + """ + + touch(source_dir / 'readme.rst') + touch(source_dir / 'SETUP.cfg') + + dist = Distribution(SETUP_ATTRS) + # the extension deliberately capitalized for this test + # to make sure the actual filename (not capitalized) gets added + # to the manifest + dist.script_name = 'setup.PY' + cmd = sdist(dist) + cmd.ensure_finalized() + + with quiet(): + cmd.run() + + # lowercase all names so we can test in a + # case-insensitive way to make sure the files + # are not included. + manifest = map(lambda x: x.lower(), cmd.filelist.files) + assert 'readme.rst' not in manifest, manifest + assert 'setup.py' not in manifest, manifest + assert 'setup.cfg' not in manifest, manifest + + def test_exclude_dev_only_cache_folders(self, source_dir): + included = { + # Emulate problem in https://github.com/pypa/setuptools/issues/4601 + "MANIFEST.in": ( + "global-include LICEN[CS]E* COPYING* NOTICE* AUTHORS*\n" + "global-include *.txt\n" + ), + # For the sake of being conservative and limiting unforeseen side-effects + # we just exclude dev-only cache folders at the root of the repository: + "test/.venv/lib/python3.9/site-packages/bar-2.dist-info/AUTHORS.rst": "", + "src/.nox/py/lib/python3.12/site-packages/bar-2.dist-info/COPYING.txt": "", + "doc/.tox/default/lib/python3.11/site-packages/foo-4.dist-info/LICENSE": "", + # Let's test against false positives with similarly named files: + ".venv-requirements.txt": "", + ".tox-coveragerc.txt": "", + ".noxy/coveragerc.txt": "", + } + + excluded = { + # .tox/.nox/.venv are well-know folders present at the root of Python repos + # and therefore should be excluded + ".tox/release/lib/python3.11/site-packages/foo-4.dist-info/LICENSE": "", + ".nox/py/lib/python3.12/site-packages/bar-2.dist-info/COPYING.txt": "", + ".venv/lib/python3.9/site-packages/bar-2.dist-info/AUTHORS.rst": "", + } + + for file, content in {**excluded, **included}.items(): + Path(source_dir, file).parent.mkdir(parents=True, exist_ok=True) + Path(source_dir, file).write_text(content, encoding="utf-8") + + cmd = self.setup_with_extension() + self.assert_package_data_in_manifest(cmd) + manifest = {f.replace(os.sep, '/') for f in cmd.filelist.files} + for path in excluded: + assert os.path.exists(path) + assert path not in manifest, (path, manifest) + for path in included: + assert os.path.exists(path) + assert path in manifest, (path, manifest) + + @fail_on_ascii + def test_manifest_is_written_with_utf8_encoding(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + # UTF-8 filename + filename = os.path.join('sdist_test', 'smörbröd.py') + + # Must create the file or it will get stripped. + touch(filename) + + # Add UTF-8 filename and write manifest + with quiet(): + mm.run() + mm.filelist.append(filename) + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + u_contents = contents.decode('UTF-8') + + # The manifest should contain the UTF-8 filename + assert posix(filename) in u_contents + + @fail_on_ascii + def test_write_manifest_allows_utf8_filenames(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + filename = os.path.join(b'sdist_test', Filenames.utf_8) + + # Must touch the file or risk removal + touch(filename) + + # Add filename and write manifest + with quiet(): + mm.run() + u_filename = filename.decode('utf-8') + mm.filelist.files.append(u_filename) + # Re-write manifest + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + contents.decode('UTF-8') + + # The manifest should contain the UTF-8 filename + assert posix(filename) in contents + + # The filelist should have been updated as well + assert u_filename in mm.filelist.files + + @skip_under_xdist + def test_write_manifest_skips_non_utf8_filenames(self): + """ + Files that cannot be encoded to UTF-8 (specifically, those that + weren't originally successfully decoded and have surrogate + escapes) should be omitted from the manifest. + See https://bitbucket.org/tarek/distribute/issue/303 for history. + """ + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + mm = manifest_maker(dist) + mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + os.mkdir('sdist_test.egg-info') + + # Latin-1 filename + filename = os.path.join(b'sdist_test', Filenames.latin_1) + + # Add filename with surrogates and write manifest + with quiet(): + mm.run() + u_filename = filename.decode('utf-8', 'surrogateescape') + mm.filelist.append(u_filename) + # Re-write manifest + mm.write_manifest() + + contents = read_all_bytes(mm.manifest) + + # The manifest should be UTF-8 encoded + contents.decode('UTF-8') + + # The Latin-1 filename should have been skipped + assert posix(filename) not in contents + + # The filelist should have been updated as well + assert u_filename not in mm.filelist.files + + @fail_on_ascii + def test_manifest_is_read_with_utf8_encoding(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Create manifest + with quiet(): + cmd.run() + + # Add UTF-8 filename to manifest + filename = os.path.join(b'sdist_test', Filenames.utf_8) + cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + manifest = open(cmd.manifest, 'ab') + manifest.write(b'\n' + filename) + manifest.close() + + # The file must exist to be included in the filelist + touch(filename) + + # Re-read manifest + cmd.filelist.files = [] + with quiet(): + cmd.read_manifest() + + # The filelist should contain the UTF-8 filename + filename = filename.decode('utf-8') + assert filename in cmd.filelist.files + + @fail_on_latin1_encoded_filenames + def test_read_manifest_skips_non_utf8_filenames(self): + # Test for #303. + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Create manifest + with quiet(): + cmd.run() + + # Add Latin-1 filename to manifest + filename = os.path.join(b'sdist_test', Filenames.latin_1) + cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') + manifest = open(cmd.manifest, 'ab') + manifest.write(b'\n' + filename) + manifest.close() + + # The file must exist to be included in the filelist + touch(filename) + + # Re-read manifest + cmd.filelist.files = [] + with quiet(): + cmd.read_manifest() + + # The Latin-1 filename should have been skipped + filename = filename.decode('latin-1') + assert filename not in cmd.filelist.files + + @fail_on_ascii + @fail_on_latin1_encoded_filenames + def test_sdist_with_utf8_encoded_filename(self): + # Test for #303. + dist = Distribution(self.make_strings(SETUP_ATTRS)) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + filename = os.path.join(b'sdist_test', Filenames.utf_8) + touch(filename) + + with quiet(): + cmd.run() + + if sys.platform == 'darwin': + filename = decompose(filename) + + fs_enc = sys.getfilesystemencoding() + + if sys.platform == 'win32': + if fs_enc == 'cp1252': + # Python mangles the UTF-8 filename + filename = filename.decode('cp1252') + assert filename in cmd.filelist.files + else: + filename = filename.decode('mbcs') + assert filename in cmd.filelist.files + else: + filename = filename.decode('utf-8') + assert filename in cmd.filelist.files + + @classmethod + def make_strings(cls, item): + if isinstance(item, dict): + return {key: cls.make_strings(value) for key, value in item.items()} + if isinstance(item, list): + return list(map(cls.make_strings, item)) + return str(item) + + @fail_on_latin1_encoded_filenames + @skip_under_xdist + def test_sdist_with_latin1_encoded_filename(self): + # Test for #303. + dist = Distribution(self.make_strings(SETUP_ATTRS)) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + + # Latin-1 filename + filename = os.path.join(b'sdist_test', Filenames.latin_1) + touch(filename) + assert os.path.isfile(filename) + + with quiet(): + cmd.run() + + # not all windows systems have a default FS encoding of cp1252 + if sys.platform == 'win32': + # Latin-1 is similar to Windows-1252 however + # on mbcs filesys it is not in latin-1 encoding + fs_enc = sys.getfilesystemencoding() + if fs_enc != 'mbcs': + fs_enc = 'latin-1' + filename = filename.decode(fs_enc) + + assert filename in cmd.filelist.files + else: + # The Latin-1 filename should have been skipped + filename = filename.decode('latin-1') + assert filename not in cmd.filelist.files + + _EXAMPLE_DIRECTIVES = { + "setup.cfg - long_description and version": """ + [metadata] + name = testing + version = file: src/VERSION.txt + license_files = DOWHATYOUWANT + long_description = file: README.rst, USAGE.rst + """, + "pyproject.toml - static readme/license files and dynamic version": """ + [project] + name = "testing" + readme = "USAGE.rst" + license-files = ["DOWHATYOUWANT"] + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = ["src/VERSION.txt"]} + """, + "pyproject.toml - directive with str instead of list": """ + [project] + name = "testing" + readme = "USAGE.rst" + license-files = ["DOWHATYOUWANT"] + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = "src/VERSION.txt"} + """, + "pyproject.toml - deprecated license table with file entry": """ + [project] + name = "testing" + readme = "USAGE.rst" + license = {file = "DOWHATYOUWANT"} + dynamic = ["version"] + [tool.setuptools.dynamic] + version = {file = "src/VERSION.txt"} + """, + } + + @pytest.mark.parametrize("config", _EXAMPLE_DIRECTIVES.keys()) + @pytest.mark.filterwarnings( + "ignore:.project.license. as a TOML table is deprecated" + ) + def test_add_files_referenced_by_config_directives(self, source_dir, config): + config_file, _, _ = config.partition(" - ") + config_text = self._EXAMPLE_DIRECTIVES[config] + (source_dir / 'src').mkdir() + (source_dir / 'src/VERSION.txt').write_text("0.42", encoding="utf-8") + (source_dir / 'README.rst').write_text("hello world!", encoding="utf-8") + (source_dir / 'USAGE.rst').write_text("hello world!", encoding="utf-8") + (source_dir / 'DOWHATYOUWANT').write_text("hello world!", encoding="utf-8") + (source_dir / config_file).write_text(config_text, encoding="utf-8") + + dist = Distribution({"packages": []}) + dist.script_name = 'setup.py' + dist.parse_config_files() + + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + + assert ( + 'src/VERSION.txt' in cmd.filelist.files + or 'src\\VERSION.txt' in cmd.filelist.files + ) + assert 'USAGE.rst' in cmd.filelist.files + assert 'DOWHATYOUWANT' in cmd.filelist.files + assert '/' not in cmd.filelist.files + assert '\\' not in cmd.filelist.files + + def test_pyproject_toml_in_sdist(self, source_dir): + """ + Check if pyproject.toml is included in source distribution if present + """ + touch(source_dir / 'pyproject.toml') + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert 'pyproject.toml' in manifest + + def test_pyproject_toml_excluded(self, source_dir): + """ + Check that pyproject.toml can excluded even if present + """ + touch(source_dir / 'pyproject.toml') + with open('MANIFEST.in', 'w', encoding="utf-8") as mts: + print('exclude pyproject.toml', file=mts) + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert 'pyproject.toml' not in manifest + + def test_build_subcommand_source_files(self, source_dir): + touch(source_dir / '.myfile~') + + # Sanity check: without custom commands file list should not be affected + dist = Distribution({**SETUP_ATTRS, "script_name": "setup.py"}) + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert '.myfile~' not in manifest + + # Test: custom command should be able to augment file list + dist = Distribution({**SETUP_ATTRS, "script_name": "setup.py"}) + build = dist.get_command_obj("build") + build.sub_commands = [*build.sub_commands, ("build_custom", None)] + + class build_custom(Command): + def initialize_options(self): ... + + def finalize_options(self): ... + + def run(self): ... + + def get_source_files(self): + return ['.myfile~'] + + dist.cmdclass.update(build_custom=build_custom) + + cmd = sdist(dist) + cmd.use_defaults = True + cmd.ensure_finalized() + with quiet(): + cmd.run() + manifest = cmd.filelist.files + assert '.myfile~' in manifest + + @pytest.mark.skipif("os.environ.get('SETUPTOOLS_USE_DISTUTILS') == 'stdlib'") + def test_build_base_pathlib(self, source_dir): + """ + Ensure if build_base is a pathlib.Path, the build still succeeds. + """ + dist = Distribution({ + **SETUP_ATTRS, + "script_name": "setup.py", + "options": {"build": {"build_base": pathlib.Path('build')}}, + }) + cmd = sdist(dist) + cmd.ensure_finalized() + with quiet(): + cmd.run() + + +def test_default_revctrl(): + """ + When _default_revctrl was removed from the `setuptools.command.sdist` + module in 10.0, it broke some systems which keep an old install of + setuptools (Distribute) around. Those old versions require that the + setuptools package continue to implement that interface, so this + function provides that interface, stubbed. See #320 for details. + + This interface must be maintained until Ubuntu 12.04 is no longer + supported (by Setuptools). + """ + (ep,) = metadata.EntryPoints._from_text( + """ + [setuptools.file_finders] + svn_cvs = setuptools.command.sdist:_default_revctrl + """ + ) + res = ep.load() + assert hasattr(res, '__iter__') + + +class TestRegressions: + """ + Can be removed/changed if the project decides to change how it handles symlinks + or external files. + """ + + @staticmethod + def files_for_symlink_in_extension_depends(tmp_path, dep_path): + return { + "external": { + "dir": {"file.h": ""}, + }, + "project": { + "setup.py": cleandoc( + f""" + from setuptools import Extension, setup + setup( + name="myproj", + version="42", + ext_modules=[ + Extension( + "hello", sources=["hello.pyx"], + depends=[{dep_path!r}] + ) + ], + ) + """ + ), + "hello.pyx": "", + "MANIFEST.in": "global-include *.h", + }, + } + + @pytest.mark.parametrize( + "dep_path", ("myheaders/dir/file.h", "myheaders/dir/../dir/file.h") + ) + def test_symlink_in_extension_depends(self, monkeypatch, tmp_path, dep_path): + # Given a project with a symlinked dir and a "depends" targeting that dir + files = self.files_for_symlink_in_extension_depends(tmp_path, dep_path) + jaraco.path.build(files, prefix=str(tmp_path)) + symlink_or_skip_test(tmp_path / "external", tmp_path / "project/myheaders") + + # When `sdist` runs, there should be no error + members = run_sdist(monkeypatch, tmp_path / "project") + # and the sdist should contain the symlinked files + for expected in ( + "myproj-42/hello.pyx", + "myproj-42/myheaders/dir/file.h", + ): + assert expected in members + + @staticmethod + def files_for_external_path_in_extension_depends(tmp_path, dep_path): + head, _, tail = dep_path.partition("$tmp_path$/") + dep_path = tmp_path / tail if tail else head + + return { + "external": { + "dir": {"file.h": ""}, + }, + "project": { + "setup.py": cleandoc( + f""" + from setuptools import Extension, setup + setup( + name="myproj", + version="42", + ext_modules=[ + Extension( + "hello", sources=["hello.pyx"], + depends=[{str(dep_path)!r}] + ) + ], + ) + """ + ), + "hello.pyx": "", + "MANIFEST.in": "global-include *.h", + }, + } + + @pytest.mark.parametrize( + "dep_path", ("$tmp_path$/external/dir/file.h", "../external/dir/file.h") + ) + def test_external_path_in_extension_depends(self, monkeypatch, tmp_path, dep_path): + # Given a project with a "depends" targeting an external dir + files = self.files_for_external_path_in_extension_depends(tmp_path, dep_path) + jaraco.path.build(files, prefix=str(tmp_path)) + # When `sdist` runs, there should be no error + members = run_sdist(monkeypatch, tmp_path / "project") + # and the sdist should not contain the external file + for name in members: + assert "file.h" not in name + + +def run_sdist(monkeypatch, project): + """Given a project directory, run the sdist and return its contents""" + monkeypatch.chdir(project) + with quiet(): + run_setup("setup.py", ["sdist"]) + + archive = next((project / "dist").glob("*.tar.gz")) + with tarfile.open(str(archive)) as tar: + return set(tar.getnames()) + + +def test_sanity_check_setuptools_own_sdist(setuptools_sdist): + with tarfile.open(setuptools_sdist) as tar: + files = tar.getnames() + + # setuptools sdist should not include the .tox folder + tox_files = [name for name in files if ".tox" in name] + assert len(tox_files) == 0, f"not empty {tox_files}" diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_setopt.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_setopt.py new file mode 100644 index 0000000..ccf2561 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_setopt.py @@ -0,0 +1,40 @@ +import configparser + +from setuptools.command import setopt + + +class TestEdit: + @staticmethod + def parse_config(filename): + parser = configparser.ConfigParser() + with open(filename, encoding='utf-8') as reader: + parser.read_file(reader) + return parser + + @staticmethod + def write_text(file, content): + with open(file, 'wb') as strm: + strm.write(content.encode('utf-8')) + + def test_utf8_encoding_retained(self, tmpdir): + """ + When editing a file, non-ASCII characters encoded in + UTF-8 should be retained. + """ + config = tmpdir.join('setup.cfg') + self.write_text(str(config), '[names]\njaraco=джарако') + setopt.edit_config(str(config), dict(names=dict(other='yes'))) + parser = self.parse_config(str(config)) + assert parser.get('names', 'jaraco') == 'джарако' + assert parser.get('names', 'other') == 'yes' + + def test_case_retained(self, tmpdir): + """ + When editing a file, case of keys should be retained. + """ + config = tmpdir.join('setup.cfg') + self.write_text(str(config), '[names]\nFoO=bAr') + setopt.edit_config(str(config), dict(names=dict(oTher='yes'))) + actual = config.read_text(encoding='ascii') + assert 'FoO' in actual + assert 'oTher' in actual diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py new file mode 100644 index 0000000..c57908b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_setuptools.py @@ -0,0 +1,294 @@ +"""Tests for the 'setuptools' package""" + +import os +import re +import sys +from zipfile import ZipFile + +import pytest +from packaging.version import Version + +import setuptools +import setuptools.depends as dep +import setuptools.dist +from setuptools.depends import Require + +import distutils.cmd +import distutils.core +from distutils.core import Extension +from distutils.errors import DistutilsSetupError + + +@pytest.fixture(autouse=True) +def isolated_dir(tmpdir_cwd): + return + + +def makeSetup(**args): + """Return distribution from 'setup(**args)', without executing commands""" + + distutils.core._setup_stop_after = "commandline" + + # Don't let system command line leak into tests! + args.setdefault('script_args', ['install']) + + try: + return setuptools.setup(**args) + finally: + distutils.core._setup_stop_after = None + + +needs_bytecode = pytest.mark.skipif( + not hasattr(dep, 'get_module_constant'), + reason="bytecode support not available", +) + + +class TestDepends: + def testExtractConst(self): + if not hasattr(dep, 'extract_constant'): + # skip on non-bytecode platforms + return + + def f1(): + global x, y, z + x = "test" + y = z # pyright: ignore[reportUnboundVariable] # Explicitly testing for this runtime issue + + fc = f1.__code__ + + # unrecognized name + assert dep.extract_constant(fc, 'q', -1) is None + + # constant assigned + assert dep.extract_constant(fc, 'x', -1) == "test" + + # expression assigned + assert dep.extract_constant(fc, 'y', -1) == -1 + + # recognized name, not assigned + assert dep.extract_constant(fc, 'z', -1) is None + + def testFindModule(self): + with pytest.raises(ImportError): + dep.find_module('no-such.-thing') + with pytest.raises(ImportError): + dep.find_module('setuptools.non-existent') + f, _p, _i = dep.find_module('setuptools.tests') + f.close() + + @pytest.fixture + def sample_module(self, monkeypatch, tmp_path): + monkeypatch.syspath_prepend(str(tmp_path)) + module = "mod_with_version" + version = "2.0.9" + file = tmp_path / f"{module}.py" + file.write_text(f"__version__ = {version!r}", encoding="utf-8") + return (module, version) + + @needs_bytecode + def testModuleExtract(self, sample_module): + (module, version) = sample_module + assert dep.get_module_constant(module, '__version__') == version + assert dep.get_module_constant('sys', 'version') == sys.version + assert dep.get_module_constant(__name__, '__doc__') == __doc__ + + @needs_bytecode + def testRequire(self, sample_module): + (module, version) = sample_module + req = Require('GivenName', '1.0.3', module) + + assert req.name == 'GivenName' + assert req.module == module + assert req.requested_version == Version('1.0.3') + assert req.attribute == '__version__' + assert req.full_name() == 'GivenName-1.0.3' + + assert str(req.get_version()) == version + assert req.version_ok('1.0.9') + assert not req.version_ok('0.9.1') + assert not req.version_ok('unknown') + + assert req.is_present() + assert req.is_current() + + req = Require('Do-what-I-mean', '1.0', 'd-w-i-m') + assert not req.is_present() + assert not req.is_current() + + @needs_bytecode + def test_require_present(self): + # In #1896, this test was failing for months with the only + # complaint coming from test runners (not end users). + # TODO: Evaluate if this code is needed at all. + req = Require('Tests', None, 'tests', homepage="http://example.com") + assert req.format is None + assert req.attribute is None + assert req.requested_version is None + assert req.full_name() == 'Tests' + assert req.homepage == 'http://example.com' + + from setuptools.tests import __path__ + + paths = [os.path.dirname(p) for p in __path__] + assert req.is_present(paths) + assert req.is_current(paths) + + +class TestDistro: + def setup_method(self, method): + self.e1 = Extension('bar.ext', ['bar.c']) + self.e2 = Extension('c.y', ['y.c']) + + self.dist = makeSetup( + packages=['a', 'a.b', 'a.b.c', 'b', 'c'], + py_modules=['b.d', 'x'], + ext_modules=(self.e1, self.e2), + package_dir={}, + ) + + def testDistroType(self): + assert isinstance(self.dist, setuptools.dist.Distribution) + + def testExcludePackage(self): + self.dist.exclude_package('a') + assert self.dist.packages == ['b', 'c'] + + self.dist.exclude_package('b') + assert self.dist.packages == ['c'] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1, self.e2] + + self.dist.exclude_package('c') + assert self.dist.packages == [] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1] + + # test removals from unspecified options + makeSetup().exclude_package('x') + + def testIncludeExclude(self): + # remove an extension + self.dist.exclude(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2] + + # add it back in + self.dist.include(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2, self.e1] + + # should not add duplicate + self.dist.include(ext_modules=[self.e1]) + assert self.dist.ext_modules == [self.e2, self.e1] + + def testExcludePackages(self): + self.dist.exclude(packages=['c', 'b', 'a']) + assert self.dist.packages == [] + assert self.dist.py_modules == ['x'] + assert self.dist.ext_modules == [self.e1] + + def testEmpty(self): + dist = makeSetup() + dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + dist = makeSetup() + dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2]) + + def testContents(self): + assert self.dist.has_contents_for('a') + self.dist.exclude_package('a') + assert not self.dist.has_contents_for('a') + + assert self.dist.has_contents_for('b') + self.dist.exclude_package('b') + assert not self.dist.has_contents_for('b') + + assert self.dist.has_contents_for('c') + self.dist.exclude_package('c') + assert not self.dist.has_contents_for('c') + + def testInvalidIncludeExclude(self): + with pytest.raises(DistutilsSetupError): + self.dist.include(nonexistent_option='x') + with pytest.raises(DistutilsSetupError): + self.dist.exclude(nonexistent_option='x') + with pytest.raises(DistutilsSetupError): + self.dist.include(packages={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(packages={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.include(ext_modules={'x': 'y'}) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(ext_modules={'x': 'y'}) + + with pytest.raises(DistutilsSetupError): + self.dist.include(package_dir=['q']) + with pytest.raises(DistutilsSetupError): + self.dist.exclude(package_dir=['q']) + + +@pytest.fixture +def example_source(tmpdir): + tmpdir.mkdir('foo') + (tmpdir / 'foo/bar.py').write('') + (tmpdir / 'readme.txt').write('') + return tmpdir + + +def test_findall(example_source): + found = list(setuptools.findall(str(example_source))) + expected = ['readme.txt', 'foo/bar.py'] + expected = [example_source.join(fn) for fn in expected] + assert found == expected + + +def test_findall_curdir(example_source): + with example_source.as_cwd(): + found = list(setuptools.findall()) + expected = ['readme.txt', os.path.join('foo', 'bar.py')] + assert found == expected + + +@pytest.fixture +def can_symlink(tmpdir): + """ + Skip if cannot create a symbolic link + """ + link_fn = 'link' + target_fn = 'target' + try: + os.symlink(target_fn, link_fn) + except (OSError, NotImplementedError, AttributeError): + pytest.skip("Cannot create symbolic links") + os.remove(link_fn) + + +@pytest.mark.usefixtures("can_symlink") +def test_findall_missing_symlink(tmpdir): + with tmpdir.as_cwd(): + os.symlink('foo', 'bar') + found = list(setuptools.findall()) + assert found == [] + + +@pytest.mark.xfail(reason="unable to exclude tests; #4475 #3260") +def test_its_own_wheel_does_not_contain_tests(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + for member in contents: + assert '/tests/' not in member + + +def test_wheel_includes_cli_scripts(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + assert any('cli-64.exe' in member for member in contents) + + +def test_wheel_includes_vendored_metadata(setuptools_wheel): + with ZipFile(setuptools_wheel) as zipfile: + contents = [f.replace(os.sep, '/') for f in zipfile.namelist()] + + assert any( + re.search(r'_vendor/.*\.dist-info/METADATA', member) for member in contents + ) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py new file mode 100644 index 0000000..74ff7e9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_shutil_wrapper.py @@ -0,0 +1,23 @@ +import stat +import sys +from unittest.mock import Mock + +from setuptools import _shutil + + +def test_rmtree_readonly(monkeypatch, tmp_path): + """Verify onerr works as expected""" + + tmp_dir = tmp_path / "with_readonly" + tmp_dir.mkdir() + some_file = tmp_dir.joinpath("file.txt") + some_file.touch() + some_file.chmod(stat.S_IREAD) + + expected_count = 1 if sys.platform.startswith("win") else 0 + chmod_fn = Mock(wraps=_shutil.attempt_chmod_verbose) + monkeypatch.setattr(_shutil, "attempt_chmod_verbose", chmod_fn) + + _shutil.rmtree(tmp_dir) + assert chmod_fn.call_count == expected_count + assert not tmp_dir.is_dir() diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py new file mode 100644 index 0000000..a24a9bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_unicode_utils.py @@ -0,0 +1,10 @@ +from setuptools import unicode_utils + + +def test_filesys_decode_fs_encoding_is_None(monkeypatch): + """ + Test filesys_decode does not raise TypeError when + getfilesystemencoding returns None. + """ + monkeypatch.setattr('sys.getfilesystemencoding', lambda: None) + unicode_utils.filesys_decode(b'test') diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py new file mode 100644 index 0000000..b02949b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_virtualenv.py @@ -0,0 +1,113 @@ +import os +import subprocess +import sys +from urllib.error import URLError +from urllib.request import urlopen + +import pytest + + +@pytest.fixture(autouse=True) +def pytest_virtualenv_works(venv): + """ + pytest_virtualenv may not work. if it doesn't, skip these + tests. See #1284. + """ + venv_prefix = venv.run(["python", "-c", "import sys; print(sys.prefix)"]).strip() + if venv_prefix == sys.prefix: + pytest.skip("virtualenv is broken (see pypa/setuptools#1284)") + + +def test_clean_env_install(venv_without_setuptools, setuptools_wheel): + """ + Check setuptools can be installed in a clean environment. + """ + cmd = ["python", "-m", "pip", "install", str(setuptools_wheel)] + venv_without_setuptools.run(cmd) + + +def access_pypi(): + # Detect if tests are being run without connectivity + if not os.environ.get('NETWORK_REQUIRED', False): # pragma: nocover + try: + urlopen('https://pypi.org', timeout=1) + except URLError: + # No network, disable most of these tests + return False + + return True + + +@pytest.mark.skipif( + 'platform.python_implementation() == "PyPy"', + reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995", +) +@pytest.mark.skipif(not access_pypi(), reason="no network") +# ^-- Even when it is not necessary to install a different version of `pip` +# the build process will still try to download `wheel`, see #3147 and #2986. +@pytest.mark.parametrize( + 'pip_version', + [ + None, + pytest.param( + 'pip<20.1', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<21', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<22', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'pip<23', + marks=pytest.mark.xfail( + 'sys.version_info >= (3, 12)', + reason="pip 23.1.2 required for Python 3.12 and later", + ), + ), + pytest.param( + 'https://github.com/pypa/pip/archive/main.zip', + marks=pytest.mark.xfail(reason='#2975'), + ), + ], +) +def test_pip_upgrade_from_source( + pip_version, venv_without_setuptools, setuptools_wheel, setuptools_sdist +): + """ + Check pip can upgrade setuptools from source. + """ + # Install pip/wheel, in a venv without setuptools (as it + # should not be needed for bootstrapping from source) + venv = venv_without_setuptools + venv.run(["pip", "install", "-U", "wheel"]) + if pip_version is not None: + venv.run(["python", "-m", "pip", "install", "-U", pip_version, "--retries=1"]) + with pytest.raises(subprocess.CalledProcessError): + # Meta-test to make sure setuptools is not installed + venv.run(["python", "-c", "import setuptools"]) + + # Then install from wheel. + venv.run(["pip", "install", str(setuptools_wheel)]) + # And finally try to upgrade from source. + venv.run(["pip", "install", "--no-cache-dir", "--upgrade", str(setuptools_sdist)]) + + +def test_no_missing_dependencies(bare_venv, request): + """ + Quick and dirty test to ensure all external dependencies are vendored. + """ + setuptools_dir = request.config.rootdir + bare_venv.run(['python', 'setup.py', '--help'], cwd=setuptools_dir) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_warnings.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_warnings.py new file mode 100644 index 0000000..41193d4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_warnings.py @@ -0,0 +1,106 @@ +from inspect import cleandoc + +import pytest + +from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning + +_EXAMPLES = { + "default": dict( + args=("Hello {x}", "\n\t{target} {v:.1f}"), + kwargs={"x": 5, "v": 3, "target": "World"}, + expected=""" + Hello 5 + !! + + ******************************************************************************** + World 3.0 + ******************************************************************************** + + !! + """, + ), + "futue_due_date": dict( + args=("Summary", "Lorem ipsum"), + kwargs={"due_date": (9999, 11, 22)}, + expected=""" + Summary + !! + + ******************************************************************************** + Lorem ipsum + + By 9999-Nov-22, you need to update your project and remove deprecated calls + or your builds will no longer be supported. + ******************************************************************************** + + !! + """, + ), + "past_due_date_with_docs": dict( + args=("Summary", "Lorem ipsum"), + kwargs={"due_date": (2000, 11, 22), "see_docs": "some_page.html"}, + expected=""" + Summary + !! + + ******************************************************************************** + Lorem ipsum + + This deprecation is overdue, please update your project and remove deprecated + calls to avoid build errors in the future. + + See https://setuptools.pypa.io/en/latest/some_page.html for details. + ******************************************************************************** + + !! + """, + ), +} + + +@pytest.mark.parametrize("example_name", _EXAMPLES.keys()) +def test_formatting(monkeypatch, example_name): + """ + It should automatically handle indentation, interpolation and things like due date. + """ + args = _EXAMPLES[example_name]["args"] + kwargs = _EXAMPLES[example_name]["kwargs"] + expected = _EXAMPLES[example_name]["expected"] + + monkeypatch.setenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false") + with pytest.warns(SetuptoolsWarning) as warn_info: + SetuptoolsWarning.emit(*args, **kwargs) + assert _get_message(warn_info) == cleandoc(expected) + + +def test_due_date_enforcement(monkeypatch): + class _MyDeprecation(SetuptoolsDeprecationWarning): + _SUMMARY = "Summary" + _DETAILS = "Lorem ipsum" + _DUE_DATE = (2000, 11, 22) + _SEE_DOCS = "some_page.html" + + monkeypatch.setenv("SETUPTOOLS_ENFORCE_DEPRECATION", "true") + with pytest.raises(SetuptoolsDeprecationWarning) as exc_info: + _MyDeprecation.emit() + + expected = """ + Summary + !! + + ******************************************************************************** + Lorem ipsum + + This deprecation is overdue, please update your project and remove deprecated + calls to avoid build errors in the future. + + See https://setuptools.pypa.io/en/latest/some_page.html for details. + ******************************************************************************** + + !! + """ + assert str(exc_info.value) == cleandoc(expected) + + +def _get_message(warn_info): + return next(warn.message.args[0] for warn in warn_info) diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_wheel.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_wheel.py new file mode 100644 index 0000000..c3b215a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_wheel.py @@ -0,0 +1,690 @@ +"""wheel tests""" + +from __future__ import annotations + +import contextlib +import glob +import inspect +import os +import pathlib +import stat +import subprocess +import sys +import sysconfig +import zipfile +from typing import Any + +import pytest +from jaraco import path +from packaging.tags import parse_tag + +from setuptools._importlib import metadata +from setuptools.wheel import Wheel + +from .contexts import tempdir +from .textwrap import DALS + +from distutils.sysconfig import get_config_var +from distutils.util import get_platform + +WHEEL_INFO_TESTS = ( + ('invalid.whl', ValueError), + ( + 'simplewheel-2.0-1-py2.py3-none-any.whl', + { + 'project_name': 'simplewheel', + 'version': '2.0', + 'build': '1', + 'py_version': 'py2.py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'simple.dist-0.1-py2.py3-none-any.whl', + { + 'project_name': 'simple.dist', + 'version': '0.1', + 'build': None, + 'py_version': 'py2.py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'example_pkg_a-1-py3-none-any.whl', + { + 'project_name': 'example_pkg_a', + 'version': '1', + 'build': None, + 'py_version': 'py3', + 'abi': 'none', + 'platform': 'any', + }, + ), + ( + 'PyQt5-5.9-5.9.1-cp35.cp36.cp37-abi3-manylinux1_x86_64.whl', + { + 'project_name': 'PyQt5', + 'version': '5.9', + 'build': '5.9.1', + 'py_version': 'cp35.cp36.cp37', + 'abi': 'abi3', + 'platform': 'manylinux1_x86_64', + }, + ), +) + + +@pytest.mark.parametrize( + ('filename', 'info'), WHEEL_INFO_TESTS, ids=[t[0] for t in WHEEL_INFO_TESTS] +) +def test_wheel_info(filename, info): + if inspect.isclass(info): + with pytest.raises(info): + Wheel(filename) + return + w = Wheel(filename) + assert {k: getattr(w, k) for k in info.keys()} == info + + +@contextlib.contextmanager +def build_wheel(extra_file_defs=None, **kwargs): + file_defs = { + 'setup.py': ( + DALS( + """ + # -*- coding: utf-8 -*- + from setuptools import setup + import setuptools + setup(**%r) + """ + ) + % kwargs + ).encode('utf-8'), + } + if extra_file_defs: + file_defs.update(extra_file_defs) + with tempdir() as source_dir: + path.build(file_defs, source_dir) + subprocess.check_call( + (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir + ) + yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0] + + +def tree_set(root): + return { + os.path.join(os.path.relpath(dirpath, root), filename) + for dirpath, dirnames, filenames in os.walk(root) + for filename in filenames + } + + +def flatten_tree(tree): + """Flatten nested dicts and lists into a full list of paths""" + output = set() + for node, contents in tree.items(): + if isinstance(contents, dict): + contents = flatten_tree(contents) + + for elem in contents: + if isinstance(elem, dict): + output |= {os.path.join(node, val) for val in flatten_tree(elem)} + else: + output.add(os.path.join(node, elem)) + return output + + +def format_install_tree(tree): + return { + x.format( + py_version=sysconfig.get_python_version(), + platform=get_platform(), + shlib_ext=get_config_var('EXT_SUFFIX') or get_config_var('SO'), + ) + for x in tree + } + + +def _check_wheel_install( + filename, install_dir, install_tree_includes, project_name, version, requires_txt +): + w = Wheel(filename) + egg_path = os.path.join(install_dir, w.egg_name()) + w.install_as_egg(egg_path) + if install_tree_includes is not None: + install_tree = format_install_tree(install_tree_includes) + exp = tree_set(install_dir) + assert install_tree.issubset(exp), install_tree - exp + + (dist,) = metadata.Distribution.discover(path=[egg_path]) + + # pyright is nitpicky; fine to assume dist.metadata.__getitem__ will fail or return None + # (https://github.com/pypa/setuptools/pull/5006#issuecomment-2894774288) + assert dist.metadata['Name'] == project_name # pyright: ignore # noqa: PGH003 + assert dist.metadata['Version'] == version # pyright: ignore # noqa: PGH003 + assert dist.read_text('requires.txt') == requires_txt + + +class Record: + def __init__(self, id, **kwargs) -> None: + self._id = id + self._fields = kwargs + + def __repr__(self) -> str: + return f'{self._id}(**{self._fields!r})' + + +# Using Any to avoid possible type union issues later in test +# making a TypedDict is not worth in a test and anonymous/inline TypedDict are experimental +# https://github.com/python/mypy/issues/9884 +WHEEL_INSTALL_TESTS: tuple[dict[str, Any], ...] = ( + dict( + id='basic', + file_defs={'foo': {'__init__.py': ''}}, + setup_kwargs=dict( + packages=['foo'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'], + 'foo': ['__init__.py'], + } + }), + ), + dict( + id='utf-8', + setup_kwargs=dict( + description='Description accentuée', + ), + ), + dict( + id='data', + file_defs={ + 'data.txt': DALS( + """ + Some data... + """ + ), + }, + setup_kwargs=dict( + data_files=[('data_dir', ['data.txt'])], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': ['PKG-INFO', 'RECORD', 'WHEEL', 'top_level.txt'], + 'data_dir': ['data.txt'], + } + }), + ), + dict( + id='extension', + file_defs={ + 'extension.c': DALS( + """ + #include "Python.h" + + #if PY_MAJOR_VERSION >= 3 + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "extension", + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + #define INITERROR return NULL + + PyMODINIT_FUNC PyInit_extension(void) + + #else + + #define INITERROR return + + void initextension(void) + + #endif + { + #if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); + #else + PyObject *module = Py_InitModule("extension", NULL); + #endif + if (module == NULL) + INITERROR; + #if PY_MAJOR_VERSION >= 3 + return module; + #endif + } + """ + ), + }, + setup_kwargs=dict( + ext_modules=[ + Record( + 'setuptools.Extension', name='extension', sources=['extension.c'] + ) + ], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}-{platform}.egg': [ + 'extension{shlib_ext}', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ] + }, + ] + }), + ), + dict( + id='header', + file_defs={ + 'header.h': DALS( + """ + """ + ), + }, + setup_kwargs=dict( + headers=['header.h'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'header.h', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ] + }, + ] + }), + ), + dict( + id='script', + file_defs={ + 'script.py': DALS( + """ + #/usr/bin/python + print('hello world!') + """ + ), + 'script.sh': DALS( + """ + #/bin/sh + echo 'hello world!' + """ + ), + }, + setup_kwargs=dict( + scripts=['script.py', 'script.sh'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + {'scripts': ['script.py', 'script.sh']}, + ] + } + }), + ), + dict( + id='requires1', + install_requires='foobar==2.0', + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'requires.txt', + 'top_level.txt', + ] + } + }), + requires_txt=DALS( + """ + foobar==2.0 + """ + ), + ), + dict( + id='requires2', + install_requires=f""" + bar + foo<=2.0; {sys.platform!r} in sys_platform + """, + requires_txt=DALS( + """ + bar + foo<=2.0 + """ + ), + ), + dict( + id='requires3', + install_requires=f""" + bar; {sys.platform!r} != sys_platform + """, + ), + dict( + id='requires4', + install_requires=""" + foo + """, + extras_require={ + 'extra': 'foobar>3', + }, + requires_txt=DALS( + """ + foo + + [extra] + foobar>3 + """ + ), + ), + dict( + id='requires5', + extras_require={ + 'extra': f'foobar; {sys.platform!r} != sys_platform', + }, + requires_txt='\n' + + DALS( + """ + [extra] + """ + ), + ), + dict( + id='requires_ensure_order', + install_requires=""" + foo + bar + baz + qux + """, + extras_require={ + 'extra': """ + foobar>3 + barbaz>4 + bazqux>5 + quxzap>6 + """, + }, + requires_txt=DALS( + """ + foo + bar + baz + qux + + [extra] + foobar>3 + barbaz>4 + bazqux>5 + quxzap>6 + """ + ), + ), + dict( + id='namespace_package', + file_defs={ + 'foo': { + 'bar': {'__init__.py': ''}, + }, + }, + setup_kwargs=dict( + namespace_packages=['foo'], + packages=['foo.bar'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'foo-1.0-py{py_version}-nspkg.pth', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'namespace_packages.txt', + 'top_level.txt', + ] + }, + { + 'foo': [ + '__init__.py', + {'bar': ['__init__.py']}, + ] + }, + ] + }), + ), + dict( + id='empty_namespace_package', + file_defs={ + 'foobar': { + '__init__.py': ( + "__import__('pkg_resources').declare_namespace(__name__)" + ) + }, + }, + setup_kwargs=dict( + namespace_packages=['foobar'], + packages=['foobar'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': [ + 'foo-1.0-py{py_version}-nspkg.pth', + { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'namespace_packages.txt', + 'top_level.txt', + ] + }, + { + 'foobar': [ + '__init__.py', + ] + }, + ] + }), + ), + dict( + id='data_in_package', + file_defs={ + 'foo': { + '__init__.py': '', + 'data_dir': { + 'data.txt': DALS( + """ + Some data... + """ + ), + }, + } + }, + setup_kwargs=dict( + packages=['foo'], + data_files=[('foo/data_dir', ['foo/data_dir/data.txt'])], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + ], + 'foo': [ + '__init__.py', + { + 'data_dir': [ + 'data.txt', + ] + }, + ], + } + }), + ), +) + + +@pytest.mark.parametrize( + 'params', + WHEEL_INSTALL_TESTS, + ids=[params['id'] for params in WHEEL_INSTALL_TESTS], +) +def test_wheel_install(params): + project_name = params.get('name', 'foo') + version = params.get('version', '1.0') + install_requires = params.get('install_requires', []) + extras_require = params.get('extras_require', {}) + requires_txt = params.get('requires_txt', None) + install_tree = params.get('install_tree') + file_defs = params.get('file_defs', {}) + setup_kwargs = params.get('setup_kwargs', {}) + with ( + build_wheel( + name=project_name, + version=version, + install_requires=install_requires, + extras_require=extras_require, + extra_file_defs=file_defs, + **setup_kwargs, + ) as filename, + tempdir() as install_dir, + ): + _check_wheel_install( + filename, install_dir, install_tree, project_name, version, requires_txt + ) + + +def test_wheel_no_dist_dir(): + project_name = 'nodistinfo' + version = '1.0' + wheel_name = f'{project_name}-{version}-py2.py3-none-any.whl' + with tempdir() as source_dir: + wheel_path = os.path.join(source_dir, wheel_name) + # create an empty zip file + zipfile.ZipFile(wheel_path, 'w').close() + with tempdir() as install_dir: + with pytest.raises(ValueError): + _check_wheel_install( + wheel_path, install_dir, None, project_name, version, None + ) + + +def test_wheel_is_compatible(monkeypatch): + def sys_tags(): + return { + (t.interpreter, t.abi, t.platform) + for t in parse_tag('cp36-cp36m-manylinux1_x86_64') + } + + monkeypatch.setattr('setuptools.wheel._get_supported_tags', sys_tags) + assert Wheel('onnxruntime-0.1.2-cp36-cp36m-manylinux1_x86_64.whl').is_compatible() + + +def test_wheel_mode(): + @contextlib.contextmanager + def build_wheel(extra_file_defs=None, **kwargs): + file_defs = { + 'setup.py': ( + DALS( + """ + # -*- coding: utf-8 -*- + from setuptools import setup + import setuptools + setup(**%r) + """ + ) + % kwargs + ).encode('utf-8'), + } + if extra_file_defs: + file_defs.update(extra_file_defs) + with tempdir() as source_dir: + path.build(file_defs, source_dir) + runsh = pathlib.Path(source_dir) / "script.sh" + os.chmod(runsh, 0o777) + subprocess.check_call( + (sys.executable, 'setup.py', '-q', 'bdist_wheel'), cwd=source_dir + ) + yield glob.glob(os.path.join(source_dir, 'dist', '*.whl'))[0] + + params = dict( + id='script', + file_defs={ + 'script.py': DALS( + """ + #/usr/bin/python + print('hello world!') + """ + ), + 'script.sh': DALS( + """ + #/bin/sh + echo 'hello world!' + """ + ), + }, + setup_kwargs=dict( + scripts=['script.py', 'script.sh'], + ), + install_tree=flatten_tree({ + 'foo-1.0-py{py_version}.egg': { + 'EGG-INFO': [ + 'PKG-INFO', + 'RECORD', + 'WHEEL', + 'top_level.txt', + {'scripts': ['script.py', 'script.sh']}, + ] + } + }), + ) + + project_name = params.get('name', 'foo') + version = params.get('version', '1.0') + install_tree = params.get('install_tree') + file_defs = params.get('file_defs', {}) + setup_kwargs = params.get('setup_kwargs', {}) + + with ( + build_wheel( + name=project_name, + version=version, + install_requires=[], + extras_require={}, + extra_file_defs=file_defs, + **setup_kwargs, + ) as filename, + tempdir() as install_dir, + ): + _check_wheel_install( + filename, install_dir, install_tree, project_name, version, None + ) + w = Wheel(filename) + base = pathlib.Path(install_dir) / w.egg_name() + script_sh = base / "EGG-INFO" / "scripts" / "script.sh" + assert script_sh.exists() + if sys.platform != 'win32': + # Editable file mode has no effect on Windows + assert oct(stat.S_IMODE(script_sh.stat().st_mode)) == "0o777" diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py b/.venv/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py new file mode 100644 index 0000000..4f990eb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/test_windows_wrappers.py @@ -0,0 +1,258 @@ +""" +Python Script Wrapper for Windows +================================= + +setuptools includes wrappers for Python scripts that allows them to be +executed like regular windows programs. There are 2 wrappers, one +for command-line programs, cli.exe, and one for graphical programs, +gui.exe. These programs are almost identical, function pretty much +the same way, and are generated from the same source file. The +wrapper programs are used by copying them to the directory containing +the script they are to wrap and with the same name as the script they +are to wrap. +""" + +import pathlib +import platform +import subprocess +import sys +import textwrap + +import pytest + +from setuptools._importlib import resources + +pytestmark = pytest.mark.skipif(sys.platform != 'win32', reason="Windows only") + + +class WrapperTester: + @classmethod + def prep_script(cls, template): + python_exe = subprocess.list2cmdline([sys.executable]) + return template % locals() + + @classmethod + def create_script(cls, tmpdir): + """ + Create a simple script, foo-script.py + + Note that the script starts with a Unix-style '#!' line saying which + Python executable to run. The wrapper will use this line to find the + correct Python executable. + """ + + script = cls.prep_script(cls.script_tmpl) + + with (tmpdir / cls.script_name).open('w') as f: + f.write(script) + + # also copy cli.exe to the sample directory + with (tmpdir / cls.wrapper_name).open('wb') as f: + w = resources.files('setuptools').joinpath(cls.wrapper_source).read_bytes() + f.write(w) + + +def win_launcher_exe(prefix): + """A simple routine to select launcher script based on platform.""" + assert prefix in ('cli', 'gui') + if platform.machine() == "ARM64": + return f"{prefix}-arm64.exe" + else: + return f"{prefix}-32.exe" + + +class TestCLI(WrapperTester): + script_name = 'foo-script.py' + wrapper_name = 'foo.exe' + wrapper_source = win_launcher_exe('cli') + + script_tmpl = textwrap.dedent( + """ + #!%(python_exe)s + import sys + input = repr(sys.stdin.read()) + print(sys.argv[0][-14:]) + print(sys.argv[1:]) + print(input) + if __debug__: + print('non-optimized') + """ + ).lstrip() + + def test_basic(self, tmpdir): + """ + When the copy of cli.exe, foo.exe in this example, runs, it examines + the path name it was run with and computes a Python script path name + by removing the '.exe' suffix and adding the '-script.py' suffix. (For + GUI programs, the suffix '-script.pyw' is added.) This is why we + named out script the way we did. Now we can run out script by running + the wrapper: + + This example was a little pathological in that it exercised windows + (MS C runtime) quoting rules: + + - Strings containing spaces are surrounded by double quotes. + + - Double quotes in strings need to be escaped by preceding them with + back slashes. + + - One or more backslashes preceding double quotes need to be escaped + by preceding each of them with back slashes. + """ + self.create_script(tmpdir) + cmd = [ + str(tmpdir / 'foo.exe'), + 'arg1', + 'arg 2', + 'arg "2\\"', + 'arg 4\\', + 'arg5 a\\\\b', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate('hello\nworld\n') + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + ['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] + 'hello\nworld\n' + non-optimized + """ + ).lstrip() + assert actual == expected + + def test_symlink(self, tmpdir): + """ + Ensure that symlink for the foo.exe is working correctly. + """ + script_dir = tmpdir / "script_dir" + script_dir.mkdir() + self.create_script(script_dir) + symlink = pathlib.Path(tmpdir / "foo.exe") + symlink.symlink_to(script_dir / "foo.exe") + + cmd = [ + str(tmpdir / 'foo.exe'), + 'arg1', + 'arg 2', + 'arg "2\\"', + 'arg 4\\', + 'arg5 a\\\\b', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate('hello\nworld\n') + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + ['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] + 'hello\nworld\n' + non-optimized + """ + ).lstrip() + assert actual == expected + + def test_with_options(self, tmpdir): + """ + Specifying Python Command-line Options + -------------------------------------- + + You can specify a single argument on the '#!' line. This can be used + to specify Python options like -O, to run in optimized mode or -i + to start the interactive interpreter. You can combine multiple + options as usual. For example, to run in optimized mode and + enter the interpreter after running the script, you could use -Oi: + """ + self.create_script(tmpdir) + tmpl = textwrap.dedent( + """ + #!%(python_exe)s -Oi + import sys + input = repr(sys.stdin.read()) + print(sys.argv[0][-14:]) + print(sys.argv[1:]) + print(input) + if __debug__: + print('non-optimized') + sys.ps1 = '---' + """ + ).lstrip() + with (tmpdir / 'foo-script.py').open('w') as f: + f.write(self.prep_script(tmpl)) + cmd = [str(tmpdir / 'foo.exe')] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + encoding="utf-8", + ) + stdout, _stderr = proc.communicate() + actual = stdout.replace('\r\n', '\n') + expected = textwrap.dedent( + r""" + \foo-script.py + [] + '' + --- + """ + ).lstrip() + assert actual == expected + + +class TestGUI(WrapperTester): + """ + Testing the GUI Version + ----------------------- + """ + + script_name = 'bar-script.pyw' + wrapper_source = win_launcher_exe('gui') + wrapper_name = 'bar.exe' + + script_tmpl = textwrap.dedent( + """ + #!%(python_exe)s + import sys + f = open(sys.argv[1], 'wb') + bytes_written = f.write(repr(sys.argv[2]).encode('utf-8')) + f.close() + """ + ).strip() + + def test_basic(self, tmpdir): + """Test the GUI version with the simple script, bar-script.py""" + self.create_script(tmpdir) + + cmd = [ + str(tmpdir / 'bar.exe'), + str(tmpdir / 'test_output.txt'), + 'Test Argument', + ] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + encoding="utf-8", + ) + stdout, stderr = proc.communicate() + assert not stdout + assert not stderr + with (tmpdir / 'test_output.txt').open('rb') as f_out: + actual = f_out.read().decode('ascii') + assert actual == repr('Test Argument') diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/text.py b/.venv/lib/python3.12/site-packages/setuptools/tests/text.py new file mode 100644 index 0000000..e05cc63 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/text.py @@ -0,0 +1,4 @@ +class Filenames: + unicode = 'smörbröd.py' + latin_1 = unicode.encode('latin-1') + utf_8 = unicode.encode('utf-8') diff --git a/.venv/lib/python3.12/site-packages/setuptools/tests/textwrap.py b/.venv/lib/python3.12/site-packages/setuptools/tests/textwrap.py new file mode 100644 index 0000000..5e39618 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/tests/textwrap.py @@ -0,0 +1,6 @@ +import textwrap + + +def DALS(s): + "dedent and left-strip" + return textwrap.dedent(s).lstrip() diff --git a/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py b/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000..f502f5b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py @@ -0,0 +1,102 @@ +import sys +import unicodedata +from configparser import RawConfigParser + +from .compat import py39 +from .warnings import SetuptoolsDeprecationWarning + + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, str): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def filesys_decode(path): + """ + Ensure that the given path is decoded, + ``None`` when no expected encoding works + """ + + if isinstance(path, str): + return path + + fs_enc = sys.getfilesystemencoding() or 'utf-8' + candidates = fs_enc, 'utf-8' + + for enc in candidates: + try: + return path.decode(enc) + except UnicodeDecodeError: + continue + + return None + + +def try_encode(string, enc): + "turn unicode encoding into a functional routine" + try: + return string.encode(enc) + except UnicodeEncodeError: + return None + + +def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str: + """ + First try to read the file with UTF-8, if there is an error fallback to a + different encoding ("locale" by default). Returns the content of the file. + Also useful when reading files that might have been produced by an older version of + setuptools. + """ + try: + with open(file, "r", encoding="utf-8") as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) + with open(file, "r", encoding=fallback_encoding) as f: + return f.read() + + +def _cfg_read_utf8_with_fallback( + cfg: RawConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING +) -> None: + """Same idea as :func:`_read_utf8_with_fallback`, but for the + :meth:`RawConfigParser.read` method. + + This method may call ``cfg.clear()``. + """ + try: + cfg.read(file, encoding="utf-8") + except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) + cfg.clear() + cfg.read(file, encoding=fallback_encoding) + + +class _Utf8EncodingNeeded(SetuptoolsDeprecationWarning): + _SUMMARY = """ + `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`. + """ + + _DETAILS = """ + Fallback behavior for UTF-8 is considered **deprecated** and future versions of + `setuptools` may not implement it. + + Please encode {file!r} with "utf-8" to ensure future builds will succeed. + + If this file was produced by `setuptools` itself, cleaning up the cached files + and re-building/re-installing the package with a newer version of `setuptools` + (e.g. by updating `build-system.requires` in its `pyproject.toml`) + might solve the problem. + """ + # TODO: Add a deadline? + # Will we be able to remove this? + # The question comes to mind mainly because of sdists that have been produced + # by old versions of setuptools and published to PyPI... diff --git a/.venv/lib/python3.12/site-packages/setuptools/version.py b/.venv/lib/python3.12/site-packages/setuptools/version.py new file mode 100644 index 0000000..ec253c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/version.py @@ -0,0 +1,6 @@ +from ._importlib import metadata + +try: + __version__ = metadata.version('setuptools') or '0.dev0+unknown' +except Exception: + __version__ = '0.dev0+unknown' diff --git a/.venv/lib/python3.12/site-packages/setuptools/warnings.py b/.venv/lib/python3.12/site-packages/setuptools/warnings.py new file mode 100644 index 0000000..9646778 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/warnings.py @@ -0,0 +1,110 @@ +"""Provide basic warnings used by setuptools modules. + +Using custom classes (other than ``UserWarning``) allow users to set +``PYTHONWARNINGS`` filters to run tests and prepare for upcoming changes in +setuptools. +""" + +from __future__ import annotations + +import os +import warnings +from datetime import date +from inspect import cleandoc +from textwrap import indent +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_DueDate: TypeAlias = tuple[int, int, int] # time tuple +_INDENT = 8 * " " +_TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}""" + + +class SetuptoolsWarning(UserWarning): + """Base class in ``setuptools`` warning hierarchy.""" + + @classmethod + def emit( + cls, + summary: str | None = None, + details: str | None = None, + due_date: _DueDate | None = None, + see_docs: str | None = None, + see_url: str | None = None, + stacklevel: int = 2, + **kwargs, + ) -> None: + """Private: reserved for ``setuptools`` internal use only""" + # Default values: + summary_ = summary or getattr(cls, "_SUMMARY", None) or "" + details_ = details or getattr(cls, "_DETAILS", None) or "" + due_date = due_date or getattr(cls, "_DUE_DATE", None) + docs_ref = see_docs or getattr(cls, "_SEE_DOCS", None) + docs_url = docs_ref and f"https://setuptools.pypa.io/en/latest/{docs_ref}" + see_url = see_url or getattr(cls, "_SEE_URL", None) + due = date(*due_date) if due_date else None + + text = cls._format(summary_, details_, due, see_url or docs_url, kwargs) + if due and due < date.today() and _should_enforce(): + raise cls(text) + warnings.warn(text, cls, stacklevel=stacklevel + 1) + + @classmethod + def _format( + cls, + summary: str, + details: str, + due_date: date | None = None, + see_url: str | None = None, + format_args: dict | None = None, + ) -> str: + """Private: reserved for ``setuptools`` internal use only""" + today = date.today() + summary = cleandoc(summary).format_map(format_args or {}) + possible_parts = [ + cleandoc(details).format_map(format_args or {}), + ( + f"\nBy {due_date:%Y-%b-%d}, you need to update your project and remove " + "deprecated calls\nor your builds will no longer be supported." + if due_date and due_date > today + else None + ), + ( + "\nThis deprecation is overdue, please update your project and remove " + "deprecated\ncalls to avoid build errors in the future." + if due_date and due_date < today + else None + ), + (f"\nSee {see_url} for details." if see_url else None), + ] + parts = [x for x in possible_parts if x] + if parts: + body = indent(_TEMPLATE.format(details="\n".join(parts)), _INDENT) + return "\n".join([summary, "!!\n", body, "\n!!"]) + return summary + + +class InformationOnly(SetuptoolsWarning): + """Currently there is no clear way of displaying messages to the users + that use the setuptools backend directly via ``pip``. + The only thing that might work is a warning, although it is not the + most appropriate tool for the job... + + See pypa/packaging-problems#558. + """ + + +class SetuptoolsDeprecationWarning(SetuptoolsWarning): + """ + Base class for warning deprecations in ``setuptools`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ + + +def _should_enforce(): + enforce = os.getenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false").lower() + return enforce in ("true", "on", "ok", "1") diff --git a/.venv/lib/python3.12/site-packages/setuptools/wheel.py b/.venv/lib/python3.12/site-packages/setuptools/wheel.py new file mode 100644 index 0000000..9366303 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/wheel.py @@ -0,0 +1,262 @@ +"""Wheels support.""" + +import contextlib +import email +import functools +import itertools +import os +import posixpath +import re +import zipfile +from collections.abc import Iterator + +from packaging.requirements import Requirement +from packaging.tags import sys_tags +from packaging.utils import canonicalize_name +from packaging.version import Version as parse_version + +import setuptools +from setuptools.archive_util import _unpack_zipfile_obj +from setuptools.command.egg_info import _egg_basename, write_requirements + +from ._discovery import extras_from_deps +from ._importlib import metadata +from .unicode_utils import _read_utf8_with_fallback + +from distutils.util import get_platform + +WHEEL_NAME = re.compile( + r"""^(?P.+?)-(?P\d.*?) + ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) + )\.whl$""", + re.VERBOSE, +).match + +NAMESPACE_PACKAGE_INIT = "__import__('pkg_resources').declare_namespace(__name__)\n" + + +@functools.cache +def _get_supported_tags(): + # We calculate the supported tags only once, otherwise calling + # this method on thousands of wheels takes seconds instead of + # milliseconds. + return {(t.interpreter, t.abi, t.platform) for t in sys_tags()} + + +def unpack(src_dir, dst_dir) -> None: + """Move everything under `src_dir` to `dst_dir`, and delete the former.""" + for dirpath, dirnames, filenames in os.walk(src_dir): + subdir = os.path.relpath(dirpath, src_dir) + for f in filenames: + src = os.path.join(dirpath, f) + dst = os.path.join(dst_dir, subdir, f) + os.renames(src, dst) + for n, d in reversed(list(enumerate(dirnames))): + src = os.path.join(dirpath, d) + dst = os.path.join(dst_dir, subdir, d) + if not os.path.exists(dst): + # Directory does not exist in destination, + # rename it and prune it from os.walk list. + os.renames(src, dst) + del dirnames[n] + # Cleanup. + for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True): + assert not filenames + os.rmdir(dirpath) + + +@contextlib.contextmanager +def disable_info_traces() -> Iterator[None]: + """ + Temporarily disable info traces. + """ + from distutils import log + + saved = log.set_threshold(log.WARN) + try: + yield + finally: + log.set_threshold(saved) + + +class Wheel: + def __init__(self, filename) -> None: + match = WHEEL_NAME(os.path.basename(filename)) + if match is None: + raise ValueError(f'invalid wheel name: {filename!r}') + self.filename = filename + for k, v in match.groupdict().items(): + setattr(self, k, v) + + def tags(self): + """List tags (py_version, abi, platform) supported by this wheel.""" + return itertools.product( + self.py_version.split('.'), + self.abi.split('.'), + self.platform.split('.'), + ) + + def is_compatible(self): + """Is the wheel compatible with the current platform?""" + return next((True for t in self.tags() if t in _get_supported_tags()), False) + + def egg_name(self): + return ( + _egg_basename( + self.project_name, + self.version, + platform=(None if self.platform == 'any' else get_platform()), + ) + + ".egg" + ) + + def get_dist_info(self, zf): + # find the correct name of the .dist-info dir in the wheel file + for member in zf.namelist(): + dirname = posixpath.dirname(member) + if dirname.endswith('.dist-info') and canonicalize_name(dirname).startswith( + canonicalize_name(self.project_name) + ): + return dirname + raise ValueError("unsupported wheel format. .dist-info not found") + + def install_as_egg(self, destination_eggdir) -> None: + """Install wheel as an egg directory.""" + with zipfile.ZipFile(self.filename) as zf: + self._install_as_egg(destination_eggdir, zf) + + def _install_as_egg(self, destination_eggdir, zf): + dist_basename = f'{self.project_name}-{self.version}' + dist_info = self.get_dist_info(zf) + dist_data = f'{dist_basename}.data' + egg_info = os.path.join(destination_eggdir, 'EGG-INFO') + + self._convert_metadata(zf, destination_eggdir, dist_info, egg_info) + self._move_data_entries(destination_eggdir, dist_data) + self._fix_namespace_packages(egg_info, destination_eggdir) + + @staticmethod + def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): + def get_metadata(name): + with zf.open(posixpath.join(dist_info, name)) as fp: + value = fp.read().decode('utf-8') + return email.parser.Parser().parsestr(value) + + wheel_metadata = get_metadata('WHEEL') + # Check wheel format version is supported. + wheel_version = parse_version(wheel_metadata.get('Wheel-Version')) + wheel_v1 = parse_version('1.0') <= wheel_version < parse_version('2.0dev0') + if not wheel_v1: + raise ValueError(f'unsupported wheel format version: {wheel_version}') + # Extract to target directory. + _unpack_zipfile_obj(zf, destination_eggdir) + dist_info = os.path.join(destination_eggdir, dist_info) + install_requires, extras_require = Wheel._convert_requires( + destination_eggdir, dist_info + ) + os.rename(dist_info, egg_info) + os.rename( + os.path.join(egg_info, 'METADATA'), + os.path.join(egg_info, 'PKG-INFO'), + ) + setup_dist = setuptools.Distribution( + attrs=dict( + install_requires=install_requires, + extras_require=extras_require, + ), + ) + with disable_info_traces(): + write_requirements( + setup_dist.get_command_obj('egg_info'), + None, + os.path.join(egg_info, 'requires.txt'), + ) + + @staticmethod + def _convert_requires(destination_eggdir, dist_info): + md = metadata.Distribution.at(dist_info).metadata + deps = md.get_all('Requires-Dist') or [] + reqs = list(map(Requirement, deps)) + + extras = extras_from_deps(deps) + + # Note: Evaluate and strip markers now, + # as it's difficult to convert back from the syntax: + # foobar; "linux" in sys_platform and extra == 'test' + def raw_req(req): + req = Requirement(str(req)) + req.marker = None + return str(req) + + def eval(req, **env): + return not req.marker or req.marker.evaluate(env) + + def for_extra(req): + try: + markers = req.marker._markers + except AttributeError: + markers = () + return set( + marker[2].value + for marker in markers + if isinstance(marker, tuple) and marker[0].value == 'extra' + ) + + install_requires = list( + map(raw_req, filter(eval, itertools.filterfalse(for_extra, reqs))) + ) + extras_require = { + extra: list( + map( + raw_req, + (req for req in reqs if for_extra(req) and eval(req, extra=extra)), + ) + ) + for extra in extras + } + return install_requires, extras_require + + @staticmethod + def _move_data_entries(destination_eggdir, dist_data): + """Move data entries to their correct location.""" + dist_data = os.path.join(destination_eggdir, dist_data) + dist_data_scripts = os.path.join(dist_data, 'scripts') + if os.path.exists(dist_data_scripts): + egg_info_scripts = os.path.join(destination_eggdir, 'EGG-INFO', 'scripts') + os.mkdir(egg_info_scripts) + for entry in os.listdir(dist_data_scripts): + # Remove bytecode, as it's not properly handled + # during easy_install scripts install phase. + if entry.endswith('.pyc'): + os.unlink(os.path.join(dist_data_scripts, entry)) + else: + os.rename( + os.path.join(dist_data_scripts, entry), + os.path.join(egg_info_scripts, entry), + ) + os.rmdir(dist_data_scripts) + for subdir in filter( + os.path.exists, + ( + os.path.join(dist_data, d) + for d in ('data', 'headers', 'purelib', 'platlib') + ), + ): + unpack(subdir, destination_eggdir) + if os.path.exists(dist_data): + os.rmdir(dist_data) + + @staticmethod + def _fix_namespace_packages(egg_info, destination_eggdir): + namespace_packages = os.path.join(egg_info, 'namespace_packages.txt') + if os.path.exists(namespace_packages): + namespace_packages = _read_utf8_with_fallback(namespace_packages).split() + + for mod in namespace_packages: + mod_dir = os.path.join(destination_eggdir, *mod.split('.')) + mod_init = os.path.join(mod_dir, '__init__.py') + if not os.path.exists(mod_dir): + os.mkdir(mod_dir) + if not os.path.exists(mod_init): + with open(mod_init, 'w', encoding="utf-8") as fp: + fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/.venv/lib/python3.12/site-packages/setuptools/windows_support.py b/.venv/lib/python3.12/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000..7a2b53a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/setuptools/windows_support.py @@ -0,0 +1,30 @@ +import platform + + +def windows_only(func): + if platform.system() != 'Windows': + return lambda *args, **kwargs: None + return func + + +@windows_only +def hide_file(path: str) -> None: + """ + Set the hidden attribute on a file or directory. + + From https://stackoverflow.com/questions/19622133/ + + `path` must be text. + """ + import ctypes + import ctypes.wintypes + + SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW + SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD + SetFileAttributes.restype = ctypes.wintypes.BOOL + + FILE_ATTRIBUTE_HIDDEN = 0x02 + + ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) + if not ret: + raise ctypes.WinError() diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE new file mode 100644 index 0000000..1cc22a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2024 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA new file mode 100644 index 0000000..cfde03c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/METADATA @@ -0,0 +1,43 @@ +Metadata-Version: 2.1 +Name: six +Version: 1.17.0 +Summary: Python 2 and 3 compatibility utilities +Home-page: https://github.com/benjaminp/six +Author: Benjamin Peterson +Author-email: benjamin@python.org +License: MIT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* +License-File: LICENSE + +.. image:: https://img.shields.io/pypi/v/six.svg + :target: https://pypi.org/project/six/ + :alt: six on PyPI + +.. image:: https://readthedocs.org/projects/six/badge/?version=latest + :target: https://six.readthedocs.io/ + :alt: six's documentation on Read the Docs + +.. image:: https://img.shields.io/badge/license-MIT-green.svg + :target: https://github.com/benjaminp/six/blob/master/LICENSE + :alt: MIT License badge + +Six is a Python 2 and 3 compatibility library. It provides utility functions +for smoothing over the differences between the Python versions with the goal of +writing Python code that is compatible on both Python versions. See the +documentation for more information on what is provided. + +Six supports Python 2.7 and 3.3+. It is contained in only one Python +file, so it can be easily copied into your project. (The copyright and license +notice must be retained.) + +Online documentation is at https://six.readthedocs.io/. + +Bugs can be reported to https://github.com/benjaminp/six. The code can also +be found there. diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD new file mode 100644 index 0000000..bb90c1a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/six.cpython-312.pyc,, +six-1.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +six-1.17.0.dist-info/LICENSE,sha256=Q3W6IOK5xsTnytKUCmKP2Q6VzD1Q7pKq51VxXYuh-9A,1066 +six-1.17.0.dist-info/METADATA,sha256=ViBCB4wnUlSfbYp8htvF3XCAiKe-bYBnLsewcQC3JGg,1658 +six-1.17.0.dist-info/RECORD,, +six-1.17.0.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109 +six-1.17.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 +six.py,sha256=xRyR9wPT1LNpbJI8tf7CE-BeddkhU5O--sfy-mo5BN8,34703 diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL new file mode 100644 index 0000000..104f387 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.6.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt new file mode 100644 index 0000000..ffe2fce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six-1.17.0.dist-info/top_level.txt @@ -0,0 +1 @@ +six diff --git a/.venv/lib/python3.12/site-packages/six.py b/.venv/lib/python3.12/site-packages/six.py new file mode 100644 index 0000000..3de5969 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/six.py @@ -0,0 +1,1003 @@ +# Copyright (c) 2010-2024 Benjamin Peterson +# +# 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. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.17.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections", "IterableUserDict", "UserDict"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +if sys.version_info[:2] < (3, 14): + _urllib_request_moved_attributes.extend( + [ + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + ] + ) +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/.venv/lib/python3.12/site-packages/src/ujson/python/version.h b/.venv/lib/python3.12/site-packages/src/ujson/python/version.h new file mode 100644 index 0000000..d0c2a50 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/src/ujson/python/version.h @@ -0,0 +1,39 @@ +/* +Developed by ESN, an Electronic Arts Inc. studio. +Copyright (c) 2014, Electronic Arts Inc. +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 ESN, Electronic Arts Inc. 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 ELECTRONIC ARTS INC. 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. + + +Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc) +http://code.google.com/p/stringencoders/ +Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved. + +Numeric decoder derived from from TCL library +http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. +*/ + +#define UJSON_VERSION "5.12.1" diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/LICENSE new file mode 100644 index 0000000..c08bbd0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012 Tom Cocagne + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/METADATA b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/METADATA new file mode 100644 index 0000000..630a6e8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/METADATA @@ -0,0 +1,50 @@ +Metadata-Version: 2.1 +Name: srp +Version: 1.0.22 +Summary: Secure Remote Password +Home-page: https://github.com/cocagne/pysrp +Download-URL: http://pypi.python.org/pypi/srp +Author: Tom Cocagne +Author-email: tom.cocagne@gmail.com +License: MIT +Platform: OS Independent +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 :: 3 +Classifier: Programming Language :: Python +Classifier: Topic :: Security +Provides: srp +License-File: LICENSE +Requires-Dist: six + + + +This package provides an implementation of the Secure Remote Password +protocol (SRP). SRP is a cryptographically strong authentication +protocol for password-based, mutual authentication over an insecure +network connection. + +Unlike other common challenge-response autentication protocols, such +as Kerberos and SSL, SRP does not rely on an external infrastructure +of trusted key servers or certificate management. Instead, SRP server +applications use verification keys derived from each user's password +to determine the authenticity of a network connection. + +SRP provides mutual-authentication in that successful authentication +requires both sides of the connection to have knowledge of the +user's password. If the client side lacks the user's password or the +server side lacks the proper verification key, the authentication will +fail. + +Unlike SSL, SRP does not directly encrypt all data flowing through +the authenticated connection. However, successful authentication does +result in a cryptographically strong shared key that can be used +for symmetric-key encryption. + +For a full description of the pysrp package and the SRP protocol, +please refer to the `srp module documentation`_. + +.. _`srp module documentation`: http://packages.python.org/srp + diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/RECORD b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/RECORD new file mode 100644 index 0000000..c204fab --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/RECORD @@ -0,0 +1,19 @@ +srp-1.0.22.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +srp-1.0.22.dist-info/LICENSE,sha256=Nxe9Cq9z4-xW7msDozjOpIfV-LxucxCLyDpHbuT4RJU,1078 +srp-1.0.22.dist-info/METADATA,sha256=_xrAptbyQTSIQ6uDr4Iz07HkKppwog5lASYZojskCw0,1890 +srp-1.0.22.dist-info/RECORD,, +srp-1.0.22.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +srp-1.0.22.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91 +srp-1.0.22.dist-info/top_level.txt,sha256=d-HvMbf3aJh-2FCfMqhIOWkzPtCRkngJSoo4i0722ec,4 +srp/__init__.py,sha256=8izcyvxJ4IiBNmgGB8qfAi76_LfD7KxMSfPBkyv52HE,653 +srp/__pycache__/__init__.cpython-312.pyc,, +srp/__pycache__/_ctsrp.cpython-312.pyc,, +srp/__pycache__/_pysrp.cpython-312.pyc,, +srp/__pycache__/test_srp.cpython-312.pyc,, +srp/_ctsrp.py,sha256=PykYoSGE_Wql_3l4DVJE0vLoZ4rIylQJtDtt1WidOtU,20474 +srp/_pysrp.py,sha256=oWAOfueyt7ID-crUiP1-CkQSl1l3Q7pUM-2cUZPno1k,14597 +srp/doc/__pycache__/conf.cpython-312.pyc,, +srp/doc/conf.py,sha256=VlMBeS1sMfS6e_MST3V2fktEr955n__VFm8DM6XA4F0,7069 +srp/doc/index.rst,sha256=hqrN-4qzOcJ_oRmwDFFPk2cEN3_P0hRC0b-jc43xNsM,481 +srp/doc/srp.rst,sha256=2w50XwgEIphXKg5kwkVxTnP2ht8z7TsHIJCKAoccWkk,14305 +srp/test_srp.py,sha256=u8O0sP58pnR6SWOjru6oifiuxUfOaHR7kdmjWbQnC2I,10179 diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/WHEEL new file mode 100644 index 0000000..9b78c44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.3.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/top_level.txt new file mode 100644 index 0000000..fa9deb9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp-1.0.22.dist-info/top_level.txt @@ -0,0 +1 @@ +srp diff --git a/.venv/lib/python3.12/site-packages/srp/__init__.py b/.venv/lib/python3.12/site-packages/srp/__init__.py new file mode 100644 index 0000000..6aabcb0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/__init__.py @@ -0,0 +1,31 @@ + +_mod = None + +try: + import srp._ctsrp + _mod = srp._ctsrp +except (ImportError, OSError): + pass + +if not _mod: + import srp._pysrp + _mod = srp._pysrp + +User = _mod.User +Verifier = _mod.Verifier +create_salted_verification_key = _mod.create_salted_verification_key + +SHA1 = _mod.SHA1 +SHA224 = _mod.SHA224 +SHA256 = _mod.SHA256 +SHA384 = _mod.SHA384 +SHA512 = _mod.SHA512 + +NG_1024 = _mod.NG_1024 +NG_2048 = _mod.NG_2048 +NG_4096 = _mod.NG_4096 +NG_8192 = _mod.NG_8192 +NG_CUSTOM = _mod.NG_CUSTOM + +rfc5054_enable = _mod.rfc5054_enable +no_username_in_x = _mod.no_username_in_x diff --git a/.venv/lib/python3.12/site-packages/srp/_ctsrp.py b/.venv/lib/python3.12/site-packages/srp/_ctsrp.py new file mode 100644 index 0000000..9dbd506 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/_ctsrp.py @@ -0,0 +1,654 @@ + # N A large safe prime (N = 2q+1, where q is prime) + # All arithmetic is done modulo N. + # g A generator modulo N + # k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + # s User's salt + # I Username + # p Cleartext Password + # H() One-way hash function + # ^ (Modular) Exponentiation + # u Random scrambling parameter + # a,b Secret ephemeral values + # A,B Public ephemeral values + # x Private key (derived from p and s) + # v Password verifier + +import os +import sys +import hashlib +import random +import ctypes +import time + + +_rfc5054_compat = False +_no_username_in_x = False + +def rfc5054_enable(enable=True): + global _rfc5054_compat + _rfc5054_compat = enable + +def no_username_in_x(enable=True): + global _no_username_in_x + _no_username_in_x = enable + + +SHA1 = 0 +SHA224 = 1 +SHA256 = 2 +SHA384 = 3 +SHA512 = 4 + +NG_1024 = 0 +NG_2048 = 1 +NG_4096 = 2 +NG_8192 = 3 +NG_CUSTOM = 4 + +_hash_map = { SHA1 : hashlib.sha1, + SHA224 : hashlib.sha224, + SHA256 : hashlib.sha256, + SHA384 : hashlib.sha384, + SHA512 : hashlib.sha512 } + + +_ng_const = ( +# 1024-bit +(('''\ +EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ +EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ +F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ +9AFD5138FE8376435B9FC61D2FC0EB06E3''').encode('ascii'), +b"2"), +# 2048 +(('''\ +AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ +A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ +95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ +747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ +8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ +60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ +FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''').encode('ascii'), +b"2"), +# 4096 +(('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ +FFFFFFFFFFFFFFFF''').encode('ascii'), +b"5"), +# 8192 +(('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ +36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ +AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ +DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ +2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ +F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ +BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ +CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ +B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ +387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ +6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ +3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ +5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ +22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ +2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ +6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ +0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ +359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ +FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ +60C980DD98EDD3DFFFFFFFFFFFFFFFFF''').encode('ascii'), +b'13') +) + + + +#N_HEX = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73" +#G_HEX = "2" +#HNxorg = None + +dlls = list() + +platform = sys.platform +if platform == 'darwin': + dlls.append( ctypes.cdll.LoadLibrary('libssl.32.dylib') ) +elif 'win' in platform: + for d in ('libeay32.dll', 'libssl32.dll', 'ssleay32.dll'): + try: + dlls.append( ctypes.cdll.LoadLibrary(d) ) + except: + pass +else: + try: + dlls.append( ctypes.cdll.LoadLibrary('libssl.so.1.1.0') ) + except OSError: + dlls.append( ctypes.cdll.LoadLibrary('libssl.so') ) + +class BIGNUM_Struct (ctypes.Structure): + _fields_ = [ ("d", ctypes.c_void_p), + ("top", ctypes.c_int), + ("dmax", ctypes.c_int), + ("neg", ctypes.c_int), + ("flags", ctypes.c_int) ] + + +class BN_CTX_Struct (ctypes.Structure): + _fields_ = [ ("_", ctypes.c_byte) ] + + +BIGNUM = ctypes.POINTER( BIGNUM_Struct ) +BN_CTX = ctypes.POINTER( BN_CTX_Struct ) + + +def load_func( name, args, returns = ctypes.c_int): + d = sys.modules[ __name__ ].__dict__ + f = None + + for dll in dlls: + try: + f = getattr(dll, name) + f.argtypes = args + f.restype = returns + d[ name ] = f + return + except: + pass + raise ImportError('Unable to load required functions from SSL dlls') + + +load_func( 'BN_new', [], BIGNUM ) +load_func( 'BN_free', [ BIGNUM ], None ) +load_func( 'BN_clear', [ BIGNUM ], None ) + +load_func( 'BN_CTX_new', [] , BN_CTX ) +load_func( 'BN_CTX_free', [ BN_CTX ], None ) + +load_func( 'BN_set_flags', [ BIGNUM, ctypes.c_int ], None ) +BN_FLG_CONSTTIME = 0x04 + +load_func( 'BN_cmp', [ BIGNUM, BIGNUM ], ctypes.c_int ) + +load_func( 'BN_num_bits', [ BIGNUM ], ctypes.c_int ) + +load_func( 'BN_add', [ BIGNUM, BIGNUM, BIGNUM ] ) +load_func( 'BN_sub', [ BIGNUM, BIGNUM, BIGNUM ] ) +load_func( 'BN_mul', [ BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) +load_func( 'BN_div', [ BIGNUM, BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) +load_func( 'BN_mod_exp', [ BIGNUM, BIGNUM, BIGNUM, BIGNUM, BN_CTX ] ) + +load_func( 'BN_rand', [ BIGNUM, ctypes.c_int, ctypes.c_int, ctypes.c_int ] ) + +load_func( 'BN_bn2bin', [ BIGNUM, ctypes.c_char_p ] ) +load_func( 'BN_bin2bn', [ ctypes.c_char_p, ctypes.c_int, BIGNUM ], BIGNUM ) + +load_func( 'BN_hex2bn', [ ctypes.POINTER(BIGNUM), ctypes.c_char_p ] ) +load_func( 'BN_bn2hex', [ BIGNUM ], ctypes.c_char_p ) + +load_func( 'CRYPTO_free', [ ctypes.c_char_p ] ) + +load_func( 'RAND_seed', [ ctypes.c_char_p, ctypes.c_int ] ) + + +def BN_num_bytes(a): + return ((BN_num_bits(a)+7)//8) + + +def BN_mod(rem,m,d,ctx): + return BN_div(None, rem, m, d, ctx) + + +def BN_is_zero( n ): + return n[0].top == 0 + + +def bn_to_bytes( n ): + b = ctypes.create_string_buffer( BN_num_bytes(n) ) + BN_bn2bin(n, b) + return b.raw + + +def bytes_to_bn( dest_bn, bytes ): + BN_bin2bn(bytes, len(bytes), dest_bn) + + +def H_str( hash_class, dest_bn, s ): + d = hash_class(s).digest() + buff = ctypes.create_string_buffer( s ) + BN_bin2bn(d, len(d), dest) + + +def H_bn( hash_class, dest, n ): + bin = ctypes.create_string_buffer( BN_num_bytes(n) ) + BN_bn2bin(n, bin) + d = hash_class( bin.raw ).digest() + BN_bin2bn(d, len(d), dest) + + +def H_bn_bn( hash_class, dest, n1, n2, width ): + h = hash_class() + bin1 = ctypes.create_string_buffer( BN_num_bytes(n1) ) + bin2 = ctypes.create_string_buffer( BN_num_bytes(n2) ) + BN_bn2bin(n1, bin1) + BN_bn2bin(n2, bin2) + if _rfc5054_compat: + h.update(bytes(width - len(bin1.raw))) + h.update( bin1.raw ) + if _rfc5054_compat: + h.update(bytes(width - len(bin2.raw))) + h.update( bin2.raw ) + d = h.digest() + BN_bin2bn(d, len(d), dest) + + +def H_bn_str( hash_class, dest, n, s ): + h = hash_class() + bin = ctypes.create_string_buffer( BN_num_bytes(n) ) + BN_bn2bin(n, bin) + h.update( bin.raw ) + h.update( s ) + d = h.digest() + BN_bin2bn(d, len(d), dest) + + +def calculate_x( hash_class, dest, salt, username, password ): + username = username.encode() if hasattr(username, 'encode') else username + password = password.encode() if hasattr(password, 'encode') else password + if _no_username_in_x: + username = b'' + up = hash_class(username + b':' + password).digest() + H_bn_str( hash_class, dest, salt, up ) + BN_set_flags(dest, BN_FLG_CONSTTIME) + + +def update_hash( ctx, n ): + buff = ctypes.create_string_buffer( BN_num_bytes(n) ) + BN_bn2bin(n, buff) + ctx.update( buff.raw ) + + +def calculate_M( hash_class, N, g, I, s, A, B, K ): + I = I.encode() if hasattr(I, 'encode') else I + h = hash_class() + h.update( HNxorg( hash_class, N, g ) ) + h.update( hash_class(I).digest() ) + update_hash( h, s ) + update_hash( h, A ) + update_hash( h, B ) + h.update( K ) + return h.digest() + + +def calculate_H_AMK( hash_class, A, M, K ): + h = hash_class() + update_hash( h, A ) + h.update( M ) + h.update( K ) + return h.digest() + + +def HNxorg( hash_class, N, g ): + bN = ctypes.create_string_buffer( BN_num_bytes(N) ) + bg = ctypes.create_string_buffer( BN_num_bytes(g) ) + + BN_bn2bin(N, bN) + BN_bn2bin(g, bg) + + padding = len(bN) - len(bg) if _rfc5054_compat else 0 + + hN = hash_class( bN.raw ).digest() + hg = hash_class( b''.join([ b'\0'*padding, bg.raw ]) ).digest() + + return ( ''.join( chr( hN[i] ^ hg[i] ) for i in range(0,len(hN)) ) ).encode('latin1') + + +def get_ngk( hash_class, ng_type, n_hex, g_hex, ctx ): + if ng_type < NG_CUSTOM: + n_hex, g_hex = _ng_const[ ng_type ] + N = BN_new() + g = BN_new() + k = BN_new() + + BN_hex2bn( N, n_hex ) + BN_hex2bn( g, g_hex ) + H_bn_bn(hash_class, k, N, g, width=BN_num_bytes(N)) + if _rfc5054_compat: + BN_mod(k, k, N, ctx) + + return N, g, k + + + +def create_salted_verification_key( username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, salt_len=4, k_hex=None ): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + s = BN_new() + v = BN_new() + x = BN_new() + ctx = BN_CTX_new() + + hash_class = _hash_map[ hash_alg ] + N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex, ctx ) + + BN_rand(s, salt_len * 8, -1, 0); + + calculate_x( hash_class, x, s, username, password ) + + BN_mod_exp(v, g, x, N, ctx) + + salt = bn_to_bytes( s ) + verifier = bn_to_bytes( v ) + + BN_free(s) + BN_free(v) + BN_free(x) + BN_free(N) + BN_free(g) + BN_free(k) + BN_CTX_free(ctx) + + return salt, verifier + + + +class Verifier: + def __init__(self, username, bytes_s, bytes_v, bytes_A=None, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, bytes_b=None, k_hex=None): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + if bytes_b and len(bytes_b) != 32: + raise ValueError("32 bytes required for bytes_b") + self.B = BN_new() + self.K = None + self.S = BN_new() + self.u = BN_new() + self.b = BN_new() + self.s = BN_new() + self.v = BN_new() + self.tmp1 = BN_new() + self.tmp2 = BN_new() + self.ctx = BN_CTX_new() + self.I = username + self.M = None + self.H_AMK = None + self._authenticated = False + + self.safety_failed = False + + hash_class = _hash_map[ hash_alg ] + N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex, self.ctx ) + if k_hex is not None: + BN_hex2bn(k, k_hex) + + self.hash_class = hash_class + self.N = N + self.g = g + self.k = k + + bytes_to_bn( self.s, bytes_s ) + bytes_to_bn( self.v, bytes_v ) + if bytes_A: + self._set_A(bytes_A) + + if not self.safety_failed: + if bytes_b: + bytes_to_bn( self.b, bytes_b ) + else: + BN_rand(self.b, 256, 0, 0) + BN_set_flags(self.b, BN_FLG_CONSTTIME) + + # B = kv + g^b + BN_mul(self.tmp1, k, self.v, self.ctx) + BN_mod_exp(self.tmp2, g, self.b, N, self.ctx) + BN_add(self.B, self.tmp1, self.tmp2) + BN_mod(self.B, self.B, N, self.ctx) + + + def __del__(self): + if not hasattr(self, 'A'): + return # __init__ threw exception. no clean up required + BN_free(self.A) + BN_free(self.B) + BN_free(self.S) + BN_free(self.u) + BN_free(self.b) + BN_free(self.s) + BN_free(self.v) + BN_free(self.N) + BN_free(self.g) + BN_free(self.k) + BN_free(self.tmp1) + BN_free(self.tmp2) + BN_CTX_free(self.ctx) + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.I + + + def get_ephemeral_secret(self): + return bn_to_bytes(self.b) + + + def get_session_key(self): + return self.K if self._authenticated else None + + + # returns (bytes_s, bytes_B) on success, (None,None) if SRP-6a safety check fails + def get_challenge(self): + if self.safety_failed: + return None, None + else: + return (bn_to_bytes(self.s), bn_to_bytes(self.B)) + + + def verify_session(self, user_M, bytes_A=None): + if bytes_A: + self._set_A(bytes_A) + if not hasattr(self, 'A'): + raise ValueError("bytes_A must be provided through Verifier constructor or verify_session parameter.") + if not self.safety_failed: + self._derive_H_AMK() + if user_M == self.M: + self._authenticated = True + return self.H_AMK + + + def _set_A(self, bytes_A): + self.A = BN_new() + bytes_to_bn( self.A, bytes_A ) + + # SRP-6a safety check + BN_mod(self.tmp1, self.A, self.N, self.ctx) + + if BN_is_zero(self.tmp1): + self.safety_failed = True + + def _derive_H_AMK(self): + H_bn_bn(self.hash_class, self.u, self.A, self.B, width=BN_num_bytes(self.N)) + + # S = (A *(v^u)) ^ b + BN_mod_exp(self.tmp1, self.v, self.u, self.N, self.ctx) + BN_mul(self.tmp2, self.A, self.tmp1, self.ctx) + BN_mod_exp(self.S, self.tmp2, self.b, self.N, self.ctx) + + self.K = self.hash_class( bn_to_bytes(self.S) ).digest() + + self.M = calculate_M( self.hash_class, self.N, self.g, self.I, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK( self.hash_class, self.A, self.M, self.K ) + + +class User: + def __init__(self, username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, bytes_a=None, bytes_A=None, k_hex=None): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + if bytes_a and len(bytes_a) != 32: + raise ValueError("32 bytes required for bytes_a") + self.username = username + self.password = password + self.a = BN_new() + self.A = BN_new() + self.B = BN_new() + self.s = BN_new() + self.S = BN_new() + self.u = BN_new() + self.x = BN_new() + self.v = BN_new() + self.tmp1 = BN_new() + self.tmp2 = BN_new() + self.tmp3 = BN_new() + self.ctx = BN_CTX_new() + self.M = None + self.K = None + self.H_AMK = None + self._authenticated = False + + hash_class = _hash_map[ hash_alg ] + N,g,k = get_ngk( hash_class, ng_type, n_hex, g_hex, self.ctx ) + if k_hex is not None: + BN_hex2bn(k, k_hex) + + self.hash_class = hash_class + self.N = N + self.g = g + self.k = k + + if bytes_a: + bytes_to_bn( self.a, bytes_a ) + else: + BN_rand(self.a, 256, 0, 0) + + if bytes_A: + bytes_to_bn( self.A, bytes_A ) + else: + BN_set_flags(self.a, BN_FLG_CONSTTIME) + BN_mod_exp(self.A, g, self.a, N, self.ctx) + + + + def __del__(self): + if not hasattr(self, 'a'): + return # __init__ threw exception. no clean up required + BN_free(self.a) + BN_free(self.A) + BN_free(self.B) + BN_free(self.s) + BN_free(self.S) + BN_free(self.u) + BN_free(self.x) + BN_free(self.v) + BN_free(self.N) + BN_free(self.g) + BN_free(self.k) + BN_free(self.tmp1) + BN_free(self.tmp2) + BN_free(self.tmp3) + BN_CTX_free(self.ctx) + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.username + + + def get_ephemeral_secret(self): + return bn_to_bytes(self.a) + + + def get_session_key(self): + return self.K if self._authenticated else None + + + def start_authentication(self): + return (self.username, bn_to_bytes(self.A)) + + + # Returns M or None if SRP-6a safety check is violated + def process_challenge(self, bytes_s, bytes_B): + + hash_class = self.hash_class + N = self.N + g = self.g + k = self.k + + bytes_to_bn( self.s, bytes_s ) + bytes_to_bn( self.B, bytes_B ) + + # SRP-6a safety check + if BN_is_zero(self.B): + return None + + H_bn_bn(hash_class, self.u, self.A, self.B, width=BN_num_bytes(N)) + + # SRP-6a safety check + if BN_is_zero(self.u): + return None + + calculate_x( hash_class, self.x, self.s, self.username, self.password ) + + BN_mod_exp(self.v, g, self.x, N, self.ctx) + + # S = (B - k*(g^x)) ^ (a + ux) + + BN_mul(self.tmp1, self.u, self.x, self.ctx) + BN_add(self.tmp2, self.a, self.tmp1) # tmp2 = (a + ux) + BN_mod_exp(self.tmp1, g, self.x, N, self.ctx) + BN_mul(self.tmp3, k, self.tmp1, self.ctx) # tmp3 = k*(g^x) + BN_sub(self.tmp1, self.B, self.tmp3) # tmp1 = (B - K*(g^x)) + BN_mod_exp(self.S, self.tmp1, self.tmp2, N, self.ctx) + + self.K = hash_class( bn_to_bytes(self.S) ).digest() + self.M = calculate_M( hash_class, N, g, self.username, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK( hash_class, self.A, self.M, self.K ) + + return self.M + + + def verify_session(self, host_HAMK): + if self.H_AMK == host_HAMK: + self._authenticated = True + + + +#--------------------------------------------------------- +# Init +# +RAND_seed( os.urandom(32), 32 ) diff --git a/.venv/lib/python3.12/site-packages/srp/_pysrp.py b/.venv/lib/python3.12/site-packages/srp/_pysrp.py new file mode 100644 index 0000000..f4d57ef --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/_pysrp.py @@ -0,0 +1,441 @@ + # N A large safe prime (N = 2q+1, where q is prime) + # All arithmetic is done modulo N. + # g A generator modulo N + # k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) + # s User's salt + # I Username + # p Cleartext Password + # H() One-way hash function + # ^ (Modular) Exponentiation + # u Random scrambling parameter + # a,b Secret ephemeral values + # A,B Public ephemeral values + # x Private key (derived from p and s) + # v Password verifier + +import hashlib +import os +import binascii +import six + + +_rfc5054_compat = False +_no_username_in_x = False + +def rfc5054_enable(enable=True): + global _rfc5054_compat + _rfc5054_compat = enable + +def no_username_in_x(enable=True): + global _no_username_in_x + _no_username_in_x = enable + + +SHA1 = 0 +SHA224 = 1 +SHA256 = 2 +SHA384 = 3 +SHA512 = 4 + +NG_1024 = 0 +NG_2048 = 1 +NG_4096 = 2 +NG_8192 = 3 +NG_CUSTOM = 4 + +_hash_map = { SHA1 : hashlib.sha1, + SHA224 : hashlib.sha224, + SHA256 : hashlib.sha256, + SHA384 : hashlib.sha384, + SHA512 : hashlib.sha512 } + + +_ng_const = ( +# 1024-bit +('''\ +EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496\ +EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E\ +F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA\ +9AFD5138FE8376435B9FC61D2FC0EB06E3''', +"2"), +# 2048 +('''\ +AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ +A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ +95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ +747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ +8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ +60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ +FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''', +"2"), +# 4096 +('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199\ +FFFFFFFFFFFFFFFF''', +"5"), +# 8192 +('''\ +FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08\ +8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B\ +302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9\ +A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6\ +49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8\ +FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D\ +670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C\ +180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718\ +3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D\ +04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D\ +B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226\ +1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C\ +BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC\ +E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26\ +99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB\ +04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2\ +233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127\ +D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492\ +36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406\ +AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918\ +DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151\ +2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03\ +F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F\ +BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA\ +CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B\ +B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632\ +387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E\ +6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA\ +3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C\ +5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9\ +22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886\ +2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6\ +6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5\ +0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268\ +359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6\ +FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71\ +60C980DD98EDD3DFFFFFFFFFFFFFFFFF''', +'0x13') +) + +def get_ng( ng_type, n_hex, g_hex ): + if ng_type < NG_CUSTOM: + n_hex, g_hex = _ng_const[ ng_type ] + return int(n_hex,16), int(g_hex,16) + + +def bytes_to_long(s): + n = 0 + for b in six.iterbytes(s): + n = (n << 8) | b + return n + + +def long_to_bytes(n): + l = list() + x = 0 + off = 0 + while x != n: + b = (n >> off) & 0xFF + l.append( chr(b) ) + x = x | (b << off) + off += 8 + l.reverse() + return six.b(''.join(l)) + + +def get_random( nbytes ): + return bytes_to_long( os.urandom( nbytes ) ) + + +def get_random_of_length( nbytes ): + offset = (nbytes*8) - 1 + return get_random( nbytes ) | (1 << offset) + + +def old_H( hash_class, s1, s2 = '', s3=''): + if isinstance(s1, six.integer_types): + s1 = long_to_bytes(s1) + if s2 and isinstance(s2, six.integer_types): + s2 = long_to_bytes(s2) + if s3 and isinstance(s3, six.integer_types): + s3 = long_to_bytes(s3) + s = s1 + s2 + s3 + return long(hash_class(s).hexdigest(), 16) + + +def H( hash_class, *args, **kwargs ): + width = kwargs.get('width', None) + + h = hash_class() + + for s in args: + if s is not None: + data = long_to_bytes(s) if isinstance(s, six.integer_types) else s + if width is not None and _rfc5054_compat: + h.update( bytes(width - len(data))) + h.update( data ) + + return h.digest() + + + +#N = 0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73; +#g = 2; +#k = H(N,g) + +def HNxorg( hash_class, N, g ): + bin_N = long_to_bytes(N) + bin_g = long_to_bytes(g) + + padding = len(bin_N) - len(bin_g) if _rfc5054_compat else 0 + + hN = hash_class( bin_N ).digest() + hg = hash_class( b''.join( [b'\0'*padding, bin_g] ) ).digest() + + return six.b( ''.join( chr( six.indexbytes(hN, i) ^ six.indexbytes(hg, i) ) for i in range(0,len(hN)) ) ) + + + +def gen_x( hash_class, salt, username, password ): + username = username.encode() if hasattr(username, 'encode') else username + password = password.encode() if hasattr(password, 'encode') else password + if _no_username_in_x: + username = six.b('') + return bytes_to_long( H(hash_class, salt, H( hash_class, username + six.b(':') + password ) )) + + + + +def create_salted_verification_key( username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, salt_len=4, k_hex=None ): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + hash_class = _hash_map[ hash_alg ] + N,g = get_ng( ng_type, n_hex, g_hex ) + _s = long_to_bytes( get_random( salt_len ) ) + _v = long_to_bytes( pow(g, gen_x( hash_class, _s, username, password ), N) ) + + return _s, _v + + + +def calculate_M( hash_class, N, g, I, s, A, B, K ): + I = I.encode() if hasattr(I, 'encode') else I + h = hash_class() + h.update( HNxorg( hash_class, N, g ) ) + h.update( hash_class(I).digest() ) + if isinstance(s, six.integer_types): + s = long_to_bytes(s) + h.update( s ) + h.update( long_to_bytes(A) ) + h.update( long_to_bytes(B) ) + h.update( K ) + return h.digest() + + +def calculate_H_AMK( hash_class, A, M, K ): + h = hash_class() + h.update( long_to_bytes(A) ) + h.update( M ) + h.update( K ) + return h.digest() + + + + +class Verifier: + + def __init__(self, username, bytes_s, bytes_v, bytes_A=None, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, bytes_b=None, k_hex=None): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + if bytes_b and len(bytes_b) != 256: + raise ValueError("256 bytes required for bytes_b") + self.s = bytes_s + self.v = bytes_to_long(bytes_v) + self.I = username + self.K = None + self._authenticated = False + + self.safety_failed = False + + N,g = get_ng( ng_type, n_hex, g_hex ) + hash_class = _hash_map[ hash_alg ] + if k_hex is None: + k = bytes_to_long( H( hash_class, N, g, width=len(long_to_bytes(N)) )) + else: + k = int(k_hex, 16) + + self.hash_class = hash_class + self.N = N + self.g = g + self.k = k + + if bytes_A: + self._set_A(bytes_A) + + if not self.safety_failed: + if bytes_b: + self.b = bytes_to_long(bytes_b) + else: + self.b = get_random_of_length( 256 ) + self.B = (k*self.v + pow(g, self.b, N)) % N + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.I + + + def get_ephemeral_secret(self): + return long_to_bytes(self.b) + + + def get_session_key(self): + return self.K if self._authenticated else None + + # returns (bytes_s, bytes_B) on success, (None,None) if SRP-6a safety check fails + def get_challenge(self): + if self.safety_failed: + return None,None + else: + return (self.s, long_to_bytes(self.B)) + + # returns H_AMK on success, None on failure + def verify_session(self, user_M, bytes_A=None): + if bytes_A: + self._set_A(bytes_A) + if not hasattr(self, 'A'): + raise ValueError("bytes_A must be provided through Verifier constructor or verify_session parameter.") + if not self.safety_failed: + self._derive_H_AMK() + if user_M == self.M: + self._authenticated = True + return self.H_AMK + + + def _set_A(self, bytes_A): + self.A = bytes_to_long(bytes_A) + # SRP-6a safety check + self.safety_failed = self.A % self.N == 0 + + + def _derive_H_AMK(self): + self.u = bytes_to_long(H(self.hash_class, self.A, self.B, width=len(long_to_bytes(self.N)))) + self.S = pow(self.A*pow(self.v, self.u, self.N ), self.b, self.N) + self.K = self.hash_class( long_to_bytes(self.S) ).digest() + self.M = calculate_M( self.hash_class, self.N, self.g, self.I, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK( self.hash_class, self.A, self.M, self.K ) + + + + +class User: + def __init__(self, username, password, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None, bytes_a=None, bytes_A=None, k_hex=None): + if ng_type == NG_CUSTOM and (n_hex is None or g_hex is None): + raise ValueError("Both n_hex and g_hex are required when ng_type = NG_CUSTOM") + if bytes_a and len(bytes_a) != 256: + raise ValueError("256 bytes required for bytes_a") + N,g = get_ng( ng_type, n_hex, g_hex ) + hash_class = _hash_map[ hash_alg ] + if k_hex is None: + k = bytes_to_long(H( hash_class, N, g, width=len(long_to_bytes(N)) )) + else: + k = int(k_hex, 16) + + self.I = username + self.p = password + if bytes_a: + self.a = bytes_to_long(bytes_a) + else: + self.a = get_random_of_length( 256 ) + if bytes_A: + self.A = bytes_to_long(bytes_A) + else: + self.A = pow(g, self.a, N) + self.v = None + self.M = None + self.K = None + self.H_AMK = None + self._authenticated = False + + self.hash_class = hash_class + self.N = N + self.g = g + self.k = k + + + def authenticated(self): + return self._authenticated + + + def get_username(self): + return self.I + + + def get_ephemeral_secret(self): + return long_to_bytes(self.a) + + + def get_session_key(self): + return self.K if self._authenticated else None + + + def start_authentication(self): + return (self.I, long_to_bytes(self.A)) + + + # Returns M or None if SRP-6a safety check is violated + def process_challenge(self, bytes_s, bytes_B): + + self.s = bytes_s + self.B = bytes_to_long( bytes_B ) + + N = self.N + g = self.g + k = self.k + + hash_class = self.hash_class + + # SRP-6a safety check + if (self.B % N) == 0: + return None + + self.u = bytes_to_long(H( hash_class, self.A, self.B, width=len(long_to_bytes(N)) )) + + # SRP-6a safety check + if self.u == 0: + return None + + self.x = gen_x( hash_class, self.s, self.I, self.p ) + + self.v = pow(g, self.x, N) + + self.S = pow((self.B - k*self.v), (self.a + self.u*self.x), N) + + self.K = hash_class( long_to_bytes(self.S) ).digest() + self.M = calculate_M( hash_class, N, g, self.I, self.s, self.A, self.B, self.K ) + self.H_AMK = calculate_H_AMK(hash_class, self.A, self.M, self.K) + + return self.M + + + def verify_session(self, host_HAMK): + if self.H_AMK == host_HAMK: + self._authenticated = True diff --git a/.venv/lib/python3.12/site-packages/srp/doc/conf.py b/.venv/lib/python3.12/site-packages/srp/doc/conf.py new file mode 100644 index 0000000..ba75a8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/doc/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# Secure Remote Password documentation build configuration file, created by +# sphinx-quickstart on Fri Mar 25 10:20:52 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Secure Remote Password' +copyright = u'2011, Tom Cocagne' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'SecureRemotePassworddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'SecureRemotePassword.tex', u'Secure Remote Password Documentation', + u'Tom Cocagne', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'secureremotepassword', u'Secure Remote Password Documentation', + [u'Tom Cocagne'], 1) +] diff --git a/.venv/lib/python3.12/site-packages/srp/doc/index.rst b/.venv/lib/python3.12/site-packages/srp/doc/index.rst new file mode 100644 index 0000000..0c13606 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/doc/index.rst @@ -0,0 +1,22 @@ +.. Secure Remote Password documentation master file, created by + sphinx-quickstart on Fri Mar 25 10:20:52 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Secure Remote Password's documentation! +================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + srp.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/.venv/lib/python3.12/site-packages/srp/doc/srp.rst b/.venv/lib/python3.12/site-packages/srp/doc/srp.rst new file mode 100644 index 0000000..fee30df --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/doc/srp.rst @@ -0,0 +1,399 @@ +:mod:`srp` --- Secure Remote Password +===================================== + +.. module:: srp + :synopsis: Secure Remote Password + +.. moduleauthor:: Tom Cocagne + +.. sectionauthor:: Tom Cocagne + + +The Secure Remote Password protocol (SRP) is a cryptographically +strong authentication protocol for password-based, mutual +authentication over an insecure network connection. Successful SRP +authentication requires both sides of the connection to have knowledge +of the user's password. In addition to password verification, the SRP +protocol also performs a secure key exchange during the authentication +process. This key may be used to protect network traffic via symmetric +key encryption. + +SRP offers security and deployment advantages over other +challenge-response protocols, such as Kerberos and SSL, in that it +does not require trusted key servers or certificate infrastructures. +Instead, small verification keys derived from each user's password are +stored and used by each SRP server application. SRP provides a +near-ideal solution for many applications requiring simple and secure +password authentication that does not rely on an external +infrastructure. + +Another favorable aspect of the SRP protocol is that compromized +verification keys are of little value to an attacker. Possesion of a +verification key does not allow a user to be impersonated +and it cannot be used to obtain the users password except by way of a +computationally infeasible dictionary attack. A compromized key would, +however, allow an attacker to impersonate the server side of an SRP +authenticated connection. Consequently, care should be taken to +prevent unauthorized access to verification keys for applications in +which the client side relies on the server being genuine. + + + +Usage +----- + +SRP usage begins with *create_salted_verification_key()*. This function +creates a salted verification key from the user's password. The resulting salt +and key are stored by the server application and will be used during the +authentication process. + +The authentication process occurs as an exchange of messages between the clent +and the server. The :ref:`example` below provides a simple demonstration of the +protocol. A comprehensive description of the SRP protocol is contained in the +:ref:`protocol-description` section. + +The *User* & *Verifier* constructors, as well as the +*create_salted_verification_key()* function, accept optional arguments +to specify which hashing algorithm and prime number arguments should +be used during the authentication process. These options may be used +to tune the security/performance tradeoff for an application. +Generally speaking, specifying arguments with a higher number of bits +will result in a greater level of security. However, it will come at +the cost of increased computation time. The default values of SHA1 +hashes and 2048 bit prime numbers strike a good balance between +performance and security. These values should be sufficient for most +applications. Regardless of which values are used, the parameters +passed to the *User* and *Verifier* constructors must exactly match +those passed to *create_salted_verification_key()* + + +.. _constants: + +Constants +--------- + +.. table:: Hashing Algorithm Constants + + ============== ============== + Hash Algorithm Number of Bits + ============== ============== + SHA1 160 + SHA224 224 + SHA256 256 + SHA384 384 + SHA512 512 + ============== ============== + +.. note:: + + Larger hashing algorithms will result in larger session keys. + +.. table:: Prime Number Constants + + ================= ============== + Prime Number Size Number of Bits + ================= ============== + NG_1024 1024 + NG_2048 2048 + NG_4096 4096 + NG_8192 8192 + NG_CUSTOM User Supplied + ================= ============== + +.. note:: + + If NG_CUSTOM is used, the 'n_hex' and 'g_hex' parameters are required. + These parameters must be ASCII text containing hexidecimal notation of the + prime number 'n_hex' and the corresponding generator number 'g_hex'. Appendix + A of RFC 5054 contains several large prime number, generator pairs that may + be used with NG_CUSTOM. + +Functions +--------- + +.. function:: create_salted_verification_key ( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) + + *username* Name of the user + + *password* Plaintext user password + + *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. + + Generate a salted verification key for the given username and password and return the tuple: + (salt_bytes, verification_key_bytes) + + +.. function:: rfc5054_enable( enable=True ) + + *enable* True if compatibility with RFC5054 is required, False otherwise. + + For backward compatibility, pysrp by default does not conform to RFC5054. If you need compatibility + with RFC5054, just call this function before using pysrp. + +:class:`Verifier` Objects +------------------------- + +A :class:`Verifier` object is used to verify the identity of a remote +user. + +.. note:: + + The standard SRP 6 protocol allows only one password attempt per + connection. + +.. class:: Verifier( username, bytes_s, bytes_v[, bytes_A=None, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) + + *username* Name of the remote user being authenticated. + + *bytes_s* Salt generated by :func:`create_salted_verification_key`. + + *bytes_v* Verification Key generated by :func:`create_salted_verification_key`. + + *bytes_A* Challenge from the remote user. Generated by + :meth:`User.start_authentication`. Useful when user generates A + before verifier generates B. If following RFC5054 section 2.2 where + B is generated prior to A, then A can instead later be provided to + Verifier via :func:`Verifier.verify_session`. + + *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. + + .. method:: Verifier.authenticated() + + Return True if the authentication succeeded. False + otherwise. + + .. method:: Verifier.get_username() + + Return the name of the user this :class:`Verifier` object is for. + + .. method:: Verifier.get_session_key() + + Return the session key for an authenticated user or None if the + authentication failed or has not yet completed. + + .. method:: Verifier.get_challenge() + + Return (bytes_s, bytes_B) on success or (None, None) if + authentication has failed. + + .. method:: Verifier.verify_session( user_M[, user_A=None] ) + + Complete the :class:`Verifier` side of the authentication process. + If A was generated after B as in RFC5054 Section 2.2, then bytes_A + can be provided here. If the authentication succeeded, bytes_H_AMK + should be returned to the remote user. On failure, this method + returns None. + + +:class:`User` Objects +------------------------- + +A :class:`User` object is used to prove a user's identity to a remote :class:`Verifier` and +verifiy that the remote :class:`Verifier` knows the verification key associated with +the user's password. + +.. class:: User( username, password[, hash_alg=SHA1, ng_type=NG_2048, n_hex=None, g_hex=None] ) + + *username* Name of the user being authenticated. + + *password* Password for the user. + + *hash_alg*, *ng_type*, *n_hex*, *g_hex* Refer to the :ref:`constants` section. + + .. method:: User.authenticated() + + Return True if authentication succeeded. False + otherwise. + + .. method:: User.get_username() + + Return the username passed to the constructor. + + .. method:: User.get_session_key() + + Return the session key if authentication succeeded or None if the + authentication failed or has not yet completed. + + .. method:: User.start_authentication() + + Return (username, bytes_A). These should be passed to the + constructor of the remote :class:`Verifer` + + .. method:: User.process_challenge( bytes_s, bytes_B ) + + Processe the challenge returned + by :meth:`Verifier.get_challenge` on success this method + returns bytes_M that should be sent + to :meth:`Verifier.verify_session` if authentication failed, + it returns None. + + .. method:: User.verify_session( bytes_H_AMK ) + + Complete the :class:`User` side of the authentication process. By + verifying the *bytes_H_AMK* value returned by + :meth:`Verifier.verify_session`. If the authentication succeded + :meth:`authenticated` will return True + +.. _example: + +Example +------- + +Simple Usage Example:: + + import srp + + # The salt and verifier returned from srp.create_salted_verification_key() should be + # stored on the server. + salt, vkey = srp.create_salted_verification_key( 'testuser', 'testpassword' ) + + class AuthenticationFailed (Exception): + pass + + # ~~~ Begin Authentication ~~~ + + usr = srp.User( 'testuser', 'testpassword' ) + uname, A = usr.start_authentication() + + # The authentication process can fail at each step from this + # point on. To comply with the SRP protocol, the authentication + # process should be aborted on the first failure. + + # Client => Server: username, A + svr = srp.Verifier( uname, salt, vkey, A ) + s,B = svr.get_challenge() + + if s is None or B is None: + raise AuthenticationFailed() + + # Server => Client: s, B + M = usr.process_challenge( s, B ) + + if M is None: + raise AuthenticationFailed() + + # Client => Server: M + HAMK = svr.verify_session( M ) + + if HAMK is None: + raise AuthenticationFailed() + + # Server => Client: HAMK + usr.verify_session( HAMK ) + + # At this point the authentication process is complete. + + assert usr.authenticated() + assert svr.authenticated() + + + +Implementation Notes +-------------------- + +This implementation of SRP consists of both a pure-python module and a C-based +implementation that is approximately 10x faster. By default, the +C-implementation will be used if it is available. An additional benefit of the C +implementation is that it can take advantage of of multiple CPUs. For cases in +which the number of connections per second is an issue, using a small pool of +threads to perform the authentication steps on multi-core systems will yield a +substantial performance increase. + + +.. _protocol-description: + +SRP 6a Protocol Description +--------------------------- + +The original SRP protocol, known as SRP-3, is defined in +RFC 2945. This implementation, however, uses SRP-6a which is a slight +improvement over SRP-3. The authoritative definition for the SRP-6a +protocol is available at http://srp.stanford.edu. An additional +resource is RFC 5054 which covers the integration of SRP into +TLS. This RFC is the source of hashing strategy and the predefined N +and g constants used in this implementation. + +The following is a complete description of the SRP-6a protocol as implemented by +this library. Note that the ^ symbol indicates exponentiaion and the | symbol +indicates concatenation. + +.. rubric:: Primary Variables used in SRP 6a + +========= ================================================================= +Variables Description +========= ================================================================= +N A large, safe prime (N = 2q+1, where q is a Sophie Germain prime) + All arithmetic is performed in the field of integers modulo N +g A generator modulo N +s Small salt for the verification key +I Username +p Cleartext password +H() One-way hash function +a,b Secret, random values +K Session key +========= ================================================================= + + +.. rubric:: Derived Values used in SRP 6a + +====================================== ==================================== +Derived Values Description +====================================== ==================================== +k = H(N,g) Multiplier Parameter +A = g^a Public ephemeral value +B = kv + g^b Public ephemeral value +x = H(s, H( I | ':' | p )) Private key (as defined by RFC 5054) +v = g^x Password verifier +u = H(A,B) Random scrambling parameter +M = H(H(N) xor H(g), H(I), s, A, B, K) Session key verifier +====================================== ==================================== + + +.. rubric:: Protocol Description + +The server stores the password verifier *v*. Authentication begins with a +message from the client:: + + client -> server: I, [A = g^a] + +where public ephemeral key *A* may be provided at this point or later +as part of verification. The server replies with the verifier salt and +challenge:: + + server -> client: s, B = kv + g^b, [N, g] + +where *N* and *g* may be provided by the server or hardcoded in the +user client. At this point, both the client and server calculate the +shared session key:: + + client & server: u = H(A,B) + +:: + + server: K = H( (Av^u) ^ b ) + +:: + + client: x = H( s, H( I + ':' + p ) ) + client: K = H( (B - kg^x) ^ (a + ux) ) + +Now both parties have a shared, strong session key *K*. To complete +authentication they need to prove to each other that their keys match:: + + client -> server: M = H(H(N) xor H(g), H(I), s, A, B, K), [A = g^a] + server -> client: H(A, M, K) + +Note that *A* is only provided here if it was not provided during the initial authentication request. + +SRP 6a requires the two parties to use the following safeguards (all of which this library automatically checks): + +1. The client will abort if it recieves B == 0 (mod N) or u == 0 +2. The server will abort if it detects A == 0 (mod N) +3. The client must show its proof of K first. If the server detects that this + proof is incorrect it must abort without showing its own proof of K + +Additionally, if the server provides N and g to the user, the user +should verify the safe prime bit length is as expected, that its +highest bit is 1 to ensure it is large, and that it is indeed a safe +prime with the expected generator. These N and g checks are not +currently provided by this library. diff --git a/.venv/lib/python3.12/site-packages/srp/test_srp.py b/.venv/lib/python3.12/site-packages/srp/test_srp.py new file mode 100644 index 0000000..af62603 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/srp/test_srp.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python + +import unittest +import os.path +import os +import sys +import time +import _thread + +this_dir = os.path.dirname( os.path.abspath(__file__) ) + +build_dir = os.path.join( os.path.dirname(this_dir), 'build' ) + +if not os.path.exists( build_dir ): + print('Please run "python setup.py build" prior to running tests') + sys.exit(1) + +plat_dirs = [ d for d in os.listdir('build') if d.startswith('lib') ] + +if not len(plat_dirs) == 1: + print('Unexpected build result... aborting') + +plat_dir = os.path.join( build_dir, plat_dirs[0] ) + +sys.path.insert(0, os.path.join('build', plat_dir) ) + + + +import srp +import srp._pysrp as _pysrp +import srp._ctsrp as _ctsrp + + +test_g_hex = b"2" +test_n_hex = ('''\ +AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4\ +A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60\ +95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF\ +747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907\ +8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861\ +60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB\ +FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73''').encode('ascii') + + +class SRPTests( unittest.TestCase ): + + def doit(self, u_mod, v_mod, g_mod, hash_alg=srp.SHA1, ng_type=srp.NG_2048, n_hex='', g_hex='', k_hex=None): + User = u_mod.User + Verifier = v_mod.Verifier + create_salted_verification_key = g_mod.create_salted_verification_key + + username = b'testuser' + password = b'testpassword' + + _s, _v = create_salted_verification_key( username, password, hash_alg, ng_type, n_hex, g_hex ) + + usr = User( username, password, hash_alg, ng_type, n_hex, g_hex, None, None, k_hex ) + bytes_a = usr.get_ephemeral_secret() + uname, A = usr.start_authentication() + + # Make sure a recreated User does all the same appropriate things + usr2 = User( username, password, hash_alg, ng_type, n_hex, g_hex, bytes_a, None, k_hex ) + self.assertEqual(bytes_a, usr2.get_ephemeral_secret()) + uname2, A2 = usr2.start_authentication() + self.assertEqual(uname, uname2) + self.assertEqual(A, A2) + + # username, A => server + svr = Verifier( uname, _s, _v, A, hash_alg, ng_type, n_hex, g_hex, None, k_hex ) + bytes_b = svr.get_ephemeral_secret() + s,B = svr.get_challenge() + + # s,B => client + M = usr.process_challenge( s, B ) + M2 = usr2.process_challenge( s, B ) + self.assertEqual(M, M2) + + # M => server + HAMK = svr.verify_session( M ) + + # Make sure that a recreated Verifier will authenticate appropriately + svr2 = Verifier( uname, _s, _v, A, hash_alg, ng_type, n_hex, g_hex, bytes_b, k_hex ) + self.assertEqual(bytes_b, svr2.get_ephemeral_secret()) + HAMK2 = svr2.verify_session( M ) + self.assertEqual(HAMK, HAMK2) + + # Make sure Verifier generating B before receiving A will not change authentication. + svr3 = Verifier ( uname, _s, _v, None, hash_alg, ng_type, n_hex, g_hex, bytes_b, k_hex ) + self.assertEqual(bytes_b, svr3.get_ephemeral_secret()) + HAMK3 = svr3.verify_session( M, A ) + self.assertEqual(HAMK, HAMK3) + + # HAMK => client + usr.verify_session( HAMK ) + usr2.verify_session( HAMK ) + + self.assertTrue( svr.authenticated() ) + self.assertTrue( svr2.authenticated() ) + self.assertTrue( svr3.authenticated() ) + + self.assertTrue( usr.authenticated() ) + self.assertTrue( svr2.authenticated() ) + + def test_pure_python_defaults(self): + self.doit( _pysrp, _pysrp, _pysrp ) + + def test_ctypes_defaults(self): + self.doit( _ctsrp, _ctsrp, _ctsrp ) + + def test_mix1(self): + self.doit( _pysrp, _ctsrp, _ctsrp ) + + def test_mix2(self): + self.doit( _pysrp, _pysrp, _ctsrp ) + + def test_mix3(self): + self.doit( _ctsrp, _pysrp, _pysrp ) + + def test_mix4(self): + self.doit( _ctsrp, _ctsrp, _pysrp ) + + + def test_hash_SHA512(self): + self.doit( _ctsrp, _ctsrp, _ctsrp, hash_alg=srp.SHA512 ) + + def test_NG_8192(self): + self.doit( _ctsrp, _ctsrp, _ctsrp, ng_type=srp.NG_8192 ) + + def test_NG_CUSTOM(self): + self.doit( _ctsrp, _ctsrp, _ctsrp, ng_type=srp.NG_CUSTOM, n_hex=test_n_hex, g_hex=test_g_hex ) + + def test_all1(self): + self.doit( _ctsrp, _pysrp, _ctsrp, hash_alg=srp.SHA256, ng_type=srp.NG_CUSTOM, n_hex=test_n_hex, g_hex=test_g_hex ) + + def test_all2(self): + self.doit( _ctsrp, _pysrp, _ctsrp, hash_alg=srp.SHA224, ng_type=srp.NG_4096 ) + + def test_random_of_length(self): + """ + Verify that the Python implementation guarantees byte length by + setting most significant bit to 1 + """ + for x in range(10): + val = _pysrp.get_random_of_length(32) + self.assertTrue(val >> 255 == 1) + + def test_ephemeral_length(self): + """ + Verify that all implementations require 32 bytes for ephemeral values + """ + random31 = _pysrp.long_to_bytes(_pysrp.get_random_of_length(31)) + random33 = _pysrp.long_to_bytes(_pysrp.get_random_of_length(33)) + + def verf_len(mod, val): + with self.assertRaises(ValueError) as ctx: + mod.User('uname', 'pwd', bytes_a=val) + self.assertIn('bytes_a', str(ctx.exception) ) + + with self.assertRaises(ValueError) as ctx: + mod.Verifier('uname', random31, random31, random31, bytes_b=val) + self.assertIn('bytes_b', str(ctx.exception) ) + + for mod in [_ctsrp, _pysrp]: + for val in [random31, random33]: + verf_len(mod, val) + + def test_authenticated_on_init(self): + usr = _pysrp.User('test', 'test') + self.assertTrue(not usr.authenticated()) + + usr = _ctsrp.User('test', 'test') + self.assertTrue(not usr.authenticated()) + + def test_custom_k(self): + self.doit( _ctsrp, _pysrp, _ctsrp, k_hex=b'5') + + def test_verifier_requires_A(self): + randbytes = _pysrp.long_to_bytes(_pysrp.get_random_of_length(32)) + + for mod in [_ctsrp, _pysrp]: + svr = mod.Verifier('uname', randbytes, randbytes) + with self.assertRaises(ValueError) as ctx: + svr.verify_session(randbytes) + self.assertIn('bytes_A', str(ctx.exception)) + + +#----------------------------------------------------------------------------------- +# Performance Testing +# +hash_map = { 0 : 'SHA1 ', 1 : 'SHA224', 2 : 'SHA256', 3 : 'SHA384', 4 : 'SHA512' } +prime_map = { 0 : 1024, 1 : 2048, 2 : 4096, 3 : 8192 } + +username = b'testuser' +password = b'testpassword' + +NLEFT = 0 + +def do_auth( mod, hash_alg, ng_type, _s, _v ): + + usr = mod.User( username, password, hash_alg, ng_type) + uname, A = usr.start_authentication() + + # username, A => server + svr = mod.Verifier( uname, _s, _v, A, hash_alg, ng_type) + s,B = svr.get_challenge() + + # s,B => client + M = usr.process_challenge( s, B ) + + # M => server + HAMK = svr.verify_session( M ) + + # HAMK => client + usr.verify_session( HAMK ) + + if not svr.authenticated() or not usr.authenticated(): + raise Exception('Authentication failed!') + + +def performance_test( mod, hash_alg, ng_type, niter=10, nthreads=1 ): + global NLEFT + _s, _v = srp.create_salted_verification_key( username, password, hash_alg, ng_type ) + + NLEFT = niter + + def test_thread(): + global NLEFT + while NLEFT > 0: + do_auth( mod, hash_alg, ng_type, _s, _v ) + NLEFT -= 1 + + start = time.time() + while nthreads > 1: + _thread.start_new_thread( test_thread, () ) + nthreads -= 1 + + test_thread() + duration = time.time() - start + + return duration + + +def get_param_str( mod, hash_alg, ng_type ): + + m = { 'srp._pysrp' : 'Python', + 'srp._ctsrp' : 'ctypes' } + + cfg = '%s, %s, %d:' % (m[mod.__name__], hash_map[hash_alg], prime_map[ng_type]) + + return cfg + + +def param_test( mod, hash_alg, ng_type, niter=10 ): + duration = performance_test( mod, hash_alg, ng_type, niter ) + cfg = get_param_str( mod, hash_alg, ng_type ) + print(' ', cfg.ljust(20), '%.6f' % (duration/niter)) + return duration/niter + + +def print_default_timings(): + print('*'*60) + print('Default Parameter Timings:') + py_time = param_test( _pysrp, srp.SHA1, srp.NG_2048 ) + ct_time = param_test( _ctsrp, srp.SHA1, srp.NG_2048 ) + print('') + print('Performance increases: ') + print(' ctypes-module : ', py_time/ct_time) + + +def print_performance_table(): + ng_types = [ srp.NG_1024, srp.NG_2048, srp.NG_4096, srp.NG_8192 ] + hash_types = [ srp.SHA1, srp.SHA224, srp.SHA256, srp.SHA384, srp.SHA512 ] + + print('*'*60) + print('Hash Algorithm vs Prime Number performance table') + print('') + print(' |', end=' ') + for ng in ng_types: + print(('NG_%d' % prime_map[ng]).rjust(12), end=' ') + print('') + print('-'*60) + + for hash_alg in hash_types: + + print('%s |' % hash_map[hash_alg], end=' ') + for ng in ng_types: + print('{0:>12f}'.format(performance_test(_ctsrp, hash_alg, ng) / 10), end=' ') + print('') + + +def print_thread_performance(): + print('*'*60) + print('Thread Performance Test:') + niter = 100 + for nthreads in range(1,11): + print(' Thread Count {0:>2}: {1:8f}'.format(nthreads, performance_test(_ctsrp, srp.SHA1, srp.NG_2048, niter, nthreads)/niter)) + + +print('*'*60) +print('*') +print('* Testing Implementation') +print('*') +suite = unittest.TestLoader().loadTestsFromTestCase(SRPTests) +unittest.TextTestRunner(verbosity=1).run(suite) + +print('*'*60) +print('*') +print('* Performance Testing') +print('*') +print_thread_performance() +print_performance_table() +print_default_timings() +#--------------------------------------------------------------- + +# Pause briefly to ensure no background threads are still executing +time.sleep(0.1) diff --git a/.venv/lib/python3.12/site-packages/tests/__init__.py b/.venv/lib/python3.12/site-packages/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/conftest.py b/.venv/lib/python3.12/site-packages/tests/conftest.py new file mode 100644 index 0000000..9cced81 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/conftest.py @@ -0,0 +1,59 @@ +import re + +import pytest + +from sanic import Sanic + +from sanic_ext import Extend, Extension +from sanic_ext.extensions.http.extension import HTTPExtension +from sanic_ext.extensions.injection.extension import InjectionExtension +from sanic_ext.extensions.openapi.builders import ( + OperationStore, + SpecificationBuilder, +) +from sanic_ext.extensions.openapi.extension import OpenAPIExtension + + +slugify = re.compile(r"[^a-zA-Z0-9_\-]") + + +@pytest.fixture(autouse=True) +def reset_globals(): + yield + SpecificationBuilder.reset() + OperationStore.reset() + Extend.reset() + Extension.reset() + Sanic._app_registry.clear() + + +@pytest.fixture(autouse=True) +def reset_extensions(): + yield + for ext in (HTTPExtension, InjectionExtension, OpenAPIExtension): + ext._singleton = None + + +@pytest.fixture +def bare_app(request): + app = Sanic(slugify.sub("-", request.node.name)) + + yield app + + +@pytest.fixture +def app(bare_app): + Extend(bare_app) + + yield bare_app + + +@pytest.fixture +def get_docs(app): + def fetch(): + nonlocal app + + _, response = app.test_client.get("/docs/openapi.json") + return response.json + + return fetch diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/http/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/http/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/http/test_methods.py b/.venv/lib/python3.12/site-packages/tests/extensions/http/test_methods.py new file mode 100644 index 0000000..a569332 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/http/test_methods.py @@ -0,0 +1,129 @@ +import pytest + +from sanic import Sanic +from sanic.response import empty, text + +from sanic_ext.bootstrap import Extend + + +def test_trace_and_connect_available(app: Sanic): + @app.route("/", methods=["trace", "connect"]) + async def handler(_): + return empty() + + _, response = app.test_client.request("", http_method="trace") + assert response.status == 204 + _, response = app.test_client.request("", http_method="connect") + assert response.status == 204 + _, response = app.test_client.request("", http_method="get") + assert response.status == 405 + + +def test_auto_head(app: Sanic, get_docs): + app.config.TOUCHUP = False + + @app.get("/foo") + async def foo_handler(_): + return text("...") + + assert app.config.HTTP_AUTO_HEAD + _, response = app.test_client.head("/foo") + assert response.status == 200 + assert len(response.body) == 0 + assert int(response.headers["content-length"]) == 3 + + schema = get_docs() + assert "get" in schema["paths"]["/foo"] + + +def test_auto_options(app: Sanic, get_docs): + @app.post("/foo") + async def foo_handler(_): + return text("...") + + _, response = app.test_client.options("/foo") + assert response.status == 204 + assert len(response.body) == 0 + assert "POST" in response.headers["allow"] + assert "OPTIONS" in response.headers["allow"] + + schema = get_docs() + assert "post" in schema["paths"]["/foo"] + + +def test_auto_trace(bare_app: Sanic): + Extend(bare_app, config={"http_auto_trace": True}) + + @bare_app.get("/foo") + async def foo_handler(_): + return text("...") + + request, response = bare_app.test_client.request( + "/foo", http_method="trace" + ) + assert response.status == 200 + assert response.body.startswith(request.head) + + +def test_auto_head_with_vhosts(app: Sanic, get_docs): + @app.get("/foo", host="one.com", name="one") + async def foo_handler_one(_): + return text(".") + + @app.get("/foo", host="two.com", name="two") + async def foo_handler_two(_): + return text("..") + + assert app.config.HTTP_AUTO_HEAD + _, response = app.test_client.head("/foo", headers={"host": "one.com"}) + assert response.status == 200 + assert len(response.body) == 0 + assert int(response.headers["content-length"]) == 1 + + _, response = app.test_client.head("/foo", headers={"host": "two.com"}) + assert response.status == 200 + assert len(response.body) == 0 + assert int(response.headers["content-length"]) == 2 + + schema = get_docs() + assert "get" in schema["paths"]["/foo"] + + +def test_auto_options_with_vhosts(app: Sanic, get_docs): + @app.post("/foo", host="one.com", name="one") + async def foo_handler_one(_): + return text(".") + + @app.post("/foo", host="two.com", name="two") + async def foo_handler_two(_): + return text("..") + + assert app.config.HTTP_AUTO_OPTIONS + _, response = app.test_client.options("/foo", headers={"host": "one.com"}) + assert response.status == 204 + assert len(response.body) == 0 + assert "POST" in response.headers["allow"] + assert "OPTIONS" in response.headers["allow"] + + _, response = app.test_client.options("/foo", headers={"host": "two.com"}) + assert response.status == 204 + assert len(response.body) == 0 + assert "POST" in response.headers["allow"] + assert "OPTIONS" in response.headers["allow"] + + +# This test also appears in Core Sanic tests but is added here as well +# because of: https://github.com/sanic-org/sanic-ext/issues/148 +@pytest.mark.parametrize("unquote", [True, False, None]) +def test_unquote_add_route(app, unquote): + async def handler1(_, foo): + return text(foo) + + app.add_route(handler1, "/", unquote=unquote) + value = "啊" if unquote else r"%E5%95%8A" + + _, response = app.test_client.get("/啊") + assert response.text == value + + _, response = app.test_client.get(r"/%E5%95%8A") + assert response.text == value diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_add_dependency.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_add_dependency.py new file mode 100644 index 0000000..ed38ff8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_add_dependency.py @@ -0,0 +1,416 @@ +from __future__ import annotations + +import asyncio + +from dataclasses import dataclass +from itertools import count +from typing import Optional +from uuid import UUID + +import pytest + +from sanic import Request, json, text +from sanic.exceptions import SanicException +from sanic.views import HTTPMethodView + +from sanic_ext import Extend + + +@dataclass +class Name: + name: str + + +@dataclass +class PersonID: + person_id: int + + +@dataclass +class NamedPerson: + name: str + + +@dataclass +class Person: + person_id: PersonID + name: str + age: int + + @classmethod + async def create(cls, person_id: int) -> Person: + return cls(person_id=PersonID(person_id), name="noname", age=111) + + +@dataclass +class AsyncName: + name: str + + def __await__(self): + return self + + +counter = count() + + +class A: + @classmethod + def create(cls, request: Request): + next(counter) + return cls() + + +class B: + def __init__(self, a: A): + self.a = a + + @classmethod + def create(cls, request: Request, a: A): + next(counter) + return cls(a) + + +class C: + def __init__(self, b: B): + self.b = b + + @classmethod + def create(cls, request: Request, b: B): + next(counter) + return cls(b) + + +class D: + def __init__(self, a: A): + self.a = a + + @classmethod + def create(cls, request: Request, a: A): + next(counter) + return cls(a) + + +class E: + def __init__(self, c: C, d: D): + self.c = c + self.d = d + + @classmethod + def create(cls, request: Request, c: C, d: D): + next(counter) + return cls(c, d) + + +class Alpha: ... + + +class Beta: + def __init__(self, alpha: Alpha) -> None: + self.alpha = alpha + + +class Gamma: + def __init__(self, beta: Beta, request: Optional[Request] = None) -> None: + self.beta = beta + self.request = request + + +class Delta: + def __init__(self, gamma: Gamma, request: Request) -> None: + self.gamma = gamma + self.request = request + + +@dataclass +class Foo: + ident: UUID + + +def make_gamma_with_request(beta: Beta, request: Request): + return Gamma(beta, request) + + +def make_gamma_without_request(beta: Beta): + return Gamma(beta) + + +def test_injection_not_allowed_when_ext_disabled(bare_app): + ext = Extend(bare_app, built_in_extensions=False) + + with pytest.raises( + SanicException, match="Injection extension not enabled" + ): + ext.add_dependency(1, 2) + + +def test_injection_of_matched_object(app): + @app.get("/person/") + def handler(request, name: Name): + request.ctx.name = name + return text(name.name) + + app.ext.add_dependency(Name) + + request, response = app.test_client.get("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.name, Name) + assert request.ctx.name.name == "george" + + +def test_injection_of_matched_object_as_deprecated_injection(app): + @app.get("/person/") + def handler(request, name: Name): + request.ctx.name = name + return text(name.name) + + message = ( + "The 'ext.injection' method has been deprecated and will be removed " + "in v22.6. Please use 'ext.add_dependency' instead." + ) + with pytest.warns(DeprecationWarning, match=message): + app.ext.injection(Name) + + request, response = app.test_client.get("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.name, Name) + assert request.ctx.name.name == "george" + + +def test_injection_of_simple_object(app): + @app.get("/person/") + def handler(request, name: str, person: NamedPerson): + request.ctx.person = person + return text(person.name) + + app.ext.add_dependency(NamedPerson) + + request, response = app.test_client.get("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.person, NamedPerson) + assert request.ctx.person.name == "george" + + +def test_injection_of_object_with_constructor(app): + @app.get("/person/") + async def person_details(request, person_id: PersonID, person: Person): + request.ctx.person_id = person_id + request.ctx.person = person + return text( + f"{person.person_id.person_id}\n{person.name}\n{person.age}" + ) + + app.ext.add_dependency(Person, Person.create) + app.ext.add_dependency(PersonID) + + request, response = app.test_client.get("/person/999") + + assert response.body == b"999\nnoname\n111" + assert isinstance(request.ctx.person_id, PersonID) + assert isinstance(request.ctx.person, Person) + assert request.ctx.person.person_id == request.ctx.person_id + assert request.ctx.person.person_id.person_id == 999 + assert request.ctx.person.name == "noname" + assert request.ctx.person.age == 111 + + +def test_injection_on_cbv(app): + class View(HTTPMethodView, attach=app, uri="/person/"): + async def get(self, request, name: Name): + request.ctx.name = name + return text(name.name) + + @staticmethod + async def post(request, name: Name): + request.ctx.name = name + return text(name.name) + + app.ext.add_dependency(Name) + + for client in (app.test_client.get, app.test_client.post): + request, response = client("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.name, Name) + assert request.ctx.name.name == "george" + + +def test_nested_dependencies(app): + app.ext.add_dependency(A, A.create) + app.ext.add_dependency(B, B.create) + app.ext.add_dependency(C, C.create) + app.ext.add_dependency(D, D.create) + app.ext.add_dependency(E, E.create) + + @app.get("/") + async def nested(request: Request, c: C, e: E): + return json( + [ + isinstance(c, C), + isinstance(c.b, B), + isinstance(c.b.a, A), + isinstance(e, E), + isinstance(e.c, C), + isinstance(e.d, D), + isinstance(e.d.a, A), + ] + ) + + _, response = app.test_client.get("/") + + assert all(response.json) + # TODO: + # - After implementing https://github.com/sanic-org/sanic-ext/issues/77 + # this should be == 5 + assert next(counter) == 9 + + +def test_injection_on_websocket(app): + ev = asyncio.Event() + + app.ext.dependency(A()) + + @app.websocket("/foo") + async def handler(request, ws, foo: A): + assert isinstance(foo, A) + if isinstance(foo, A): + ev.set() + + request, response = app.test_client.websocket("/foo") + assert ev.is_set() + + +def test_injection_of_awaitable_variable_in_do_cast(app): + """Test for do_cast() iscoroutine() check""" + + @app.get("/person/") + def handler(request, name: AsyncName): + request.ctx.name = name + return text(name.name) + + app.ext.add_dependency(AsyncName) + + request, response = app.test_client.get("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.name, AsyncName) + assert request.ctx.name.name == "george" + + +def test_injection_of_awaitable_variable_in_call(app): + """Test for __call__() iscoroutine() check""" + + @app.get("/person/") + def handler(request, name: AsyncName): + request.ctx.name = name + return text(name.name) + + def test(): + return AsyncName("george") + + app.ext.dependency(test()) + + request, response = app.test_client.get("/person/george") + + assert response.body == b"george" + assert isinstance(request.ctx.name, AsyncName) + assert request.ctx.name.name == "george" + + +def test_injection_class_constructors(app): + app.ext.add_dependency(Alpha) + app.ext.add_dependency(Beta) + + @app.get("/") + def handler(request: Request, beta: Beta): + return json({"is_beta": isinstance(beta, Beta)}) + + _, response = app.test_client.get("/") + assert response.json == {"is_beta": True} + + +def test_injection_class_constructors_with_optional_request(app): + app.ext.add_dependency(Alpha) + app.ext.add_dependency(Beta) + app.ext.add_dependency(Gamma) + + @app.get("/") + def handler(request: Request, gamma: Gamma): + return json( + { + "is_gamma": isinstance(gamma, Gamma), + "has_request": bool(gamma.request), + } + ) + + _, response = app.test_client.get("/") + assert response.json == {"is_gamma": True, "has_request": True} + + +def test_injection_class_constructors_with_request(app): + app.ext.add_dependency(Alpha) + app.ext.add_dependency(Beta) + app.ext.add_dependency(Gamma) + app.ext.add_dependency(Delta) + + @app.get("/") + def handler(request: Request, delta: Delta): + return json( + { + "is_delta": isinstance(delta, Delta), + "has_request": bool(delta.request), + } + ) + + _, response = app.test_client.get("/") + assert response.json == {"is_delta": True, "has_request": True} + + +def test_injection_class_constructors_with_func_and_request(app): + app.ext.add_dependency(Alpha) + app.ext.add_dependency(Beta) + app.ext.add_dependency(Gamma, make_gamma_with_request) + + @app.get("/") + def handler(request: Request, gamma: Gamma): + return json( + { + "is_gamma": isinstance(gamma, Gamma), + "has_request": bool(gamma.request), + } + ) + + _, response = app.test_client.get("/") + assert response.json == {"is_gamma": True, "has_request": True} + + +def test_injection_class_constructors_with_func_and_no_request(app): + app.ext.add_dependency(Alpha) + app.ext.add_dependency(Beta) + app.ext.add_dependency(Gamma, make_gamma_without_request) + + @app.get("/") + def handler(request: Request, gamma: Gamma): + return json( + { + "is_gamma": isinstance(gamma, Gamma), + "has_request": bool(gamma.request), + } + ) + + _, response = app.test_client.get("/") + assert response.json == {"is_gamma": True, "has_request": False} + + +def test_injection_of_lambda_properties(app): + @app.get("/foo") + def handler(request, foo: Foo): + return json(request.id == foo.ident and isinstance(foo.ident, UUID)) + + app.ext.add_dependency(Foo, lambda request: Foo(request.id), "request") + + _, response = app.test_client.get("/foo") + + assert response.body == b"true" diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_constants.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_constants.py new file mode 100644 index 0000000..cd03780 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_constants.py @@ -0,0 +1,69 @@ +from dataclasses import dataclass + +import pytest + +from sanic import Sanic, json + + +@dataclass +class Foo: + bar: int + + +def test_constant_in_registry(app: Sanic): + assert len(app.ext._constant_registry._registry) == 0 + app.ext.add_constant("bar", 999) + assert len(app.ext._constant_registry._registry) == 1 + assert "bar" in app.ext._constant_registry + + +def test_constant_is_injected(app: Sanic): + app.ext.add_constant("bar", 999) + + @app.get("/") + async def handler(_, bar: int): + return json({"bar": bar}) + + _, response = app.test_client.get("/") + + assert response.json["bar"] == 999 + assert app.config.BAR == 999 + + +def test_constant_is_injected_into_constructor(app: Sanic): + app.ext.add_constant("bar", 999) + app.ext.add_dependency(Foo) + + @app.get("/") + async def handler(_, foo: Foo): + return json({"foo": foo.bar}) + + _, response = app.test_client.get("/") + + assert response.json["foo"] == 999 + assert app.config.BAR == 999 + + +def test_load_config(app: Sanic): + app.ext.load_constants() + assert len(app.ext._constant_registry._registry) == 0 + app.ext.load_constants({**app.config}, overwrite=True) + assert len(app.ext._constant_registry._registry) == len( + [k for k in app.config.keys() if k.isupper()] + ) + assert "request_buffer_size" in app.ext._constant_registry + + +def test_load_custom(app: Sanic): + app.ext.load_constants({"foo": "bar"}) + assert len(app.ext._constant_registry._registry) == 1 + assert app.ext._constant_registry.get("foo") == "bar" + + +def test_load_overwrite(app: Sanic): + app.ext.load_constants({"foo": "bar"}) + with pytest.raises( + ValueError, match="A value for FOO has already been assigned" + ): + app.ext.load_constants({"foo": "bar"}) + app.ext.load_constants({"foo": "bar"}, True) diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_dependency.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_dependency.py new file mode 100644 index 0000000..3f064ab --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_dependency.py @@ -0,0 +1,45 @@ +from sanic import Request, text + + +class Foo: + def bar(self): + return "foobar" + + +def test_dependency_added(app): + foo = Foo() + foobar = Foo() + + app.ext.dependency(foo) + app.ext.dependency(foobar, name="something") + + assert app.ctx._dependencies.foo is foo + assert app.ctx._dependencies.something is foobar + + +def test_dependency_injection(app): + foo = Foo() + + app.ext.dependency(foo) + + @app.get("/getfoo") + async def getfoo(request: Request, foo: Foo): + return text(foo.bar()) + + _, response = app.test_client.get("/getfoo") + + assert response.text == "foobar" + + +def test_dependency_injection_head(app): + foo = Foo() + + app.ext.dependency(foo) + + @app.get("/getfoo") + async def getfoo(request: Request, foo: Foo): + return text(foo.bar()) + + _, response = app.test_client.head("/getfoo") + + assert int(response.headers.get("content-length", 0)) == 6 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_config.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_config.py new file mode 100644 index 0000000..ea77acb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_config.py @@ -0,0 +1,42 @@ +from unittest.mock import Mock + +import pytest + +from sanic.exceptions import SanicException +from sanic.signals import Event + +from sanic_ext.config import Config +from sanic_ext.extensions.injection.injector import add_injection + + +def test_default_config_signal(): + config = Config() + assert config.INJECTION_SIGNAL == Event.HTTP_ROUTING_AFTER + + +def test_not_allowed_signals_error(): + with pytest.raises(SanicException): + Config(injection_signal=Event.HTTP_LIFECYCLE_REQUEST) + + +def test_http_routing_after_succeeds(): + Config(injection_signal=Event.HTTP_ROUTING_AFTER) + + +@pytest.mark.skipif( + not hasattr(Event, "HTTP_HANDLER_BEFORE"), + reason="Sanic version does not support HTTP_HANDLER_BEFORE", +) +def test_http_handler_before_succeeds(): + Config(injection_signal=Event.HTTP_HANDLER_BEFORE) + + +def test_add_injection_uses_signal_config(): + app = Mock() + + app.signal = Mock(return_value=Mock()) + app.ext.config.INJECTION_SIGNAL = "random_string" + app.ext.config.INJECTION_PRIORITY = 99999 + add_injection(app, Mock(), Mock()) + + app.signal.assert_called_once_with("random_string", priority=99999) diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_registry.py b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_registry.py new file mode 100644 index 0000000..35b2bbe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/injection/test_injection_registry.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +from unittest.mock import AsyncMock, Mock, call + +import pytest + +from sanic import Request +from sanic.exceptions import ServerError + +from sanic_ext.exceptions import InitError +from sanic_ext.extensions.injection.constructor import Constructor, gather_args +from sanic_ext.extensions.injection.registry import ( + ConstantRegistry, + InjectionRegistry, +) + + +class Foo: + def __init__(self, num: int): + self.num = num + self.mock = AsyncMock() + + @classmethod + async def create(cls, request: Request, bar: int): + instance = cls(bar) + await instance.mock(request, bar) + return instance + + +class Bar: + def __init__(self, foo: Foo): + self.foo = foo + + @classmethod + async def create(cls, request: Request, foo: Foo): + return cls(foo) + + +class Chicken: + @classmethod + def create(cls, egg: Egg): + return cls() + + +class Egg: + @classmethod + def create(cls, chicken: Chicken): + return cls() + + +class InheritedRequest(Request): ... + + +class Baz: + @classmethod + async def create(cls, request: InheritedRequest): + return cls() + + +@pytest.mark.asyncio +async def test_gather_args(): + func = AsyncMock() + func.return_value = 999 + injections = {"some_foo": (Foo, func)} + request = object() + kwargs = {"zero": 0, "one": True, "two": "2", "three": None} + + args = await gather_args(injections, request, **kwargs) + assert args == {"some_foo": 999} + assert func.call_args == call(request, **kwargs) + + +@pytest.mark.asyncio +async def test_circular_refs(): + injections = InjectionRegistry() + injections.register(Chicken, Chicken.create) + injections.register(Egg, Egg.create) + + assert isinstance(injections[Chicken], Constructor) + assert isinstance(injections[Egg], Constructor) + + message = ( + "Circular dependency injection detected on 'create'. Check " + "dependencies of 'create' which may contain circular dependency " + f"chain with {Chicken}." + ) + with pytest.raises(InitError, match=message): + injections.finalize(Mock(), ConstantRegistry({}), []) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("raises,allowed", ((False, (int,)), (True, []))) +async def test_finalize_allowed_types(raises, allowed): + injections = InjectionRegistry() + injections.register(Foo, Foo.create) + + if raises: + with pytest.raises( + InitError, match=".Could not find the following dependencies." + ): + injections.finalize(Mock(), ConstantRegistry({}), allowed) + else: + injections.finalize(Mock(), ConstantRegistry({}), allowed) + + +@pytest.mark.asyncio +async def test_constructor(): + injections = InjectionRegistry() + injections.register(Foo, Foo.create) + injections.finalize(Mock(), ConstantRegistry({}), {int}) + request = object() + constructor = injections[Foo] + + foo = await constructor(request, bar=999) + + assert isinstance(foo, Foo) + assert foo.num == 999 + assert constructor.pass_kwargs + foo.mock.assert_awaited_once_with(request, 999) + + +@pytest.mark.asyncio +async def test_constructor_nested(): + injections = InjectionRegistry() + injections.register(Foo, Foo.create) + injections.register(Bar, Bar.create) + injections.finalize(Mock(), ConstantRegistry({}), {int}) + request = object() + constructor_bar = injections[Bar] + + bar = await constructor_bar(request, bar=999) + + assert isinstance(bar, Bar) + assert isinstance(bar.foo, Foo) + assert bar.foo.num == 999 + assert not constructor_bar.pass_kwargs + bar.foo.mock.assert_awaited_once_with(request, 999) + + +@pytest.mark.asyncio +async def test_constructor_failure_kwargs(): + injections = InjectionRegistry() + injections.register(Foo, Foo.create) + injections.finalize(Mock(), ConstantRegistry({}), {int}) + request = object() + constructor = injections[Foo] + + message = ( + "Failure to inject dependencies. Make sure that all dependencies " + "for 'create' have been registered." + ) + with pytest.raises(ServerError, match=message): + await constructor(request) + + +@pytest.mark.asyncio +async def test_constructor_failure_nested(): + injections = InjectionRegistry() + injections.register(Foo, Foo.create) + injections.register(Bar, Bar.create) + injections.finalize(Mock(), ConstantRegistry({}), {int}) + request = object() + constructor: Bar = injections[Bar] + + message = ( + "Failure to inject dependencies. Make sure that all dependencies " + "for 'create' have been registered." + ) + with pytest.raises(ServerError, match=message): + await constructor(request) + + +@pytest.mark.asyncio +async def test_constructor_with_inherited_request(): + injections = InjectionRegistry() + injections.register(Baz, Baz.create) + injections.finalize(Mock(), ConstantRegistry({}), []) + + request = object() + constructor_baz = injections[Baz] + + baz = await constructor_baz(request) + + assert isinstance(baz, Baz) + assert not constructor_baz.pass_kwargs diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/logging/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/logging/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/logging/test_custom_background_logger.py b/.venv/lib/python3.12/site-packages/tests/extensions/logging/test_custom_background_logger.py new file mode 100644 index 0000000..eaa6cdc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/logging/test_custom_background_logger.py @@ -0,0 +1,20 @@ +from sanic import Sanic + +from sanic_ext.extensions.logging.logger import Logger + + +def test_defult_config(app: Sanic): + assert hasattr(app.config, "LOGGERS") + + +def test_custom_background_logger(app: Sanic): + assert Logger.LOGGERS == [] + Logger.prepare(app) + assert hasattr(app.shared_ctx, "logger_queue") + assert Logger.LOGGERS == app.config.LOGGERS + assert "sanic.root" in Logger.LOGGERS + + logger = Logger() + assert ( + len(logger.loggers) == len(Logger.LOGGERS) == len(app.config.LOGGERS) + ) diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_autodoc.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_autodoc.py new file mode 100644 index 0000000..c5669be --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_autodoc.py @@ -0,0 +1,56 @@ +from sanic_ext.extensions.openapi.autodoc import YamlStyleParametersParser + + +tests = [] + +_ = "" + +tests.append({"doc": _, "expects": {}}) + +_ = "one line docstring" + +tests.append({"doc": _, "expects": {"summary": "one line docstring"}}) + +_ = """ +first line + +more lines +""" + +tests.append( + { + "doc": _, + "expects": {"summary": "first line", "description": "more lines"}, + } +) + + +_ = """ +first line + +more lines + +openapi: +--- +responses: + '200': + description: OK +""" + +tests.append( + { + "doc": _, + "expects": { + "summary": "first line", + "description": "more lines", + "responses": {"200": {"description": "OK"}}, + }, + } +) + + +def test_autodoc(): + for t in tests: + parser = YamlStyleParametersParser(t["doc"]) + assert parser.to_openAPI_2() == t["expects"] + assert parser.to_openAPI_3() == t["expects"] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_body.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_body.py new file mode 100644 index 0000000..a57d825 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_body.py @@ -0,0 +1,83 @@ +from dataclasses import dataclass + +from sanic import Request, Sanic +from sanic.response import text + +from sanic_ext import openapi +from sanic_ext.extensions.openapi.definitions import RequestBody + +from .utils import get_spec + + +@dataclass +class UserProfile: + name: str + age: int + email: str + + +def test_body(app: Sanic): + @app.route("/test0") + async def handler0(request: Request): + """ + openapi: + --- + summary: Updates a pet in the store with form data + description: some description + operationId: someId + requestBody: + description: user to add to the system + required: true + content: + application/json: + schema: + type: object + required: + - name + - age + - email + properties: + name: + type: string + email: + type: string + age: + type: integer + format: int32 + minimum: 0 + """ + return text("ok") + + @app.route("/test1") + @openapi.body({"application/json": UserProfile}) + async def handler1(request: Request): + return text("ok") + + @app.route("/test2") + @openapi.body(RequestBody({"application/json": UserProfile})) + async def handler2(request: Request): + return text("ok") + + @app.route("/test3") + @openapi.body(RequestBody(UserProfile)) + async def handler3(request: Request): + return text("ok") + + @app.route("/test4") + @openapi.body(UserProfile) + async def handler4(request: Request): + return text("ok") + + spec = get_spec(app) + for i in range(5): + assert f"/test{i}" in spec["paths"] + content = spec["paths"][f"/test{i}"]["get"]["requestBody"]["content"] + media_type = next(iter(content.keys())) + if i <= 2: + assert media_type == "application/json" + else: + assert media_type == "*/*" + properties = content[media_type]["schema"]["properties"] + assert properties["name"]["type"] == "string" + assert properties["age"]["type"] == "integer" + assert properties["email"]["type"] == "string" diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_config.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_config.py new file mode 100644 index 0000000..df47828 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_config.py @@ -0,0 +1,34 @@ +import pytest + +from .utils import get_spec + + +@pytest.mark.parametrize( + "schemes,expected", + ( + (None, ("http://example.com/",)), + ("https", ("https://example.com/",)), + ( + "http,https", + ( + "http://example.com/", + "https://example.com/", + ), + ), + ( + ["http", "https"], + ( + "http://example.com/", + "https://example.com/", + ), + ), + ), +) +def test_config_scheme(app, schemes, expected): + app.config.API_SCHEMES = schemes + app.config.API_HOST = "example.com" + + spec = get_spec(app) + urls = {s["url"] for s in spec["servers"]} + + assert urls == set(expected) diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_decorators.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_decorators.py new file mode 100644 index 0000000..50e1a4e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_decorators.py @@ -0,0 +1,890 @@ +from datetime import date, datetime +from enum import Enum +from typing import Any, Optional, Union +from uuid import UUID + +import pytest + +from sanic.exceptions import SanicException +from sanic.views import HTTPMethodView + +from sanic_ext import openapi + +from .utils import get_path, get_spec + + +class Choice(Enum): + ONE = 1 + TWO = 2 + + +class Bar: + name: str + + +class LittleFoo: + iid: int + bar: Bar + + +class BigFoo: + iid: int + uid: UUID + created: date + updated: datetime + multi: Union[str, int] + nullable_single: Optional[bool] + nullable_multi: Optional[Union[str, int]] + bar: Bar + adict: dict[str, Any] + bdict: dict[str, bool] + anything: Any + choice: Choice + + +@pytest.mark.parametrize( + "args,content_type", + ( + ((LittleFoo,), "*/*"), + (({"application/json": LittleFoo},), "application/json"), + ((openapi.definitions.RequestBody(LittleFoo),), "*/*"), + ( + ( + openapi.definitions.RequestBody( + {"application/json": LittleFoo} + ), + ), + "application/json", + ), + ), +) +def test_body_decorator(app, args, content_type): + @app.route("/") + @openapi.body(*args, description="something") + async def handler(_): ... + + spec = get_path(app, "/") + + assert spec["requestBody"]["description"] == "something" + + content = spec["requestBody"]["content"] + + assert content_type in content + + schema = content[content_type]["schema"] + + assert schema["type"] == "object" + assert schema["properties"]["bar"]["type"] == "object" + assert ( + schema["properties"]["bar"]["properties"]["name"]["type"] == "string" + ) + + +@pytest.mark.parametrize( + "decorator", (openapi.deprecated(), openapi.deprecated) +) +def test_deprecated_decorator(app, decorator): + @app.route("/") + @decorator + async def handler(_): ... + + spec = get_path(app, "/") + assert spec["deprecated"] + + +def test_description_decorator(app): + @app.route("/") + @openapi.description("foo") + async def handler(_): ... + + spec = get_path(app, "/") + assert spec["description"] == "foo" + + +@pytest.mark.parametrize( + "args", + ( + ("http://example.com/docs",), + ( + openapi.definitions.ExternalDocumentation( + "http://example.com/docs" + ), + ), + ), +) +def test_document_decorator(app, args): + @app.route("/") + @openapi.document(*args) + async def handler(_): ... + + spec = get_path(app, "/") + assert spec["externalDocs"]["url"] == "http://example.com/docs" + + +@pytest.mark.parametrize( + "decorator,excluded", + ( + (openapi.exclude(), True), + (openapi.exclude(True), True), + (openapi.exclude(False), False), + ), +) +def test_exclude_decorator(app, decorator, excluded): + @app.route("/") + @decorator + async def handler(_): ... + + if excluded: + with pytest.raises(KeyError): + get_path(app, "/") + else: + get_path(app, "/") + + +def test_operation_decorator(app): + @app.route("/") + @openapi.operation("foo") + async def handler(_): ... + + spec = get_path(app, "/") + assert spec["operationId"] == "foo" + + +@pytest.mark.parametrize( + "decorator,expected", + ( + ( + openapi.parameter("thing"), + { + "name": "thing", + "schema": {"type": "string"}, + "in": "query", + }, + ), + ( + openapi.parameter("Authorization", str, "header"), + { + "name": "Authorization", + "schema": {"type": "string"}, + "in": "header", + }, + ), + ( + openapi.parameter( + parameter=openapi.definitions.Parameter( + "foobar", deprecated=True + ) + ), + { + "name": "foobar", + "schema": {"type": "string"}, + "deprecated": True, + "in": "query", + }, + ), + ( + openapi.parameter("thing", required=True), + { + "name": "thing", + "schema": {"type": "string"}, + "required": True, + "in": "query", + }, + ), + ), +) +def test_parameter_decorator(app, decorator, expected): + @app.route("/") + @decorator + def handler_one(_): ... + + parameters = get_path(app, "/")["parameters"] + assert parameters[0] == expected + + +@pytest.mark.parametrize( + "decorator,expected", + ( + ( + openapi.response(), + { + "default": { + "content": {"*/*": {"schema": {"type": "string"}}}, + "description": "Default Response", + } + }, + ), + ( + openapi.response(200, str, "foobar"), + { + "200": { + "content": {"*/*": {"schema": {"type": "string"}}}, + "description": "foobar", + } + }, + ), + ( + openapi.response(200, Bar, "..."), + { + "200": { + "content": { + "*/*": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "...", + } + }, + ), + ( + openapi.response( + content={"application/json": Bar}, description="..." + ), + { + "default": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "...", + } + }, + ), + ( + openapi.response( + response=openapi.definitions.Response( + {"application/json": Bar}, description="...", status=201 + ) + ), + { + "201": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "...", + } + }, + ), + ( + openapi.response(content={"application/json": BigFoo}), + { + "default": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "iid": { + "type": "integer", + "format": "int32", + }, + "uid": { + "type": "string", + "format": "uuid", + }, + "created": { + "type": "string", + "format": "date", + }, + "updated": { + "type": "string", + "format": "date-time", + }, + "multi": { + "oneOf": [ + {"type": "string"}, + { + "type": "integer", + "format": "int32", + }, + ] + }, + "nullable_single": { + "type": "boolean", + "nullable": True, + }, + "nullable_multi": { + "nullable": True, + "oneOf": [ + { + "type": "string", + }, + { + "type": "integer", + "format": "int32", + }, + ], + }, + "bar": { + "type": "object", + "properties": { + "name": {"type": "string"} + }, + }, + "adict": { + "type": "object", + "additionalProperties": {}, + }, + "bdict": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + }, + "anything": {}, + "choice": { + "type": "integer", + "format": "int32", + "enum": [1, 2], + }, + }, + } + } + }, + "description": "Default Response", + } + }, + ), + ), +) +def test_response_decorator(app, decorator, expected): + @app.route("/") + @decorator + def handler_one(_): ... + + responses = get_path(app, "/")["responses"] + assert responses == expected + + +def test_summary_decorator(app): + @app.route("/") + @openapi.summary("foo") + async def handler(_): ... + + spec = get_path(app, "/") + assert spec["summary"] == "foo" + + +@pytest.mark.parametrize( + "decorator,tags", + ( + (openapi.tag("foo"), ("foo",)), + (openapi.tag("foo", openapi.definitions.Tag("bar")), ("foo", "bar")), + ), +) +def test_tag_decorator(app, decorator, tags): + @app.route("/") + @decorator + def handler_one(_): ... + + spec = get_spec(app) + for tag in tags: + assert {"name": tag} in spec["tags"] + + tagged = get_path(app, "/")["tags"] + assert list(tagged) == list(tags) + + +def test_definition_decorator_body_dict_w_obj(app): + @app.route("/") + @openapi.definition(body={"application/json": Bar}) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_body_dict_only_schema_head(app): + @app.route("/") + @openapi.definition( + body={ + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + ) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_body_dict_types_schema_head(app): + @app.route("/") + @openapi.definition(body={"application/json": {"schema": {"name": str}}}) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_body_dict_only_schema_root(app): + @app.route("/") + @openapi.definition( + body={ + "application/json": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + ) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_body_dict_types_schema_root(app): + @app.route("/") + @openapi.definition(body={"application/json": {"name": str}}) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_httpmethodview(app): + class View(HTTPMethodView, uri="/", attach=app): + @openapi.definition(body={"application/json": Bar}) + async def get(self, request): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_body_dict_multi(app): + @app.route("/") + @openapi.definition(body={"application/json": Bar, "text/plain": str}) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + }, + "text/plain": {"schema": {"type": "string"}}, + } + } + + +def test_definition_decorator_body_request_body(app): + @app.route("/") + @openapi.definition( + body=openapi.definitions.RequestBody(str, required=True) + ) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": {"*/*": {"schema": {"type": "string"}}}, + "required": True, + } + + +def test_definition_decorator_body_model(app): + @app.route("/") + @openapi.definition(body=Bar) + async def handler(_): ... + + body = get_path(app, "/")["requestBody"] + assert body == { + "content": { + "*/*": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + } + } + + +def test_definition_decorator_deprecated(app): + @app.route("/") + @openapi.definition(deprecated=True) + async def handler(_): ... + + assert get_path(app, "/")["deprecated"] + + +def test_definition_decorator_description(app): + @app.route("/") + @openapi.definition(description="foo") + async def handler(_): ... + + assert get_path(app, "/")["description"] == "foo" + + +def test_definition_decorator_document_string(app): + @app.route("/") + @openapi.definition(document="foo") + async def handler(_): ... + + assert get_path(app, "/")["externalDocs"] == {"url": "foo"} + + +def test_definition_decorator_document_obj(app): + @app.route("/") + @openapi.definition( + document=openapi.definitions.ExternalDocumentation("foo", "bar") + ) + async def handler(_): ... + + assert get_path(app, "/")["externalDocs"] == { + "url": "foo", + "description": "bar", + } + + +def test_definition_decorator_operation(app): + @app.route("/") + @openapi.definition(operation="foo") + async def handler(_): ... + + assert get_path(app, "/")["operationId"] == "foo" + + +def test_definition_decorator_parameter_string(app): + @app.route("/") + @openapi.definition(parameter="foo") + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + } in parameters + + +def test_definition_decorator_parameter_dict(app): + @app.route("/") + @openapi.definition(parameter={"name": "foo"}) + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + } in parameters + + +def test_definition_decorator_parameter_obj(app): + @app.route("/") + @openapi.definition( + parameter=openapi.definitions.Parameter("foo", description="bar") + ) + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + "description": "bar", + } in parameters + + +def test_definition_decorator_parameter_string_multi(app): + @app.route("/") + @openapi.definition(parameter=["foo", "bar"]) + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + } in parameters + assert { + "name": "bar", + "schema": {"type": "string"}, + "in": "query", + } in parameters + + +def test_definition_decorator_parameter_dict_multi(app): + @app.route("/") + @openapi.definition( + parameter=[{"name": "foo"}, {"name": "bar", "location": "header"}] + ) + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + } in parameters + assert { + "name": "bar", + "schema": {"type": "string"}, + "in": "header", + } in parameters + + +def test_definition_decorator_parameter_obj_multi(app): + @app.route("/") + @openapi.definition( + parameter=[ + openapi.definitions.Parameter("foo"), + openapi.definitions.Parameter("bar"), + ] + ) + async def handler(_): ... + + parameters = get_path(app, "/")["parameters"] + assert { + "name": "foo", + "schema": {"type": "string"}, + "in": "query", + } in parameters + assert { + "name": "bar", + "schema": {"type": "string"}, + "in": "query", + } in parameters + + +def test_definition_decorator_response_dict(app): + @app.route("/") + @openapi.definition(response={"application/json": Bar}) + async def handler(_): ... + + responses = get_path(app, "/")["responses"] + assert responses["default"] == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "Default Response", + } + + +def test_definition_decorator_response_obj(app): + @app.route("/") + @openapi.definition( + response=openapi.definitions.Response( + {"application/json": Bar}, status=201 + ) + ) + async def handler(_): ... + + responses = get_path(app, "/")["responses"] + assert responses["201"] == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "Default Response", + } + + +def test_definition_decorator_response_model(app): + @app.route("/") + @openapi.definition(response=Bar) + async def handler(_): ... + + responses = get_path(app, "/")["responses"] + assert responses["default"] == { + "content": { + "*/*": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "Default Response", + } + + +def test_definition_decorator_response_dict_multi(app): + @app.route("/") + @openapi.definition( + response=[ + {"application/json": Bar, "text/plain": str}, + {"text/html": str}, + ] + ) + async def handler(_): ... + + responses = get_path(app, "/")["responses"] + + assert responses["default"] == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + }, + "text/plain": {"schema": {"type": "string"}}, + "text/html": {"schema": {"type": "string"}}, + }, + "description": "Default Response", + } + + +def test_definition_decorator_response_obj_multi(app): + @app.route("/") + @openapi.definition( + response=[ + openapi.definitions.Response( + {"application/json": Bar}, status=201 + ), + openapi.definitions.Response( + {"*/*": str}, status=400, description="Something bad" + ), + ] + ) + async def handler(_): ... + + responses = get_path(app, "/")["responses"] + assert responses["201"] == { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {"name": {"type": "string"}}, + } + } + }, + "description": "Default Response", + } + assert responses["400"] == { + "content": {"*/*": {"schema": {"type": "string"}}}, + "description": "Something bad", + } + + +def test_definition_decorator_response_model_multi(app): + with pytest.raises(SanicException): + + @app.route("/") + @openapi.definition(response=[Bar, LittleFoo]) + async def handler(_): ... + + +def test_definition_decorator_summary(app): + @app.route("/") + @openapi.definition(summary="foo") + async def handler(_): ... + + assert get_path(app, "/")["summary"] == "foo" + + +def test_definition_decorator_tag_string(app): + @app.route("/") + @openapi.definition(tag="foo") + async def handler(_): ... + + assert list(get_path(app, "/")["tags"]) == ["foo"] + + +def test_definition_decorator_tag_obj(app): + @app.route("/") + @openapi.definition(tag=openapi.definitions.Tag("foo")) + async def handler(_): ... + + assert list(get_path(app, "/")["tags"]) == ["foo"] + + +def test_definition_decorator_tag_string_multi(app): + @app.route("/") + @openapi.definition(tag=["foo", "bar"]) + async def handler(_): ... + + assert list(get_path(app, "/")["tags"]) == ["foo", "bar"] + + +def test_definition_decorator_tag_obj_multi(app): + @app.route("/") + @openapi.definition( + tag=[openapi.definitions.Tag("foo"), openapi.definitions.Tag("bar")] + ) + async def handler(_): ... + + assert list(get_path(app, "/")["tags"]) == ["foo", "bar"] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_definitions.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_definitions.py new file mode 100644 index 0000000..23f75a1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_definitions.py @@ -0,0 +1,56 @@ +from typing import Optional + +import pytest + +from sanic_ext.extensions.openapi.definitions import Server +from sanic_ext.extensions.openapi.types import Definition + + +@pytest.fixture +def Thing(): + class Thing(Definition): + name: str + foo: Optional[bool] + bar: Optional[bool] + + def __init__(self, name, foo=None, bar=None): + super().__init__(name=name, foo=foo, bar=bar) + + return Thing + + +def test_serialize_null_show_by_default(Thing): + thing = Thing(name="ok") + serialized = thing.serialize() + + assert serialized["name"] == "ok" + assert serialized["foo"] is None + assert serialized["bar"] is None + + +def test_serialize_some_nullable(Thing): + Thing.__nullable__ = ["foo"] + thing = Thing(name="ok") + serialized = thing.serialize() + + assert serialized["name"] == "ok" + assert serialized["foo"] is None + assert "bar" not in serialized + + +def test_serialize_no_nullable(Thing): + Thing.__nullable__ = False + thing = Thing(name="ok") + serialized = thing.serialize() + + assert serialized["name"] == "ok" + assert "foo" not in serialized + assert "bar" not in serialized + + +def test_server_definitions_defaults(): + assert Server(url="url").fields == { + "url": "url", + "description": None, + "variables": {}, + } diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_deprecated.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_deprecated.py new file mode 100644 index 0000000..469f249 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_deprecated.py @@ -0,0 +1,39 @@ +from sanic import Request, Sanic +from sanic.response import text + +from sanic_ext import openapi + +from .utils import get_spec + + +def test_deprecated(app: Sanic): + @app.route("/test0") + @openapi.deprecated() + async def handler0(request: Request): + return text("ok") + + @app.route("/test1") + @openapi.deprecated + async def handler1(request: Request): + return text("ok") + + @app.route("/test2") + async def handler2(request: Request): + """ + openapi: + --- + summary: This is a summary. + deprecated: true + """ + return text("ok") + + @app.route("/test3") + @openapi.definition(deprecated=True) + async def handler3(request: Request): + return text("ok") + + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 4 + for i in range(4): + assert paths[f"/test{i}"]["get"]["deprecated"] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_exclude.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_exclude.py new file mode 100644 index 0000000..d964533 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_exclude.py @@ -0,0 +1,52 @@ +from sanic import Blueprint, Request, Sanic, text + +from sanic_ext.extensions.openapi import openapi + +from .utils import get_spec + + +def test_exclude_decorator(app: Sanic): + @app.route("/test0") + @openapi.exclude() + async def handler0(request: Request): + """ + openapi: + --- + summary: This is a summary. + """ + return text("ok") + + @app.route("/test1") + @openapi.definition(summary="This is a summary.", exclude=True) + async def handler1(request: Request): + return text("ok") + + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 0 + + +def test_exclude_bp(app: Sanic): + bp1 = Blueprint("blueprint1") + bp2 = Blueprint("blueprint2") + + @bp1.route("/op1") + @openapi.summary("handler 1") + async def handler1(request: Request): + return text("bp1, ok") + + @bp2.route("/op2") + @openapi.summary("handler 2") + async def handler2(request: Request): + return text("bp2, ok") + + app.blueprint(bp1) + app.blueprint(bp2) + + openapi.exclude(bp=bp1) + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 1 + assert "/op2" in paths + assert "/op1" not in paths + assert paths["/op2"]["get"]["summary"] == "handler 2" diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_external_docs.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_external_docs.py new file mode 100644 index 0000000..6b22467 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_external_docs.py @@ -0,0 +1,56 @@ +from sanic import Request, Sanic +from sanic.response import text + +from sanic_ext import openapi +from sanic_ext.extensions.openapi.definitions import ExternalDocumentation + +from .utils import get_spec + + +def test_external_docs(app: Sanic): + @app.route("/test0") + @openapi.document("http://example.com/more", "Find more info here") + async def handler0(request: Request): + return text("ok") + + @app.route("/test1") + @openapi.definition( + document=ExternalDocumentation( + "http://example.com/more", "Find more info here" + ) + ) + async def handler1(request: Request): + return text("ok") + + @app.route("/test2") + @openapi.definition(document="http://example.com/more") + async def handler2(request: Request): + return text("ok") + + @app.route("/test3") + async def handler3(request: Request): + """ + openapi: + --- + summary: This is a summary. + externalDocs: + description: Find more info here + url: http://example.com/more + """ + return text("ok") + + @app.route("/test4") + @openapi.document( + ExternalDocumentation("http://example.com/more", "Find more info here") + ) + async def handler4(request: Request): + return text("ok") + + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 5 + for i in range(5): + doc_obj = paths[f"/test{i}"]["get"]["externalDocs"] + assert doc_obj["url"] == "http://example.com/more" + if i != 2: + assert doc_obj["description"] == "Find more info here" diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_func_handler.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_func_handler.py new file mode 100644 index 0000000..dd24d8b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_func_handler.py @@ -0,0 +1,51 @@ +from sanic import Sanic +from sanic.response import text + +from sanic_ext import openapi + +from .utils import get_spec + + +def test_func_handlers(app: Sanic): + class TestClass: + @staticmethod + @openapi.definition( + summary="staticmethod custom summary", + tag="Test", + ) + async def staticmethod_handler(request): + return text("...") + + @classmethod + @openapi.definition( + summary="classmethod custom summary", + tag="Test", + ) + async def classmethod_handler(cls, request): + return text("...") + + @openapi.definition( + summary="instance method custom summary", + tag="Test", + ) + async def instance_method_handler(self, request): + return text("...") + + app.add_route(TestClass.staticmethod_handler, "/staticmethod") + app.add_route(TestClass.classmethod_handler, "/classmethod") + app.add_route(TestClass().instance_method_handler, "/instance_method") + + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 3 + assert ( + paths["/staticmethod"]["get"]["summary"] + == "staticmethod custom summary" + ) + assert ( + paths["/classmethod"]["get"]["summary"] == "classmethod custom summary" + ) + assert ( + paths["/instance_method"]["get"]["summary"] + == "instance method custom summary" + ) diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_fields.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_fields.py new file mode 100644 index 0000000..617c2c7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_fields.py @@ -0,0 +1,103 @@ +from dataclasses import dataclass, field +from typing import Annotated +from uuid import UUID + +import attrs +import pytest + +from msgspec import Meta, Struct +from pydantic import BaseModel, Field +from pydantic.dataclasses import dataclass as pydataclass + +from sanic_ext import openapi + +from .utils import get_spec + + +@dataclass +class FooDataclass: + links: list[UUID] + priority: int = field( + metadata={"openapi": {"exclusiveMinimum": 1, "exclusiveMaximum": 10}} + ) + ident: str = field( + default="XXXX", metadata={"openapi": {"example": "ABC123"}} + ) + + +@attrs.define +class FooAttrs: + links: list[UUID] + priority: int = attrs.field( + metadata={"openapi": {"exclusiveMinimum": 1, "exclusiveMaximum": 10}} + ) + ident: str = attrs.field( + default="XXXX", metadata={"openapi": {"example": "ABC123"}} + ) + + +class FooPydanticBaseModel(BaseModel): + links: list[UUID] + priority: int = Field(gt=1, lt=10) + ident: str = Field("XXXX", example="ABC123") + + +@pydataclass +class FooPydanticDataclass: + links: list[UUID] + priority: int = Field(gt=1, lt=10) + ident: str = Field("XXXX", example="ABC123") + + +class FooStruct(Struct): + links: list[UUID] + priority: Annotated[ + int, + Meta( + extra={"openapi": {"exclusiveMinimum": 1, "exclusiveMaximum": 10}} + ), + ] + ident: Annotated[str, Meta(extra={"openapi": {"example": "ABC123"}})] = ( + "XXXX" + ) + + +models = [ + FooDataclass, + FooAttrs, + FooPydanticBaseModel, + FooPydanticDataclass, +] + +models.append(FooStruct) + + +@pytest.mark.parametrize("Foo", models) +def test_models(app, Foo): + @app.get("/") + @openapi.definition(body={"application/json": Foo}) + async def handler(_): ... + + spec = get_spec(app) + foo_props = spec["paths"]["/"]["get"]["requestBody"]["content"][ + "application/json" + ]["schema"]["properties"] + + assert foo_props["links"] == { + "title": "Links", + "type": "array", + "items": {"type": "string", "format": "uuid"}, + } + assert foo_props["ident"] == { + "title": "Ident", + "type": "string", + "default": "XXXX", + "example": "ABC123", + } + assert foo_props["priority"] == { + "title": "Priority", + "type": "integer", + "format": "int32", + "exclusiveMinimum": 1, + "exclusiveMaximum": 10, + } diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_spec.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_spec.py new file mode 100644 index 0000000..494a5c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_model_spec.py @@ -0,0 +1,108 @@ +from dataclasses import dataclass +from datetime import datetime + +import attrs +import pytest + +from msgspec import Struct +from pydantic import BaseModel +from pydantic.dataclasses import dataclass as pydataclass + +from sanic_ext import openapi + +from .utils import get_spec + + +@dataclass +class AlertDataclass: + hit: dict[str, int] + last_updated: datetime + + +@dataclass +class AlertResponseDataclass: + alert: AlertDataclass + rule_id: str + + +class AlertPydanticBaseModel(BaseModel): + hit: dict[str, int] + last_updated: datetime + + +class AlertResponsePydanticBaseModel(BaseModel): + alert: AlertPydanticBaseModel + rule_id: str + + +class AlertMsgspecBaseModel(Struct): + hit: dict[str, int] + last_updated: datetime + + +class AlertResponseMsgspecBaseModel(Struct): + alert: AlertMsgspecBaseModel + rule_id: str + + +@pydataclass +class AlertPydanticDataclass: + hit: dict[str, int] + last_updated: datetime + + +@pydataclass +class AlertResponsePydanticDataclass: + alert: AlertPydanticDataclass + rule_id: str + + +@attrs.define +class AlertAttrs: + hit: dict[str, int] + last_updated: datetime + + +@attrs.define +class AlertResponseAttrs: + alert: AlertAttrs + rule_id: str + + +@pytest.mark.parametrize( + "AlertResponse,check_alert", + ( + (AlertResponseDataclass, False), + (AlertResponseAttrs, False), + (AlertResponseMsgspecBaseModel, True), + (AlertResponsePydanticBaseModel, True), + (AlertResponsePydanticDataclass, True), + ), +) +def test_pydantic_base_model(app, AlertResponse, check_alert): + @app.get("/") + @openapi.definition( + body={"application/json": openapi.Component(AlertResponse)} + ) + async def handler(_): ... + + spec = get_spec(app) + alert_response_name = AlertResponse.__name__ + alert_name = "Alert" + alert_response_name[13:] + + assert spec["paths"]["/"]["get"]["requestBody"] == { + "content": { + "application/json": { + "schema": { + "$ref": f"#/components/schemas/{alert_response_name}" + } + } + } + } + assert alert_response_name in spec["components"]["schemas"] + + if check_alert: + assert alert_name in spec["components"]["schemas"] + assert spec["components"]["schemas"][alert_response_name][ + "properties" + ]["alert"] == {"$ref": f"#/components/schemas/{alert_name}"} diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_parameter.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_parameter.py new file mode 100644 index 0000000..2c8c473 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_parameter.py @@ -0,0 +1,149 @@ +from sanic import Request, Sanic, text + +from sanic_ext.extensions.openapi import openapi +from sanic_ext.extensions.openapi.definitions import Parameter + +from .utils import get_spec + + +def test_parameter(app: Sanic): + DESCRIPTION = "val1 path param" + NAME = "val1" + LOCATION = "path" + TYPE = "integer" + + @app.route("/test1/") + async def handler1(request: Request, val1: int): + """ + openapi: + --- + operationId: get.test1 + parameters: + - name: val1 + in: path + description: val1 path param + required: true + schema: + type: integer + format: int32 + """ + return text("ok") + + @app.route("/test2/") + @openapi.parameter( + parameter=Parameter( + name="val1", + schema=int, + location=LOCATION, + description=DESCRIPTION, + required=True, + ) + ) + async def handler2(request: Request, val1: int): + return text("ok") + + @app.route("/test3/") + @openapi.parameter( + "val1", + description=DESCRIPTION, + required=True, + schema=int, + location=LOCATION, + ) + async def handler3(request: Request, val1: int): + return text("ok") + + @app.route("/test4/") + @openapi.definition( + parameter={ + "name": "val1", + "description": DESCRIPTION, + "required": True, + "schema": int, + "location": LOCATION, + } + ) + async def handler4(request: Request, val1: int): + return text("ok") + + @app.route("/test5/") + @openapi.definition( + parameter=Parameter( + name="val1", + schema=int, + location=LOCATION, + description=DESCRIPTION, + required=True, + ) + ) + async def handler5(request: Request, val1: int): + return text("ok") + + @app.route("/test6/") + async def handler6(request: Request, val1: str): + """ + openapi: + --- + operationId: get.test1 + parameters: + - name: val1 + in: path + description: val1 path param + required: false + """ + return text("ok") + + @app.route("/test7/") + @openapi.parameter( + parameter=Parameter( + name="val1", + location=LOCATION, + description=DESCRIPTION, + required=True, + ) + ) + async def handler7(request: Request, val1: int): + return text("ok") + + @app.route("/test8/") + @openapi.parameter( + parameter=Parameter( + name="val1", + location=LOCATION, + description=DESCRIPTION, + required=True, + ) + ) + async def handler8(request: Request, val1: int): + return text("ok") + + @app.route("/test9/") + @openapi.parameter( + parameter=Parameter( + name="val1", + location=LOCATION, + description=DESCRIPTION, + required=True, + ) + ) + async def handler9(request: Request, val1: int): + return text("ok") + + spec = get_spec(app) + for i in range(1, 6): + assert f"/test{i}/{{val1}}" in spec["paths"] + parameter = spec["paths"][f"/test{i}/{{val1}}"]["get"]["parameters"][0] + assert parameter["name"] == NAME + assert parameter["in"] == LOCATION + assert parameter["required"] is True + assert parameter["schema"]["type"] == TYPE + assert parameter["description"] == DESCRIPTION + + for i in range(6, 10): + assert f"/test{i}/{{val1}}" in spec["paths"] + parameter = spec["paths"][f"/test{i}/{{val1}}"]["get"]["parameters"][0] + assert parameter["name"] == NAME + assert parameter["in"] == LOCATION + assert parameter["required"] is not (i == 6) + assert parameter["schema"]["type"] == "string" + assert parameter["description"] == DESCRIPTION diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_paths.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_paths.py new file mode 100644 index 0000000..86f29bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_paths.py @@ -0,0 +1,58 @@ +import pytest + +from sanic import Sanic + +from .utils import get_spec + + +@pytest.fixture(autouse=True) +def handlers(app: Sanic): + @app.route("/test1") + async def handler1(_): ... + + @app.route("/test2/1") + async def handler2(_): ... + + @app.route("/test3/") + async def handler3(_): ... + + @app.route("/test4/2/") + async def handler4(_): ... + + +def test_paths_with_all_uri_filter(app: Sanic): + app.config.API_URI_FILTER = "all" + + spec = get_spec(app) + assert list(spec["paths"].keys()) == [ + "/test1", + "/test1/", + "/test2/1", + "/test2/1/", + "/test3", + "/test3/", + "/test4/2", + "/test4/2/", + ] + + +def test_paths_with_slash_uri_filter(app: Sanic): + app.config.API_URI_FILTER = "slash" + spec = get_spec(app) + + assert list(spec["paths"].keys()) == [ + "/test1/", + "/test2/1/", + "/test3/", + "/test4/2/", + ] + + +def test_paths_without_uri_filter(app: Sanic): + spec = get_spec(app) + assert list(spec["paths"].keys()) == [ + "/test1", + "/test2/1", + "/test3", + "/test4/2", + ] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_schema.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_schema.py new file mode 100644 index 0000000..2d1696a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_schema.py @@ -0,0 +1,75 @@ +from sys import version_info + +import pytest + +from sanic_ext.extensions.openapi.types import Schema, String + + +@pytest.mark.skipif(version_info < (3, 9), reason="Not needed on 3.8") +def test_schema_list(): + class Foo: + list1: list[int] + list2: list[int] + + @property + def show(self) -> bool: + return True + + def no_show_method(self) -> None: ... + + @classmethod + def no_show_classmethod(self) -> None: ... + + @staticmethod + def no_show_staticmethod() -> None: ... + + schema = Schema.make(Foo) + serialized = schema.serialize() + + assert "no_show_method" not in serialized + assert "no_show_classmethod" not in serialized + assert "no_show_staticmethod" not in serialized + assert serialized == { + "type": "object", + "properties": { + "list1": { + "type": "array", + "items": {"type": "integer", "format": "int32"}, + }, + "list2": { + "type": "array", + "items": {"type": "integer", "format": "int32"}, + }, + "show": {"type": "boolean"}, + }, + } + + +def test_schema_fields(): + class Pet: + name = String(example="Snoopy") + + class Single: + pet = Pet + + class Ignore: ... + + class Multiple: + pets = [Pet] + + pet_schema = Schema.make(Pet).serialize() + single_schema = Schema.make(Single).serialize() + multiple_schema = Schema.make(Multiple).serialize() + + assert pet_schema == { + "type": "object", + "properties": {"name": {"type": "string", "example": "Snoopy"}}, + } + assert single_schema == { + "type": "object", + "properties": {"pet": pet_schema}, + } + assert multiple_schema == { + "type": "object", + "properties": {"pets": {"type": "array", "items": pet_schema}}, + } diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_security.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_security.py new file mode 100644 index 0000000..47b2508 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_security.py @@ -0,0 +1,98 @@ +from sanic import Sanic + +from sanic_ext import openapi +from tests.extensions.openapi.utils import get_path, get_spec + + +def test_secured(app: Sanic): + @app.route("/one") + async def handler1(request): + """ + openapi: + --- + security: + - foo: [] + """ + + @app.route("/two") + @openapi.secured("foo") + @openapi.secured({"bar": []}) + @openapi.secured(baz=[]) + async def handler2(request): ... + + @app.route("/three") + @openapi.definition(secured="foo") + @openapi.definition(secured={"bar": []}) + async def handler3(request): ... + + spec = get_path(app, "/one") + assert {"foo": []} in spec["security"] + + spec = get_path(app, "/two") + assert {"foo": []} in spec["security"] + assert {"bar": []} in spec["security"] + assert {"baz": []} in spec["security"] + + spec = get_path(app, "/three") + assert {"foo": []} in spec["security"] + assert {"bar": []} in spec["security"] + + +def test_security_scheme(app: Sanic): + app.ext.openapi.add_security_scheme("api_key", "apiKey") + app.ext.openapi.add_security_scheme("token1", "http") + app.ext.openapi.add_security_scheme( + "token2", "http", scheme="bearer", bearer_format="JWT" + ) + app.ext.openapi.add_security_scheme("oldschool", "http", scheme="basic") + app.ext.openapi.add_security_scheme( + "oa2", + "oauth2", + flows={ + "implicit": { + "authorizationUrl": "http://example.com/auth", + "scopes": { + "one:two": "something", + "three:four": "something else", + }, + } + }, + ) + + spec = get_spec(app) + schemes = spec["components"]["securitySchemes"] + + assert schemes["api_key"] == { + "type": "apiKey", + "name": "authorization", + "in": "header", + } + assert schemes["token1"] == { + "type": "http", + "scheme": "Bearer", + } + assert schemes["token2"] == { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + } + assert schemes["oa2"] == { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "http://example.com/auth", + "scopes": { + "one:two": "something", + "three:four": "something else", + }, + } + }, + } + + +def test_raw(app: Sanic): + app.ext.openapi.raw({"security": [{}, {"foo": []}]}) + + spec = get_spec(app) + assert {} in spec["security"] + assert {"foo": []} in spec["security"] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_specification.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_specification.py new file mode 100644 index 0000000..47ec558 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_specification.py @@ -0,0 +1,37 @@ +from pathlib import Path + +from sanic_ext.extensions.openapi.builders import SpecificationBuilder + + +def test_specification_singleton(): + spec = SpecificationBuilder() + spec.tag("foo") + + new_spec = SpecificationBuilder() + + assert spec is new_spec + assert spec.tags == new_spec.tags + + +def test_specification_reset(get_docs): + spec = SpecificationBuilder() + + docs = get_docs() + assert len(docs["tags"]) == 0 + + spec.tag("foo") + docs = get_docs() + assert len(docs["tags"]) == 1 + + spec.reset() + docs = get_docs() + assert len(docs["tags"]) == 0 + + +def test_custom_specification(app): + petstore_file = Path(__file__).parent / "static" / "petstore.json" + petstore_data = petstore_file.read_text() + app.config.OAS_CUSTOM_FILE = petstore_file + + _, response = app.test_client.get("/docs/openapi.json") + assert response.text == petstore_data diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_summary.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_summary.py new file mode 100644 index 0000000..14b3cc3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_summary.py @@ -0,0 +1,38 @@ +from sanic import Request, Sanic +from sanic.response import text + +from sanic_ext import openapi + +from .utils import get_spec + + +def test_summary(app: Sanic): + @app.route("/test0") + async def handler0(request: Request): + """This is a summary.""" + return text("ok") + + @app.route("/test1") + @openapi.summary("This is a summary.") + async def handler1(request: Request): + return text("ok") + + @app.route("/test2") + @openapi.definition(summary="This is a summary.") + async def handler2(request: Request): + return text("ok") + + @app.route("/test3") + async def handler3(request: Request): + """ + openapi: + --- + summary: This is a summary. + """ + return text("ok") + + spec = get_spec(app) + paths = spec["paths"] + assert len(paths) == 4 + for i in range(4): + assert paths[f"/test{i}"]["get"]["summary"] == "This is a summary." diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_tag.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_tag.py new file mode 100644 index 0000000..30cbab4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_tag.py @@ -0,0 +1,35 @@ +from sanic import Request, Sanic, text + +from sanic_ext.extensions.openapi import openapi + +from .utils import get_spec + + +def test_tag(app: Sanic): + TAG_NAME = "test-tag-1" + + @app.route("/test0") + async def handler0(request: Request, val1: int): + """ + openapi: + --- + tags: + - test-tag-1 + """ + return text("ok") + + @app.route("/test1") + @openapi.tag(TAG_NAME) + async def handler1(request: Request, val1: int): + return text("ok") + + @app.route("/test2") + @openapi.definition(tag=TAG_NAME) + async def handler2(request: Request, val1: int): + return text("ok") + + spec = get_spec(app) + for i in range(3): + assert f"/test{i}" in spec["paths"] + tag = spec["paths"][f"/test{i}"]["get"]["tags"][0] + assert tag == TAG_NAME diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_typing.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_typing.py new file mode 100644 index 0000000..99a306d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/test_typing.py @@ -0,0 +1,49 @@ +import sys + +from typing import Optional + +import pytest + +from sanic_ext.utils.typing import contains_annotations, flat_values + + +class Foo: ... + + +def test_dict_values_nested(): + values = flat_values( + { + "level1": { + "level2": { + "level3": { + "level4": {"item": 999, "items": [888, 777]}, + "item": 666, + }, + "item": True, + }, + "multiple": [ + {"nested": {"object": None}}, + {"nested": {"object": None}}, + ], + }, + "item": True, + } + ) + assert values == {999, 888, 777, 666, None, True} + + +params = [ + ({"foo": str}, True), + ({"foo": list[str]}, True), + ({"foo": Optional[str]}, True), + ({"foo": Foo}, True), + ({"foo": "str"}, False), +] +params.append(({"foo": list[str]}, True)) +if sys.version_info >= (3, 10): + params.append(({"foo": str | None}, True)) + + +@pytest.mark.parametrize("item,expected", params) +def test_contains_annotations(item, expected): + assert contains_annotations(item) == expected diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/openapi/utils.py b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/utils.py new file mode 100644 index 0000000..34291d5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/openapi/utils.py @@ -0,0 +1,14 @@ +from typing import Any + +from sanic import Sanic + + +def get_spec(app: Sanic) -> dict[str, Any]: + test_client = app.test_client + _, response = test_client.get("/docs/openapi.json") + return response.json + + +def get_path(app: Sanic, path: str, method: str = "get") -> dict[str, Any]: + spec = get_spec(app) + return spec["paths"][path][method.lower()] diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/templating/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/templating/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/templating/templates/__init__.py b/.venv/lib/python3.12/site-packages/tests/extensions/templating/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/templating/test_templating.py b/.venv/lib/python3.12/site-packages/tests/extensions/templating/test_templating.py new file mode 100644 index 0000000..7954cb0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/templating/test_templating.py @@ -0,0 +1,153 @@ +from pathlib import Path + +from sanic import Sanic + +from sanic_ext import render + + +def test_default_templates(): + app = Sanic("templating") + app.extend( + config={ + "templating_path_to_templates": Path(__file__).parent / "templates" + } + ) + + @app.get("/1") + @app.ext.template("foo.html") + async def handler(_): + return {"seq": ["one", "two"]} + + @app.get("/2") + async def handler2(_): + return await render( + "foo.html", context={"seq": ["three", "four"]}, app=app + ) + + @app.get("/3") + @app.ext.template("foo.html") + async def handler3(_): + return await render( + context={"seq": ["five", "six"]}, status=201, app=app + ) + + _, response = app.test_client.get("/1") + assert response.content_type == "text/html; charset=utf-8" + assert "
  • one
  • " in response.text + assert "
  • two
  • " in response.text + + _, response = app.test_client.get("/2") + assert response.content_type == "text/html; charset=utf-8" + assert "
  • three
  • " in response.text + assert "
  • four
  • " in response.text + + _, response = app.test_client.get("/3") + assert response.content_type == "text/html; charset=utf-8" + assert "
  • five
  • " in response.text + assert "
  • six
  • " in response.text + assert response.status == 201 + + +def test_render_from_string(): + app = Sanic("templating-from-string") + app.extend() + + template = """ + + + + + My Webpage + + + +

    Hello, world!!!!

    +
      + {% for item in seq %} +
    • {{ item }}
    • + {% endfor %} +
    + + + + + """ + + @app.get("/2") + async def handler2(_): + return await render( + template_source=template, + context={"seq": ["three", "four"]}, + app=app, + ) + + _, response = app.test_client.get("/2") + assert response.content_type == "text/html; charset=utf-8" + assert "
  • three
  • " in response.text + assert "
  • four
  • " in response.text + + +def test_config_templating_dir(): + app = Sanic("templating") + app.config.TEMPLATING_PATH_TO_TEMPLATES = ( + Path(__file__).parent / "templates" + ) + + assert app.ext.templating.environment.get_template( + "foo.html" + ).filename == str(Path(__file__).parent / "templates" / "foo.html") + + +def test_url_for(): + app = Sanic("templating-from-string") + app.extend() + + template = r"url: {{ url_for('handler') }}" + + @app.get("/one/two/three") + async def handler(_): + return await render(template_source=template) + + _, response = app.test_client.get("/one/two/three") + assert response.text == "url: /one/two/three" + + +def test_default_context(): + app = Sanic("templating-from-string") + app.extend( + config={ + "templating_path_to_templates": Path(__file__).parent / "templates" + } + ) + + template = r"{{ request.args.get('test') }}" + + @app.get("/1") + async def handler1(_): + return await render(template_source=template) + + @app.get("/2") + @app.ext.template("request_test.html") + async def handler2(_): + return {} + + @app.get("/3") + async def handler3(_): + return await render("request_test.html", context={}, app=app) + + @app.get("/4") + @app.ext.template("request_test.html") + async def handler4(_): + return await render(context={}, app=app) + + _, response = app.test_client.get("/1?test=passing") + assert response.text == "passing" + + _, response = app.test_client.get("/2?test=passing") + assert response.text == "passing" + + _, response = app.test_client.get("/3?test=passing") + assert response.text == "passing" + + _, response = app.test_client.get("/4?test=passing") + assert response.text == "passing" diff --git a/.venv/lib/python3.12/site-packages/tests/extensions/test_startup.py b/.venv/lib/python3.12/site-packages/tests/extensions/test_startup.py new file mode 100644 index 0000000..f2ffecb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extensions/test_startup.py @@ -0,0 +1,63 @@ +from typing import Union +from unittest.mock import Mock + +import pytest + +from sanic import Sanic + +from sanic_ext import Extend, Extension + + +def test_multiple_extensions(bare_app: Sanic): + mock = Mock() + + class FooExtension(Extension): + name = "foo" + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + def startup(self, _) -> None: + mock() + + class BarExtension(Extension): + name = "bar" + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + def startup(self, _) -> None: + mock() + + bare_app.extend(extensions=[FooExtension, BarExtension, BarExtension]) + + assert mock.call_count == 2 + + +@pytest.mark.parametrize("instance", (True, False)) +def test_multiple_extensions_pre_register(bare_app: Sanic, instance: bool): + mock = Mock() + + class FooExtension(Extension): + name = "foo" + + def startup(self, _) -> None: + mock() + + class BarExtension(Extension): + name = "bar" + + def startup(self, _) -> None: + mock() + + foo: Union[type[Extension], Extension] = FooExtension + bar: Union[type[Extension], Extension] = BarExtension + if instance: + foo = foo() # type: ignore + bar = bar() # type: ignore + + Extend.register(foo) + Extend.register(bar) + bare_app.extend() + + assert mock.call_count == 2 diff --git a/.venv/lib/python3.12/site-packages/tests/extra/__init__.py b/.venv/lib/python3.12/site-packages/tests/extra/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tests/extra/__models__.py b/.venv/lib/python3.12/site-packages/tests/extra/__models__.py new file mode 100644 index 0000000..36075b7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/__models__.py @@ -0,0 +1,159 @@ +import sys + +from dataclasses import dataclass, field +from typing import Literal, Optional, Union + + +@dataclass +class ModelStr: + foo: str + + +@dataclass +class ModelInt: + foo: int + + +@dataclass +class ModelFloat: + foo: float + + +@dataclass +class ModelBool: + foo: bool + + +@dataclass +class ModelOptionalStr: + foo: Optional[str] + + +@dataclass +class ModelUnion: + foo: Union[int, float] + + +@dataclass +class ModelUnionModels: + foo: Union[ModelInt, ModelFloat] + + +@dataclass +class ModelUnionStrInt: + foo: Union[str, int] + + +@dataclass +class ModelUnionIntStr: + foo: Union[int, str] + + +@dataclass +class ModelOptionalUnionStrInt: + foo: Optional[Union[str, int]] + + +@dataclass +class ModelOptionalUnionIntStr: + foo: Optional[Union[int, str]] + + +@dataclass +class ModelListStr: + foo: list[str] + + +@dataclass +class ModelListModel: + foo: list[ModelStr] + + +@dataclass +class ModelOptionalList: + foo: Optional[list[str]] + + +@dataclass +class ModelListUnion: + foo: list[Union[int, float]] + + +@dataclass +class ModelOptionalListUnion: + foo: Optional[list[Union[int, float]]] + + +@dataclass +class ModelModel: + foo: ModelStr + + +@dataclass +class ModelOptionalModel: + foo: Optional[ModelStr] + + +@dataclass +class ModelDictStr: + foo: dict[str, str] + + +@dataclass +class ModelDictModel: + foo: dict[str, ModelStr] + + +@dataclass +class ModelOptionalDict: + foo: Optional[dict[str, str]] + + +@dataclass +class ModelDictUnion: + foo: dict[str, Union[int, float]] + + +@dataclass +class ModelOptionalDictUnion: + foo: Optional[dict[str, Union[int, float]]] + + +@dataclass +class ModelSingleLiteral: + foo: Literal[True] + + +@dataclass +class ModelMultipleLiteral: + foo: Literal[True, "y", "Y", 1] + + +@dataclass +class ModelOptionalSingleLiteral: + foo: Optional[Literal[True]] + + +@dataclass +class ModelOptionalMultipleLiteral: + foo: Optional[Literal[True, "y", "Y", 1]] + + +@dataclass +class ModelListStrWithDefaultFactory: + foo: list[str] = field(default_factory=list) + + +if sys.version_info > (3, 10): + + @dataclass + class ModelUnionTypeStrNone: + foo: str | None + + @dataclass + class ModelUnionTypeStrIntNone: + foo: str | int | None + + @dataclass + class ModelUnionTypeStrInt: + foo: str | int diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_parse_hint.py b/.venv/lib/python3.12/site-packages/tests/extra/test_parse_hint.py new file mode 100644 index 0000000..d2dd93c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_parse_hint.py @@ -0,0 +1,24 @@ +from sys import version_info +from typing import Any + +import pytest + +from sanic_ext.extras.validation.schema import parse_hint + + +@pytest.mark.skipif(version_info < (3, 9), reason="Not needed on 3.8") +def test_parse_generic_list(): + hint_1 = parse_hint(list[int]) + hint_2 = parse_hint(list[int]) + + assert hint_1.origin == hint_2.origin + assert hint_1.allowed == hint_2.allowed + + +@pytest.mark.skipif(version_info < (3, 9), reason="Not needed on 3.8") +def test_parse_generic_dict(): + hint_1 = parse_hint(dict[str, Any]) + hint_2 = parse_hint(dict[str, Any]) + + assert hint_1.origin == hint_2.origin + assert hint_1.allowed == hint_2.allowed diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_request_counted.py b/.venv/lib/python3.12/site-packages/tests/extra/test_request_counted.py new file mode 100644 index 0000000..355ec1d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_request_counted.py @@ -0,0 +1,43 @@ +from unittest.mock import Mock + +import pytest + +from sanic import Sanic +from sanic.compat import Header +from sanic.response import json +from sanic_testing.reusable import ReusableClient + +from sanic_ext import CountedRequest + + +@pytest.fixture(autouse=True) +def reset_counter(): + yield + CountedRequest.reset_count() + + +def test_counter_increments(app: Sanic): + app.request_class = CountedRequest + + @app.get("/") + async def handler(request: CountedRequest): + return json({"count": request.count}) + + @app.get("/info") + async def info(request: CountedRequest): + return json({"state": request.app.m.state}) + + with ReusableClient(app) as client: + for i in range(1, 10): + _, response = client.get("/") + assert response.json["count"] == i + + +def test_counter_increment_on_state(app: Sanic): + mock = Mock() + mock.state = {} + app.multiplexer = mock + + for i in range(1, 10): + CountedRequest(b"/", Header({}), "", "", Mock(), app) + assert CountedRequest.count == i diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_serializer.py b/.venv/lib/python3.12/site-packages/tests/extra/test_serializer.py new file mode 100644 index 0000000..fd592c3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_serializer.py @@ -0,0 +1,70 @@ +from sanic import text +from sanic.response import json + +from sanic_ext import serializer + + +def test_serializer_with_builtin(app): + @app.get("/") + @serializer(text) + async def handler(request): + return "hello" + + @app.get("/201") + @serializer(text, status=201) + async def handler_201(request): + return "hello" + + _, response = app.test_client.get("/") + assert response.status_code == 200 + assert response.text == "hello" + assert response.content_type == "text/plain; charset=utf-8" + + _, response = app.test_client.get("/201") + assert response.status_code == 201 + assert response.content_type == "text/plain; charset=utf-8" + + +def test_serializer_with_custom(app): + def custom(message, status): + return json({"message": message}, status=status) + + @app.get("/") + @serializer(custom) + async def handler(request): + return "hello" + + @app.get("/201") + @serializer(custom, status=201) + async def handler_201(request): + return "hello" + + _, response = app.test_client.get("/") + assert response.status_code == 200 + assert response.content_type == "application/json" + assert response.json["message"] == "hello" + + _, response = app.test_client.get("/201") + assert response.status_code == 201 + assert response.content_type == "application/json" + + +def test_serializer_with_params(app): + def message(retval, request, action, status): + return json( + { + "request_id": str(request.id), + "action": action, + "message": retval, + }, + status=status, + ) + + @app.get("/") + @serializer(message) + async def do_action(request, action: str): + return "This is a message" + + _, response = app.test_client.get("/this") + assert response.status_code == 200 + assert response.json["action"] == "this" diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation.py new file mode 100644 index 0000000..8eda77f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation.py @@ -0,0 +1,100 @@ +from sys import version_info +from typing import Literal, Union + +import pytest + +from sanic import text + +from sanic_ext import validate + + +def _dataclass_spec(annotation): + from dataclasses import dataclass + + @dataclass + class Spec: + name: annotation + + return Spec + + +def _attrs_spec(annotation): + import attrs + + @attrs.define + class Spec: + name: annotation + + return Spec + + +def _msgspec_spec(annotation): + from msgspec import Struct + + class Spec(Struct): + name: annotation + + return Spec + + +def _pydantic_spec(annotation): + from pydantic.dataclasses import dataclass as pydataclass + + @pydataclass + class Spec: + name: annotation + + return Spec + + +@pytest.mark.parametrize( + "annotation", + ( + ( + Literal["foo"], + Literal["foo", "bar"], + Union[Literal["foo"], Literal["bar"]], + ) + ), +) +@pytest.mark.parametrize( + "spec_builder", + ( + _dataclass_spec, + _attrs_spec, + _msgspec_spec, + _pydantic_spec, + ), +) +def test_literal(app, annotation, spec_builder): + Spec = spec_builder(annotation) + + @app.get("/") + @validate(query=Spec) + def route(_, query: Spec): + return text(query.name) + + _, response = app.test_client.get("", params={"name": "foo"}) + assert response.text == "foo" + + +@pytest.mark.skipif(version_info < (3, 10), reason="Not needed on 3.10") +@pytest.mark.parametrize( + "spec_builder", + ( + _dataclass_spec, + _attrs_spec, + _msgspec_spec, + _pydantic_spec, + ), +) +def test_literal_3_10(app, spec_builder): + Spec = spec_builder(Literal["foo"] | Literal["bar"]) + + @app.get("/") + @validate(query=Spec) + def route(_, query: Spec): + return text(query.name) + + _, response = app.test_client.get("", params={"name": "foo"}) + assert response.text == "foo" diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation_attrs.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_attrs.py new file mode 100644 index 0000000..14a67e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_attrs.py @@ -0,0 +1,112 @@ +import attrs + +from sanic import json +from sanic.views import HTTPMethodView + +from sanic_ext import validate + + +SNOOPY_DATA = {"name": "Snoopy", "alter_ego": ["Flying Ace", "Joe Cool"]} + + +def test_validate_json(app): + @attrs.define + class Pet: + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(json=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(json=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_form(app): + @attrs.define + class Pet: + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(form=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(form=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_query(app): + @attrs.define + class Search: + q: str + + @app.get("/function") + @validate(query=Search) + async def handler(_, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(query=Search)] + + async def get(self, _, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + _, response = app.test_client.get("/function", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" + + _, response = app.test_client.get("/method", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation_dataclass.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_dataclass.py new file mode 100644 index 0000000..9f04dfa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_dataclass.py @@ -0,0 +1,410 @@ +import sys + +from dataclasses import dataclass +from typing import Optional + +import pytest + +from sanic import json +from sanic.views import HTTPMethodView + +from sanic_ext import validate +from sanic_ext.extras.validation.check import check_data +from sanic_ext.extras.validation.schema import make_schema, parse_hint + +from . import __models__ as models + + +SNOOPY_DATA = {"name": "Snoopy", "alter_ego": ["Flying Ace", "Joe Cool"]} + + +def test_schema(): + @dataclass + class Pet: + name: str + + @dataclass + class Person: + name: str + age: int + pets: Optional[list[Pet]] + + schema = make_schema({}, Person) + + assert "Person" in schema + assert schema["Person"]["hints"]["name"] == parse_hint(str) + assert schema["Person"]["hints"]["age"] == parse_hint(int) + assert schema["Person"]["hints"]["pets"] == parse_hint(Optional[list[Pet]]) + + assert "Pet" in schema + assert schema["Pet"]["hints"]["name"] == parse_hint(str) + + +def test_should_hydrate(): + @dataclass + class Pet: + name: str + + @dataclass + class Person: + name: str + age: int + pets: list[Pet] + + data = {"name": "Charlie Brown", "age": 8, "pets": [{"name": "Snoopy"}]} + + schema = make_schema({}, Person) + cb = check_data(Person, data, schema) + + assert cb.name == "Charlie Brown" + assert cb.age == 8 + assert cb.pets[0].name == "Snoopy" + + +@pytest.mark.parametrize( + "data", + ( + {"name": "Charlie Brown", "age": 8, "pets": {"name": "Snoopy"}}, + {"name": "Charlie Brown", "age": 8, "pets": [{"name": 123}]}, + {"name": "Charlie Brown", "age": 8, "pets": [123]}, + {"name": "Charlie Brown", "age": 8, "pets": 123}, + {"name": "Charlie Brown", "age": "8", "pets": {"name": "Snoopy"}}, + {"name": True, "age": 8, "pets": {"name": "Snoopy"}}, + ), +) +def test_should_not_hydrate(data): + @dataclass + class Pet: + name: str + + @dataclass + class Person: + name: str + age: int + pets: list[Pet] + + schema = make_schema({}, Person) + with pytest.raises(TypeError): + check_data(Person, data, schema) + + +@pytest.mark.parametrize( + "model,okay,data", + ( + (models.ModelStr, True, {"foo": "bar"}), + (models.ModelStr, False, {"foo": 1}), + (models.ModelStr, False, {"foo": True}), + (models.ModelStr, False, {"foo": ["bar"]}), + (models.ModelStr, False, {"bar": "bar"}), + (models.ModelStr, False, {"foo": None}), + (models.ModelStr, False, 123), + (models.ModelInt, True, {"foo": 1}), + (models.ModelInt, True, {"foo": True}), + (models.ModelInt, False, {"foo": "1"}), + (models.ModelInt, False, {"foo": 1.1}), + (models.ModelInt, False, {"foo": None}), + (models.ModelFloat, True, {"foo": 1.1}), + (models.ModelFloat, False, {"foo": 1}), + (models.ModelFloat, False, {"foo": "1.1"}), + (models.ModelFloat, False, {"foo": None}), + (models.ModelBool, True, {"foo": True}), + (models.ModelBool, True, {"foo": False}), + (models.ModelBool, False, {"foo": 1}), + (models.ModelBool, False, {"foo": 0}), + (models.ModelBool, False, {"foo": 2}), + (models.ModelBool, False, {"foo": "True"}), + (models.ModelBool, False, {"foo": None}), + (models.ModelOptionalStr, True, {"foo": "bar"}), + (models.ModelOptionalStr, True, {"foo": None}), + (models.ModelOptionalStr, False, {"foo": 0}), + (models.ModelUnion, True, {"foo": 1}), + (models.ModelUnion, True, {"foo": 1.1}), + (models.ModelUnion, False, {"foo": "1.1"}), + (models.ModelUnion, False, {"foo": None}), + (models.ModelUnionModels, True, {"foo": {"foo": 1}}), + (models.ModelUnionModels, True, {"foo": {"foo": 1.1}}), + (models.ModelUnionModels, False, {"foo": {"foo": "1.1"}}), + (models.ModelUnionModels, False, {"foo": 1}), + (models.ModelUnionModels, False, {"foo": 1.1}), + (models.ModelUnionModels, False, {"foo": None}), + (models.ModelUnionStrInt, True, {"foo": "1"}), + (models.ModelUnionStrInt, True, {"foo": "1q"}), + (models.ModelUnionStrInt, True, {"foo": 1}), + (models.ModelUnionStrInt, False, {"foo": 1.1}), + (models.ModelUnionStrInt, False, {"foo": None}), + (models.ModelUnionIntStr, True, {"foo": "1"}), + (models.ModelUnionIntStr, True, {"foo": "1q"}), + (models.ModelUnionIntStr, True, {"foo": 1}), + (models.ModelUnionIntStr, False, {"foo": 1.1}), + (models.ModelUnionIntStr, False, {"foo": None}), + (models.ModelOptionalUnionStrInt, True, {"foo": "1"}), + (models.ModelOptionalUnionStrInt, True, {"foo": "1q"}), + (models.ModelOptionalUnionStrInt, True, {"foo": 1}), + (models.ModelOptionalUnionStrInt, False, {"foo": 1.1}), + (models.ModelOptionalUnionStrInt, True, {"foo": None}), + (models.ModelOptionalUnionIntStr, True, {"foo": "1"}), + (models.ModelOptionalUnionIntStr, True, {"foo": "1q"}), + (models.ModelOptionalUnionIntStr, True, {"foo": 1}), + (models.ModelOptionalUnionIntStr, False, {"foo": 1.1}), + (models.ModelOptionalUnionIntStr, True, {"foo": None}), + (models.ModelListStr, True, {"foo": ["bar"]}), + (models.ModelListStr, True, {"foo": ["one", "two"]}), + (models.ModelListStr, False, {"foo": "bar"}), + (models.ModelListStr, False, {"foo": ["one", 2]}), + (models.ModelListStr, False, {"foo": ["one", None]}), + (models.ModelListStr, False, {"foo": None}), + (models.ModelListModel, True, {"foo": [{"foo": "bar"}]}), + ( + models.ModelListModel, + True, + {"foo": [{"foo": "one"}, {"foo": "two"}]}, + ), + (models.ModelListModel, False, {"foo": {"foo": "bar"}}), + (models.ModelListModel, False, {"foo": [{"foo": "bar"}, 2]}), + (models.ModelListModel, False, {"foo": [{"foo": "bar"}, None]}), + (models.ModelListModel, False, {"foo": None}), + (models.ModelOptionalList, True, {"foo": None}), + (models.ModelOptionalList, True, {"foo": ["bar"]}), + (models.ModelOptionalList, False, {"foo": [1]}), + (models.ModelOptionalList, False, {"foo": [None]}), + (models.ModelListUnion, True, {"foo": [1]}), + (models.ModelListUnion, True, {"foo": [1.1]}), + (models.ModelListUnion, True, {"foo": [1, 1.1]}), + (models.ModelListUnion, False, {"foo": [1, 1.1, "one"]}), + (models.ModelListUnion, False, {"foo": [1, 1.1, None]}), + (models.ModelListUnion, False, {"foo": 1}), + (models.ModelListUnion, False, {"foo": 1.1}), + (models.ModelListUnion, False, {"foo": None}), + (models.ModelOptionalListUnion, True, {"foo": [1]}), + (models.ModelOptionalListUnion, True, {"foo": [1.1]}), + (models.ModelOptionalListUnion, True, {"foo": [1, 1.1]}), + (models.ModelOptionalListUnion, True, {"foo": None}), + (models.ModelOptionalListUnion, False, {"foo": [1, 1.1, "one"]}), + (models.ModelOptionalListUnion, False, {"foo": [1, 1.1, None]}), + (models.ModelOptionalListUnion, False, {"foo": 1}), + (models.ModelOptionalListUnion, False, {"foo": 1.1}), + (models.ModelModel, True, {"foo": {"foo": "one"}}), + (models.ModelModel, False, {"foo": {"foo": 1}}), + (models.ModelModel, False, {"foo": {"foo": None}}), + (models.ModelModel, False, {"foo": "one"}), + (models.ModelModel, False, {"foo": None}), + (models.ModelOptionalModel, True, {"foo": {"foo": "one"}}), + (models.ModelOptionalModel, True, {"foo": None}), + (models.ModelOptionalModel, False, {"foo": {"foo": 1}}), + (models.ModelOptionalModel, False, {"foo": {"foo": None}}), + (models.ModelOptionalModel, False, {"foo": "one"}), + (models.ModelDictStr, True, {"foo": {"foo": "one"}}), + (models.ModelDictStr, False, {"foo": {"foo": 1}}), + (models.ModelDictStr, False, {"foo": {"foo": None}}), + (models.ModelDictStr, False, {"foo": "one"}), + (models.ModelDictStr, False, {"foo": None}), + (models.ModelDictModel, True, {"foo": {"foo": {"foo": "one"}}}), + (models.ModelDictModel, False, {"foo": {"foo": {"foo": 1}}}), + (models.ModelDictModel, False, {"foo": {"foo": 1}}), + (models.ModelDictModel, False, {"foo": {"foo": None}}), + (models.ModelDictModel, False, {"foo": "one"}), + (models.ModelDictModel, False, {"foo": None}), + (models.ModelOptionalDict, True, {"foo": {"foo": "one"}}), + (models.ModelOptionalDict, True, {"foo": None}), + (models.ModelOptionalDict, False, {"foo": {"foo": 1}}), + (models.ModelOptionalDict, False, {"foo": {"foo": None}}), + (models.ModelOptionalDict, False, {"foo": "one"}), + (models.ModelDictUnion, True, {"foo": {"foo": 1}}), + (models.ModelDictUnion, True, {"foo": {"foo": 1.1}}), + (models.ModelDictUnion, False, {"foo": {"foo": "one"}}), + (models.ModelDictUnion, False, {"foo": {"foo": None}}), + (models.ModelDictUnion, False, {"foo": "one"}), + (models.ModelDictUnion, False, {"foo": 1}), + (models.ModelDictUnion, False, {"foo": 1.1}), + (models.ModelDictUnion, False, {"foo": None}), + (models.ModelOptionalDictUnion, True, {"foo": {"foo": 1}}), + (models.ModelOptionalDictUnion, True, {"foo": {"foo": 1.1}}), + (models.ModelOptionalDictUnion, True, {"foo": None}), + (models.ModelOptionalDictUnion, False, {"foo": {"foo": "one"}}), + (models.ModelOptionalDictUnion, False, {"foo": {"foo": None}}), + (models.ModelOptionalDictUnion, False, {"foo": "one"}), + (models.ModelOptionalDictUnion, False, {"foo": 1}), + (models.ModelOptionalDictUnion, False, {"foo": 1.1}), + (models.ModelSingleLiteral, True, {"foo": True}), + (models.ModelSingleLiteral, False, {"foo": False}), + (models.ModelSingleLiteral, False, {"foo": "True"}), + (models.ModelSingleLiteral, False, {"foo": None}), + (models.ModelOptionalSingleLiteral, True, {"foo": True}), + (models.ModelOptionalSingleLiteral, True, {"foo": None}), + (models.ModelOptionalSingleLiteral, False, {"foo": False}), + (models.ModelOptionalSingleLiteral, False, {"foo": "True"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": True}), + (models.ModelOptionalMultipleLiteral, True, {"foo": 1}), + (models.ModelOptionalMultipleLiteral, True, {"foo": "y"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": "Y"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": None}), + (models.ModelOptionalMultipleLiteral, False, {"foo": "n"}), + (models.ModelOptionalMultipleLiteral, False, {"foo": False}), + (models.ModelListStrWithDefaultFactory, True, {}), + (models.ModelListStrWithDefaultFactory, True, {"foo": ["bar"]}), + (models.ModelListStrWithDefaultFactory, True, {"foo": []}), + (models.ModelListStrWithDefaultFactory, False, {"foo": [1]}), + (models.ModelListStrWithDefaultFactory, False, {"foo": None}), + ), +) +def test_modeling(model, okay, data): + schema = make_schema({}, model) + + if okay: + check_data(model, data, schema) + else: + with pytest.raises(TypeError): + check_data(model, data, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrNone(): + schema = make_schema({}, models.ModelUnionTypeStrNone) + + check_data(models.ModelUnionTypeStrNone, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrNone, {"foo": None}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrNone, {"foo": 0}, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrIntNone(): + schema = make_schema({}, models.ModelUnionTypeStrIntNone) + + check_data(models.ModelUnionTypeStrIntNone, {"foo": "1"}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": None}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": 1}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": 0}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrIntNone, {"foo": 1.1}, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrInt(): + schema = make_schema({}, models.ModelUnionTypeStrInt) + + check_data(models.ModelUnionTypeStrInt, {"foo": "1"}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": 1}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": 0}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrInt, {"foo": None}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrInt, {"foo": 1.1}, schema) + + +def test_validate_json(app): + @dataclass + class Pet: + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(json=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(json=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_form(app): + @dataclass + class Pet: + name: str + alter_ego: list[str] + description: Optional[str] = None + + @app.post("/function") + @validate(form=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + "description": body.description if body.description else "", + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(form=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + "description": body.description + if body.description + else "", + } + ) + + _, response = app.test_client.post("/function", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + assert response.json["description"] == "" + + _, response = app.test_client.post("/method", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + assert response.json["description"] == "" + + +def test_validate_query(app): + @dataclass + class Search: + q: str + + @app.get("/function") + @validate(query=Search) + async def handler(_, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(query=Search)] + + async def get(self, _, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + _, response = app.test_client.get("/function", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" + + _, response = app.test_client.get("/method", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation_msgspec.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_msgspec.py new file mode 100644 index 0000000..8aa73ca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_msgspec.py @@ -0,0 +1,394 @@ +import sys + +from typing import Optional + +import pytest + +from msgspec import Struct +from sanic import json +from sanic.views import HTTPMethodView + +from sanic_ext import validate +from sanic_ext.extras.validation.check import check_data +from sanic_ext.extras.validation.schema import make_schema, parse_hint + +from . import __models__ as models + + +SNOOPY_DATA = {"name": "Snoopy", "alter_ego": ["Flying Ace", "Joe Cool"]} + + +def test_schema(): + class Pet(Struct): + name: str + + class Person(Struct): + name: str + age: int + pets: Optional[list[Pet]] + + schema = make_schema({}, Person) + + assert "Person" in schema + assert schema["Person"]["hints"]["name"] == parse_hint(str) + assert schema["Person"]["hints"]["age"] == parse_hint(int) + assert schema["Person"]["hints"]["pets"] == parse_hint(Optional[list[Pet]]) + + assert "Pet" in schema + assert schema["Pet"]["hints"]["name"] == parse_hint(str) + + +def test_should_hydrate(): + class Pet(Struct): + name: str + + class Person(Struct): + name: str + age: int + pets: list[Pet] + + data = {"name": "Charlie Brown", "age": 8, "pets": [{"name": "Snoopy"}]} + + schema = make_schema({}, Person) + cb = check_data(Person, data, schema) + + assert cb.name == "Charlie Brown" + assert cb.age == 8 + assert cb.pets[0].name == "Snoopy" + + +@pytest.mark.parametrize( + "data", + ( + {"name": "Charlie Brown", "age": 8, "pets": {"name": "Snoopy"}}, + {"name": "Charlie Brown", "age": 8, "pets": [{"name": 123}]}, + {"name": "Charlie Brown", "age": 8, "pets": [123]}, + {"name": "Charlie Brown", "age": 8, "pets": 123}, + {"name": "Charlie Brown", "age": "8", "pets": {"name": "Snoopy"}}, + {"name": True, "age": 8, "pets": {"name": "Snoopy"}}, + ), +) +def test_should_not_hydrate(data): + class Pet(Struct): + name: str + + class Person(Struct): + name: str + age: int + pets: list[Pet] + + schema = make_schema({}, Person) + with pytest.raises(TypeError): + check_data(Person, data, schema) + + +@pytest.mark.parametrize( + "model,okay,data", + ( + (models.ModelStr, True, {"foo": "bar"}), + (models.ModelStr, False, {"foo": 1}), + (models.ModelStr, False, {"foo": True}), + (models.ModelStr, False, {"foo": ["bar"]}), + (models.ModelStr, False, {"bar": "bar"}), + (models.ModelStr, False, {"foo": None}), + (models.ModelStr, False, 123), + (models.ModelInt, True, {"foo": 1}), + (models.ModelInt, True, {"foo": True}), + (models.ModelInt, False, {"foo": "1"}), + (models.ModelInt, False, {"foo": 1.1}), + (models.ModelInt, False, {"foo": None}), + (models.ModelFloat, True, {"foo": 1.1}), + (models.ModelFloat, False, {"foo": 1}), + (models.ModelFloat, False, {"foo": "1.1"}), + (models.ModelFloat, False, {"foo": None}), + (models.ModelBool, True, {"foo": True}), + (models.ModelBool, True, {"foo": False}), + (models.ModelBool, False, {"foo": 1}), + (models.ModelBool, False, {"foo": 0}), + (models.ModelBool, False, {"foo": 2}), + (models.ModelBool, False, {"foo": "True"}), + (models.ModelBool, False, {"foo": None}), + (models.ModelOptionalStr, True, {"foo": "bar"}), + (models.ModelOptionalStr, True, {"foo": None}), + (models.ModelOptionalStr, False, {"foo": 0}), + (models.ModelUnion, True, {"foo": 1}), + (models.ModelUnion, True, {"foo": 1.1}), + (models.ModelUnion, False, {"foo": "1.1"}), + (models.ModelUnion, False, {"foo": None}), + (models.ModelUnionModels, True, {"foo": {"foo": 1}}), + (models.ModelUnionModels, True, {"foo": {"foo": 1.1}}), + (models.ModelUnionModels, False, {"foo": {"foo": "1.1"}}), + (models.ModelUnionModels, False, {"foo": 1}), + (models.ModelUnionModels, False, {"foo": 1.1}), + (models.ModelUnionModels, False, {"foo": None}), + (models.ModelUnionStrInt, True, {"foo": "1"}), + (models.ModelUnionStrInt, True, {"foo": "1q"}), + (models.ModelUnionStrInt, True, {"foo": 1}), + (models.ModelUnionStrInt, False, {"foo": 1.1}), + (models.ModelUnionStrInt, False, {"foo": None}), + (models.ModelUnionIntStr, True, {"foo": "1"}), + (models.ModelUnionIntStr, True, {"foo": "1q"}), + (models.ModelUnionIntStr, True, {"foo": 1}), + (models.ModelUnionIntStr, False, {"foo": 1.1}), + (models.ModelUnionIntStr, False, {"foo": None}), + (models.ModelOptionalUnionStrInt, True, {"foo": "1"}), + (models.ModelOptionalUnionStrInt, True, {"foo": "1q"}), + (models.ModelOptionalUnionStrInt, True, {"foo": 1}), + (models.ModelOptionalUnionStrInt, False, {"foo": 1.1}), + (models.ModelOptionalUnionStrInt, True, {"foo": None}), + (models.ModelOptionalUnionIntStr, True, {"foo": "1"}), + (models.ModelOptionalUnionIntStr, True, {"foo": "1q"}), + (models.ModelOptionalUnionIntStr, True, {"foo": 1}), + (models.ModelOptionalUnionIntStr, False, {"foo": 1.1}), + (models.ModelOptionalUnionIntStr, True, {"foo": None}), + (models.ModelListStr, True, {"foo": ["bar"]}), + (models.ModelListStr, True, {"foo": ["one", "two"]}), + (models.ModelListStr, False, {"foo": "bar"}), + (models.ModelListStr, False, {"foo": ["one", 2]}), + (models.ModelListStr, False, {"foo": ["one", None]}), + (models.ModelListStr, False, {"foo": None}), + (models.ModelListModel, True, {"foo": [{"foo": "bar"}]}), + ( + models.ModelListModel, + True, + {"foo": [{"foo": "one"}, {"foo": "two"}]}, + ), + (models.ModelListModel, False, {"foo": {"foo": "bar"}}), + (models.ModelListModel, False, {"foo": [{"foo": "bar"}, 2]}), + (models.ModelListModel, False, {"foo": [{"foo": "bar"}, None]}), + (models.ModelListModel, False, {"foo": None}), + (models.ModelOptionalList, True, {"foo": None}), + (models.ModelOptionalList, True, {"foo": ["bar"]}), + (models.ModelOptionalList, False, {"foo": [1]}), + (models.ModelOptionalList, False, {"foo": [None]}), + (models.ModelListUnion, True, {"foo": [1]}), + (models.ModelListUnion, True, {"foo": [1.1]}), + (models.ModelListUnion, True, {"foo": [1, 1.1]}), + (models.ModelListUnion, False, {"foo": [1, 1.1, "one"]}), + (models.ModelListUnion, False, {"foo": [1, 1.1, None]}), + (models.ModelListUnion, False, {"foo": 1}), + (models.ModelListUnion, False, {"foo": 1.1}), + (models.ModelListUnion, False, {"foo": None}), + (models.ModelOptionalListUnion, True, {"foo": [1]}), + (models.ModelOptionalListUnion, True, {"foo": [1.1]}), + (models.ModelOptionalListUnion, True, {"foo": [1, 1.1]}), + (models.ModelOptionalListUnion, True, {"foo": None}), + (models.ModelOptionalListUnion, False, {"foo": [1, 1.1, "one"]}), + (models.ModelOptionalListUnion, False, {"foo": [1, 1.1, None]}), + (models.ModelOptionalListUnion, False, {"foo": 1}), + (models.ModelOptionalListUnion, False, {"foo": 1.1}), + (models.ModelModel, True, {"foo": {"foo": "one"}}), + (models.ModelModel, False, {"foo": {"foo": 1}}), + (models.ModelModel, False, {"foo": {"foo": None}}), + (models.ModelModel, False, {"foo": "one"}), + (models.ModelModel, False, {"foo": None}), + (models.ModelOptionalModel, True, {"foo": {"foo": "one"}}), + (models.ModelOptionalModel, True, {"foo": None}), + (models.ModelOptionalModel, False, {"foo": {"foo": 1}}), + (models.ModelOptionalModel, False, {"foo": {"foo": None}}), + (models.ModelOptionalModel, False, {"foo": "one"}), + (models.ModelDictStr, True, {"foo": {"foo": "one"}}), + (models.ModelDictStr, False, {"foo": {"foo": 1}}), + (models.ModelDictStr, False, {"foo": {"foo": None}}), + (models.ModelDictStr, False, {"foo": "one"}), + (models.ModelDictStr, False, {"foo": None}), + (models.ModelDictModel, True, {"foo": {"foo": {"foo": "one"}}}), + (models.ModelDictModel, False, {"foo": {"foo": {"foo": 1}}}), + (models.ModelDictModel, False, {"foo": {"foo": 1}}), + (models.ModelDictModel, False, {"foo": {"foo": None}}), + (models.ModelDictModel, False, {"foo": "one"}), + (models.ModelDictModel, False, {"foo": None}), + (models.ModelOptionalDict, True, {"foo": {"foo": "one"}}), + (models.ModelOptionalDict, True, {"foo": None}), + (models.ModelOptionalDict, False, {"foo": {"foo": 1}}), + (models.ModelOptionalDict, False, {"foo": {"foo": None}}), + (models.ModelOptionalDict, False, {"foo": "one"}), + (models.ModelDictUnion, True, {"foo": {"foo": 1}}), + (models.ModelDictUnion, True, {"foo": {"foo": 1.1}}), + (models.ModelDictUnion, False, {"foo": {"foo": "one"}}), + (models.ModelDictUnion, False, {"foo": {"foo": None}}), + (models.ModelDictUnion, False, {"foo": "one"}), + (models.ModelDictUnion, False, {"foo": 1}), + (models.ModelDictUnion, False, {"foo": 1.1}), + (models.ModelDictUnion, False, {"foo": None}), + (models.ModelOptionalDictUnion, True, {"foo": {"foo": 1}}), + (models.ModelOptionalDictUnion, True, {"foo": {"foo": 1.1}}), + (models.ModelOptionalDictUnion, True, {"foo": None}), + (models.ModelOptionalDictUnion, False, {"foo": {"foo": "one"}}), + (models.ModelOptionalDictUnion, False, {"foo": {"foo": None}}), + (models.ModelOptionalDictUnion, False, {"foo": "one"}), + (models.ModelOptionalDictUnion, False, {"foo": 1}), + (models.ModelOptionalDictUnion, False, {"foo": 1.1}), + (models.ModelSingleLiteral, True, {"foo": True}), + (models.ModelSingleLiteral, False, {"foo": False}), + (models.ModelSingleLiteral, False, {"foo": "True"}), + (models.ModelSingleLiteral, False, {"foo": None}), + (models.ModelOptionalSingleLiteral, True, {"foo": True}), + (models.ModelOptionalSingleLiteral, True, {"foo": None}), + (models.ModelOptionalSingleLiteral, False, {"foo": False}), + (models.ModelOptionalSingleLiteral, False, {"foo": "True"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": True}), + (models.ModelOptionalMultipleLiteral, True, {"foo": 1}), + (models.ModelOptionalMultipleLiteral, True, {"foo": "y"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": "Y"}), + (models.ModelOptionalMultipleLiteral, True, {"foo": None}), + (models.ModelOptionalMultipleLiteral, False, {"foo": "n"}), + (models.ModelOptionalMultipleLiteral, False, {"foo": False}), + (models.ModelListStrWithDefaultFactory, True, {}), + (models.ModelListStrWithDefaultFactory, True, {"foo": ["bar"]}), + (models.ModelListStrWithDefaultFactory, True, {"foo": []}), + (models.ModelListStrWithDefaultFactory, False, {"foo": [1]}), + (models.ModelListStrWithDefaultFactory, False, {"foo": None}), + ), +) +def test_modeling(model, okay, data): + schema = make_schema({}, model) + + if okay: + check_data(model, data, schema) + else: + with pytest.raises(TypeError): + check_data(model, data, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrNone(): + schema = make_schema({}, models.ModelUnionTypeStrNone) + + check_data(models.ModelUnionTypeStrNone, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrNone, {"foo": None}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrNone, {"foo": 0}, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrIntNone(): + schema = make_schema({}, models.ModelUnionTypeStrIntNone) + + check_data(models.ModelUnionTypeStrIntNone, {"foo": "1"}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": None}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": 1}, schema) + check_data(models.ModelUnionTypeStrIntNone, {"foo": 0}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrIntNone, {"foo": 1.1}, schema) + + +@pytest.mark.skipif( + sys.version_info < (3, 10), reason="UnionType added in 3.10" +) +def test_modeling_union_type_ModelUnionTypeStrInt(): + schema = make_schema({}, models.ModelUnionTypeStrInt) + + check_data(models.ModelUnionTypeStrInt, {"foo": "1"}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": "bar"}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": 1}, schema) + check_data(models.ModelUnionTypeStrInt, {"foo": 0}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrInt, {"foo": None}, schema) + with pytest.raises(TypeError): + check_data(models.ModelUnionTypeStrInt, {"foo": 1.1}, schema) + + +def test_validate_json(app): + class Pet(Struct): + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(json=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(json=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_form(app): + class Pet(Struct): + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(form=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(form=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_query(app): + class Search(Struct): + q: str + + @app.get("/function") + @validate(query=Search) + async def handler(_, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(query=Search)] + + async def get(self, _, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + _, response = app.test_client.get("/function", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" + + _, response = app.test_client.get("/method", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation_multiple.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_multiple.py new file mode 100644 index 0000000..b983b8b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_multiple.py @@ -0,0 +1,42 @@ +from dataclasses import asdict, dataclass + +from sanic import Sanic, json + +from sanic_ext.extras.validation.decorator import validate + + +@dataclass +class ModelA: + a: str + + +@dataclass +class ModelB: + b: str + + +def test_both_body_and_query(app: Sanic): + @app.post("") + @validate(json=ModelA, query=ModelB) + async def test(_, body: ModelA, query: ModelB): + return json( + { + "body": asdict(body), + "query": asdict(query), + } + ) + + _, response = app.test_client.post("") + assert response.status == 400 + + _, response = app.test_client.post("", params={"b": "bbb"}) + assert response.status == 400 + + _, response = app.test_client.post( + "", params={"b": "bbb"}, json={"a": "aaa"} + ) + assert response.status == 200 + assert response.json == { + "body": {"a": "aaa"}, + "query": {"b": "bbb"}, + } diff --git a/.venv/lib/python3.12/site-packages/tests/extra/test_validation_pydantic.py b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_pydantic.py new file mode 100644 index 0000000..1bb4ddd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tests/extra/test_validation_pydantic.py @@ -0,0 +1,148 @@ +import pydantic + +from pydantic.dataclasses import dataclass +from sanic import json +from sanic.views import HTTPMethodView + +from sanic_ext import validate +from sanic_ext.exceptions import ValidationError + + +SNOOPY_DATA = {"name": "Snoopy", "alter_ego": ["Flying Ace", "Joe Cool"]} + + +def test_validate_json(app): + @dataclass + class Pet: + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(json=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(json=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", json=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_form(app): + @dataclass + class Pet: + name: str + alter_ego: list[str] + + @app.post("/function") + @validate(form=Pet) + async def handler(_, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(form=Pet)] + + async def post(self, _, body: Pet): + return json( + { + "is_pet": isinstance(body, Pet), + "pet": {"name": body.name, "alter_ego": body.alter_ego}, + } + ) + + _, response = app.test_client.post("/function", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + _, response = app.test_client.post("/method", data=SNOOPY_DATA) + assert response.status == 200 + assert response.json["is_pet"] + assert response.json["pet"] == SNOOPY_DATA + + +def test_validate_query(app): + @dataclass + class Search: + q: str + + @app.get("/function") + @validate(query=Search) + async def handler(_, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + class MethodView(HTTPMethodView, attach=app, uri="/method"): + decorators = [validate(query=Search)] + + async def get(self, _, query: Search): + return json({"q": query.q, "is_search": isinstance(query, Search)}) + + _, response = app.test_client.get("/function", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" + + _, response = app.test_client.get("/method", params={"q": "Snoopy"}) + assert response.status == 200 + assert response.json["is_search"] + assert response.json["q"] == "Snoopy" + + +class User(pydantic.BaseModel): + name: str + age: int + email: str + + +def test_success_validate_form_custom_message(app): + @app.post("/user") + @validate(form=User) + async def create_user(request, body: User): + return json(body.dict()) + + user = {"name": "Alison", "age": 25, "email": "alison@almeida.com"} + _, response = app.test_client.post("/user", data=user) + assert response.status == 200 + + +def test_error_validate_form_custom_message(app): + async def server_error_validate_form(request, exception: ValidationError): + error = exception.extra["exception"] + return json(error, status=400) + + @app.post("/user") + @validate(form=User) + async def create_user(request, body: User): + return json(body.dict()) + + user = {"name": "Alison", "age": 25} + + app.error_handler.add(ValidationError, server_error_validate_form) + _, response = app.test_client.post("/user", data=user) + assert response.status == 400 diff --git a/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/METADATA new file mode 100644 index 0000000..79feab1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/METADATA @@ -0,0 +1,125 @@ +Metadata-Version: 2.4 +Name: tracerite +Version: 2.3.1 +Summary: Human-readable HTML tracebacks for Python exceptions +Project-URL: Homepage, https://github.com/sanic-org/tracerite +Project-URL: Repository, https://github.com/sanic-org/tracerite +Project-URL: Issues, https://github.com/sanic-org/tracerite/issues +Author-email: Sanic Community +License: Public Domain +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: License :: Public Domain +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Software Development :: Debuggers +Requires-Python: >=3.9 +Requires-Dist: html5tagger>=1.2.1 +Provides-Extra: nb +Requires-Dist: ipykernel>=6.29.5; extra == 'nb' +Requires-Dist: ipywidgets>=8.1.8; extra == 'nb' +Requires-Dist: jupyter-client>=8.6.3; extra == 'nb' +Requires-Dist: jupyter>=1.1.1; extra == 'nb' +Requires-Dist: jupyterlab>=4.3.8; extra == 'nb' +Requires-Dist: notebook>=7.3.3; extra == 'nb' +Description-Content-Type: text/markdown + +# TraceRite + +**Beautiful, readable error messages for Python, with text and HTML formatting.** + +[![PyPI version](https://badge.fury.io/py/tracerite.svg)](https://pypi.org/project/tracerite/) +![Tests](https://raw.githubusercontent.com/sanic-org/tracerite/main/docs/img/tests-badge.svg) +![Coverage](https://raw.githubusercontent.com/sanic-org/tracerite/main/docs/img/coverage-badge.svg) + +![TraceRite features](https://raw.githubusercontent.com/sanic-org/tracerite/main/docs/screenshots/features-composite.webp) + +## Installation + +### Python scripts or REPL + +```sh +pip install tracerite +``` + +```python +import tracerite; tracerite.load() +``` + +Any error message after that call will be prettified. Handles any syntax errors and uncaught exceptions, even captures `logging.exception` (optionally). + +### IPython or Jupyter Notebook + +```ipython +%pip install tracerite +%load_ext tracerite +``` + +This enables tracebacks in text or HTML format depending on where you are running. Add to `~/.ipython/profile_default/startup/tracerite.ipy` to make it load automatically for all your ipython and notebook sessions. Alternatively, put the two lines at the top of your notebook. + +### FastAPI + +Add the extension loader at the top of your app module: + +```python +from tracerite import patch_fastapi; patch_fastapi() +``` + +This monkeypatches Starlette error handling and FastAPI routing to work with HTML tracebacks. Note: this runs regardless of whether you are in production mode or debug mode, so you might want to call that function only conditionally in the latter. + +### Sanic + +Comes with TraceRite built in whenever running in debug mode. + +## Clarity in complex situations + +![Exception chain comparison](https://raw.githubusercontent.com/sanic-org/tracerite/main/docs/screenshots/chain-comparison.webp) +*TraceRite shows even complex exception chains in chronological order, as opposed to the convoluted order of Python's own tracebacks where the entry point `func()` is near bottom and the flow jumps back and forth.* + +## Features + +- **Chronological order** - Single timeline with the program entry point is at top, and the finally uncaught exception bottom. +- **Minimalistic output** - Smart pruning to show only relevant pieces of information, excluding library internals where not relevant and avoiding any repetition. +- **ExceptionGroups** - Full tracebacks of the subexceptions from exceptions that occurred in parallel execution. +- **Variable inspection** - See the values of your variables in a pretty printed HTML format, Terminal or JSON-compatible machine-readable dict. +- **JSON output** - Intermediady dict format is JSON-compatible, useful for machine processing and used by our HTML and TTY modules. +- **HTML output** - Works in Jupyter, Colab, and web frameworks such as FastAPI and Sanic as the debug mode error handler. +- **TTY output** - Colorful, formatted tracebacks for terminal applications and Python REPL. +- **Custom Styling** - Theme with your colors by defining CSS variables or tty.COLORS. +- **Automatic dark mode** - Saves your eyes. + +![ExceptionGroup comparison](https://raw.githubusercontent.com/sanic-org/tracerite/main/docs/screenshots/group-comparison.webp) +*Python 3.11+ introduced `ExceptionGroup` for parallel execution errors (e.g., `asyncio.TaskGroup`). TraceRite displays these clearly.* + +## Usage + +### `html_traceback(exc)` + +Renders an exception as interactive HTML that can be included on a page. Pass an exception object, or call with no arguments inside an `except` block to use the current exception. + +### `extract_chain(exc)` + +Extracts exception information as a list of dictionaries—useful for logging, custom formatting, or machine processing. + +### `prettyvalue(value)` + +Formats any value with smart truncation, array shape display, and SI-scaled numerics. Useful beyond exceptions for debugging tools or custom logging. + +### `extract_variables(locals, source)` + +Extracts and formats variables mentioned in a line of source code. + +### `load()` / `unload()` + +Load or remove TraceRite as the default exception handler for terminal applications. Handles both `sys.excepthook` and `threading.excepthook`. + +### `tty_traceback(exc)` + +Renders an exception as colorful terminal output with ANSI escape codes. Pass an exception object, or call with no arguments inside an `except` block. + +See the [API documentation](https://github.com/sanic-org/tracerite/blob/main/docs/API.md) for details, or [Development guide](https://github.com/sanic-org/tracerite/blob/main/docs/Development.md) for contributors. + +## License + +Public Domain or equivalent. diff --git a/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/RECORD new file mode 100644 index 0000000..5d914d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/RECORD @@ -0,0 +1,29 @@ +tracerite-2.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +tracerite-2.3.1.dist-info/METADATA,sha256=ATix6Kr13GGNkDPv2QuNA3r3OAeBy_dr0l6zcla236o,5705 +tracerite-2.3.1.dist-info/RECORD,, +tracerite-2.3.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +tracerite-2.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87 +tracerite/__init__.py,sha256=A9XLqrEoeZzchfyYGVlhigSuihCNT4qjK4ekX2w3hXQ,503 +tracerite/__pycache__/__init__.cpython-312.pyc,, +tracerite/__pycache__/chain_analysis.cpython-312.pyc,, +tracerite/__pycache__/fastapi.cpython-312.pyc,, +tracerite/__pycache__/html.cpython-312.pyc,, +tracerite/__pycache__/inspector.cpython-312.pyc,, +tracerite/__pycache__/logging.cpython-312.pyc,, +tracerite/__pycache__/notebook.cpython-312.pyc,, +tracerite/__pycache__/syntaxerror.cpython-312.pyc,, +tracerite/__pycache__/trace.cpython-312.pyc,, +tracerite/__pycache__/trace_cpy.cpython-312.pyc,, +tracerite/__pycache__/tty.cpython-312.pyc,, +tracerite/chain_analysis.py,sha256=udM58egvoXNlJJ1rTUDjnx3L7H4FHdBVg49QMyF9JRg,26476 +tracerite/fastapi.py,sha256=5cA4Ey3TayORys1ImYNkNcEvlWHqw9iKWvHHq_o5t60,2131 +tracerite/html.py,sha256=hWGJiDSADCfsX2hboB7Gp6j0p1-ZHRbXON1zA6UvZh8,18870 +tracerite/inspector.py,sha256=SEaK5yhNRZvhrAaT7TDzErRWHBNaieNSqmy6Smsozaw,17615 +tracerite/logging.py,sha256=Zc6tHJn31aVo0UcBGQSSBwkrQPW2CIMMBLtYZdMXYV8,86 +tracerite/notebook.py,sha256=EbFXFsRykPknvaWZsvMwYqqrzQqhdyNd7Mqo6LIps_Y,3922 +tracerite/script.js,sha256=sEt37ft1udt2TyIASUnX9dUDgd3JCV2jqu24ITje8bg,750 +tracerite/style.css,sha256=UT-49NLqxzbh4wZgUHHDRoxppVU_MMT_O--fmOA6bzQ,13505 +tracerite/syntaxerror.py,sha256=iDZcE95etiuKPcV1JE7JuW18jIrkmfIkxdw-1IA4QFM,14779 +tracerite/trace.py,sha256=zfmUI2AWg5BdizzZYiGisp0DP70pV_4C_pzLEQrTTgs,69647 +tracerite/trace_cpy.py,sha256=5HYah0bGU4Q_vAZGQpFZQXPF20YSHtC_3_If27J38EA,14152 +tracerite/tty.py,sha256=AsHiNlRzQZA7yW_QKxVBHs2-gi8DGhRtHaoCQrykDiM,53207 diff --git a/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/WHEEL new file mode 100644 index 0000000..ae8ec1b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite-2.3.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.28.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/tracerite/__init__.py b/.venv/lib/python3.12/site-packages/tracerite/__init__.py new file mode 100644 index 0000000..5b2abaf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite/__init__.py @@ -0,0 +1,19 @@ +from .fastapi import patch_fastapi +from .html import html_traceback +from .inspector import extract_variables, prettyvalue +from .notebook import load_ipython_extension, unload_ipython_extension +from .trace import extract_chain +from .tty import load, tty_traceback, unload + +__all__ = [ + "load", + "unload", + "tty_traceback", + "html_traceback", + "extract_chain", + "prettyvalue", + "extract_variables", + "load_ipython_extension", + "unload_ipython_extension", + "patch_fastapi", +] diff --git a/.venv/lib/python3.12/site-packages/tracerite/chain_analysis.py b/.venv/lib/python3.12/site-packages/tracerite/chain_analysis.py new file mode 100644 index 0000000..5697f89 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite/chain_analysis.py @@ -0,0 +1,703 @@ +"""AST-based analysis for exception chain try-except block matching. + +This module provides utilities to build a chronological chain of events +during a multi-exception chain by analyzing the AST to identify which +try block contains the code that raised the inner exception, relative +to the except block where the outer exception was raised. + +Key insight about Python exception frames: +- Inner exception frames start from the try block (not app entry point) + So the first frame is always in the try body where the exception occurred. +- Outer exception frames start from app entry point and traverse through + the call stack. We search these to find a frame in an except handler + that corresponds to the inner's try block. + +ExceptionGroups (Python 3.11+) introduce parallel timelines: +- An ExceptionGroup contains multiple subexceptions that occurred concurrently +- Each subexception has its own traceback chain +- These parallel timelines are represented as branches in the chronological view +- The ExceptionGroup's own traceback provides the surrounding context +""" + +from __future__ import annotations + +import ast + +__all__ = [ + "TryExceptBlock", + "ChainLink", + "parse_source_for_try_except", + "parse_source_string_for_try_except", + "find_try_block_for_except_line", + "find_matching_try_for_inner_exception", + "analyze_exception_chain_links", + "enrich_chain_with_links", + "build_chronological_frames", +] +import linecache +from dataclasses import dataclass + +from .logging import logger + + +@dataclass +class TryExceptBlock: + """Represents a try-except block with its line ranges.""" + + try_start: int # First line of try keyword + try_end: int # Last line of try body (before except/else/finally) + except_start: int | None # First line of except handlers + except_end: int | None # Last line of except handlers + finally_start: int | None = None + finally_end: int | None = None + + def contains_in_try(self, lineno: int) -> bool: + """Check if a line number is within the try body.""" + return self.try_start <= lineno <= self.try_end + + def contains_in_except(self, lineno: int) -> bool: + """Check if a line number is within an except handler.""" + if self.except_start is None or self.except_end is None: + return False + return self.except_start <= lineno <= self.except_end + + +@dataclass +class ChainLink: + """Represents a link between two exceptions in a chain. + + Attributes: + outer_frame_idx: Index of the frame in the outer exception that's in the except block + try_block: The TryExceptBlock that links the inner and outer exceptions + matched: Whether we successfully matched the try-except relationship + """ + + outer_frame_idx: int + try_block: TryExceptBlock | None + matched: bool + + +class TryExceptVisitor(ast.NodeVisitor): + """AST visitor that collects all try-except blocks with their line ranges.""" + + def __init__(self): + self.try_except_blocks: list[TryExceptBlock] = [] + + def visit_Try(self, node: ast.Try): + """Visit a Try node and record its structure.""" + # Find the end of the try body + try_body_end = self._get_last_line(node.body) + + # Process except handlers + except_start = None + except_end = None + if node.handlers: + except_start = node.handlers[0].lineno + except_end = self._get_last_line(list(node.handlers)) + + # Process finally block + finally_start = None + finally_end = None + if node.finalbody: + finally_start = node.finalbody[0].lineno + finally_end = self._get_last_line(node.finalbody) + + if except_start is not None: + block = TryExceptBlock( + try_start=node.lineno, + try_end=try_body_end, + except_start=except_start, + except_end=except_end, + finally_start=finally_start, + finally_end=finally_end, + ) + self.try_except_blocks.append(block) + + # Continue visiting nested structures + self.generic_visit(node) + + def _get_last_line(self, nodes) -> int: + """Get the last line number from a list of AST nodes.""" + last_line = 0 + for node in nodes: + last_line = max(last_line, getattr(node, "end_lineno", node.lineno)) + return last_line + + +def parse_source_for_try_except( + filename: str, function_name: str | None = None +) -> list[TryExceptBlock]: + """Parse source file and extract try-except blocks. + + Args: + filename: Path to the source file + function_name: Optional function name to limit scope + + Returns: + List of TryExceptBlock objects found in the source + """ + try: + lines = linecache.getlines(filename) + if not lines: + return [] + + source = "".join(lines) + tree = ast.parse(source, filename=filename) + + visitor = TryExceptVisitor() + visitor.visit(tree) + + return visitor.try_except_blocks + except (SyntaxError, OSError, ValueError) as e: + logger.debug(f"Failed to parse {filename} for try-except analysis: {e}") + return [] + + +def parse_source_string_for_try_except( + source: str, start_line: int = 1 +) -> list[TryExceptBlock]: + """Parse source string and extract try-except blocks. + + Args: + source: The source code as a string + start_line: The line number where this source starts (for offset adjustment) + + Returns: + List of TryExceptBlock objects found in the source + """ + try: + if not source: + return [] + + tree = ast.parse(source) + + visitor = TryExceptVisitor() + visitor.visit(tree) + + # Adjust line numbers if source doesn't start at line 1 + if start_line != 1: + offset = start_line - 1 + adjusted_blocks = [] + for block in visitor.try_except_blocks: + adjusted_blocks.append( + TryExceptBlock( + try_start=block.try_start + offset, + try_end=block.try_end + offset, + except_start=block.except_start + offset + if block.except_start + else None, + except_end=block.except_end + offset + if block.except_end + else None, + finally_start=block.finally_start + offset + if block.finally_start + else None, + finally_end=block.finally_end + offset + if block.finally_end + else None, + ) + ) + return adjusted_blocks + + return visitor.try_except_blocks + except (SyntaxError, ValueError) as e: + logger.debug(f"Failed to parse source string for try-except analysis: {e}") + return [] + + +def find_try_block_for_except_line( + blocks: list[TryExceptBlock], except_lineno: int +) -> TryExceptBlock | None: + """Find the try-except block that contains the given line in its except handler. + + Args: + blocks: List of TryExceptBlock objects + except_lineno: Line number that should be within an except handler + + Returns: + The TryExceptBlock if found, None otherwise + """ + # Sort by specificity - prefer innermost blocks (those with higher try_start) + matching_blocks = [b for b in blocks if b.contains_in_except(except_lineno)] + + if not matching_blocks: + return None + + # Return the most specific (innermost) block + return max(matching_blocks, key=lambda b: b.try_start) + + +def find_matching_try_for_inner_exception( + blocks: list[TryExceptBlock], inner_first_lineno: int, outer_except_lineno: int +) -> TryExceptBlock | None: + """Find the try block that contains the inner exception's first frame + and whose except block contains the outer exception's frame. + + This is the key function for building the chronological chain: + - inner_first_lineno: The line in the inner exception's first frame (in the try body) + - outer_except_lineno: A line from the outer exception that's in an except block + + Args: + blocks: List of TryExceptBlock objects + inner_first_lineno: First line of the inner exception's traceback + outer_except_lineno: Line from outer exception that should be in except block + + Returns: + The TryExceptBlock that links both exceptions, or None if no match + """ + for block in blocks: + # The outer exception line must be in the except handler + if not block.contains_in_except(outer_except_lineno): + continue + # The inner exception's first line must be in the try body + if block.contains_in_try(inner_first_lineno): + return block + + return None + + +def analyze_exception_chain_links(chain: list[dict]) -> list[ChainLink | None]: + """Analyze an exception chain to find try-except relationships. + + For each pair of consecutive exceptions in the chain (inner, outer), + find the try-except block that links them. + + Args: + chain: List of exception info dicts from extract_chain (oldest first) + + Returns: + List of ChainLink objects (one per exception, first is always None + since the first exception has no prior exception to link to) + """ + if len(chain) <= 1: + return [None] * len(chain) + + links: list[ChainLink | None] = [None] # First exception has no link + + for i in range(1, len(chain)): + inner_exc = chain[i - 1] + outer_exc = chain[i] + + link = _find_chain_link(inner_exc, outer_exc) + links.append(link) + + return links + + +def _get_frame_lineno(frame: dict) -> int | None: + """Extract the line number from a frame dict. + + Tries in order: + 1. range[0] (lfirst) - most precise, from Python 3.11+ co_positions + 2. lineno - actual traceback line number (always available) + 3. linenostart - first line of displayed source (fallback) + """ + frame_range = frame.get("range") + if frame_range: + return frame_range[0] # lfirst from Range namedtuple + # Fallback to lineno (actual error line from traceback) + if frame.get("lineno"): + return frame.get("lineno") + # Final fallback to linenostart (first line of displayed source) + return frame.get("linenostart") + + +def _find_chain_link(inner_exc: dict, outer_exc: dict) -> ChainLink | None: + """Find the try-except link between two consecutive exceptions. + + The inner exception's frames start from the try block (not app entry point), + so its first frame is always the one in the try block we're looking for. + + The outer exception's frames start from app entry point and traverse through + the call stack. We need to search these frames to find one that's within + an except handler that corresponds to the inner's try block. + + Args: + inner_exc: The earlier/inner exception info dict + outer_exc: The later/outer exception info dict + + Returns: + ChainLink if a relationship is found, None otherwise + """ + inner_frames = inner_exc.get("frames", []) + outer_frames = outer_exc.get("frames", []) + + if not inner_frames or not outer_frames: + return None + + # Inner exception: first frame is always in the try block + # (Python captures frames starting from the try block, not app entry) + inner_first_frame = inner_frames[0] + inner_first_lineno = _get_frame_lineno(inner_first_frame) + + if inner_first_lineno is None: + return None + + # Get try-except blocks from the frame's full source (works with any source) + # This avoids reading from files, using the source Python already has + inner_full_source = inner_first_frame.get("full_source") + inner_source_start = inner_first_frame.get("full_source_start", 1) + + if inner_full_source: + try_except_blocks = parse_source_string_for_try_except( + inner_full_source, inner_source_start + ) + else: + # Fallback to file-based parsing if full_source not available + inner_filename = inner_first_frame.get( + "original_filename" + ) or inner_first_frame.get("filename") + if not inner_filename: + return None + try_except_blocks = parse_source_for_try_except(inner_filename) + + if not try_except_blocks: + return None + + # Outer exception: search through ALL frames to find one in an except block + # The frame list starts from app entry point and may visit the same function + # multiple times, or have additional frames after the except block + for frame_idx, frame in enumerate(outer_frames): + frame_lineno = _get_frame_lineno(frame) + if frame_lineno is None: + continue + + # Try to find a try-except block where: + # - inner_first_lineno is in the try body + # - frame_lineno is in the except handler + matching_block = find_matching_try_for_inner_exception( + try_except_blocks, inner_first_lineno, frame_lineno + ) + + if matching_block: + return ChainLink( + outer_frame_idx=frame_idx, + try_block=matching_block, + matched=True, + ) + + return None + + +def enrich_chain_with_links(chain: list[dict]) -> list[dict]: + """Enrich exception chain with try-except link information. + + Adds a 'chain_link' key to each exception dict with information + about how it relates to the previous exception in the chain. + + Args: + chain: List of exception info dicts from extract_chain (oldest first) + + Returns: + The same chain list, with 'chain_link' added to each dict + """ + links = analyze_exception_chain_links(chain) + + for _i, (exc, link) in enumerate(zip(chain, links)): + if link and link.matched: + exc["chain_link"] = { + "outer_frame_idx": link.outer_frame_idx, + "try_start": link.try_block.try_start, + "try_end": link.try_block.try_end, + "except_start": link.try_block.except_start, + "except_end": link.try_block.except_end, + } + else: + exc["chain_link"] = None + + return chain + + +def build_chronological_frames(chain: list[dict]) -> list[dict]: + """Build a chronological list of frames showing the actual sequence of events. + + This creates a flat list of frames in the order they were executed, with + exception information embedded in the frames. The result shows: + 1. Frames from entry point leading to the try block + 2. The inner exception's frames (from try block to error) + 3. The except handler frame (promoted to relevance="except") + 4. Any frames after except leading to the next exception + 5. ...and so on for nested chains + + For ExceptionGroups: + - Subexceptions form parallel timelines that occurred concurrently + - These are represented as a special "parallel" frame containing multiple branches + - Each branch is itself a list of chronological frames + - The parallel block is inserted before the ExceptionGroup's error frame + + The key insight is that the LAST exception in the chain has the full call + stack from entry point. Inner exceptions only have partial stacks starting + from where they were raised. We use the outer exception's frames as the + "backbone" and insert inner exception frames at the appropriate positions. + + Hidden frames (marked with hidden=True, e.g., from __tracebackhide__) are + kept during chain analysis to enable proper try-except matching, then + filtered out at the end. + + Args: + chain: List of exception info dicts from extract_chain (oldest first), + should already be enriched with chain_link info + + Returns: + List of frame dicts in chronological order. Each frame may have: + - "exception": dict with exception info (type, message, from) if this + frame is where an exception was raised + - "relevance": may be promoted to "except" for except handler frames + - "parallel": list of parallel branches (for ExceptionGroup subexceptions) + """ + if not chain: + return [] + + # First, ensure chain has link info + links = analyze_exception_chain_links(chain) + + # Build chronological frames by working backwards from the last exception + # The last exception has the full call stack; inner exceptions have partial stacks + chronological: list[dict] = [] + + # Process from the last (outermost) exception backwards + # This lets us use the outer exception's frames as the backbone + for exc_idx in range(len(chain) - 1, -1, -1): + exc = chain[exc_idx] + frames = exc.get("frames", []) + + if not frames: + continue + + # Check if this exception has a link to an inner exception + # (i.e., is there a next exception in the chain that links to this one?) + links[exc_idx + 1] if exc_idx + 1 < len(chain) else None + + if exc_idx == len(chain) - 1: + # This is the outermost exception - use its frames as the backbone + # But we need to insert inner exception frames at the right positions + _build_backbone_frames(chronological, exc, exc_idx, frames, links, chain) + # Inner exceptions are handled by _build_backbone_frames inserting them + + # Filter out hidden frames (kept for chain analysis, not for display) + chronological = _filter_hidden_frames(chronological) + + # Apply suppression for BaseExceptions (KeyboardInterrupt, SystemExit, etc.) + # These should only show frames up to the last user code frame ("bug frame"), + # suppressing library internals that came after + chronological = _apply_base_exception_suppression(chronological, chain) + + return chronological + + +def _filter_hidden_frames(chronological: list[dict]) -> list[dict]: + """Filter out hidden frames, handling parallel branches recursively.""" + result = [] + for frame in chronological: + if frame.get("hidden"): + continue + # Recursively filter parallel branches + if "parallel" in frame: + filtered_branches = [] + for branch in frame["parallel"]: + filtered_branch = _filter_hidden_frames(branch) + if filtered_branch: + filtered_branches.append(filtered_branch) + if filtered_branches: + frame = {**frame, "parallel": filtered_branches} + result.append(frame) + else: + result.append(frame) + return result + + +def _apply_base_exception_suppression( + chronological: list[dict], chain: list[dict] +) -> list[dict]: + """Suppress library frames after the last user code frame for BaseExceptions. + + For BaseExceptions (KeyboardInterrupt, SystemExit, etc.), we don't want to + show library internals - only show frames up to the last user code frame + (the "bug frame"). The exception info is transferred to the last shown frame. + + Args: + chronological: The full list of chronological frames + chain: The exception chain (to check suppress_inner flag) + + Returns: + Filtered list of frames with appropriate adjustments + """ + if not chronological or not chain: + return chronological + + # Check if any exception in the chain has suppress_inner=True + # (This is set for BaseExceptions like KeyboardInterrupt, SystemExit) + has_suppress = any(exc.get("suppress_inner") for exc in chain) + if not has_suppress: + return chronological + + # Find the last "bug frame" (last user code frame before library code) + # This is marked by relevance="warning" during extraction + last_bug_frame_idx = None + for idx, frame in enumerate(chronological): + if frame.get("relevance") == "warning": + last_bug_frame_idx = idx + + if last_bug_frame_idx is None: + # No bug frame found, return as-is + return chronological + + # Suppress all frames after the bug frame + result = chronological[: last_bug_frame_idx + 1] + + # Transfer exception info and parallel branches from suppressed frames to the last shown frame + if result: # pragma: no cover + # Check if any suppressed frame had an exception or parallel branches attached + suppressed_exception = None + suppressed_parallel = None + for suppressed in chronological[last_bug_frame_idx + 1 :]: + if suppressed.get("exception") and not suppressed_exception: + suppressed_exception = suppressed["exception"] + if suppressed.get("parallel") and not suppressed_parallel: + suppressed_parallel = suppressed["parallel"] + + # If we're suppressing frames with an exception, transfer it to the last shown frame + if suppressed_exception and not result[-1].get("exception"): + result[-1] = {**result[-1], "exception": suppressed_exception} + + # Transfer parallel branches (subexceptions) to the last shown frame + if suppressed_parallel and not result[-1].get("parallel"): + result[-1] = {**result[-1], "parallel": suppressed_parallel} + + # Change relevance to "stop" to indicate suppression point + # (unless it already has an exception, which sets its own relevance) + if result[-1].get("relevance") in ("call", "warning"): # pragma: no cover + result[-1] = {**result[-1], "relevance": "stop"} + + return result + + +def _build_backbone_frames( + chronological: list[dict], + exc: dict, + exc_idx: int, + frames: list[dict], + links: list, + chain: list[dict], +) -> None: + """Build chronological frames using this exception's frames as backbone. + + Recursively inserts inner exception frames at the appropriate positions. + Also handles ExceptionGroup subexceptions as parallel branches. + """ + # Find if there's an inner exception that links to one of our frames + inner_exc_idx = exc_idx - 1 + inner_link = links[exc_idx] if exc_idx > 0 else None + + # If we have an inner exception with a matched link, we need to: + # 1. Output frames from start up to (but not including) the except handler + # 2. Insert the inner exception's frames + # 3. Output the except handler frame and any frames after it + + if inner_link and inner_link.matched and inner_exc_idx >= 0: + except_frame_idx = inner_link.outer_frame_idx + inner_exc = chain[inner_exc_idx] + inner_frames = inner_exc.get("frames", []) + + # Output frames before the except handler (these are the call stack leading to try) + for frame_idx in range(except_frame_idx): + frame = frames[frame_idx] + chrono_frame = _copy_frame(frame) + chronological.append(chrono_frame) + + # Recursively handle the inner exception + # Always recurse to handle even more inner exceptions (best effort) + _build_backbone_frames( + chronological, inner_exc, inner_exc_idx, inner_frames, links, chain + ) + + # Now output the except handler frame and frames after it + for frame_idx in range(except_frame_idx, len(frames)): + frame = frames[frame_idx] + chrono_frame = _copy_frame(frame) + + is_except_entry = frame_idx == except_frame_idx + is_last = frame_idx == len(frames) - 1 + + if is_except_entry: + # Promote relevance to "except" if it was just a "call" or "warning" frame + if chrono_frame.get("relevance") in ("call", "warning"): + chrono_frame["relevance"] = "except" + chrono_frame["function_suffix"] = "⚡except" + + if is_last: + chrono_frame["exception"] = { + "type": exc.get("type"), + "message": exc.get("message"), + "summary": exc.get("summary"), + "from": exc.get("from"), + "exc_idx": exc_idx, + } + # Handle subexceptions for this ExceptionGroup + _add_subexceptions_to_frame(chrono_frame, exc) + + chronological.append(chrono_frame) + else: + # No matched inner exception link - but still include inner exceptions (best effort) + # First, recursively process any inner exceptions + if exc_idx > 0: + inner_exc = chain[exc_idx - 1] + inner_frames = inner_exc.get("frames", []) + if inner_frames: + _build_backbone_frames( + chronological, + inner_exc, + exc_idx - 1, + inner_frames, + links, + chain, + ) + + # Then output all frames for this exception + for frame_idx, frame in enumerate(frames): + chrono_frame = _copy_frame(frame) + is_last = frame_idx == len(frames) - 1 + + if is_last: + chrono_frame["exception"] = { + "type": exc.get("type"), + "message": exc.get("message"), + "summary": exc.get("summary"), + "from": exc.get("from"), + "exc_idx": exc_idx, + } + # Handle subexceptions for this ExceptionGroup + _add_subexceptions_to_frame(chrono_frame, exc) + + chronological.append(chrono_frame) + + +def _add_subexceptions_to_frame(frame: dict, exc: dict) -> None: + """Add subexceptions from an ExceptionGroup as parallel branches. + + If the exception has subexceptions (from an ExceptionGroup), each one + is processed into its own chronological frame list and added as a + parallel branch to the frame. + + Args: + frame: The frame dict to add parallel branches to + exc: The exception dict that may contain subexceptions + """ + subexceptions = exc.get("subexceptions") + if not subexceptions: + return + + parallel_branches = [] + for sub_chain in subexceptions: + # Build chronological frames for this subexception chain + sub_chrono = build_chronological_frames(sub_chain) + if sub_chrono: + parallel_branches.append(sub_chrono) + + if parallel_branches: + frame["parallel"] = parallel_branches + + +def _copy_frame(frame: dict) -> dict: + """Create a shallow copy of a frame dict.""" + return {**frame} diff --git a/.venv/lib/python3.12/site-packages/tracerite/fastapi.py b/.venv/lib/python3.12/site-packages/tracerite/fastapi.py new file mode 100644 index 0000000..8d93795 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite/fastapi.py @@ -0,0 +1,57 @@ +"""TraceRite extension for FastAPI/Starlette applications.""" + +from __future__ import annotations + +from typing import Any + +from .html import html_traceback +from .logging import logger +from .tty import load as load_tty + +_original_debug_response = None + + +def patch_fastapi(*, tty: bool = True) -> None: + """ + Load TraceRite extension for FastAPI by patching ServerErrorMiddleware. + + This patches Starlette's ServerErrorMiddleware.debug_response to return + TraceRite HTML tracebacks instead of the default debug HTML when running + in debug mode and the client accepts HTML. + + Additionally, we load the TTY formatting for console output. + """ + global _original_debug_response + if _original_debug_response is not None: + return # Already loaded + try: + import fastapi # ty: ignore + from starlette.middleware.errors import ServerErrorMiddleware # ty: ignore + from starlette.responses import HTMLResponse # ty: ignore + except ImportError: + logger.info("TraceRite FastAPI cannot load: FastAPI/Starlette not found") + return + _original_debug_response = ServerErrorMiddleware.debug_response + + def tracerite_debug_response(self, request, exc) -> Any: + """Return TraceRite HTML traceback instead of Starlette's debug response.""" + accept = request.headers.get("accept", "") + if "text/html" not in accept: + return _original_debug_response(self, request, exc) # type: ignore + try: + html = str(html_traceback(exc=exc, include_js_css=True)) + return HTMLResponse( + "FastAPI TraceRite" + html, + status_code=500, + ) + except Exception as e: + logger.error(f"Failed to generate TraceRite response: {e}") + return _original_debug_response(self, request, exc) # type: ignore + + ServerErrorMiddleware.debug_response = tracerite_debug_response # type: ignore + fastapi.routing.__tracebackhide__ = "until" # type: ignore + + if tty: + load_tty() + + logger.info("TraceRite FastAPI extension loaded") diff --git a/.venv/lib/python3.12/site-packages/tracerite/html.py b/.venv/lib/python3.12/site-packages/tracerite/html.py new file mode 100644 index 0000000..93374cb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/tracerite/html.py @@ -0,0 +1,526 @@ +from __future__ import annotations + +from importlib.resources import files +from typing import Any, cast + +from html5tagger import HTML, E # type: ignore[import] + +from .chain_analysis import build_chronological_frames +from .trace import build_chain_header, chainmsg, extract_chain, symbols, symdesc + +style = files(cast(str, __package__)).joinpath("style.css").read_text(encoding="UTF-8") +javascript = ( + files(cast(str, __package__)).joinpath("script.js").read_text(encoding="UTF-8") +) + +detail_show = "{display: inherit}" + + +def _collapse_call_runs( + frames: list[dict[str, Any]], min_run_length: int = 10 +) -> list[Any]: + """Collapse consecutive runs of 'call' frames, keeping first and last of each run. + + Only collapses runs of frames with relevance='call'. Non-call frames + (error, warning, stop) are never collapsed. + """ + if not frames: + return frames + + result = [] + run_start = None + + for i, frinfo in enumerate(frames): + if frinfo["relevance"] == "call": + if run_start is None: + run_start = i + else: + # End of a call run - process it + if run_start is not None: + run_length = i - run_start + if run_length >= min_run_length: + # Keep first and last of the run, add ellipsis + result.append(frames[run_start]) + result.append(...) + result.append(frames[i - 1]) + else: + # Run too short, keep all + result.extend(frames[run_start:i]) + run_start = None + # Add the non-call frame + result.append(frinfo) + + # Handle final run at end + if run_start is not None: + run_length = len(frames) - run_start + if run_length >= min_run_length: + result.append(frames[run_start]) + result.append(...) + result.append(frames[-1]) + else: + result.extend(frames[run_start:]) + + return result + + +def html_traceback( + exc: BaseException | None = None, + chain: list[dict[str, Any]] | None = None, + *, + msg: str | None = ..., # type: ignore[assignment] + include_js_css: bool = True, + local_urls: bool = False, + replace_previous: bool = False, + cleanup_mode: str = "replace", + autodark: bool = True, + **extract_args: Any, +) -> Any: + chain = chain or extract_chain(exc=exc, **extract_args)[-3:] + # Chain is oldest-first from extract_chain + classes = "tracerite autodark" if autodark else "tracerite" + with E.div( + class_=classes, + data_replace_previous="1" if replace_previous else None, + data_cleanup_mode=cleanup_mode if replace_previous else None, + ) as doc: + if include_js_css: + doc._style(style) + + # Add chain header (use default if msg is ..., skip if msg is None/empty) + if msg is ...: + msg = build_chain_header(chain) if chain else None + if msg: + doc.h2(msg) + + _chronological_output(doc, chain, local_urls=local_urls) + + if include_js_css: + doc._script(javascript) + return doc + + +def _chronological_output( + doc: Any, + chain: list[dict[str, Any]], + *, + local_urls: bool = False, +) -> None: + """Output frames in chronological order with exception info after error frames.""" + chrono_frames = build_chronological_frames(chain) + if not chrono_frames: + # No frames, but still show exception banners for any exceptions in chain + for exc in chain: + exc_info = { + "type": exc.get("type"), + "message": exc.get("message"), + "summary": exc.get("summary"), + "from": exc.get("from"), + } + _exception_banner(doc, exc_info) + return + + _render_frame_list(doc, chrono_frames, local_urls=local_urls) + + +def _render_frame_list( + doc: Any, + frames: list[dict[str, Any]], + *, + local_urls: bool = False, +) -> None: + """Render a list of chronological frames, handling parallel branches.""" + # Collapse consecutive call runs + limited_frames = _collapse_call_runs(frames, min_run_length=10) + + for frinfo in limited_frames: + if frinfo is ...: + doc.p("...", class_="traceback-ellipsis") + continue + + relevance = frinfo["relevance"] + exc_info = frinfo.get("exception") + parallel_branches = frinfo.get("parallel") + + attrs = { + "class_": f"traceback-details traceback-{relevance}", + "data_function": frinfo["function"], + "id": frinfo["id"], + } + with doc.div(**attrs): + # Hidden checkbox for CSS-only toggle (all frames are collapsible) + toggle_id = f"toggle-{frinfo['id']}" + # Non-call frames are open by default (checked) + if relevance == "call": + doc.input_( + type="checkbox", id=toggle_id, class_="frame-toggle-checkbox" + ) + else: + doc.input_( + type="checkbox", + id=toggle_id, + class_="frame-toggle-checkbox", + checked="checked", + ) + _frame_label( + doc, + frinfo, + local_urls=local_urls, + toggle_id=toggle_id, + ) + with doc.span(class_="compact-call-line"): + _compact_code_line(doc, frinfo) + # Animated wrapper for expandable content + with doc.div(class_="expand-wrapper"), doc.div(class_="expand-content"): + _traceback_detail_chrono(doc, frinfo) + variable_inspector(doc, frinfo["variables"]) + + # Render parallel branches (subexceptions) before the exception banner + if parallel_branches: + _render_parallel_branches(doc, parallel_branches, local_urls=local_urls) + + # Print exception info AFTER the error frame (and parallel branches) + if exc_info: + _exception_banner(doc, exc_info) + + +def _render_parallel_branches( + doc: Any, + branches: list[list[dict[str, Any]]], + *, + local_urls: bool = False, +) -> None: + """Render parallel exception branches from an ExceptionGroup. + + Each branch is rendered side by side. + """ + with doc.div(class_="parallel-branches"): + for branch in branches: + with doc.div(class_="parallel-branch"): + _render_frame_list(doc, branch, local_urls=local_urls) + + +def _exception_banner(doc: Any, exc_info: dict[str, Any]) -> None: + """Output exception type and message as a banner after the error frame.""" + exc_type = exc_info.get("type", "Exception") + summary = exc_info.get("summary", "") + message = exc_info.get("message", "") + from_type = exc_info.get("from", "none") + + chain_suffix = chainmsg.get(from_type, "") + + doc.h3(E.span(f"{exc_type}{chain_suffix}:", class_="exctype")(f" {summary}")) + # Show remaining lines in pre only if message has multiple lines + # Summary is always the first line, so we strip that from pre + parts = message.split("\n", 1) + if len(parts) > 1: + rest = parts[1].rstrip() # Only strip trailing whitespace, preserve leading + if rest: + doc.pre(rest, class_="excmessage") + + +def _compact_code_line(doc: Any, frinfo: dict[str, Any]) -> None: + """Render compact one-liner showing all marked code regions. + + Em parts (typically function arguments) longer than 20 chars are + collapsed to show only first and last char with ellipsis. + """ + fragments = frinfo["fragments"] + relevance = frinfo["relevance"] + symbol = symbols.get(relevance, symbols["call"]) + # Use highlight styling (yellow bg, red caret) for error/stop frames + use_highlight = relevance in ("error", "stop") + + if fragments: + # First pass: collect text and track em ranges + code_parts = [] # [(text, is_em), ...] + + for line_info in fragments: + for fragment in line_info.get("fragments", []): + mark = fragment.get("mark") + if mark: + em = fragment.get("em") + text = fragment["code"] + is_em = em is not None + code_parts.append((text, is_em)) + + # Find the em span (from first em start to last em end) + em_indices = [i for i, (_, is_em) in enumerate(code_parts) if is_em] + + # Collapse em parts longer than 20 chars + if em_indices: + first_em_idx = min(em_indices) + last_em_idx = max(em_indices) + em_text = "".join( + text + for i, (text, _) in enumerate(code_parts) + if first_em_idx <= i <= last_em_idx + ) + if len(em_text) > 20: + # Collapse: keep first and last char (typically parentheses) + collapsed = em_text[0] + "…" + em_text[-1] + # Rebuild code_parts with collapsed em + new_parts = [] + for i, (text, _is_em) in enumerate(code_parts): + if i < first_em_idx: + new_parts.append((text, False)) + elif i == first_em_idx: + new_parts.append((collapsed, True)) + elif i > last_em_idx: # pragma: no cover + new_parts.append((text, False)) + # Skip parts within em range (already collapsed) + code_parts = new_parts + + with doc.code(class_="compact-code"): + if use_highlight: + doc(HTML("")) + + for text, is_em in code_parts: + if is_em: + doc(HTML("")) + doc(text) + if is_em: + doc(HTML("")) + + if use_highlight: + doc(HTML("")) + # Add space before symbol for error/stop frames + if use_highlight: + doc(" ") + doc.span(symbol, class_="compact-symbol") + + +def _traceback_detail_chrono(doc: Any, frinfo: dict[str, Any]) -> None: + """Render frame detail in chronological mode.""" + fragments = frinfo["fragments"] + relevance = frinfo["relevance"] + + if not fragments: + # Show "(no source code)" with the symbol emoji like a code line would have + symbol = symbols.get(relevance, "") + doc.p(f"(no source code) {symbol}") + return + + with doc.pre, doc.code: + start = frinfo["linenostart"] + for line_info in fragments: + line_num = line_info["line"] + abs_line = start + line_num - 1 + line_fragments = line_info["fragments"] + + # Prepare tooltip attributes for tooltip span on final line + tooltip_attrs = {} + frame_range = frinfo["range"] + if frame_range and abs_line == frame_range.lfinal: + relevance = frinfo["relevance"] + symbol = symbols.get(relevance, relevance) + text = symdesc.get(relevance, relevance) + tooltip_attrs = { + "class": "tracerite-tooltip", + "data-symbol": symbol, + "data-tooltip": text, + } + + with doc.span(class_="codeline", data_lineno=abs_line): + non_trailing_fragments = [] + trailing_fragment = None + for fragment in line_fragments: + if "trailing" in fragment: + trailing_fragment = fragment + break + non_trailing_fragments.append(fragment) + + if non_trailing_fragments: + first_fragment = non_trailing_fragments[0] + code = first_fragment["code"] + leading_whitespace = code[: len(code) - len(code.lstrip())] + if leading_whitespace: + doc(leading_whitespace) + first_fragment_modified = { + **first_fragment, + "code": code.lstrip(), + } + non_trailing_fragments[0] = first_fragment_modified + + if tooltip_attrs and non_trailing_fragments: + with doc.span( + class_="tracerite-tooltip", + data_tooltip=tooltip_attrs["data-tooltip"], + ): + for fragment in non_trailing_fragments: + _render_fragment(doc, fragment) + doc.span( + class_="tracerite-symbol", + data_symbol=tooltip_attrs["data-symbol"], + ) + doc.span( + class_="tracerite-tooltip-text", + data_tooltip=tooltip_attrs["data-tooltip"], + ) + else: + for fragment in non_trailing_fragments: + _render_fragment(doc, fragment) + + fragment = trailing_fragment + if fragment: + _render_fragment(doc, fragment) + + +def _frame_label( + doc: Any, + frinfo: dict[str, Any], + local_urls: bool = False, + toggle_id: str | None = None, +) -> None: + function_name = frinfo["function"] + function_suffix = frinfo.get("function_suffix", "") + if function_name: + function_display = f"{function_name}{function_suffix}" + elif function_suffix: + function_display = function_suffix + else: + function_display = None # No function to display + + # Location comes first, with line number for non-notebook frames + # Notebook cells (In [N]) don't need line numbers displayed + notebook_cell = frinfo.get("notebook_cell", False) + if notebook_cell: + lineno = None + else: + # Use cursor_line for display (preferred error position) + cursor_line = frinfo.get("cursor_line") or frinfo.get("linenostart", 1) + lineno = E.span( + f":{cursor_line}", + class_="frame-lineno", + ) + + # Colon after function name, or after location if no function + colon = E.span(":", class_="frame-colon") + + # Get VS Code URL if local_urls enabled + urls = frinfo.get("urls", {}) + vscode_url = urls.get("VS Code") if local_urls else None + + # Build location element - wrap in if we have a VS Code URL + def render_location(with_colon: bool = False): + location_parts = ( + (frinfo["location"], lineno) if lineno else (frinfo["location"],) + ) + if with_colon: + location_parts = (*location_parts, colon) + if vscode_url: + doc.a(*location_parts, href=vscode_url, class_="frame-location") + else: + doc.span(*location_parts, class_="frame-location") + + if toggle_id: + # Wrap both location and function in a label for click-to-toggle + with doc.label(for_=toggle_id, class_="frame-label-wrapper"): + if function_display: + render_location() + doc.span(function_display, colon, class_="frame-function") + else: + # No function: colon goes with location, empty span for grid column 2 + render_location(with_colon=True) + doc.span(class_="frame-function") + else: + if function_display: + render_location() + doc.span(function_display, colon, class_="frame-function") + else: + # No function: colon goes with location, empty span for grid column 2 + render_location(with_colon=True) + doc.span(class_="frame-function") + + +def _render_fragment(doc: Any, fragment: dict[str, Any]) -> None: + """Render a single fragment with appropriate styling.""" + code = fragment["code"] + + mark = fragment.get("mark") + em = fragment.get("em") + + # Render opening tags for "mark" and "em" if applicable + if mark in ["solo", "beg"]: + doc(HTML("")) + if em in ["solo", "beg"]: + doc(HTML("")) + + # Render the code + doc(code) + + # Render closing tags for "mark" and "em" if applicable + if em in ["fin", "solo"]: + doc(HTML("")) + if mark in ["fin", "solo"]: + doc(HTML("")) + + +def variable_inspector(doc: Any, variables: list[Any]) -> None: + if not variables: + return + with doc.dl(class_="inspector"): + for var_info in variables: + # Handle both old tuple format and new VarInfo namedtuple + if hasattr(var_info, "name"): + n, t, v, fmt = ( + var_info.name, + var_info.typename, + var_info.value, + var_info.format_hint, + ) + else: + # Backwards compatibility with old tuple format + n, t, v = var_info + fmt = "inline" + + doc.dt.span(n, class_="var") + if t: + doc(": ").span(f"{t}\u00a0=\u00a0", class_="type") + else: + doc("\u00a0").span("=\u00a0", class_="type") # No type printed + doc.dd(class_=f"val val-{fmt}") + if isinstance(v, str): + if fmt == "block": + # For block format, use
     tag for proper formatting
    +                    doc.pre(v)
    +                else:
    +                    doc(v)
    +            elif isinstance(v, dict) and v.get("type") == "keyvalue":
    +                _format_keyvalue(doc, v["rows"])
    +            elif isinstance(v, dict) and v.get("type") == "array":
    +                with doc.div(class_="array-with-scale"):
    +                    _format_matrix(doc, v["rows"])
    +                    if v.get("suffix"):
    +                        doc.span(v["suffix"], class_="scale-suffix")
    +            else:
    +                _format_matrix(doc, v)
    +
    +
    +def _format_keyvalue(doc: Any, rows: list[tuple[str, Any]]) -> None:
    +    """Format key-value pairs (dicts, dataclasses) as a definition list."""
    +    with doc.dl(class_="keyvalue-dl"):
    +        for key, val in rows:
    +            doc.dt(key)
    +            doc.dd(val)
    +
    +
    +def _format_matrix(doc: Any, v: Any) -> None:
    +    skipcol = skiprow = False
    +    with doc.table:
    +        for row in v:
    +            if row[0] is None:
    +                skiprow = True
    +                continue
    +            doc.tr()
    +            if skiprow:
    +                skiprow = False
    +                doc(class_="skippedabove")
    +            for e in row:
    +                if e is None:
    +                    skipcol = True
    +                    continue
    +                if skipcol:
    +                    skipcol = False
    +                    doc.td(e, class_="skippedleft")
    +                else:
    +                    doc.td(e)
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/inspector.py b/.venv/lib/python3.12/site-packages/tracerite/inspector.py
    new file mode 100644
    index 0000000..c87082b
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/inspector.py
    @@ -0,0 +1,452 @@
    +from __future__ import annotations
    +
    +import ast
    +import contextlib
    +import dataclasses
    +import math
    +import re
    +import types
    +from collections import namedtuple
    +from functools import reduce
    +from typing import Any, Callable
    +
    +from .logging import logger
    +
    +# Variable info with formatting metadata
    +VarInfo = namedtuple("VarInfo", ["name", "typename", "value", "format_hint"])
    +
    +
    +def _format_scalar(v: Any) -> str:
    +    """Format a single numeric value intelligently."""
    +    # Integer types (including numpy integers) - display without decimals
    +    if isinstance(v, int) or (hasattr(v, "dtype") and "int" in str(v.dtype)):
    +        return str(int(v))
    +    # Float types
    +    if isinstance(v, float) or (hasattr(v, "dtype") and "float" in str(v.dtype)):
    +        v = float(v)
    +        if v != v:  # NaN
    +            return "NaN"
    +        if abs(v) == float("inf"):
    +            return "∞" if v > 0 else "-∞"
    +        if v == 0:
    +            return "0"
    +        if v == int(v) and abs(v) < 1e15:
    +            return str(int(v))
    +        # Compact fixed-point: strip trailing zeros
    +        return f"{v:.6f}".rstrip("0").rstrip(".")
    +    return str(v)
    +
    +
    +# Superscript digits for formatting powers of 10
    +_SUPERSCRIPT_DIGITS = str.maketrans("-0123456789", "⁻⁰¹²³⁴⁵⁶⁷⁸⁹")
    +
    +
    +def _get_flat(arr: Any) -> list[Any]:
    +    """Get a flat/1D view of an array, supporting numpy and torch."""
    +    # Try numpy-style .flat first
    +    if hasattr(arr, "flat"):
    +        return arr.flat
    +    # PyTorch tensors use .flatten()
    +    if hasattr(arr, "flatten"):
    +        return arr.flatten()
    +    # Fallback - just return the array itself
    +    return arr
    +
    +
    +def _array_formatter(arr: Any) -> tuple[Callable[[Any], str], str]:
    +    """
    +    Create an optimal formatter for displaying array values consistently.
    +
    +    For integers: display as integers without decimals.
    +    For floats: determine scale from max(abs(values)), apply SI-style
    +    scaling (×10⁶, ×10⁻³, etc.), and display with consistent fixed precision.
    +    Returns (formatter_func, scale_suffix) where scale_suffix may be empty.
    +    """
    +    try:
    +        dtype_str = str(arr.dtype)
    +    except AttributeError:
    +        dtype_str = ""
    +
    +    # Integer arrays - display as integers, no scaling
    +    if "int" in dtype_str or "bool" in dtype_str:
    +        return lambda v: str(int(v)), ""
    +
    +    # For float arrays, analyze the values to determine optimal formatting
    +    if "float" in dtype_str or "complex" in dtype_str:
    +        flat = _get_flat(arr)
    +        # Sample values for analysis
    +        n = len(flat)
    +        if n <= 200:
    +            sample = [float(v) for v in flat]
    +        else:
    +            sample = [float(flat[i]) for i in range(100)]
    +            sample += [float(flat[n - 100 + i]) for i in range(100)]
    +
    +        # Filter out non-finite values for scale analysis
    +        finite = [abs(v) for v in sample if v == v and abs(v) != float("inf")]
    +        if not finite:
    +            # All NaN/Inf
    +            return lambda v: ("NaN" if v != v else ("∞" if v > 0 else "-∞")), ""
    +
    +        max_abs = max(finite) if finite else 0
    +        log_max = math.log10(max_abs) if max_abs > 0 else 0
    +        scale_power = int(log_max // 3) * 3
    +        if scale_power in (-3, 0, 3):
    +            scale_power = 0  # No scientific notation for average scales
    +        scale_suffix = (
    +            f"×10{str(scale_power).translate(_SUPERSCRIPT_DIGITS)}"
    +            if scale_power
    +            else ""
    +        )
    +        scale_factor = 10.0 ** (-scale_power) if scale_power else 1.0
    +        log_scaled = log_max - scale_power if scale_power else log_max
    +        decimals = max(0, 2 - math.floor(log_scaled)) if max_abs > 0 else 0
    +
    +        def fmt(v: Any, sf: float = scale_factor, d: int = decimals) -> str:
    +            if v != v:
    +                return "NaN"
    +            if v == float("inf"):
    +                return "∞"
    +            if v == float("-inf"):
    +                return "-∞"
    +            scaled = v * sf
    +            if scaled == 0:
    +                return "0"
    +            return f"{scaled:.{d}f}"
    +
    +        return fmt, scale_suffix
    +
    +    # Fallback for other types
    +    return (lambda v: f"{v}"), ""
    +
    +
    +blacklist_names = {"_", "In", "Out"}
    +blacklist_types = (
    +    type,
    +    types.ModuleType,
    +    types.FunctionType,
    +    types.MethodType,
    +    types.BuiltinFunctionType,
    +)
    +no_str_conv = re.compile(r"<.* object at 0x[0-9a-fA-F]{5,}>")
    +
    +
    +def _extract_identifiers_ast(sourcecode: str) -> set[str] | None:
    +    """
    +    Extract variable identifiers from source code using AST.
    +
    +    Returns a set of variable names (including attribute access like "obj.attr"),
    +    or None if AST parsing fails.
    +    """
    +    # Try to parse as an expression first (most common case for error lines)
    +    for wrapper in ("({})", "{}"):
    +        try:
    +            code = wrapper.format(sourcecode)
    +            tree = ast.parse(code, mode="eval")
    +            break
    +        except SyntaxError:
    +            continue
    +    else:
    +        # Try parsing as statements
    +        try:
    +            tree = ast.parse(sourcecode, mode="exec")
    +        except SyntaxError:
    +            return None
    +
    +    identifiers: set[str] = set()
    +
    +    class IdentifierVisitor(ast.NodeVisitor):
    +        def visit_Name(self, node: ast.Name) -> None:
    +            identifiers.add(node.id)
    +            self.generic_visit(node)
    +
    +        def visit_Attribute(self, node: ast.Attribute) -> None:
    +            # Build the full dotted name for attribute access
    +            parts = [node.attr]
    +            current = node.value
    +            while isinstance(current, ast.Attribute):
    +                parts.append(current.attr)
    +                current = current.value
    +            if isinstance(current, ast.Name):
    +                parts.append(current.id)
    +                # Add the full dotted name (e.g., "obj.attr")
    +                identifiers.add(".".join(reversed(parts)))
    +                # Also add intermediate names (e.g., "obj" for "obj.attr.sub")
    +                for i in range(len(parts)):
    +                    identifiers.add(".".join(reversed(parts[i:])))
    +            self.generic_visit(node)
    +
    +    IdentifierVisitor().visit(tree)
    +    return identifiers
    +
    +
    +def _extract_identifiers_regex(sourcecode: str) -> set[str]:
    +    """
    +    Extract variable identifiers from source code using regex (fallback).
    +
    +    This is less accurate than AST as it can match names in strings/comments,
    +    but works when AST parsing fails.
    +    """
    +    return {
    +        m.group(0) for p in (r"\w+", r"\w+\.\w+") for m in re.finditer(p, sourcecode)
    +    }
    +
    +
    +def extract_variables(variables: dict[str, Any], sourcecode: str) -> list[VarInfo]:
    +    # Try AST-based extraction first, fall back to regex
    +    identifiers = _extract_identifiers_ast(sourcecode)
    +    if identifiers is None:
    +        identifiers = _extract_identifiers_regex(sourcecode)
    +    rows = []
    +    for name, value in variables.items():
    +        if name in blacklist_names or isinstance(value, blacklist_types):
    +            continue
    +        try:
    +            typename = type(value).__name__
    +            if name not in identifiers:
    +                continue
    +            try:
    +                strvalue = str(value)
    +                reprvalue = repr(value)
    +            except Exception:
    +                continue  # Skip variables failing str() or repr()
    +            # Using repr is better for empty strings and some other cases
    +            if not strvalue and reprvalue:
    +                strvalue = reprvalue
    +            # Try to print members of objects that don't have proper __str__
    +            elif no_str_conv.fullmatch(strvalue):
    +                found = False
    +                try:
    +                    members = safe_vars(value).items()
    +                except Exception:
    +                    members = []
    +                for n, v in members:
    +                    mname = f"{name}.{n}"
    +                    if sourcecode and mname not in identifiers:
    +                        continue
    +                    tname = type(v).__name__
    +                    if isinstance(v, blacklist_types):
    +                        continue
    +                    # Check if the member also has a poor representation
    +                    try:
    +                        member_str = str(v)
    +                        if no_str_conv.fullmatch(member_str):
    +                            continue  # Skip members with poor representations
    +                    except Exception:
    +                        continue  # Skip members that fail str()
    +                    tname += f" in {typename}"
    +                    val_str, val_fmt = prettyvalue(v)
    +                    rows += (VarInfo(mname, tname, val_str, val_fmt),)
    +                    found = True
    +                if found:
    +                    continue
    +                value = "⋯"
    +            # Full types for Numpy-like arrays, PyTorch tensors, etc.
    +            try:
    +                dtype = str(object.__getattribute__(value, "dtype")).rsplit(".", 1)[-1]
    +                if typename == dtype:
    +                    raise AttributeError  # Numpy scalars need no further info
    +                shape = object.__getattribute__(value, "shape")
    +                dims = "×".join(str(d + 0) for d in shape) + " " if shape else ""
    +                try:
    +                    dev = object.__getattribute__(value, "device")
    +                    dev = f"@{dev}" if dev and dev.type != "cpu" else ""
    +                except AttributeError:
    +                    dev = ""
    +                typename += f" of {dims}{dtype}{dev}"
    +            except AttributeError:
    +                pass
    +            val_str, val_fmt = prettyvalue(value)
    +            # Don't show type for None values
    +            if typename == "NoneType":
    +                typename = ""
    +            rows += (VarInfo(name, typename, val_str, val_fmt),)
    +        except Exception:
    +            logger.exception("Variable inspector failed (please report a bug)")
    +    return rows
    +
    +
    +def safe_vars(obj: Any) -> dict[str, Any]:
    +    """Like vars(), but also supports objects with slots."""
    +    ret = {}
    +    for attr in dir(obj):
    +        with contextlib.suppress(AttributeError):
    +            ret[attr] = object.__getattribute__(obj, attr)
    +    return ret
    +
    +
    +def prettyvalue(val: Any) -> tuple[Any, str]:
    +    """
    +    Format a value for display in the inspector.
    +
    +    Returns:
    +        tuple: (formatted_value, format_hint) where format_hint is one of:
    +               'block' - left-aligned block format (for multi-line strings)
    +               'inline' - inline right-aligned format (default)
    +    """
    +    # Handle namedtuple formatting before regular tuple check
    +    # namedtuples have _fields attribute which is a tuple of field names
    +    if (
    +        isinstance(val, tuple)
    +        and hasattr(val, "_fields")
    +        and isinstance(val._fields, tuple)
    +    ):
    +        fields = val._fields
    +        if not fields:
    +            return (f"{type(val).__name__}()", "inline")
    +        # For small namedtuples, return as structured table
    +        if len(fields) <= 10:
    +            rows = []
    +            for name in fields:
    +                key_str = name if len(name) <= 40 else name[:37] + "…"
    +                field_val = getattr(val, name)
    +                val_str = (
    +                    f"{field_val!s}"
    +                    if len(f"{field_val!s}") <= 60
    +                    else f"{field_val!s}"[:57] + "…"
    +                )
    +                rows.append([key_str, val_str])
    +            return ({"type": "keyvalue", "rows": rows}, "inline")
    +        # For larger namedtuples, show summary
    +        return (f"({len(fields)} fields)", "inline")
    +    if isinstance(val, (list, tuple)):
    +        if not 0 < len(val) <= 10:
    +            return (f"({len(val)} items)", "inline")
    +        return (", ".join(repr(v)[:80] for v in val), "inline")
    +    if isinstance(val, dict):
    +        # Handle dict formatting specially
    +        if not val:
    +            return ("{}", "inline")
    +        # For small dicts, return as structured table
    +        if len(val) <= 10:
    +            # Return list of [key, value] pairs for structured rendering
    +            rows = []
    +            for k, v in val.items():
    +                key_str = f"{k!s}" if len(f"{k!s}") <= 40 else f"{k!s}"[:37] + "…"
    +                val_str = f"{v!s}" if len(f"{v!s}") <= 60 else f"{v!s}"[:57] + "…"
    +                rows.append([key_str, val_str])
    +            return ({"type": "keyvalue", "rows": rows}, "inline")
    +        # For larger dicts, show summary
    +        return (f"({len(val)} items)", "inline")
    +    if dataclasses.is_dataclass(val) and not isinstance(val, type):
    +        # Handle dataclass formatting similar to dict
    +        fields = dataclasses.fields(val)
    +        if not fields:
    +            return (f"{type(val).__name__}()", "inline")
    +        # For small dataclasses, return as structured table
    +        if len(fields) <= 10:
    +            rows = []
    +            for field in fields:
    +                key_str = field.name if len(field.name) <= 40 else field.name[:37] + "…"
    +                field_val = object.__getattribute__(val, field.name)
    +                val_str = (
    +                    f"{field_val!s}"
    +                    if len(f"{field_val!s}") <= 60
    +                    else f"{field_val!s}"[:57] + "…"
    +                )
    +                rows.append([key_str, val_str])
    +            return ({"type": "keyvalue", "rows": rows}, "inline")
    +        # For larger dataclasses, show summary
    +        return (f"({len(fields)} fields)", "inline")
    +    # msgspec Struct and Pydantic BaseModel support (without importing either)
    +    # msgspec uses __struct_fields__ (tuple), Pydantic v2 uses model_fields (dict)
    +    struct_fields = None
    +    if isinstance(fields := getattr(type(val), "__struct_fields__", None), tuple):
    +        struct_fields = fields
    +    elif isinstance(fields := getattr(type(val), "model_fields", None), dict):
    +        struct_fields = tuple(fields.keys())
    +    if struct_fields is not None:
    +        if not struct_fields:
    +            return (f"{type(val).__name__}()", "inline")
    +        if len(struct_fields) <= 10:
    +            rows = []
    +            for name in struct_fields:
    +                key_str = name if len(name) <= 40 else name[:37] + "…"
    +                field_val = object.__getattribute__(val, name)
    +                val_str = (
    +                    f"{field_val!s}"
    +                    if len(f"{field_val!s}") <= 60
    +                    else f"{field_val!s}"[:57] + "…"
    +                )
    +                rows.append([key_str, val_str])
    +            return ({"type": "keyvalue", "rows": rows}, "inline")
    +        return (f"({len(struct_fields)} fields)", "inline")
    +    if isinstance(val, type):
    +        return (f"{val.__module__}.{val.__name__}", "inline")
    +    try:
    +        # This only works for Numpy-like arrays, and should cause exceptions otherwise
    +        shape = object.__getattribute__(val, "shape")
    +        if isinstance(shape, tuple) and val.shape:
    +            numelem = reduce(lambda x, y: x * y, shape)
    +            if numelem <= 1:
    +                flat = _get_flat(val)
    +                return (_format_scalar(flat[0]), "inline")
    +            # 1D arrays
    +            if len(shape) == 1:
    +                fmt, suffix = _array_formatter(val)
    +                if shape[0] <= 100:
    +                    result = ", ".join(fmt(v) for v in val)
    +                else:
    +                    formatted = [fmt(v) for v in (*val[:3], *val[-3:])]
    +                    result = ", ".join([*formatted[:3], "…", *formatted[-3:]])
    +                if suffix:
    +                    result = f"{result} {suffix}"
    +                return (result, "inline")
    +            # 2D arrays
    +            if len(shape) == 2 and shape[0] <= 10 and shape[1] <= 10:
    +                fmt, suffix = _array_formatter(val)
    +                table = [[fmt(v) for v in row] for row in val]
    +                if suffix:
    +                    return (
    +                        {"type": "array", "rows": table, "suffix": suffix},
    +                        "inline",
    +                    )
    +                return (table, "inline")
    +    except (AttributeError, ValueError):
    +        pass
    +    except Exception:
    +        logger.exception(
    +            "Pretty-printing in variable inspector failed (please report a bug)"
    +        )
    +
    +    try:
    +        # Handle numpy scalars and plain floats/ints (but not arrays)
    +        dtype_str = str(getattr(val, "dtype", ""))
    +        # Check it's not an array (no shape, or empty shape)
    +        shape = getattr(val, "shape", ())
    +        is_scalar = not shape or (isinstance(shape, tuple) and len(shape) == 0)
    +        is_numeric = isinstance(val, (int, float)) or (dtype_str and is_scalar)
    +        if is_numeric and not isinstance(val, bool):
    +            return (_format_scalar(val), "inline")
    +    except (AttributeError, TypeError, ValueError):
    +        pass
    +
    +    # Determine format hint based on content
    +    format_hint = "inline"
    +
    +    # Format exceptions using str() for cleaner display
    +    if isinstance(val, BaseException):
    +        ret = str(val)
    +    elif isinstance(val, str):
    +        ret = str(val)
    +        # Multi-line strings should be displayed as blocks
    +        if "\n" in ret.rstrip():
    +            format_hint = "block"
    +    else:
    +        ret = repr(val)
    +
    +    # For inline format, collapse newlines to avoid display issues
    +    if format_hint == "inline":
    +        if "\n" in ret:
    +            ret = " ".join(line.strip() for line in ret.split("\n") if line.strip())
    +        # Only truncate inline values
    +        if len(ret) > 120:
    +            ret = ret[:30] + " … " + ret[-30:]
    +    # For block format, don't truncate but limit line count if needed
    +    else:
    +        lines = ret.split("\n")
    +        if len(lines) > 20:
    +            # Show first 10 and last 10 lines
    +            ret = "\n".join(lines[:10] + ["⋯"] + lines[-10:])
    +
    +    return (ret, format_hint)
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/logging.py b/.venv/lib/python3.12/site-packages/tracerite/logging.py
    new file mode 100644
    index 0000000..f05cfdb
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/logging.py
    @@ -0,0 +1,4 @@
    +import logging
    +
    +logger = logging.getLogger("tracerite")
    +logger.setLevel(logging.INFO)
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/notebook.py b/.venv/lib/python3.12/site-packages/tracerite/notebook.py
    new file mode 100644
    index 0000000..c0c8fea
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/notebook.py
    @@ -0,0 +1,115 @@
    +from __future__ import annotations
    +
    +import contextlib
    +import sys
    +from typing import Any
    +
    +from . import trace
    +from .html import html_traceback
    +from .logging import logger
    +from .tty import tty_traceback
    +
    +# Cleanup mode: "replace" (default) removes old reports, "keep" only removes script/style
    +_cleanup_mode = "replace"
    +
    +
    +def _can_display_html() -> bool:
    +    # Spyder runs IPython ZMQInteractiveShell but lacks HTML support. Using
    +    # argv seems like the most portable way to autodetect HTML capability.
    +    #
    +    # "ipykernel_launcher.py" in Jupyter Notebook/Lab
    +    # "ipykernel/__main__.py" in Azure Notebooks
    +    # "colab_kernel_launcher.py" in Google Colab
    +    return any(name in sys.argv[0] for name in ["ipykernel", "colab_kernel_launcher"])
    +
    +
    +def load_ipython_extension(ipython: Any) -> None:
    +    trace.ipython = ipython
    +
    +    # Hide IPython's internal frames from tracebacks
    +    try:
    +        from IPython.core import interactiveshell  # type: ignore[import]
    +
    +        interactiveshell.__tracebackhide__ = True  # type: ignore[attr-defined]
    +    except ImportError:
    +        pass
    +
    +    # Define handlers that check HTML capability at call time, not load time.
    +    # This allows the same extension to work in both Jupyter and terminal.
    +    def showtraceback(*args: Any, **kwargs: Any) -> None:
    +        try:
    +            if _can_display_html():
    +                from IPython.display import display  # type: ignore[import]
    +
    +                display(
    +                    html_traceback(
    +                        skip_until=" None:
    +        try:
    +            if _can_display_html():
    +                from IPython.display import display  # type: ignore[import]
    +
    +                display(
    +                    html_traceback(
    +                        skip_until=" None:
    +        """Configure tracerite behavior.
    +
    +        Usage:
    +            %tracerite keep - Keep all previous error reports visible
    +        """
    +        global _cleanup_mode
    +        if line.strip().lower() == "keep":
    +            _cleanup_mode = "keep"
    +        else:
    +            print("Usage: %tracerite keep")
    +
    +
    +def unload_ipython_extension(ipython: Any) -> None:
    +    with contextlib.suppress(AttributeError):
    +        del ipython.showtraceback
    +    with contextlib.suppress(AttributeError):
    +        del ipython.showsyntaxerror
    +    # Remove the __tracebackhide__ we injected
    +    try:
    +        from IPython.core import interactiveshell  # type: ignore[import]
    +
    +        with contextlib.suppress(AttributeError):
    +            del interactiveshell.__tracebackhide__  # type: ignore[attr-defined]
    +    except ImportError:
    +        pass
    +    trace.ipython = None
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/script.js b/.venv/lib/python3.12/site-packages/tracerite/script.js
    new file mode 100644
    index 0000000..1317b49
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/script.js
    @@ -0,0 +1,20 @@
    +(()=>{
    +// Move style to head (replacing any old version) to preserve it when .tracerite elements are deleted
    +const current=document.currentScript?.parentElement
    +const style=current?.querySelector('style')
    +if(style){
    +  document.getElementById('tracerite-style')?.remove()
    +  style.id='tracerite-style'
    +  document.head.appendChild(style)
    +}
    +// Remove contents of any previous tracerite elements based on cleanup mode
    +if(current?.dataset.replacePrevious){
    +  const mode=current.dataset.cleanupMode||'replace'
    +  document.querySelectorAll('.tracerite').forEach(el=>{
    +    if(el!==current){
    +      // In replace mode, remove all direct children except h2
    +      if(mode==='replace')[...el.children].forEach(c=>{if(c.tagName!=='H2')c.remove()})
    +    }
    +  })
    +}
    +})()
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/style.css b/.venv/lib/python3.12/site-packages/tracerite/style.css
    new file mode 100644
    index 0000000..b85e44e
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/style.css
    @@ -0,0 +1,466 @@
    +/* Compact mode uses CSS Grid - see COMPACT MODE section below */
    +@import url('https://cdn.jsdelivr.net/npm/@fontsource/monaspace-krypton/index.css');
    +
    +/** TraceRite **/
    +:root {
    +  --tracerite-var: #8af;
    +  --tracerite-type: #5c8;
    +  --tracerite-val: #8af;
    +  --tracerite-highlight: #ff8;
    +  --tracerite-highlight-text: #000;
    +  --tracerite-call-symbol-color: #ff8;
    +  --tracerite-call-symbol-shadow: 0 0 .1em black;
    +  --tracerite-call-highlight: #da0;
    +  --tracerite-caret: #f00;
    +  --tracerite-exception: #777;
    +  --tracerite-tooltip: #000;
    +  --tracerite-tooltip-text: inherit;
    +  --tracerite-code: inherit;
    +  --tracerite-lineno: #888;
    +  --tracerite-link-bg: #fff5;
    +  --tracerite-link-hover: #fff8;
    +  --tracerite-function: #68f;
    +  --tracerite-location: #5a6;
    +  --tracerite-code-font: 'Monaspace Krypton', 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Source Code Pro', 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Ubuntu Mono', 'Consolas', 'Courier New';
    +  --tracerite-ui-font: system-ui, -apple-system, 'Segoe UI', 'Roboto', 'Ubuntu', 'Cantarell', 'Noto Sans', sans-serif;
    +}
    +
    +:root:has(.tracerite.autodark) {
    +  color-scheme: light dark;
    +}
    +
    +:root:has(.tracerite.autodark) {
    +  @media (prefers-color-scheme: dark) {
    +    --tracerite-var: #8af;
    +    --tracerite-type: #5c8;
    +    --tracerite-val: #8af;
    +    --tracerite-highlight: #ff0;
    +    --tracerite-highlight-text: #000;
    +    --tracerite-call-symbol-color: #ff0;
    +    --tracerite-call-symbol-shadow: none;
    +    --tracerite-call-highlight: #ff0;
    +    --tracerite-caret: #f55;
    +    --tracerite-exception: #aaa;
    +    --tracerite-tooltip: #fff;
    +    --tracerite-tooltip-text: #fff;
    +    --tracerite-code: #ccc;
    +    --tracerite-lineno: #888;
    +    --tracerite-link-bg: #0005;
    +    --tracerite-link-hover: #0008;
    +    --tracerite-function: #8af;
    +    --tracerite-location: #6b8;
    +  }
    +}
    +
    +:root .tracerite { font-family: var(--tracerite-ui-font); font-size: 16px; }
    +:root .tracerite,
    +:root .tracerite *,
    +:root .tracerite .traceback-details table,
    +:root .tracerite > h2,
    +:root .tracerite > h3 { margin: 0; padding: 0; outline: none; box-sizing: border-box; line-height: 1.2; font: var(--tracerite-ui-font); font-weight: 700;}
    +
    +:root .tracerite > h2 { font-size: 1.1em; }
    +:root .tracerite > h3 { font-size: 1em; }
    +
    +/* Code font declarations with high specificity - only for code tags */
    +:root .tracerite pre,
    +:root .tracerite code {
    +  font-family: var(--tracerite-code-font);
    +  font-feature-settings: "liga" 1, "ss01" 1, "ss02" 1;
    +  font-variant-ligatures: common-ligatures;
    +  background: none;
    +  color: var(--tracerite-code);
    +  text-overflow: ellipsis;
    +  word-break: normal;
    +}
    +
    +:root .tracerite strong,
    +:root .tracerite > h3 { font-weight: bold; padding: 0.2em 0; }
    +:root .tracerite .excmessage { font-size: 0.8em; max-height: 12em; overflow: auto; border-left: .2em solid var(--tracerite-exception); margin-left: 0.2em; padding-left: 0.5em;}
    +:root .tracerite .exctype { color: var(--tracerite-exception); }
    +
    +:root .tracerite .traceback-details { font-size: 0.8em; }
    +:root .tracerite .traceback-details p { margin: 1em 0; }
    +:root .tracerite .traceback-details pre { width: 50vw; padding: .5em; font-size: 0.8em; }
    +:root .tracerite .traceback-details .codeline { text-indent: 4ch each-line; }
    +:root .tracerite .traceback-details .codeline::before {
    +  content: attr(data-lineno);
    +  color: var(--tracerite-lineno);
    +  opacity: 0.0;
    +  transition: all 0.4s;
    +  display: inline-block;
    +  text-align: right;
    +  text-indent: 0;
    +  white-space: nowrap;
    +  word-break: keep-all;
    +  padding-right: 1ch;
    +  width: 4ch;
    +}
    +:root .tracerite .traceback-details pre:hover .codeline::before { opacity: 1.0; }
    +
    +:root .tracerite .traceback-details mark { background: var(--tracerite-highlight); color: var(--tracerite-highlight-text); padding: 0.2em; margin: -0.1em; }
    +:root .tracerite .traceback-details em { font-style: normal; color: var(--tracerite-caret); }
    +
    +/* Symbol element - display symbol with specific styling */
    +:root .tracerite .traceback-details .tracerite-symbol {
    +  display: inline-block;
    +  margin-left: 0.5ch;
    +  padding: 0.1em;
    +  font-size: 1.5em;
    +  margin: -0.25em 0 -.25em 0.3em;
    +  font-weight: bold;
    +  vertical-align: middle;
    +  font-family: var(--tracerite-ui-font);
    +}
    +
    +/* Call symbols get special color and shadow */
    +:root .tracerite .traceback-call .tracerite-symbol {
    +  color: var(--tracerite-call-symbol-color);
    +  text-shadow: var(--tracerite-call-symbol-shadow);
    +}
    +
    +/* Display symbol using pseudo-element and data attribute */
    +:root .tracerite .traceback-details .tracerite-symbol::before { content: attr(data-symbol); }
    +
    +/* Tooltip text element - display text with different styling */
    +:root .tracerite .traceback-details .tracerite-tooltip-text {
    +  display: inline-block;
    +  margin-left: 0.3ch;
    +  font-size: 0.9em;
    +  font-weight: bold;
    +  vertical-align: middle;
    +  font-family: var(--tracerite-ui-font);
    +  white-space: nowrap;
    +  color: var(--tracerite-tooltip-text);
    +}
    +
    +/* Display tooltip text using pseudo-element and data attribute */
    +:root .tracerite .traceback-details .tracerite-tooltip-text::before { content: attr(data-tooltip); }
    +
    +:root .tracerite .traceback-details {
    +  position: relative;
    +  min-width: 20ch;
    +  max-width: 100%;
    +  margin: 0 .2em;
    +  flex-shrink: 0;
    +  border-radius: .5em;
    +  padding: .2em;
    +  padding-left: 0;
    +}
    +
    +:root .tracerite .traceback-details:last-child { width: 100%; }
    +:root .tracerite .traceback-ellipsis { min-width: 4ch; text-align: center; }
    +
    +/* Base styles for frame-function and frame-location */
    +:root .tracerite .frame-function { font-weight: 600; color: var(--tracerite-function); line-height: 1.2; }
    +:root .tracerite .frame-location { color: var(--tracerite-location); line-height: 1.2; }
    +:root .tracerite .frame-colon { color: var(--tracerite-code); font-weight: 700; }
    +:root .tracerite .frame-lineno { color: var(--tracerite-lineno); display: inline; }
    +:root .tracerite .frame-link {
    +  margin-left: 0.3em;
    +  padding: 0.1em 0.4em;
    +  background: var(--tracerite-link-bg);
    +  border-radius: 0.3em;
    +  color: inherit;
    +  text-decoration: none;
    +  font-size: 0.85em;
    +}
    +:root .tracerite .frame-link:hover { background: var(--tracerite-link-hover); }
    +
    +/* Variable inspector: grid layout with fixed first column, flexible second */
    +:root .tracerite dl.inspector {
    +  display: grid;
    +  grid-template-columns: auto 1fr;
    +  margin: 0;
    +  min-width: 8em;
    +  width: 100%;
    +  word-break: break-word;
    +}
    +
    +:root .tracerite dl.inspector dt,
    +:root .tracerite dl.inspector dd {
    +  margin: 0;
    +  padding: 0;
    +  width: 100%;
    +  text-align: right;
    +  vertical-align: top;
    +  font-weight: normal;
    +  float: none;
    +}
    +:root .tracerite dl.inspector dt {
    +  white-space: nowrap;
    +  text-overflow: ellipsis;
    +  overflow: hidden;
    +}
    +:root .tracerite .inspector .var { font-weight: bold; color: var(--tracerite-var); }
    +:root .tracerite .inspector .type { white-space: nowrap; color: var(--tracerite-type); }
    +:root .tracerite .inspector .val { white-space: pre; text-overflow: ellipsis; overflow: hidden; color: var(--tracerite-val); }
    +
    +/* Block format for multi-line strings - left aligned */
    +:root .tracerite .inspector .val-block {
    +  text-align: left;
    +  white-space: pre-wrap;
    +  overflow: visible;
    +  max-width: none;
    +  word-break: break-word;
    +}
    +
    +:root .tracerite .inspector .val-block pre {
    +  margin: 0;
    +  padding: 0;
    +  padding-top: 0.4em;
    +  border-radius: 3px;
    +  font-size: 0.8em;
    +  white-space: pre;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  max-width: 30em;
    +  color: inherit;
    +  font-family: var(--tracerite-code-font);
    +}
    +
    +/* Inline format - left aligned (default) */
    +:root .tracerite .inspector .val-inline {
    +  text-align: left;
    +}
    +
    +/* Nested table styling (for matrices) */
    +:root .tracerite .inspector table td {
    +  color: var(--tracerite-val);
    +  word-break: keep-all;
    +  overflow: hidden;
    +  font-size: 0.8em;
    +  border-collapse: collapse;
    +  text-align: right;
    +}
    +
    +:root .tracerite .inspector tr {
    +  background: none;
    +}
    +
    +/* matrix value on a variable */
    +:root .tracerite .inspector .val-inline table td {
    +  padding: 0 0.4em;
    +  min-width: 2em;
    +}
    +
    +/* Key-value dl styling (dicts, dataclasses) */
    +:root .tracerite .inspector dl.keyvalue-dl {
    +  display: grid;
    +  grid-template-columns: auto 1fr;
    +  margin: 0;
    +  padding-top: 0.2em;
    +  font-size: 0.8em;
    +}
    +:root .tracerite .inspector dl.keyvalue-dl dt,
    +:root .tracerite .inspector dl.keyvalue-dl dd {
    +  margin: 0;
    +  padding: 0 0.3em;
    +  white-space: nowrap;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +}
    +:root .tracerite .inspector dl.keyvalue-dl dt {
    +  text-align: right;
    +  color: var(--tracerite-var);
    +  font-weight: bold;
    +  width: max-content;
    +  float: none;
    +}
    +:root .tracerite .inspector dl.keyvalue-dl dd {
    +  text-align: left;
    +  width: auto;
    +  float: none;
    +}
    +
    +/* Array with scale factor container */
    +:root .tracerite .inspector .array-with-scale {
    +  display: flex;
    +  align-items: center;
    +  gap: 0.3em;
    +}
    +
    +/* Scale suffix for arrays (e.g., ×10⁶) */
    +:root .tracerite .inspector .scale-suffix {
    +  font-size: 1.4em;
    +  font-weight: bold;
    +  white-space: nowrap;
    +  align-self: center;
    +}
    +
    +
    +/* ============================================
    +   COMPACT MODE TOGGLE AND STYLING (with :has)
    +   ============================================ */
    +
    +/* CSS Grid layout for compact mode */
    +:root .tracerite {
    +  display: grid;
    +  grid-template-columns: auto auto auto 1fr;
    +  align-items: baseline;
    +  gap: 0 0.5em;
    +  justify-content: start;
    +}
    +
    +/* Hidden checkbox for toggle */
    +:root .tracerite .frame-toggle-checkbox {
    +  display: none;
    +}
    +
    +/* Expand wrapper - uses grid-template-rows for height animation */
    +:root .tracerite .expand-wrapper {
    +  grid-column: 1 / -1;
    +  display: grid;
    +  grid-template-rows: 0fr;
    +  grid-template-columns: subgrid;
    +  transition: grid-template-rows 0.25s ease-out;
    +}
    +:root .tracerite .expand-content {
    +  overflow: hidden;
    +  min-height: 0;
    +  display: grid;
    +  grid-template-columns: subgrid;
    +  grid-column: 1 / -1;
    +}
    +:root .tracerite .expand-content > pre {
    +  grid-column: 1 / 4;
    +  width: auto;
    +  white-space: pre-wrap;
    +  word-wrap: break-word;
    +  text-overflow: clip;
    +  overflow: hidden;  /* Prevent scrollbar flash during animation */
    +}
    +:root .tracerite .expand-content > dl.inspector {
    +  grid-column: 4;
    +  align-self: start;
    +  margin-top: auto;
    +  margin-bottom: auto;
    +  height: fit-content;
    +}
    +
    +/* When checkbox is checked, expand the wrapper */
    +:root .tracerite .frame-toggle-checkbox:checked ~ .compact-call-line {
    +  display: none;
    +}
    +:root .tracerite .frame-toggle-checkbox:checked ~ .expand-wrapper {
    +  grid-template-rows: 1fr;
    +}
    +
    +/* Colon always visible */
    +:root .tracerite .frame-colon {
    +  color: var(--tracerite-code);
    +  font-weight: 700;
    +}
    +
    +/* Exception headers and messages span all columns */
    +:root .tracerite > h2,
    +:root .tracerite > h3,
    +:root .tracerite > pre.excmessage {
    +  grid-column: 1 / -1;
    +}
    +
    +/* Make intermediate containers transparent to grid */
    +:root .tracerite .traceback-details {
    +  display: contents;
    +}
    +
    +/* Grid column assignments for all frame elements */
    +/* Location comes first, then function */
    +:root .tracerite .traceback-details .frame-location {
    +  grid-column: 1;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  max-width: 20em;
    +}
    +:root .tracerite .traceback-details .frame-function {
    +  grid-column: 2;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  max-width: 20em;
    +}
    +:root .tracerite .traceback-details .compact-call-line {
    +  grid-column: 3 / -1;
    +}
    +
    +/* Wrapper label for clickable location+function - uses display:contents to not affect grid */
    +:root .tracerite .frame-label-wrapper {
    +  display: contents;
    +  cursor: pointer;
    +}
    +
    +/* Ellipsis frame spans all columns */
    +:root .tracerite .traceback-ellipsis {
    +  grid-column: 1 / -1;
    +  min-width: auto;
    +  text-align: left;
    +  padding: 0 0.5em;
    +  color: var(--tracerite-lineno);
    +}
    +
    +/* Compact code line styling */
    +:root .tracerite .compact-call-line {
    +  display: inline;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  white-space: nowrap;
    +  max-width: 40em;
    +  line-height: 1;
    +  vertical-align: baseline;
    +}
    +:root .tracerite .compact-call-line code.compact-code {
    +  display: inline;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  line-height: inherit;
    +  vertical-align: baseline;
    +  background: transparent;
    +}
    +:root .tracerite .compact-call-line .compact-symbol {
    +  font-size: 1.1em;
    +  vertical-align: baseline;
    +  flex-shrink: 0;
    +}
    +
    +/* Call symbols in compact lines get special color and shadow */
    +:root .tracerite .traceback-call .compact-call-line .compact-symbol {
    +  color: var(--tracerite-call-symbol-color);
    +  text-shadow: var(--tracerite-call-symbol-shadow);
    +}
    +:root .tracerite .compact-call-line em {
    +  color: var(--tracerite-call-highlight);
    +}
    +/* Error/stop frames use red caret in compact mode */
    +:root .tracerite .traceback-error .compact-call-line em,
    +:root .tracerite .traceback-stop .compact-call-line em {
    +  color: var(--tracerite-caret);
    +}
    +
    +/* Parallel branches for ExceptionGroups - side by side layout */
    +:root .tracerite .parallel-branches {
    +  grid-column: 1 / -1;
    +  display: flex;
    +  flex-wrap: wrap;
    +  gap: 0.5em;
    +  align-items: flex-start;
    +  border-left: .2em solid var(--tracerite-exception);
    +  margin-left: 0.2em;
    +  padding-left: 0.5em;
    +}
    +
    +:root .tracerite .parallel-branch {
    +  flex: 1 1 300px;
    +  min-width: 250px;
    +  max-width: 100%;
    +  /* Same grid layout as .tracerite for consistent frame alignment */
    +  display: grid;
    +  grid-template-columns: auto auto auto 1fr;
    +  align-items: baseline;
    +  gap: 0 0.5em;
    +  justify-content: start;
    +}
    +
    +/* Exception headers and messages inside parallel branches span all columns */
    +:root .tracerite .parallel-branch > h3,
    +:root .tracerite .parallel-branch > pre.excmessage {
    +  grid-column: 1 / -1;
    +}
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/syntaxerror.py b/.venv/lib/python3.12/site-packages/tracerite/syntaxerror.py
    new file mode 100644
    index 0000000..f8751a9
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/syntaxerror.py
    @@ -0,0 +1,417 @@
    +"""Enhanced SyntaxError position extraction.
    +
    +Python's SyntaxError often provides poor position information, especially for
    +multi-line errors like mismatched brackets. This module parses common error
    +patterns and source code to provide better highlighting ranges.
    +"""
    +
    +import re
    +from collections import namedtuple
    +
    +# Position range: lines are 1-based inclusive, columns are 0-based exclusive
    +Range = namedtuple("Range", ["lfirst", "lfinal", "cbeg", "cend"])
    +
    +# Patterns for extracting information from SyntaxError messages
    +MISMATCH_PATTERN = re.compile(
    +    r"closing parenthesis '([)\]}])' does not match opening parenthesis '([(\[{])' on line (\d+)"
    +)
    +UNCLOSED_PATTERN = re.compile(r"'([(\[{])' was never closed")
    +INCOMPLETE_INPUT_PATTERN = re.compile(r"incomplete input")
    +# Match "unterminated string literal" and "unterminated f-string literal"
    +UNTERMINATED_STRING_PATTERN = re.compile(r"unterminated (?:f-)?string literal")
    +# Match "unterminated triple-quoted string literal" and "unterminated triple-quoted f-string literal"
    +UNTERMINATED_TRIPLE_PATTERN = re.compile(
    +    r"unterminated triple-quoted (?:f-)?string literal"
    +)
    +
    +# Pattern to clean up redundant line info from messages
    +DETECTED_AT_LINE_PATTERN = re.compile(r" \(detected at line \d+\)$")
    +ON_LINE_PATTERN = re.compile(r" on line \d+$")
    +FILENAME_LINE_PATTERN = re.compile(r" \([^)]+, line \d+\)$")
    +
    +BRACKET_PAIRS = {")": "(", "]": "[", "}": "{"}
    +BRACKET_PAIRS_REV = {"(": ")", "[": "]", "{": "}"}
    +ALL_OPENERS = "([{"
    +
    +
    +def _iter_code_chars(source_lines, end_line=None, end_col=None):
    +    """Iterate over characters in source code, skipping strings and comments.
    +
    +    Yields (line_idx_1based, col, char) for each character that is actual code
    +    (not inside a string literal or comment).
    +    """
    +    if end_line is None:
    +        end_line = len(source_lines)
    +
    +    in_string = None  # None, or the quote character(s) that opened the string
    +
    +    for line_idx in range(min(end_line, len(source_lines))):
    +        line = source_lines[line_idx].rstrip("\n\r")
    +        line_num = line_idx + 1  # 1-based
    +
    +        # Determine where to stop on this line
    +        line_end = len(line)
    +        if line_num == end_line and end_col is not None:
    +            line_end = min(line_end, end_col)
    +
    +        col = 0
    +        while col < line_end:
    +            char = line[col]
    +            rest = line[col:]
    +
    +            if in_string:
    +                # Check for end of string
    +                if rest.startswith(in_string):
    +                    # Check it's not escaped (count preceding backslashes)
    +                    num_backslashes = 0
    +                    check_col = col - 1
    +                    while check_col >= 0 and line[check_col] == "\\":
    +                        num_backslashes += 1
    +                        check_col -= 1
    +                    if num_backslashes % 2 == 0:  # Not escaped
    +                        col += len(in_string)
    +                        in_string = None
    +                        continue
    +                col += 1
    +                continue
    +
    +            # Check for start of string
    +            if rest.startswith('"""') or rest.startswith("'''"):
    +                in_string = rest[:3]
    +                col += 3
    +                continue
    +            if char in "\"'":
    +                in_string = char
    +                col += 1
    +                continue
    +
    +            # Check for comment
    +            if char == "#":
    +                break  # Rest of line is comment
    +
    +            # This is actual code
    +            yield line_num, col, char
    +            col += 1
    +
    +        # Single-quoted strings don't span lines (would be a syntax error)
    +        if in_string and len(in_string) == 1:
    +            in_string = None
    +
    +
    +def clean_syntax_error_message(message):
    +    """Clean up redundant information from SyntaxError messages.
    +
    +    Removes patterns like:
    +    - " (detected at line 1)" from unterminated strings
    +    - " on line 2" from bracket mismatches
    +    - " (filename.py, line N)" suffix
    +    These are redundant since we show the line in the traceback.
    +    """
    +    message = DETECTED_AT_LINE_PATTERN.sub("", message)
    +    message = ON_LINE_PATTERN.sub("", message)
    +    message = FILENAME_LINE_PATTERN.sub("", message)
    +    return message
    +
    +
    +def extract_enhanced_positions(e, source_lines):
    +    """Extract enhanced position information for a SyntaxError.
    +
    +    Args:
    +        e: The SyntaxError exception
    +        source_lines: List of source lines (strings with newlines)
    +
    +    Returns:
    +        Tuple of (mark_range, em_ranges) where:
    +        mark_range: Range for the full highlight (e.g., from opening to closing bracket), or None
    +        em_ranges: List of Range objects for emphasized positions (e.g., both mismatched brackets), or None
    +    """
    +    message = str(e)
    +
    +    # Try to handle mismatched brackets: "closing parenthesis ')' does not match opening parenthesis '{' on line 1"
    +    match = MISMATCH_PATTERN.search(message)
    +    if match:
    +        return _handle_mismatch(e, source_lines, match)
    +
    +    # Try to handle unclosed brackets: "'(' was never closed"
    +    match = UNCLOSED_PATTERN.search(message)
    +    if match:
    +        return _handle_unclosed(e, source_lines, match)
    +
    +    # Try to handle unterminated triple-quoted string (check before single)
    +    match = UNTERMINATED_TRIPLE_PATTERN.search(message)
    +    if match:
    +        return _handle_unterminated_triple_string(e, source_lines)
    +
    +    # Try to handle unterminated string literal
    +    match = UNTERMINATED_STRING_PATTERN.search(message)
    +    if match:
    +        return _handle_unterminated_string(e, source_lines)
    +
    +    # Try to handle incomplete input (e.g., _IncompleteInputError)
    +    match = INCOMPLETE_INPUT_PATTERN.search(message)
    +    if match:
    +        return _handle_incomplete(e, source_lines)
    +
    +    # Default: use Python's positions
    +    return None, None
    +
    +
    +def _handle_mismatch(e, source_lines, match):
    +    """Handle mismatched bracket errors."""
    +    opening_char = match.group(2)  # The opening bracket it should match
    +    opening_line = int(match.group(3))  # Line number of opening bracket (1-based)
    +
    +    closing_line = e.lineno
    +    closing_col = (e.offset - 1) if e.offset else 0
    +
    +    # Find the opening bracket position on its line
    +    opening_col = None
    +    if 0 < opening_line <= len(source_lines):
    +        # Find the opening bracket - search for the one that would be unmatched
    +        opening_col = _find_unmatched_opener(
    +            source_lines, opening_line, opening_char, closing_line, closing_col
    +        )
    +
    +    if opening_col is None:
    +        # Fallback: just find first occurrence
    +        if 0 < opening_line <= len(source_lines):
    +            opening_col = source_lines[opening_line - 1].find(opening_char)
    +            if opening_col < 0:
    +                opening_col = 0
    +        else:
    +            opening_col = 0
    +
    +    # Mark range spans from opening bracket to closing bracket
    +    mark_range = Range(opening_line, closing_line, opening_col, closing_col + 1)
    +
    +    # Emphasis on both mismatched brackets
    +    em_ranges = [
    +        Range(opening_line, opening_line, opening_col, opening_col + 1),
    +        Range(closing_line, closing_line, closing_col, closing_col + 1),
    +    ]
    +
    +    return mark_range, em_ranges
    +
    +
    +def _handle_unclosed(e, source_lines, match):
    +    """Handle unclosed bracket errors."""
    +    opening_char = match.group(1)
    +
    +    # Python gives us the line where it detected the problem
    +    # The opening bracket is somewhere before
    +    error_line = e.lineno
    +    error_col = (e.offset - 1) if e.offset else 0
    +
    +    # Search backwards for the unclosed opener
    +    opening_line, opening_col = _find_unclosed_opener(
    +        source_lines, error_line, opening_char
    +    )
    +
    +    if opening_line is None or opening_col is None:
    +        return None, None
    +
    +    # Mark from opener to error position
    +    mark_range = Range(opening_line, error_line, opening_col, error_col + 1)
    +    em_ranges = [Range(opening_line, opening_line, opening_col, opening_col + 1)]
    +
    +    return mark_range, em_ranges
    +
    +
    +def _handle_incomplete(e, source_lines):
    +    """Handle incomplete input errors (e.g., _IncompleteInputError).
    +
    +    These occur when code is syntactically valid but incomplete (unclosed bracket,
    +    unterminated string, etc.). Python only gives us the final line number.
    +    We need to find the unclosed construct and mark from there to the end.
    +    """
    +    # Find the last non-empty line (trimmed, ignoring comments)
    +    end_line = len(source_lines)
    +    end_col = 0
    +    for i in range(len(source_lines) - 1, -1, -1):
    +        line = source_lines[i].rstrip("\n\r")
    +        # Remove comments for checking if line is empty
    +        code_part = line.split("#")[0].rstrip()
    +        if code_part:
    +            end_line = i + 1  # 1-based
    +            end_col = len(line)
    +            break
    +
    +    # Try to find any unclosed bracket
    +    opening_line, opening_col, opener_char = _find_any_unclosed_opener(
    +        source_lines, end_line
    +    )
    +
    +    if opening_line is None or opening_col is None:
    +        return None, None
    +
    +    # Mark from opener to end of meaningful content
    +    mark_range = Range(opening_line, end_line, opening_col, end_col)
    +    em_ranges = [Range(opening_line, opening_line, opening_col, opening_col + 1)]
    +
    +    return mark_range, em_ranges
    +
    +
    +def _find_any_unclosed_opener(source_lines, end_line):
    +    """Find any unclosed opening bracket by scanning the source."""
    +    # Track all bracket types using proper tokenization
    +    stacks = {char: [] for char in ALL_OPENERS}
    +
    +    for line_num, col, char in _iter_code_chars(source_lines, end_line):
    +        if char in ALL_OPENERS:
    +            stacks[char].append((line_num, col))
    +        elif char in BRACKET_PAIRS:
    +            opener = BRACKET_PAIRS[char]
    +            if stacks[opener]:
    +                stacks[opener].pop()
    +
    +    # Find the first unclosed opener (earliest in code)
    +    first_unclosed = None
    +    first_opener = None
    +    for opener_char, stack in stacks.items():
    +        if stack:
    +            pos = stack[0]  # First unclosed of this type
    +            if first_unclosed is None or (pos[0], pos[1]) < (
    +                first_unclosed[0],
    +                first_unclosed[1],
    +            ):
    +                first_unclosed = pos
    +                first_opener = opener_char
    +
    +    if first_unclosed:
    +        return first_unclosed[0], first_unclosed[1], first_opener
    +    return None, None, None
    +
    +
    +def _find_unmatched_opener(
    +    source_lines, opener_line, opener_char, closer_line, closer_col
    +):
    +    """Find the column of the unmatched opening bracket.
    +
    +    Scans from the indicated opener_line to find which opening bracket
    +    is actually unmatched with the closer at closer_line:closer_col.
    +    Uses proper tokenization to skip brackets inside strings and comments.
    +    """
    +    closer_char = BRACKET_PAIRS_REV.get(opener_char, ")")
    +
    +    # Track bracket depth as we scan
    +    # We need to find the opener that would be matched by the closer
    +    stack = []  # Stack of (line, col) for opening brackets
    +
    +    # Use tokenizer, but only scan from opener_line to closer position
    +    for line_num, col, char in _iter_code_chars(source_lines, closer_line, closer_col):
    +        if line_num < opener_line:
    +            continue
    +        if char == opener_char:
    +            stack.append((line_num, col))
    +        elif char == closer_char and stack:
    +            stack.pop()
    +
    +    # The last unmatched opener is what we want
    +    if stack:
    +        return stack[-1][1]
    +    return None
    +
    +
    +def _find_unclosed_opener(source_lines, error_line, opener_char):
    +    """Find an unclosed opening bracket by scanning the source.
    +
    +    Uses proper tokenization to skip brackets inside strings and comments.
    +    """
    +    closer_char = BRACKET_PAIRS_REV.get(opener_char, ")")
    +
    +    # Scan through code tracking bracket balance
    +    stack = []  # Stack of (line, col) for opening brackets
    +
    +    for line_num, col, char in _iter_code_chars(source_lines, error_line):
    +        if char == opener_char:
    +            stack.append((line_num, col))
    +        elif char == closer_char and stack:
    +            stack.pop()
    +
    +    # Return the first unclosed opener
    +    if stack:
    +        return stack[0]
    +    return None, None
    +
    +
    +def _get_string_opener_length(line, col):
    +    """Get the length of a string opener (prefix + quotes) starting at col.
    +
    +    Returns the length of the full opener, e.g.:
    +    - ' or " -> 1
    +    - ''' or \"\"\" -> 3
    +    - f' or f" -> 2
    +    - f''' or f\"\"\" -> 4
    +    - rf' or fr" -> 3
    +    - rf''' or rf\"\"\" -> 5
    +    """
    +    rest = line[col:]
    +
    +    # Check for string prefix (case insensitive: f, r, b, u, fr, rf, br, rb)
    +    prefix_len = 0
    +    prefix_rest = rest.lower()
    +    if prefix_rest[:2] in ("fr", "rf", "br", "rb"):
    +        prefix_len = 2
    +    elif prefix_rest[:1] in ("f", "r", "b", "u"):
    +        prefix_len = 1
    +
    +    # Check for quotes after prefix
    +    after_prefix = rest[prefix_len:]
    +    if after_prefix.startswith('"""') or after_prefix.startswith("'''"):
    +        return prefix_len + 3
    +    elif after_prefix and after_prefix[0] in "\"'":
    +        return prefix_len + 1
    +
    +    # Fallback: just one character
    +    return 1
    +
    +
    +def _handle_unterminated_string(e, source_lines):
    +    """Handle unterminated string literal errors.
    +
    +    For single-line strings, mark from the opening to end of the line,
    +    and emphasize the full opener (prefix + quote).
    +    """
    +    error_line = e.lineno
    +    error_col = (e.offset - 1) if e.offset else 0
    +
    +    if not source_lines or error_line < 1 or error_line > len(source_lines):
    +        return None, None
    +
    +    line = source_lines[error_line - 1].rstrip("\n\r")
    +    end_col = len(line)
    +
    +    # Get the full string opener length (prefix + quote)
    +    opener_len = _get_string_opener_length(line, error_col)
    +
    +    # Mark from the opening to end of line
    +    mark_range = Range(error_line, error_line, error_col, end_col)
    +    # Emphasize the full opener (prefix + quote)
    +    em_ranges = [Range(error_line, error_line, error_col, error_col + opener_len)]
    +
    +    return mark_range, em_ranges
    +
    +
    +def _handle_unterminated_triple_string(e, source_lines):
    +    """Handle unterminated triple-quoted string literal errors.
    +
    +    Mark from opening to end of line, emphasize the full opener (prefix + triple quotes).
    +    """
    +    error_line = e.lineno
    +    error_col = (e.offset - 1) if e.offset else 0
    +
    +    if not source_lines or error_line < 1 or error_line > len(source_lines):
    +        return None, None
    +
    +    line = source_lines[error_line - 1].rstrip("\n\r")
    +    end_col = len(line)
    +
    +    # Get the full string opener length (prefix + triple quotes)
    +    opener_len = _get_string_opener_length(line, error_col)
    +
    +    # Mark from opening to end of line (not end of input - per user feedback)
    +    mark_range = Range(error_line, error_line, error_col, end_col)
    +    # Emphasize the full opener (prefix + triple quotes)
    +    em_ranges = [Range(error_line, error_line, error_col, error_col + opener_len)]
    +
    +    return mark_range, em_ranges
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/trace.py b/.venv/lib/python3.12/site-packages/tracerite/trace.py
    new file mode 100644
    index 0000000..0752ff3
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/trace.py
    @@ -0,0 +1,1916 @@
    +from __future__ import annotations
    +
    +import inspect
    +import linecache
    +import re
    +import sys
    +import tokenize
    +from collections import namedtuple
    +from contextlib import suppress
    +from pathlib import Path
    +from secrets import token_urlsafe
    +from urllib.parse import quote
    +
    +from . import trace_cpy
    +from .inspector import extract_variables
    +from .logging import logger
    +from .syntaxerror import clean_syntax_error_message, extract_enhanced_positions
    +
    +# Position range: lines are 1-based inclusive, columns are 0-based exclusive
    +Range = namedtuple("Range", ["lfirst", "lfinal", "cbeg", "cend"])
    +
    +
    +def compute_cursor_position(
    +    mark_range: Range | None,
    +    em_ranges: Range | list[Range] | None,
    +    linenostart: int,
    +    common_indent: str = "",
    +) -> tuple[int, int]:
    +    """Compute the preferred cursor position from mark and emphasis ranges.
    +
    +    Prefers the end of emphasis (em) ranges if available, as these mark the
    +    error position more precisely. Falls back to end of mark range, then
    +    to line 1, column 0.
    +
    +    Args:
    +        mark_range: The marked region Range, or None
    +        em_ranges: The emphasis Range, list of Ranges, or None
    +        linenostart: The starting line number of the displayed code (for conversion)
    +        common_indent: The common indent string that was stripped (to restore original columns)
    +
    +    Returns:
    +        Tuple of (line, column) where line is 1-based absolute line number
    +        and column is 0-based.
    +    """
    +    indent_len = len(common_indent)
    +
    +    # Try emphasis ranges first (more precise error position)
    +    if em_ranges:
    +        if isinstance(em_ranges, list) and em_ranges:
    +            # Use the last em range's end position
    +            last_em = em_ranges[-1]
    +            # Convert from context-relative to absolute line number
    +            # lfinal is 1-based relative to displayed code, linenostart is absolute
    +            line = linenostart + last_em.lfinal - 1
    +            # cend is 0-based exclusive in dedented code, add indent for original
    +            col = last_em.cend + indent_len
    +            return (line, col)
    +        elif isinstance(em_ranges, Range):
    +            line = linenostart + em_ranges.lfinal - 1
    +            col = em_ranges.cend + indent_len
    +            return (line, col)
    +
    +    # Fall back to mark range
    +    if mark_range:
    +        line = linenostart + mark_range.lfinal - 1
    +        col = mark_range.cend + indent_len
    +        return (line, col)
    +
    +    # No range information available
    +    return (linenostart, 0)
    +
    +
    +# Will be set to an instance if loaded as an IPython extension by %load_ext
    +ipython = None
    +
    +# Locations considered to be bug-free (library code, not user code), capture pretty suffix
    +libdir = re.compile(
    +    r".*(?:site-packages|dist-packages)/(.+)"
    +    r"|.*/lib/python\d+\.\d+/(.+)"
    +    r"|.*/bin/([^/]+)(? str:
    +    """Build a header message describing the exception chain."""
    +    if not chain:
    +        return ""
    +
    +    # Chain is oldest-first: chain[0] is first exception, chain[-1] is last (uncaught)
    +    last_exc = chain[-1]
    +
    +    # For ExceptionGroups, show the final exception types from subexceptions
    +    subexceptions = last_exc.get("subexceptions")
    +    if subexceptions:
    +        leaf_types = _collect_leaf_exception_types(subexceptions)
    +        if leaf_types:
    +            exc_type = " | ".join(leaf_types)
    +            # Don't say "Uncaught" for ExceptionGroups, just show the leaf types
    +            if len(chain) == 1:
    +                return f"⚠️  {exc_type}"
    +        else:
    +            exc_type = last_exc.get("type", "Exception")
    +    else:
    +        exc_type = last_exc.get("type", "Exception")
    +
    +    if len(chain) == 1:
    +        return f"⚠️  Uncaught {exc_type}"
    +
    +    # Build from last to first
    +    parts = [f"⚠️  {exc_type}"]
    +
    +    # Add each previous exception with appropriate joiner
    +    for i in range(len(chain) - 2, -1, -1):
    +        exc = chain[i]
    +        next_exc = chain[i + 1]
    +        from_type = next_exc.get("from", "none")
    +        joiner = "from" if from_type == "cause" else "while handling"
    +        parts.append(f"{joiner} {exc.get('type', 'Exception')}")
    +
    +    return " ".join(parts)
    +
    +
    +def _collect_leaf_exception_types(subexceptions: list[list[dict]]) -> list[str]:
    +    """Collect the final exception types from all subexception chains.
    +
    +    For nested ExceptionGroups, recursively collects leaf exception types.
    +    Returns a flat list of exception type names.
    +    """
    +    leaf_types = []
    +    for sub_chain in subexceptions:
    +        if not sub_chain:
    +            continue
    +        # Get the last exception in this chain (the one that was raised)
    +        last_exc = sub_chain[-1]
    +        # Check if this is itself an ExceptionGroup with subexceptions
    +        nested_subs = last_exc.get("subexceptions")
    +        if nested_subs:
    +            # Recursively collect from nested ExceptionGroup
    +            leaf_types.extend(_collect_leaf_exception_types(nested_subs))
    +        else:
    +            # This is a leaf exception
    +            leaf_types.append(last_exc.get("type", "Exception"))
    +    return leaf_types
    +
    +
    +def extract_chain(exc=None, **kwargs) -> list:
    +    """Extract information on current exception.
    +
    +    Returns a list of exception info dicts, ordered from oldest to newest
    +    (i.e., the original exception first, then any exceptions that occurred
    +    while handling it or were raised from it).
    +    """
    +    chain = []
    +    exc = exc or sys.exc_info()[1]
    +    while exc:
    +        chain.append(exc)
    +        exc = exc.__cause__ or None if exc.__suppress_context__ else exc.__context__
    +    # Reverse to get oldest first (chain is built newest-first)
    +    chain = list(reversed(chain))
    +    result = [extract_exception(e, **(kwargs if e is chain[-1] else {})) for e in chain]
    +    # Deduplicate variable inspectors: only keep variables for the last occurrence
    +    # of each (filename, function) pair across the entire chain
    +    _deduplicate_variables(result)
    +    return result
    +
    +
    +def _deduplicate_variables(chain: list) -> None:
    +    """Remove duplicate variables from inspectors, showing each only once.
    +
    +    Variables are only shown if they appear in the frame's highlighted code
    +    (the lines indicated by the error range, expanded to include full
    +    comprehensions). If a variable appears in multiple frames' highlighted
    +    code (same filename/function), it's only shown in the last frame where
    +    it appears.
    +    """
    +
    +    def _get_highlighted_lines(frame: dict) -> str:
    +        """Extract the highlighted lines from a frame based on its range.
    +
    +        Expands to include full comprehension if error is inside one.
    +        """
    +        lines = frame.get("lines", "")
    +        range_obj = frame.get("range")
    +        if not range_obj or not lines:
    +            return lines  # Fall back to all lines if no range
    +
    +        start = frame.get("linenostart", 1)
    +        lfirst, lfinal = range_obj.lfirst, range_obj.lfinal
    +
    +        # Check if error is inside a comprehension - if so, return full comprehension
    +        comp_range = _find_comprehension_range(lines, lfirst, start)
    +        if comp_range is not None:
    +            # Error is inside a comprehension - return full lines (already trimmed to comprehension)
    +            return lines
    +
    +        # No comprehension, return just the highlighted lines
    +        lines_list = lines.splitlines()
    +
    +        # Convert to 0-based indices relative to displayed lines
    +        first_idx = lfirst - start
    +        final_idx = lfinal - start + 1
    +
    +        if first_idx < 0 or first_idx >= len(lines_list):
    +            return lines  # Fall back if range is invalid
    +
    +        return "\n".join(lines_list[first_idx:final_idx])
    +
    +    def _variable_in_code(name: str, lines: str) -> bool:
    +        """Check if a variable name appears in the code as a word."""
    +        return bool(re.search(rf"\b{re.escape(name)}\b", lines))
    +
    +    # First pass: collect frames by (filename, function) key
    +    # Maps key -> list of (exception_idx, frame_idx)
    +    frame_groups: dict[tuple, list[tuple[int, int]]] = {}
    +    for ei, exc in enumerate(chain):
    +        for fi, frame in enumerate(exc.get("frames", [])):
    +            if frame.get("relevance") == "call":
    +                continue
    +            key = (frame.get("filename"), frame.get("function"))
    +            if key not in frame_groups:
    +                frame_groups[key] = []
    +            frame_groups[key].append((ei, fi))
    +
    +    # Second pass: for each group, determine which variables to show in each frame
    +    for _key, occurrences in frame_groups.items():
    +        # For each variable, find the LAST frame where it appears in highlighted code
    +        # variable_name -> (exception_idx, frame_idx) of last appearance in highlighted code
    +        last_appearance: dict[str, tuple[int, int]] = {}
    +
    +        for ei, fi in occurrences:
    +            frame = chain[ei]["frames"][fi]
    +            highlighted = _get_highlighted_lines(frame)
    +            for v in frame.get("variables", []):  # pragma: no cover
    +                if v.name and _variable_in_code(v.name, highlighted):
    +                    # Update to this frame (later frames overwrite earlier)
    +                    last_appearance[v.name] = (ei, fi)
    +
    +        # Now filter each frame's variables: keep only if this is the last appearance
    +        for ei, fi in occurrences:
    +            frame = chain[ei]["frames"][fi]
    +            frame["variables"] = [
    +                v
    +                for v in frame.get("variables", [])
    +                if v.name and last_appearance.get(v.name) == (ei, fi)
    +            ]
    +
    +
    +def _create_summary(message):
    +    """Extract the first line of the exception message as summary."""
    +    return message.split("\n", 1)[0]
    +
    +
    +def _set_relevances(frames: list, e: BaseException) -> None:
    +    """Set relevance for frames after extraction.
    +
    +    - The last frame gets "error" (regular Exception) or "stop" (BaseException like KeyboardInterrupt)
    +    - ExceptionGroups also get "stop" since the interesting parts are in subexceptions
    +    - If the last frame is in library code, the last user code frame gets "warning"
    +    - All other frames remain "call"
    +    """
    +    if not frames:
    +        return
    +
    +    # Last frame is where the exception occurred
    +    # ExceptionGroups get "stop" like BaseExceptions - the real errors are in subexceptions
    +    is_regular_exception = isinstance(e, Exception) and not _is_exception_group(e)
    +    frames[-1]["relevance"] = "error" if is_regular_exception else "stop"
    +
    +    # Check if the last frame (error frame) is in user code
    +    last_filename = (
    +        frames[-1].get("original_filename") or frames[-1].get("filename") or ""
    +    )
    +    if _libdir_match(Path(last_filename).as_posix()) is None:
    +        return
    +    # Error is in library code - find the last user code frame to mark as warning
    +    for frame in reversed(frames[:-1]):  # Exclude the last frame  # pragma: no cover
    +        filename = frame.get("original_filename") or frame.get("filename") or ""
    +        if _libdir_match(Path(filename).as_posix()) is None:
    +            # This is user code - mark as warning (bug origin)
    +            frame["relevance"] = "warning"
    +            break
    +
    +
    +def extract_exception(e, *, skip_outmost=0, skip_until=None) -> dict:
    +    raw_tb = e.__traceback__
    +    try:
    +        tb = inspect.getinnerframes(raw_tb)
    +    except IndexError:  # Bug in inspect internals, find_source()
    +        logger.exception("Bug in inspect?")
    +        tb = []
    +        raw_tb = None
    +
    +    # For SyntaxError, check if the error is in user code (notebook cell or matching skip_until)
    +    syntax_frame = None
    +    if isinstance(e, SyntaxError):
    +        syntax_frame = _extract_syntax_error_frame(e)
    +        if syntax_frame:
    +            # Check if this is a notebook cell (using IPython's filename map) or matches skip_until
    +            is_user_code = _is_notebook_cell(e.filename) or (
    +                skip_until and skip_until in (e.filename or "")
    +            )
    +            if is_user_code:
    +                skip_outmost = len(tb)  # Skip all frames
    +
    +    if skip_until and skip_outmost == 0:
    +        for i, frame in enumerate(tb):
    +            if skip_until in frame.filename:
    +                skip_outmost = i
    +                break
    +    tb = tb[skip_outmost:]
    +
    +    # Also skip the same number of frames from raw_tb
    +    if raw_tb and skip_outmost > 0:
    +        for _ in range(skip_outmost):
    +            if raw_tb:
    +                raw_tb = raw_tb.tb_next
    +
    +    # Header and exception message
    +    message = getattr(e, "message", "") or str(e)
    +    # For SyntaxError, trim redundant location info from message
    +    if isinstance(e, SyntaxError):
    +        message = clean_syntax_error_message(message)
    +    summary = _create_summary(message)
    +    # Check if context is suppressed (raise X from None) - affects source trimming
    +    f = (
    +        "cause"
    +        if e.__cause__
    +        else "context"
    +        if e.__context__ and not e.__suppress_context__
    +        else "none"
    +    )
    +    try:
    +        frames = extract_frames(tb, raw_tb, except_block=(f != "none"), exc=e)
    +        # For SyntaxError, add the synthetic frame showing the problematic code
    +        if syntax_frame:
    +            # Demote the previous frame (compile, exec, etc.) to call only
    +            if frames and frames[-1]["relevance"] == "error":
    +                frames[-1]["relevance"] = "call"
    +            frames.append(syntax_frame)
    +    except Exception:
    +        logger.exception("Error extracting traceback")
    +        frames = None
    +
    +    # Determine if this is a "stop" type exception (BaseException or ExceptionGroup)
    +    # These suppress inner library frames, showing only up to the last user code frame.
    +    # ExceptionGroups suppress because the interesting parts are in subexceptions.
    +    is_stop_type = not isinstance(e, Exception) or _is_exception_group(e)
    +
    +    result = {
    +        "type": type(e).__name__,
    +        "message": message,
    +        "summary": summary,
    +        "from": f,
    +        "repr": repr(e),
    +        "frames": frames or [],
    +        "suppress_inner": is_stop_type,
    +    }
    +
    +    # Extract subexceptions for ExceptionGroups (Python 3.11+)
    +    # These form parallel timelines within the group's traceback
    +    subexceptions = _extract_subexceptions(
    +        e, skip_outmost=skip_outmost, skip_until=skip_until
    +    )
    +    if subexceptions:
    +        result["subexceptions"] = subexceptions
    +
    +    return result
    +
    +
    +def _extract_subexceptions(
    +    e, *, skip_outmost=0, skip_until=None
    +) -> list[list[dict]] | None:
    +    """Extract subexceptions from an ExceptionGroup.
    +
    +    ExceptionGroups (Python 3.11+) contain multiple exceptions that occurred
    +    in parallel (e.g., in concurrent tasks). Each subexception forms its own
    +    traceback chain that ran in parallel with others.
    +
    +    Args:
    +        e: The exception to check for subexceptions
    +        skip_outmost: Number of outermost frames to skip
    +        skip_until: Skip frames until this string is found in filename
    +
    +    Returns:
    +        List of exception chains (each chain is a list of exception info dicts),
    +        or None if not an ExceptionGroup or has no subexceptions.
    +        Each chain represents a parallel timeline of exceptions.
    +    """
    +    # Check if this is an ExceptionGroup (Python 3.11+)
    +    # BaseExceptionGroup is the base class for both ExceptionGroup and BaseExceptionGroup
    +    if not hasattr(e, "exceptions") or not isinstance(
    +        getattr(e, "exceptions", None), (tuple, list)
    +    ):
    +        return None
    +
    +    subexceptions = e.exceptions
    +    if not subexceptions:
    +        return None
    +
    +    # Extract each subexception as its own chain
    +    # Each subexception may itself be an ExceptionGroup with nested subexceptions
    +    parallel_chains = []
    +    for sub_exc in subexceptions:
    +        # Recursively extract the chain for this subexception
    +        # This handles nested ExceptionGroups and exception chaining within each sub
    +        sub_chain = _extract_subexception_chain(
    +            sub_exc, skip_outmost=skip_outmost, skip_until=skip_until
    +        )
    +        if sub_chain:  # pragma: no cover
    +            parallel_chains.append(sub_chain)
    +
    +    return parallel_chains if parallel_chains else None
    +
    +
    +def _extract_subexception_chain(exc, *, skip_outmost=0, skip_until=None) -> list[dict]:
    +    """Extract the full exception chain for a single subexception.
    +
    +    Similar to extract_chain but for a subexception that may have its own
    +    __cause__ or __context__ chain.
    +
    +    Args:
    +        exc: The subexception to extract
    +        skip_outmost: Number of outermost frames to skip
    +        skip_until: Skip frames until this string is found in filename
    +
    +    Returns:
    +        List of exception info dicts, ordered from oldest to newest
    +    """
    +    chain = []
    +    current = exc
    +    while current:
    +        chain.append(current)
    +        current = (
    +            current.__cause__ or None
    +            if current.__suppress_context__
    +            else current.__context__
    +        )
    +    # Reverse to get oldest first
    +    chain = list(reversed(chain))
    +
    +    # Extract info for each exception in the chain
    +    # Pass skip args only to the last one (the actual subexception)
    +    kwargs = {"skip_outmost": skip_outmost, "skip_until": skip_until}
    +    result = [extract_exception(e, **(kwargs if e is chain[-1] else {})) for e in chain]
    +    return result
    +
    +
    +def _is_notebook_cell(filename):
    +    """Check if the filename corresponds to a Jupyter notebook cell."""
    +    try:
    +        return filename in ipython.compile._filename_map  # type: ignore[attr-defined]
    +    except (AttributeError, KeyError, TypeError):
    +        return False
    +
    +
    +def _is_exception_group(e: BaseException) -> bool:
    +    """Check if exception is an ExceptionGroup (Python 3.11+)."""
    +    # Check for BaseExceptionGroup which is the base class for both
    +    # ExceptionGroup and BaseExceptionGroup
    +    return hasattr(e, "exceptions") and isinstance(
    +        getattr(e, "exceptions", None), (tuple, list)
    +    )
    +
    +
    +def _find_except_start_for_line(frame, lineno: int) -> int | None:
    +    """If lineno is inside an except handler, return the except line number.
    +
    +    Uses AST analysis to find if the given line is within an except block.
    +    Returns the line number of the 'except' keyword for the innermost matching
    +    except handler, or None if not in an except block.
    +    """
    +    from .chain_analysis import (
    +        find_try_block_for_except_line,
    +        parse_source_for_try_except,
    +    )
    +
    +    try:
    +        filename = frame.f_code.co_filename
    +        blocks = parse_source_for_try_except(filename)
    +        # Find the innermost except block containing this line
    +        block = find_try_block_for_except_line(blocks, lineno)
    +        if block:
    +            return block.except_start
    +    except Exception:  # pragma: no cover
    +        pass
    +    return None
    +
    +
    +def _get_source_lines_from_code(code, lineno: int, end_lineno: int | None = None):
    +    """Get source lines from a code object using Python 3.11+ linecache API.
    +
    +    This provides a fallback for getting source code for interactive code
    +    (REPL, -c command, exec'd strings) where inspect.getsourcelines() fails.
    +
    +    Args:
    +        code: The code object from a frame (frame.f_code)
    +        lineno: The line number where the error occurred (1-based)
    +        end_lineno: Optional end line number for multi-line errors
    +
    +    Returns:
    +        (lines, start) tuple where lines is a list of source lines with
    +        newlines, or (None, None) if source cannot be retrieved.
    +    """
    +    # Python 3.13+ has linecache._getline_from_code for interactive code
    +    if not hasattr(linecache, "_getline_from_code"):
    +        return None, None  # pragma: no cover
    +
    +    # First, check if we can get the error line at all
    +    error_line = linecache._getline_from_code(code, lineno)
    +    if not error_line:
    +        return None, None
    +
    +    first_lineno = code.co_firstlineno
    +    is_module = code.co_name in (
    +        "",
    +        "",
    +        "",
    +        "",
    +        "",
    +    )
    +
    +    # For module level, just get context around the error line
    +    if is_module:
    +        start = max(1, lineno - 10)
    +        final = (end_lineno or lineno) + 3
    +        lines = []
    +        actual_start = None
    +        for ln in range(start, final + 1):
    +            line = linecache._getline_from_code(code, ln)
    +            if line:
    +                if actual_start is None:
    +                    actual_start = ln
    +                lines.append(line)
    +            elif lines and ln > (end_lineno or lineno):  # pragma: no cover
    +                break  # Stop at empty lines after error (e.g., end of source)
    +        # Defensive: error_line check above guarantees we have lines
    +        if not lines or actual_start is None:  # pragma: no cover
    +            return None, None
    +        return lines, actual_start
    +
    +    # For functions/methods, collect all lines starting from definition
    +    # then use inspect.getblock to find the function boundaries
    +    all_lines = []
    +    ln = first_lineno
    +    while True:
    +        line = linecache._getline_from_code(code, ln)
    +        if not line:
    +            break
    +        all_lines.append(line)
    +        ln += 1
    +
    +    # Defensive: error_line check above guarantees we have lines
    +    if not all_lines:  # pragma: no cover
    +        return None, None
    +
    +    # Use inspect.getblock to find the function's extent (same as inspect.getsourcelines)
    +    try:
    +        block_lines = inspect.getblock(all_lines)
    +    except (IndentationError, SyntaxError, tokenize.TokenError):  # pragma: no cover
    +        # Fallback: just use lines up to a reasonable extent
    +        block_lines = all_lines[: (end_lineno or lineno) - first_lineno + 3]
    +
    +    return block_lines, first_lineno
    +
    +
    +def extract_source_lines(
    +    frame, lineno, end_lineno=None, *, notebook_cell=False, except_block=False
    +):
    +    try:
    +        lines, start = inspect.getsourcelines(frame)
    +        if start == 0:
    +            start = 1
    +
    +        # Check if lineno is inside an except handler BEFORE trimming
    +        # This ensures we include the except line even for notebook cells
    +        # Skip this detection if context was suppressed (raise X from None)
    +        except_start = (
    +            _find_except_start_for_line(frame, lineno) if except_block else None
    +        )
    +
    +        # For notebook cells, show only the error lines (no context)
    +        # For regular files, show 10 lines before and 2 lines after
    +        # Exception: if we're in an except block, ensure except line is included
    +        if notebook_cell:
    +            if except_start is not None and except_start >= start:
    +                # In except block: include from except line to lineno
    +                lines_before = lineno - except_start  # pragma: no cover
    +            else:
    +                lines_before = 0
    +            lines_after = (end_lineno - lineno) if end_lineno else 0
    +        else:
    +            lines_before = 10
    +            lines_after = (end_lineno - lineno + 2) if end_lineno else 2
    +        # Calculate slice bounds
    +        slice_start = max(0, lineno - start - lines_before)
    +        slice_end = max(0, lineno - start + lines_after + 1)
    +
    +        # Skip forward if the slice would start inside a string or unclosed parens
    +        # Analyze all lines before slice_start to determine context state
    +        skip_to = _find_clean_start_line(lines, slice_start)
    +        if skip_to > slice_start:
    +            slice_start = skip_to
    +
    +        lines = lines[slice_start:slice_end]
    +        start += slice_start
    +
    +        # If lineno is inside an except handler, trim to start from the except line
    +        # (For non-notebook cells, this may still trim if lines_before > distance to except)
    +        if except_start is not None and except_start > start:
    +            skip = except_start - start
    +            if skip < len(lines):  # pragma: no branch
    +                lines = lines[skip:]
    +                start = except_start
    +
    +        # Calculate error line position
    +        error_idx = lineno - start
    +        end_idx = (end_lineno - start) if end_lineno else error_idx
    +
    +        # Safety check: ensure error_idx is valid
    +        if not lines or error_idx < 0 or error_idx >= len(lines):
    +            return "", lineno, ""
    +
    +        # Get the indentation of the first marked line (error line) before any dedenting
    +        error_indent = 0
    +        error_line = lines[error_idx]
    +        error_indent = len(error_line) - len(error_line.lstrip(" \t"))
    +
    +        # Trim leading lines that have more indentation than error line
    +        while lines and error_idx > 0:
    +            first_line = lines[0]
    +            if first_line.strip():
    +                first_indent = len(first_line) - len(first_line.lstrip(" \t"))
    +                if first_indent <= error_indent:
    +                    break  # This line has same or less indent, keep it
    +            start += 1
    +            lines.pop(0)
    +            error_idx -= 1
    +            end_idx -= 1
    +
    +        # Trim trailing lines with less indentation than the error line
    +        # (hides external structures like else/except that aren't relevant)
    +        # But don't trim if we're inside unclosed brackets (e.g., list comprehension)
    +        trim_after = end_idx + 1
    +        bracket_depth = _count_bracket_depth("".join(lines[: end_idx + 1]))
    +        while trim_after < len(lines):
    +            line = lines[trim_after]
    +            # Keep lines if brackets are still open
    +            if bracket_depth > 0:
    +                bracket_depth += _count_bracket_depth(line)
    +                trim_after += 1
    +                continue
    +            # Keep empty lines, but check non-empty lines for indentation
    +            if line.strip():
    +                line_indent = len(line) - len(line.lstrip(" \t"))
    +                if line_indent < error_indent:
    +                    break  # Found a line with less indent, trim from here
    +            trim_after += 1
    +        lines = lines[:trim_after]
    +
    +        # Calculate common indentation and dedent AFTER pruning
    +        common_indent = _calculate_common_indent(lines)
    +        lines = [ln.removeprefix(common_indent) for ln in lines]
    +
    +        return "".join(lines), start, common_indent
    +    except OSError:
    +        # Fallback: try to get source from code object (Python 3.13+ interactive code)
    +        # This is tested via subprocess tests in test_tty.py::TestInteractiveSourceRetrieval
    +        code = frame.f_code if hasattr(frame, "f_code") else frame  # pragma: no cover
    +        fallback_lines, fallback_start = (
    +            _get_source_lines_from_code(  # pragma: no cover
    +                code, lineno, end_lineno
    +            )
    +        )
    +        if fallback_lines:  # pragma: no cover
    +            common_indent = _calculate_common_indent(fallback_lines)
    +            lines = [ln.removeprefix(common_indent) for ln in fallback_lines]
    +            return "".join(lines), fallback_start, common_indent
    +        return "", lineno, ""  # Source not available (non-Python module)
    +
    +
    +def _count_bracket_depth(text: str) -> int:
    +    """Count net bracket depth change in text, ignoring brackets in strings/comments.
    +
    +    Returns positive for more opens than closes, negative for more closes.
    +    """
    +    depth = 0
    +    in_string = False
    +    string_char = None
    +    escape_next = False
    +    i = 0
    +
    +    while i < len(text):
    +        char = text[i]
    +
    +        if escape_next:
    +            escape_next = False
    +            i += 1
    +            continue
    +
    +        if char == "\\":
    +            escape_next = True
    +            i += 1
    +            continue
    +
    +        # Handle comments (outside strings)
    +        if not in_string and char == "#":
    +            break  # Rest of line is comment
    +
    +        # Handle string boundaries
    +        if not in_string:
    +            # Check for triple-quoted strings
    +            if char in ('"', "'") and text[i : i + 3] in ('"""', "'''"):
    +                in_string = True
    +                string_char = text[i : i + 3]
    +                i += 3
    +                continue
    +            elif char in ('"', "'"):
    +                in_string = True
    +                string_char = char
    +        else:
    +            # Check for end of string
    +            if string_char in ('"""', "'''") and text[i : i + 3] == string_char:
    +                in_string = False
    +                string_char = None
    +                i += 3
    +                continue
    +            elif len(string_char) == 1 and char == string_char:
    +                in_string = False
    +                string_char = None
    +
    +        # Count brackets only outside strings
    +        if not in_string:
    +            if char in "([{":
    +                depth += 1
    +            elif char in ")]}":
    +                depth -= 1
    +
    +        i += 1
    +
    +    return depth
    +
    +
    +def _find_clean_start_line(lines: list[str], target_idx: int) -> int:
    +    """Find the first line at or after target_idx that isn't inside an unclosed context.
    +
    +    Analyzes lines[0:target_idx] to determine if target_idx would start inside:
    +    - A multi-line string (triple-quoted docstring, etc.)
    +    - An unclosed parenthesis/bracket/brace expression
    +
    +    If so, scans forward from target_idx to find where that context closes,
    +    returning the index of the first "clean" line.
    +
    +    Args:
    +        lines: List of source lines (with newlines)
    +        target_idx: The 0-based index we want to start displaying from
    +
    +    Returns:
    +        Index >= target_idx of the first line not inside an unclosed context
    +    """
    +    if target_idx <= 0 or target_idx >= len(lines):
    +        return target_idx
    +
    +    # Parse all lines before target to determine state at target_idx
    +    in_string = False
    +    string_char = None  # The quote char(s) that opened the string
    +    bracket_depth = 0
    +
    +    for line in lines[:target_idx]:
    +        i = 0
    +        text = line
    +        escape_next = False
    +
    +        while i < len(text):
    +            char = text[i]
    +
    +            if escape_next:
    +                escape_next = False
    +                i += 1
    +                continue
    +
    +            if char == "\\" and in_string:
    +                escape_next = True
    +                i += 1
    +                continue
    +
    +            # Handle comments (outside strings)
    +            if not in_string and char == "#":
    +                break  # Rest of line is comment
    +
    +            # Handle string boundaries
    +            if not in_string:
    +                # Check for triple-quoted strings first
    +                if char in ('"', "'") and text[i : i + 3] in ('"""', "'''"):
    +                    in_string = True
    +                    string_char = text[i : i + 3]
    +                    i += 3
    +                    continue
    +                elif char in ('"', "'"):
    +                    in_string = True
    +                    string_char = char
    +            else:
    +                # Check for end of string
    +                if string_char in ('"""', "'''") and text[i : i + 3] == string_char:
    +                    in_string = False
    +                    string_char = None
    +                    i += 3
    +                    continue
    +                elif len(string_char) == 1 and char == string_char:
    +                    in_string = False
    +                    string_char = None
    +
    +            # Count brackets only outside strings
    +            if not in_string:
    +                if char in "([{":
    +                    bracket_depth += 1
    +                elif char in ")]}":
    +                    bracket_depth -= 1
    +
    +            i += 1
    +
    +    # If we're not in a bad context, target_idx is fine
    +    if not in_string and bracket_depth <= 0:
    +        return target_idx
    +
    +    # Scan forward from target_idx until context closes
    +    # This is defensive code for rare edge cases (multiline strings/brackets at slice boundary)
    +    for idx in range(target_idx, len(lines)):  # pragma: no cover
    +        text = lines[idx]
    +        i = 0
    +        escape_next = False
    +
    +        while i < len(text):
    +            char = text[i]
    +
    +            if escape_next:
    +                escape_next = False
    +                i += 1
    +                continue
    +
    +            if char == "\\" and in_string:
    +                escape_next = True
    +                i += 1
    +                continue
    +
    +            # Handle comments (outside strings)
    +            if not in_string and char == "#":
    +                break
    +
    +            # Handle string boundaries
    +            if not in_string:
    +                if char in ('"', "'") and text[i : i + 3] in ('"""', "'''"):
    +                    in_string = True
    +                    string_char = text[i : i + 3]
    +                    i += 3
    +                    continue
    +                elif char in ('"', "'"):
    +                    in_string = True
    +                    string_char = char
    +            else:
    +                if string_char in ('"""', "'''") and text[i : i + 3] == string_char:
    +                    in_string = False
    +                    string_char = None
    +                    i += 3
    +                    continue
    +                elif string_char and len(string_char) == 1 and char == string_char:
    +                    in_string = False
    +                    string_char = None
    +
    +            if not in_string:
    +                if char in "([{":
    +                    bracket_depth += 1
    +                elif char in ")]}":
    +                    bracket_depth -= 1
    +
    +            i += 1
    +
    +        # After processing this line, check if we've exited the bad context
    +        if not in_string and bracket_depth <= 0:
    +            return idx + 1  # Start from the line AFTER the context closes
    +
    +    # Couldn't find clean exit, fall back to target
    +    return target_idx  # pragma: no cover
    +
    +
    +def _get_full_source(frame, lineno=None):
    +    """Get the full source code for a frame using inspect.
    +
    +    Returns (source, start_line) tuple. This works with any source Python
    +    knows about, including notebook cells and exec'd strings.
    +
    +    Args:
    +        frame: The frame object or code object
    +        lineno: Optional line number hint for fallback source retrieval
    +    """
    +    try:
    +        lines, start = inspect.getsourcelines(frame)
    +        if start == 0:
    +            start = 1
    +        return "".join(lines), start
    +    except OSError:
    +        # Fallback: try to get source from code object (Python 3.13+ interactive code)
    +        # This is tested via subprocess tests in test_tty.py::TestInteractiveSourceRetrieval
    +        code = frame.f_code if hasattr(frame, "f_code") else frame  # pragma: no cover
    +        if lineno is None:  # pragma: no cover
    +            lineno = getattr(frame, "f_lineno", code.co_firstlineno)
    +        fallback_lines, fallback_start = _get_source_lines_from_code(
    +            code, lineno
    +        )  # pragma: no cover
    +        if fallback_lines:  # pragma: no cover
    +            return "".join(fallback_lines), fallback_start
    +        return None, None
    +
    +
    +def _libdir_match(path):
    +    """Check if path is in a library directory and return the short suffix if so."""
    +    m = libdir.fullmatch(path)
    +    if m:
    +        return next((g for g in m.groups() if g), "")
    +    return None
    +
    +
    +def format_location(filename, lineno, col=1):
    +    """Format location information for a frame.
    +
    +    Args:
    +        filename: The source file path
    +        lineno: Line number (1-based)
    +        col: Column number (1-based, default 1)
    +
    +    Returns:
    +        Tuple of (filename, location, urls) where:
    +        - filename: Possibly shortened file path
    +        - location: Display string for the location
    +        - urls: Dict of URL schemes to URLs (e.g., VS Code, Jupyter)
    +    """
    +    urls = {}
    +    location = None
    +    try:
    +        ipython_in = ipython.compile._filename_map[filename]  # type: ignore[attr-defined]
    +        location = f"In [{ipython_in}]"
    +        filename = None
    +    except (AttributeError, KeyError):
    +        pass
    +    if filename and Path(filename).is_file():
    +        fn = Path(filename).resolve()
    +        # vscode:// URLs use format vscode://file/path:line:col
    +        urls["VS Code"] = f"vscode://file{quote(fn.as_posix())}:{lineno}:{col}"
    +        cwd = Path.cwd()
    +        if cwd in fn.parents:
    +            fn = fn.relative_to(cwd)
    +            if ipython is not None:
    +                urls["Jupyter"] = f"/edit/{quote(fn.as_posix())}"
    +        filename = fn.as_posix()
    +    if not location and filename:
    +        # Use library short path if available, otherwise truncate long paths
    +        location = _libdir_match(filename)
    +        if location is None:
    +            split = (
    +                filename.rfind("/", 10, len(filename) - 20) + 1
    +                if len(filename) > 40
    +                else 0
    +            )
    +            location = filename[split:]
    +    # Ensure location is never None (fallback for edge cases)
    +    if not location:
    +        location = ""
    +    return filename, location, urls
    +
    +
    +def _get_qualified_function_name(frame, function):
    +    """Get qualified function name with class prefix if available."""
    +    if function == "":
    +        return None
    +    try:
    +        cls = next(
    +            v.__class__ if n == "self" else v
    +            for n, v in frame.f_locals.items()
    +            if n in ("self", "cls") and v is not None
    +        )
    +        function = f"{cls.__name__}.{function}"
    +    except StopIteration:
    +        pass
    +    return ".".join(function.split(".")[-2:])
    +
    +
    +def _extract_text_from_range(lines: str, mark_range) -> str | None:
    +    """Extract the text covered by a Range from source lines.
    +
    +    Args:
    +        lines: The source code (may contain multiple lines)
    +        mark_range: Range object with lfirst, lfinal (1-based inclusive lines),
    +                   cbeg, cend (0-based exclusive columns), or None
    +
    +    Returns:
    +        The extracted text, or None if mark_range is None.
    +    """
    +    if mark_range is None:
    +        return None
    +
    +    lines_list = lines.splitlines(keepends=True)
    +
    +    # Convert to 0-based line indices
    +    start_line_idx = mark_range.lfirst - 1
    +    end_line_idx = mark_range.lfinal - 1
    +
    +    # Bounds check
    +    if start_line_idx < 0 or end_line_idx >= len(lines_list):
    +        return None
    +
    +    extracted_parts = []
    +    for line_idx in range(start_line_idx, end_line_idx + 1):
    +        line = lines_list[line_idx].rstrip("\r\n")
    +
    +        if line_idx == start_line_idx == end_line_idx:
    +            # Single line case
    +            extracted_parts.append(line[mark_range.cbeg : mark_range.cend])
    +        elif line_idx == start_line_idx:
    +            # First line of multi-line
    +            extracted_parts.append(line[mark_range.cbeg :])
    +        elif line_idx == end_line_idx:
    +            # Last line of multi-line
    +            extracted_parts.append(line[: mark_range.cend])
    +        else:
    +            # Middle lines of multi-line
    +            extracted_parts.append(line)
    +
    +    return " ".join(extracted_parts)
    +
    +
    +def _expand_source_for_comprehension(
    +    lines: str, lineno: int, start: int
    +) -> str:  # pragma: no cover
    +    """Expand source to include full comprehension/generator expression if error is inside one.
    +
    +    This helps show relevant variables like the iterator source (e.g., `data` in `for item in data`).
    +    Note: Currently unused but kept for future use.
    +
    +    Args:
    +        lines: The source code snippet
    +        lineno: The 1-based line number where the error occurred
    +        start: The 1-based starting line number of the snippet
    +
    +    Returns:
    +        Source code that includes the full comprehension, or original lines if not in one.
    +    """
    +    result = _find_comprehension_range(lines, lineno, start)
    +    if result:
    +        lines_list = lines.splitlines(keepends=True)
    +        comp_start, comp_end = result
    +        return "".join(lines_list[comp_start:comp_end])
    +    return lines
    +
    +
    +def _find_comprehension_range(lines: str, lineno: int, start: int):
    +    """Find the line range of a comprehension containing the error line.
    +
    +    Args:
    +        lines: The source code snippet
    +        lineno: The 1-based line number where the error occurred
    +        start: The 1-based starting line number of the snippet
    +
    +    Returns:
    +        Tuple of (start_idx, end_idx) as 0-based indices into lines_list,
    +        or None if error is not inside a comprehension.
    +    """
    +    import ast
    +
    +    # Try to parse the source and find comprehensions containing the error line
    +    try:
    +        tree = ast.parse(lines)
    +    except SyntaxError:
    +        return None
    +
    +    error_line_in_source = lineno - start + 1
    +
    +    # Find comprehension nodes that contain the error line
    +    comprehension_types = (ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp)
    +    for node in ast.walk(tree):
    +        if isinstance(
    +            node, comprehension_types
    +        ) and node.lineno <= error_line_in_source <= (node.end_lineno or node.lineno):
    +            comp_start = node.lineno - 1  # 0-based
    +            comp_end = node.end_lineno or node.lineno  # 1-based, inclusive
    +            return (comp_start, comp_end)
    +
    +    return None
    +
    +
    +def _trim_source_to_comprehension(lines: str, lineno: int, start: int):
    +    """Trim source context to just the comprehension if error is inside one.
    +
    +    Args:
    +        lines: The source code snippet
    +        lineno: The 1-based line number where the error occurred
    +        start: The 1-based starting line number of the snippet
    +
    +    Returns:
    +        Tuple of (trimmed_lines, new_start) where new_start is adjusted line number,
    +        or (lines, start) if not inside a comprehension.
    +    """
    +    result = _find_comprehension_range(lines, lineno, start)
    +    if result:
    +        lines_list = lines.splitlines(keepends=True)
    +        comp_start_idx, comp_end_idx = result
    +        trimmed = "".join(lines_list[comp_start_idx:comp_end_idx])
    +        new_start = start + comp_start_idx
    +        return trimmed, new_start
    +    return lines, start
    +
    +
    +def _get_variable_source_for_comprehension(
    +    lines: str, lineno: int, start: int, mark_range
    +) -> str:
    +    """Get the source code to use for variable extraction, handling comprehensions.
    +
    +    For comprehensions, includes the entire comprehension plus the marked region.
    +    This ensures external variables used anywhere in the comprehension are visible,
    +    even when the error occurs in a specific part (e.g., the filter clause).
    +
    +    Comprehension loop variables (like 'x' in 'for x in data') won't be accessible
    +    in frame.f_locals anyway, so including them doesn't hurt - they'll just be
    +    filtered out during variable extraction.
    +
    +    Args:
    +        lines: The source code snippet
    +        lineno: The 1-based line number where the error occurred
    +        start: The 1-based starting line number of the snippet
    +        mark_range: Range object with the marked region, or None
    +
    +    Returns:
    +        Source code string for variable extraction.
    +    """
    +    # Check if we're inside a comprehension
    +    comp_range = _find_comprehension_range(lines, lineno, start)
    +
    +    if comp_range is not None:
    +        # Inside a comprehension: use full comprehension text
    +        lines_list = lines.splitlines(keepends=True)
    +        comp_start_idx, comp_end_idx = comp_range
    +        return "".join(lines_list[comp_start_idx:comp_end_idx])
    +
    +    # Not in a comprehension: use marked text or fall back to full lines
    +    marked_text = _extract_text_from_range(lines, mark_range)
    +    return marked_text or lines
    +
    +
    +def _extract_emphasis_columns(
    +    lines, error_line_in_context, end_line, start_col, end_col, start
    +):
    +    """Extract emphasis columns using caret anchors from the code segment.
    +
    +    Returns Range with 1-based inclusive line numbers and 0-based exclusive columns,
    +    or None if no anchors found.
    +    """
    +    if not (end_line and start_col is not None and end_col is not None):
    +        return None
    +
    +    all_lines = lines.splitlines(keepends=True)
    +    segment_start = error_line_in_context - 1  # Convert to 0-based for indexing
    +    segment_end = end_line if end_line else error_line_in_context
    +
    +    if not (0 <= segment_start < len(all_lines) and segment_end <= len(all_lines)):
    +        return None
    +
    +    # Extract the segment using CPython's approach
    +    relevant_lines = all_lines[segment_start:segment_end]
    +    if not relevant_lines:
    +        # This can happen when re-raising an existing exception where CPython's
    +        # position info refers to the original raise site but end_line < error_line
    +        return None
    +
    +    segment = "".join(relevant_lines)
    +
    +    # Trim segment using start_col and end_col
    +    segment = segment[start_col : len(segment) - (len(relevant_lines[-1]) - end_col)]
    +    # Attempt to parse for anchors
    +    anchors = None
    +    with suppress(Exception):
    +        anchors = trace_cpy._extract_caret_anchors_from_line_segment(segment)
    +    if not anchors:
    +        return None
    +
    +    l0, l1, c0, c1 = (
    +        anchors.left_end_lineno,
    +        anchors.right_start_lineno,
    +        anchors.left_end_offset,
    +        anchors.right_start_offset,
    +    )
    +    # We get 0-based line numbers and offsets within the segment,
    +    # so we need to adjust them to match the original code.
    +    if l0 == 0:
    +        c0 += start_col
    +    if l1 == 0:
    +        c1 += start_col
    +
    +    # Convert to 1-based inclusive line numbers for consistency
    +    lfirst = l0 + segment_start + 1
    +    lfinal = l1 + segment_start + 1
    +
    +    return Range(lfirst, lfinal, c0, c1)
    +
    +
    +def _build_position_map(raw_tb):
    +    """Build mapping from frame objects to position tuples."""
    +    position_map = {}
    +    if not raw_tb:
    +        return position_map
    +    try:
    +        for frame_obj, positions in trace_cpy._walk_tb_with_full_positions(raw_tb):
    +            position_map[frame_obj] = positions
    +    except Exception:
    +        logger.exception("Error extracting position information")
    +    return position_map
    +
    +
    +def _extract_syntax_error_frame(e):
    +    """Create a synthetic frame dict for a SyntaxError showing the problematic code."""
    +    if not isinstance(e, SyntaxError):
    +        return None
    +
    +    filename = e.filename
    +    lineno = e.lineno
    +    if not filename or not lineno:
    +        return None
    +
    +    # SyntaxError attributes: filename, lineno, offset, text, end_lineno, end_offset
    +    end_lineno = getattr(e, "end_lineno", None) or lineno
    +    # offset is 1-based in SyntaxError, convert to 0-based for our Range
    +    start_col = (e.offset - 1) if e.offset else 0
    +    end_col = getattr(e, "end_offset", None)
    +
    +    if end_col:
    +        end_col = end_col - 1  # Convert to 0-based
    +        # Ensure we have at least one character highlighted
    +        if end_col <= start_col and end_lineno == lineno:
    +            end_col = start_col + 1
    +    else:
    +        end_col = start_col + 1  # Default to single character
    +
    +    assert start_col is not None and end_col is not None
    +
    +    # Get source lines
    +    notebook_cell = _is_notebook_cell(filename)
    +    lines = None
    +    all_lines = None
    +    start = 1  # For SyntaxErrors, we want full source to show bracket matches etc.
    +
    +    # Try to get source from the file or notebook
    +    try:
    +        import linecache
    +
    +        # For notebook cells, try to get from IPython's cache
    +        if notebook_cell and ipython:
    +            try:
    +                cell_source = ipython.compile._filename_map.get(filename)
    +                if cell_source is not None:
    +                    # Get the cell content from the history
    +                    all_lines = linecache.getlines(filename)
    +                    if all_lines:
    +                        # For SyntaxErrors, get full source to enable bracket matching
    +                        lines = "".join(all_lines)
    +            except Exception:
    +                pass
    +
    +        # Fallback: try linecache directly
    +        if not lines:
    +            all_lines = linecache.getlines(filename)
    +            if all_lines:
    +                # For SyntaxErrors, get full source to enable bracket matching
    +                lines = "".join(all_lines)
    +
    +        # Last resort: use the text attribute from SyntaxError itself
    +        if not lines and e.text:
    +            lines = e.text if e.text.endswith("\n") else e.text + "\n"
    +            start = lineno
    +    except Exception:
    +        if e.text:
    +            lines = e.text if e.text.endswith("\n") else e.text + "\n"
    +            start = lineno
    +
    +    if not lines:
    +        return None
    +
    +    # Calculate error position within the displayed lines
    +    error_line_in_context = lineno - start + 1
    +    end_line = end_lineno - start + 1 if end_lineno else None
    +
    +    # Calculate common indentation
    +    lines_list = lines.splitlines(keepends=True)
    +    common_indent = _calculate_common_indent(lines_list)
    +
    +    # Try enhanced SyntaxError position extraction for better highlighting
    +    enhanced_mark, enhanced_em = extract_enhanced_positions(e, lines_list)
    +
    +    if enhanced_mark:
    +        # Override lineno/end_lineno with the enhanced range (e.g., from opening bracket)
    +        lineno = enhanced_mark.lfirst
    +        end_lineno = enhanced_mark.lfinal
    +
    +        # Trim source to start from the mark's first line
    +        lines_list = lines_list[lineno - 1 :]
    +        lines = "".join(lines_list)
    +        start = lineno
    +        common_indent = _calculate_common_indent(lines_list)
    +
    +        error_line_in_context = 1  # Now lineno is the first line
    +        end_line = end_lineno - start + 1
    +
    +        # Adjust enhanced ranges from absolute line numbers to context-relative
    +        mark_range = Range(
    +            1,
    +            enhanced_mark.lfinal - start + 1,
    +            max(0, enhanced_mark.cbeg - len(common_indent)),
    +            max(0, enhanced_mark.cend - len(common_indent)),
    +        )
    +        # Convert list of em ranges to context-relative
    +        em_ranges = (
    +            [
    +                Range(
    +                    em.lfirst - start + 1,
    +                    em.lfinal - start + 1,
    +                    max(0, em.cbeg - len(common_indent)),
    +                    max(0, em.cend - len(common_indent)),
    +                )
    +                for em in enhanced_em
    +            ]
    +            if enhanced_em
    +            else None
    +        )
    +    else:
    +        # Fallback to Python's positions
    +        # Adjust columns for dedenting
    +        adjusted_start_col = max(0, start_col - len(common_indent))
    +        adjusted_end_col = max(0, end_col - len(common_indent))
    +
    +        # Create mark range
    +        mark_range = None
    +        mark_lfinal = end_line or error_line_in_context
    +        mark_range = Range(
    +            error_line_in_context, mark_lfinal, adjusted_start_col, adjusted_end_col
    +        )
    +
    +        # Build emphasis range
    +        em_ranges = _extract_emphasis_columns(
    +            lines,
    +            error_line_in_context,
    +            end_line,
    +            adjusted_start_col,
    +            adjusted_end_col,
    +            start,
    +        )
    +
    +    fragments = _parse_lines_to_fragments(lines, mark_range, em_ranges)
    +
    +    # Compute cursor position (prefer em end, fall back to mark end)
    +    cursor_line, cursor_col = compute_cursor_position(
    +        mark_range, em_ranges, start, common_indent
    +    )
    +
    +    # Format location info (after enhanced positions may have updated lineno)
    +    fmt_filename, location, urls = format_location(filename, cursor_line, cursor_col)
    +
    +    # Get the code line for display
    +    codeline = lines_list[error_line_in_context - 1].strip() if lines_list else None
    +
    +    return {
    +        "id": f"tb-{token_urlsafe(12)}",
    +        "relevance": "error",
    +        "filename": fmt_filename,
    +        "location": location,
    +        "notebook_cell": notebook_cell,
    +        "codeline": codeline,
    +        "range": Range(lineno, end_lineno or lineno, start_col, end_col)
    +        if start_col is not None
    +        else None,
    +        "cursor_line": cursor_line,
    +        "cursor_col": cursor_col,
    +        "linenostart": start,
    +        "lines": lines,
    +        "fragments": fragments,
    +        "function": None,
    +        "function_suffix": "",
    +        "urls": urls,
    +        "variables": [],
    +    }
    +
    +
    +def extract_frames(tb, raw_tb=None, *, except_block=False, exc=None) -> list:
    +    if not tb:
    +        return []
    +
    +    position_map = _build_position_map(raw_tb)
    +
    +    frames = []
    +    for frame, filename, lineno, function, codeline, _ in tb:
    +        hide = frame.f_globals.get("__tracebackhide__") or frame.f_locals.get(
    +            "__tracebackhide__"
    +        )
    +        if hide:
    +            if hide == "until":
    +                # Hide this frame and all previous frames
    +                frames = []
    +                continue
    +            # Mark frame as hidden but keep it for chain analysis
    +            # (will be filtered out after chronological ordering is built)
    +            hidden = True
    +        else:
    +            hidden = False
    +
    +        # Relevance is set later in extract_exception via _set_frame_relevance
    +        relevance = "call"
    +
    +        # Extract position information first so we can use it for source extraction
    +        pos = position_map.get(frame, [None] * 4)
    +        pos_end_lineno, start_col, end_col = pos[1], pos[2], pos[3]
    +
    +        # Check if this is a notebook cell (to reduce context)
    +        notebook_cell = _is_notebook_cell(filename)
    +
    +        lines, start, original_common_indent = extract_source_lines(
    +            frame,
    +            lineno,
    +            pos_end_lineno,
    +            notebook_cell=notebook_cell,
    +            except_block=except_block,
    +        )
    +        is_last_frame = frame is tb[-1][0]
    +        if not lines and not is_last_frame:
    +            if hidden:
    +                # Still include hidden frames with minimal info for chain analysis
    +                full_source, full_source_start = _get_full_source(frame)
    +                frames.append(
    +                    {
    +                        "id": f"tb-{token_urlsafe(12)}",
    +                        "relevance": relevance,
    +                        "hidden": True,
    +                        "lineno": lineno,
    +                        "full_source": full_source,
    +                        "full_source_start": full_source_start,
    +                    }
    +                )
    +            continue
    +
    +        # Get full source for chain analysis (AST parsing for try-except matching)
    +        # This uses inspect which works with any source Python knows about
    +        full_source, full_source_start = _get_full_source(frame)
    +
    +        # For comprehensions/generators, trim context to just the expression
    +        lines, start = _trim_source_to_comprehension(lines, lineno, start)
    +        # Recalculate common indent after trimming and dedent again if needed
    +        lines_list = lines.splitlines(keepends=True)
    +        extra_indent = _calculate_common_indent(lines_list)
    +        lines = "".join(ln.removeprefix(extra_indent) for ln in lines_list)
    +        # Total indent removed is original + any extra from trimming
    +        total_indent = len(original_common_indent) + len(extra_indent)
    +
    +        # Preserve original filename for chain analysis (needed for AST parsing)
    +        original_filename = filename
    +        function = _get_qualified_function_name(frame, function)
    +
    +        error_line_in_context = lineno - start + 1
    +        end_line = pos_end_lineno - start + 1 if pos_end_lineno else None
    +
    +        # Adjust column positions to account for dedenting
    +        # Python's column numbers are based on the original indented code,
    +        # but we display dedented code, so we need to subtract total indentation removed
    +        adjusted_start_col = start_col - total_indent if start_col is not None else None
    +        adjusted_end_col = end_col - total_indent if end_col is not None else None
    +
    +        # Create mark range (1-based inclusive lines, 0-based exclusive columns)
    +        mark_range = None
    +        if adjusted_start_col is not None and adjusted_end_col is not None:
    +            # Ensure columns are not negative after dedenting adjustment
    +            adjusted_start_col = max(0, adjusted_start_col)
    +            adjusted_end_col = max(0, adjusted_end_col)
    +            mark_lfinal = end_line or error_line_in_context
    +            mark_range = Range(
    +                error_line_in_context, mark_lfinal, adjusted_start_col, adjusted_end_col
    +            )
    +
    +        # Build emphasis range and fragments
    +        em_range = _extract_emphasis_columns(
    +            lines,
    +            error_line_in_context,
    +            end_line,
    +            adjusted_start_col,
    +            adjusted_end_col,
    +            start,
    +        )
    +        fragments = _parse_lines_to_fragments(lines, mark_range, em_range)
    +
    +        # Compute cursor position (prefer em end, fall back to mark end)
    +        # original_common_indent + extra_indent = total common indent removed
    +        cursor_line, cursor_col = compute_cursor_position(
    +            mark_range, em_range, start, original_common_indent + extra_indent
    +        )
    +
    +        # Format location with cursor position for precise navigation
    +        filename, location, urls = format_location(
    +            original_filename, cursor_line, cursor_col
    +        )
    +
    +        # Extract variable source: use marked region + comprehension expansion if inside one
    +        variable_source = _get_variable_source_for_comprehension(
    +            lines, lineno, start, mark_range
    +        )
    +
    +        frames.append(
    +            {
    +                "id": f"tb-{token_urlsafe(12)}",
    +                "relevance": relevance,
    +                "hidden": hidden,  # For chain analysis; filtered out after ordering
    +                "filename": filename,
    +                "original_filename": original_filename,  # For chain analysis AST parsing
    +                "location": location,
    +                "notebook_cell": notebook_cell,
    +                "codeline": codeline[0].strip() if codeline else None,
    +                "range": Range(lineno, pos_end_lineno or lineno, start_col, end_col)
    +                if start_col is not None
    +                else None,
    +                "lineno": lineno,  # Actual error line from traceback (always available)
    +                "cursor_line": cursor_line,
    +                "cursor_col": cursor_col,
    +                "linenostart": start,
    +                "lines": lines,
    +                "fragments": fragments,
    +                "function": function,
    +                "function_suffix": "",
    +                "urls": urls,
    +                "variables": extract_variables(frame.f_locals, variable_source)
    +                if not hidden
    +                else [],
    +                # Full source for chain analysis (try-except matching via AST)
    +                "full_source": full_source,
    +                "full_source_start": full_source_start,
    +            }
    +        )
    +
    +    if exc is not None:
    +        _set_relevances(frames, exc)
    +    return frames
    +
    +
    +def _calculate_common_indent(lines):
    +    """Calculate common indentation across all non-empty lines."""
    +    non_empty_lines = [line.rstrip("\r\n") for line in lines if line.strip()]
    +    if not non_empty_lines:
    +        return ""
    +    indent_len = min(len(ln) - len(ln.lstrip(" \t")) for ln in non_empty_lines)
    +    return non_empty_lines[0][:indent_len]
    +
    +
    +def _convert_range_to_positions(range_obj, lines):
    +    """Convert Range (1-based inclusive lines, 0-based exclusive columns) to absolute character positions."""
    +    positions = set()
    +
    +    if not range_obj:
    +        return positions
    +
    +    # Convert to 0-based line indices for processing
    +    start_line_idx = range_obj.lfirst - 1
    +    end_line_idx = range_obj.lfinal - 1
    +
    +    # Calculate absolute positions
    +    char_pos = 0
    +    for line_idx, line in enumerate(lines):
    +        if start_line_idx <= line_idx <= end_line_idx:
    +            line_content = line.rstrip("\r\n")
    +
    +            if line_idx == start_line_idx == end_line_idx:
    +                # Single line case
    +                for col in range(
    +                    max(0, range_obj.cbeg), min(len(line_content), range_obj.cend)
    +                ):
    +                    positions.add(char_pos + col)
    +            elif line_idx == start_line_idx:
    +                # First line of multi-line
    +                for col in range(max(0, range_obj.cbeg), len(line_content)):
    +                    positions.add(char_pos + col)
    +            elif line_idx == end_line_idx:
    +                # Last line of multi-line
    +                for col in range(0, min(len(line_content), range_obj.cend)):
    +                    positions.add(char_pos + col)
    +            else:
    +                # Middle lines of multi-line
    +                for col in range(len(line_content)):
    +                    positions.add(char_pos + col)
    +
    +        char_pos += len(line)
    +
    +    return positions
    +
    +
    +def _create_unified_fragments(lines_text, common_indent, mark_positions, em_positions):
    +    """Create fragments with unified mark/em highlighting."""
    +    lines = lines_text.splitlines(keepends=True)
    +    result = []
    +
    +    for line_idx, line in enumerate(lines):
    +        line_num = line_idx + 1
    +        fragments = _parse_line_to_fragments_unified(
    +            line,
    +            common_indent,
    +            mark_positions,
    +            em_positions,
    +            sum(len(lines[i]) for i in range(line_idx)),  # char offset for this line
    +        )
    +        result.append({"line": line_num, "fragments": fragments})
    +
    +    return result
    +
    +
    +def _parse_line_to_fragments_unified(
    +    line, common_indent, mark_positions, em_positions, line_char_offset
    +):
    +    """Parse a single line into fragments using unified highlighting."""
    +    line_content, line_ending = _split_line_content(line)
    +    if not line_content and not line_ending:
    +        return []
    +
    +    # Process indentation
    +    fragments, remaining, pos = _process_indentation(line_content, common_indent)
    +
    +    # Find comment split
    +    comment_start = _find_comment_start(remaining)
    +
    +    if comment_start is not None:
    +        # Handle line with comment
    +        code_part = remaining[:comment_start]
    +        comment_part = remaining[comment_start:]
    +
    +        # Process code part (with trimming)
    +        code_trimmed = code_part.rstrip()
    +        code_whitespace = code_part[len(code_trimmed) :]
    +
    +        if code_trimmed:
    +            fragments.extend(
    +                _create_highlighted_fragments_unified(
    +                    code_trimmed, line_char_offset + pos, mark_positions, em_positions
    +                )
    +            )
    +
    +        # Process comment part
    +        comment_trimmed = comment_part.rstrip()
    +        comment_trailing = comment_part[len(comment_trimmed) :]
    +
    +        comment_with_leading_space = code_whitespace + comment_trimmed
    +        fragments.append({"code": comment_with_leading_space, "comment": "solo"})
    +
    +        # Add trailing content
    +        trailing_content = comment_trailing + line_ending
    +        if trailing_content:
    +            fragments.append({"code": trailing_content, "trailing": "solo"})
    +    else:
    +        # Handle line without comment
    +        code_trimmed = remaining.rstrip()
    +        trailing_whitespace = remaining[len(code_trimmed) :]
    +
    +        if code_trimmed:
    +            fragments.extend(
    +                _create_highlighted_fragments_unified(
    +                    code_trimmed, line_char_offset + pos, mark_positions, em_positions
    +                )
    +            )
    +
    +        trailing_content = trailing_whitespace + line_ending
    +        if trailing_content:
    +            fragments.append({"code": trailing_content, "trailing": "solo"})
    +
    +    return fragments
    +
    +
    +def _create_highlighted_fragments_unified(
    +    text, start_pos, mark_positions, em_positions
    +):
    +    """Create fragments with mark/em highlighting using unified position sets."""
    +    if not text:
    +        return []
    +
    +    # Convert absolute positions to text-relative positions
    +    text_mark_positions = set()
    +    text_em_positions = set()
    +
    +    for i in range(len(text)):
    +        abs_pos = start_pos + i
    +        if abs_pos in mark_positions:
    +            text_mark_positions.add(i)
    +        if abs_pos in em_positions:
    +            text_em_positions.add(i)
    +
    +    # Create fragments using existing logic
    +    return _create_fragments_with_highlighting(
    +        text, text_mark_positions, text_em_positions
    +    )
    +
    +
    +def _parse_lines_to_fragments(lines_text, mark_range=None, em_ranges=None):
    +    """
    +    Parse lines of code into fragments with mark/em highlighting information.
    +
    +    Args:
    +        lines_text: The multi-line string containing code
    +        mark_range: Range object for mark highlighting (or None)
    +        em_ranges: Range object or list of Range objects for em highlighting (or None)
    +
    +    Returns:
    +        List of line dictionaries with fragment information
    +    """
    +    lines = lines_text.splitlines(keepends=True)
    +    if not lines:
    +        return []
    +
    +    common_indent = _calculate_common_indent(lines)
    +
    +    # Convert both mark and em to position sets using unified logic
    +    mark_positions = _convert_range_to_positions(mark_range, lines)
    +
    +    # Handle em_ranges as either a single Range or a list of Ranges
    +    em_positions = set()
    +    if em_ranges:
    +        if isinstance(em_ranges, list):
    +            for em_range in em_ranges:
    +                em_positions |= _convert_range_to_positions(em_range, lines)
    +        else:
    +            em_positions = _convert_range_to_positions(em_ranges, lines)
    +
    +    # Create fragments using unified highlighting
    +    return _create_unified_fragments(
    +        lines_text, common_indent, mark_positions, em_positions
    +    )
    +
    +
    +def _split_line_content(line):
    +    """Split line into content and line ending."""
    +    if line.endswith("\r\n"):
    +        return line[:-2], "\r\n"
    +    elif line.endswith("\n"):
    +        return line[:-1], "\n"
    +    elif line.endswith("\r"):
    +        return line[:-1], "\r"
    +    else:
    +        return line, ""
    +
    +
    +def _process_indentation(line_content, common_indent):
    +    """Process dedent and additional indentation, return fragments and remaining content."""
    +    fragments = []
    +    pos = 0
    +
    +    # Handle dedent (common indentation)
    +    if common_indent and len(line_content) > len(common_indent):
    +        dedent_text = line_content[: len(common_indent)]
    +        fragments.append({"code": dedent_text, "dedent": "solo"})
    +        pos = len(common_indent)
    +
    +    # Handle additional indentation
    +    remaining = line_content[pos:]
    +    indent_match = re.match(r"^(\s+)", remaining)
    +    if indent_match:
    +        indent_text = indent_match.group(1)
    +        fragments.append({"code": indent_text, "indent": "solo"})
    +        pos += len(indent_text)
    +        remaining = remaining[len(indent_text) :]
    +
    +    return fragments, remaining, pos
    +
    +
    +def _find_comment_start(text):
    +    """Find the start of a comment, ignoring # inside strings."""
    +    in_string = False
    +    string_char = None
    +    escape_next = False
    +
    +    for i, char in enumerate(text):
    +        if escape_next:
    +            escape_next = False
    +            continue
    +        if char == "\\":
    +            escape_next = True
    +            continue
    +        if not in_string and char == "#":
    +            return i
    +        if not in_string and char in ('"', "'"):
    +            in_string = True
    +            string_char = char
    +        elif in_string and char == string_char:
    +            in_string = False
    +            string_char = None
    +
    +    return None
    +
    +
    +def _positions_to_consecutive_ranges(positions):
    +    """Convert a set/list of positions to consecutive (start, end) ranges."""
    +    if not positions:
    +        return []
    +
    +    sorted_positions = sorted(set(positions))
    +    ranges = []
    +    start = sorted_positions[0]
    +    end = start + 1
    +
    +    for pos in sorted_positions[1:]:
    +        if pos == end:
    +            # Consecutive position, extend current range
    +            end = pos + 1
    +        else:
    +            # Gap found, close current range and start new one
    +            ranges.append((start, end))
    +            start = pos
    +            end = pos + 1
    +
    +    # Close the last range
    +    ranges.append((start, end))
    +    return ranges
    +
    +
    +def _get_highlight_boundaries(text, mark_positions, em_positions):
    +    """Get all boundaries for highlighting (start/end of mark and em regions)."""
    +    boundaries = {0, len(text)}
    +
    +    # Add mark boundaries
    +    for start, end in _positions_to_consecutive_ranges(mark_positions):
    +        boundaries.add(start)
    +        boundaries.add(end)
    +
    +    # Add em boundaries
    +    for start, end in _positions_to_consecutive_ranges(em_positions):
    +        boundaries.add(start)
    +        boundaries.add(end)
    +
    +    return sorted(boundaries)
    +
    +
    +def _create_fragments_with_highlighting(text, mark_positions, em_positions):
    +    """Create fragments with mark/em highlighting using beg/mid/fin/solo logic."""
    +    if not text:
    +        return []
    +
    +    # Get all boundaries and create fragments
    +    boundaries = _get_highlight_boundaries(text, mark_positions, em_positions)
    +    mark_ranges = _positions_to_consecutive_ranges(mark_positions)
    +    em_ranges = _positions_to_consecutive_ranges(em_positions)
    +
    +    fragments = []
    +    for i in range(len(boundaries) - 1):
    +        start = boundaries[i]
    +        end = boundaries[i + 1]
    +
    +        if start >= len(text):
    +            break
    +
    +        fragment_text = text[start:end]
    +        fragment = {"code": fragment_text}
    +
    +        # Determine mark status
    +        mark_status = _get_highlight_status(start, end, mark_ranges)
    +        if mark_status:
    +            fragment["mark"] = mark_status
    +
    +        # Determine em status
    +        em_status = _get_highlight_status(start, end, em_ranges)
    +        if em_status:
    +            fragment["em"] = em_status
    +
    +        fragments.append(fragment)
    +
    +    return fragments
    +
    +
    +def _get_highlight_status(frag_start, frag_end, ranges):
    +    """Determine beg/mid/fin/solo status for a fragment within ranges."""
    +    # Find overlapping ranges
    +    overlapping = []
    +    for range_start, range_end in ranges:
    +        if frag_start < range_end and frag_end > range_start:
    +            overlapping.append((range_start, range_end))
    +
    +    if not overlapping:
    +        return None
    +
    +    # Use the first overlapping range (they should align with fragment boundaries)
    +    range_start, range_end = overlapping[0]
    +
    +    is_start = frag_start <= range_start
    +    is_end = frag_end >= range_end
    +
    +    if is_start and is_end:
    +        return "solo"
    +    elif is_start:
    +        return "beg"
    +    elif is_end:
    +        return "fin"
    +    else:
    +        return "mid"
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/trace_cpy.py b/.venv/lib/python3.12/site-packages/tracerite/trace_cpy.py
    new file mode 100644
    index 0000000..9158760
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/trace_cpy.py
    @@ -0,0 +1,408 @@
    +# Copied from https://github.com/python/cpython/blob/main/Lib/traceback.py
    +# We need to use internal functions that are not part of the public API,
    +# and that are not available in earlier Python versions.
    +
    +# Unused functionality is removed and we run a formatter.
    +# One modification is made for Python 3.9 and 3.10 compatibility (see comment).
    +# ruff: noqa
    +
    +"""Extract, format and print information about Python stack traces."""
    +
    +import collections.abc
    +import itertools
    +import sys
    +
    +
    +class _Sentinel:
    +    def __repr__(self):
    +        return ""
    +
    +
    +_sentinel = _Sentinel()
    +
    +
    +def _parse_value_tb(exc, value, tb):
    +    if (value is _sentinel) != (tb is _sentinel):
    +        raise ValueError("Both or neither of value and tb must be given")
    +    if value is tb is _sentinel:
    +        if exc is not None:
    +            if isinstance(exc, BaseException):
    +                return exc, exc.__traceback__
    +
    +            raise TypeError(f"Exception expected for value, {type(exc).__name__} found")
    +        else:
    +            return None, None
    +    return value, tb
    +
    +
    +BUILTIN_EXCEPTION_LIMIT = object()
    +
    +
    +def _safe_string(value, what, func=str):
    +    try:
    +        return func(value)
    +    except:
    +        return f"<{what} {func.__name__}() failed>"
    +
    +
    +def _walk_tb_with_full_positions(tb):
    +    # Internal version of walk_tb that yields full code positions including
    +    # end line and column information.
    +    while tb is not None:
    +        positions = _get_code_position(tb.tb_frame.f_code, tb.tb_lasti)
    +        # Yield tb_lineno when co_positions does not have a line number to
    +        # maintain behavior with walk_tb.
    +        if positions[0] is None:
    +            yield tb.tb_frame, (tb.tb_lineno,) + positions[1:]
    +        else:
    +            yield tb.tb_frame, positions
    +        tb = tb.tb_next
    +
    +
    +def _get_code_position(code, instruction_index):
    +    if instruction_index < 0:
    +        return (None, None, None, None)
    +    # TRACERITE MODIFICATION: co_positions() was added in Python 3.11
    +    # Fallback for Python 3.9 and 3.10 compatibility
    +    if not hasattr(code, "co_positions"):
    +        return (None, None, None, None)
    +    positions_gen = code.co_positions()
    +    return next(itertools.islice(positions_gen, instruction_index // 2, None))
    +
    +
    +def _byte_offset_to_character_offset(str, offset):
    +    as_utf8 = str.encode("utf-8")
    +    return len(as_utf8[:offset].decode("utf-8", errors="replace"))
    +
    +
    +_Anchors = collections.namedtuple(
    +    "_Anchors",
    +    [
    +        "left_end_lineno",
    +        "left_end_offset",
    +        "right_start_lineno",
    +        "right_start_offset",
    +        "primary_char",
    +        "secondary_char",
    +    ],
    +    defaults=["~", "^"],
    +)
    +
    +
    +def _extract_caret_anchors_from_line_segment(segment):
    +    """
    +    Given source code `segment` corresponding to a FrameSummary, determine:
    +        - for binary ops, the location of the binary op
    +        - for indexing and function calls, the location of the brackets.
    +    `segment` is expected to be a valid Python expression.
    +    """
    +    import ast
    +
    +    try:
    +        # Without parentheses, `segment` is parsed as a statement.
    +        # Binary ops, subscripts, and calls are expressions, so
    +        # we can wrap them with parentheses to parse them as
    +        # (possibly multi-line) expressions.
    +        # e.g. if we try to highlight the addition in
    +        # x = (
    +        #     a +
    +        #     b
    +        # )
    +        # then we would ast.parse
    +        #     a +
    +        #     b
    +        # which is not a valid statement because of the newline.
    +        # Adding brackets makes it a valid expression.
    +        # (
    +        #     a +
    +        #     b
    +        # )
    +        # Line locations will be different than the original,
    +        # which is taken into account later on.
    +        tree = ast.parse(f"(\n{segment}\n)")
    +    except SyntaxError:
    +        return None
    +
    +    if len(tree.body) != 1:
    +        return None
    +
    +    lines = segment.splitlines()
    +
    +    def normalize(lineno, offset):
    +        """Get character index given byte offset"""
    +        return _byte_offset_to_character_offset(lines[lineno], offset)
    +
    +    def next_valid_char(lineno, col):
    +        """Gets the next valid character index in `lines`, if
    +        the current location is not valid. Handles empty lines.
    +        """
    +        while lineno < len(lines) and col >= len(lines[lineno]):
    +            col = 0
    +            lineno += 1
    +        assert lineno < len(lines) and col < len(lines[lineno])
    +        return lineno, col
    +
    +    def increment(lineno, col):
    +        """Get the next valid character index in `lines`."""
    +        col += 1
    +        lineno, col = next_valid_char(lineno, col)
    +        return lineno, col
    +
    +    def nextline(lineno, col):
    +        """Get the next valid character at least on the next line"""
    +        col = 0
    +        lineno += 1
    +        lineno, col = next_valid_char(lineno, col)
    +        return lineno, col
    +
    +    def increment_until(lineno, col, stop):
    +        """Get the next valid non-"\\#" character that satisfies the `stop` predicate"""
    +        while True:
    +            ch = lines[lineno][col]
    +            if ch in "\\#":
    +                lineno, col = nextline(lineno, col)
    +            elif not stop(ch):
    +                lineno, col = increment(lineno, col)
    +            else:
    +                break
    +        return lineno, col
    +
    +    def setup_positions(expr, force_valid=True):
    +        """Get the lineno/col position of the end of `expr`. If `force_valid` is True,
    +        forces the position to be a valid character (e.g. if the position is beyond the
    +        end of the line, move to the next line)
    +        """
    +        # -2 since end_lineno is 1-indexed and because we added an extra
    +        # bracket + newline to `segment` when calling ast.parse
    +        lineno = expr.end_lineno - 2
    +        col = normalize(lineno, expr.end_col_offset)
    +        return next_valid_char(lineno, col) if force_valid else (lineno, col)
    +
    +    statement = tree.body[0]
    +    if isinstance(statement, ast.Expr):
    +        expr = statement.value
    +        if isinstance(expr, ast.BinOp):
    +            # ast gives these locations for BinOp subexpressions
    +            # ( left_expr ) + ( right_expr )
    +            #   left^^^^^       right^^^^^
    +            lineno, col = setup_positions(expr.left)
    +
    +            # First operator character is the first non-space/')' character
    +            lineno, col = increment_until(
    +                lineno, col, lambda x: not x.isspace() and x != ")"
    +            )
    +
    +            # binary op is 1 or 2 characters long, on the same line,
    +            # before the right subexpression
    +            right_col = col + 1
    +            if (
    +                right_col < len(lines[lineno])
    +                and (
    +                    # operator char should not be in the right subexpression
    +                    expr.right.lineno - 2 > lineno
    +                    or right_col
    +                    < normalize(expr.right.lineno - 2, expr.right.col_offset)
    +                )
    +                and not (ch := lines[lineno][right_col]).isspace()
    +                and ch not in "\\#"
    +            ):
    +                right_col += 1
    +
    +            # right_col can be invalid since it is exclusive
    +            return _Anchors(lineno, col, lineno, right_col)
    +        if isinstance(expr, ast.Subscript):
    +            # ast gives these locations for value and slice subexpressions
    +            # ( value_expr ) [ slice_expr ]
    +            #   value^^^^^     slice^^^^^
    +            # subscript^^^^^^^^^^^^^^^^^^^^
    +
    +            # find left bracket
    +            left_lineno, left_col = setup_positions(expr.value)
    +            left_lineno, left_col = increment_until(
    +                left_lineno, left_col, lambda x: x == "["
    +            )
    +            # find right bracket (final character of expression)
    +            right_lineno, right_col = setup_positions(expr, force_valid=False)
    +            return _Anchors(left_lineno, left_col, right_lineno, right_col)
    +        if isinstance(expr, ast.Call):
    +            # ast gives these locations for function call expressions
    +            # ( func_expr ) (args, kwargs)
    +            #   func^^^^^
    +            # call^^^^^^^^^^^^^^^^^^^^^^^^
    +
    +            # find left bracket
    +            left_lineno, left_col = setup_positions(expr.func)
    +            left_lineno, left_col = increment_until(
    +                left_lineno, left_col, lambda x: x == "("
    +            )
    +            # find right bracket (final character of expression)
    +            right_lineno, right_col = setup_positions(expr, force_valid=False)
    +            return _Anchors(left_lineno, left_col, right_lineno, right_col)
    +
    +    return None
    +
    +
    +_MAX_CANDIDATE_ITEMS = 750
    +_MAX_STRING_SIZE = 40
    +_MOVE_COST = 2
    +_CASE_COST = 1
    +
    +
    +def _substitution_cost(ch_a, ch_b):
    +    if ch_a == ch_b:
    +        return 0
    +    if ch_a.lower() == ch_b.lower():
    +        return _CASE_COST
    +    return _MOVE_COST
    +
    +
    +def _compute_suggestion_error(exc_value, tb, wrong_name):
    +    if wrong_name is None or not isinstance(wrong_name, str):
    +        return None
    +    if isinstance(exc_value, AttributeError):
    +        obj = exc_value.obj
    +        try:
    +            try:
    +                d = dir(obj)
    +            except TypeError:  # Attributes are unsortable, e.g. int and str
    +                d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
    +            d = sorted([x for x in d if isinstance(x, str)])
    +            hide_underscored = wrong_name[:1] != "_"
    +            if hide_underscored and tb is not None:
    +                while tb.tb_next is not None:
    +                    tb = tb.tb_next
    +                frame = tb.tb_frame
    +                if "self" in frame.f_locals and frame.f_locals["self"] is obj:
    +                    hide_underscored = False
    +            if hide_underscored:
    +                d = [x for x in d if x[:1] != "_"]
    +        except Exception:
    +            return None
    +    elif isinstance(exc_value, ImportError):
    +        try:
    +            mod = __import__(exc_value.name)
    +            try:
    +                d = dir(mod)
    +            except TypeError:  # Attributes are unsortable, e.g. int and str
    +                d = list(mod.__dict__.keys())
    +            d = sorted([x for x in d if isinstance(x, str)])
    +            if wrong_name[:1] != "_":
    +                d = [x for x in d if x[:1] != "_"]
    +        except Exception:
    +            return None
    +    else:
    +        assert isinstance(exc_value, NameError)
    +        # find most recent frame
    +        if tb is None:
    +            return None
    +        while tb.tb_next is not None:
    +            tb = tb.tb_next
    +        frame = tb.tb_frame
    +        d = list(frame.f_locals) + list(frame.f_globals) + list(frame.f_builtins)
    +        d = [x for x in d if isinstance(x, str)]
    +
    +        # Check first if we are in a method and the instance
    +        # has the wrong name as attribute
    +        if "self" in frame.f_locals:
    +            self = frame.f_locals["self"]
    +            try:
    +                has_wrong_name = hasattr(self, wrong_name)
    +            except Exception:
    +                has_wrong_name = False
    +            if has_wrong_name:
    +                return f"self.{wrong_name}"
    +
    +    try:
    +        import _suggestions  # type: ignore[import]
    +    except ImportError:
    +        pass
    +    else:
    +        return _suggestions._generate_suggestions(d, wrong_name)
    +
    +    # Compute closest match
    +
    +    if len(d) > _MAX_CANDIDATE_ITEMS:
    +        return None
    +    wrong_name_len = len(wrong_name)
    +    if wrong_name_len > _MAX_STRING_SIZE:
    +        return None
    +    best_distance = wrong_name_len
    +    suggestion = None
    +    for possible_name in d:
    +        if possible_name == wrong_name:
    +            # A missing attribute is "found". Don't suggest it (see GH-88821).
    +            continue
    +        # No more than 1/3 of the involved characters should need changed.
    +        max_distance = (len(possible_name) + wrong_name_len + 3) * _MOVE_COST // 6
    +        # Don't take matches we've already beaten.
    +        max_distance = min(max_distance, best_distance - 1)
    +        current_distance = _levenshtein_distance(
    +            wrong_name, possible_name, max_distance
    +        )
    +        if current_distance > max_distance:
    +            continue
    +        if not suggestion or current_distance < best_distance:
    +            suggestion = possible_name
    +            best_distance = current_distance
    +    return suggestion
    +
    +
    +def _levenshtein_distance(a, b, max_cost):
    +    # A Python implementation of Python/suggestions.c:levenshtein_distance.
    +
    +    # Both strings are the same
    +    if a == b:
    +        return 0
    +
    +    # Trim away common affixes
    +    pre = 0
    +    while a[pre:] and b[pre:] and a[pre] == b[pre]:
    +        pre += 1
    +    a = a[pre:]
    +    b = b[pre:]
    +    post = 0
    +    while a[: post or None] and b[: post or None] and a[post - 1] == b[post - 1]:
    +        post -= 1
    +    a = a[: post or None]
    +    b = b[: post or None]
    +    if not a or not b:
    +        return _MOVE_COST * (len(a) + len(b))
    +    if len(a) > _MAX_STRING_SIZE or len(b) > _MAX_STRING_SIZE:
    +        return max_cost + 1
    +
    +    # Prefer shorter buffer
    +    if len(b) < len(a):
    +        a, b = b, a
    +
    +    # Quick fail when a match is impossible
    +    if (len(b) - len(a)) * _MOVE_COST > max_cost:
    +        return max_cost + 1
    +
    +    # Instead of producing the whole traditional len(a)-by-len(b)
    +    # matrix, we can update just one row in place.
    +    # Initialize the buffer row
    +    row = list(range(_MOVE_COST, _MOVE_COST * (len(a) + 1), _MOVE_COST))
    +
    +    result = 0
    +    for bindex in range(len(b)):
    +        bchar = b[bindex]
    +        distance = result = bindex * _MOVE_COST
    +        minimum = sys.maxsize
    +        for index in range(len(a)):
    +            # 1) Previous distance in this row is cost(b[:b_index], a[:index])
    +            substitute = distance + _substitution_cost(bchar, a[index])
    +            # 2) cost(b[:b_index], a[:index+1]) from previous row
    +            distance = row[index]
    +            # 3) existing result is cost(b[:b_index+1], a[index])
    +
    +            insert_delete = min(result, distance) + _MOVE_COST
    +            result = min(insert_delete, substitute)
    +
    +            # cost(b[:b_index+1], a[:index+1])
    +            row[index] = result
    +            if result < minimum:
    +                minimum = result
    +        if minimum > max_cost:
    +            # Everything in this row is too big, so bail early.
    +            return max_cost + 1
    +    return result
    diff --git a/.venv/lib/python3.12/site-packages/tracerite/tty.py b/.venv/lib/python3.12/site-packages/tracerite/tty.py
    new file mode 100644
    index 0000000..584c234
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/tracerite/tty.py
    @@ -0,0 +1,1380 @@
    +from __future__ import annotations
    +
    +import logging
    +import os
    +import re
    +import sys
    +import textwrap
    +import threading
    +import unicodedata
    +from pathlib import Path
    +from typing import Any, TextIO
    +
    +from .chain_analysis import build_chronological_frames
    +from .trace import build_chain_header, chainmsg, extract_chain, symbols, symdesc
    +
    +# ANSI escape codes for terminal colors (can be monkeypatched for styling)
    +ESC = "\x1b["
    +RESET = f"{ESC}0m"
    +DIM = f"{ESC}2m"
    +LINE_PREFIX_TOP = f"{DIM}╭{RESET} "  # Dim rounded top-left corner for first line
    +LINE_PREFIX = f"{DIM}│{RESET} "  # Dim vertical line prefix for middle lines
    +LINE_PREFIX_BOT = f"{DIM}╰{RESET} "  # Dim rounded bottom-left corner for last line
    +EOL = f"\n{LINE_PREFIX}"  # End of line: newline, add prefix
    +MARK_BG = f"{ESC}103m"  # Bright yellow background
    +MARK_TEXT = f"{ESC}30m"
    +EM = f"{ESC}31m"
    +LOCFN = f"{ESC}32m"
    +EM_CALL = f"{ESC}93m"  # Bright yellow
    +EXC = f"{ESC}90m"  # Dark grey for exception text
    +ELLIPSIS = f"{ESC}90m"  # Dark grey for ellipsis/skipped calls
    +LOC_LINENO = f"{ESC}90m"  # Dark grey for :lineno
    +TYPE_COLOR = f"{ESC}32m"  # Green for type in inspector (matches HTML --tracerite-type)
    +NO_SOURCE = f"{ESC}2m"  # Dim for 'source code not available' message
    +SYMBOLDESC = f"{ESC}1m"  # Bright white for symbol desc (e.g. Call from your code)
    +FUNC = f"{ESC}38;5;153m"  # Light blue (xterm256 LightSkyBlue1) for function names
    +VAR = f"{ESC}38;5;153m"  # Light blue (xterm256 LightSkyBlue1) for variable names (same as FUNC)
    +BOLD = f"{ESC}1m"
    +
    +# Box drawing characters
    +BOX_H = "─"
    +BOX_V = "│"
    +BOX_VL = "┤"  # Vertical with left branch
    +BOX_TL = "╭"  # Rounded top-left
    +BOX_BL = "╰"  # Rounded bottom-left
    +BOX_TR = "╮"  # Rounded top-right
    +BOX_BR = "╯"  # Rounded bottom-right
    +ARROW_LEFT = "◀"
    +SINGLE_T = "❴"  # T-junction for single line
    +
    +INDENT = ""  # No indent for function/location lines
    +CODE_INDENT = "  "  # Indent for code in frame
    +
    +# Regex pattern to strip ANSI escape sequences
    +ANSI_ESCAPE_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
    +
    +
    +def _display_width(s: str) -> int:
    +    """Calculate the display width of a string in terminal columns."""
    +    plain = ANSI_ESCAPE_RE.sub("", s)
    +    return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in plain)
    +
    +
    +# Store the original hooks for unload
    +_original_excepthook = None
    +_original_threading_excepthook = None
    +_original_stream_handler_emit = None
    +
    +
    +def load(capture_logging: bool = True) -> None:
    +    """Load TraceRite as the default exception handler.
    +
    +    Replaces sys.excepthook to use TraceRite's pretty TTY formatting
    +    for all unhandled exceptions, including those in threads and
    +    logging.exception() calls.
    +    Call unload() to restore the original exception handlers.
    +
    +    Args:
    +        capture_logging: Whether to monkeypatch logging.StreamHandler.emit
    +            to format exceptions in logging.exception() calls. Defaults to True.
    +
    +    Usage:
    +        import tracerite
    +        tracerite.load()  # Captures logging by default
    +        tracerite.load(capture_logging=False)  # Only captures sys.excepthook
    +    """
    +    global \
    +        _original_excepthook, \
    +        _original_threading_excepthook, \
    +        _original_stream_handler_emit
    +
    +    if _original_excepthook is None:
    +        _original_excepthook = sys.excepthook
    +
    +    if _original_threading_excepthook is None:
    +        _original_threading_excepthook = threading.excepthook
    +
    +    if capture_logging and _original_stream_handler_emit is None:
    +        _original_stream_handler_emit = logging.StreamHandler.emit
    +
    +    def _tracerite_excepthook(exc_type, exc_value, exc_tb):
    +        try:
    +            tty_traceback(exc=exc_value)
    +        except Exception:
    +            # Fall back to original excepthook on any error
    +            if _original_excepthook:
    +                _original_excepthook(exc_type, exc_value, exc_tb)
    +            else:
    +                sys.__excepthook__(exc_type, exc_value, exc_tb)
    +
    +    def _tracerite_threading_excepthook(args):  # pragma: no cover (pytest intercepts)
    +        try:
    +            tty_traceback(exc=args.exc_value)
    +        except Exception:
    +            # Fall back to original threading excepthook on any error
    +            if _original_threading_excepthook:
    +                _original_threading_excepthook(args)
    +            else:
    +                sys.__excepthook__(args.exc_type, args.exc_value, args.exc_traceback)
    +
    +    def _tracerite_stream_handler_emit(self, record: logging.LogRecord) -> None:
    +        """Emit a log record with TraceRite formatting for exceptions."""
    +        try:
    +            # Check if we have exception info to format specially
    +            if not record.exc_info or record.exc_info[1] is None:
    +                # No exception, use original emit
    +                return _original_stream_handler_emit(self, record)
    +            # Temporarily clear exc_info so format() doesn't include traceback
    +            exc_info = record.exc_info
    +            record.exc_info = None
    +            record.exc_text = None
    +            try:
    +                msg = self.format(record)
    +            finally:
    +                record.exc_info = exc_info
    +
    +            # Temporarily restore original handler to avoid recursion
    +            original_emit = logging.StreamHandler.emit
    +            logging.StreamHandler.emit = _original_stream_handler_emit
    +            try:
    +                # Now format and write the exception using TraceRite
    +                tty_traceback(exc=exc_info[1], file=self.stream, msg=msg)
    +            finally:
    +                logging.StreamHandler.emit = original_emit
    +        except RecursionError:
    +            raise
    +        except Exception:
    +            self.handleError(record)
    +
    +    sys.excepthook = _tracerite_excepthook
    +    threading.excepthook = _tracerite_threading_excepthook
    +    if capture_logging:
    +        logging.StreamHandler.emit = _tracerite_stream_handler_emit  # type: ignore[attr-defined]
    +
    +
    +def unload() -> None:
    +    """Restore the original exception handlers.
    +
    +    Removes TraceRite from sys.excepthook, threading.excepthook, and
    +    logging.StreamHandler.emit, restoring the previous handlers.
    +    """
    +    global \
    +        _original_excepthook, \
    +        _original_threading_excepthook, \
    +        _original_stream_handler_emit
    +
    +    if _original_excepthook is not None:
    +        sys.excepthook = _original_excepthook
    +        _original_excepthook = None
    +
    +    if _original_threading_excepthook is not None:
    +        threading.excepthook = _original_threading_excepthook
    +        _original_threading_excepthook = None
    +
    +    if _original_stream_handler_emit is not None:
    +        logging.StreamHandler.emit = _original_stream_handler_emit
    +        _original_stream_handler_emit = None
    +
    +
    +def tty_traceback(
    +    exc: BaseException | None = None,
    +    chain: list[dict[str, Any]] | None = None,
    +    *,
    +    file: TextIO | None = None,
    +    msg: str | None = None,
    +    tag: str = "",
    +    term_width: int | None = None,
    +    **extract_args: Any,
    +) -> None:
    +    """Format and print a traceback for terminal output (TTY).
    +
    +    Outputs directly to the terminal (or specified file) to adapt to
    +    terminal features like window size. The chain is printed with the
    +    oldest exception first (order they occurred).
    +
    +    Args:
    +        exc: The exception to format. If None, uses the current exception.
    +        chain: Pre-extracted exception chain. If provided, exc is ignored.
    +        file: Output file. Defaults to sys.stderr.
    +        msg: Header message. If None, builds from exception chain.
    +        tag: Optional tag to display after the message (e.g., "#TR1").
    +        term_width: Terminal width. Auto-detected if None.
    +        **extract_args: Additional arguments passed to extract_chain().
    +    """
    +    chain = chain or extract_chain(exc=exc, **extract_args)
    +
    +    # Build header message if not provided
    +    if msg is None and chain:
    +        msg = build_chain_header(chain)
    +
    +    if file is None:
    +        file = sys.stderr
    +
    +    is_tty = file.isatty() if hasattr(file, "isatty") else False
    +    no_color = not is_tty
    +    no_inspector = not is_tty
    +
    +    # Start with rounded top corner
    +    output = LINE_PREFIX_TOP
    +
    +    # Print the original log message if provided, with optional tag at the end
    +    if msg:
    +        # Strip trailing newlines and left-trim two spaces if present (to align with prefix)
    +        msg = msg.rstrip("\n")
    +        if msg.startswith("  "):
    +            msg = msg[2:]
    +
    +        # Append tag (dim color) if provided
    +        if tag:
    +            msg = f"{msg} {DIM}{tag}{RESET}"
    +
    +        output += msg + EOL
    +
    +    if term_width is None:
    +        try:
    +            term_width = os.get_terminal_size(file.fileno()).columns
    +        except (OSError, ValueError):
    +            term_width = 80
    +
    +    output += _print_chronological(chain, term_width, no_inspector)
    +
    +    # Strip trailing EOL (which ends with LINE_PREFIX for an empty line we don't want)
    +    eol_suffix = f"\n{LINE_PREFIX}"
    +    if output.endswith(eol_suffix):
    +        output = output[: -len(eol_suffix)]
    +
    +    # Replace the last line prefix with bottom corner and reset to original terminal colors
    +    last_prefix_pos = output.rfind(LINE_PREFIX)
    +    if last_prefix_pos != -1:
    +        output = (
    +            output[:last_prefix_pos]
    +            + LINE_PREFIX_BOT
    +            + output[last_prefix_pos + len(LINE_PREFIX) :]
    +        )
    +    output += "\n" + RESET
    +
    +    if no_color:
    +        # Strip all ANSI escape sequences for non-TTY output
    +        output = ANSI_ESCAPE_RE.sub("", output)
    +
    +    file.write(output)
    +
    +
    +def _find_all_inspector_frames(
    +    frame_info_list: list[dict[str, Any]],
    +) -> list[int]:
    +    """Find all non-call frames that have variables to show.
    +
    +    Returns list of frame indices with variables.
    +    """
    +    result = []
    +    for i, info in enumerate(frame_info_list):
    +        if info["relevance"] != "call" and info["frinfo"].get("variables"):
    +            result.append(i)
    +    return result
    +
    +
    +def _find_frame_line_range(
    +    output_lines: list[tuple[str, int, int, bool]], inspector_frame_idx: int
    +) -> tuple[int, int]:
    +    """Find the line range for the inspector frame in output_lines.
    +
    +    Returns (frame_line_start, frame_line_end) - both are valid indices.
    +    The caller guarantees inspector_frame_idx exists in output_lines.
    +    """
    +    frame_line_start = -1
    +    frame_line_end = -1
    +
    +    for li, (_, _, fidx, _) in enumerate(output_lines):
    +        if fidx == inspector_frame_idx:
    +            if frame_line_start == -1:
    +                frame_line_start = li
    +            frame_line_end = li
    +
    +    # By contract, the frame must exist in output_lines
    +    assert frame_line_start >= 0 and frame_line_end >= 0
    +    return frame_line_start, frame_line_end
    +
    +
    +def _find_last_marked_line(
    +    output_lines: list[tuple[str, int, int, bool]],
    +    frame_line_start: int,
    +    frame_line_end: int,
    +) -> int:
    +    """Find the last marked line within the frame range.
    +
    +    Returns the line index of the last marked line, or frame_line_end if none are marked.
    +    """
    +    last_marked = frame_line_end  # Fallback to last line of frame
    +
    +    for li in range(frame_line_start, frame_line_end + 1):
    +        _, _, _, is_marked = output_lines[li]
    +        if is_marked:
    +            last_marked = li
    +
    +    return last_marked
    +
    +
    +def _find_collapsible_call_runs(
    +    frame_info_list: list[dict[str, Any]], min_run_length: int = 10
    +) -> list[tuple[int, int]]:
    +    """Find consecutive runs of 'call' frames that should be collapsed.
    +
    +    Returns list of (start_idx, end_idx) tuples for runs of consecutive
    +    call frames with length >= min_run_length. end_idx is inclusive.
    +    """
    +    runs = []
    +    run_start = None
    +
    +    for i, info in enumerate(frame_info_list):
    +        if info["relevance"] == "call":
    +            if run_start is None:
    +                run_start = i
    +        else:
    +            # End of a call run
    +            if run_start is not None:
    +                run_length = i - run_start
    +                if run_length >= min_run_length:
    +                    runs.append((run_start, i - 1))
    +                run_start = None
    +
    +    return runs
    +
    +
    +def _print_chronological(
    +    chain: list[dict[str, Any]],
    +    term_width: int,
    +    no_inspector: bool = False,
    +) -> str:
    +    """Print frames in chronological order with exception info after error frames."""
    +    output = ""
    +    chrono_frames = build_chronological_frames(chain)
    +    if not chrono_frames:
    +        # No frames, but still show exception banners for any exceptions in chain
    +        for exc in chain:
    +            exc_info = {
    +                "type": exc.get("type"),
    +                "message": exc.get("message"),
    +                "summary": exc.get("summary"),
    +                "from": exc.get("from"),
    +            }
    +            output += _build_exception_banner(exc_info, term_width)
    +        return output
    +
    +    # Build frame info list for all chronological frames
    +    frame_info_list = []
    +    for frinfo in chrono_frames:
    +        info = _get_chrono_frame_info(frinfo)
    +        frame_info_list.append(info)
    +
    +    # Find collapsible call runs
    +    collapse_ranges = _find_collapsible_call_runs(frame_info_list, min_run_length=10)
    +
    +    # Build set of frame indices to skip
    +    skip_indices = set()
    +    ellipsis_after = {}
    +    for start_idx, end_idx in collapse_ranges:
    +        skipped_count = end_idx - start_idx - 1
    +        for i in range(start_idx + 1, end_idx):
    +            skip_indices.add(i)
    +        ellipsis_after[start_idx] = skipped_count
    +
    +    # Calculate max location and function widths for alignment
    +    location_widths = [
    +        _display_width(info["location_part"])
    +        for i, info in enumerate(frame_info_list)
    +        if i not in skip_indices
    +    ]
    +    function_widths = [
    +        _display_width(info["function_part"])
    +        for i, info in enumerate(frame_info_list)
    +        if i not in skip_indices
    +    ]
    +    location_width = max(location_widths, default=0)
    +    function_width = max(function_widths, default=0)
    +
    +    # Build output lines
    +    output_lines = []
    +    exception_banners = []  # List of (insert_after_line_idx, banner_output)
    +
    +    for i, info in enumerate(frame_info_list):
    +        if i in skip_indices:
    +            continue
    +
    +        lines = _build_chrono_frame_lines(
    +            info, location_width, function_width, term_width
    +        )
    +        len(output_lines)
    +        for line, plain_len, is_marked in lines:
    +            output_lines.append((line, plain_len, i, is_marked))
    +
    +        # Add ellipsis after first frame of collapsed run
    +        if i in ellipsis_after:
    +            skipped = ellipsis_after[i]
    +            ellipsis_line = f"{INDENT}{ELLIPSIS}⋮ {skipped} more calls{RESET}"
    +            ellipsis_plain_len = len(INDENT) + 2 + len(f"{skipped} more calls")
    +            output_lines.append((ellipsis_line, ellipsis_plain_len, i, False))
    +
    +        # Check if this frame has parallel branches (subexceptions) to print
    +        parallel_branches = info["frinfo"].get("parallel")
    +        if parallel_branches:
    +            # Build subexception summaries
    +            sub_output = _build_subexception_summaries(parallel_branches, term_width)
    +            exception_banners.append((len(output_lines), sub_output))
    +
    +        # Check if this frame has exception info to print after it
    +        exc_info = info["frinfo"].get("exception")
    +        info["relevance"]
    +        if exc_info:
    +            # Record that we need to insert exception banner after this frame's lines
    +            banner = _build_exception_banner(exc_info, term_width)
    +            exception_banners.append((len(output_lines), banner))
    +
    +    # Get variable inspector lines for all frames with variables
    +    all_inspector_lines = []
    +    all_inspector_min_widths = []
    +    inspector_frame_indices = []
    +    if not no_inspector:
    +        inspector_frame_indices = _find_all_inspector_frames(frame_info_list)
    +        for frame_idx in inspector_frame_indices:
    +            frinfo = frame_info_list[frame_idx]["frinfo"]
    +            variables = frinfo.get("variables", [])
    +            insp_lines, min_width = _build_variable_inspector(variables, term_width)
    +            all_inspector_lines.append(insp_lines)
    +            all_inspector_min_widths.append(min_width)
    +
    +    # Build final output, inserting exception banners at the right positions
    +    if all_inspector_lines:
    +        # Complex case: merge inspectors and banners
    +        output += _merge_chrono_output(
    +            output_lines,
    +            all_inspector_lines,
    +            all_inspector_min_widths,
    +            term_width,
    +            inspector_frame_indices,
    +            exception_banners,
    +            frame_info_list,
    +        )
    +    else:
    +        # Simpler case: just insert banners
    +        banner_idx = 0
    +        for li, (line, _, _, _) in enumerate(output_lines):
    +            output += f"{line}{EOL}"
    +            # Check if we need to insert a banner after this line
    +            while banner_idx < len(exception_banners):
    +                insert_pos, banner = exception_banners[banner_idx]
    +                if li + 1 == insert_pos:
    +                    output += banner
    +                    banner_idx += 1
    +                else:
    +                    break
    +        # Any remaining banners (when banner position > last output line)
    +        for _, banner in exception_banners[banner_idx:]:  # pragma: no cover
    +            output += banner
    +
    +    return output
    +
    +
    +def _build_exception_banner(exc_info: dict[str, Any], term_width: int) -> str:
    +    """Build exception banner output to show after error frame."""
    +    output = ""
    +    exc_type = exc_info.get("type", "Exception")
    +    summary = exc_info.get("summary", "")
    +    message = exc_info.get("message", "")
    +    from_type = exc_info.get("from", "none")
    +
    +    chain_suffix = chainmsg.get(from_type, "")
    +    type_prefix = f"{exc_type}{chain_suffix}: "
    +    type_prefix_len = len(type_prefix)
    +    cont_prefix = f"{EXC}{BOX_V}{RESET} "
    +    cont_prefix_len = 2
    +
    +    # Check if the full title fits on one line
    +    full_title_len = type_prefix_len + len(summary)
    +    if full_title_len <= term_width:
    +        output += f"{EXC}{type_prefix}{RESET}{BOLD}{summary}{RESET}{EOL}"
    +    elif len(summary) <= term_width - cont_prefix_len:
    +        output += f"{EXC}{type_prefix}{RESET}{EOL}"
    +        output += f"{cont_prefix}{BOLD}{summary}{RESET}{EOL}"
    +    else:
    +        padding = "\x00" * (type_prefix_len - cont_prefix_len)
    +        wrapped = textwrap.wrap(
    +            padding + summary,
    +            width=term_width - cont_prefix_len,
    +            break_long_words=False,
    +            break_on_hyphens=False,
    +        )
    +        wrapped[0] = wrapped[0].lstrip("\x00")
    +        for i, line in enumerate(wrapped):
    +            if i == 0:
    +                output += f"{EXC}{type_prefix}{RESET}{BOLD}{line}{RESET}{EOL}"
    +            else:
    +                output += f"{cont_prefix}{BOLD}{line}{RESET}{EOL}"
    +
    +    if summary != message:
    +        if message.startswith(summary):  # pragma: no cover
    +            message = message[len(summary) :].strip("\n")
    +        wrap_width = term_width - cont_prefix_len
    +        for line in message.split("\n"):
    +            if line:
    +                wrapped = textwrap.wrap(
    +                    line,
    +                    width=wrap_width,
    +                    break_long_words=False,
    +                    break_on_hyphens=False,
    +                ) or [line]
    +                for wrapped_line in wrapped:
    +                    output += f"{cont_prefix}{wrapped_line}{EOL}"
    +            else:
    +                output += f"{cont_prefix.rstrip()}{EOL}"
    +
    +    return output
    +
    +
    +def _build_subexception_summaries(
    +    parallel_branches: list[list[dict[str, Any]]], term_width: int
    +) -> str:
    +    """Build one-line summaries for each subexception branch.
    +
    +    For TTY output, we don't have space for full tracebacks, so we show
    +    a compact summary: location, function, exception type and message.
    +    """
    +    output = ""
    +    prefix = f"{EXC}{BOX_V}{RESET} "
    +    prefix_len = 2  # "│ "
    +
    +    for branch in parallel_branches:
    +        # Get the summary for this branch
    +        summary = _get_branch_summary(branch, term_width - prefix_len)
    +        branch_line = f"{prefix}{summary}"
    +        output += f"{branch_line}{EOL}"
    +
    +    return output
    +
    +
    +def _get_branch_summary(branch: list[dict[str, Any]], max_width: int) -> str:
    +    """Get a one-line summary for a subexception branch.
    +
    +    Returns something like "file.py:10 func: ValueError: message"
    +    Truncates at the end if needed to fit max_width.
    +    """
    +    if not branch:
    +        return f"{EXC}(empty){RESET}"
    +
    +    # Find the last frame with an exception (the final error in this branch)
    +    last_exc_info = None
    +    last_frame = None
    +    last_frame_with_parallel = None
    +    for frame in branch:
    +        if frame.get("exception"):
    +            last_exc_info = frame["exception"]
    +            last_frame = frame
    +        if frame.get("parallel"):
    +            last_frame_with_parallel = frame
    +
    +    # If there are nested parallel branches, show them recursively
    +    if last_frame_with_parallel and last_frame_with_parallel.get("parallel"):
    +        nested = last_frame_with_parallel["parallel"]
    +        nested_summaries = []
    +        for nested_branch in nested:
    +            nested_summaries.append(_get_branch_summary(nested_branch, max_width - 4))
    +        return (
    +            f"{EXC}[{RESET}"
    +            + f"{EXC}, {RESET}".join(nested_summaries)
    +            + f"{EXC}]{RESET}"
    +        )
    +
    +    if not last_exc_info:
    +        return f"{EXC}(no exception){RESET}"
    +
    +    # Build location:lineno function prefix
    +    loc_prefix = ""
    +    if last_frame:  # pragma: no cover
    +        location = last_frame["location"]
    +        lineno = last_frame["cursor_line"]
    +        function = last_frame["function"]
    +        notebook_cell = last_frame["notebook_cell"]
    +
    +        # Notebook cells (In [N]) don't need line numbers displayed
    +        if location and lineno and not notebook_cell:
    +            loc_prefix = f"{LOCFN}{location}{LOC_LINENO}:{lineno}{RESET} "
    +            if function:
    +                loc_prefix += f"{FUNC}{function}{RESET}: "
    +        elif location and notebook_cell:
    +            loc_prefix = f"{LOCFN}{location}{RESET} "
    +            if function:
    +                loc_prefix += f"{FUNC}{function}{RESET}: "
    +        elif function:
    +            loc_prefix = f"{FUNC}{function}{RESET}: "
    +
    +    exc_type = last_exc_info.get("type", "Exception")
    +    summary = last_exc_info.get("summary", "")
    +
    +    # Calculate plain text length (without ANSI codes)
    +    loc_plain = ANSI_ESCAPE_RE.sub("", loc_prefix)
    +    exc_part = f"{exc_type}: {summary}"
    +    total_plain_len = len(loc_plain) + len(exc_part)
    +
    +    # Truncate summary if too long
    +    if total_plain_len > max_width:
    +        available = max_width - len(loc_plain) - len(exc_type) - 3  # ": " + "…"
    +        summary = summary[:available] + "…" if available > 0 else "…"
    +
    +    return f"{loc_prefix}{EXC}{exc_type}:{RESET} {BOLD}{summary}{RESET}"
    +
    +
    +def _get_frame_label(frinfo: dict[str, Any]) -> tuple[str, str]:
    +    """Get the label for a frame (path:lineno function)."""
    +    cursor_line = frinfo["cursor_line"]
    +    notebook_cell = frinfo["notebook_cell"]
    +
    +    # Use relative path if file is within CWD, otherwise use prettified location
    +    filename = frinfo["filename"]  # Full path (may be None for notebook cells)
    +    location = frinfo["location"]  # Display path (always set)
    +    if filename:
    +        try:
    +            fn = Path(filename)
    +            cwd = Path.cwd()
    +            if fn.is_absolute() and cwd in fn.parents:
    +                location = fn.relative_to(cwd).as_posix()
    +        except (ValueError, OSError):
    +            pass
    +
    +    # Build label with colors: green filename, dark grey :lineno, light blue function
    +    # Location comes first, then function (if present)
    +    # Colon goes after function if present, otherwise after location
    +    function_name = frinfo["function"]
    +    function_suffix = frinfo["function_suffix"]
    +    if function_name:
    +        function_display = f"{function_name}{function_suffix}"
    +    elif function_suffix:
    +        function_display = function_suffix
    +    else:
    +        function_display = None
    +
    +    # Build the location text with colors
    +    if notebook_cell:
    +        location_text = location
    +        location_suffix = "" if function_display else ":"
    +        location_part = f"{LOCFN}{location_text}{location_suffix}{RESET}"
    +    else:
    +        location_text = f"{location}{LOC_LINENO}:{cursor_line}{RESET}"
    +        location_suffix = "" if function_display else ":"
    +        location_part = f"{LOCFN}{location_text}{location_suffix}{RESET}"
    +
    +    function_part = f"{FUNC}{function_display}{RESET}:" if function_display else ""
    +
    +    return location_part, function_part
    +
    +
    +def _get_chrono_frame_info(frinfo: dict[str, Any]) -> dict[str, Any]:
    +    """Gather info needed to print a chronological frame."""
    +    location_part, function_part = _get_frame_label(frinfo)
    +    fragments = frinfo["fragments"]
    +    frame_range = frinfo["range"]
    +    relevance = frinfo["relevance"]
    +    exc_info = frinfo.get("exception")
    +
    +    # Get marked lines
    +    marked_lines = [
    +        li for li in fragments if any(f.get("mark") for f in li.get("fragments", []))
    +    ]
    +
    +    return {
    +        "location_part": location_part,
    +        "function_part": function_part,
    +        "fragments": fragments,
    +        "frame_range": frame_range,
    +        "relevance": relevance,
    +        "exc_info": exc_info,
    +        "marked_lines": marked_lines,
    +        "frinfo": frinfo,
    +    }
    +
    +
    +def _build_chrono_frame_lines(
    +    info: dict[str, Any], location_width: int, function_width: int, term_width: int
    +) -> list[tuple[str, int, bool]]:
    +    """Build output lines for a chronological frame."""
    +    location_part = info["location_part"]
    +    function_part = info["function_part"]
    +    fragments = info["fragments"]
    +    frame_range = info["frame_range"]
    +    relevance = info["relevance"]
    +    info["exc_info"]
    +    frinfo = info["frinfo"]
    +
    +    # Calculate padding for alignment
    +    loc_pad = " " * (location_width - _display_width(location_part))
    +    func_pad = " " * (function_width - _display_width(function_part))
    +    label = f"{location_part}{loc_pad} {function_part}{func_pad}"
    +    location_width + 1 + function_width
    +
    +    lines = []
    +
    +    if not fragments:
    +        # Show "(no source code)" with the symbol emoji like a code line would have
    +        symbol = symbols.get(relevance, "")
    +        msg = f"(no source code) {symbol}"
    +        line = f"{INDENT}{label} {NO_SOURCE}{msg}{RESET}"
    +        lines.append((line, _display_width(line), False))
    +        return lines
    +
    +    start = frinfo["linenostart"]
    +    symbol = symbols.get(relevance, "")
    +    symbol_colored = f"{EM_CALL}{symbol}{RESET}" if symbol else ""
    +
    +    desc = symdesc[relevance]
    +
    +    # Account for LINE_PREFIX "│ " (2 chars) added to each line
    +    margin = 2
    +
    +    if relevance == "call":
    +        # One-liner for call frames
    +        if info["marked_lines"]:
    +            # Build full code with em parts
    +            code_parts = []
    +            # Also track em parts for potential collapsing
    +            em_ranges = []  # [(start_idx, end_idx), ...] in plain text
    +            em_start = None
    +            plain_len = 0  # Track position for em_ranges
    +
    +            for line_info in info["marked_lines"]:
    +                for fragment in line_info.get("fragments", []):
    +                    mark = fragment.get("mark")
    +                    em = fragment.get("em")
    +                    if mark:
    +                        colored = _format_fragment_call(fragment)
    +                        plain = fragment["code"].rstrip("\n\r")
    +                        # Track em ranges
    +                        if em in ("solo", "beg"):
    +                            em_start = plain_len
    +                        code_parts.append(colored)
    +                        plain_len += len(plain)
    +                        if em in ("solo", "fin") and em_start is not None:
    +                            em_ranges.append((em_start, plain_len))
    +                            em_start = None
    +                # Add space between marked regions from different lines
    +                if (
    +                    code_parts and line_info != info["marked_lines"][-1]
    +                ):  # pragma: no cover
    +                    code_parts.append(" ")
    +                    plain_len += 1
    +            code_colored = "".join(code_parts)
    +            code_plain = ANSI_ESCAPE_RE.sub("", code_colored)
    +
    +            # Collapse em parts longer than 20 chars
    +            if em_ranges:  # pragma: no cover
    +                em_start_pos = min(s for s, e in em_ranges)
    +                em_end_pos = max(e for s, e in em_ranges)
    +                em_text = code_plain[em_start_pos:em_end_pos]
    +
    +                if len(em_text) > 20:
    +                    collapsed_em = em_text[0] + "…" + em_text[-1]
    +                    code_plain = (
    +                        code_plain[:em_start_pos]
    +                        + collapsed_em
    +                        + code_plain[em_end_pos:]
    +                    )
    +                    # Rebuild colored version
    +                    code_colored = (
    +                        code_plain[:em_start_pos]
    +                        + EM_CALL
    +                        + collapsed_em
    +                        + RESET
    +                        + code_plain[em_start_pos + len(collapsed_em) :]
    +                    )
    +
    +            line = f"{INDENT}{label} {code_colored}{symbol_colored}"
    +            line_width = margin + _display_width(line)
    +            lines.append((line, line_width, False))
    +        else:  # pragma: no cover
    +            line = f"{INDENT}{label} {symbol_colored}"
    +            lines.append(
    +                (
    +                    line,
    +                    margin + _display_width(line),
    +                    False,
    +                )
    +            )
    +    else:
    +        # Full format for error/warning/stop/except frames
    +        label_line = f"{INDENT}{label}"
    +        lines.append((label_line, _display_width(label_line), False))
    +
    +        marked_line_nums = set()
    +        for ml in info["marked_lines"]:
    +            marked_line_nums.add(ml["line"])
    +
    +        for line_info in fragments:
    +            line_num = line_info["line"]
    +            abs_line = start + line_num - 1
    +            line_fragments = line_info.get("fragments", [])
    +            is_marked = line_num in marked_line_nums
    +
    +            code_colored = "".join(_format_fragment(f) for f in line_fragments)
    +
    +            if frame_range and abs_line == frame_range.lfinal and symbol:
    +                line = f"{CODE_INDENT}{code_colored} {symbol_colored}  {SYMBOLDESC}{desc}{RESET}"
    +            else:
    +                line = f"{CODE_INDENT}{code_colored}"
    +
    +            lines.append((line, _display_width(line), is_marked))
    +
    +    return lines
    +
    +
    +def _find_call_line_ranges(
    +    output_lines: list[tuple[str, int, int, bool]],
    +    frame_info_list: list[dict[str, Any]],
    +) -> list[tuple[int, int]]:
    +    """Find line ranges for call frames that can be used for inspector expansion.
    +
    +    Returns list of (start_line, end_line) tuples for call frames.
    +    """
    +    call_ranges = []
    +    current_start = None
    +
    +    for li, (_, _, fidx, _) in enumerate(output_lines):  # pragma: no cover
    +        if fidx < len(frame_info_list) and frame_info_list[fidx]["relevance"] == "call":
    +            if current_start is None:
    +                current_start = li
    +        else:
    +            if current_start is not None:
    +                call_ranges.append((current_start, li - 1))
    +                current_start = None
    +
    +    # Handle trailing call frames
    +    if current_start is not None:  # pragma: no cover
    +        call_ranges.append((current_start, len(output_lines) - 1))
    +
    +    return call_ranges
    +
    +
    +def _compute_inspector_positions(
    +    output_lines: list[tuple[str, int, int, bool]],
    +    inspector_frames: list[int],
    +    inspector_data: list[
    +        tuple[list[tuple[str, int]], int]
    +    ],  # [(lines, error_line), ...]
    +    frame_info_list: list[dict[str, Any]],
    +) -> tuple[list[int], int]:
    +    """Compute vertical positions for all inspectors, avoiding overlap.
    +
    +    Positioning rules:
    +    1. Ideally stay within own frame
    +    2. If needed, expand to surrounding call lines
    +    3. If still not enough, add empty lines after the frame
    +
    +    Returns (list of inspector_start positions, total_extra_lines needed).
    +    """
    +    if not inspector_frames:  # pragma: no cover
    +        return [], 0
    +
    +    # Get call line ranges that can be used for expansion
    +    call_ranges = _find_call_line_ranges(output_lines, frame_info_list)
    +
    +    positions = []
    +    extra_lines_after = {}  # frame_idx -> extra lines needed after it
    +    min_allowed_start = 0  # Tracks where next inspector can start (prevents overlap)
    +
    +    for idx, frame_idx in enumerate(inspector_frames):
    +        inspector_lines, error_line = inspector_data[idx]
    +        inspector_height = len(inspector_lines)
    +
    +        # Find frame boundaries
    +        frame_start, frame_end = _find_frame_line_range(output_lines, frame_idx)
    +
    +        # Account for any extra lines added after previous frames
    +        extra_before = sum(v for k, v in extra_lines_after.items() if k < frame_idx)
    +        frame_start += extra_before
    +        frame_end += extra_before
    +        error_line += extra_before
    +
    +        # Find adjacent call lines that we can expand into
    +        expandable_above = frame_start
    +        expandable_below = frame_end
    +
    +        # Look for call frames before this frame
    +        for call_start, call_end in call_ranges:  # pragma: no cover
    +            adj_call_start = call_start + extra_before
    +            adj_call_end = call_end + extra_before
    +            if adj_call_end == frame_start - 1:
    +                expandable_above = adj_call_start
    +            if adj_call_start == frame_end + 1:
    +                expandable_below = adj_call_end
    +
    +        # Respect minimum start to prevent overlap
    +        expandable_above = max(expandable_above, min_allowed_start)
    +
    +        # Calculate ideal position (arrow in middle, pointing at error line)
    +        ideal_arrow_pos = inspector_height // 2
    +        ideal_start = error_line - ideal_arrow_pos
    +
    +        # Strategy 1: Try to fit within own frame
    +        if frame_end - frame_start + 1 >= inspector_height:
    +            # Enough space in frame, position centered on error line
    +            inspector_start = max(
    +                frame_start, min(ideal_start, frame_end - inspector_height + 1)
    +            )
    +        # Strategy 2: Expand to adjacent call lines
    +        elif (
    +            expandable_below - expandable_above + 1 >= inspector_height
    +        ):  # pragma: no cover
    +            # Enough space with expansion
    +            inspector_start = max(
    +                expandable_above,
    +                min(ideal_start, expandable_below - inspector_height + 1),
    +            )
    +        # Strategy 3: Add extra empty lines after frame
    +        else:  # pragma: no cover
    +            # Not enough space even with expansion, need extra lines
    +            available_space = expandable_below - expandable_above + 1
    +            needed_extra = inspector_height - available_space
    +            extra_lines_after[frame_idx] = needed_extra
    +
    +            # Position at the top of available space
    +            inspector_start = expandable_above
    +
    +        # Ensure we respect the minimum allowed start
    +        inspector_start = max(inspector_start, min_allowed_start)
    +
    +        # Ensure arrow points at error line
    +        arrow_line_idx = error_line - inspector_start
    +        if arrow_line_idx < 0:  # pragma: no cover
    +            inspector_start = error_line
    +            arrow_line_idx = 0
    +        elif arrow_line_idx >= inspector_height:  # pragma: no cover
    +            inspector_start = error_line - inspector_height + 1
    +            arrow_line_idx = inspector_height - 1
    +
    +        positions.append(inspector_start)
    +
    +        # Update minimum start for next inspector (prevent overlap)
    +        inspector_end = inspector_start + inspector_height
    +        # Add any extra lines we're adding after this frame
    +        if frame_idx in extra_lines_after:
    +            inspector_end += extra_lines_after[frame_idx]
    +        min_allowed_start = inspector_end
    +
    +    total_extra = sum(extra_lines_after.values())
    +    return positions, total_extra
    +
    +
    +def _merge_chrono_output(
    +    output_lines: list[tuple[str, int, int, bool]],
    +    all_inspector_lines: list[list[tuple[str, int, int]]],
    +    all_inspector_min_widths: list[int],
    +    term_width: int,
    +    inspector_frame_indices: list[int],
    +    exception_banners: list[tuple[int, str]],
    +    frame_info_list: list[dict[str, Any]],
    +) -> str:
    +    """Merge chronological output with multiple inspectors and exception banners.
    +
    +    Args:
    +        output_lines: List of (line, plain_len, frame_idx, is_marked) tuples
    +        all_inspector_lines: List of inspector line lists, one per inspector frame
    +            Each line is (colored_line, plain_width, value_start_col)
    +        all_inspector_min_widths: Minimum required widths for each inspector
    +        term_width: Terminal width
    +        inspector_frame_indices: List of frame indices that have inspectors
    +        exception_banners: List of (insert_pos, banner) tuples
    +        frame_info_list: Frame info for all frames
    +    """
    +    if not inspector_frame_indices:  # pragma: no cover
    +        # No inspectors, just output lines and banners
    +        output = ""
    +        banner_idx = 0
    +        for li, (line, _, _, _) in enumerate(output_lines):
    +            output += f"{line}{EOL}"
    +            while banner_idx < len(exception_banners):
    +                insert_pos, banner = exception_banners[banner_idx]
    +                if li + 1 == insert_pos:
    +                    output += banner
    +                    banner_idx += 1
    +                else:
    +                    break
    +        for _, banner in exception_banners[banner_idx:]:
    +            output += banner
    +        return output
    +
    +    # Build inspector data: (lines, error_line) for each inspector
    +    inspector_data = []
    +    for i, frame_idx in enumerate(inspector_frame_indices):
    +        frame_start, frame_end = _find_frame_line_range(output_lines, frame_idx)
    +        error_line = _find_last_marked_line(output_lines, frame_start, frame_end)
    +        inspector_data.append((all_inspector_lines[i], error_line))
    +
    +    # Compute positions
    +    positions, total_extra = _compute_inspector_positions(
    +        output_lines, inspector_frame_indices, inspector_data, frame_info_list
    +    )
    +
    +    # Build a map of which inspector is active at each line
    +    inspector_at: dict[
    +        int, tuple[int, int, int, int]
    +    ] = {}  # line -> (insp_idx, insp_line_idx, arrow_line, col)
    +
    +    # Calculate column for each inspector and populate inspector_at
    +    for insp_idx, (frame_idx, insp_lines) in enumerate(
    +        zip(inspector_frame_indices, all_inspector_lines)
    +    ):
    +        inspector_start = positions[insp_idx]
    +        inspector_height = len(insp_lines)
    +
    +        # Get error line for this inspector (adjusted for any extra lines)
    +        frame_start, frame_end = _find_frame_line_range(output_lines, frame_idx)
    +        error_line = _find_last_marked_line(output_lines, frame_start, frame_end)
    +
    +        # Account for extra lines added by earlier inspectors
    +        extra_before = 0
    +        for prev_idx in range(insp_idx):  # pragma: no cover
    +            prev_frame_idx = inspector_frame_indices[prev_idx]
    +            prev_frame_start, prev_frame_end = _find_frame_line_range(
    +                output_lines, prev_frame_idx
    +            )
    +            if prev_frame_end < frame_start:
    +                # Check if this inspector needed extra lines
    +                prev_height = len(all_inspector_lines[prev_idx])
    +                prev_available = prev_frame_end - max(0, positions[prev_idx]) + 1
    +                if prev_height > prev_available:
    +                    extra_before += prev_height - prev_available
    +
    +        # Calculate arrow position
    +        arrow_line = error_line - inspector_start
    +        if arrow_line < 0:  # pragma: no cover
    +            arrow_line = 0
    +        elif arrow_line >= inspector_height:  # pragma: no cover
    +            arrow_line = inspector_height - 1
    +
    +        # Calculate column position for this inspector
    +        max_line_len = 0
    +        for li in range(  # pragma: no cover
    +            inspector_start, min(inspector_start + inspector_height, len(output_lines))
    +        ):
    +            if li < len(output_lines):
    +                max_line_len = max(max_line_len, output_lines[li][1])
    +
    +        inspector_col = max_line_len + 2
    +
    +        # Calculate available space - inspector must not overlap with code
    +        # Available = term_width - inspector_col - 4 (for box/arrow chars)
    +        available_width = term_width - inspector_col - 4
    +
    +        # Get minimum required width for this inspector (name: type = + some value)
    +        min_required = all_inspector_min_widths[insp_idx]
    +
    +        # Skip this inspector if not enough space to show meaningful content
    +        if available_width < min_required:
    +            continue
    +
    +        # Mark lines where this inspector is active
    +        for insp_line_idx in range(inspector_height):
    +            line_idx = inspector_start + insp_line_idx
    +            inspector_at[line_idx] = (
    +                insp_idx,
    +                insp_line_idx,
    +                arrow_line,
    +                inspector_col,
    +            )
    +
    +    # Build output
    +    output = ""
    +    banner_idx = 0
    +
    +    for li in range(len(output_lines)):
    +        line, plain_len, frame_idx, is_marked = output_lines[li]
    +
    +        # Check if inspector is active at this line
    +        if li in inspector_at:
    +            insp_idx, insp_line_idx, arrow_line, inspector_col = inspector_at[li]
    +            insp_lines = all_inspector_lines[insp_idx]
    +            insp_line, insp_width, value_start = insp_lines[insp_line_idx]
    +            inspector_height = len(insp_lines)
    +
    +            cursor_pos = f"{ESC}{inspector_col + 1}G"
    +            is_first = insp_line_idx == 0
    +            is_last = insp_line_idx == inspector_height - 1
    +            is_arrow = insp_line_idx == arrow_line
    +
    +            # Truncate inspector content if it exceeds available width
    +            # Only truncate the value portion, preserving name/type coloring
    +            available_for_content = (
    +                term_width - inspector_col - 5
    +            )  # 5 = box chars + space
    +            if insp_width > available_for_content > 0:
    +                # Get plain text to find where to truncate
    +                insp_plain = ANSI_ESCAPE_RE.sub("", insp_line)
    +                # Available space for value = total available - prefix
    +                available_for_value = available_for_content - value_start
    +                if available_for_value > 1:
    +                    # Truncate value only, keep prefix with its coloring
    +                    # Find the ANSI position corresponding to value_start in plain text
    +                    # by scanning the colored string
    +                    plain_idx = 0
    +                    colored_idx = 0
    +                    while plain_idx < value_start and colored_idx < len(insp_line):
    +                        if insp_line[colored_idx] == "\x1b":
    +                            # Skip ANSI sequence
    +                            while (
    +                                colored_idx < len(insp_line)
    +                                and insp_line[colored_idx] != "m"
    +                            ):
    +                                colored_idx += 1
    +                            colored_idx += 1  # skip the 'm'
    +                        else:
    +                            plain_idx += 1
    +                            colored_idx += 1
    +                    # colored_idx is now at the start of the value in the colored string
    +                    prefix_colored = insp_line[:colored_idx]
    +                    value_plain = insp_plain[value_start:]
    +                    truncated_value = value_plain[: available_for_value - 1] + "…"
    +                    insp_line = f"{prefix_colored}{VAR}{truncated_value}{RESET}"
    +                elif available_for_content > 0:  # pragma: no cover
    +                    # Not enough space, just show ellipsis
    +                    insp_line = f"{VAR}…{RESET}"
    +
    +            if is_arrow:
    +                if is_first and is_last:
    +                    box_char = SINGLE_T
    +                elif is_first:
    +                    box_char = BOX_TR
    +                elif is_last:
    +                    box_char = BOX_BR
    +                else:
    +                    box_char = BOX_VL
    +                output += f"{line}{cursor_pos}{DIM}{ARROW_LEFT}{BOX_H}{box_char}{RESET} {insp_line}{EOL}"
    +            else:
    +                if is_first:
    +                    box_char = BOX_TL
    +                elif is_last:
    +                    box_char = BOX_BL  # pragma: no cover
    +                else:
    +                    box_char = BOX_V
    +                output += f"{line}{cursor_pos}  {DIM}{box_char}{RESET} {insp_line}{EOL}"
    +
    +        else:
    +            output += f"{line}{EOL}"
    +
    +        # Insert exception banner if needed
    +        while banner_idx < len(exception_banners):
    +            insert_pos, banner = exception_banners[banner_idx]
    +            if li + 1 == insert_pos:
    +                output += banner
    +                banner_idx += 1
    +            else:
    +                break
    +
    +        # Check if we need to emit extra lines for inspector overflow
    +        # Find if any inspector ends after this line and needs extra lines
    +        for insp_idx, frame_idx in enumerate(inspector_frame_indices):
    +            frame_start, frame_end = _find_frame_line_range(output_lines, frame_idx)
    +            if li == frame_end:
    +                # Check if this inspector needs extra lines
    +                inspector_start = positions[insp_idx]
    +                inspector_height = len(all_inspector_lines[insp_idx])
    +                inspector_end = inspector_start + inspector_height
    +
    +                # How many lines extend beyond the current output?
    +                if inspector_end > li + 1:  # pragma: no cover
    +                    for extra_li in range(li + 1, inspector_end):
    +                        if extra_li in inspector_at:
    +                            _, insp_line_idx, arrow_line, inspector_col = inspector_at[
    +                                extra_li
    +                            ]
    +                            insp_lines = all_inspector_lines[insp_idx]
    +                            insp_line, _, _ = insp_lines[insp_line_idx]
    +
    +                            cursor_pos = f"{ESC}{inspector_col + 1}G"
    +                            is_last = insp_line_idx == len(insp_lines) - 1
    +                            is_arrow = insp_line_idx == arrow_line
    +
    +                            if is_arrow:
    +                                box_char = BOX_BR if is_last else BOX_VL
    +                                output += f"{cursor_pos}{DIM}{ARROW_LEFT}{BOX_H}{box_char}{RESET} {insp_line}{EOL}"
    +                            else:
    +                                box_char = BOX_BL if is_last else BOX_V
    +                                output += f"{cursor_pos}  {DIM}{box_char}{RESET} {insp_line}{EOL}"
    +
    +    # Any remaining banners
    +    for _, banner in exception_banners[banner_idx:]:
    +        output += banner
    +
    +    return output
    +
    +
    +def _format_fragment(fragment: dict[str, Any]) -> str:
    +    """Format a fragment returning colored string."""
    +    code = fragment["code"].rstrip("\n\r")
    +    mark = fragment.get("mark")
    +    em = fragment.get("em")
    +
    +    colored_parts = []
    +
    +    # Open mark if starting
    +    if mark in ("solo", "beg"):
    +        colored_parts.append(MARK_BG + MARK_TEXT)
    +
    +    # Open em if starting (red text within the mark)
    +    if em in ("solo", "beg"):
    +        colored_parts.append(EM)
    +
    +    # Add the code
    +    colored_parts.append(code)
    +
    +    # Close em if ending
    +    if em in ("fin", "solo") and mark not in ("fin", "solo"):
    +        colored_parts.append(MARK_TEXT)
    +
    +    # Close mark if ending
    +    if mark in ("fin", "solo"):
    +        colored_parts.append(RESET)
    +
    +    return "".join(colored_parts)
    +
    +
    +def _format_fragment_call(fragment: dict[str, Any]) -> str:
    +    """Format a fragment for call frames: default color, only em in yellow."""
    +    code = fragment["code"].rstrip("\n\r")
    +    em = fragment.get("em")
    +
    +    colored_parts = []
    +
    +    # Open em if starting (yellow text)
    +    if em in ("solo", "beg"):
    +        colored_parts.append(EM_CALL)
    +
    +    # Add the code
    +    colored_parts.append(code)
    +
    +    # Close em if ending
    +    if em in ("fin", "solo"):
    +        colored_parts.append(RESET)
    +
    +    return "".join(colored_parts)
    +
    +
    +def _build_variable_inspector(
    +    variables: list[Any], term_width: int
    +) -> tuple[list[tuple[str, int, int]], int]:
    +    """Build variable inspector lines.
    +
    +    Returns:
    +        tuple: (list of (colored_line, plain_width, value_start_col), min_required_width)
    +            - list of lines for the inspector with metadata for smart truncation
    +            - minimum width needed to display "name: type = " + some value chars
    +
    +    Uses simple left-side vertical bar only, no top/bottom borders.
    +    Multi-line string values are rendered with continuation lines properly indented.
    +    Variable names are right-aligned so that = signs line up.
    +    """
    +    if not variables:
    +        return [], 0
    +
    +    # First pass: collect variable info and filter out non-displayable values
    +    var_data = []  # [(name, typename, val_str, fmt_hint), ...]
    +    for var_info in variables:
    +        # Handle both old tuple format and new VarInfo namedtuple
    +        if hasattr(var_info, "name"):
    +            name, typename, value, fmt_hint = (
    +                var_info.name,
    +                var_info.typename,
    +                var_info.value,
    +                var_info.format_hint,
    +            )
    +        else:
    +            name, typename, value = var_info
    +            fmt_hint = "inline"
    +
    +        # Format the value as a string
    +        if isinstance(value, str):
    +            val_str = value
    +        elif isinstance(value, dict) and value.get("type") == "keyvalue":
    +            # Format key-value pairs inline
    +            pairs = [f"{k}: {v}" for k, v in value.get("rows", [])]
    +            val_str = "{" + ", ".join(pairs) + "}"
    +        elif isinstance(value, dict) and value.get("type") == "array":
    +            # Format array inline (simplified)
    +            rows = value.get("rows", [])
    +            if rows:
    +                val_str = (
    +                    "[" + ", ".join(str(c) for c in rows[0] if c is not None) + ", ...]"
    +                )
    +            else:
    +                val_str = "[]"
    +        elif isinstance(value, list):
    +            # Simple matrix/list format
    +            if value and isinstance(value[0], list):
    +                val_str = (
    +                    "["
    +                    + ", ".join(str(c) for c in value[0] if c is not None)
    +                    + ", ...]"
    +                )
    +            else:
    +                val_str = str(value)
    +        else:
    +            val_str = str(value)
    +
    +        # Skip variables with no displayable value
    +        if val_str == "⋯":
    +            continue
    +
    +        var_data.append((name, typename, val_str, fmt_hint))
    +
    +    if not var_data:
    +        return [], 0
    +
    +    # Calculate max width of "name: type" or "name" part for alignment
    +    max_name_part_len = 0
    +    for name, typename, _, _ in var_data:
    +        name_part_len = len(name) + len(": ") + len(typename) if typename else len(name)
    +        max_name_part_len = max(max_name_part_len, name_part_len)
    +
    +    # Calculate minimum required width: "name: type = " + at least 5 chars of value
    +    prefix_width = max_name_part_len + len(" = ")
    +    min_required_width = prefix_width + 5
    +
    +    # Pre-truncate values to a reasonable width (final truncation happens at render)
    +    value_width = max(5, term_width // 2 - 4 - prefix_width)
    +
    +    # Build variable lines with right-aligned names
    +    # Each result entry: (colored_line, plain_width, value_start_col)
    +    # value_start_col is where the value starts, for smart truncation at render time
    +    result = []
    +    for name, typename, val_str, fmt_hint in var_data:
    +        name_part = f"{name}: {typename}" if typename else name
    +        padding = " " * (max_name_part_len - len(name_part))
    +        indent = " " * prefix_width
    +
    +        # Handle multi-line block format
    +        if fmt_hint == "block" and "\n" in val_str:  # pragma: no cover
    +            for i, val_line in enumerate(val_str.split("\n")):
    +                # Truncate value line if needed
    +                if len(val_line) > value_width:
    +                    val_line = val_line[: value_width - 1] + "…"
    +                if i == 0:
    +                    # First line with name and type
    +                    if typename:
    +                        line = f"{VAR}{padding}{name}: {TYPE_COLOR}{typename} = {VAR}{val_line}{RESET}"
    +                    else:
    +                        line = f"{VAR}{padding}{name} = {val_line}{RESET}"
    +                    plain = f"{padding}{name_part} = {val_line}"
    +                    result.append((line, len(plain), prefix_width))
    +                else:
    +                    # Continuation lines (indented) - all value
    +                    line = f"{VAR}{indent}{val_line}{RESET}"
    +                    plain = f"{indent}{val_line}"
    +                    result.append((line, len(plain), prefix_width))
    +        else:
    +            # Single line format
    +            if len(val_str) > value_width:
    +                val_str = val_str[: value_width - 1] + "…"
    +            if typename:
    +                line = f"{VAR}{padding}{name}: {TYPE_COLOR}{typename} = {VAR}{val_str}{RESET}"
    +            else:
    +                line = f"{VAR}{padding}{name} = {val_str}{RESET}"
    +            plain = f"{padding}{name_part} = {val_str}"
    +            result.append((line, len(plain), prefix_width))
    +
    +    return result, min_required_width
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER
    new file mode 100644
    index 0000000..a1b589e
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA
    new file mode 100644
    index 0000000..b09cb50
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA
    @@ -0,0 +1,72 @@
    +Metadata-Version: 2.4
    +Name: typing_extensions
    +Version: 4.15.0
    +Summary: Backported and Experimental Type Hints for Python 3.9+
    +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
    +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" 
    +Requires-Python: >=3.9
    +Description-Content-Type: text/markdown
    +License-Expression: PSF-2.0
    +Classifier: Development Status :: 5 - Production/Stable
    +Classifier: Environment :: Console
    +Classifier: Intended Audience :: Developers
    +Classifier: Operating System :: OS Independent
    +Classifier: Programming Language :: Python :: 3
    +Classifier: Programming Language :: Python :: 3 :: Only
    +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 :: 3.14
    +Classifier: Topic :: Software Development
    +License-File: LICENSE
    +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues
    +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md
    +Project-URL: Documentation, https://typing-extensions.readthedocs.io/
    +Project-URL: Home, https://github.com/python/typing_extensions
    +Project-URL: Q & A, https://github.com/python/typing/discussions
    +Project-URL: Repository, https://github.com/python/typing_extensions
    +
    +# Typing Extensions
    +
    +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)
    +
    +[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) –
    +[PyPI](https://pypi.org/project/typing-extensions/)
    +
    +## Overview
    +
    +The `typing_extensions` module serves two related purposes:
    +
    +- Enable use of new type system features on older Python versions. For example,
    +  `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows
    +  users on previous Python versions to use it too.
    +- Enable experimentation with new type system PEPs before they are accepted and
    +  added to the `typing` module.
    +
    +`typing_extensions` is treated specially by static type checkers such as
    +mypy and pyright. Objects defined in `typing_extensions` are treated the same
    +way as equivalent forms in `typing`.
    +
    +`typing_extensions` uses
    +[Semantic Versioning](https://semver.org/). The
    +major version will be incremented only for backwards-incompatible changes.
    +Therefore, it's safe to depend
    +on `typing_extensions` like this: `typing_extensions ~=x.y`,
    +where `x.y` is the first version that includes all features you need.
    +[This](https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release)
    +is equivalent to `typing_extensions >=x.y, <(x+1)`. Do not depend on `~= x.y.z`
    +unless you really know what you're doing; that defeats the purpose of
    +semantic versioning.
    +
    +## Included items
    +
    +See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a
    +complete listing of module contents.
    +
    +## Contributing
    +
    +See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md)
    +for how to contribute to `typing_extensions`.
    +
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD
    new file mode 100644
    index 0000000..6ab09b5
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD
    @@ -0,0 +1,7 @@
    +__pycache__/typing_extensions.cpython-312.pyc,,
    +typing_extensions-4.15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +typing_extensions-4.15.0.dist-info/METADATA,sha256=wTg3j-jxiTSsmd4GBTXFPsbBOu7WXpTDJkHafuMZKnI,3259
    +typing_extensions-4.15.0.dist-info/RECORD,,
    +typing_extensions-4.15.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
    +typing_extensions-4.15.0.dist-info/licenses/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
    +typing_extensions.py,sha256=Qz0R0XDTok0usGXrwb_oSM6n49fOaFZ6tSvqLUwvftg,160429
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL
    new file mode 100644
    index 0000000..d8b9936
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL
    @@ -0,0 +1,4 @@
    +Wheel-Version: 1.0
    +Generator: flit 3.12.0
    +Root-Is-Purelib: true
    +Tag: py3-none-any
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE
    new file mode 100644
    index 0000000..f26bcf4
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE
    @@ -0,0 +1,279 @@
    +A. HISTORY OF THE SOFTWARE
    +==========================
    +
    +Python was created in the early 1990s by Guido van Rossum at Stichting
    +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
    +as a successor of a language called ABC.  Guido remains Python's
    +principal author, although it includes many contributions from others.
    +
    +In 1995, Guido continued his work on Python at the Corporation for
    +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
    +in Reston, Virginia where he released several versions of the
    +software.
    +
    +In May 2000, Guido and the Python core development team moved to
    +BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
    +year, the PythonLabs team moved to Digital Creations, which became
    +Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
    +https://www.python.org/psf/) was formed, a non-profit organization
    +created specifically to own Python-related Intellectual Property.
    +Zope Corporation was a sponsoring member of the PSF.
    +
    +All Python releases are Open Source (see https://opensource.org for
    +the Open Source Definition).  Historically, most, but not all, Python
    +releases have also been GPL-compatible; the table below summarizes
    +the various releases.
    +
    +    Release         Derived     Year        Owner       GPL-
    +                    from                                compatible? (1)
    +
    +    0.9.0 thru 1.2              1991-1995   CWI         yes
    +    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
    +    1.6             1.5.2       2000        CNRI        no
    +    2.0             1.6         2000        BeOpen.com  no
    +    1.6.1           1.6         2001        CNRI        yes (2)
    +    2.1             2.0+1.6.1   2001        PSF         no
    +    2.0.1           2.0+1.6.1   2001        PSF         yes
    +    2.1.1           2.1+2.0.1   2001        PSF         yes
    +    2.1.2           2.1.1       2002        PSF         yes
    +    2.1.3           2.1.2       2002        PSF         yes
    +    2.2 and above   2.1.1       2001-now    PSF         yes
    +
    +Footnotes:
    +
    +(1) GPL-compatible doesn't mean that we're distributing Python under
    +    the GPL.  All Python licenses, unlike the GPL, let you distribute
    +    a modified version without making your changes open source.  The
    +    GPL-compatible licenses make it possible to combine Python with
    +    other software that is released under the GPL; the others don't.
    +
    +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
    +    because its license has a choice of law clause.  According to
    +    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
    +    is "not incompatible" with the GPL.
    +
    +Thanks to the many outside volunteers who have worked under Guido's
    +direction to make these releases possible.
    +
    +
    +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
    +===============================================================
    +
    +Python software and documentation are licensed under the
    +Python Software Foundation License Version 2.
    +
    +Starting with Python 3.8.6, examples, recipes, and other code in
    +the documentation are dual licensed under the PSF License Version 2
    +and the Zero-Clause BSD license.
    +
    +Some software incorporated into Python is under different licenses.
    +The licenses are listed with code falling under that license.
    +
    +
    +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, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 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.
    +
    +
    +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
    +-------------------------------------------
    +
    +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
    +
    +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
    +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
    +Individual or Organization ("Licensee") accessing and otherwise using
    +this software in source or binary form and its associated
    +documentation ("the Software").
    +
    +2. Subject to the terms and conditions of this BeOpen Python License
    +Agreement, BeOpen hereby grants Licensee a non-exclusive,
    +royalty-free, world-wide license to reproduce, analyze, test, perform
    +and/or display publicly, prepare derivative works, distribute, and
    +otherwise use the Software alone or in any derivative version,
    +provided, however, that the BeOpen Python License is retained in the
    +Software, alone or in any derivative version prepared by Licensee.
    +
    +3. BeOpen is making the Software available to Licensee on an "AS IS"
    +basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
    +IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
    +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
    +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
    +INFRINGE ANY THIRD PARTY RIGHTS.
    +
    +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
    +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
    +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
    +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
    +
    +5. This License Agreement will automatically terminate upon a material
    +breach of its terms and conditions.
    +
    +6. This License Agreement shall be governed by and interpreted in all
    +respects by the law of the State of California, excluding conflict of
    +law provisions.  Nothing in this License Agreement shall be deemed to
    +create any relationship of agency, partnership, or joint venture
    +between BeOpen and Licensee.  This License Agreement does not grant
    +permission to use BeOpen trademarks or trade names in a trademark
    +sense to endorse or promote products or services of Licensee, or any
    +third party.  As an exception, the "BeOpen Python" logos available at
    +http://www.pythonlabs.com/logos.html may be used according to the
    +permissions granted on that web page.
    +
    +7. By copying, installing or otherwise using the software, Licensee
    +agrees to be bound by the terms and conditions of this License
    +Agreement.
    +
    +
    +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
    +---------------------------------------
    +
    +1. This LICENSE AGREEMENT is between the Corporation for National
    +Research Initiatives, having an office at 1895 Preston White Drive,
    +Reston, VA 20191 ("CNRI"), and the Individual or Organization
    +("Licensee") accessing and otherwise using Python 1.6.1 software in
    +source or binary form and its associated documentation.
    +
    +2. Subject to the terms and conditions of this License Agreement, CNRI
    +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 1.6.1
    +alone or in any derivative version, provided, however, that CNRI's
    +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
    +1995-2001 Corporation for National Research Initiatives; All Rights
    +Reserved" are retained in Python 1.6.1 alone or in any derivative
    +version prepared by Licensee.  Alternately, in lieu of CNRI's License
    +Agreement, Licensee may substitute the following text (omitting the
    +quotes): "Python 1.6.1 is made available subject to the terms and
    +conditions in CNRI's License Agreement.  This Agreement together with
    +Python 1.6.1 may be located on the internet using the following
    +unique, persistent identifier (known as a handle): 1895.22/1013.  This
    +Agreement may also be obtained from a proxy server on the internet
    +using the following URL: http://hdl.handle.net/1895.22/1013".
    +
    +3. In the event Licensee prepares a derivative work that is based on
    +or incorporates Python 1.6.1 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 1.6.1.
    +
    +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
    +basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
    +IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
    +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
    +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
    +INFRINGE ANY THIRD PARTY RIGHTS.
    +
    +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
    +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
    +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
    +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. This License Agreement shall be governed by the federal
    +intellectual property law of the United States, including without
    +limitation the federal copyright law, and, to the extent such
    +U.S. federal law does not apply, by the law of the Commonwealth of
    +Virginia, excluding Virginia's conflict of law provisions.
    +Notwithstanding the foregoing, with regard to derivative works based
    +on Python 1.6.1 that incorporate non-separable material that was
    +previously distributed under the GNU General Public License (GPL), the
    +law of the Commonwealth of Virginia shall govern this License
    +Agreement only as to issues arising under or with respect to
    +Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
    +License Agreement shall be deemed to create any relationship of
    +agency, partnership, or joint venture between CNRI and Licensee.  This
    +License Agreement does not grant permission to use CNRI trademarks or
    +trade name in a trademark sense to endorse or promote products or
    +services of Licensee, or any third party.
    +
    +8. By clicking on the "ACCEPT" button where indicated, or by copying,
    +installing or otherwise using Python 1.6.1, Licensee agrees to be
    +bound by the terms and conditions of this License Agreement.
    +
    +        ACCEPT
    +
    +
    +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
    +--------------------------------------------------
    +
    +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
    +The Netherlands.  All rights reserved.
    +
    +Permission to use, copy, modify, and distribute this software and its
    +documentation for any purpose and without fee is hereby granted,
    +provided that the above copyright notice appear in all copies and that
    +both that copyright notice and this permission notice appear in
    +supporting documentation, and that the name of Stichting Mathematisch
    +Centrum or CWI not be used in advertising or publicity pertaining to
    +distribution of the software without specific, written prior
    +permission.
    +
    +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
    +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
    +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
    +FOR ANY SPECIAL, 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.
    +
    +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
    +----------------------------------------------------------------------
    +
    +Permission to use, copy, modify, and/or distribute this software for any
    +purpose with or without fee is hereby granted.
    +
    +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.
    diff --git a/.venv/lib/python3.12/site-packages/typing_extensions.py b/.venv/lib/python3.12/site-packages/typing_extensions.py
    new file mode 100644
    index 0000000..77f33e1
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_extensions.py
    @@ -0,0 +1,4317 @@
    +import abc
    +import builtins
    +import collections
    +import collections.abc
    +import contextlib
    +import enum
    +import functools
    +import inspect
    +import io
    +import keyword
    +import operator
    +import sys
    +import types as _types
    +import typing
    +import warnings
    +
    +# Breakpoint: https://github.com/python/cpython/pull/119891
    +if sys.version_info >= (3, 14):
    +    import annotationlib
    +
    +__all__ = [
    +    # Super-special typing primitives.
    +    'Any',
    +    'ClassVar',
    +    'Concatenate',
    +    'Final',
    +    'LiteralString',
    +    'ParamSpec',
    +    'ParamSpecArgs',
    +    'ParamSpecKwargs',
    +    'Self',
    +    'Type',
    +    'TypeVar',
    +    'TypeVarTuple',
    +    'Unpack',
    +
    +    # ABCs (from collections.abc).
    +    'Awaitable',
    +    'AsyncIterator',
    +    'AsyncIterable',
    +    'Coroutine',
    +    'AsyncGenerator',
    +    'AsyncContextManager',
    +    'Buffer',
    +    'ChainMap',
    +
    +    # Concrete collection types.
    +    'ContextManager',
    +    'Counter',
    +    'Deque',
    +    'DefaultDict',
    +    'NamedTuple',
    +    'OrderedDict',
    +    'TypedDict',
    +
    +    # Structural checks, a.k.a. protocols.
    +    'SupportsAbs',
    +    'SupportsBytes',
    +    'SupportsComplex',
    +    'SupportsFloat',
    +    'SupportsIndex',
    +    'SupportsInt',
    +    'SupportsRound',
    +    'Reader',
    +    'Writer',
    +
    +    # One-off things.
    +    'Annotated',
    +    'assert_never',
    +    'assert_type',
    +    'clear_overloads',
    +    'dataclass_transform',
    +    'deprecated',
    +    'disjoint_base',
    +    'Doc',
    +    'evaluate_forward_ref',
    +    'get_overloads',
    +    'final',
    +    'Format',
    +    'get_annotations',
    +    'get_args',
    +    'get_origin',
    +    'get_original_bases',
    +    'get_protocol_members',
    +    'get_type_hints',
    +    'IntVar',
    +    'is_protocol',
    +    'is_typeddict',
    +    'Literal',
    +    'NewType',
    +    'overload',
    +    'override',
    +    'Protocol',
    +    'Sentinel',
    +    'reveal_type',
    +    'runtime',
    +    'runtime_checkable',
    +    'Text',
    +    'TypeAlias',
    +    'TypeAliasType',
    +    'TypeForm',
    +    'TypeGuard',
    +    'TypeIs',
    +    'TYPE_CHECKING',
    +    'type_repr',
    +    'Never',
    +    'NoReturn',
    +    'ReadOnly',
    +    'Required',
    +    'NotRequired',
    +    'NoDefault',
    +    'NoExtraItems',
    +
    +    # Pure aliases, have always been in typing
    +    'AbstractSet',
    +    'AnyStr',
    +    'BinaryIO',
    +    'Callable',
    +    'Collection',
    +    'Container',
    +    'Dict',
    +    'ForwardRef',
    +    'FrozenSet',
    +    'Generator',
    +    'Generic',
    +    'Hashable',
    +    'IO',
    +    'ItemsView',
    +    'Iterable',
    +    'Iterator',
    +    'KeysView',
    +    'List',
    +    'Mapping',
    +    'MappingView',
    +    'Match',
    +    'MutableMapping',
    +    'MutableSequence',
    +    'MutableSet',
    +    'Optional',
    +    'Pattern',
    +    'Reversible',
    +    'Sequence',
    +    'Set',
    +    'Sized',
    +    'TextIO',
    +    'Tuple',
    +    'Union',
    +    'ValuesView',
    +    'cast',
    +    'no_type_check',
    +    'no_type_check_decorator',
    +]
    +
    +# for backward compatibility
    +PEP_560 = True
    +GenericMeta = type
    +# Breakpoint: https://github.com/python/cpython/pull/116129
    +_PEP_696_IMPLEMENTED = sys.version_info >= (3, 13, 0, "beta")
    +
    +# Added with bpo-45166 to 3.10.1+ and some 3.9 versions
    +_FORWARD_REF_HAS_CLASS = "__forward_is_class__" in typing.ForwardRef.__slots__
    +
    +# The functions below are modified copies of typing internal helpers.
    +# They are needed by _ProtocolMeta and they provide support for PEP 646.
    +
    +
    +class _Sentinel:
    +    def __repr__(self):
    +        return ""
    +
    +
    +_marker = _Sentinel()
    +
    +
    +# Breakpoint: https://github.com/python/cpython/pull/27342
    +if sys.version_info >= (3, 10):
    +    def _should_collect_from_parameters(t):
    +        return isinstance(
    +            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
    +        )
    +else:
    +    def _should_collect_from_parameters(t):
    +        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
    +
    +
    +NoReturn = typing.NoReturn
    +
    +# Some unconstrained type variables.  These are used by the container types.
    +# (These are not for export.)
    +T = typing.TypeVar('T')  # Any type.
    +KT = typing.TypeVar('KT')  # Key type.
    +VT = typing.TypeVar('VT')  # Value type.
    +T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
    +T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
    +
    +
    +# Breakpoint: https://github.com/python/cpython/pull/31841
    +if sys.version_info >= (3, 11):
    +    from typing import Any
    +else:
    +
    +    class _AnyMeta(type):
    +        def __instancecheck__(self, obj):
    +            if self is Any:
    +                raise TypeError("typing_extensions.Any cannot be used with isinstance()")
    +            return super().__instancecheck__(obj)
    +
    +        def __repr__(self):
    +            if self is Any:
    +                return "typing_extensions.Any"
    +            return super().__repr__()
    +
    +    class Any(metaclass=_AnyMeta):
    +        """Special type indicating an unconstrained type.
    +        - Any is compatible with every type.
    +        - Any assumed to have all methods.
    +        - All values assumed to be instances of Any.
    +        Note that all the above statements are true from the point of view of
    +        static type checkers. At runtime, Any should not be used with instance
    +        checks.
    +        """
    +        def __new__(cls, *args, **kwargs):
    +            if cls is Any:
    +                raise TypeError("Any cannot be instantiated")
    +            return super().__new__(cls, *args, **kwargs)
    +
    +
    +ClassVar = typing.ClassVar
    +
    +# Vendored from cpython typing._SpecialFrom
    +# Having a separate class means that instances will not be rejected by
    +# typing._type_check.
    +class _SpecialForm(typing._Final, _root=True):
    +    __slots__ = ('_name', '__doc__', '_getitem')
    +
    +    def __init__(self, getitem):
    +        self._getitem = getitem
    +        self._name = getitem.__name__
    +        self.__doc__ = getitem.__doc__
    +
    +    def __getattr__(self, item):
    +        if item in {'__name__', '__qualname__'}:
    +            return self._name
    +
    +        raise AttributeError(item)
    +
    +    def __mro_entries__(self, bases):
    +        raise TypeError(f"Cannot subclass {self!r}")
    +
    +    def __repr__(self):
    +        return f'typing_extensions.{self._name}'
    +
    +    def __reduce__(self):
    +        return self._name
    +
    +    def __call__(self, *args, **kwds):
    +        raise TypeError(f"Cannot instantiate {self!r}")
    +
    +    def __or__(self, other):
    +        return typing.Union[self, other]
    +
    +    def __ror__(self, other):
    +        return typing.Union[other, self]
    +
    +    def __instancecheck__(self, obj):
    +        raise TypeError(f"{self} cannot be used with isinstance()")
    +
    +    def __subclasscheck__(self, cls):
    +        raise TypeError(f"{self} cannot be used with issubclass()")
    +
    +    @typing._tp_cache
    +    def __getitem__(self, parameters):
    +        return self._getitem(self, parameters)
    +
    +
    +# Note that inheriting from this class means that the object will be
    +# rejected by typing._type_check, so do not use it if the special form
    +# is arguably valid as a type by itself.
    +class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
    +    def __repr__(self):
    +        return 'typing_extensions.' + self._name
    +
    +
    +Final = typing.Final
    +
    +# Breakpoint: https://github.com/python/cpython/pull/30530
    +if sys.version_info >= (3, 11):
    +    final = typing.final
    +else:
    +    # @final exists in 3.8+, but we backport it for all versions
    +    # before 3.11 to keep support for the __final__ attribute.
    +    # See https://bugs.python.org/issue46342
    +    def final(f):
    +        """This decorator can be used to indicate to type checkers that
    +        the decorated method cannot be overridden, and decorated class
    +        cannot be subclassed. For example:
    +
    +            class Base:
    +                @final
    +                def done(self) -> None:
    +                    ...
    +            class Sub(Base):
    +                def done(self) -> None:  # Error reported by type checker
    +                    ...
    +            @final
    +            class Leaf:
    +                ...
    +            class Other(Leaf):  # Error reported by type checker
    +                ...
    +
    +        There is no runtime checking of these properties. The decorator
    +        sets the ``__final__`` attribute to ``True`` on the decorated object
    +        to allow runtime introspection.
    +        """
    +        try:
    +            f.__final__ = True
    +        except (AttributeError, TypeError):
    +            # Skip the attribute silently if it is not writable.
    +            # AttributeError happens if the object has __slots__ or a
    +            # read-only property, TypeError if it's a builtin class.
    +            pass
    +        return f
    +
    +
    +if hasattr(typing, "disjoint_base"):  # 3.15
    +    disjoint_base = typing.disjoint_base
    +else:
    +    def disjoint_base(cls):
    +        """This decorator marks a class as a disjoint base.
    +
    +        Child classes of a disjoint base cannot inherit from other disjoint bases that are
    +        not parent classes of the disjoint base.
    +
    +        For example:
    +
    +            @disjoint_base
    +            class Disjoint1: pass
    +
    +            @disjoint_base
    +            class Disjoint2: pass
    +
    +            class Disjoint3(Disjoint1, Disjoint2): pass  # Type checker error
    +
    +        Type checkers can use knowledge of disjoint bases to detect unreachable code
    +        and determine when two types can overlap.
    +
    +        See PEP 800."""
    +        cls.__disjoint_base__ = True
    +        return cls
    +
    +
    +def IntVar(name):
    +    return typing.TypeVar(name)
    +
    +
    +# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8
    +# Breakpoint: https://github.com/python/cpython/pull/29334
    +if sys.version_info >= (3, 10, 1):
    +    Literal = typing.Literal
    +else:
    +    def _flatten_literal_params(parameters):
    +        """An internal helper for Literal creation: flatten Literals among parameters"""
    +        params = []
    +        for p in parameters:
    +            if isinstance(p, _LiteralGenericAlias):
    +                params.extend(p.__args__)
    +            else:
    +                params.append(p)
    +        return tuple(params)
    +
    +    def _value_and_type_iter(params):
    +        for p in params:
    +            yield p, type(p)
    +
    +    class _LiteralGenericAlias(typing._GenericAlias, _root=True):
    +        def __eq__(self, other):
    +            if not isinstance(other, _LiteralGenericAlias):
    +                return NotImplemented
    +            these_args_deduped = set(_value_and_type_iter(self.__args__))
    +            other_args_deduped = set(_value_and_type_iter(other.__args__))
    +            return these_args_deduped == other_args_deduped
    +
    +        def __hash__(self):
    +            return hash(frozenset(_value_and_type_iter(self.__args__)))
    +
    +    class _LiteralForm(_ExtensionsSpecialForm, _root=True):
    +        def __init__(self, doc: str):
    +            self._name = 'Literal'
    +            self._doc = self.__doc__ = doc
    +
    +        def __getitem__(self, parameters):
    +            if not isinstance(parameters, tuple):
    +                parameters = (parameters,)
    +
    +            parameters = _flatten_literal_params(parameters)
    +
    +            val_type_pairs = list(_value_and_type_iter(parameters))
    +            try:
    +                deduped_pairs = set(val_type_pairs)
    +            except TypeError:
    +                # unhashable parameters
    +                pass
    +            else:
    +                # similar logic to typing._deduplicate on Python 3.9+
    +                if len(deduped_pairs) < len(val_type_pairs):
    +                    new_parameters = []
    +                    for pair in val_type_pairs:
    +                        if pair in deduped_pairs:
    +                            new_parameters.append(pair[0])
    +                            deduped_pairs.remove(pair)
    +                    assert not deduped_pairs, deduped_pairs
    +                    parameters = tuple(new_parameters)
    +
    +            return _LiteralGenericAlias(self, parameters)
    +
    +    Literal = _LiteralForm(doc="""\
    +                           A type that can be used to indicate to type checkers
    +                           that the corresponding value has a value literally equivalent
    +                           to the provided parameter. For example:
    +
    +                               var: Literal[4] = 4
    +
    +                           The type checker understands that 'var' is literally equal to
    +                           the value 4 and no other value.
    +
    +                           Literal[...] cannot be subclassed. There is no runtime
    +                           checking verifying that the parameter is actually a value
    +                           instead of a type.""")
    +
    +
    +_overload_dummy = typing._overload_dummy
    +
    +
    +if hasattr(typing, "get_overloads"):  # 3.11+
    +    overload = typing.overload
    +    get_overloads = typing.get_overloads
    +    clear_overloads = typing.clear_overloads
    +else:
    +    # {module: {qualname: {firstlineno: func}}}
    +    _overload_registry = collections.defaultdict(
    +        functools.partial(collections.defaultdict, dict)
    +    )
    +
    +    def overload(func):
    +        """Decorator for overloaded functions/methods.
    +
    +        In a stub file, place two or more stub definitions for the same
    +        function in a row, each decorated with @overload.  For example:
    +
    +        @overload
    +        def utf8(value: None) -> None: ...
    +        @overload
    +        def utf8(value: bytes) -> bytes: ...
    +        @overload
    +        def utf8(value: str) -> bytes: ...
    +
    +        In a non-stub file (i.e. a regular .py file), do the same but
    +        follow it with an implementation.  The implementation should *not*
    +        be decorated with @overload.  For example:
    +
    +        @overload
    +        def utf8(value: None) -> None: ...
    +        @overload
    +        def utf8(value: bytes) -> bytes: ...
    +        @overload
    +        def utf8(value: str) -> bytes: ...
    +        def utf8(value):
    +            # implementation goes here
    +
    +        The overloads for a function can be retrieved at runtime using the
    +        get_overloads() function.
    +        """
    +        # classmethod and staticmethod
    +        f = getattr(func, "__func__", func)
    +        try:
    +            _overload_registry[f.__module__][f.__qualname__][
    +                f.__code__.co_firstlineno
    +            ] = func
    +        except AttributeError:
    +            # Not a normal function; ignore.
    +            pass
    +        return _overload_dummy
    +
    +    def get_overloads(func):
    +        """Return all defined overloads for *func* as a sequence."""
    +        # classmethod and staticmethod
    +        f = getattr(func, "__func__", func)
    +        if f.__module__ not in _overload_registry:
    +            return []
    +        mod_dict = _overload_registry[f.__module__]
    +        if f.__qualname__ not in mod_dict:
    +            return []
    +        return list(mod_dict[f.__qualname__].values())
    +
    +    def clear_overloads():
    +        """Clear all overloads in the registry."""
    +        _overload_registry.clear()
    +
    +
    +# This is not a real generic class.  Don't use outside annotations.
    +Type = typing.Type
    +
    +# Various ABCs mimicking those in collections.abc.
    +# A few are simply re-exported for completeness.
    +Awaitable = typing.Awaitable
    +Coroutine = typing.Coroutine
    +AsyncIterable = typing.AsyncIterable
    +AsyncIterator = typing.AsyncIterator
    +Deque = typing.Deque
    +DefaultDict = typing.DefaultDict
    +OrderedDict = typing.OrderedDict
    +Counter = typing.Counter
    +ChainMap = typing.ChainMap
    +Text = typing.Text
    +TYPE_CHECKING = typing.TYPE_CHECKING
    +
    +
    +# Breakpoint: https://github.com/python/cpython/pull/118681
    +if sys.version_info >= (3, 13, 0, "beta"):
    +    from typing import AsyncContextManager, AsyncGenerator, ContextManager, Generator
    +else:
    +    def _is_dunder(attr):
    +        return attr.startswith('__') and attr.endswith('__')
    +
    +
    +    class _SpecialGenericAlias(typing._SpecialGenericAlias, _root=True):
    +        def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()):
    +            super().__init__(origin, nparams, inst=inst, name=name)
    +            self._defaults = defaults
    +
    +        def __setattr__(self, attr, val):
    +            allowed_attrs = {'_name', '_inst', '_nparams', '_defaults'}
    +            if _is_dunder(attr) or attr in allowed_attrs:
    +                object.__setattr__(self, attr, val)
    +            else:
    +                setattr(self.__origin__, attr, val)
    +
    +        @typing._tp_cache
    +        def __getitem__(self, params):
    +            if not isinstance(params, tuple):
    +                params = (params,)
    +            msg = "Parameters to generic types must be types."
    +            params = tuple(typing._type_check(p, msg) for p in params)
    +            if (
    +                self._defaults
    +                and len(params) < self._nparams
    +                and len(params) + len(self._defaults) >= self._nparams
    +            ):
    +                params = (*params, *self._defaults[len(params) - self._nparams:])
    +            actual_len = len(params)
    +
    +            if actual_len != self._nparams:
    +                if self._defaults:
    +                    expected = f"at least {self._nparams - len(self._defaults)}"
    +                else:
    +                    expected = str(self._nparams)
    +                if not self._nparams:
    +                    raise TypeError(f"{self} is not a generic class")
    +                raise TypeError(
    +                    f"Too {'many' if actual_len > self._nparams else 'few'}"
    +                    f" arguments for {self};"
    +                    f" actual {actual_len}, expected {expected}"
    +                )
    +            return self.copy_with(params)
    +
    +    _NoneType = type(None)
    +    Generator = _SpecialGenericAlias(
    +        collections.abc.Generator, 3, defaults=(_NoneType, _NoneType)
    +    )
    +    AsyncGenerator = _SpecialGenericAlias(
    +        collections.abc.AsyncGenerator, 2, defaults=(_NoneType,)
    +    )
    +    ContextManager = _SpecialGenericAlias(
    +        contextlib.AbstractContextManager,
    +        2,
    +        name="ContextManager",
    +        defaults=(typing.Optional[bool],)
    +    )
    +    AsyncContextManager = _SpecialGenericAlias(
    +        contextlib.AbstractAsyncContextManager,
    +        2,
    +        name="AsyncContextManager",
    +        defaults=(typing.Optional[bool],)
    +    )
    +
    +
    +_PROTO_ALLOWLIST = {
    +    'collections.abc': [
    +        'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable',
    +        'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer',
    +    ],
    +    'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'],
    +    'typing_extensions': ['Buffer'],
    +}
    +
    +
    +_EXCLUDED_ATTRS = frozenset(typing.EXCLUDED_ATTRIBUTES) | {
    +    "__match_args__", "__protocol_attrs__", "__non_callable_proto_members__",
    +    "__final__",
    +}
    +
    +
    +def _get_protocol_attrs(cls):
    +    attrs = set()
    +    for base in cls.__mro__[:-1]:  # without object
    +        if base.__name__ in {'Protocol', 'Generic'}:
    +            continue
    +        annotations = getattr(base, '__annotations__', {})
    +        for attr in (*base.__dict__, *annotations):
    +            if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS):
    +                attrs.add(attr)
    +    return attrs
    +
    +
    +def _caller(depth=1, default='__main__'):
    +    try:
    +        return sys._getframemodulename(depth + 1) or default
    +    except AttributeError:  # For platforms without _getframemodulename()
    +        pass
    +    try:
    +        return sys._getframe(depth + 1).f_globals.get('__name__', default)
    +    except (AttributeError, ValueError):  # For platforms without _getframe()
    +        pass
    +    return None
    +
    +
    +# `__match_args__` attribute was removed from protocol members in 3.13,
    +# we want to backport this change to older Python versions.
    +# Breakpoint: https://github.com/python/cpython/pull/110683
    +if sys.version_info >= (3, 13):
    +    Protocol = typing.Protocol
    +else:
    +    def _allow_reckless_class_checks(depth=2):
    +        """Allow instance and class checks for special stdlib modules.
    +        The abc and functools modules indiscriminately call isinstance() and
    +        issubclass() on the whole MRO of a user class, which may contain protocols.
    +        """
    +        return _caller(depth) in {'abc', 'functools', None}
    +
    +    def _no_init(self, *args, **kwargs):
    +        if type(self)._is_protocol:
    +            raise TypeError('Protocols cannot be instantiated')
    +
    +    def _type_check_issubclass_arg_1(arg):
    +        """Raise TypeError if `arg` is not an instance of `type`
    +        in `issubclass(arg, )`.
    +
    +        In most cases, this is verified by type.__subclasscheck__.
    +        Checking it again unnecessarily would slow down issubclass() checks,
    +        so, we don't perform this check unless we absolutely have to.
    +
    +        For various error paths, however,
    +        we want to ensure that *this* error message is shown to the user
    +        where relevant, rather than a typing.py-specific error message.
    +        """
    +        if not isinstance(arg, type):
    +            # Same error message as for issubclass(1, int).
    +            raise TypeError('issubclass() arg 1 must be a class')
    +
    +    # Inheriting from typing._ProtocolMeta isn't actually desirable,
    +    # but is necessary to allow typing.Protocol and typing_extensions.Protocol
    +    # to mix without getting TypeErrors about "metaclass conflict"
    +    class _ProtocolMeta(type(typing.Protocol)):
    +        # This metaclass is somewhat unfortunate,
    +        # but is necessary for several reasons...
    +        #
    +        # NOTE: DO NOT call super() in any methods in this class
    +        # That would call the methods on typing._ProtocolMeta on Python <=3.11
    +        # and those are slow
    +        def __new__(mcls, name, bases, namespace, **kwargs):
    +            if name == "Protocol" and len(bases) < 2:
    +                pass
    +            elif {Protocol, typing.Protocol} & set(bases):
    +                for base in bases:
    +                    if not (
    +                        base in {object, typing.Generic, Protocol, typing.Protocol}
    +                        or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, [])
    +                        or is_protocol(base)
    +                    ):
    +                        raise TypeError(
    +                            f"Protocols can only inherit from other protocols, "
    +                            f"got {base!r}"
    +                        )
    +            return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs)
    +
    +        def __init__(cls, *args, **kwargs):
    +            abc.ABCMeta.__init__(cls, *args, **kwargs)
    +            if getattr(cls, "_is_protocol", False):
    +                cls.__protocol_attrs__ = _get_protocol_attrs(cls)
    +
    +        def __subclasscheck__(cls, other):
    +            if cls is Protocol:
    +                return type.__subclasscheck__(cls, other)
    +            if (
    +                getattr(cls, '_is_protocol', False)
    +                and not _allow_reckless_class_checks()
    +            ):
    +                if not getattr(cls, '_is_runtime_protocol', False):
    +                    _type_check_issubclass_arg_1(other)
    +                    raise TypeError(
    +                        "Instance and class checks can only be used with "
    +                        "@runtime_checkable protocols"
    +                    )
    +                if (
    +                    # this attribute is set by @runtime_checkable:
    +                    cls.__non_callable_proto_members__
    +                    and cls.__dict__.get("__subclasshook__") is _proto_hook
    +                ):
    +                    _type_check_issubclass_arg_1(other)
    +                    non_method_attrs = sorted(cls.__non_callable_proto_members__)
    +                    raise TypeError(
    +                        "Protocols with non-method members don't support issubclass()."
    +                        f" Non-method members: {str(non_method_attrs)[1:-1]}."
    +                    )
    +            return abc.ABCMeta.__subclasscheck__(cls, other)
    +
    +        def __instancecheck__(cls, instance):
    +            # We need this method for situations where attributes are
    +            # assigned in __init__.
    +            if cls is Protocol:
    +                return type.__instancecheck__(cls, instance)
    +            if not getattr(cls, "_is_protocol", False):
    +                # i.e., it's a concrete subclass of a protocol
    +                return abc.ABCMeta.__instancecheck__(cls, instance)
    +
    +            if (
    +                not getattr(cls, '_is_runtime_protocol', False) and
    +                not _allow_reckless_class_checks()
    +            ):
    +                raise TypeError("Instance and class checks can only be used with"
    +                                " @runtime_checkable protocols")
    +
    +            if abc.ABCMeta.__instancecheck__(cls, instance):
    +                return True
    +
    +            for attr in cls.__protocol_attrs__:
    +                try:
    +                    val = inspect.getattr_static(instance, attr)
    +                except AttributeError:
    +                    break
    +                # this attribute is set by @runtime_checkable:
    +                if val is None and attr not in cls.__non_callable_proto_members__:
    +                    break
    +            else:
    +                return True
    +
    +            return False
    +
    +        def __eq__(cls, other):
    +            # Hack so that typing.Generic.__class_getitem__
    +            # treats typing_extensions.Protocol
    +            # as equivalent to typing.Protocol
    +            if abc.ABCMeta.__eq__(cls, other) is True:
    +                return True
    +            return cls is Protocol and other is typing.Protocol
    +
    +        # This has to be defined, or the abc-module cache
    +        # complains about classes with this metaclass being unhashable,
    +        # if we define only __eq__!
    +        def __hash__(cls) -> int:
    +            return type.__hash__(cls)
    +
    +    @classmethod
    +    def _proto_hook(cls, other):
    +        if not cls.__dict__.get('_is_protocol', False):
    +            return NotImplemented
    +
    +        for attr in cls.__protocol_attrs__:
    +            for base in other.__mro__:
    +                # Check if the members appears in the class dictionary...
    +                if attr in base.__dict__:
    +                    if base.__dict__[attr] is None:
    +                        return NotImplemented
    +                    break
    +
    +                # ...or in annotations, if it is a sub-protocol.
    +                annotations = getattr(base, '__annotations__', {})
    +                if (
    +                    isinstance(annotations, collections.abc.Mapping)
    +                    and attr in annotations
    +                    and is_protocol(other)
    +                ):
    +                    break
    +            else:
    +                return NotImplemented
    +        return True
    +
    +    class Protocol(typing.Generic, metaclass=_ProtocolMeta):
    +        __doc__ = typing.Protocol.__doc__
    +        __slots__ = ()
    +        _is_protocol = True
    +        _is_runtime_protocol = False
    +
    +        def __init_subclass__(cls, *args, **kwargs):
    +            super().__init_subclass__(*args, **kwargs)
    +
    +            # Determine if this is a protocol or a concrete subclass.
    +            if not cls.__dict__.get('_is_protocol', False):
    +                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
    +
    +            # Set (or override) the protocol subclass hook.
    +            if '__subclasshook__' not in cls.__dict__:
    +                cls.__subclasshook__ = _proto_hook
    +
    +            # Prohibit instantiation for protocol classes
    +            if cls._is_protocol and cls.__init__ is Protocol.__init__:
    +                cls.__init__ = _no_init
    +
    +
    +# Breakpoint: https://github.com/python/cpython/pull/113401
    +if sys.version_info >= (3, 13):
    +    runtime_checkable = typing.runtime_checkable
    +else:
    +    def runtime_checkable(cls):
    +        """Mark a protocol class as a runtime protocol.
    +
    +        Such protocol can be used with isinstance() and issubclass().
    +        Raise TypeError if applied to a non-protocol class.
    +        This allows a simple-minded structural check very similar to
    +        one trick ponies in collections.abc such as Iterable.
    +
    +        For example::
    +
    +            @runtime_checkable
    +            class Closable(Protocol):
    +                def close(self): ...
    +
    +            assert isinstance(open('/some/file'), Closable)
    +
    +        Warning: this will check only the presence of the required methods,
    +        not their type signatures!
    +        """
    +        if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False):
    +            raise TypeError(f'@runtime_checkable can be only applied to protocol classes,'
    +                            f' got {cls!r}')
    +        cls._is_runtime_protocol = True
    +
    +        # typing.Protocol classes on <=3.11 break if we execute this block,
    +        # because typing.Protocol classes on <=3.11 don't have a
    +        # `__protocol_attrs__` attribute, and this block relies on the
    +        # `__protocol_attrs__` attribute. Meanwhile, typing.Protocol classes on 3.12.2+
    +        # break if we *don't* execute this block, because *they* assume that all
    +        # protocol classes have a `__non_callable_proto_members__` attribute
    +        # (which this block sets)
    +        if isinstance(cls, _ProtocolMeta) or sys.version_info >= (3, 12, 2):
    +            # PEP 544 prohibits using issubclass()
    +            # with protocols that have non-method members.
    +            # See gh-113320 for why we compute this attribute here,
    +            # rather than in `_ProtocolMeta.__init__`
    +            cls.__non_callable_proto_members__ = set()
    +            for attr in cls.__protocol_attrs__:
    +                try:
    +                    is_callable = callable(getattr(cls, attr, None))
    +                except Exception as e:
    +                    raise TypeError(
    +                        f"Failed to determine whether protocol member {attr!r} "
    +                        "is a method member"
    +                    ) from e
    +                else:
    +                    if not is_callable:
    +                        cls.__non_callable_proto_members__.add(attr)
    +
    +        return cls
    +
    +
    +# The "runtime" alias exists for backwards compatibility.
    +runtime = runtime_checkable
    +
    +
    +# Our version of runtime-checkable protocols is faster on Python <=3.11
    +# Breakpoint: https://github.com/python/cpython/pull/112717
    +if sys.version_info >= (3, 12):
    +    SupportsInt = typing.SupportsInt
    +    SupportsFloat = typing.SupportsFloat
    +    SupportsComplex = typing.SupportsComplex
    +    SupportsBytes = typing.SupportsBytes
    +    SupportsIndex = typing.SupportsIndex
    +    SupportsAbs = typing.SupportsAbs
    +    SupportsRound = typing.SupportsRound
    +else:
    +    @runtime_checkable
    +    class SupportsInt(Protocol):
    +        """An ABC with one abstract method __int__."""
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __int__(self) -> int:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsFloat(Protocol):
    +        """An ABC with one abstract method __float__."""
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __float__(self) -> float:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsComplex(Protocol):
    +        """An ABC with one abstract method __complex__."""
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __complex__(self) -> complex:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsBytes(Protocol):
    +        """An ABC with one abstract method __bytes__."""
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __bytes__(self) -> bytes:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsIndex(Protocol):
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __index__(self) -> int:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsAbs(Protocol[T_co]):
    +        """
    +        An ABC with one abstract method __abs__ that is covariant in its return type.
    +        """
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __abs__(self) -> T_co:
    +            pass
    +
    +    @runtime_checkable
    +    class SupportsRound(Protocol[T_co]):
    +        """
    +        An ABC with one abstract method __round__ that is covariant in its return type.
    +        """
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def __round__(self, ndigits: int = 0) -> T_co:
    +            pass
    +
    +
    +if hasattr(io, "Reader") and hasattr(io, "Writer"):
    +    Reader = io.Reader
    +    Writer = io.Writer
    +else:
    +    @runtime_checkable
    +    class Reader(Protocol[T_co]):
    +        """Protocol for simple I/O reader instances.
    +
    +        This protocol only supports blocking I/O.
    +        """
    +
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def read(self, size: int = ..., /) -> T_co:
    +            """Read data from the input stream and return it.
    +
    +            If *size* is specified, at most *size* items (bytes/characters) will be
    +            read.
    +            """
    +
    +    @runtime_checkable
    +    class Writer(Protocol[T_contra]):
    +        """Protocol for simple I/O writer instances.
    +
    +        This protocol only supports blocking I/O.
    +        """
    +
    +        __slots__ = ()
    +
    +        @abc.abstractmethod
    +        def write(self, data: T_contra, /) -> int:
    +            """Write *data* to the output stream and return the number of items written."""  # noqa: E501
    +
    +
    +_NEEDS_SINGLETONMETA = (
    +    not hasattr(typing, "NoDefault") or not hasattr(typing, "NoExtraItems")
    +)
    +
    +if _NEEDS_SINGLETONMETA:
    +    class SingletonMeta(type):
    +        def __setattr__(cls, attr, value):
    +            # TypeError is consistent with the behavior of NoneType
    +            raise TypeError(
    +                f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}"
    +            )
    +
    +
    +if hasattr(typing, "NoDefault"):
    +    NoDefault = typing.NoDefault
    +else:
    +    class NoDefaultType(metaclass=SingletonMeta):
    +        """The type of the NoDefault singleton."""
    +
    +        __slots__ = ()
    +
    +        def __new__(cls):
    +            return globals().get("NoDefault") or object.__new__(cls)
    +
    +        def __repr__(self):
    +            return "typing_extensions.NoDefault"
    +
    +        def __reduce__(self):
    +            return "NoDefault"
    +
    +    NoDefault = NoDefaultType()
    +    del NoDefaultType
    +
    +if hasattr(typing, "NoExtraItems"):
    +    NoExtraItems = typing.NoExtraItems
    +else:
    +    class NoExtraItemsType(metaclass=SingletonMeta):
    +        """The type of the NoExtraItems singleton."""
    +
    +        __slots__ = ()
    +
    +        def __new__(cls):
    +            return globals().get("NoExtraItems") or object.__new__(cls)
    +
    +        def __repr__(self):
    +            return "typing_extensions.NoExtraItems"
    +
    +        def __reduce__(self):
    +            return "NoExtraItems"
    +
    +    NoExtraItems = NoExtraItemsType()
    +    del NoExtraItemsType
    +
    +if _NEEDS_SINGLETONMETA:
    +    del SingletonMeta
    +
    +
    +# Update this to something like >=3.13.0b1 if and when
    +# PEP 728 is implemented in CPython
    +_PEP_728_IMPLEMENTED = False
    +
    +if _PEP_728_IMPLEMENTED:
    +    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
    +    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
    +    # The standard library TypedDict below Python 3.11 does not store runtime
    +    # information about optional and required keys when using Required or NotRequired.
    +    # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
    +    # Aaaand on 3.12 we add __orig_bases__ to TypedDict
    +    # to enable better runtime introspection.
    +    # On 3.13 we deprecate some odd ways of creating TypedDicts.
    +    # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier.
    +    # PEP 728 (still pending) makes more changes.
    +    TypedDict = typing.TypedDict
    +    _TypedDictMeta = typing._TypedDictMeta
    +    is_typeddict = typing.is_typeddict
    +else:
    +    # 3.10.0 and later
    +    _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters
    +
    +    def _get_typeddict_qualifiers(annotation_type):
    +        while True:
    +            annotation_origin = get_origin(annotation_type)
    +            if annotation_origin is Annotated:
    +                annotation_args = get_args(annotation_type)
    +                if annotation_args:
    +                    annotation_type = annotation_args[0]
    +                else:
    +                    break
    +            elif annotation_origin is Required:
    +                yield Required
    +                annotation_type, = get_args(annotation_type)
    +            elif annotation_origin is NotRequired:
    +                yield NotRequired
    +                annotation_type, = get_args(annotation_type)
    +            elif annotation_origin is ReadOnly:
    +                yield ReadOnly
    +                annotation_type, = get_args(annotation_type)
    +            else:
    +                break
    +
    +    class _TypedDictMeta(type):
    +
    +        def __new__(cls, name, bases, ns, *, total=True, closed=None,
    +                    extra_items=NoExtraItems):
    +            """Create new typed dict class object.
    +
    +            This method is called when TypedDict is subclassed,
    +            or when TypedDict is instantiated. This way
    +            TypedDict supports all three syntax forms described in its docstring.
    +            Subclasses and instances of TypedDict return actual dictionaries.
    +            """
    +            for base in bases:
    +                if type(base) is not _TypedDictMeta and base is not typing.Generic:
    +                    raise TypeError('cannot inherit from both a TypedDict type '
    +                                    'and a non-TypedDict base class')
    +            if closed is not None and extra_items is not NoExtraItems:
    +                raise TypeError(f"Cannot combine closed={closed!r} and extra_items")
    +
    +            if any(issubclass(b, typing.Generic) for b in bases):
    +                generic_base = (typing.Generic,)
    +            else:
    +                generic_base = ()
    +
    +            ns_annotations = ns.pop('__annotations__', None)
    +
    +            # typing.py generally doesn't let you inherit from plain Generic, unless
    +            # the name of the class happens to be "Protocol"
    +            tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns)
    +            tp_dict.__name__ = name
    +            if tp_dict.__qualname__ == "Protocol":
    +                tp_dict.__qualname__ = name
    +
    +            if not hasattr(tp_dict, '__orig_bases__'):
    +                tp_dict.__orig_bases__ = bases
    +
    +            annotations = {}
    +            own_annotate = None
    +            if ns_annotations is not None:
    +                own_annotations = ns_annotations
    +            elif sys.version_info >= (3, 14):
    +                if hasattr(annotationlib, "get_annotate_from_class_namespace"):
    +                    own_annotate = annotationlib.get_annotate_from_class_namespace(ns)
    +                else:
    +                    # 3.14.0a7 and earlier
    +                    own_annotate = ns.get("__annotate__")
    +                if own_annotate is not None:
    +                    own_annotations = annotationlib.call_annotate_function(
    +                        own_annotate, Format.FORWARDREF, owner=tp_dict
    +                    )
    +                else:
    +                    own_annotations = {}
    +            else:
    +                own_annotations = {}
    +            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
    +            if _TAKES_MODULE:
    +                own_checked_annotations = {
    +                    n: typing._type_check(tp, msg, module=tp_dict.__module__)
    +                    for n, tp in own_annotations.items()
    +                }
    +            else:
    +                own_checked_annotations = {
    +                    n: typing._type_check(tp, msg)
    +                    for n, tp in own_annotations.items()
    +                }
    +            required_keys = set()
    +            optional_keys = set()
    +            readonly_keys = set()
    +            mutable_keys = set()
    +            extra_items_type = extra_items
    +
    +            for base in bases:
    +                base_dict = base.__dict__
    +
    +                if sys.version_info <= (3, 14):
    +                    annotations.update(base_dict.get('__annotations__', {}))
    +                required_keys.update(base_dict.get('__required_keys__', ()))
    +                optional_keys.update(base_dict.get('__optional_keys__', ()))
    +                readonly_keys.update(base_dict.get('__readonly_keys__', ()))
    +                mutable_keys.update(base_dict.get('__mutable_keys__', ()))
    +
    +            # This was specified in an earlier version of PEP 728. Support
    +            # is retained for backwards compatibility, but only for Python
    +            # 3.13 and lower.
    +            if (closed and sys.version_info < (3, 14)
    +                       and "__extra_items__" in own_checked_annotations):
    +                annotation_type = own_checked_annotations.pop("__extra_items__")
    +                qualifiers = set(_get_typeddict_qualifiers(annotation_type))
    +                if Required in qualifiers:
    +                    raise TypeError(
    +                        "Special key __extra_items__ does not support "
    +                        "Required"
    +                    )
    +                if NotRequired in qualifiers:
    +                    raise TypeError(
    +                        "Special key __extra_items__ does not support "
    +                        "NotRequired"
    +                    )
    +                extra_items_type = annotation_type
    +
    +            annotations.update(own_checked_annotations)
    +            for annotation_key, annotation_type in own_checked_annotations.items():
    +                qualifiers = set(_get_typeddict_qualifiers(annotation_type))
    +
    +                if Required in qualifiers:
    +                    required_keys.add(annotation_key)
    +                elif NotRequired in qualifiers:
    +                    optional_keys.add(annotation_key)
    +                elif total:
    +                    required_keys.add(annotation_key)
    +                else:
    +                    optional_keys.add(annotation_key)
    +                if ReadOnly in qualifiers:
    +                    mutable_keys.discard(annotation_key)
    +                    readonly_keys.add(annotation_key)
    +                else:
    +                    mutable_keys.add(annotation_key)
    +                    readonly_keys.discard(annotation_key)
    +
    +            # Breakpoint: https://github.com/python/cpython/pull/119891
    +            if sys.version_info >= (3, 14):
    +                def __annotate__(format):
    +                    annos = {}
    +                    for base in bases:
    +                        if base is Generic:
    +                            continue
    +                        base_annotate = base.__annotate__
    +                        if base_annotate is None:
    +                            continue
    +                        base_annos = annotationlib.call_annotate_function(
    +                            base_annotate, format, owner=base)
    +                        annos.update(base_annos)
    +                    if own_annotate is not None:
    +                        own = annotationlib.call_annotate_function(
    +                            own_annotate, format, owner=tp_dict)
    +                        if format != Format.STRING:
    +                            own = {
    +                                n: typing._type_check(tp, msg, module=tp_dict.__module__)
    +                                for n, tp in own.items()
    +                            }
    +                    elif format == Format.STRING:
    +                        own = annotationlib.annotations_to_string(own_annotations)
    +                    elif format in (Format.FORWARDREF, Format.VALUE):
    +                        own = own_checked_annotations
    +                    else:
    +                        raise NotImplementedError(format)
    +                    annos.update(own)
    +                    return annos
    +
    +                tp_dict.__annotate__ = __annotate__
    +            else:
    +                tp_dict.__annotations__ = annotations
    +            tp_dict.__required_keys__ = frozenset(required_keys)
    +            tp_dict.__optional_keys__ = frozenset(optional_keys)
    +            tp_dict.__readonly_keys__ = frozenset(readonly_keys)
    +            tp_dict.__mutable_keys__ = frozenset(mutable_keys)
    +            tp_dict.__total__ = total
    +            tp_dict.__closed__ = closed
    +            tp_dict.__extra_items__ = extra_items_type
    +            return tp_dict
    +
    +        __call__ = dict  # static method
    +
    +        def __subclasscheck__(cls, other):
    +            # Typed dicts are only for static structural subtyping.
    +            raise TypeError('TypedDict does not support instance and class checks')
    +
    +        __instancecheck__ = __subclasscheck__
    +
    +    _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
    +
    +    def _create_typeddict(
    +        typename,
    +        fields,
    +        /,
    +        *,
    +        typing_is_inline,
    +        total,
    +        closed,
    +        extra_items,
    +        **kwargs,
    +    ):
    +        if fields is _marker or fields is None:
    +            if fields is _marker:
    +                deprecated_thing = (
    +                    "Failing to pass a value for the 'fields' parameter"
    +                )
    +            else:
    +                deprecated_thing = "Passing `None` as the 'fields' parameter"
    +
    +            example = f"`{typename} = TypedDict({typename!r}, {{}})`"
    +            deprecation_msg = (
    +                f"{deprecated_thing} is deprecated and will be disallowed in "
    +                "Python 3.15. To create a TypedDict class with 0 fields "
    +                "using the functional syntax, pass an empty dictionary, e.g. "
    +            ) + example + "."
    +            warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
    +            # Support a field called "closed"
    +            if closed is not False and closed is not True and closed is not None:
    +                kwargs["closed"] = closed
    +                closed = None
    +            # Or "extra_items"
    +            if extra_items is not NoExtraItems:
    +                kwargs["extra_items"] = extra_items
    +                extra_items = NoExtraItems
    +            fields = kwargs
    +        elif kwargs:
    +            raise TypeError("TypedDict takes either a dict or keyword arguments,"
    +                            " but not both")
    +        if kwargs:
    +            # Breakpoint: https://github.com/python/cpython/pull/104891
    +            if sys.version_info >= (3, 13):
    +                raise TypeError("TypedDict takes no keyword arguments")
    +            warnings.warn(
    +                "The kwargs-based syntax for TypedDict definitions is deprecated "
    +                "in Python 3.11, will be removed in Python 3.13, and may not be "
    +                "understood by third-party type checkers.",
    +                DeprecationWarning,
    +                stacklevel=2,
    +            )
    +
    +        ns = {'__annotations__': dict(fields)}
    +        module = _caller(depth=4 if typing_is_inline else 2)
    +        if module is not None:
    +            # Setting correct module is necessary to make typed dict classes
    +            # pickleable.
    +            ns['__module__'] = module
    +
    +        td = _TypedDictMeta(typename, (), ns, total=total, closed=closed,
    +                            extra_items=extra_items)
    +        td.__orig_bases__ = (TypedDict,)
    +        return td
    +
    +    class _TypedDictSpecialForm(_SpecialForm, _root=True):
    +        def __call__(
    +            self,
    +            typename,
    +            fields=_marker,
    +            /,
    +            *,
    +            total=True,
    +            closed=None,
    +            extra_items=NoExtraItems,
    +            **kwargs
    +        ):
    +            return _create_typeddict(
    +                typename,
    +                fields,
    +                typing_is_inline=False,
    +                total=total,
    +                closed=closed,
    +                extra_items=extra_items,
    +                **kwargs,
    +            )
    +
    +        def __mro_entries__(self, bases):
    +            return (_TypedDict,)
    +
    +    @_TypedDictSpecialForm
    +    def TypedDict(self, args):
    +        """A simple typed namespace. At runtime it is equivalent to a plain dict.
    +
    +        TypedDict creates a dictionary type such that a type checker will expect all
    +        instances to have a certain set of keys, where each key is
    +        associated with a value of a consistent type. This expectation
    +        is not checked at runtime.
    +
    +        Usage::
    +
    +            class Point2D(TypedDict):
    +                x: int
    +                y: int
    +                label: str
    +
    +            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
    +            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
    +
    +            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
    +
    +        The type info can be accessed via the Point2D.__annotations__ dict, and
    +        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
    +        TypedDict supports an additional equivalent form::
    +
    +            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
    +
    +        By default, all keys must be present in a TypedDict. It is possible
    +        to override this by specifying totality::
    +
    +            class Point2D(TypedDict, total=False):
    +                x: int
    +                y: int
    +
    +        This means that a Point2D TypedDict can have any of the keys omitted. A type
    +        checker is only expected to support a literal False or True as the value of
    +        the total argument. True is the default, and makes all items defined in the
    +        class body be required.
    +
    +        The Required and NotRequired special forms can also be used to mark
    +        individual keys as being required or not required::
    +
    +            class Point2D(TypedDict):
    +                x: int  # the "x" key must always be present (Required is the default)
    +                y: NotRequired[int]  # the "y" key can be omitted
    +
    +        See PEP 655 for more details on Required and NotRequired.
    +        """
    +        # This runs when creating inline TypedDicts:
    +        if not isinstance(args, dict):
    +            raise TypeError(
    +                "TypedDict[...] should be used with a single dict argument"
    +            )
    +
    +        return _create_typeddict(
    +            "",
    +            args,
    +            typing_is_inline=True,
    +            total=True,
    +            closed=True,
    +            extra_items=NoExtraItems,
    +        )
    +
    +    _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
    +
    +    def is_typeddict(tp):
    +        """Check if an annotation is a TypedDict class
    +
    +        For example::
    +            class Film(TypedDict):
    +                title: str
    +                year: int
    +
    +            is_typeddict(Film)  # => True
    +            is_typeddict(Union[list, str])  # => False
    +        """
    +        return isinstance(tp, _TYPEDDICT_TYPES)
    +
    +
    +if hasattr(typing, "assert_type"):
    +    assert_type = typing.assert_type
    +
    +else:
    +    def assert_type(val, typ, /):
    +        """Assert (to the type checker) that the value is of the given type.
    +
    +        When the type checker encounters a call to assert_type(), it
    +        emits an error if the value is not of the specified type::
    +
    +            def greet(name: str) -> None:
    +                assert_type(name, str)  # ok
    +                assert_type(name, int)  # type checker error
    +
    +        At runtime this returns the first argument unchanged and otherwise
    +        does nothing.
    +        """
    +        return val
    +
    +
    +if hasattr(typing, "ReadOnly"):  # 3.13+
    +    get_type_hints = typing.get_type_hints
    +else:  # <=3.13
    +    # replaces _strip_annotations()
    +    def _strip_extras(t):
    +        """Strips Annotated, Required and NotRequired from a given type."""
    +        if isinstance(t, typing._AnnotatedAlias):
    +            return _strip_extras(t.__origin__)
    +        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly):
    +            return _strip_extras(t.__args__[0])
    +        if isinstance(t, typing._GenericAlias):
    +            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
    +            if stripped_args == t.__args__:
    +                return t
    +            return t.copy_with(stripped_args)
    +        if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias):
    +            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
    +            if stripped_args == t.__args__:
    +                return t
    +            return _types.GenericAlias(t.__origin__, stripped_args)
    +        if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType):
    +            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
    +            if stripped_args == t.__args__:
    +                return t
    +            return functools.reduce(operator.or_, stripped_args)
    +
    +        return t
    +
    +    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
    +        """Return type hints for an object.
    +
    +        This is often the same as obj.__annotations__, but it handles
    +        forward references encoded as string literals, adds Optional[t] if a
    +        default value equal to None is set and recursively replaces all
    +        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
    +        (unless 'include_extras=True').
    +
    +        The argument may be a module, class, method, or function. The annotations
    +        are returned as a dictionary. For classes, annotations include also
    +        inherited members.
    +
    +        TypeError is raised if the argument is not of a type that can contain
    +        annotations, and an empty dictionary is returned if no annotations are
    +        present.
    +
    +        BEWARE -- the behavior of globalns and localns is counterintuitive
    +        (unless you are familiar with how eval() and exec() work).  The
    +        search order is locals first, then globals.
    +
    +        - If no dict arguments are passed, an attempt is made to use the
    +          globals from obj (or the respective module's globals for classes),
    +          and these are also used as the locals.  If the object does not appear
    +          to have globals, an empty dictionary is used.
    +
    +        - If one dict argument is passed, it is used for both globals and
    +          locals.
    +
    +        - If two dict arguments are passed, they specify globals and
    +          locals, respectively.
    +        """
    +        hint = typing.get_type_hints(
    +            obj, globalns=globalns, localns=localns, include_extras=True
    +        )
    +        # Breakpoint: https://github.com/python/cpython/pull/30304
    +        if sys.version_info < (3, 11):
    +            _clean_optional(obj, hint, globalns, localns)
    +        if include_extras:
    +            return hint
    +        return {k: _strip_extras(t) for k, t in hint.items()}
    +
    +    _NoneType = type(None)
    +
    +    def _could_be_inserted_optional(t):
    +        """detects Union[..., None] pattern"""
    +        if not isinstance(t, typing._UnionGenericAlias):
    +            return False
    +        # Assume if last argument is not None they are user defined
    +        if t.__args__[-1] is not _NoneType:
    +            return False
    +        return True
    +
    +    # < 3.11
    +    def _clean_optional(obj, hints, globalns=None, localns=None):
    +        # reverts injected Union[..., None] cases from typing.get_type_hints
    +        # when a None default value is used.
    +        # see https://github.com/python/typing_extensions/issues/310
    +        if not hints or isinstance(obj, type):
    +            return
    +        defaults = typing._get_defaults(obj)  # avoid accessing __annotations___
    +        if not defaults:
    +            return
    +        original_hints = obj.__annotations__
    +        for name, value in hints.items():
    +            # Not a Union[..., None] or replacement conditions not fullfilled
    +            if (not _could_be_inserted_optional(value)
    +                or name not in defaults
    +                or defaults[name] is not None
    +            ):
    +                continue
    +            original_value = original_hints[name]
    +            # value=NoneType should have caused a skip above but check for safety
    +            if original_value is None:
    +                original_value = _NoneType
    +            # Forward reference
    +            if isinstance(original_value, str):
    +                if globalns is None:
    +                    if isinstance(obj, _types.ModuleType):
    +                        globalns = obj.__dict__
    +                    else:
    +                        nsobj = obj
    +                        # Find globalns for the unwrapped object.
    +                        while hasattr(nsobj, '__wrapped__'):
    +                            nsobj = nsobj.__wrapped__
    +                        globalns = getattr(nsobj, '__globals__', {})
    +                    if localns is None:
    +                        localns = globalns
    +                elif localns is None:
    +                    localns = globalns
    +
    +                original_value = ForwardRef(
    +                    original_value,
    +                    is_argument=not isinstance(obj, _types.ModuleType)
    +                )
    +            original_evaluated = typing._eval_type(original_value, globalns, localns)
    +            # Compare if values differ. Note that even if equal
    +            # value might be cached by typing._tp_cache contrary to original_evaluated
    +            if original_evaluated != value or (
    +                # 3.10: ForwardRefs of UnionType might be turned into _UnionGenericAlias
    +                hasattr(_types, "UnionType")
    +                and isinstance(original_evaluated, _types.UnionType)
    +                and not isinstance(value, _types.UnionType)
    +            ):
    +                hints[name] = original_evaluated
    +
    +# Python 3.9 has get_origin() and get_args() but those implementations don't support
    +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
    +# Breakpoint: https://github.com/python/cpython/pull/25298
    +if sys.version_info >= (3, 10):
    +    get_origin = typing.get_origin
    +    get_args = typing.get_args
    +# 3.9
    +else:
    +    def get_origin(tp):
    +        """Get the unsubscripted version of a type.
    +
    +        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
    +        and Annotated. Return None for unsupported types. Examples::
    +
    +            get_origin(Literal[42]) is Literal
    +            get_origin(int) is None
    +            get_origin(ClassVar[int]) is ClassVar
    +            get_origin(Generic) is Generic
    +            get_origin(Generic[T]) is Generic
    +            get_origin(Union[T, int]) is Union
    +            get_origin(List[Tuple[T, T]][int]) == list
    +            get_origin(P.args) is P
    +        """
    +        if isinstance(tp, typing._AnnotatedAlias):
    +            return Annotated
    +        if isinstance(tp, (typing._BaseGenericAlias, _types.GenericAlias,
    +                           ParamSpecArgs, ParamSpecKwargs)):
    +            return tp.__origin__
    +        if tp is typing.Generic:
    +            return typing.Generic
    +        return None
    +
    +    def get_args(tp):
    +        """Get type arguments with all substitutions performed.
    +
    +        For unions, basic simplifications used by Union constructor are performed.
    +        Examples::
    +            get_args(Dict[str, int]) == (str, int)
    +            get_args(int) == ()
    +            get_args(Union[int, Union[T, int], str][int]) == (int, str)
    +            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
    +            get_args(Callable[[], T][int]) == ([], int)
    +        """
    +        if isinstance(tp, typing._AnnotatedAlias):
    +            return (tp.__origin__, *tp.__metadata__)
    +        if isinstance(tp, (typing._GenericAlias, _types.GenericAlias)):
    +            res = tp.__args__
    +            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
    +                res = (list(res[:-1]), res[-1])
    +            return res
    +        return ()
    +
    +
    +# 3.10+
    +if hasattr(typing, 'TypeAlias'):
    +    TypeAlias = typing.TypeAlias
    +# 3.9
    +else:
    +    @_ExtensionsSpecialForm
    +    def TypeAlias(self, parameters):
    +        """Special marker indicating that an assignment should
    +        be recognized as a proper type alias definition by type
    +        checkers.
    +
    +        For example::
    +
    +            Predicate: TypeAlias = Callable[..., bool]
    +
    +        It's invalid when used anywhere except as in the example above.
    +        """
    +        raise TypeError(f"{self} is not subscriptable")
    +
    +
    +def _set_default(type_param, default):
    +    type_param.has_default = lambda: default is not NoDefault
    +    type_param.__default__ = default
    +
    +
    +def _set_module(typevarlike):
    +    # for pickling:
    +    def_mod = _caller(depth=2)
    +    if def_mod != 'typing_extensions':
    +        typevarlike.__module__ = def_mod
    +
    +
    +class _DefaultMixin:
    +    """Mixin for TypeVarLike defaults."""
    +
    +    __slots__ = ()
    +    __init__ = _set_default
    +
    +
    +# Classes using this metaclass must provide a _backported_typevarlike ClassVar
    +class _TypeVarLikeMeta(type):
    +    def __instancecheck__(cls, __instance: Any) -> bool:
    +        return isinstance(__instance, cls._backported_typevarlike)
    +
    +
    +if _PEP_696_IMPLEMENTED:
    +    from typing import TypeVar
    +else:
    +    # Add default and infer_variance parameters from PEP 696 and 695
    +    class TypeVar(metaclass=_TypeVarLikeMeta):
    +        """Type variable."""
    +
    +        _backported_typevarlike = typing.TypeVar
    +
    +        def __new__(cls, name, *constraints, bound=None,
    +                    covariant=False, contravariant=False,
    +                    default=NoDefault, infer_variance=False):
    +            if hasattr(typing, "TypeAliasType"):
    +                # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar
    +                typevar = typing.TypeVar(name, *constraints, bound=bound,
    +                                         covariant=covariant, contravariant=contravariant,
    +                                         infer_variance=infer_variance)
    +            else:
    +                typevar = typing.TypeVar(name, *constraints, bound=bound,
    +                                         covariant=covariant, contravariant=contravariant)
    +                if infer_variance and (covariant or contravariant):
    +                    raise ValueError("Variance cannot be specified with infer_variance.")
    +                typevar.__infer_variance__ = infer_variance
    +
    +            _set_default(typevar, default)
    +            _set_module(typevar)
    +
    +            def _tvar_prepare_subst(alias, args):
    +                if (
    +                    typevar.has_default()
    +                    and alias.__parameters__.index(typevar) == len(args)
    +                ):
    +                    args += (typevar.__default__,)
    +                return args
    +
    +            typevar.__typing_prepare_subst__ = _tvar_prepare_subst
    +            return typevar
    +
    +        def __init_subclass__(cls) -> None:
    +            raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type")
    +
    +
    +# Python 3.10+ has PEP 612
    +if hasattr(typing, 'ParamSpecArgs'):
    +    ParamSpecArgs = typing.ParamSpecArgs
    +    ParamSpecKwargs = typing.ParamSpecKwargs
    +# 3.9
    +else:
    +    class _Immutable:
    +        """Mixin to indicate that object should not be copied."""
    +        __slots__ = ()
    +
    +        def __copy__(self):
    +            return self
    +
    +        def __deepcopy__(self, memo):
    +            return self
    +
    +    class ParamSpecArgs(_Immutable):
    +        """The args for a ParamSpec object.
    +
    +        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
    +
    +        ParamSpecArgs objects have a reference back to their ParamSpec:
    +
    +        P.args.__origin__ is P
    +
    +        This type is meant for runtime introspection and has no special meaning to
    +        static type checkers.
    +        """
    +        def __init__(self, origin):
    +            self.__origin__ = origin
    +
    +        def __repr__(self):
    +            return f"{self.__origin__.__name__}.args"
    +
    +        def __eq__(self, other):
    +            if not isinstance(other, ParamSpecArgs):
    +                return NotImplemented
    +            return self.__origin__ == other.__origin__
    +
    +    class ParamSpecKwargs(_Immutable):
    +        """The kwargs for a ParamSpec object.
    +
    +        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
    +
    +        ParamSpecKwargs objects have a reference back to their ParamSpec:
    +
    +        P.kwargs.__origin__ is P
    +
    +        This type is meant for runtime introspection and has no special meaning to
    +        static type checkers.
    +        """
    +        def __init__(self, origin):
    +            self.__origin__ = origin
    +
    +        def __repr__(self):
    +            return f"{self.__origin__.__name__}.kwargs"
    +
    +        def __eq__(self, other):
    +            if not isinstance(other, ParamSpecKwargs):
    +                return NotImplemented
    +            return self.__origin__ == other.__origin__
    +
    +
    +if _PEP_696_IMPLEMENTED:
    +    from typing import ParamSpec
    +
    +# 3.10+
    +elif hasattr(typing, 'ParamSpec'):
    +
    +    # Add default parameter - PEP 696
    +    class ParamSpec(metaclass=_TypeVarLikeMeta):
    +        """Parameter specification."""
    +
    +        _backported_typevarlike = typing.ParamSpec
    +
    +        def __new__(cls, name, *, bound=None,
    +                    covariant=False, contravariant=False,
    +                    infer_variance=False, default=NoDefault):
    +            if hasattr(typing, "TypeAliasType"):
    +                # PEP 695 implemented, can pass infer_variance to typing.TypeVar
    +                paramspec = typing.ParamSpec(name, bound=bound,
    +                                             covariant=covariant,
    +                                             contravariant=contravariant,
    +                                             infer_variance=infer_variance)
    +            else:
    +                paramspec = typing.ParamSpec(name, bound=bound,
    +                                             covariant=covariant,
    +                                             contravariant=contravariant)
    +                paramspec.__infer_variance__ = infer_variance
    +
    +            _set_default(paramspec, default)
    +            _set_module(paramspec)
    +
    +            def _paramspec_prepare_subst(alias, args):
    +                params = alias.__parameters__
    +                i = params.index(paramspec)
    +                if i == len(args) and paramspec.has_default():
    +                    args = [*args, paramspec.__default__]
    +                if i >= len(args):
    +                    raise TypeError(f"Too few arguments for {alias}")
    +                # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.
    +                if len(params) == 1 and not typing._is_param_expr(args[0]):
    +                    assert i == 0
    +                    args = (args,)
    +                # Convert lists to tuples to help other libraries cache the results.
    +                elif isinstance(args[i], list):
    +                    args = (*args[:i], tuple(args[i]), *args[i + 1:])
    +                return args
    +
    +            paramspec.__typing_prepare_subst__ = _paramspec_prepare_subst
    +            return paramspec
    +
    +        def __init_subclass__(cls) -> None:
    +            raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type")
    +
    +# 3.9
    +else:
    +
    +    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
    +    class ParamSpec(list, _DefaultMixin):
    +        """Parameter specification variable.
    +
    +        Usage::
    +
    +           P = ParamSpec('P')
    +
    +        Parameter specification variables exist primarily for the benefit of static
    +        type checkers.  They are used to forward the parameter types of one
    +        callable to another callable, a pattern commonly found in higher order
    +        functions and decorators.  They are only valid when used in ``Concatenate``,
    +        or s the first argument to ``Callable``. In Python 3.10 and higher,
    +        they are also supported in user-defined Generics at runtime.
    +        See class Generic for more information on generic types.  An
    +        example for annotating a decorator::
    +
    +           T = TypeVar('T')
    +           P = ParamSpec('P')
    +
    +           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
    +               '''A type-safe decorator to add logging to a function.'''
    +               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
    +                   logging.info(f'{f.__name__} was called')
    +                   return f(*args, **kwargs)
    +               return inner
    +
    +           @add_logging
    +           def add_two(x: float, y: float) -> float:
    +               '''Add two numbers together.'''
    +               return x + y
    +
    +        Parameter specification variables defined with covariant=True or
    +        contravariant=True can be used to declare covariant or contravariant
    +        generic types.  These keyword arguments are valid, but their actual semantics
    +        are yet to be decided.  See PEP 612 for details.
    +
    +        Parameter specification variables can be introspected. e.g.:
    +
    +           P.__name__ == 'T'
    +           P.__bound__ == None
    +           P.__covariant__ == False
    +           P.__contravariant__ == False
    +
    +        Note that only parameter specification variables defined in global scope can
    +        be pickled.
    +        """
    +
    +        # Trick Generic __parameters__.
    +        __class__ = typing.TypeVar
    +
    +        @property
    +        def args(self):
    +            return ParamSpecArgs(self)
    +
    +        @property
    +        def kwargs(self):
    +            return ParamSpecKwargs(self)
    +
    +        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
    +                     infer_variance=False, default=NoDefault):
    +            list.__init__(self, [self])
    +            self.__name__ = name
    +            self.__covariant__ = bool(covariant)
    +            self.__contravariant__ = bool(contravariant)
    +            self.__infer_variance__ = bool(infer_variance)
    +            if bound:
    +                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
    +            else:
    +                self.__bound__ = None
    +            _DefaultMixin.__init__(self, default)
    +
    +            # for pickling:
    +            def_mod = _caller()
    +            if def_mod != 'typing_extensions':
    +                self.__module__ = def_mod
    +
    +        def __repr__(self):
    +            if self.__infer_variance__:
    +                prefix = ''
    +            elif self.__covariant__:
    +                prefix = '+'
    +            elif self.__contravariant__:
    +                prefix = '-'
    +            else:
    +                prefix = '~'
    +            return prefix + self.__name__
    +
    +        def __hash__(self):
    +            return object.__hash__(self)
    +
    +        def __eq__(self, other):
    +            return self is other
    +
    +        def __reduce__(self):
    +            return self.__name__
    +
    +        # Hack to get typing._type_check to pass.
    +        def __call__(self, *args, **kwargs):
    +            pass
    +
    +
    +# 3.9
    +if not hasattr(typing, 'Concatenate'):
    +    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
    +
    +    # 3.9.0-1
    +    if not hasattr(typing, '_type_convert'):
    +        def _type_convert(arg, module=None, *, allow_special_forms=False):
    +            """For converting None to type(None), and strings to ForwardRef."""
    +            if arg is None:
    +                return type(None)
    +            if isinstance(arg, str):
    +                if sys.version_info <= (3, 9, 6):
    +                    return ForwardRef(arg)
    +                if sys.version_info <= (3, 9, 7):
    +                    return ForwardRef(arg, module=module)
    +                return ForwardRef(arg, module=module, is_class=allow_special_forms)
    +            return arg
    +    else:
    +        _type_convert = typing._type_convert
    +
    +    class _ConcatenateGenericAlias(list):
    +
    +        # Trick Generic into looking into this for __parameters__.
    +        __class__ = typing._GenericAlias
    +
    +        def __init__(self, origin, args):
    +            super().__init__(args)
    +            self.__origin__ = origin
    +            self.__args__ = args
    +
    +        def __repr__(self):
    +            _type_repr = typing._type_repr
    +            return (f'{_type_repr(self.__origin__)}'
    +                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
    +
    +        def __hash__(self):
    +            return hash((self.__origin__, self.__args__))
    +
    +        # Hack to get typing._type_check to pass in Generic.
    +        def __call__(self, *args, **kwargs):
    +            pass
    +
    +        @property
    +        def __parameters__(self):
    +            return tuple(
    +                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
    +            )
    +
    +        # 3.9 used by __getitem__ below
    +        def copy_with(self, params):
    +            if isinstance(params[-1], _ConcatenateGenericAlias):
    +                params = (*params[:-1], *params[-1].__args__)
    +            elif isinstance(params[-1], (list, tuple)):
    +                return (*params[:-1], *params[-1])
    +            elif (not (params[-1] is ... or isinstance(params[-1], ParamSpec))):
    +                raise TypeError("The last parameter to Concatenate should be a "
    +                        "ParamSpec variable or ellipsis.")
    +            return self.__class__(self.__origin__, params)
    +
    +        # 3.9; accessed during GenericAlias.__getitem__ when substituting
    +        def __getitem__(self, args):
    +            if self.__origin__ in (Generic, Protocol):
    +                # Can't subscript Generic[...] or Protocol[...].
    +                raise TypeError(f"Cannot subscript already-subscripted {self}")
    +            if not self.__parameters__:
    +                raise TypeError(f"{self} is not a generic class")
    +
    +            if not isinstance(args, tuple):
    +                args = (args,)
    +            args = _unpack_args(*(_type_convert(p) for p in args))
    +            params = self.__parameters__
    +            for param in params:
    +                prepare = getattr(param, "__typing_prepare_subst__", None)
    +                if prepare is not None:
    +                    args = prepare(self, args)
    +                # 3.9 & typing.ParamSpec
    +                elif isinstance(param, ParamSpec):
    +                    i = params.index(param)
    +                    if (
    +                        i == len(args)
    +                        and getattr(param, '__default__', NoDefault) is not NoDefault
    +                    ):
    +                        args = [*args, param.__default__]
    +                    if i >= len(args):
    +                        raise TypeError(f"Too few arguments for {self}")
    +                    # Special case for Z[[int, str, bool]] == Z[int, str, bool]
    +                    if len(params) == 1 and not _is_param_expr(args[0]):
    +                        assert i == 0
    +                        args = (args,)
    +                    elif (
    +                        isinstance(args[i], list)
    +                        # 3.9
    +                        # This class inherits from list do not convert
    +                        and not isinstance(args[i], _ConcatenateGenericAlias)
    +                    ):
    +                        args = (*args[:i], tuple(args[i]), *args[i + 1:])
    +
    +            alen = len(args)
    +            plen = len(params)
    +            if alen != plen:
    +                raise TypeError(
    +                    f"Too {'many' if alen > plen else 'few'} arguments for {self};"
    +                    f" actual {alen}, expected {plen}"
    +                )
    +
    +            subst = dict(zip(self.__parameters__, args))
    +            # determine new args
    +            new_args = []
    +            for arg in self.__args__:
    +                if isinstance(arg, type):
    +                    new_args.append(arg)
    +                    continue
    +                if isinstance(arg, TypeVar):
    +                    arg = subst[arg]
    +                    if (
    +                        (isinstance(arg, typing._GenericAlias) and _is_unpack(arg))
    +                        or (
    +                            hasattr(_types, "GenericAlias")
    +                            and isinstance(arg, _types.GenericAlias)
    +                            and getattr(arg, "__unpacked__", False)
    +                        )
    +                    ):
    +                        raise TypeError(f"{arg} is not valid as type argument")
    +
    +                elif isinstance(arg,
    +                    typing._GenericAlias
    +                    if not hasattr(_types, "GenericAlias") else
    +                    (typing._GenericAlias, _types.GenericAlias)
    +                ):
    +                    subparams = arg.__parameters__
    +                    if subparams:
    +                        subargs = tuple(subst[x] for x in subparams)
    +                        arg = arg[subargs]
    +                new_args.append(arg)
    +            return self.copy_with(tuple(new_args))
    +
    +# 3.10+
    +else:
    +    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias
    +
    +    # 3.10
    +    if sys.version_info < (3, 11):
    +
    +        class _ConcatenateGenericAlias(typing._ConcatenateGenericAlias, _root=True):
    +            # needed for checks in collections.abc.Callable to accept this class
    +            __module__ = "typing"
    +
    +            def copy_with(self, params):
    +                if isinstance(params[-1], (list, tuple)):
    +                    return (*params[:-1], *params[-1])
    +                if isinstance(params[-1], typing._ConcatenateGenericAlias):
    +                    params = (*params[:-1], *params[-1].__args__)
    +                elif not (params[-1] is ... or isinstance(params[-1], ParamSpec)):
    +                    raise TypeError("The last parameter to Concatenate should be a "
    +                            "ParamSpec variable or ellipsis.")
    +                return super(typing._ConcatenateGenericAlias, self).copy_with(params)
    +
    +            def __getitem__(self, args):
    +                value = super().__getitem__(args)
    +                if isinstance(value, tuple) and any(_is_unpack(t) for t in value):
    +                    return tuple(_unpack_args(*(n for n in value)))
    +                return value
    +
    +
    +# 3.9.2
    +class _EllipsisDummy: ...
    +
    +
    +# <=3.10
    +def _create_concatenate_alias(origin, parameters):
    +    if parameters[-1] is ... and sys.version_info < (3, 9, 2):
    +        # Hack: Arguments must be types, replace it with one.
    +        parameters = (*parameters[:-1], _EllipsisDummy)
    +    if sys.version_info >= (3, 10, 3):
    +        concatenate = _ConcatenateGenericAlias(origin, parameters,
    +                                        _typevar_types=(TypeVar, ParamSpec),
    +                                        _paramspec_tvars=True)
    +    else:
    +        concatenate = _ConcatenateGenericAlias(origin, parameters)
    +    if parameters[-1] is not _EllipsisDummy:
    +        return concatenate
    +    # Remove dummy again
    +    concatenate.__args__ = tuple(p if p is not _EllipsisDummy else ...
    +                                    for p in concatenate.__args__)
    +    if sys.version_info < (3, 10):
    +        # backport needs __args__ adjustment only
    +        return concatenate
    +    concatenate.__parameters__ = tuple(p for p in concatenate.__parameters__
    +                                        if p is not _EllipsisDummy)
    +    return concatenate
    +
    +
    +# <=3.10
    +@typing._tp_cache
    +def _concatenate_getitem(self, parameters):
    +    if parameters == ():
    +        raise TypeError("Cannot take a Concatenate of no types.")
    +    if not isinstance(parameters, tuple):
    +        parameters = (parameters,)
    +    if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
    +        raise TypeError("The last parameter to Concatenate should be a "
    +                        "ParamSpec variable or ellipsis.")
    +    msg = "Concatenate[arg, ...]: each arg must be a type."
    +    parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]),
    +                    parameters[-1])
    +    return _create_concatenate_alias(self, parameters)
    +
    +
    +# 3.11+; Concatenate does not accept ellipsis in 3.10
    +# Breakpoint: https://github.com/python/cpython/pull/30969
    +if sys.version_info >= (3, 11):
    +    Concatenate = typing.Concatenate
    +# <=3.10
    +else:
    +    @_ExtensionsSpecialForm
    +    def Concatenate(self, parameters):
    +        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
    +        higher order function which adds, removes or transforms parameters of a
    +        callable.
    +
    +        For example::
    +
    +           Callable[Concatenate[int, P], int]
    +
    +        See PEP 612 for detailed information.
    +        """
    +        return _concatenate_getitem(self, parameters)
    +
    +
    +# 3.10+
    +if hasattr(typing, 'TypeGuard'):
    +    TypeGuard = typing.TypeGuard
    +# 3.9
    +else:
    +    @_ExtensionsSpecialForm
    +    def TypeGuard(self, parameters):
    +        """Special typing form used to annotate the return type of a user-defined
    +        type guard function.  ``TypeGuard`` only accepts a single type argument.
    +        At runtime, functions marked this way should return a boolean.
    +
    +        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
    +        type checkers to determine a more precise type of an expression within a
    +        program's code flow.  Usually type narrowing is done by analyzing
    +        conditional code flow and applying the narrowing to a block of code.  The
    +        conditional expression here is sometimes referred to as a "type guard".
    +
    +        Sometimes it would be convenient to use a user-defined boolean function
    +        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
    +        return type to alert static type checkers to this intention.
    +
    +        Using  ``-> TypeGuard`` tells the static type checker that for a given
    +        function:
    +
    +        1. The return value is a boolean.
    +        2. If the return value is ``True``, the type of its argument
    +        is the type inside ``TypeGuard``.
    +
    +        For example::
    +
    +            def is_str(val: Union[str, float]):
    +                # "isinstance" type guard
    +                if isinstance(val, str):
    +                    # Type of ``val`` is narrowed to ``str``
    +                    ...
    +                else:
    +                    # Else, type of ``val`` is narrowed to ``float``.
    +                    ...
    +
    +        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
    +        form of ``TypeA`` (it can even be a wider form) and this may lead to
    +        type-unsafe results.  The main reason is to allow for things like
    +        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
    +        a subtype of the former, since ``List`` is invariant.  The responsibility of
    +        writing type-safe type guards is left to the user.
    +
    +        ``TypeGuard`` also works with type variables.  For more information, see
    +        PEP 647 (User-Defined Type Guards).
    +        """
    +        item = typing._type_check(parameters, f'{self} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +
    +# 3.13+
    +if hasattr(typing, 'TypeIs'):
    +    TypeIs = typing.TypeIs
    +# <=3.12
    +else:
    +    @_ExtensionsSpecialForm
    +    def TypeIs(self, parameters):
    +        """Special typing form used to annotate the return type of a user-defined
    +        type narrower function.  ``TypeIs`` only accepts a single type argument.
    +        At runtime, functions marked this way should return a boolean.
    +
    +        ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static
    +        type checkers to determine a more precise type of an expression within a
    +        program's code flow.  Usually type narrowing is done by analyzing
    +        conditional code flow and applying the narrowing to a block of code.  The
    +        conditional expression here is sometimes referred to as a "type guard".
    +
    +        Sometimes it would be convenient to use a user-defined boolean function
    +        as a type guard.  Such a function should use ``TypeIs[...]`` as its
    +        return type to alert static type checkers to this intention.
    +
    +        Using  ``-> TypeIs`` tells the static type checker that for a given
    +        function:
    +
    +        1. The return value is a boolean.
    +        2. If the return value is ``True``, the type of its argument
    +        is the intersection of the type inside ``TypeIs`` and the argument's
    +        previously known type.
    +
    +        For example::
    +
    +            def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]:
    +                return hasattr(val, '__await__')
    +
    +            def f(val: Union[int, Awaitable[int]]) -> int:
    +                if is_awaitable(val):
    +                    assert_type(val, Awaitable[int])
    +                else:
    +                    assert_type(val, int)
    +
    +        ``TypeIs`` also works with type variables.  For more information, see
    +        PEP 742 (Narrowing types with TypeIs).
    +        """
    +        item = typing._type_check(parameters, f'{self} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +
    +# 3.14+?
    +if hasattr(typing, 'TypeForm'):
    +    TypeForm = typing.TypeForm
    +# <=3.13
    +else:
    +    class _TypeFormForm(_ExtensionsSpecialForm, _root=True):
    +        # TypeForm(X) is equivalent to X but indicates to the type checker
    +        # that the object is a TypeForm.
    +        def __call__(self, obj, /):
    +            return obj
    +
    +    @_TypeFormForm
    +    def TypeForm(self, parameters):
    +        """A special form representing the value that results from the evaluation
    +        of a type expression. This value encodes the information supplied in the
    +        type expression, and it represents the type described by that type expression.
    +
    +        When used in a type expression, TypeForm describes a set of type form objects.
    +        It accepts a single type argument, which must be a valid type expression.
    +        ``TypeForm[T]`` describes the set of all type form objects that represent
    +        the type T or types that are assignable to T.
    +
    +        Usage:
    +
    +            def cast[T](typ: TypeForm[T], value: Any) -> T: ...
    +
    +            reveal_type(cast(int, "x"))  # int
    +
    +        See PEP 747 for more information.
    +        """
    +        item = typing._type_check(parameters, f'{self} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +
    +
    +
    +if hasattr(typing, "LiteralString"):  # 3.11+
    +    LiteralString = typing.LiteralString
    +else:
    +    @_SpecialForm
    +    def LiteralString(self, params):
    +        """Represents an arbitrary literal string.
    +
    +        Example::
    +
    +          from typing_extensions import LiteralString
    +
    +          def query(sql: LiteralString) -> ...:
    +              ...
    +
    +          query("SELECT * FROM table")  # ok
    +          query(f"SELECT * FROM {input()}")  # not ok
    +
    +        See PEP 675 for details.
    +
    +        """
    +        raise TypeError(f"{self} is not subscriptable")
    +
    +
    +if hasattr(typing, "Self"):  # 3.11+
    +    Self = typing.Self
    +else:
    +    @_SpecialForm
    +    def Self(self, params):
    +        """Used to spell the type of "self" in classes.
    +
    +        Example::
    +
    +          from typing import Self
    +
    +          class ReturnsSelf:
    +              def parse(self, data: bytes) -> Self:
    +                  ...
    +                  return self
    +
    +        """
    +
    +        raise TypeError(f"{self} is not subscriptable")
    +
    +
    +if hasattr(typing, "Never"):  # 3.11+
    +    Never = typing.Never
    +else:
    +    @_SpecialForm
    +    def Never(self, params):
    +        """The bottom type, a type that has no members.
    +
    +        This can be used to define a function that should never be
    +        called, or a function that never returns::
    +
    +            from typing_extensions import Never
    +
    +            def never_call_me(arg: Never) -> None:
    +                pass
    +
    +            def int_or_str(arg: int | str) -> None:
    +                never_call_me(arg)  # type checker error
    +                match arg:
    +                    case int():
    +                        print("It's an int")
    +                    case str():
    +                        print("It's a str")
    +                    case _:
    +                        never_call_me(arg)  # ok, arg is of type Never
    +
    +        """
    +
    +        raise TypeError(f"{self} is not subscriptable")
    +
    +
    +if hasattr(typing, 'Required'):  # 3.11+
    +    Required = typing.Required
    +    NotRequired = typing.NotRequired
    +else:  # <=3.10
    +    @_ExtensionsSpecialForm
    +    def Required(self, parameters):
    +        """A special typing construct to mark a key of a total=False TypedDict
    +        as required. For example:
    +
    +            class Movie(TypedDict, total=False):
    +                title: Required[str]
    +                year: int
    +
    +            m = Movie(
    +                title='The Matrix',  # typechecker error if key is omitted
    +                year=1999,
    +            )
    +
    +        There is no runtime checking that a required key is actually provided
    +        when instantiating a related TypedDict.
    +        """
    +        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +    @_ExtensionsSpecialForm
    +    def NotRequired(self, parameters):
    +        """A special typing construct to mark a key of a TypedDict as
    +        potentially missing. For example:
    +
    +            class Movie(TypedDict):
    +                title: str
    +                year: NotRequired[int]
    +
    +            m = Movie(
    +                title='The Matrix',  # typechecker error if key is omitted
    +                year=1999,
    +            )
    +        """
    +        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +
    +if hasattr(typing, 'ReadOnly'):
    +    ReadOnly = typing.ReadOnly
    +else:  # <=3.12
    +    @_ExtensionsSpecialForm
    +    def ReadOnly(self, parameters):
    +        """A special typing construct to mark an item of a TypedDict as read-only.
    +
    +        For example:
    +
    +            class Movie(TypedDict):
    +                title: ReadOnly[str]
    +                year: int
    +
    +            def mutate_movie(m: Movie) -> None:
    +                m["year"] = 1992  # allowed
    +                m["title"] = "The Matrix"  # typechecker error
    +
    +        There is no runtime checking for this property.
    +        """
    +        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
    +        return typing._GenericAlias(self, (item,))
    +
    +
    +_UNPACK_DOC = """\
    +Type unpack operator.
    +
    +The type unpack operator takes the child types from some container type,
    +such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For
    +example:
    +
    +  # For some generic class `Foo`:
    +  Foo[Unpack[tuple[int, str]]]  # Equivalent to Foo[int, str]
    +
    +  Ts = TypeVarTuple('Ts')
    +  # Specifies that `Bar` is generic in an arbitrary number of types.
    +  # (Think of `Ts` as a tuple of an arbitrary number of individual
    +  #  `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
    +  #  `Generic[]`.)
    +  class Bar(Generic[Unpack[Ts]]): ...
    +  Bar[int]  # Valid
    +  Bar[int, str]  # Also valid
    +
    +From Python 3.11, this can also be done using the `*` operator:
    +
    +    Foo[*tuple[int, str]]
    +    class Bar(Generic[*Ts]): ...
    +
    +The operator can also be used along with a `TypedDict` to annotate
    +`**kwargs` in a function signature. For instance:
    +
    +  class Movie(TypedDict):
    +    name: str
    +    year: int
    +
    +  # This function expects two keyword arguments - *name* of type `str` and
    +  # *year* of type `int`.
    +  def foo(**kwargs: Unpack[Movie]): ...
    +
    +Note that there is only some runtime checking of this operator. Not
    +everything the runtime allows may be accepted by static type checkers.
    +
    +For more information, see PEP 646 and PEP 692.
    +"""
    +
    +
    +# PEP 692 changed the repr of Unpack[]
    +# Breakpoint: https://github.com/python/cpython/pull/104048
    +if sys.version_info >= (3, 12):
    +    Unpack = typing.Unpack
    +
    +    def _is_unpack(obj):
    +        return get_origin(obj) is Unpack
    +
    +else:  # <=3.11
    +    class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True):
    +        def __init__(self, getitem):
    +            super().__init__(getitem)
    +            self.__doc__ = _UNPACK_DOC
    +
    +    class _UnpackAlias(typing._GenericAlias, _root=True):
    +        if sys.version_info < (3, 11):
    +            # needed for compatibility with Generic[Unpack[Ts]]
    +            __class__ = typing.TypeVar
    +
    +        @property
    +        def __typing_unpacked_tuple_args__(self):
    +            assert self.__origin__ is Unpack
    +            assert len(self.__args__) == 1
    +            arg, = self.__args__
    +            if isinstance(arg, (typing._GenericAlias, _types.GenericAlias)):
    +                if arg.__origin__ is not tuple:
    +                    raise TypeError("Unpack[...] must be used with a tuple type")
    +                return arg.__args__
    +            return None
    +
    +        @property
    +        def __typing_is_unpacked_typevartuple__(self):
    +            assert self.__origin__ is Unpack
    +            assert len(self.__args__) == 1
    +            return isinstance(self.__args__[0], TypeVarTuple)
    +
    +        def __getitem__(self, args):
    +            if self.__typing_is_unpacked_typevartuple__:
    +                return args
    +            return super().__getitem__(args)
    +
    +    @_UnpackSpecialForm
    +    def Unpack(self, parameters):
    +        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
    +        return _UnpackAlias(self, (item,))
    +
    +    def _is_unpack(obj):
    +        return isinstance(obj, _UnpackAlias)
    +
    +
    +def _unpack_args(*args):
    +    newargs = []
    +    for arg in args:
    +        subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
    +        if subargs is not None and (not (subargs and subargs[-1] is ...)):
    +            newargs.extend(subargs)
    +        else:
    +            newargs.append(arg)
    +    return newargs
    +
    +
    +if _PEP_696_IMPLEMENTED:
    +    from typing import TypeVarTuple
    +
    +elif hasattr(typing, "TypeVarTuple"):  # 3.11+
    +
    +    # Add default parameter - PEP 696
    +    class TypeVarTuple(metaclass=_TypeVarLikeMeta):
    +        """Type variable tuple."""
    +
    +        _backported_typevarlike = typing.TypeVarTuple
    +
    +        def __new__(cls, name, *, default=NoDefault):
    +            tvt = typing.TypeVarTuple(name)
    +            _set_default(tvt, default)
    +            _set_module(tvt)
    +
    +            def _typevartuple_prepare_subst(alias, args):
    +                params = alias.__parameters__
    +                typevartuple_index = params.index(tvt)
    +                for param in params[typevartuple_index + 1:]:
    +                    if isinstance(param, TypeVarTuple):
    +                        raise TypeError(
    +                            f"More than one TypeVarTuple parameter in {alias}"
    +                        )
    +
    +                alen = len(args)
    +                plen = len(params)
    +                left = typevartuple_index
    +                right = plen - typevartuple_index - 1
    +                var_tuple_index = None
    +                fillarg = None
    +                for k, arg in enumerate(args):
    +                    if not isinstance(arg, type):
    +                        subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
    +                        if subargs and len(subargs) == 2 and subargs[-1] is ...:
    +                            if var_tuple_index is not None:
    +                                raise TypeError(
    +                                    "More than one unpacked "
    +                                    "arbitrary-length tuple argument"
    +                                )
    +                            var_tuple_index = k
    +                            fillarg = subargs[0]
    +                if var_tuple_index is not None:
    +                    left = min(left, var_tuple_index)
    +                    right = min(right, alen - var_tuple_index - 1)
    +                elif left + right > alen:
    +                    raise TypeError(f"Too few arguments for {alias};"
    +                                    f" actual {alen}, expected at least {plen - 1}")
    +                if left == alen - right and tvt.has_default():
    +                    replacement = _unpack_args(tvt.__default__)
    +                else:
    +                    replacement = args[left: alen - right]
    +
    +                return (
    +                    *args[:left],
    +                    *([fillarg] * (typevartuple_index - left)),
    +                    replacement,
    +                    *([fillarg] * (plen - right - left - typevartuple_index - 1)),
    +                    *args[alen - right:],
    +                )
    +
    +            tvt.__typing_prepare_subst__ = _typevartuple_prepare_subst
    +            return tvt
    +
    +        def __init_subclass__(self, *args, **kwds):
    +            raise TypeError("Cannot subclass special typing classes")
    +
    +else:  # <=3.10
    +    class TypeVarTuple(_DefaultMixin):
    +        """Type variable tuple.
    +
    +        Usage::
    +
    +            Ts = TypeVarTuple('Ts')
    +
    +        In the same way that a normal type variable is a stand-in for a single
    +        type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
    +        type such as ``Tuple[int, str]``.
    +
    +        Type variable tuples can be used in ``Generic`` declarations.
    +        Consider the following example::
    +
    +            class Array(Generic[*Ts]): ...
    +
    +        The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
    +        where ``T1`` and ``T2`` are type variables. To use these type variables
    +        as type parameters of ``Array``, we must *unpack* the type variable tuple using
    +        the star operator: ``*Ts``. The signature of ``Array`` then behaves
    +        as if we had simply written ``class Array(Generic[T1, T2]): ...``.
    +        In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
    +        us to parameterise the class with an *arbitrary* number of type parameters.
    +
    +        Type variable tuples can be used anywhere a normal ``TypeVar`` can.
    +        This includes class definitions, as shown above, as well as function
    +        signatures and variable annotations::
    +
    +            class Array(Generic[*Ts]):
    +
    +                def __init__(self, shape: Tuple[*Ts]):
    +                    self._shape: Tuple[*Ts] = shape
    +
    +                def get_shape(self) -> Tuple[*Ts]:
    +                    return self._shape
    +
    +            shape = (Height(480), Width(640))
    +            x: Array[Height, Width] = Array(shape)
    +            y = abs(x)  # Inferred type is Array[Height, Width]
    +            z = x + x   #        ...    is Array[Height, Width]
    +            x.get_shape()  #     ...    is tuple[Height, Width]
    +
    +        """
    +
    +        # Trick Generic __parameters__.
    +        __class__ = typing.TypeVar
    +
    +        def __iter__(self):
    +            yield self.__unpacked__
    +
    +        def __init__(self, name, *, default=NoDefault):
    +            self.__name__ = name
    +            _DefaultMixin.__init__(self, default)
    +
    +            # for pickling:
    +            def_mod = _caller()
    +            if def_mod != 'typing_extensions':
    +                self.__module__ = def_mod
    +
    +            self.__unpacked__ = Unpack[self]
    +
    +        def __repr__(self):
    +            return self.__name__
    +
    +        def __hash__(self):
    +            return object.__hash__(self)
    +
    +        def __eq__(self, other):
    +            return self is other
    +
    +        def __reduce__(self):
    +            return self.__name__
    +
    +        def __init_subclass__(self, *args, **kwds):
    +            if '_root' not in kwds:
    +                raise TypeError("Cannot subclass special typing classes")
    +
    +
    +if hasattr(typing, "reveal_type"):  # 3.11+
    +    reveal_type = typing.reveal_type
    +else:  # <=3.10
    +    def reveal_type(obj: T, /) -> T:
    +        """Reveal the inferred type of a variable.
    +
    +        When a static type checker encounters a call to ``reveal_type()``,
    +        it will emit the inferred type of the argument::
    +
    +            x: int = 1
    +            reveal_type(x)
    +
    +        Running a static type checker (e.g., ``mypy``) on this example
    +        will produce output similar to 'Revealed type is "builtins.int"'.
    +
    +        At runtime, the function prints the runtime type of the
    +        argument and returns it unchanged.
    +
    +        """
    +        print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr)
    +        return obj
    +
    +
    +if hasattr(typing, "_ASSERT_NEVER_REPR_MAX_LENGTH"):  # 3.11+
    +    _ASSERT_NEVER_REPR_MAX_LENGTH = typing._ASSERT_NEVER_REPR_MAX_LENGTH
    +else:  # <=3.10
    +    _ASSERT_NEVER_REPR_MAX_LENGTH = 100
    +
    +
    +if hasattr(typing, "assert_never"):  # 3.11+
    +    assert_never = typing.assert_never
    +else:  # <=3.10
    +    def assert_never(arg: Never, /) -> Never:
    +        """Assert to the type checker that a line of code is unreachable.
    +
    +        Example::
    +
    +            def int_or_str(arg: int | str) -> None:
    +                match arg:
    +                    case int():
    +                        print("It's an int")
    +                    case str():
    +                        print("It's a str")
    +                    case _:
    +                        assert_never(arg)
    +
    +        If a type checker finds that a call to assert_never() is
    +        reachable, it will emit an error.
    +
    +        At runtime, this throws an exception when called.
    +
    +        """
    +        value = repr(arg)
    +        if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH:
    +            value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...'
    +        raise AssertionError(f"Expected code to be unreachable, but got: {value}")
    +
    +
    +# dataclass_transform exists in 3.11 but lacks the frozen_default parameter
    +# Breakpoint: https://github.com/python/cpython/pull/99958
    +if sys.version_info >= (3, 12):  # 3.12+
    +    dataclass_transform = typing.dataclass_transform
    +else:  # <=3.11
    +    def dataclass_transform(
    +        *,
    +        eq_default: bool = True,
    +        order_default: bool = False,
    +        kw_only_default: bool = False,
    +        frozen_default: bool = False,
    +        field_specifiers: typing.Tuple[
    +            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
    +            ...
    +        ] = (),
    +        **kwargs: typing.Any,
    +    ) -> typing.Callable[[T], T]:
    +        """Decorator that marks a function, class, or metaclass as providing
    +        dataclass-like behavior.
    +
    +        Example:
    +
    +            from typing_extensions import dataclass_transform
    +
    +            _T = TypeVar("_T")
    +
    +            # Used on a decorator function
    +            @dataclass_transform()
    +            def create_model(cls: type[_T]) -> type[_T]:
    +                ...
    +                return cls
    +
    +            @create_model
    +            class CustomerModel:
    +                id: int
    +                name: str
    +
    +            # Used on a base class
    +            @dataclass_transform()
    +            class ModelBase: ...
    +
    +            class CustomerModel(ModelBase):
    +                id: int
    +                name: str
    +
    +            # Used on a metaclass
    +            @dataclass_transform()
    +            class ModelMeta(type): ...
    +
    +            class ModelBase(metaclass=ModelMeta): ...
    +
    +            class CustomerModel(ModelBase):
    +                id: int
    +                name: str
    +
    +        Each of the ``CustomerModel`` classes defined in this example will now
    +        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
    +        decorator. For example, the type checker will synthesize an ``__init__``
    +        method.
    +
    +        The arguments to this decorator can be used to customize this behavior:
    +        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
    +          True or False if it is omitted by the caller.
    +        - ``order_default`` indicates whether the ``order`` parameter is
    +          assumed to be True or False if it is omitted by the caller.
    +        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
    +          assumed to be True or False if it is omitted by the caller.
    +        - ``frozen_default`` indicates whether the ``frozen`` parameter is
    +          assumed to be True or False if it is omitted by the caller.
    +        - ``field_specifiers`` specifies a static list of supported classes
    +          or functions that describe fields, similar to ``dataclasses.field()``.
    +
    +        At runtime, this decorator records its arguments in the
    +        ``__dataclass_transform__`` attribute on the decorated object.
    +
    +        See PEP 681 for details.
    +
    +        """
    +        def decorator(cls_or_fn):
    +            cls_or_fn.__dataclass_transform__ = {
    +                "eq_default": eq_default,
    +                "order_default": order_default,
    +                "kw_only_default": kw_only_default,
    +                "frozen_default": frozen_default,
    +                "field_specifiers": field_specifiers,
    +                "kwargs": kwargs,
    +            }
    +            return cls_or_fn
    +        return decorator
    +
    +
    +if hasattr(typing, "override"):  # 3.12+
    +    override = typing.override
    +else:  # <=3.11
    +    _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
    +
    +    def override(arg: _F, /) -> _F:
    +        """Indicate that a method is intended to override a method in a base class.
    +
    +        Usage:
    +
    +            class Base:
    +                def method(self) -> None:
    +                    pass
    +
    +            class Child(Base):
    +                @override
    +                def method(self) -> None:
    +                    super().method()
    +
    +        When this decorator is applied to a method, the type checker will
    +        validate that it overrides a method with the same name on a base class.
    +        This helps prevent bugs that may occur when a base class is changed
    +        without an equivalent change to a child class.
    +
    +        There is no runtime checking of these properties. The decorator
    +        sets the ``__override__`` attribute to ``True`` on the decorated object
    +        to allow runtime introspection.
    +
    +        See PEP 698 for details.
    +
    +        """
    +        try:
    +            arg.__override__ = True
    +        except (AttributeError, TypeError):
    +            # Skip the attribute silently if it is not writable.
    +            # AttributeError happens if the object has __slots__ or a
    +            # read-only property, TypeError if it's a builtin class.
    +            pass
    +        return arg
    +
    +
    +# Python 3.13.3+ contains a fix for the wrapped __new__
    +# Breakpoint: https://github.com/python/cpython/pull/132160
    +if sys.version_info >= (3, 13, 3):
    +    deprecated = warnings.deprecated
    +else:
    +    _T = typing.TypeVar("_T")
    +
    +    class deprecated:
    +        """Indicate that a class, function or overload is deprecated.
    +
    +        When this decorator is applied to an object, the type checker
    +        will generate a diagnostic on usage of the deprecated object.
    +
    +        Usage:
    +
    +            @deprecated("Use B instead")
    +            class A:
    +                pass
    +
    +            @deprecated("Use g instead")
    +            def f():
    +                pass
    +
    +            @overload
    +            @deprecated("int support is deprecated")
    +            def g(x: int) -> int: ...
    +            @overload
    +            def g(x: str) -> int: ...
    +
    +        The warning specified by *category* will be emitted at runtime
    +        on use of deprecated objects. For functions, that happens on calls;
    +        for classes, on instantiation and on creation of subclasses.
    +        If the *category* is ``None``, no warning is emitted at runtime.
    +        The *stacklevel* determines where the
    +        warning is emitted. If it is ``1`` (the default), the warning
    +        is emitted at the direct caller of the deprecated object; if it
    +        is higher, it is emitted further up the stack.
    +        Static type checker behavior is not affected by the *category*
    +        and *stacklevel* arguments.
    +
    +        The deprecation message passed to the decorator is saved in the
    +        ``__deprecated__`` attribute on the decorated object.
    +        If applied to an overload, the decorator
    +        must be after the ``@overload`` decorator for the attribute to
    +        exist on the overload as returned by ``get_overloads()``.
    +
    +        See PEP 702 for details.
    +
    +        """
    +        def __init__(
    +            self,
    +            message: str,
    +            /,
    +            *,
    +            category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
    +            stacklevel: int = 1,
    +        ) -> None:
    +            if not isinstance(message, str):
    +                raise TypeError(
    +                    "Expected an object of type str for 'message', not "
    +                    f"{type(message).__name__!r}"
    +                )
    +            self.message = message
    +            self.category = category
    +            self.stacklevel = stacklevel
    +
    +        def __call__(self, arg: _T, /) -> _T:
    +            # Make sure the inner functions created below don't
    +            # retain a reference to self.
    +            msg = self.message
    +            category = self.category
    +            stacklevel = self.stacklevel
    +            if category is None:
    +                arg.__deprecated__ = msg
    +                return arg
    +            elif isinstance(arg, type):
    +                import functools
    +                from types import MethodType
    +
    +                original_new = arg.__new__
    +
    +                @functools.wraps(original_new)
    +                def __new__(cls, /, *args, **kwargs):
    +                    if cls is arg:
    +                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
    +                    if original_new is not object.__new__:
    +                        return original_new(cls, *args, **kwargs)
    +                    # Mirrors a similar check in object.__new__.
    +                    elif cls.__init__ is object.__init__ and (args or kwargs):
    +                        raise TypeError(f"{cls.__name__}() takes no arguments")
    +                    else:
    +                        return original_new(cls)
    +
    +                arg.__new__ = staticmethod(__new__)
    +
    +                original_init_subclass = arg.__init_subclass__
    +                # We need slightly different behavior if __init_subclass__
    +                # is a bound method (likely if it was implemented in Python)
    +                if isinstance(original_init_subclass, MethodType):
    +                    original_init_subclass = original_init_subclass.__func__
    +
    +                    @functools.wraps(original_init_subclass)
    +                    def __init_subclass__(*args, **kwargs):
    +                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
    +                        return original_init_subclass(*args, **kwargs)
    +
    +                    arg.__init_subclass__ = classmethod(__init_subclass__)
    +                # Or otherwise, which likely means it's a builtin such as
    +                # object's implementation of __init_subclass__.
    +                else:
    +                    @functools.wraps(original_init_subclass)
    +                    def __init_subclass__(*args, **kwargs):
    +                        warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
    +                        return original_init_subclass(*args, **kwargs)
    +
    +                    arg.__init_subclass__ = __init_subclass__
    +
    +                arg.__deprecated__ = __new__.__deprecated__ = msg
    +                __init_subclass__.__deprecated__ = msg
    +                return arg
    +            elif callable(arg):
    +                import asyncio.coroutines
    +                import functools
    +                import inspect
    +
    +                @functools.wraps(arg)
    +                def wrapper(*args, **kwargs):
    +                    warnings.warn(msg, category=category, stacklevel=stacklevel + 1)
    +                    return arg(*args, **kwargs)
    +
    +                if asyncio.coroutines.iscoroutinefunction(arg):
    +                    # Breakpoint: https://github.com/python/cpython/pull/99247
    +                    if sys.version_info >= (3, 12):
    +                        wrapper = inspect.markcoroutinefunction(wrapper)
    +                    else:
    +                        wrapper._is_coroutine = asyncio.coroutines._is_coroutine
    +
    +                arg.__deprecated__ = wrapper.__deprecated__ = msg
    +                return wrapper
    +            else:
    +                raise TypeError(
    +                    "@deprecated decorator with non-None category must be applied to "
    +                    f"a class or callable, not {arg!r}"
    +                )
    +
    +# Breakpoint: https://github.com/python/cpython/pull/23702
    +if sys.version_info < (3, 10):
    +    def _is_param_expr(arg):
    +        return arg is ... or isinstance(
    +            arg, (tuple, list, ParamSpec, _ConcatenateGenericAlias)
    +        )
    +else:
    +    def _is_param_expr(arg):
    +        return arg is ... or isinstance(
    +            arg,
    +            (
    +                tuple,
    +                list,
    +                ParamSpec,
    +                _ConcatenateGenericAlias,
    +                typing._ConcatenateGenericAlias,
    +            ),
    +        )
    +
    +
    +# We have to do some monkey patching to deal with the dual nature of
    +# Unpack/TypeVarTuple:
    +# - We want Unpack to be a kind of TypeVar so it gets accepted in
    +#   Generic[Unpack[Ts]]
    +# - We want it to *not* be treated as a TypeVar for the purposes of
    +#   counting generic parameters, so that when we subscript a generic,
    +#   the runtime doesn't try to substitute the Unpack with the subscripted type.
    +if not hasattr(typing, "TypeVarTuple"):
    +    def _check_generic(cls, parameters, elen=_marker):
    +        """Check correct count for parameters of a generic cls (internal helper).
    +
    +        This gives a nice error message in case of count mismatch.
    +        """
    +        # If substituting a single ParamSpec with multiple arguments
    +        # we do not check the count
    +        if (inspect.isclass(cls) and issubclass(cls, typing.Generic)
    +            and len(cls.__parameters__) == 1
    +            and isinstance(cls.__parameters__[0], ParamSpec)
    +            and parameters
    +            and not _is_param_expr(parameters[0])
    +        ):
    +            # Generic modifies parameters variable, but here we cannot do this
    +            return
    +
    +        if not elen:
    +            raise TypeError(f"{cls} is not a generic class")
    +        if elen is _marker:
    +            if not hasattr(cls, "__parameters__") or not cls.__parameters__:
    +                raise TypeError(f"{cls} is not a generic class")
    +            elen = len(cls.__parameters__)
    +        alen = len(parameters)
    +        if alen != elen:
    +            expect_val = elen
    +            if hasattr(cls, "__parameters__"):
    +                parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
    +                num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
    +                if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
    +                    return
    +
    +                # deal with TypeVarLike defaults
    +                # required TypeVarLikes cannot appear after a defaulted one.
    +                if alen < elen:
    +                    # since we validate TypeVarLike default in _collect_type_vars
    +                    # or _collect_parameters we can safely check parameters[alen]
    +                    if (
    +                        getattr(parameters[alen], '__default__', NoDefault)
    +                        is not NoDefault
    +                    ):
    +                        return
    +
    +                    num_default_tv = sum(getattr(p, '__default__', NoDefault)
    +                                         is not NoDefault for p in parameters)
    +
    +                    elen -= num_default_tv
    +
    +                    expect_val = f"at least {elen}"
    +
    +            # Breakpoint: https://github.com/python/cpython/pull/27515
    +            things = "arguments" if sys.version_info >= (3, 10) else "parameters"
    +            raise TypeError(f"Too {'many' if alen > elen else 'few'} {things}"
    +                            f" for {cls}; actual {alen}, expected {expect_val}")
    +else:
    +    # Python 3.11+
    +
    +    def _check_generic(cls, parameters, elen):
    +        """Check correct count for parameters of a generic cls (internal helper).
    +
    +        This gives a nice error message in case of count mismatch.
    +        """
    +        if not elen:
    +            raise TypeError(f"{cls} is not a generic class")
    +        alen = len(parameters)
    +        if alen != elen:
    +            expect_val = elen
    +            if hasattr(cls, "__parameters__"):
    +                parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
    +
    +                # deal with TypeVarLike defaults
    +                # required TypeVarLikes cannot appear after a defaulted one.
    +                if alen < elen:
    +                    # since we validate TypeVarLike default in _collect_type_vars
    +                    # or _collect_parameters we can safely check parameters[alen]
    +                    if (
    +                        getattr(parameters[alen], '__default__', NoDefault)
    +                        is not NoDefault
    +                    ):
    +                        return
    +
    +                    num_default_tv = sum(getattr(p, '__default__', NoDefault)
    +                                         is not NoDefault for p in parameters)
    +
    +                    elen -= num_default_tv
    +
    +                    expect_val = f"at least {elen}"
    +
    +            raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments"
    +                            f" for {cls}; actual {alen}, expected {expect_val}")
    +
    +if not _PEP_696_IMPLEMENTED:
    +    typing._check_generic = _check_generic
    +
    +
    +def _has_generic_or_protocol_as_origin() -> bool:
    +    try:
    +        frame = sys._getframe(2)
    +    # - Catch AttributeError: not all Python implementations have sys._getframe()
    +    # - Catch ValueError: maybe we're called from an unexpected module
    +    #   and the call stack isn't deep enough
    +    except (AttributeError, ValueError):
    +        return False  # err on the side of leniency
    +    else:
    +        # If we somehow get invoked from outside typing.py,
    +        # also err on the side of leniency
    +        if frame.f_globals.get("__name__") != "typing":
    +            return False
    +        origin = frame.f_locals.get("origin")
    +        # Cannot use "in" because origin may be an object with a buggy __eq__ that
    +        # throws an error.
    +        return origin is typing.Generic or origin is Protocol or origin is typing.Protocol
    +
    +
    +_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)}
    +
    +
    +def _is_unpacked_typevartuple(x) -> bool:
    +    if get_origin(x) is not Unpack:
    +        return False
    +    args = get_args(x)
    +    return (
    +        bool(args)
    +        and len(args) == 1
    +        and type(args[0]) in _TYPEVARTUPLE_TYPES
    +    )
    +
    +
    +# Python 3.11+ _collect_type_vars was renamed to _collect_parameters
    +if hasattr(typing, '_collect_type_vars'):
    +    def _collect_type_vars(types, typevar_types=None):
    +        """Collect all type variable contained in types in order of
    +        first appearance (lexicographic order). For example::
    +
    +            _collect_type_vars((T, List[S, T])) == (T, S)
    +        """
    +        if typevar_types is None:
    +            typevar_types = typing.TypeVar
    +        tvars = []
    +
    +        # A required TypeVarLike cannot appear after a TypeVarLike with a default
    +        # if it was a direct call to `Generic[]` or `Protocol[]`
    +        enforce_default_ordering = _has_generic_or_protocol_as_origin()
    +        default_encountered = False
    +
    +        # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple
    +        type_var_tuple_encountered = False
    +
    +        for t in types:
    +            if _is_unpacked_typevartuple(t):
    +                type_var_tuple_encountered = True
    +            elif (
    +                isinstance(t, typevar_types) and not isinstance(t, _UnpackAlias)
    +                and t not in tvars
    +            ):
    +                if enforce_default_ordering:
    +                    has_default = getattr(t, '__default__', NoDefault) is not NoDefault
    +                    if has_default:
    +                        if type_var_tuple_encountered:
    +                            raise TypeError('Type parameter with a default'
    +                                            ' follows TypeVarTuple')
    +                        default_encountered = True
    +                    elif default_encountered:
    +                        raise TypeError(f'Type parameter {t!r} without a default'
    +                                        ' follows type parameter with a default')
    +
    +                tvars.append(t)
    +            if _should_collect_from_parameters(t):
    +                tvars.extend([t for t in t.__parameters__ if t not in tvars])
    +            elif isinstance(t, tuple):
    +                # Collect nested type_vars
    +                # tuple wrapped by  _prepare_paramspec_params(cls, params)
    +                for x in t:
    +                    for collected in _collect_type_vars([x]):
    +                        if collected not in tvars:
    +                            tvars.append(collected)
    +        return tuple(tvars)
    +
    +    typing._collect_type_vars = _collect_type_vars
    +else:
    +    def _collect_parameters(args):
    +        """Collect all type variables and parameter specifications in args
    +        in order of first appearance (lexicographic order).
    +
    +        For example::
    +
    +            assert _collect_parameters((T, Callable[P, T])) == (T, P)
    +        """
    +        parameters = []
    +
    +        # A required TypeVarLike cannot appear after a TypeVarLike with default
    +        # if it was a direct call to `Generic[]` or `Protocol[]`
    +        enforce_default_ordering = _has_generic_or_protocol_as_origin()
    +        default_encountered = False
    +
    +        # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple
    +        type_var_tuple_encountered = False
    +
    +        for t in args:
    +            if isinstance(t, type):
    +                # We don't want __parameters__ descriptor of a bare Python class.
    +                pass
    +            elif isinstance(t, tuple):
    +                # `t` might be a tuple, when `ParamSpec` is substituted with
    +                # `[T, int]`, or `[int, *Ts]`, etc.
    +                for x in t:
    +                    for collected in _collect_parameters([x]):
    +                        if collected not in parameters:
    +                            parameters.append(collected)
    +            elif hasattr(t, '__typing_subst__'):
    +                if t not in parameters:
    +                    if enforce_default_ordering:
    +                        has_default = (
    +                            getattr(t, '__default__', NoDefault) is not NoDefault
    +                        )
    +
    +                        if type_var_tuple_encountered and has_default:
    +                            raise TypeError('Type parameter with a default'
    +                                            ' follows TypeVarTuple')
    +
    +                        if has_default:
    +                            default_encountered = True
    +                        elif default_encountered:
    +                            raise TypeError(f'Type parameter {t!r} without a default'
    +                                            ' follows type parameter with a default')
    +
    +                    parameters.append(t)
    +            else:
    +                if _is_unpacked_typevartuple(t):
    +                    type_var_tuple_encountered = True
    +                for x in getattr(t, '__parameters__', ()):
    +                    if x not in parameters:
    +                        parameters.append(x)
    +
    +        return tuple(parameters)
    +
    +    if not _PEP_696_IMPLEMENTED:
    +        typing._collect_parameters = _collect_parameters
    +
    +# Backport typing.NamedTuple as it exists in Python 3.13.
    +# In 3.11, the ability to define generic `NamedTuple`s was supported.
    +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
    +# On 3.12, we added __orig_bases__ to call-based NamedTuples
    +# On 3.13, we deprecated kwargs-based NamedTuples
    +# Breakpoint: https://github.com/python/cpython/pull/105609
    +if sys.version_info >= (3, 13):
    +    NamedTuple = typing.NamedTuple
    +else:
    +    def _make_nmtuple(name, types, module, defaults=()):
    +        fields = [n for n, t in types]
    +        annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
    +                       for n, t in types}
    +        nm_tpl = collections.namedtuple(name, fields,
    +                                        defaults=defaults, module=module)
    +        nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
    +        return nm_tpl
    +
    +    _prohibited_namedtuple_fields = typing._prohibited
    +    _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
    +
    +    class _NamedTupleMeta(type):
    +        def __new__(cls, typename, bases, ns):
    +            assert _NamedTuple in bases
    +            for base in bases:
    +                if base is not _NamedTuple and base is not typing.Generic:
    +                    raise TypeError(
    +                        'can only inherit from a NamedTuple type and Generic')
    +            bases = tuple(tuple if base is _NamedTuple else base for base in bases)
    +            if "__annotations__" in ns:
    +                types = ns["__annotations__"]
    +            elif "__annotate__" in ns:
    +                # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated
    +                types = ns["__annotate__"](1)
    +            else:
    +                types = {}
    +            default_names = []
    +            for field_name in types:
    +                if field_name in ns:
    +                    default_names.append(field_name)
    +                elif default_names:
    +                    raise TypeError(f"Non-default namedtuple field {field_name} "
    +                                    f"cannot follow default field"
    +                                    f"{'s' if len(default_names) > 1 else ''} "
    +                                    f"{', '.join(default_names)}")
    +            nm_tpl = _make_nmtuple(
    +                typename, types.items(),
    +                defaults=[ns[n] for n in default_names],
    +                module=ns['__module__']
    +            )
    +            nm_tpl.__bases__ = bases
    +            if typing.Generic in bases:
    +                if hasattr(typing, '_generic_class_getitem'):  # 3.12+
    +                    nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem)
    +                else:
    +                    class_getitem = typing.Generic.__class_getitem__.__func__
    +                    nm_tpl.__class_getitem__ = classmethod(class_getitem)
    +            # update from user namespace without overriding special namedtuple attributes
    +            for key, val in ns.items():
    +                if key in _prohibited_namedtuple_fields:
    +                    raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
    +                elif key not in _special_namedtuple_fields:
    +                    if key not in nm_tpl._fields:
    +                        setattr(nm_tpl, key, ns[key])
    +                    try:
    +                        set_name = type(val).__set_name__
    +                    except AttributeError:
    +                        pass
    +                    else:
    +                        try:
    +                            set_name(val, nm_tpl, key)
    +                        except BaseException as e:
    +                            msg = (
    +                                f"Error calling __set_name__ on {type(val).__name__!r} "
    +                                f"instance {key!r} in {typename!r}"
    +                            )
    +                            # BaseException.add_note() existed on py311,
    +                            # but the __set_name__ machinery didn't start
    +                            # using add_note() until py312.
    +                            # Making sure exceptions are raised in the same way
    +                            # as in "normal" classes seems most important here.
    +                            # Breakpoint: https://github.com/python/cpython/pull/95915
    +                            if sys.version_info >= (3, 12):
    +                                e.add_note(msg)
    +                                raise
    +                            else:
    +                                raise RuntimeError(msg) from e
    +
    +            if typing.Generic in bases:
    +                nm_tpl.__init_subclass__()
    +            return nm_tpl
    +
    +    _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
    +
    +    def _namedtuple_mro_entries(bases):
    +        assert NamedTuple in bases
    +        return (_NamedTuple,)
    +
    +    def NamedTuple(typename, fields=_marker, /, **kwargs):
    +        """Typed version of namedtuple.
    +
    +        Usage::
    +
    +            class Employee(NamedTuple):
    +                name: str
    +                id: int
    +
    +        This is equivalent to::
    +
    +            Employee = collections.namedtuple('Employee', ['name', 'id'])
    +
    +        The resulting class has an extra __annotations__ attribute, giving a
    +        dict that maps field names to types.  (The field names are also in
    +        the _fields attribute, which is part of the namedtuple API.)
    +        An alternative equivalent functional syntax is also accepted::
    +
    +            Employee = NamedTuple('Employee', [('name', str), ('id', int)])
    +        """
    +        if fields is _marker:
    +            if kwargs:
    +                deprecated_thing = "Creating NamedTuple classes using keyword arguments"
    +                deprecation_msg = (
    +                    "{name} is deprecated and will be disallowed in Python {remove}. "
    +                    "Use the class-based or functional syntax instead."
    +                )
    +            else:
    +                deprecated_thing = "Failing to pass a value for the 'fields' parameter"
    +                example = f"`{typename} = NamedTuple({typename!r}, [])`"
    +                deprecation_msg = (
    +                    "{name} is deprecated and will be disallowed in Python {remove}. "
    +                    "To create a NamedTuple class with 0 fields "
    +                    "using the functional syntax, "
    +                    "pass an empty list, e.g. "
    +                ) + example + "."
    +        elif fields is None:
    +            if kwargs:
    +                raise TypeError(
    +                    "Cannot pass `None` as the 'fields' parameter "
    +                    "and also specify fields using keyword arguments"
    +                )
    +            else:
    +                deprecated_thing = "Passing `None` as the 'fields' parameter"
    +                example = f"`{typename} = NamedTuple({typename!r}, [])`"
    +                deprecation_msg = (
    +                    "{name} is deprecated and will be disallowed in Python {remove}. "
    +                    "To create a NamedTuple class with 0 fields "
    +                    "using the functional syntax, "
    +                    "pass an empty list, e.g. "
    +                ) + example + "."
    +        elif kwargs:
    +            raise TypeError("Either list of fields or keywords"
    +                            " can be provided to NamedTuple, not both")
    +        if fields is _marker or fields is None:
    +            warnings.warn(
    +                deprecation_msg.format(name=deprecated_thing, remove="3.15"),
    +                DeprecationWarning,
    +                stacklevel=2,
    +            )
    +            fields = kwargs.items()
    +        nt = _make_nmtuple(typename, fields, module=_caller())
    +        nt.__orig_bases__ = (NamedTuple,)
    +        return nt
    +
    +    NamedTuple.__mro_entries__ = _namedtuple_mro_entries
    +
    +
    +if hasattr(collections.abc, "Buffer"):
    +    Buffer = collections.abc.Buffer
    +else:
    +    class Buffer(abc.ABC):  # noqa: B024
    +        """Base class for classes that implement the buffer protocol.
    +
    +        The buffer protocol allows Python objects to expose a low-level
    +        memory buffer interface. Before Python 3.12, it is not possible
    +        to implement the buffer protocol in pure Python code, or even
    +        to check whether a class implements the buffer protocol. In
    +        Python 3.12 and higher, the ``__buffer__`` method allows access
    +        to the buffer protocol from Python code, and the
    +        ``collections.abc.Buffer`` ABC allows checking whether a class
    +        implements the buffer protocol.
    +
    +        To indicate support for the buffer protocol in earlier versions,
    +        inherit from this ABC, either in a stub file or at runtime,
    +        or use ABC registration. This ABC provides no methods, because
    +        there is no Python-accessible methods shared by pre-3.12 buffer
    +        classes. It is useful primarily for static checks.
    +
    +        """
    +
    +    # As a courtesy, register the most common stdlib buffer classes.
    +    Buffer.register(memoryview)
    +    Buffer.register(bytearray)
    +    Buffer.register(bytes)
    +
    +
    +# Backport of types.get_original_bases, available on 3.12+ in CPython
    +if hasattr(_types, "get_original_bases"):
    +    get_original_bases = _types.get_original_bases
    +else:
    +    def get_original_bases(cls, /):
    +        """Return the class's "original" bases prior to modification by `__mro_entries__`.
    +
    +        Examples::
    +
    +            from typing import TypeVar, Generic
    +            from typing_extensions import NamedTuple, TypedDict
    +
    +            T = TypeVar("T")
    +            class Foo(Generic[T]): ...
    +            class Bar(Foo[int], float): ...
    +            class Baz(list[str]): ...
    +            Eggs = NamedTuple("Eggs", [("a", int), ("b", str)])
    +            Spam = TypedDict("Spam", {"a": int, "b": str})
    +
    +            assert get_original_bases(Bar) == (Foo[int], float)
    +            assert get_original_bases(Baz) == (list[str],)
    +            assert get_original_bases(Eggs) == (NamedTuple,)
    +            assert get_original_bases(Spam) == (TypedDict,)
    +            assert get_original_bases(int) == (object,)
    +        """
    +        try:
    +            return cls.__dict__.get("__orig_bases__", cls.__bases__)
    +        except AttributeError:
    +            raise TypeError(
    +                f'Expected an instance of type, not {type(cls).__name__!r}'
    +            ) from None
    +
    +
    +# NewType is a class on Python 3.10+, making it pickleable
    +# The error message for subclassing instances of NewType was improved on 3.11+
    +# Breakpoint: https://github.com/python/cpython/pull/30268
    +if sys.version_info >= (3, 11):
    +    NewType = typing.NewType
    +else:
    +    class NewType:
    +        """NewType creates simple unique types with almost zero
    +        runtime overhead. NewType(name, tp) is considered a subtype of tp
    +        by static type checkers. At runtime, NewType(name, tp) returns
    +        a dummy callable that simply returns its argument. Usage::
    +            UserId = NewType('UserId', int)
    +            def name_by_id(user_id: UserId) -> str:
    +                ...
    +            UserId('user')          # Fails type check
    +            name_by_id(42)          # Fails type check
    +            name_by_id(UserId(42))  # OK
    +            num = UserId(5) + 1     # type: int
    +        """
    +
    +        def __call__(self, obj, /):
    +            return obj
    +
    +        def __init__(self, name, tp):
    +            self.__qualname__ = name
    +            if '.' in name:
    +                name = name.rpartition('.')[-1]
    +            self.__name__ = name
    +            self.__supertype__ = tp
    +            def_mod = _caller()
    +            if def_mod != 'typing_extensions':
    +                self.__module__ = def_mod
    +
    +        def __mro_entries__(self, bases):
    +            # We defined __mro_entries__ to get a better error message
    +            # if a user attempts to subclass a NewType instance. bpo-46170
    +            supercls_name = self.__name__
    +
    +            class Dummy:
    +                def __init_subclass__(cls):
    +                    subcls_name = cls.__name__
    +                    raise TypeError(
    +                        f"Cannot subclass an instance of NewType. "
    +                        f"Perhaps you were looking for: "
    +                        f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`"
    +                    )
    +
    +            return (Dummy,)
    +
    +        def __repr__(self):
    +            return f'{self.__module__}.{self.__qualname__}'
    +
    +        def __reduce__(self):
    +            return self.__qualname__
    +
    +        # Breakpoint: https://github.com/python/cpython/pull/21515
    +        if sys.version_info >= (3, 10):
    +            # PEP 604 methods
    +            # It doesn't make sense to have these methods on Python <3.10
    +
    +            def __or__(self, other):
    +                return typing.Union[self, other]
    +
    +            def __ror__(self, other):
    +                return typing.Union[other, self]
    +
    +
    +# Breakpoint: https://github.com/python/cpython/pull/124795
    +if sys.version_info >= (3, 14):
    +    TypeAliasType = typing.TypeAliasType
    +# <=3.13
    +else:
    +    # Breakpoint: https://github.com/python/cpython/pull/103764
    +    if sys.version_info >= (3, 12):
    +        # 3.12-3.13
    +        def _is_unionable(obj):
    +            """Corresponds to is_unionable() in unionobject.c in CPython."""
    +            return obj is None or isinstance(obj, (
    +                type,
    +                _types.GenericAlias,
    +                _types.UnionType,
    +                typing.TypeAliasType,
    +                TypeAliasType,
    +            ))
    +    else:
    +        # <=3.11
    +        def _is_unionable(obj):
    +            """Corresponds to is_unionable() in unionobject.c in CPython."""
    +            return obj is None or isinstance(obj, (
    +                type,
    +                _types.GenericAlias,
    +                _types.UnionType,
    +                TypeAliasType,
    +            ))
    +
    +    if sys.version_info < (3, 10):
    +        # Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582,
    +        # so that we emulate the behaviour of `types.GenericAlias`
    +        # on the latest versions of CPython
    +        _ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({
    +            "__class__",
    +            "__bases__",
    +            "__origin__",
    +            "__args__",
    +            "__unpacked__",
    +            "__parameters__",
    +            "__typing_unpacked_tuple_args__",
    +            "__mro_entries__",
    +            "__reduce_ex__",
    +            "__reduce__",
    +            "__copy__",
    +            "__deepcopy__",
    +        })
    +
    +        class _TypeAliasGenericAlias(typing._GenericAlias, _root=True):
    +            def __getattr__(self, attr):
    +                if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS:
    +                    return object.__getattr__(self, attr)
    +                return getattr(self.__origin__, attr)
    +
    +
    +    class TypeAliasType:
    +        """Create named, parameterized type aliases.
    +
    +        This provides a backport of the new `type` statement in Python 3.12:
    +
    +            type ListOrSet[T] = list[T] | set[T]
    +
    +        is equivalent to:
    +
    +            T = TypeVar("T")
    +            ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,))
    +
    +        The name ListOrSet can then be used as an alias for the type it refers to.
    +
    +        The type_params argument should contain all the type parameters used
    +        in the value of the type alias. If the alias is not generic, this
    +        argument is omitted.
    +
    +        Static type checkers should only support type aliases declared using
    +        TypeAliasType that follow these rules:
    +
    +        - The first argument (the name) must be a string literal.
    +        - The TypeAliasType instance must be immediately assigned to a variable
    +          of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid,
    +          as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)').
    +
    +        """
    +
    +        def __init__(self, name: str, value, *, type_params=()):
    +            if not isinstance(name, str):
    +                raise TypeError("TypeAliasType name must be a string")
    +            if not isinstance(type_params, tuple):
    +                raise TypeError("type_params must be a tuple")
    +            self.__value__ = value
    +            self.__type_params__ = type_params
    +
    +            default_value_encountered = False
    +            parameters = []
    +            for type_param in type_params:
    +                if (
    +                    not isinstance(type_param, (TypeVar, TypeVarTuple, ParamSpec))
    +                    # <=3.11
    +                    # Unpack Backport passes isinstance(type_param, TypeVar)
    +                    or _is_unpack(type_param)
    +                ):
    +                    raise TypeError(f"Expected a type param, got {type_param!r}")
    +                has_default = (
    +                    getattr(type_param, '__default__', NoDefault) is not NoDefault
    +                )
    +                if default_value_encountered and not has_default:
    +                    raise TypeError(f"non-default type parameter '{type_param!r}'"
    +                                    " follows default type parameter")
    +                if has_default:
    +                    default_value_encountered = True
    +                if isinstance(type_param, TypeVarTuple):
    +                    parameters.extend(type_param)
    +                else:
    +                    parameters.append(type_param)
    +            self.__parameters__ = tuple(parameters)
    +            def_mod = _caller()
    +            if def_mod != 'typing_extensions':
    +                self.__module__ = def_mod
    +            # Setting this attribute closes the TypeAliasType from further modification
    +            self.__name__ = name
    +
    +        def __setattr__(self, name: str, value: object, /) -> None:
    +            if hasattr(self, "__name__"):
    +                self._raise_attribute_error(name)
    +            super().__setattr__(name, value)
    +
    +        def __delattr__(self, name: str, /) -> Never:
    +            self._raise_attribute_error(name)
    +
    +        def _raise_attribute_error(self, name: str) -> Never:
    +            # Match the Python 3.12 error messages exactly
    +            if name == "__name__":
    +                raise AttributeError("readonly attribute")
    +            elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}:
    +                raise AttributeError(
    +                    f"attribute '{name}' of 'typing.TypeAliasType' objects "
    +                    "is not writable"
    +                )
    +            else:
    +                raise AttributeError(
    +                    f"'typing.TypeAliasType' object has no attribute '{name}'"
    +                )
    +
    +        def __repr__(self) -> str:
    +            return self.__name__
    +
    +        if sys.version_info < (3, 11):
    +            def _check_single_param(self, param, recursion=0):
    +                # Allow [], [int], [int, str], [int, ...], [int, T]
    +                if param is ...:
    +                    return ...
    +                if param is None:
    +                    return None
    +                # Note in <= 3.9 _ConcatenateGenericAlias inherits from list
    +                if isinstance(param, list) and recursion == 0:
    +                    return [self._check_single_param(arg, recursion+1)
    +                            for arg in param]
    +                return typing._type_check(
    +                        param, f'Subscripting {self.__name__} requires a type.'
    +                    )
    +
    +        def _check_parameters(self, parameters):
    +            if sys.version_info < (3, 11):
    +                return tuple(
    +                    self._check_single_param(item)
    +                    for item in parameters
    +                )
    +            return tuple(typing._type_check(
    +                        item, f'Subscripting {self.__name__} requires a type.'
    +                    )
    +                    for item in parameters
    +            )
    +
    +        def __getitem__(self, parameters):
    +            if not self.__type_params__:
    +                raise TypeError("Only generic type aliases are subscriptable")
    +            if not isinstance(parameters, tuple):
    +                parameters = (parameters,)
    +            # Using 3.9 here will create problems with Concatenate
    +            if sys.version_info >= (3, 10):
    +                return _types.GenericAlias(self, parameters)
    +            type_vars = _collect_type_vars(parameters)
    +            parameters = self._check_parameters(parameters)
    +            alias = _TypeAliasGenericAlias(self, parameters)
    +            # alias.__parameters__ is not complete if Concatenate is present
    +            # as it is converted to a list from which no parameters are extracted.
    +            if alias.__parameters__ != type_vars:
    +                alias.__parameters__ = type_vars
    +            return alias
    +
    +        def __reduce__(self):
    +            return self.__name__
    +
    +        def __init_subclass__(cls, *args, **kwargs):
    +            raise TypeError(
    +                "type 'typing_extensions.TypeAliasType' is not an acceptable base type"
    +            )
    +
    +        # The presence of this method convinces typing._type_check
    +        # that TypeAliasTypes are types.
    +        def __call__(self):
    +            raise TypeError("Type alias is not callable")
    +
    +        # Breakpoint: https://github.com/python/cpython/pull/21515
    +        if sys.version_info >= (3, 10):
    +            def __or__(self, right):
    +                # For forward compatibility with 3.12, reject Unions
    +                # that are not accepted by the built-in Union.
    +                if not _is_unionable(right):
    +                    return NotImplemented
    +                return typing.Union[self, right]
    +
    +            def __ror__(self, left):
    +                if not _is_unionable(left):
    +                    return NotImplemented
    +                return typing.Union[left, self]
    +
    +
    +if hasattr(typing, "is_protocol"):
    +    is_protocol = typing.is_protocol
    +    get_protocol_members = typing.get_protocol_members
    +else:
    +    def is_protocol(tp: type, /) -> bool:
    +        """Return True if the given type is a Protocol.
    +
    +        Example::
    +
    +            >>> from typing_extensions import Protocol, is_protocol
    +            >>> class P(Protocol):
    +            ...     def a(self) -> str: ...
    +            ...     b: int
    +            >>> is_protocol(P)
    +            True
    +            >>> is_protocol(int)
    +            False
    +        """
    +        return (
    +            isinstance(tp, type)
    +            and getattr(tp, '_is_protocol', False)
    +            and tp is not Protocol
    +            and tp is not typing.Protocol
    +        )
    +
    +    def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
    +        """Return the set of members defined in a Protocol.
    +
    +        Example::
    +
    +            >>> from typing_extensions import Protocol, get_protocol_members
    +            >>> class P(Protocol):
    +            ...     def a(self) -> str: ...
    +            ...     b: int
    +            >>> get_protocol_members(P)
    +            frozenset({'a', 'b'})
    +
    +        Raise a TypeError for arguments that are not Protocols.
    +        """
    +        if not is_protocol(tp):
    +            raise TypeError(f'{tp!r} is not a Protocol')
    +        if hasattr(tp, '__protocol_attrs__'):
    +            return frozenset(tp.__protocol_attrs__)
    +        return frozenset(_get_protocol_attrs(tp))
    +
    +
    +if hasattr(typing, "Doc"):
    +    Doc = typing.Doc
    +else:
    +    class Doc:
    +        """Define the documentation of a type annotation using ``Annotated``, to be
    +         used in class attributes, function and method parameters, return values,
    +         and variables.
    +
    +        The value should be a positional-only string literal to allow static tools
    +        like editors and documentation generators to use it.
    +
    +        This complements docstrings.
    +
    +        The string value passed is available in the attribute ``documentation``.
    +
    +        Example::
    +
    +            >>> from typing_extensions import Annotated, Doc
    +            >>> def hi(to: Annotated[str, Doc("Who to say hi to")]) -> None: ...
    +        """
    +        def __init__(self, documentation: str, /) -> None:
    +            self.documentation = documentation
    +
    +        def __repr__(self) -> str:
    +            return f"Doc({self.documentation!r})"
    +
    +        def __hash__(self) -> int:
    +            return hash(self.documentation)
    +
    +        def __eq__(self, other: object) -> bool:
    +            if not isinstance(other, Doc):
    +                return NotImplemented
    +            return self.documentation == other.documentation
    +
    +
    +_CapsuleType = getattr(_types, "CapsuleType", None)
    +
    +if _CapsuleType is None:
    +    try:
    +        import _socket
    +    except ImportError:
    +        pass
    +    else:
    +        _CAPI = getattr(_socket, "CAPI", None)
    +        if _CAPI is not None:
    +            _CapsuleType = type(_CAPI)
    +
    +if _CapsuleType is not None:
    +    CapsuleType = _CapsuleType
    +    __all__.append("CapsuleType")
    +
    +
    +if sys.version_info >= (3, 14):
    +    from annotationlib import Format, get_annotations
    +else:
    +    # Available since Python 3.14.0a3
    +    # PR: https://github.com/python/cpython/pull/124415
    +    class Format(enum.IntEnum):
    +        VALUE = 1
    +        VALUE_WITH_FAKE_GLOBALS = 2
    +        FORWARDREF = 3
    +        STRING = 4
    +
    +    # Available since Python 3.14.0a1
    +    # PR: https://github.com/python/cpython/pull/119891
    +    def get_annotations(obj, *, globals=None, locals=None, eval_str=False,
    +                        format=Format.VALUE):
    +        """Compute the annotations dict for an object.
    +
    +        obj may be a callable, class, or module.
    +        Passing in an object of any other type raises TypeError.
    +
    +        Returns a dict.  get_annotations() returns a new dict every time
    +        it's called; calling it twice on the same object will return two
    +        different but equivalent dicts.
    +
    +        This is a backport of `inspect.get_annotations`, which has been
    +        in the standard library since Python 3.10. See the standard library
    +        documentation for more:
    +
    +            https://docs.python.org/3/library/inspect.html#inspect.get_annotations
    +
    +        This backport adds the *format* argument introduced by PEP 649. The
    +        three formats supported are:
    +        * VALUE: the annotations are returned as-is. This is the default and
    +          it is compatible with the behavior on previous Python versions.
    +        * FORWARDREF: return annotations as-is if possible, but replace any
    +          undefined names with ForwardRef objects. The implementation proposed by
    +          PEP 649 relies on language changes that cannot be backported; the
    +          typing-extensions implementation simply returns the same result as VALUE.
    +        * STRING: return annotations as strings, in a format close to the original
    +          source. Again, this behavior cannot be replicated directly in a backport.
    +          As an approximation, typing-extensions retrieves the annotations under
    +          VALUE semantics and then stringifies them.
    +
    +        The purpose of this backport is to allow users who would like to use
    +        FORWARDREF or STRING semantics once PEP 649 is implemented, but who also
    +        want to support earlier Python versions, to simply write:
    +
    +            typing_extensions.get_annotations(obj, format=Format.FORWARDREF)
    +
    +        """
    +        format = Format(format)
    +        if format is Format.VALUE_WITH_FAKE_GLOBALS:
    +            raise ValueError(
    +                "The VALUE_WITH_FAKE_GLOBALS format is for internal use only"
    +            )
    +
    +        if eval_str and format is not Format.VALUE:
    +            raise ValueError("eval_str=True is only supported with format=Format.VALUE")
    +
    +        if isinstance(obj, type):
    +            # class
    +            obj_dict = getattr(obj, '__dict__', None)
    +            if obj_dict and hasattr(obj_dict, 'get'):
    +                ann = obj_dict.get('__annotations__', None)
    +                if isinstance(ann, _types.GetSetDescriptorType):
    +                    ann = None
    +            else:
    +                ann = None
    +
    +            obj_globals = None
    +            module_name = getattr(obj, '__module__', None)
    +            if module_name:
    +                module = sys.modules.get(module_name, None)
    +                if module:
    +                    obj_globals = getattr(module, '__dict__', None)
    +            obj_locals = dict(vars(obj))
    +            unwrap = obj
    +        elif isinstance(obj, _types.ModuleType):
    +            # module
    +            ann = getattr(obj, '__annotations__', None)
    +            obj_globals = obj.__dict__
    +            obj_locals = None
    +            unwrap = None
    +        elif callable(obj):
    +            # this includes types.Function, types.BuiltinFunctionType,
    +            # types.BuiltinMethodType, functools.partial, functools.singledispatch,
    +            # "class funclike" from Lib/test/test_inspect... on and on it goes.
    +            ann = getattr(obj, '__annotations__', None)
    +            obj_globals = getattr(obj, '__globals__', None)
    +            obj_locals = None
    +            unwrap = obj
    +        elif hasattr(obj, '__annotations__'):
    +            ann = obj.__annotations__
    +            obj_globals = obj_locals = unwrap = None
    +        else:
    +            raise TypeError(f"{obj!r} is not a module, class, or callable.")
    +
    +        if ann is None:
    +            return {}
    +
    +        if not isinstance(ann, dict):
    +            raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None")
    +
    +        if not ann:
    +            return {}
    +
    +        if not eval_str:
    +            if format is Format.STRING:
    +                return {
    +                    key: value if isinstance(value, str) else typing._type_repr(value)
    +                    for key, value in ann.items()
    +                }
    +            return dict(ann)
    +
    +        if unwrap is not None:
    +            while True:
    +                if hasattr(unwrap, '__wrapped__'):
    +                    unwrap = unwrap.__wrapped__
    +                    continue
    +                if isinstance(unwrap, functools.partial):
    +                    unwrap = unwrap.func
    +                    continue
    +                break
    +            if hasattr(unwrap, "__globals__"):
    +                obj_globals = unwrap.__globals__
    +
    +        if globals is None:
    +            globals = obj_globals
    +        if locals is None:
    +            locals = obj_locals or {}
    +
    +        # "Inject" type parameters into the local namespace
    +        # (unless they are shadowed by assignments *in* the local namespace),
    +        # as a way of emulating annotation scopes when calling `eval()`
    +        if type_params := getattr(obj, "__type_params__", ()):
    +            locals = {param.__name__: param for param in type_params} | locals
    +
    +        return_value = {key:
    +            value if not isinstance(value, str) else eval(value, globals, locals)
    +            for key, value in ann.items() }
    +        return return_value
    +
    +
    +if hasattr(typing, "evaluate_forward_ref"):
    +    evaluate_forward_ref = typing.evaluate_forward_ref
    +else:
    +    # Implements annotationlib.ForwardRef.evaluate
    +    def _eval_with_owner(
    +        forward_ref, *, owner=None, globals=None, locals=None, type_params=None
    +    ):
    +        if forward_ref.__forward_evaluated__:
    +            return forward_ref.__forward_value__
    +        if getattr(forward_ref, "__cell__", None) is not None:
    +            try:
    +                value = forward_ref.__cell__.cell_contents
    +            except ValueError:
    +                pass
    +            else:
    +                forward_ref.__forward_evaluated__ = True
    +                forward_ref.__forward_value__ = value
    +                return value
    +        if owner is None:
    +            owner = getattr(forward_ref, "__owner__", None)
    +
    +        if (
    +            globals is None
    +            and getattr(forward_ref, "__forward_module__", None) is not None
    +        ):
    +            globals = getattr(
    +                sys.modules.get(forward_ref.__forward_module__, None), "__dict__", None
    +            )
    +        if globals is None:
    +            globals = getattr(forward_ref, "__globals__", None)
    +        if globals is None:
    +            if isinstance(owner, type):
    +                module_name = getattr(owner, "__module__", None)
    +                if module_name:
    +                    module = sys.modules.get(module_name, None)
    +                    if module:
    +                        globals = getattr(module, "__dict__", None)
    +            elif isinstance(owner, _types.ModuleType):
    +                globals = getattr(owner, "__dict__", None)
    +            elif callable(owner):
    +                globals = getattr(owner, "__globals__", None)
    +
    +        # If we pass None to eval() below, the globals of this module are used.
    +        if globals is None:
    +            globals = {}
    +
    +        if locals is None:
    +            locals = {}
    +            if isinstance(owner, type):
    +                locals.update(vars(owner))
    +
    +        if type_params is None and owner is not None:
    +            # "Inject" type parameters into the local namespace
    +            # (unless they are shadowed by assignments *in* the local namespace),
    +            # as a way of emulating annotation scopes when calling `eval()`
    +            type_params = getattr(owner, "__type_params__", None)
    +
    +        # Type parameters exist in their own scope, which is logically
    +        # between the locals and the globals. We simulate this by adding
    +        # them to the globals.
    +        if type_params is not None:
    +            globals = dict(globals)
    +            for param in type_params:
    +                globals[param.__name__] = param
    +
    +        arg = forward_ref.__forward_arg__
    +        if arg.isidentifier() and not keyword.iskeyword(arg):
    +            if arg in locals:
    +                value = locals[arg]
    +            elif arg in globals:
    +                value = globals[arg]
    +            elif hasattr(builtins, arg):
    +                return getattr(builtins, arg)
    +            else:
    +                raise NameError(arg)
    +        else:
    +            code = forward_ref.__forward_code__
    +            value = eval(code, globals, locals)
    +        forward_ref.__forward_evaluated__ = True
    +        forward_ref.__forward_value__ = value
    +        return value
    +
    +    def evaluate_forward_ref(
    +        forward_ref,
    +        *,
    +        owner=None,
    +        globals=None,
    +        locals=None,
    +        type_params=None,
    +        format=None,
    +        _recursive_guard=frozenset(),
    +    ):
    +        """Evaluate a forward reference as a type hint.
    +
    +        This is similar to calling the ForwardRef.evaluate() method,
    +        but unlike that method, evaluate_forward_ref() also:
    +
    +        * Recursively evaluates forward references nested within the type hint.
    +        * Rejects certain objects that are not valid type hints.
    +        * Replaces type hints that evaluate to None with types.NoneType.
    +        * Supports the *FORWARDREF* and *STRING* formats.
    +
    +        *forward_ref* must be an instance of ForwardRef. *owner*, if given,
    +        should be the object that holds the annotations that the forward reference
    +        derived from, such as a module, class object, or function. It is used to
    +        infer the namespaces to use for looking up names. *globals* and *locals*
    +        can also be explicitly given to provide the global and local namespaces.
    +        *type_params* is a tuple of type parameters that are in scope when
    +        evaluating the forward reference. This parameter must be provided (though
    +        it may be an empty tuple) if *owner* is not given and the forward reference
    +        does not already have an owner set. *format* specifies the format of the
    +        annotation and is a member of the annotationlib.Format enum.
    +
    +        """
    +        if format == Format.STRING:
    +            return forward_ref.__forward_arg__
    +        if forward_ref.__forward_arg__ in _recursive_guard:
    +            return forward_ref
    +
    +        # Evaluate the forward reference
    +        try:
    +            value = _eval_with_owner(
    +                forward_ref,
    +                owner=owner,
    +                globals=globals,
    +                locals=locals,
    +                type_params=type_params,
    +            )
    +        except NameError:
    +            if format == Format.FORWARDREF:
    +                return forward_ref
    +            else:
    +                raise
    +
    +        if isinstance(value, str):
    +            value = ForwardRef(value)
    +
    +        # Recursively evaluate the type
    +        if isinstance(value, ForwardRef):
    +            if getattr(value, "__forward_module__", True) is not None:
    +                globals = None
    +            return evaluate_forward_ref(
    +                value,
    +                globals=globals,
    +                locals=locals,
    +                 type_params=type_params, owner=owner,
    +                _recursive_guard=_recursive_guard, format=format
    +            )
    +        if sys.version_info < (3, 12, 5) and type_params:
    +            # Make use of type_params
    +            locals = dict(locals) if locals else {}
    +            for tvar in type_params:
    +                if tvar.__name__ not in locals:  # lets not overwrite something present
    +                    locals[tvar.__name__] = tvar
    +        if sys.version_info < (3, 12, 5):
    +            return typing._eval_type(
    +                value,
    +                globals,
    +                locals,
    +                recursive_guard=_recursive_guard | {forward_ref.__forward_arg__},
    +            )
    +        else:
    +            return typing._eval_type(
    +                value,
    +                globals,
    +                locals,
    +                type_params,
    +                recursive_guard=_recursive_guard | {forward_ref.__forward_arg__},
    +            )
    +
    +
    +class Sentinel:
    +    """Create a unique sentinel object.
    +
    +    *name* should be the name of the variable to which the return value shall be assigned.
    +
    +    *repr*, if supplied, will be used for the repr of the sentinel object.
    +    If not provided, "" will be used.
    +    """
    +
    +    def __init__(
    +        self,
    +        name: str,
    +        repr: typing.Optional[str] = None,
    +    ):
    +        self._name = name
    +        self._repr = repr if repr is not None else f'<{name}>'
    +
    +    def __repr__(self):
    +        return self._repr
    +
    +    if sys.version_info < (3, 11):
    +        # The presence of this method convinces typing._type_check
    +        # that Sentinels are types.
    +        def __call__(self, *args, **kwargs):
    +            raise TypeError(f"{type(self).__name__!r} object is not callable")
    +
    +    # Breakpoint: https://github.com/python/cpython/pull/21515
    +    if sys.version_info >= (3, 10):
    +        def __or__(self, other):
    +            return typing.Union[self, other]
    +
    +        def __ror__(self, other):
    +            return typing.Union[other, self]
    +
    +    def __getstate__(self):
    +        raise TypeError(f"Cannot pickle {type(self).__name__!r} object")
    +
    +
    +if sys.version_info >= (3, 14, 0, "beta"):
    +    type_repr = annotationlib.type_repr
    +else:
    +    def type_repr(value):
    +        """Convert a Python value to a format suitable for use with the STRING format.
    +
    +        This is intended as a helper for tools that support the STRING format but do
    +        not have access to the code that originally produced the annotations. It uses
    +        repr() for most objects.
    +
    +        """
    +        if isinstance(value, (type, _types.FunctionType, _types.BuiltinFunctionType)):
    +            if value.__module__ == "builtins":
    +                return value.__qualname__
    +            return f"{value.__module__}.{value.__qualname__}"
    +        if value is ...:
    +            return "..."
    +        return repr(value)
    +
    +
    +# Aliases for items that are in typing in all supported versions.
    +# We use hasattr() checks so this library will continue to import on
    +# future versions of Python that may remove these names.
    +_typing_names = [
    +    "AbstractSet",
    +    "AnyStr",
    +    "BinaryIO",
    +    "Callable",
    +    "Collection",
    +    "Container",
    +    "Dict",
    +    "FrozenSet",
    +    "Hashable",
    +    "IO",
    +    "ItemsView",
    +    "Iterable",
    +    "Iterator",
    +    "KeysView",
    +    "List",
    +    "Mapping",
    +    "MappingView",
    +    "Match",
    +    "MutableMapping",
    +    "MutableSequence",
    +    "MutableSet",
    +    "Optional",
    +    "Pattern",
    +    "Reversible",
    +    "Sequence",
    +    "Set",
    +    "Sized",
    +    "TextIO",
    +    "Tuple",
    +    "Union",
    +    "ValuesView",
    +    "cast",
    +    "no_type_check",
    +    "no_type_check_decorator",
    +    # This is private, but it was defined by typing_extensions for a long time
    +    # and some users rely on it.
    +    "_AnnotatedAlias",
    +]
    +globals().update(
    +    {name: getattr(typing, name) for name in _typing_names if hasattr(typing, name)}
    +)
    +# These are defined unconditionally because they are used in
    +# typing-extensions itself.
    +Generic = typing.Generic
    +ForwardRef = typing.ForwardRef
    +Annotated = typing.Annotated
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER
    new file mode 100644
    index 0000000..a1b589e
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/METADATA
    new file mode 100644
    index 0000000..b0eba85
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/METADATA
    @@ -0,0 +1,49 @@
    +Metadata-Version: 2.4
    +Name: typing-inspection
    +Version: 0.4.2
    +Summary: Runtime typing introspection tools
    +Project-URL: Homepage, https://github.com/pydantic/typing-inspection
    +Project-URL: Documentation, https://pydantic.github.io/typing-inspection/dev/
    +Project-URL: Source, https://github.com/pydantic/typing-inspection
    +Project-URL: Changelog, https://github.com/pydantic/typing-inspection/blob/main/HISTORY.md
    +Author-email: Victorien Plot 
    +License-Expression: MIT
    +License-File: LICENSE
    +Classifier: Development Status :: 3 - Alpha
    +Classifier: Intended Audience :: Developers
    +Classifier: Programming Language :: Python
    +Classifier: Programming Language :: Python :: 3
    +Classifier: Programming Language :: Python :: 3 :: Only
    +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 :: 3.14
    +Classifier: Programming Language :: Python :: Implementation :: CPython
    +Classifier: Topic :: Software Development :: Libraries :: Python Modules
    +Classifier: Typing :: Typed
    +Requires-Python: >=3.9
    +Requires-Dist: typing-extensions>=4.12.0
    +Description-Content-Type: text/markdown
    +
    +# typing-inspection
    +
    +[![CI](https://img.shields.io/github/actions/workflow/status/pydantic/typing-inspection/ci.yml?branch=main&logo=github&label=CI)](https://github.com/pydantic/typing-inspection/actions?query=event%3Apush+branch%3Amain+workflow%3ACI)
    +[![Coverage](https://coverage-badge.samuelcolvin.workers.dev/pydantic/typing-inspection.svg)](https://coverage-badge.samuelcolvin.workers.dev/redirect/pydantic/typing-inspection)
    +[![PyPI](https://img.shields.io/pypi/v/typing-inspection.svg)](https://pypi.org/project/typing-inspection/)
    +[![Versions](https://img.shields.io/pypi/pyversions/typing-inspection.svg)](https://github.com/pydantic/typing-inspection)
    +[![License](https://img.shields.io/github/license/pydantic/typing-inspection.svg)](https://github.com/pydantic/typing-inspection/blob/main/LICENSE)
    +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
    +
    +`typing-inspection` provides tools to inspect type annotations at runtime.
    +
    +## Installation
    +
    +From [PyPI](https://pypi.org/project/typing-inspection/):
    +
    +```bash
    +pip install typing-inspection
    +```
    +
    +The library can be imported from the `typing_inspection` module.
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/RECORD
    new file mode 100644
    index 0000000..8c67483
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/RECORD
    @@ -0,0 +1,13 @@
    +typing_inspection-0.4.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
    +typing_inspection-0.4.2.dist-info/METADATA,sha256=YQls0L_jxwQLb5jCKwLRkP4Bk20P92FsUTT-0CiRlTo,2552
    +typing_inspection-0.4.2.dist-info/RECORD,,
    +typing_inspection-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
    +typing_inspection-0.4.2.dist-info/licenses/LICENSE,sha256=gEtZsl8sMb0nj5ICoZrkmjlFqiZkOH4tChKMfKzGHsM,1090
    +typing_inspection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +typing_inspection/__pycache__/__init__.cpython-312.pyc,,
    +typing_inspection/__pycache__/introspection.cpython-312.pyc,,
    +typing_inspection/__pycache__/typing_objects.cpython-312.pyc,,
    +typing_inspection/introspection.py,sha256=dD5Ad4J6hAfF6UBzBO4sqSs1h2ybQVThkQofLWWVBP0,22534
    +typing_inspection/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
    +typing_inspection/typing_objects.py,sha256=kajVgh8J7UZ7wTidVxzFMpjwSnFGBoDoTqfVAAvOHZ8,17166
    +typing_inspection/typing_objects.pyi,sha256=u1NDpl_RJFnAUMAMx-WBd0SBKwVG7luLEc8ukSvRnZs,9401
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/WHEEL
    new file mode 100644
    index 0000000..12228d4
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/WHEEL
    @@ -0,0 +1,4 @@
    +Wheel-Version: 1.0
    +Generator: hatchling 1.27.0
    +Root-Is-Purelib: true
    +Tag: py3-none-any
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE
    new file mode 100644
    index 0000000..e825ad5
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE
    @@ -0,0 +1,21 @@
    +MIT License
    +
    +Copyright (c) Pydantic Services Inc. 2025 to present
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/__init__.py b/.venv/lib/python3.12/site-packages/typing_inspection/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py b/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py
    new file mode 100644
    index 0000000..d6c083e
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py
    @@ -0,0 +1,587 @@
    +"""High-level introspection utilities, used to inspect type annotations."""
    +
    +from __future__ import annotations
    +
    +import sys
    +import types
    +from collections.abc import Generator
    +from dataclasses import InitVar
    +from enum import Enum, IntEnum, auto
    +from typing import Any, Literal, NamedTuple, cast
    +
    +from typing_extensions import TypeAlias, assert_never, get_args, get_origin
    +
    +from . import typing_objects
    +
    +__all__ = (
    +    'AnnotationSource',
    +    'ForbiddenQualifier',
    +    'InspectedAnnotation',
    +    'Qualifier',
    +    'get_literal_values',
    +    'inspect_annotation',
    +    'is_union_origin',
    +)
    +
    +if sys.version_info >= (3, 14) or sys.version_info < (3, 10):
    +
    +    def is_union_origin(obj: Any, /) -> bool:
    +        """Return whether the provided origin is the union form.
    +
    +        ```pycon
    +        >>> is_union_origin(typing.Union)
    +        True
    +        >>> is_union_origin(get_origin(int | str))
    +        True
    +        >>> is_union_origin(types.UnionType)
    +        True
    +        ```
    +
    +        !!! note
    +            Since Python 3.14, both `Union[, , ...]` and ` |  | ...` forms create instances
    +            of the same [`typing.Union`][] class. As such, it is recommended to not use this function
    +            anymore (provided that you only support Python 3.14 or greater), and instead use the
    +            [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly:
    +
    +            ```python
    +            from typing import Union, get_origin
    +
    +            from typing_inspection import typing_objects
    +
    +            typ = int | str  # Or Union[int, str]
    +            origin = get_origin(typ)
    +            if typing_objects.is_union(origin):
    +                ...
    +            ```
    +        """
    +        return typing_objects.is_union(obj)
    +
    +
    +else:
    +
    +    def is_union_origin(obj: Any, /) -> bool:
    +        """Return whether the provided origin is the union form.
    +
    +        ```pycon
    +        >>> is_union_origin(typing.Union)
    +        True
    +        >>> is_union_origin(get_origin(int | str))
    +        True
    +        >>> is_union_origin(types.UnionType)
    +        True
    +        ```
    +
    +        !!! note
    +            Since Python 3.14, both `Union[, , ...]` and ` |  | ...` forms create instances
    +            of the same [`typing.Union`][] class. As such, it is recommended to not use this function
    +            anymore (provided that you only support Python 3.14 or greater), and instead use the
    +            [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly:
    +
    +            ```python
    +            from typing import Union, get_origin
    +
    +            from typing_inspection import typing_objects
    +
    +            typ = int | str  # Or Union[int, str]
    +            origin = get_origin(typ)
    +            if typing_objects.is_union(origin):
    +                ...
    +            ```
    +        """
    +        return typing_objects.is_union(obj) or obj is types.UnionType
    +
    +
    +def _literal_type_check(value: Any, /) -> None:
    +    """Type check the provided literal value against the legal parameters."""
    +    if (
    +        not isinstance(value, (int, bytes, str, bool, Enum, typing_objects.NoneType))
    +        and value is not typing_objects.NoneType
    +    ):
    +        raise TypeError(f'{value} is not a valid literal value, must be one of: int, bytes, str, Enum, None.')
    +
    +
    +def get_literal_values(
    +    annotation: Any,
    +    /,
    +    *,
    +    type_check: bool = False,
    +    unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager',
    +) -> Generator[Any]:
    +    """Yield the values contained in the provided [`Literal`][typing.Literal] [special form][].
    +
    +    Args:
    +        annotation: The [`Literal`][typing.Literal] [special form][] to unpack.
    +        type_check: Whether to check if the literal values are [legal parameters][literal-legal-parameters].
    +            Raises a [`TypeError`][] otherwise.
    +        unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/)
    +            [type aliases][type-aliases]. Can be one of:
    +
    +            - `'skip'`: Do not try to parse type aliases. Note that this can lead to incorrect results:
    +              ```pycon
    +              >>> type MyAlias = Literal[1, 2]
    +              >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="skip"))
    +              [MyAlias, 3]
    +              ```
    +
    +            - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias can't be inspected
    +              (because of an undefined forward reference).
    +
    +            - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions (the default):
    +              ```pycon
    +              >>> type MyAlias = Literal[1, 2]
    +              >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="eager"))
    +              [1, 2, 3]
    +              ```
    +
    +    Note:
    +        While `None` is [equivalent to][none] `type(None)`, the runtime implementation of [`Literal`][typing.Literal]
    +        does not de-duplicate them. This function makes sure this de-duplication is applied:
    +
    +        ```pycon
    +        >>> list(get_literal_values(Literal[NoneType, None]))
    +        [None]
    +        ```
    +
    +    Example:
    +        ```pycon
    +        >>> type Ints = Literal[1, 2]
    +        >>> list(get_literal_values(Literal[1, Ints], unpack_type_alias="skip"))
    +        ["a", Ints]
    +        >>> list(get_literal_values(Literal[1, Ints]))
    +        [1, 2]
    +        >>> list(get_literal_values(Literal[1.0], type_check=True))
    +        Traceback (most recent call last):
    +        ...
    +        TypeError: 1.0 is not a valid literal value, must be one of: int, bytes, str, Enum, None.
    +        ```
    +    """
    +    # `literal` is guaranteed to be a `Literal[...]` special form, so use
    +    # `__args__` directly instead of calling `get_args()`.
    +
    +    if unpack_type_aliases == 'skip':
    +        _has_none = False
    +        # `Literal` parameters are already deduplicated, no need to do it ourselves.
    +        # (we only check for `None` and `NoneType`, which should be considered as duplicates).
    +        for arg in annotation.__args__:
    +            if type_check:
    +                _literal_type_check(arg)
    +            if arg is None or arg is typing_objects.NoneType:
    +                if not _has_none:
    +                    yield None
    +                _has_none = True
    +            else:
    +                yield arg
    +    else:
    +        # We'll need to manually deduplicate parameters, see the `Literal` implementation in `typing`.
    +        values_and_type: list[tuple[Any, type[Any]]] = []
    +
    +        for arg in annotation.__args__:
    +            # Note: we could also check for generic aliases with a type alias as an origin.
    +            # However, it is very unlikely that this happens as type variables can't appear in
    +            # `Literal` forms, so the only valid (but unnecessary) use case would be something like:
    +            # `type Test[T] = Literal['a']` (and then use `Test[SomeType]`).
    +            if typing_objects.is_typealiastype(arg):
    +                try:
    +                    alias_value = arg.__value__
    +                except NameError:
    +                    if unpack_type_aliases == 'eager':
    +                        raise
    +                    # unpack_type_aliases == "lenient":
    +                    if type_check:
    +                        _literal_type_check(arg)
    +                    values_and_type.append((arg, type(arg)))
    +                else:
    +                    sub_args = get_literal_values(
    +                        alias_value, type_check=type_check, unpack_type_aliases=unpack_type_aliases
    +                    )
    +                    values_and_type.extend((a, type(a)) for a in sub_args)  # pyright: ignore[reportUnknownArgumentType]
    +            else:
    +                if type_check:
    +                    _literal_type_check(arg)
    +                if arg is typing_objects.NoneType:
    +                    values_and_type.append((None, typing_objects.NoneType))
    +                else:
    +                    values_and_type.append((arg, type(arg)))  # pyright: ignore[reportUnknownArgumentType]
    +
    +        try:
    +            dct = dict.fromkeys(values_and_type)
    +        except TypeError:
    +            # Unhashable parameters, the Python implementation allows them
    +            yield from (p for p, _ in values_and_type)
    +        else:
    +            yield from (p for p, _ in dct)
    +
    +
    +Qualifier: TypeAlias = Literal['required', 'not_required', 'read_only', 'class_var', 'init_var', 'final']
    +"""A [type qualifier][]."""
    +
    +_all_qualifiers: set[Qualifier] = set(get_args(Qualifier))
    +
    +
    +# TODO at some point, we could switch to an enum flag, so that multiple sources
    +# can be combined. However, is there a need for this?
    +class AnnotationSource(IntEnum):
    +    # TODO if/when https://peps.python.org/pep-0767/ is accepted, add 'read_only'
    +    # to CLASS and NAMED_TUPLE (even though for named tuples it is redundant).
    +
    +    """The source of an annotation, e.g. a class or a function.
    +
    +    Depending on the source, different [type qualifiers][type qualifier] may be (dis)allowed.
    +    """
    +
    +    ASSIGNMENT_OR_VARIABLE = auto()
    +    """An annotation used in an assignment or variable annotation:
    +
    +    ```python
    +    x: Final[int] = 1
    +    y: Final[str]
    +    ```
    +
    +    **Allowed type qualifiers:** [`Final`][typing.Final].
    +    """
    +
    +    CLASS = auto()
    +    """An annotation used in the body of a class:
    +
    +    ```python
    +    class Test:
    +        x: Final[int] = 1
    +        y: ClassVar[str]
    +    ```
    +
    +    **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final].
    +    """
    +
    +    DATACLASS = auto()
    +    """An annotation used in the body of a dataclass:
    +
    +    ```python
    +    @dataclass
    +    class Test:
    +        x: Final[int] = 1
    +        y: InitVar[str] = 'test'
    +    ```
    +
    +    **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final], [`InitVar`][dataclasses.InitVar].
    +    """  # noqa: E501
    +
    +    TYPED_DICT = auto()
    +    """An annotation used in the body of a [`TypedDict`][typing.TypedDict]:
    +
    +    ```python
    +    class TD(TypedDict):
    +        x: Required[ReadOnly[int]]
    +        y: ReadOnly[NotRequired[str]]
    +    ```
    +
    +    **Allowed type qualifiers:** [`ReadOnly`][typing.ReadOnly], [`Required`][typing.Required],
    +    [`NotRequired`][typing.NotRequired].
    +    """
    +
    +    NAMED_TUPLE = auto()
    +    """An annotation used in the body of a [`NamedTuple`][typing.NamedTuple].
    +
    +    ```python
    +    class NT(NamedTuple):
    +        x: int
    +        y: str
    +    ```
    +
    +    **Allowed type qualifiers:** none.
    +    """
    +
    +    FUNCTION = auto()
    +    """An annotation used in a function, either for a parameter or the return value.
    +
    +    ```python
    +    def func(a: int) -> str:
    +        ...
    +    ```
    +
    +    **Allowed type qualifiers:** none.
    +    """
    +
    +    ANY = auto()
    +    """An annotation that might come from any source.
    +
    +    **Allowed type qualifiers:** all.
    +    """
    +
    +    BARE = auto()
    +    """An annotation that is inspected as is.
    +
    +    **Allowed type qualifiers:** none.
    +    """
    +
    +    @property
    +    def allowed_qualifiers(self) -> set[Qualifier]:
    +        """The allowed [type qualifiers][type qualifier] for this annotation source."""
    +        # TODO use a match statement when Python 3.9 support is dropped.
    +        if self is AnnotationSource.ASSIGNMENT_OR_VARIABLE:
    +            return {'final'}
    +        elif self is AnnotationSource.CLASS:
    +            return {'final', 'class_var'}
    +        elif self is AnnotationSource.DATACLASS:
    +            return {'final', 'class_var', 'init_var'}
    +        elif self is AnnotationSource.TYPED_DICT:
    +            return {'required', 'not_required', 'read_only'}
    +        elif self in (AnnotationSource.NAMED_TUPLE, AnnotationSource.FUNCTION, AnnotationSource.BARE):
    +            return set()
    +        elif self is AnnotationSource.ANY:
    +            return _all_qualifiers
    +        else:  # pragma: no cover
    +            assert_never(self)
    +
    +
    +class ForbiddenQualifier(Exception):
    +    """The provided [type qualifier][] is forbidden."""
    +
    +    qualifier: Qualifier
    +    """The forbidden qualifier."""
    +
    +    def __init__(self, qualifier: Qualifier, /) -> None:
    +        self.qualifier = qualifier
    +
    +
    +class _UnknownTypeEnum(Enum):
    +    UNKNOWN = auto()
    +
    +    def __str__(self) -> str:
    +        return 'UNKNOWN'
    +
    +    def __repr__(self) -> str:
    +        return ''
    +
    +
    +UNKNOWN = _UnknownTypeEnum.UNKNOWN
    +"""A sentinel value used when no [type expression][] is present."""
    +
    +_UnkownType: TypeAlias = Literal[_UnknownTypeEnum.UNKNOWN]
    +"""The type of the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel value."""
    +
    +
    +class InspectedAnnotation(NamedTuple):
    +    """The result of the inspected annotation."""
    +
    +    type: Any | _UnkownType
    +    """The final [type expression][], with [type qualifiers][type qualifier] and annotated metadata stripped.
    +
    +    If no type expression is available, the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel
    +    value is used instead. This is the case when a [type qualifier][] is used with no type annotation:
    +
    +    ```python
    +    ID: Final = 1
    +
    +    class C:
    +        x: ClassVar = 'test'
    +    ```
    +    """
    +
    +    qualifiers: set[Qualifier]
    +    """The [type qualifiers][type qualifier] present on the annotation."""
    +
    +    metadata: list[Any]
    +    """The annotated metadata."""
    +
    +
    +def inspect_annotation(  # noqa: PLR0915
    +    annotation: Any,
    +    /,
    +    *,
    +    annotation_source: AnnotationSource,
    +    unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'skip',
    +) -> InspectedAnnotation:
    +    """Inspect an [annotation expression][], extracting any [type qualifier][] and metadata.
    +
    +    An [annotation expression][] is a [type expression][] optionally surrounded by one or more
    +    [type qualifiers][type qualifier] or by [`Annotated`][typing.Annotated]. This function will:
    +
    +    - Unwrap the type expression, keeping track of the type qualifiers.
    +    - Unwrap [`Annotated`][typing.Annotated] forms, keeping track of the annotated metadata.
    +
    +    Args:
    +        annotation: The annotation expression to be inspected.
    +        annotation_source: The source of the annotation. Depending on the source (e.g. a class), different type
    +            qualifiers may be (dis)allowed. To allow any type qualifier, use
    +            [`AnnotationSource.ANY`][typing_inspection.introspection.AnnotationSource.ANY].
    +        unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/)
    +            [type aliases][type-aliases]. Can be one of:
    +
    +            - `'skip'`: Do not try to parse type aliases (the default):
    +              ```pycon
    +              >>> type MyInt = Annotated[int, 'meta']
    +              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='skip')
    +              InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[])
    +              ```
    +
    +            - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias
    +              can't be inspected (because of an undefined forward reference):
    +              ```pycon
    +              >>> type MyInt = Annotated[Undefined, 'meta']
    +              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient')
    +              InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[])
    +              >>> Undefined = int
    +              >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient')
    +              InspectedAnnotation(type=int, qualifiers={}, metadata=['meta'])
    +              ```
    +
    +            - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions.
    +
    +    Returns:
    +        The result of the inspected annotation, where the type expression, used qualifiers and metadata is stored.
    +
    +    Example:
    +        ```pycon
    +        >>> inspect_annotation(
    +        ...     Final[Annotated[ClassVar[Annotated[int, 'meta_1']], 'meta_2']],
    +        ...     annotation_source=AnnotationSource.CLASS,
    +        ... )
    +        ...
    +        InspectedAnnotation(type=int, qualifiers={'class_var', 'final'}, metadata=['meta_1', 'meta_2'])
    +        ```
    +    """
    +    allowed_qualifiers = annotation_source.allowed_qualifiers
    +    qualifiers: set[Qualifier] = set()
    +    metadata: list[Any] = []
    +
    +    while True:
    +        annotation, _meta = _unpack_annotated(annotation, unpack_type_aliases=unpack_type_aliases)
    +        if _meta:
    +            metadata = _meta + metadata
    +            continue
    +
    +        origin = get_origin(annotation)
    +        if origin is not None:
    +            if typing_objects.is_classvar(origin):
    +                if 'class_var' not in allowed_qualifiers:
    +                    raise ForbiddenQualifier('class_var')
    +                qualifiers.add('class_var')
    +                annotation = annotation.__args__[0]
    +            elif typing_objects.is_final(origin):
    +                if 'final' not in allowed_qualifiers:
    +                    raise ForbiddenQualifier('final')
    +                qualifiers.add('final')
    +                annotation = annotation.__args__[0]
    +            elif typing_objects.is_required(origin):
    +                if 'required' not in allowed_qualifiers:
    +                    raise ForbiddenQualifier('required')
    +                qualifiers.add('required')
    +                annotation = annotation.__args__[0]
    +            elif typing_objects.is_notrequired(origin):
    +                if 'not_required' not in allowed_qualifiers:
    +                    raise ForbiddenQualifier('not_required')
    +                qualifiers.add('not_required')
    +                annotation = annotation.__args__[0]
    +            elif typing_objects.is_readonly(origin):
    +                if 'read_only' not in allowed_qualifiers:
    +                    raise ForbiddenQualifier('not_required')
    +                qualifiers.add('read_only')
    +                annotation = annotation.__args__[0]
    +            else:
    +                # origin is not None but not a type qualifier nor `Annotated` (e.g. `list[int]`):
    +                break
    +        elif isinstance(annotation, InitVar):
    +            if 'init_var' not in allowed_qualifiers:
    +                raise ForbiddenQualifier('init_var')
    +            qualifiers.add('init_var')
    +            annotation = cast(Any, annotation.type)
    +        else:
    +            break
    +
    +    # `Final`, `ClassVar` and `InitVar` are type qualifiers allowed to be used as a bare annotation:
    +    if typing_objects.is_final(annotation):
    +        if 'final' not in allowed_qualifiers:
    +            raise ForbiddenQualifier('final')
    +        qualifiers.add('final')
    +        annotation = UNKNOWN
    +    elif typing_objects.is_classvar(annotation):
    +        if 'class_var' not in allowed_qualifiers:
    +            raise ForbiddenQualifier('class_var')
    +        qualifiers.add('class_var')
    +        annotation = UNKNOWN
    +    elif annotation is InitVar:
    +        if 'init_var' not in allowed_qualifiers:
    +            raise ForbiddenQualifier('init_var')
    +        qualifiers.add('init_var')
    +        annotation = UNKNOWN
    +
    +    return InspectedAnnotation(annotation, qualifiers, metadata)
    +
    +
    +def _unpack_annotated_inner(
    +    annotation: Any, unpack_type_aliases: Literal['lenient', 'eager'], check_annotated: bool
    +) -> tuple[Any, list[Any]]:
    +    origin = get_origin(annotation)
    +    if check_annotated and typing_objects.is_annotated(origin):
    +        annotated_type = annotation.__origin__
    +        metadata = list(annotation.__metadata__)
    +
    +        # The annotated type might be a PEP 695 type alias, so we need to recursively
    +        # unpack it. Because Python already flattens `Annotated[Annotated[, ...], ...]` forms,
    +        # we can skip the `is_annotated()` check in the next call:
    +        annotated_type, sub_meta = _unpack_annotated_inner(
    +            annotated_type, unpack_type_aliases=unpack_type_aliases, check_annotated=False
    +        )
    +        metadata = sub_meta + metadata
    +        return annotated_type, metadata
    +    elif typing_objects.is_typealiastype(annotation):
    +        try:
    +            value = annotation.__value__
    +        except NameError:
    +            if unpack_type_aliases == 'eager':
    +                raise
    +        else:
    +            typ, metadata = _unpack_annotated_inner(
    +                value, unpack_type_aliases=unpack_type_aliases, check_annotated=True
    +            )
    +            if metadata:
    +                # Having metadata means the type alias' `__value__` was an `Annotated` form
    +                # (or, recursively, a type alias to an `Annotated` form). It is important to check
    +                # for this, as we don't want to unpack other type aliases (e.g. `type MyInt = int`).
    +                return typ, metadata
    +            return annotation, []
    +    elif typing_objects.is_typealiastype(origin):
    +        # When parameterized, PEP 695 type aliases become generic aliases
    +        # (e.g. with `type MyList[T] = Annotated[list[T], ...]`, `MyList[int]`
    +        # is a generic alias).
    +        try:
    +            value = origin.__value__
    +        except NameError:
    +            if unpack_type_aliases == 'eager':
    +                raise
    +        else:
    +            # While Python already handles type variable replacement for simple `Annotated` forms,
    +            # we need to manually apply the same logic for PEP 695 type aliases:
    +            # - With `MyList = Annotated[list[T], ...]`, `MyList[int] == Annotated[list[int], ...]`
    +            # - With `type MyList[T] = Annotated[list[T], ...]`, `MyList[int].__value__ == Annotated[list[T], ...]`.
    +
    +            try:
    +                # To do so, we emulate the parameterization of the value with the arguments:
    +                # with `type MyList[T] = Annotated[list[T], ...]`, to emulate `MyList[int]`,
    +                # we do `Annotated[list[T], ...][int]` (which gives `Annotated[list[T], ...]`):
    +                value = value[annotation.__args__]
    +            except TypeError:
    +                # Might happen if the type alias is parameterized, but its value doesn't have any
    +                # type variables, e.g. `type MyInt[T] = int`.
    +                pass
    +            typ, metadata = _unpack_annotated_inner(
    +                value, unpack_type_aliases=unpack_type_aliases, check_annotated=True
    +            )
    +            if metadata:
    +                return typ, metadata
    +            return annotation, []
    +
    +    return annotation, []
    +
    +
    +# This could eventually be made public:
    +def _unpack_annotated(
    +    annotation: Any, /, *, unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager'
    +) -> tuple[Any, list[Any]]:
    +    if unpack_type_aliases == 'skip':
    +        if typing_objects.is_annotated(get_origin(annotation)):
    +            return annotation.__origin__, list(annotation.__metadata__)
    +        else:
    +            return annotation, []
    +
    +    return _unpack_annotated_inner(annotation, unpack_type_aliases=unpack_type_aliases, check_annotated=True)
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/py.typed b/.venv/lib/python3.12/site-packages/typing_inspection/py.typed
    new file mode 100644
    index 0000000..e69de29
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py
    new file mode 100644
    index 0000000..dc44ba9
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py
    @@ -0,0 +1,607 @@
    +"""Low-level introspection utilities for [`typing`][] members.
    +
    +The provided functions in this module check against both the [`typing`][] and [`typing_extensions`][]
    +variants, if they exists and are different.
    +"""
    +# ruff: noqa: UP006
    +
    +import collections.abc
    +import contextlib
    +import re
    +import sys
    +import typing
    +import warnings
    +from textwrap import dedent
    +from types import FunctionType, GenericAlias
    +from typing import Any, Final
    +
    +import typing_extensions
    +from typing_extensions import LiteralString, TypeAliasType, TypeIs, deprecated
    +
    +__all__ = (
    +    'DEPRECATED_ALIASES',
    +    'NoneType',
    +    'is_annotated',
    +    'is_any',
    +    'is_classvar',
    +    'is_concatenate',
    +    'is_deprecated',
    +    'is_final',
    +    'is_forwardref',
    +    'is_generic',
    +    'is_literal',
    +    'is_literalstring',
    +    'is_namedtuple',
    +    'is_never',
    +    'is_newtype',
    +    'is_nodefault',
    +    'is_noextraitems',
    +    'is_noreturn',
    +    'is_notrequired',
    +    'is_paramspec',
    +    'is_paramspecargs',
    +    'is_paramspeckwargs',
    +    'is_readonly',
    +    'is_required',
    +    'is_self',
    +    'is_typealias',
    +    'is_typealiastype',
    +    'is_typeguard',
    +    'is_typeis',
    +    'is_typevar',
    +    'is_typevartuple',
    +    'is_union',
    +    'is_unpack',
    +)
    +
    +_IS_PY310 = sys.version_info[:2] == (3, 10)
    +
    +
    +def _compile_identity_check_function(member: LiteralString, function_name: LiteralString) -> FunctionType:
    +    """Create a function checking that the function argument is the (unparameterized) typing `member`.
    +
    +    The function will make sure to check against both the `typing` and `typing_extensions`
    +    variants as depending on the Python version, the `typing_extensions` variant might be different.
    +    For instance, on Python 3.9:
    +
    +    ```pycon
    +    >>> from typing import Literal as t_Literal
    +    >>> from typing_extensions import Literal as te_Literal, get_origin
    +
    +    >>> t_Literal is te_Literal
    +    False
    +    >>> get_origin(t_Literal[1])
    +    typing.Literal
    +    >>> get_origin(te_Literal[1])
    +    typing_extensions.Literal
    +    ```
    +    """
    +    in_typing = hasattr(typing, member)
    +    in_typing_extensions = hasattr(typing_extensions, member)
    +
    +    if in_typing and in_typing_extensions:
    +        if getattr(typing, member) is getattr(typing_extensions, member):
    +            check_code = f'obj is typing.{member}'
    +        else:
    +            check_code = f'obj is typing.{member} or obj is typing_extensions.{member}'
    +    elif in_typing and not in_typing_extensions:
    +        check_code = f'obj is typing.{member}'
    +    elif not in_typing and in_typing_extensions:
    +        check_code = f'obj is typing_extensions.{member}'
    +    else:
    +        check_code = 'False'
    +
    +    func_code = dedent(f"""
    +    def {function_name}(obj: Any, /) -> bool:
    +        return {check_code}
    +    """)
    +
    +    locals_: dict[str, Any] = {}
    +    globals_: dict[str, Any] = {'Any': Any, 'typing': typing, 'typing_extensions': typing_extensions}
    +    exec(func_code, globals_, locals_)
    +    return locals_[function_name]
    +
    +
    +def _compile_isinstance_check_function(member: LiteralString, function_name: LiteralString) -> FunctionType:
    +    """Create a function checking that the function is an instance of the typing `member`.
    +
    +    The function will make sure to check against both the `typing` and `typing_extensions`
    +    variants as depending on the Python version, the `typing_extensions` variant might be different.
    +    """
    +    in_typing = hasattr(typing, member)
    +    in_typing_extensions = hasattr(typing_extensions, member)
    +
    +    if in_typing and in_typing_extensions:
    +        if getattr(typing, member) is getattr(typing_extensions, member):
    +            check_code = f'isinstance(obj, typing.{member})'
    +        else:
    +            check_code = f'isinstance(obj, (typing.{member}, typing_extensions.{member}))'
    +    elif in_typing and not in_typing_extensions:
    +        check_code = f'isinstance(obj, typing.{member})'
    +    elif not in_typing and in_typing_extensions:
    +        check_code = f'isinstance(obj, typing_extensions.{member})'
    +    else:
    +        check_code = 'False'
    +
    +    func_code = dedent(f"""
    +    def {function_name}(obj: Any, /) -> 'TypeIs[{member}]':
    +        return {check_code}
    +    """)
    +
    +    locals_: dict[str, Any] = {}
    +    globals_: dict[str, Any] = {'Any': Any, 'typing': typing, 'typing_extensions': typing_extensions}
    +    exec(func_code, globals_, locals_)
    +    return locals_[function_name]
    +
    +
    +if sys.version_info >= (3, 10):
    +    from types import NoneType
    +else:
    +    NoneType = type(None)
    +
    +# Keep this ordered, as per `typing.__all__`:
    +
    +is_annotated = _compile_identity_check_function('Annotated', 'is_annotated')
    +is_annotated.__doc__ = """
    +Return whether the argument is the [`Annotated`][typing.Annotated] [special form][].
    +
    +```pycon
    +>>> is_annotated(Annotated)
    +True
    +>>> is_annotated(Annotated[int, ...])
    +False
    +```
    +"""
    +
    +is_any = _compile_identity_check_function('Any', 'is_any')
    +is_any.__doc__ = """
    +Return whether the argument is the [`Any`][typing.Any] [special form][].
    +
    +```pycon
    +>>> is_any(Any)
    +True
    +```
    +"""
    +
    +is_classvar = _compile_identity_check_function('ClassVar', 'is_classvar')
    +is_classvar.__doc__ = """
    +Return whether the argument is the [`ClassVar`][typing.ClassVar] [type qualifier][].
    +
    +```pycon
    +>>> is_classvar(ClassVar)
    +True
    +>>> is_classvar(ClassVar[int])
    +>>> False
    +```
    +"""
    +
    +is_concatenate = _compile_identity_check_function('Concatenate', 'is_concatenate')
    +is_concatenate.__doc__ = """
    +Return whether the argument is the [`Concatenate`][typing.Concatenate] [special form][].
    +
    +```pycon
    +>>> is_concatenate(Concatenate)
    +True
    +>>> is_concatenate(Concatenate[int, P])
    +False
    +```
    +"""
    +
    +is_final = _compile_identity_check_function('Final', 'is_final')
    +is_final.__doc__ = """
    +Return whether the argument is the [`Final`][typing.Final] [type qualifier][].
    +
    +```pycon
    +>>> is_final(Final)
    +True
    +>>> is_final(Final[int])
    +False
    +```
    +"""
    +
    +
    +# Unlikely to have a different version in `typing-extensions`, but keep it consistent.
    +# Also note that starting in 3.14, this is an alias to `annotationlib.ForwardRef`, but
    +# accessing it from `typing` doesn't seem to be deprecated.
    +is_forwardref = _compile_isinstance_check_function('ForwardRef', 'is_forwardref')
    +is_forwardref.__doc__ = """
    +Return whether the argument is an instance of [`ForwardRef`][typing.ForwardRef].
    +
    +```pycon
    +>>> is_forwardref(ForwardRef('T'))
    +True
    +```
    +"""
    +
    +
    +is_generic = _compile_identity_check_function('Generic', 'is_generic')
    +is_generic.__doc__ = """
    +Return whether the argument is the [`Generic`][typing.Generic] [special form][].
    +
    +```pycon
    +>>> is_generic(Generic)
    +True
    +>>> is_generic(Generic[T])
    +False
    +```
    +"""
    +
    +is_literal = _compile_identity_check_function('Literal', 'is_literal')
    +is_literal.__doc__ = """
    +Return whether the argument is the [`Literal`][typing.Literal] [special form][].
    +
    +```pycon
    +>>> is_literal(Literal)
    +True
    +>>> is_literal(Literal["a"])
    +False
    +```
    +"""
    +
    +
    +# `get_origin(Optional[int]) is Union`, so `is_optional()` isn't implemented.
    +
    +is_paramspec = _compile_isinstance_check_function('ParamSpec', 'is_paramspec')
    +is_paramspec.__doc__ = """
    +Return whether the argument is an instance of [`ParamSpec`][typing.ParamSpec].
    +
    +```pycon
    +>>> P = ParamSpec('P')
    +>>> is_paramspec(P)
    +True
    +```
    +"""
    +
    +# Protocol?
    +
    +is_typevar = _compile_isinstance_check_function('TypeVar', 'is_typevar')
    +is_typevar.__doc__ = """
    +Return whether the argument is an instance of [`TypeVar`][typing.TypeVar].
    +
    +```pycon
    +>>> T = TypeVar('T')
    +>>> is_typevar(T)
    +True
    +```
    +"""
    +
    +is_typevartuple = _compile_isinstance_check_function('TypeVarTuple', 'is_typevartuple')
    +is_typevartuple.__doc__ = """
    +Return whether the argument is an instance of [`TypeVarTuple`][typing.TypeVarTuple].
    +
    +```pycon
    +>>> Ts = TypeVarTuple('Ts')
    +>>> is_typevartuple(Ts)
    +True
    +```
    +"""
    +
    +is_union = _compile_identity_check_function('Union', 'is_union')
    +is_union.__doc__ = """
    +Return whether the argument is the [`Union`][typing.Union] [special form][].
    +
    +This function can also be used to check for the [`Optional`][typing.Optional] [special form][],
    +as at runtime, `Optional[int]` is equivalent to `Union[int, None]`.
    +
    +```pycon
    +>>> is_union(Union)
    +True
    +>>> is_union(Union[int, str])
    +False
    +```
    +
    +!!! warning
    +    This does not check for unions using the [new syntax][types-union] (e.g. `int | str`).
    +"""
    +
    +
    +def is_namedtuple(obj: Any, /) -> bool:
    +    """Return whether the argument is a named tuple type.
    +
    +    This includes [`NamedTuple`][typing.NamedTuple] subclasses and classes created from the
    +    [`collections.namedtuple`][] factory function.
    +
    +    ```pycon
    +    >>> class User(NamedTuple):
    +    ...     name: str
    +    ...
    +    >>> is_namedtuple(User)
    +    True
    +    >>> City = collections.namedtuple('City', [])
    +    >>> is_namedtuple(City)
    +    True
    +    >>> is_namedtuple(NamedTuple)
    +    False
    +    ```
    +    """
    +    return isinstance(obj, type) and issubclass(obj, tuple) and hasattr(obj, '_fields')  # pyright: ignore[reportUnknownArgumentType]
    +
    +
    +# TypedDict?
    +
    +# BinaryIO? IO? TextIO?
    +
    +is_literalstring = _compile_identity_check_function('LiteralString', 'is_literalstring')
    +is_literalstring.__doc__ = """
    +Return whether the argument is the [`LiteralString`][typing.LiteralString] [special form][].
    +
    +```pycon
    +>>> is_literalstring(LiteralString)
    +True
    +```
    +"""
    +
    +is_never = _compile_identity_check_function('Never', 'is_never')
    +is_never.__doc__ = """
    +Return whether the argument is the [`Never`][typing.Never] [special form][].
    +
    +```pycon
    +>>> is_never(Never)
    +True
    +```
    +"""
    +
    +if sys.version_info >= (3, 10):
    +    is_newtype = _compile_isinstance_check_function('NewType', 'is_newtype')
    +else:  # On Python 3.10, `NewType` is a function.
    +
    +    def is_newtype(obj: Any, /) -> bool:
    +        return hasattr(obj, '__supertype__')
    +
    +
    +is_newtype.__doc__ = """
    +Return whether the argument is a [`NewType`][typing.NewType].
    +
    +```pycon
    +>>> UserId = NewType("UserId", int)
    +>>> is_newtype(UserId)
    +True
    +```
    +"""
    +
    +is_nodefault = _compile_identity_check_function('NoDefault', 'is_nodefault')
    +is_nodefault.__doc__ = """
    +Return whether the argument is the [`NoDefault`][typing.NoDefault] sentinel object.
    +
    +```pycon
    +>>> is_nodefault(NoDefault)
    +True
    +```
    +"""
    +
    +is_noextraitems = _compile_identity_check_function('NoExtraItems', 'is_noextraitems')
    +is_noextraitems.__doc__ = """
    +Return whether the argument is the `NoExtraItems` sentinel object.
    +
    +```pycon
    +>>> is_noextraitems(NoExtraItems)
    +True
    +```
    +"""
    +
    +is_noreturn = _compile_identity_check_function('NoReturn', 'is_noreturn')
    +is_noreturn.__doc__ = """
    +Return whether the argument is the [`NoReturn`][typing.NoReturn] [special form][].
    +
    +```pycon
    +>>> is_noreturn(NoReturn)
    +True
    +>>> is_noreturn(Never)
    +False
    +```
    +"""
    +
    +is_notrequired = _compile_identity_check_function('NotRequired', 'is_notrequired')
    +is_notrequired.__doc__ = """
    +Return whether the argument is the [`NotRequired`][typing.NotRequired] [special form][].
    +
    +```pycon
    +>>> is_notrequired(NotRequired)
    +True
    +```
    +"""
    +
    +is_paramspecargs = _compile_isinstance_check_function('ParamSpecArgs', 'is_paramspecargs')
    +is_paramspecargs.__doc__ = """
    +Return whether the argument is an instance of [`ParamSpecArgs`][typing.ParamSpecArgs].
    +
    +```pycon
    +>>> P = ParamSpec('P')
    +>>> is_paramspecargs(P.args)
    +True
    +```
    +"""
    +
    +is_paramspeckwargs = _compile_isinstance_check_function('ParamSpecKwargs', 'is_paramspeckwargs')
    +is_paramspeckwargs.__doc__ = """
    +Return whether the argument is an instance of [`ParamSpecKwargs`][typing.ParamSpecKwargs].
    +
    +```pycon
    +>>> P = ParamSpec('P')
    +>>> is_paramspeckwargs(P.kwargs)
    +True
    +```
    +"""
    +
    +is_readonly = _compile_identity_check_function('ReadOnly', 'is_readonly')
    +is_readonly.__doc__ = """
    +Return whether the argument is the [`ReadOnly`][typing.ReadOnly] [special form][].
    +
    +```pycon
    +>>> is_readonly(ReadOnly)
    +True
    +```
    +"""
    +
    +is_required = _compile_identity_check_function('Required', 'is_required')
    +is_required.__doc__ = """
    +Return whether the argument is the [`Required`][typing.Required] [special form][].
    +
    +```pycon
    +>>> is_required(Required)
    +True
    +```
    +"""
    +
    +is_self = _compile_identity_check_function('Self', 'is_self')
    +is_self.__doc__ = """
    +Return whether the argument is the [`Self`][typing.Self] [special form][].
    +
    +```pycon
    +>>> is_self(Self)
    +True
    +```
    +"""
    +
    +# TYPE_CHECKING?
    +
    +is_typealias = _compile_identity_check_function('TypeAlias', 'is_typealias')
    +is_typealias.__doc__ = """
    +Return whether the argument is the [`TypeAlias`][typing.TypeAlias] [special form][].
    +
    +```pycon
    +>>> is_typealias(TypeAlias)
    +True
    +```
    +"""
    +
    +is_typeguard = _compile_identity_check_function('TypeGuard', 'is_typeguard')
    +is_typeguard.__doc__ = """
    +Return whether the argument is the [`TypeGuard`][typing.TypeGuard] [special form][].
    +
    +```pycon
    +>>> is_typeguard(TypeGuard)
    +True
    +```
    +"""
    +
    +is_typeis = _compile_identity_check_function('TypeIs', 'is_typeis')
    +is_typeis.__doc__ = """
    +Return whether the argument is the [`TypeIs`][typing.TypeIs] [special form][].
    +
    +```pycon
    +>>> is_typeis(TypeIs)
    +True
    +```
    +"""
    +
    +_is_typealiastype_inner = _compile_isinstance_check_function('TypeAliasType', '_is_typealiastype_inner')
    +
    +
    +if _IS_PY310:
    +    # Parameterized PEP 695 type aliases are instances of `types.GenericAlias` in typing_extensions>=4.13.0.
    +    # On Python 3.10, with `Alias[int]` being such an instance of `GenericAlias`,
    +    # `isinstance(Alias[int], TypeAliasType)` returns `True`.
    +    # See https://github.com/python/cpython/issues/89828.
    +    def is_typealiastype(obj: Any, /) -> 'TypeIs[TypeAliasType]':
    +        return type(obj) is not GenericAlias and _is_typealiastype_inner(obj)
    +else:
    +    is_typealiastype = _compile_isinstance_check_function('TypeAliasType', 'is_typealiastype')
    +
    +is_typealiastype.__doc__ = """
    +Return whether the argument is a [`TypeAliasType`][typing.TypeAliasType] instance.
    +
    +```pycon
    +>>> type MyInt = int
    +>>> is_typealiastype(MyInt)
    +True
    +>>> MyStr = TypeAliasType("MyStr", str)
    +>>> is_typealiastype(MyStr):
    +True
    +>>> type MyList[T] = list[T]
    +>>> is_typealiastype(MyList[int])
    +False
    +```
    +"""
    +
    +is_unpack = _compile_identity_check_function('Unpack', 'is_unpack')
    +is_unpack.__doc__ = """
    +Return whether the argument is the [`Unpack`][typing.Unpack] [special form][].
    +
    +```pycon
    +>>> is_unpack(Unpack)
    +True
    +>>> is_unpack(Unpack[Ts])
    +False
    +```
    +"""
    +
    +
    +if sys.version_info >= (3, 13):
    +
    +    def is_deprecated(obj: Any, /) -> 'TypeIs[deprecated]':
    +        return isinstance(obj, (warnings.deprecated, typing_extensions.deprecated))
    +
    +else:
    +
    +    def is_deprecated(obj: Any, /) -> 'TypeIs[deprecated]':
    +        return isinstance(obj, typing_extensions.deprecated)
    +
    +
    +is_deprecated.__doc__ = """
    +Return whether the argument is a [`deprecated`][warnings.deprecated] instance.
    +
    +This also includes the [`typing_extensions` backport][typing_extensions.deprecated].
    +
    +```pycon
    +>>> is_deprecated(warnings.deprecated('message'))
    +True
    +>>> is_deprecated(typing_extensions.deprecated('message'))
    +True
    +```
    +"""
    +
    +
    +# Aliases defined in the `typing` module using `typing._SpecialGenericAlias` (itself aliased as `alias()`):
    +DEPRECATED_ALIASES: Final[dict[Any, type[Any]]] = {
    +    typing.Hashable: collections.abc.Hashable,
    +    typing.Awaitable: collections.abc.Awaitable,
    +    typing.Coroutine: collections.abc.Coroutine,
    +    typing.AsyncIterable: collections.abc.AsyncIterable,
    +    typing.AsyncIterator: collections.abc.AsyncIterator,
    +    typing.Iterable: collections.abc.Iterable,
    +    typing.Iterator: collections.abc.Iterator,
    +    typing.Reversible: collections.abc.Reversible,
    +    typing.Sized: collections.abc.Sized,
    +    typing.Container: collections.abc.Container,
    +    typing.Collection: collections.abc.Collection,
    +    # type ignore reason: https://github.com/python/typeshed/issues/6257:
    +    typing.Callable: collections.abc.Callable,  # pyright: ignore[reportAssignmentType, reportUnknownMemberType]
    +    typing.AbstractSet: collections.abc.Set,
    +    typing.MutableSet: collections.abc.MutableSet,
    +    typing.Mapping: collections.abc.Mapping,
    +    typing.MutableMapping: collections.abc.MutableMapping,
    +    typing.Sequence: collections.abc.Sequence,
    +    typing.MutableSequence: collections.abc.MutableSequence,
    +    typing.Tuple: tuple,
    +    typing.List: list,
    +    typing.Deque: collections.deque,
    +    typing.Set: set,
    +    typing.FrozenSet: frozenset,
    +    typing.MappingView: collections.abc.MappingView,
    +    typing.KeysView: collections.abc.KeysView,
    +    typing.ItemsView: collections.abc.ItemsView,
    +    typing.ValuesView: collections.abc.ValuesView,
    +    typing.Dict: dict,
    +    typing.DefaultDict: collections.defaultdict,
    +    typing.OrderedDict: collections.OrderedDict,
    +    typing.Counter: collections.Counter,
    +    typing.ChainMap: collections.ChainMap,
    +    typing.Generator: collections.abc.Generator,
    +    typing.AsyncGenerator: collections.abc.AsyncGenerator,
    +    typing.Type: type,
    +    # Defined in `typing.__getattr__`:
    +    typing.Pattern: re.Pattern,
    +    typing.Match: re.Match,
    +    typing.ContextManager: contextlib.AbstractContextManager,
    +    typing.AsyncContextManager: contextlib.AbstractAsyncContextManager,
    +    # Skipped: `ByteString` (deprecated, removed in 3.14)
    +}
    +"""A mapping between the deprecated typing aliases to their replacement, as per [PEP 585](https://peps.python.org/pep-0585/)."""
    +
    +
    +# Add the `typing_extensions` aliases:
    +for alias, target in list(DEPRECATED_ALIASES.items()):
    +    # Use `alias.__name__` when we drop support for Python 3.9
    +    if (te_alias := getattr(typing_extensions, alias._name, None)) is not None:
    +        DEPRECATED_ALIASES[te_alias] = target
    diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi
    new file mode 100644
    index 0000000..5071598
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi
    @@ -0,0 +1,417 @@
    +# Stub file generated using:
    +# `stubgen --inspect-mode --include-docstrings -m typing_inspection.typing_objects`
    +# (manual edits need to be applied).
    +"""Low-level introspection utilities for [`typing`][] members.
    +
    +The provided functions in this module check against both the [`typing`][] and [`typing_extensions`][]
    +variants, if they exists and are different.
    +"""
    +
    +import sys
    +from typing import Any, Final, ForwardRef, NewType, TypeVar
    +
    +from typing_extensions import ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeAliasType, TypeIs, TypeVarTuple, deprecated
    +
    +__all__ = [
    +    'DEPRECATED_ALIASES',
    +    'NoneType',
    +    'is_annotated',
    +    'is_any',
    +    'is_classvar',
    +    'is_concatenate',
    +    'is_deprecated',
    +    'is_final',
    +    'is_generic',
    +    'is_literal',
    +    'is_literalstring',
    +    'is_namedtuple',
    +    'is_never',
    +    'is_newtype',
    +    'is_nodefault',
    +    'is_noextraitems',
    +    'is_noreturn',
    +    'is_notrequired',
    +    'is_paramspec',
    +    'is_paramspecargs',
    +    'is_paramspeckwargs',
    +    'is_readonly',
    +    'is_required',
    +    'is_self',
    +    'is_typealias',
    +    'is_typealiastype',
    +    'is_typeguard',
    +    'is_typeis',
    +    'is_typevar',
    +    'is_typevartuple',
    +    'is_union',
    +    'is_unpack',
    +]
    +
    +if sys.version_info >= (3, 10):
    +    from types import NoneType
    +else:
    +    NoneType = type(None)
    +
    +def is_annotated(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Annotated`][typing.Annotated] [special form][].
    +
    +    ```pycon
    +    >>> is_annotated(Annotated)
    +    True
    +    >>> is_annotated(Annotated[int, ...])
    +    False
    +    ```
    +    """
    +
    +def is_any(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Any`][typing.Any] [special form][].
    +
    +    ```pycon
    +    >>> is_any(Any)
    +    True
    +    ```
    +    """
    +
    +def is_classvar(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`ClassVar`][typing.ClassVar] [type qualifier][].
    +
    +    ```pycon
    +    >>> is_classvar(ClassVar)
    +    True
    +    >>> is_classvar(ClassVar[int])
    +    >>> False
    +    ```
    +    """
    +
    +def is_concatenate(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Concatenate`][typing.Concatenate] [special form][].
    +
    +    ```pycon
    +    >>> is_concatenate(Concatenate)
    +    True
    +    >>> is_concatenate(Concatenate[int, P])
    +    False
    +    ```
    +    """
    +
    +def is_final(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Final`][typing.Final] [type qualifier][].
    +
    +    ```pycon
    +    >>> is_final(Final)
    +    True
    +    >>> is_final(Final[int])
    +    False
    +    ```
    +    """
    +
    +def is_forwardref(obj: Any, /) -> TypeIs[ForwardRef]:
    +    """
    +    Return whether the argument is an instance of [`ForwardRef`][typing.ForwardRef].
    +
    +    ```pycon
    +    >>> is_forwardref(ForwardRef('T'))
    +    True
    +    ```
    +    """
    +
    +def is_generic(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Generic`][typing.Generic] [special form][].
    +
    +    ```pycon
    +    >>> is_generic(Generic)
    +    True
    +    >>> is_generic(Generic[T])
    +    False
    +    ```
    +    """
    +
    +def is_literal(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Literal`][typing.Literal] [special form][].
    +
    +    ```pycon
    +    >>> is_literal(Literal)
    +    True
    +    >>> is_literal(Literal["a"])
    +    False
    +    ```
    +    """
    +
    +def is_paramspec(obj: Any, /) -> TypeIs[ParamSpec]:
    +    """
    +    Return whether the argument is an instance of [`ParamSpec`][typing.ParamSpec].
    +
    +    ```pycon
    +    >>> P = ParamSpec('P')
    +    >>> is_paramspec(P)
    +    True
    +    ```
    +    """
    +
    +def is_typevar(obj: Any, /) -> TypeIs[TypeVar]:
    +    """
    +    Return whether the argument is an instance of [`TypeVar`][typing.TypeVar].
    +
    +    ```pycon
    +    >>> T = TypeVar('T')
    +    >>> is_typevar(T)
    +    True
    +    ```
    +    """
    +
    +def is_typevartuple(obj: Any, /) -> TypeIs[TypeVarTuple]:
    +    """
    +    Return whether the argument is an instance of [`TypeVarTuple`][typing.TypeVarTuple].
    +
    +    ```pycon
    +    >>> Ts = TypeVarTuple('Ts')
    +    >>> is_typevartuple(Ts)
    +    True
    +    ```
    +    """
    +
    +def is_union(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Union`][typing.Union] [special form][].
    +
    +    This function can also be used to check for the [`Optional`][typing.Optional] [special form][],
    +    as at runtime, `Optional[int]` is equivalent to `Union[int, None]`.
    +
    +    ```pycon
    +    >>> is_union(Union)
    +    True
    +    >>> is_union(Union[int, str])
    +    False
    +    ```
    +
    +    !!! warning
    +        This does not check for unions using the [new syntax][types-union] (e.g. `int | str`).
    +    """
    +
    +def is_namedtuple(obj: Any, /) -> bool:
    +    """Return whether the argument is a named tuple type.
    +
    +    This includes [`NamedTuple`][typing.NamedTuple] subclasses and classes created from the
    +    [`collections.namedtuple`][] factory function.
    +
    +    ```pycon
    +    >>> class User(NamedTuple):
    +    ...     name: str
    +    ...
    +    >>> is_namedtuple(User)
    +    True
    +    >>> City = collections.namedtuple('City', [])
    +    >>> is_namedtuple(City)
    +    True
    +    >>> is_namedtuple(NamedTuple)
    +    False
    +    ```
    +    """
    +
    +def is_literalstring(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`LiteralString`][typing.LiteralString] [special form][].
    +
    +    ```pycon
    +    >>> is_literalstring(LiteralString)
    +    True
    +    ```
    +    """
    +
    +def is_never(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Never`][typing.Never] [special form][].
    +
    +    ```pycon
    +    >>> is_never(Never)
    +    True
    +    ```
    +    """
    +
    +def is_newtype(obj: Any, /) -> TypeIs[NewType]:
    +    """
    +    Return whether the argument is a [`NewType`][typing.NewType].
    +
    +    ```pycon
    +    >>> UserId = NewType("UserId", int)
    +    >>> is_newtype(UserId)
    +    True
    +    ```
    +    """
    +
    +def is_nodefault(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`NoDefault`][typing.NoDefault] sentinel object.
    +
    +    ```pycon
    +    >>> is_nodefault(NoDefault)
    +    True
    +    ```
    +    """
    +
    +def is_noextraitems(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the `NoExtraItems` sentinel object.
    +
    +    ```pycon
    +    >>> is_noextraitems(NoExtraItems)
    +    True
    +    ```
    +    """
    +
    +def is_noreturn(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`NoReturn`][typing.NoReturn] [special form][].
    +
    +    ```pycon
    +    >>> is_noreturn(NoReturn)
    +    True
    +    >>> is_noreturn(Never)
    +    False
    +    ```
    +    """
    +
    +def is_notrequired(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`NotRequired`][typing.NotRequired] [special form][].
    +
    +    ```pycon
    +    >>> is_notrequired(NotRequired)
    +    True
    +    ```
    +    """
    +
    +def is_paramspecargs(obj: Any, /) -> TypeIs[ParamSpecArgs]:
    +    """
    +    Return whether the argument is an instance of [`ParamSpecArgs`][typing.ParamSpecArgs].
    +
    +    ```pycon
    +    >>> P = ParamSpec('P')
    +    >>> is_paramspecargs(P.args)
    +    True
    +    ```
    +    """
    +
    +def is_paramspeckwargs(obj: Any, /) -> TypeIs[ParamSpecKwargs]:
    +    """
    +    Return whether the argument is an instance of [`ParamSpecKwargs`][typing.ParamSpecKwargs].
    +
    +    ```pycon
    +    >>> P = ParamSpec('P')
    +    >>> is_paramspeckwargs(P.kwargs)
    +    True
    +    ```
    +    """
    +
    +def is_readonly(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`ReadOnly`][typing.ReadOnly] [special form][].
    +
    +    ```pycon
    +    >>> is_readonly(ReadOnly)
    +    True
    +    ```
    +    """
    +
    +def is_required(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Required`][typing.Required] [special form][].
    +
    +    ```pycon
    +    >>> is_required(Required)
    +    True
    +    ```
    +    """
    +
    +def is_self(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Self`][typing.Self] [special form][].
    +
    +    ```pycon
    +    >>> is_self(Self)
    +    True
    +    ```
    +    """
    +
    +def is_typealias(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`TypeAlias`][typing.TypeAlias] [special form][].
    +
    +    ```pycon
    +    >>> is_typealias(TypeAlias)
    +    True
    +    ```
    +    """
    +
    +def is_typeguard(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`TypeGuard`][typing.TypeGuard] [special form][].
    +
    +    ```pycon
    +    >>> is_typeguard(TypeGuard)
    +    True
    +    ```
    +    """
    +
    +def is_typeis(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`TypeIs`][typing.TypeIs] [special form][].
    +
    +    ```pycon
    +    >>> is_typeis(TypeIs)
    +    True
    +    ```
    +    """
    +
    +def is_typealiastype(obj: Any, /) -> TypeIs[TypeAliasType]:
    +    """
    +    Return whether the argument is a [`TypeAliasType`][typing.TypeAliasType] instance.
    +
    +    ```pycon
    +    >>> type MyInt = int
    +    >>> is_typealiastype(MyInt)
    +    True
    +    >>> MyStr = TypeAliasType("MyStr", str)
    +    >>> is_typealiastype(MyStr):
    +    True
    +    >>> type MyList[T] = list[T]
    +    >>> is_typealiastype(MyList[int])
    +    False
    +    ```
    +    """
    +
    +def is_unpack(obj: Any, /) -> bool:
    +    """
    +    Return whether the argument is the [`Unpack`][typing.Unpack] [special form][].
    +
    +    ```pycon
    +    >>> is_unpack(Unpack)
    +    True
    +    >>> is_unpack(Unpack[Ts])
    +    False
    +    ```
    +    """
    +
    +def is_deprecated(obj: Any, /) -> TypeIs[deprecated]:
    +    """
    +    Return whether the argument is a [`deprecated`][warnings.deprecated] instance.
    +
    +    This also includes the [`typing_extensions` backport][typing_extensions.deprecated].
    +
    +    ```pycon
    +    >>> is_deprecated(warnings.deprecated('message'))
    +    True
    +    >>> is_deprecated(typing_extensions.deprecated('deprecated'))
    +    True
    +    ```
    +    """
    +
    +DEPRECATED_ALIASES: Final[dict[Any, type[Any]]]
    +"""A mapping between the deprecated typing aliases to their replacement, as per [PEP 585](https://peps.python.org/pep-0585/)."""
    diff --git a/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/INSTALLER
    new file mode 100644
    index 0000000..a1b589e
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/INSTALLER
    @@ -0,0 +1 @@
    +pip
    diff --git a/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/METADATA
    new file mode 100644
    index 0000000..5aabea9
    --- /dev/null
    +++ b/.venv/lib/python3.12/site-packages/ujson-5.12.1.dist-info/METADATA
    @@ -0,0 +1,205 @@
    +Metadata-Version: 2.4
    +Name: ujson
    +Version: 5.12.1
    +Summary: Ultra fast JSON encoder and decoder for Python
    +Home-page: https://github.com/ultrajson/ultrajson
    +Download-URL: https://github.com/ultrajson/ultrajson
    +Author: Jonas Tarnstrom
    +License: BSD-3-Clause AND TCL
    +Project-URL: Source, https://github.com/ultrajson/ultrajson
    +Platform: any
    +Classifier: Development Status :: 5 - Production/Stable
    +Classifier: Intended Audience :: Developers
    +Classifier: Programming Language :: C
    +Classifier: Programming Language :: Python :: 3
    +Classifier: Programming Language :: Python :: 3 :: Only
    +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 :: 3.14
    +Classifier: Programming Language :: Python :: Implementation :: CPython
    +Classifier: Programming Language :: Python :: Implementation :: GraalPy
    +Classifier: Programming Language :: Python :: Implementation :: PyPy
    +Classifier: Typing :: Typed
    +Requires-Python: >=3.10
    +Description-Content-Type: text/markdown
    +License-File: LICENSE.txt
    +Dynamic: download-url
    +Dynamic: license-file
    +
    +# UltraJSON
    +
    +[![PyPI version](https://img.shields.io/pypi/v/ujson.svg?logo=pypi&logoColor=FFE873)](https://pypi.org/project/ujson)
    +[![Supported Python versions](https://img.shields.io/pypi/pyversions/ujson.svg?logo=python&logoColor=FFE873)](https://pypi.org/project/ujson)
    +[![PyPI downloads](https://img.shields.io/pypi/dm/ujson.svg)](https://pypistats.org/packages/ujson)
    +[![GitHub Actions status](https://github.com/ultrajson/ultrajson/workflows/Test/badge.svg)](https://github.com/ultrajson/ultrajson/actions)
    +[![codecov](https://codecov.io/gh/ultrajson/ultrajson/branch/main/graph/badge.svg)](https://codecov.io/gh/ultrajson/ultrajson)
    +[![DOI](https://zenodo.org/badge/1418941.svg)](https://zenodo.org/badge/latestdoi/1418941)
    +[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
    +
    +UltraJSON is an ultra fast JSON encoder and decoder written in pure C with bindings for
    +Python.
    +
    +Install with pip:
    +
    +```sh
    +python -m pip install ujson
    +```
    +
    +## Project status
    +
    +> [!WARNING]
    +> UltraJSON's architecture is fundamentally ill-suited to making changes without
    +> risk of introducing new security vulnerabilities. As a result, this library
    +> has been put into a *maintenance-only* mode. Support for new Python versions
    +> will be added and critical bugs and security issues will still be
    +> fixed but all other changes will be rejected. Users are encouraged to migrate
    +> to [orjson](https://pypi.org/project/orjson/) which is both much faster and
    +> less likely to introduce a surprise buffer overflow vulnerability in the
    +> future.
    +
    +## Usage
    +
    +May be used as a drop in replacement for most other JSON parsers for Python:
    +
    +```pycon
    +>>> import ujson
    +>>> ujson.dumps([{"key": "value"}, 81, True])
    +'[{"key":"value"},81,true]'
    +>>> ujson.loads("""[{"key": "value"}, 81, true]""")
    +[{'key': 'value'}, 81, True]
    +```
    +
    +### Encoder options
    +
    +#### encode_html_chars
    +
    +Used to enable special encoding of "unsafe" HTML characters into safer Unicode
    +sequences. Default is `False`:
    +
    +```pycon
    +>>> ujson.dumps("

    JMh`rF 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�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μOU>|jXr8y6E~FM?0=|_QcC^> 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%0A>ffL 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®DYe4qIskA)zl5Z+Zjx2!aIG6 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

    Tv=T<6pZ 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